GAP8 SDK

 docker run -it --privileged -p 828:22 --gpus all -e "TERM=xterm-256color" hyeondg/gapuino_v3 

TL;DR

 xhost +localhost; docker run -it --rm --gpus all -e "TERM=xterm-256color" -e "DISPLAY=$DISPLAY" -v /tmp/.X11-unix/:/tmp/.X11-unix/ hyeondg/gapuino_v3 

PMSIS API

#include "pmsis.h"
#include "stdio.h"

// This is the entry point executed by all processing elements when the cluster
// controller is doing a fork
static void pe_entry(void* arg) {
  printf("(%ld, %ld) HI! Entering processing element\n", pi_cluster_id(), pi_core_id());

  // Just execute a barrier and leave
  pi_cl_team_barrier(); // meaningless
}

// This is the entry point executed by the cluster controller
static void cluster_entry(void* arg) {
  // cluster controller = master core = core 0
  printf("(%ld, %ld) Entering cluster controller\n", pi_cluster_id(), pi_core_id());

  // fork nb_cores with args
  pi_cl_team_fork(pi_cl_cluster_nb_cores(), pe_entry, NULL);
}

static void cluster_task_callback(void* arg) { printf("async done: %d %d", pi_cluster_id(), pi_core_id()); }

static inline int test_entry() {
  printf("(%ld, %ld) Entering main controller\n", pi_cluster_id(), pi_core_id());

  struct pi_device cluster_dev;
  struct pi_cluster_conf conf;
  struct pi_cluster_task cluster_task;
  struct pi_task task;
  int errors = 0;

  // First open the cluster
  pi_cluster_conf_init(&conf);
  conf.id = 0;

  pi_open_from_conf(&cluster_dev, &conf);

  pi_cluster_open(&cluster_dev);

  pi_cluster_task(&cluster_task, cluster_entry, (void*)&errors);

  pi_task_callback(&task, cluster_task_callback, (void*)&task);

  // pi_cluster_send_task_to_cl_async(&cluster_dev, &cluster_task, &task);
  pi_cluster_send_task_async(&cluster_dev, &cluster_task, &task);
  // pi_cluster_send_task_to_cl(&cluster_dev, pi_cluster_task(&cluster_task, cluster_entry, NULL));
  // pi_cluster_send_task(&cluster_dev, pi_cluster_task(&cluster_task, cluster_entry, NULL));

  pi_cl_team_barrier();
  pi_cluster_close(&cluster_dev);
  return 0;
}

void test_kickoff(void* arg) {
  int ret = test_entry();
  pmsis_exit(ret);
}

int main() { return pmsis_kickoff((void*)test_kickoff); }

GAP’s Memory Hierarchy

GAP’s memory hierarchy is made up of three levels:

  1. Shared level 1 memory

    Internal and tightly coupled with GAP cluster, it can deliver up to 8 parallel memory accesses in a single cycle. This is by far GAP’s fastest memory and has the highest bandwidth. As it is quite costly, its size has had to be kept relatively small, 64 Kbytes in the current configuration.

  2. Level 2 memory

    Internal memory significantly larger than level 1, but with higher access latency (approximately 6 cycles) and lower bandwidth. Its primary role is to store programs that are then fetched by the different instruction caches attached to GAP’s various cores and to store relatively large data structures. In the current version its size is 512 Kbytes.

  3. Level 3 memory

    External and optional (in the case of RAM). It is either read only (Flash) or read/write (RAM). Read only memory is mapped onto either the quad-SPI or HyperBus interfaces. Read/write memory is mapped onto the HyperBus interface. The latency, the access time and the bandwidth is even more limited than the other 2 memory areas and importantly accesses to level 3 memory consume more energy.

AT Architecture

  1. Basic Kernels
  • tools/autotiler_v3/Generators/MatAdd/MatAddBasicKernels.c ```c typedef struct MatrixAdd_Arg{ int *In1; int *In2; int *Out; unsigned int W; unsigned int H; } MatrixAdd_Arg_T;

void MatSumPar(MatrixAdd_Arg_T *KerArg0 );


- `tools/autotiler_v3/Generators/MatAdd/MatAddGenerator.c`
```c
void LoadMatAddLibrary()
{

    LibKernel("MatSumPar", CALL_PARALLEL,
        CArgs(5,
            TCArg("int * __restrict__", "In1"),
            TCArg("int * __restrict__", "In2"),
            TCArg("int * __restrict__", "Out"),

            TCArg("unsigned int", "W"),
            TCArg("unsigned int", "H")
        ),
        "MatrixAdd_Arg_T",
        NULL
    );

}


