GAP8
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:
- 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.
- 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.
- 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
- 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);
}
- User Kernels
TODO
- 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
--