void GenerateMatAddPar(char *UserKernelName, int W, int H)
{

    UserKernel(UserKernelName,
        // 2D kernel
        KernelIterSpace(1, IterTiledSpace(KER_ITER_TILE0)),
        // Tile horizontally
        TILE_HOR,
        // C template for this user kernel
        CArgs(3,
          TCArg("int * __restrict__", "In1"),
          TCArg("int * __restrict__", "In2"),
          TCArg("int * __restrict__", "Out")
        ),
        // Basic kernel we want to call on tiles, in this case MatSumPar
        Calls(1,
            Call("MatSumPar", LOC_LOOP,
                Bindings(5,
                    K_Arg("In1", KER_ARG_TILE),   // A tile from In1
                    K_Arg("In2", KER_ARG_TILE),   // A tile from In2
                    K_Arg("Out", KER_ARG_TILE),   // A tile from Out
                    // Tile(Out) = Tile(In1) + Tile(In2)

                    K_Arg("In1", KER_ARG_TILE_W), // Tile width
                    K_Arg("In1", KER_ARG_TILE_H)  // Tile height
                )
            )
        ),
        // All 3 user kernel arguments are of dimension WxH with
        // basic type = plain int
        KerArgs(3,
            // First kernel argument, bound to In1. In1 in L2, double buffered access
            KerArg("In1", KerArgSpace(1,KER_ITER_TILE0), O_IN|O_DB, W, H,  sizeof(int), 0, 0, 0, "In1"),
            // Second kernel argument, bound to In2. In2 in L2, double buffered access
            KerArg("In2", KerArgSpace(1,KER_ITER_TILE0), O_IN|O_DB, W, H,  sizeof(int), 0, 0, 0, "In2"),
            // Third kernel argument, bound to Out. Out in L2, double buffered access
            KerArg("Out", KerArgSpace(1,KER_ITER_TILE0), O_OUT|O_DB, W, H, sizeof(int), 0, 0, 0, "Out")
        )
    );
}

void MatAddConfiguration(unsigned int L1Memory)
{
    // Inline the kernel
    SetInlineMode(ALWAYS_INLINE);

    // Dynamically allocate memory
    SetSymbolDynamics();

    // Define the names that we want to create
    SetUsedFilesNames(0, 1, "MatAddBasicKernels.h");
    SetGeneratedFilesNames("MatAddKernels.c", "MatAddKernels.h");

    // Set the shared L1 memory available
    SetL1MemorySize(L1Memory);
}
  1. User Kernels

TODO

  1. User Kernel Group

TODO

--
$(MODEL_STATE): $(MODEL_PATH) $(IMAGES) $(NNTOOL_SCRIPT) | $(MODEL_BUILD) -> MnistModel.json
	echo "GENERATING NNTOOL STATE FILE"
	$(NNTOOL) -s $(NNTOOL_SCRIPT) $< $(NNTOOL_EXTRA_FLAGS)

nntool_state: $(MODEL_STATE)

# Runs NNTOOL with its state file to generate the autotiler model code
$(AT_MODEL_PATH) $(MODEL_EXPRESSIONS): $(MODEL_STATE) $(MODEL_PATH) | $(MODEL_BUILD)
	echo "GENERATING AUTOTILER MODEL"
	$(NNTOOL) -g -M $(MODEL_BUILD) -m $(MODEL_SRC) -T $(TENSORS_DIR) -H $(MODEL_HEADER) $(MODEL_GENFLAGS_EXTRA) $<

$(MODEL_GEN_EXE): $(CNN_GEN) $(AT_MODEL_PATH) $(EXTRA_GENERATOR_SRC) | $(MODEL_BUILD) -> GenTile
	echo "COMPILING AUTOTILER MODEL"
	gcc -g -o $(MODEL_GEN_EXE) -I. -I$(TILER_INC) -I$(TILER_EMU_INC) $(CNN_GEN_INCLUDE) $(CNN_LIB_INCLUDE) $^ $(TILER_LIB) $(SDL_FLAGS)


# Run the code generator to generate GAP graph and kernel code
$(MODEL_GEN_C): $(MODEL_GEN_EXE) -> MnistKerel.c
	echo "RUNNING AUTOTILER MODEL - START"
	$(MODEL_GEN_EXE) -o $(MODEL_BUILD) -c $(MODEL_BUILD) $(MODEL_GEN_EXTRA_FLAGS)
	echo "RUNNING AUTOTILER MODEL - FINISH"

all:: build image flash_fs

$(BUILDDIR):
	mkdir -p $@

# Rules for creating dependency files (.d).
#------------------------------------------
$(T_OBJECTS_C) : $(BUILDDIR)/%.o : %.c
	@mkdir -p $(dir $@)
	$(CC) $(PULP_CFLAGS) $(TCFLAGS) $< $(INC_PATH) -MD -MF $(basename $@).d -o $@

$(BUILDDIR)/pulp-os/conf.o: $(GAP_SDK_HOME)/gap8/rtos/pulp/pulp-os/kernel/conf.c
	@mkdir -p $(dir $@)
	$(CC) $(PULP_CFLAGS) $(TCFLAGS) $< $(INC_PATH) -MD -MF $(basename $@).d -o $@

$(T_OBJECTS_CXX) : $(BUILDDIR)/%.o : %.cpp
	mkdir -p $(dir $@)
	$(CXX) $(CXXFLAGS) $(PULP_CXXFLAGS) $(TCFLAGS) $< $(INC_PATH) -MD -MF $(basename $@).d -o $@

$(BIN): $(OBJECTS)
	$(CC) $(PULP_ARCH_LDFLAGS) -MMD -MP $(WRAP_FLAGS) $(PULP_LDFLAGS) $(INC_PATH) -o $(BIN) $(OBJECTS) $(LIBS) $(LDFLAGS) $(LIBSFLAGS) $(INC_DEFINE)

override config_args += $(foreach file, $(READFS_FILES), --config-opt=flash/content/partitions/readfs/files=$(file))
override config_args += $(foreach file, $(HOSTFS_FILES), --config-opt=flash/content/partitions/hostfs/files=$(file))

ifdef LFS_ROOT_DIR
override config_args += --config-opt=flash/content/partitions/lfs/root_dir=$(LFS_ROOT_DIR)
endif

ifneq ($(wsl),)
WSL_ENV="--wsl=$(wsl)"
endif

flash_noforce:

flash:
	gapy --target=$(GAPY_TARGET) --platform=$(platform) --work-dir=$(BUILDDIR) $(config_args) $(gapy_args) run --flash --force --binary=$(BIN) $(runner_args) $(WSL_ENV)

flash_fs:
	gapy --target=$(GAPY_TARGET) --platform=$(platform) --work-dir=$(BUILDDIR) $(config_args) $(gapy_args) run --flash --binary=$(BIN) $(runner_args) $(WSL_ENV)

image:
	gapy --target=$(GAPY_TARGET) --platform=$(platform) --work-dir=$(BUILDDIR) $(config_args) $(gapy_args) run --image --binary=$(BIN) $(runner_args) $(WSL_ENV)

run.prepare:
	gapy --target=$(GAPY_TARGET) --platform=$(platform) --work-dir=$(BUILDDIR) $(config_args) $(gapy_args) run --exec-prepare --binary=$(BIN) $(runner_args) $(WSL_ENV)

run.exec:
	gapy --target=$(GAPY_TARGET) --platform=$(platform) --work-dir=$(BUILDDIR) $(config_args) $(gapy_args) run --exec --binary=$(BIN) $(runner_args) $(WSL_ENV)

run:
	gapy --target=$(GAPY_TARGET) --platform=$(platform) --work-dir=$(BUILDDIR) $(config_args) $(gapy_args) run --exec-prepare --exec --binary=$(BIN) $(runner_args) $(WSL_ENV)

  1. CC (MnistModel.c) => GenTile

  2. GenTile =>  MnistKernel.c, MnistKernel.h, *.dat

		- C Code for user kernels and the user kernel graph
		- Header file
		- Flash content for graph constants

  3. CC (MnistKernel.c) => Mnist.bin

  4. gapy --target=gapuino_v3 --platform=gvsoc --work-dir=/gap_sdk/examples/gap8/nn/autotiler/Mnist/BUILD/GAP8_V3/GCC_RISCV_PULPOS      run --flash --binary=/gap_sdk/examples/gap8/nn/autotiler/Mnist/BUILD/GAP8_V3/GCC_RISCV_PULPOS/Mnist.bin




--