diff --git a/asm/asm.py b/asm/asm.py index 5bc3121..ad555d6 100644 --- a/asm/asm.py +++ b/asm/asm.py @@ -123,6 +123,8 @@ def write(token, value, bits): elif kind == 'i20': value = self.parse_int(20, token) write(token, value, 20) + elif kind == 'p4': + write(token, 0, 4) elif kind == 'p8': write(token, 0, 8) elif kind == 'p16': diff --git a/asm/sim.py b/asm/sim.py index 95c7cec..99440ba 100644 --- a/asm/sim.py +++ b/asm/sim.py @@ -313,12 +313,14 @@ def read(bits): result.append(read(16)) elif kind == 'i20': result.append(read(20)) + elif kind == 'p4': + read(4) elif kind == 'p8': - pass + read(8) elif kind == 'p16': - pass + read(16) elif kind == 'p20': - pass + read(20) else: raise Exception(f'Illegal parameter type: {kind}') diff --git a/examples/with-load-store-vadd.bf16.asm b/examples/with-load-store-vadd.bf16.asm index fb4b2a9..f601579 100644 --- a/examples/with-load-store-vadd.bf16.asm +++ b/examples/with-load-store-vadd.bf16.asm @@ -30,13 +30,17 @@ def finalize(host): # Load 0x200000 into A seti %a 0x20 # (128) / 4 -seti %b 0x4000 # 0x200000 / 128 +# seti %b 0x4000 # 0x200000 / 128 +seti_low %b 0x4000 # 0x200000 / 128 +seti_high %b 0x0000 # 0x200000 / 128 seti %c 1024 # (2048 * 2) / 4 load %a %b %c # Load 0x201000 into B seti %a 0x420 # (128 + 4096) / 4 -seti %b 0x4020 # 0x201000 / 128 +# seti %b 0x4020 # 0x201000 / 128 +seti_low %b 0x4020 # 0x201000 / 128 +seti_high %b 0x0000 # 0x201000 / 128 seti %c 1024 # (2048 * 2) / 4 load %a %b %c @@ -48,8 +52,10 @@ seti %d 2048 # count is 2048 vadd.bf16 %c %a %b %d -# Store C to 0x202000 -seti %a 0x4040 # 0x202000 / 128 +# Store C to 0x202000 0x203000 0x204000 0x205000 +# seti %a 0x4040 # 0x202000 / 128 +seti_low %a 0x4040 # 0x202000 / 128 0x4040 0x4060 0x4080 0x40a0 +seti_high %a 0x0000 # 0x202000 / 128 seti %b 0x820 # (128 + 4096 + 4096) / 4 seti %c 1024 # (2048 * 2) / 4 store %a %b %c diff --git a/examples/with-load-store-vdiv.bf16.asm b/examples/with-load-store-vdiv.bf16.asm index e866a0b..66e0707 100644 --- a/examples/with-load-store-vdiv.bf16.asm +++ b/examples/with-load-store-vdiv.bf16.asm @@ -30,13 +30,17 @@ def finalize(host): # Load 0x200000 into A seti %a 0x20 # (128) / 4 -seti %b 0x4000 # 0x200000 / 128 +# seti %b 0x4000 # 0x200000 / 128 +seti_low %b 0x4000 # 0x200000 / 128 +seti_high %b 0x0000 # 0x200000 / 128 seti %c 1024 # (2048 * 2) / 4 load %a %b %c # Load 0x201000 into B seti %a 0x420 # (128 + 4096) / 4 -seti %b 0x4020 # 0x201000 / 128 +# seti %b 0x4020 # 0x201000 / 128 +seti_low %b 0x4020 # 0x201000 / 128 +seti_high %b 0x0000 # 0x201000 / 128 seti %c 1024 # (2048 * 2) / 4 load %a %b %c @@ -49,7 +53,9 @@ seti %d 2048 # count is 2048 vdiv.bf16 %c %a %b %d # Store C to 0x202000 -seti %a 0x4040 # 0x202000 / 128 +# seti %a 0x4040 # 0x202000 / 128 +seti_low %a 0x4040 # 0x202000 / 128 +seti_high %a 0x0000 # 0x202000 / 128 seti %b 0x820 # (128 + 4096 + 4096) / 4 seti %c 1024 # (2048 * 2) / 4 store %a %b %c diff --git a/examples/with-load-store-vmul.bf16.asm b/examples/with-load-store-vmul.bf16.asm index b819b73..bbc6d4d 100644 --- a/examples/with-load-store-vmul.bf16.asm +++ b/examples/with-load-store-vmul.bf16.asm @@ -30,13 +30,17 @@ def finalize(host): # Load 0x200000 into A seti %a 0x20 # (128) / 4 -seti %b 0x4000 # 0x200000 / 128 +# seti %b 0x4000 # 0x200000 / 128 +seti_low %b 0x4000 # 0x200000 / 128 +seti_high %b 0x0000 # 0x200000 / 128 seti %c 1024 # (2048 * 2) / 4 load %a %b %c # Load 0x201000 into B seti %a 0x420 # (128 + 4096) / 4 -seti %b 0x4020 # 0x201000 / 128 +# seti %b 0x4020 # 0x201000 / 128 +seti_low %b 0x4020 # 0x201000 / 128 +seti_high %b 0x0000 # 0x201000 / 128 seti %c 1024 # (2048 * 2) / 4 load %a %b %c @@ -49,7 +53,9 @@ seti %d 2048 # count is 2048 vmul.bf16 %c %a %b %d # Store C to 0x202000 -seti %a 0x4040 # 0x202000 / 128 +# seti %a 0x4040 # 0x202000 / 128 +seti_low %a 0x4040 # 0x202000 / 128 +seti_high %a 0x0000 # 0x202000 / 128 seti %b 0x820 # (128 + 4096 + 4096) / 4 seti %c 1024 # (2048 * 2) / 4 store %a %b %c diff --git a/examples/with-load-store-vsub.bf16.asm b/examples/with-load-store-vsub.bf16.asm index 99dabb2..be43773 100644 --- a/examples/with-load-store-vsub.bf16.asm +++ b/examples/with-load-store-vsub.bf16.asm @@ -30,13 +30,17 @@ def finalize(host): # Load 0x200000 into A seti %a 0x20 # (128) / 4 -seti %b 0x4000 # 0x200000 / 128 +# seti %b 0x4000 # 0x200000 / 128 +seti_low %b 0x4000 # 0x200000 / 128 +seti_high %b 0x0000 # 0x200000 / 128 seti %c 1024 # (2048 * 2) / 4 load %a %b %c # Load 0x201000 into B seti %a 0x420 # (128 + 4096) / 4 -seti %b 0x4020 # 0x201000 / 128 +# seti %b 0x4020 # 0x201000 / 128 +seti_low %b 0x4020 # 0x201000 / 128 +seti_high %b 0x0000 # 0x201000 / 128 seti %c 1024 # (2048 * 2) / 4 load %a %b %c @@ -49,7 +53,9 @@ seti %d 2048 # count is 2048 vsub.bf16 %c %a %b %d # Store C to 0x202000 -seti %a 0x4040 # 0x202000 / 128 +# seti %a 0x4040 # 0x202000 / 128 +seti_low %a 0x4040 # 0x202000 / 128 +seti_high %a 0x0000 # 0x202000 / 128 seti %b 0x820 # (128 + 4096 + 4096) / 4 seti %c 1024 # (2048 * 2) / 4 store %a %b %c diff --git a/examples/without-load-store-vadd.bf16.asm b/examples/without-load-store-vadd.bf16.asm index 9506b36..7fc31a9 100644 --- a/examples/without-load-store-vadd.bf16.asm +++ b/examples/without-load-store-vadd.bf16.asm @@ -38,8 +38,13 @@ seti %a 0x20 # 128 / 4 # A is stored at 128 seti %b 0x420 # (128 + 4096) / 4 # B is stored at 128 + 4096 seti %c 0x820 # (128 + 4096 *2) / 4 # C is stored at 128 + 4096 + 4096 seti %d 2048 # count is 2048 +seti %e 0x400 # 1024 +seti %f 0x0 vadd.bf16 %c %a %b %d +add.i32 %f %zero 0x1 +#ifneq %e %f 0xc010 +ifneq %e %f -8 # Interrupt to CPU return diff --git a/src/testdriver/fpc.c b/src/testdriver/fpc.c new file mode 100644 index 0000000..346dd65 --- /dev/null +++ b/src/testdriver/fpc.c @@ -0,0 +1,579 @@ +#include +#include +#include +#include +#include +#include "fpc.h" + +// operating frequence in MHz +#define FREQ 50 + +// the number of element to be tested +#define MAX_ELEMENT_SIZE 2048 // 2048 +#define ELEMENT_SIZE 512 + +#define VARIABLE_ALIGN 128 + +#define START_CORE_ID 0 +#define END_CORE_ID 2 + +#define USED_DATA 1 // 0: random, 1: index + +//#define PRINT_SUCCESS_RESULT_DATA + +//#define LOAD_STORE_TEST + +#ifdef LOAD_STORE_TEST +#define SRAM_POSITION 0 // 0: input_A, 1: input_B, 2: result_C +#endif + +// core id +#define CORE0 0 +#define CORE1 1 +#define CORE2 2 +#define CORE3 3 +#define MAX_CORE 2 + + +// registers +#define FPC_ID 0x00000000 +#define PROC_STATUS 0x00000008 +#define KERNEL_OFFSET 0x00000010 +#define KERNEL_SIZE 0x00000018 +#define KERNEL_LOAD 0x00000020 +#define RUN_FPU 0x00000028 +#define RUN_CYCLE 0x00000030 + +// FPU Code +#define FPU_ADD 0 +#define FPU_SUB 1 +#define FPU_MUL 2 +#define FPU_DIV 3 +#define MAX_FPU 4 + +#define SYS_CLK 50000000 + +#define SIZE_M 1 + +#define _USE_VARIABLES_ 1 + +// variables +#if _USE_VARIABLES_ +//__attribute__ ((aligned (VARIABLE_ALIGN))) volatile static float a[ELEMENT_SIZE]; +__attribute__ ((aligned (VARIABLE_ALIGN))) volatile static float y[ELEMENT_SIZE] __attribute__((section(".nocache"))); +#ifdef LOAD_STORE_TEST +__attribute__ ((aligned (VARIABLE_ALIGN))) volatile static float y1[ELEMENT_SIZE] __attribute__((section(".nocache"))); +__attribute__ ((aligned (VARIABLE_ALIGN))) volatile static float y2[ELEMENT_SIZE] __attribute__((section(".nocache"))); +#endif +__attribute__ ((aligned (VARIABLE_ALIGN))) volatile static float a[ELEMENT_SIZE]; +__attribute__ ((aligned (VARIABLE_ALIGN))) volatile static float b[ELEMENT_SIZE]; +__attribute__ ((aligned (VARIABLE_ALIGN))) volatile static float r[ELEMENT_SIZE]; +#else +float *d; +float *a; +float *b; +float *y; +#ifdef LOAD_STORE_TEST +float *y1; +float *y2; +#endif +float *r; +#endif +__attribute__ ((aligned (VARIABLE_ALIGN))) volatile uint32_t kbuf[1024]; +__attribute__ ((aligned (VARIABLE_ALIGN))) volatile int load_cycle[MAX_FPU][MAX_CORE]; +__attribute__ ((aligned (VARIABLE_ALIGN))) volatile int fpu_cycle[MAX_FPU][MAX_CORE]; +volatile uint32_t klen = 0; + +volatile uint32_t not_ok = 0; +volatile uint32_t not_cok = 0; +// functions +void reg_write(uint32_t adr, uint64_t wd) +{ + FpcWrite(adr, wd); +} + +uint64_t reg_read(uint64_t adr) +{ + uint64_t rd; + FpcRead(adr, rd); + return rd; +} + +static inline uint64_t get_time() { + uint64_t tmp; + + asm volatile("csrrs %0, cycle, x0":"=r"(tmp)); + return tmp; +} + +void delay_in_usec(int us) { + + uint64_t cycle_start; + uint64_t cycle_end; + int elapsedTime; + + cycle_start = get_time(); + cycle_end = get_time(); + elapsedTime = (cycle_end - cycle_start) / (SYS_CLK / 1000000); + while(elapsedTime < us) { + cycle_end = get_time(); + elapsedTime = (cycle_end - cycle_start) / (SYS_CLK / 1000000); + } +} + +//---- generate data +#define set_data(a, r, o) (((a) << 16) | ((r) << 8) | (o)) +void generate_kernel(int fpu) +{ + int size = MAX_ELEMENT_SIZE; + int count = ELEMENT_SIZE; + int bsiz, aa, ba, ya, am, bm, ym; + + bsiz = size * 4; + + // main memory address + am = (int)a; + bm = (int)b; + ym = (int)y; + + // local sram address + aa = 0x100; + ba = aa + bsiz; + ya = ba + bsiz; + + // create kernel message + int pos = 0; + + // load + kbuf[pos++] = set_data((am>>16)&0xFFFF, 0x01, 0x01); // set high value in reg1 + kbuf[pos++] = set_data(am&0xFFFF, 0x01, 0x02); // set low value in reg1 + kbuf[pos++] = set_data((aa>>16)&0xFFFF, 0x02, 0x01); // set high value in reg2 + kbuf[pos++] = set_data(aa&0xFFFF, 0x02, 0x02); // set low value in reg2 + kbuf[pos++] = set_data(0x00, count&0xFFFF, 0x03); // load data + + // load + kbuf[pos++] = set_data((bm>>16)&0xFFFF, 0x01, 0x01); // set high value in reg1 + kbuf[pos++] = set_data(bm&0xFFFF, 0x01, 0x02); // set low value in reg1 + kbuf[pos++] = set_data((ba>>16)&0xFFFF, 0x02, 0x01); // set high value in reg2 + kbuf[pos++] = set_data(ba&0xFFFF, 0x02, 0x02); // set low value in reg2 + kbuf[pos++] = set_data(0x00, count&0xFFFF, 0x03); // load data + + // fpu + kbuf[pos++] = set_data((aa>>16)&0xFFFF, 0x01, 0x01); // set high value in reg1 + kbuf[pos++] = set_data(aa&0xFFFF, 0x01, 0x02); // set low value in reg1 + kbuf[pos++] = set_data((ba>>16)&0xFFFF, 0x02, 0x01); // set high value in reg2 + kbuf[pos++] = set_data(ba&0xFFFF, 0x02, 0x02); // set low value in reg2 + kbuf[pos++] = set_data((ya>>16)&0xFFFF, 0x03, 0x01); // set high value in reg3 + kbuf[pos++] = set_data(ya&0xFFFF, 0x03, 0x02); // set low value in reg3 + kbuf[pos++] = set_data(0x00, count&0xFFFF, fpu+5); // fpu code + + // store + kbuf[pos++] = set_data((ym>>16)&0xFFFF, 0x01, 0x01); // set high value in reg1 + kbuf[pos++] = set_data(ym&0xFFFF, 0x01, 0x02); // set low value in reg1 + kbuf[pos++] = set_data((ya>>16)&0xFFFF, 0x02, 0x01); // set high value in reg2 + kbuf[pos++] = set_data(ya&0xFFFF, 0x02, 0x02); // set low value in reg2 + kbuf[pos++] = set_data(0x00, count&0xFFFF, 0x04); // store data + + // return + kbuf[pos++] = set_data(0x00, 0x00, 0x09); // return + + klen = 4 * pos; +} + +#ifdef LOAD_STORE_TEST +void generate_load_store_kernel(int fpu) +{ + int size = MAX_ELEMENT_SIZE; + int count = ELEMENT_SIZE; + int bsiz, aa, am, y0m, y1m,y2m; + + // buffer size in 256 elements + bsiz = size; + + // main memory address + am = (int)a; + y0m = (int)y; + y1m = (int)y1; + y2m = (int)y2; + + printf("am: %08x, y0m: %08x, y1m: %08x, y2m: %08x\n", am, y0m, y1m, y2m); + + aa = 0x100 + bsiz * SRAM_POSITION; + + // create kernel message + int pos = 0; + + // load + kbuf[pos++] = set_data((am>>16)&0xFFFF, 0x01, 0x01); // set high value in reg1 + kbuf[pos++] = set_data(am&0xFFFF, 0x01, 0x02); // set low value in reg1 + kbuf[pos++] = set_data((aa>>16)&0xFFFF, 0x02, 0x01); // set high value in reg2 + kbuf[pos++] = set_data(aa&0xFFFF, 0x02, 0x02); // set low value in reg2 + kbuf[pos++] = set_data(0x00, (count / SIZE_M)&0xFFFF, 0x03); // load data + + // store + kbuf[pos++] = set_data((y0m>>16)&0xFFFF, 0x01, 0x01); // set high value in reg1 + kbuf[pos++] = set_data(y0m&0xFFFF, 0x01, 0x02); // set low value in reg1 + kbuf[pos++] = set_data((aa>>16)&0xFFFF, 0x02, 0x01); // set high value in reg2 + kbuf[pos++] = set_data(aa&0xFFFF, 0x02, 0x02); // set low value in reg2 + kbuf[pos++] = set_data(0x00, (count / SIZE_M)&0xFFFF, 0x04); // store data + + // store + kbuf[pos++] = set_data((y1m>>16)&0xFFFF, 0x01, 0x01); // set high value in reg1 + kbuf[pos++] = set_data(y1m&0xFFFF, 0x01, 0x02); // set low value in reg1 + kbuf[pos++] = set_data((aa>>16)&0xFFFF, 0x02, 0x01); // set high value in reg2 + kbuf[pos++] = set_data(aa&0xFFFF, 0x02, 0x02); // set low value in reg2 + kbuf[pos++] = set_data(0x00, (count / SIZE_M)&0xFFFF, 0x04); // store data + + // store + kbuf[pos++] = set_data((y2m>>16)&0xFFFF, 0x01, 0x01); // set high value in reg1 + kbuf[pos++] = set_data(y2m&0xFFFF, 0x01, 0x02); // set low value in reg1 + kbuf[pos++] = set_data((aa>>16)&0xFFFF, 0x02, 0x01); // set high value in reg2 + kbuf[pos++] = set_data(aa&0xFFFF, 0x02, 0x02); // set low value in reg2 + kbuf[pos++] = set_data(0x00, (count / SIZE_M)&0xFFFF, 0x04); // store data + + // return + kbuf[pos++] = set_data(0x00, 0x00, 0x09); // return + + klen = 4 * pos; +} +#endif + +void wait_done(int core) +{ + uint64_t rd; + while((rd = reg_read(PROC_STATUS)) & (1 << core)) ; +} + +static void floatToString(float floatValue, char* strValue, int maxLength) { + + int intPart = (int)floatValue; + int decimalPart = (int)((floatValue - intPart) * 10000000); // Assuming 3 decimal places + + if(floatValue < 0) { + decimalPart = 0 - decimalPart; + } + if (maxLength < 40) { + // Buffer is too small to store anything meaningful + return; + } + + memset(strValue, 0, maxLength); + snprintf(strValue, maxLength, "%d.%07d", intPart, decimalPart); +} + +void print_value(int ok, int i, float fa, float fb) +{ + uint32_t *pa = (uint32_t *)&fa; + uint32_t *pb = (uint32_t *)&fb; + char aFloatStrValue[50]; + char bFloatStrValue[50]; + + if(!ok) { + floatToString(fa, aFloatStrValue, sizeof(aFloatStrValue)); + floatToString(fb, bFloatStrValue, sizeof(bFloatStrValue)); + printf("[F] : FPU[%4d]: %08X(%s) => %08X(%s)\n", i, *pa, aFloatStrValue, *pb, bFloatStrValue); + } else { +#ifdef PRINT_SUCCESS_RESULT_DATA + printf("[S] : FPU[%4d]: %08X => %08X\n", i, *pa, *pb); +#endif + } +} + +#ifdef LOAD_STORE_TEST +int loaf_store_test(void) { + + printf("---- FPU Controller Load-Store Test (Element size = %d) \n", ELEMENT_SIZE); + + printf("a: %p, y: %p, y1: %p, y2: %p\n", a, y, y1, y2); + + memset(a, 0, sizeof(a)); + memset(y, 0, sizeof(y)); + memset(y1, 0, sizeof(y1)); + memset(y2, 0, sizeof(y2)); + //---- main test + for(int fpu = 0;fpu < 1;fpu++) + { + // generate kernel data + generate_load_store_kernel(fpu); + + // init data + for(int i = 0;i < ELEMENT_SIZE;i++) + { +#if USED_DATA + a[i] = (float)(i + 1); // a sources +#else + a[i] = (float)rand() / RAND_MAX * 2000.0 - 1000.0; +#endif + } + + // run fpu + for(int core = START_CORE_ID;core < END_CORE_ID;core++) + { + printf("\tCORE: %d\n", core); + + // load kerneal + reg_write(KERNEL_OFFSET, (uint64_t)kbuf); + reg_write(KERNEL_SIZE, (uint64_t)(klen)); + reg_write(KERNEL_LOAD, (uint64_t)(1 << core)); + wait_done(core); + + // get load cycle + load_cycle[fpu][core] = reg_read(8 * (6 + core)); + + // run fpu + reg_write(RUN_FPU, (uint64_t)core); + wait_done(core); + + // get fpu cycles + fpu_cycle[fpu][core] = reg_read(8 * (6 + core)); + + printf("\nY0:\n"); + for(int i = 0;i < ELEMENT_SIZE;i++) + { + int ok = (y[i] >= a[i] ? y[i] - a[i] : a[i] - y[i]) < 0.0000001; + print_value(ok, i, a[i], y[i]); + if(!ok) { + not_ok++; + } + } + printf("\nY1:\n"); + for(int i = 0;i < ELEMENT_SIZE;i++) + { + int ok = (y1[i] >= a[i] ? y1[i] - a[i] : a[i] - y1[i]) < 0.0000001; + print_value(ok, i, a[i], y1[i]); + if(!ok) { + not_ok++; + } + } + printf("\nY2:\n"); + for(int i = 0;i < ELEMENT_SIZE;i++) + { + int ok = (y2[i] >= a[i] ? y2[i] - a[i] : a[i] - y2[i]) < 0.0000001; + print_value(ok, i, a[i], y2[i]); + if(!ok) { + not_ok++; + } + } + } + } + + // print result + printf("[ Result ]\n"); + + printf("---- FPU Controller Test Finished(not_ok: %d)\n", not_ok); + + return 0; +} +#endif + +#define BUFFER_ALIGNMENT 0 + +#if !_USE_VARIABLES_ +void release_variables() { + if(d != NULL) { + free(d); + } + if(a != NULL) { + free(a); + } + if(b != NULL) { + free(b); + } + if(y != NULL) { + free(y); + } +#ifdef LOAD_STORE_TEST + if(y1 != NULL) { + free(y1); + } + if(y2 != NULL) { + free(y2); + } +#endif + if(r != NULL) { + free(r); + } +} +#endif + +#if 0 +void non_cacheable() +{ + asm volatile("li t0, 0x80000000"); // Base address of the memory region + asm volatile("li t1, 0x80001000"); // End address of the memory region + asm volatile("li t2, 0x8"); // Set MMU flags to mark the region as non-cacheable + asm volatile("csrw pmpaddr0, t0"); + asm volatile("csrw pmpaddr1, t1"); + asm volatile("csrw pmpcfg0, t2"); +} +#endif + +int main(void) { + +#if !_USE_VARIABLES_ +// d = malloc(sizeof(float) * ELEMENT_SIZE); +// if(d == NULL) { +// printf("OOM(d) %u.\n", sizeof(float) * ELEMENT_SIZE + BUFFER_ALIGNMENT); +// return -1; +// } + a = malloc(sizeof(float) * ELEMENT_SIZE); + if(a == NULL) { + printf("OOM(a) %u.\n", sizeof(float) * ELEMENT_SIZE + BUFFER_ALIGNMENT); + release_variables(); + return -1; + } + b = malloc(sizeof(float) * ELEMENT_SIZE); + if(b == NULL) { + printf("OOM(b) %u.\n", sizeof(float) * ELEMENT_SIZE + BUFFER_ALIGNMENT); + release_variables(); + return -1; + } + y = malloc(sizeof(float) * ELEMENT_SIZE); + if(y == NULL) { + printf("OOM(y) %u.\n", sizeof(float) * ELEMENT_SIZE + BUFFER_ALIGNMENT); + release_variables(); + return -1; + } +#ifdef LOAD_STORE_TEST + y1 = malloc(sizeof(float) * ELEMENT_SIZE); + if(y1 == NULL) { + printf("OOM(y1) %u.\n", sizeof(float) * ELEMENT_SIZE + BUFFER_ALIGNMENT); + release_variables(); + return -1; + } + y2 = malloc(sizeof(float) * ELEMENT_SIZE); + if(y2 == NULL) { + printf("OOM(y2) %u.\n", sizeof(float) * ELEMENT_SIZE + BUFFER_ALIGNMENT); + release_variables(); + return -1; + } +#endif + r = malloc(sizeof(float) * ELEMENT_SIZE); + if(r == NULL) { + printf("OOM(r) %u.\n", sizeof(float) * ELEMENT_SIZE + BUFFER_ALIGNMENT); + release_variables(); + return -1; + } +#endif + + memset(a, 0, sizeof(float) * ELEMENT_SIZE); + memset(b, 0, sizeof(float) * ELEMENT_SIZE); + memset(y, 0, sizeof(float) * ELEMENT_SIZE); +#ifdef LOAD_STORE_TEST + memset(y1, 0, sizeof(float) * ELEMENT_SIZE); + memset(y2, 0, sizeof(float) * ELEMENT_SIZE); +#endif + memset(r, 0, sizeof(float) * ELEMENT_SIZE); + +#ifdef LOAD_STORE_TEST + int rst; + + rst = loaf_store_test(); + release_variables(); + return rst; +#endif + + printf("---- FPU Controller Test (Element size = %d) \n", ELEMENT_SIZE); + printf("a: %p, b: %p, y: %p, r: %p\n", a, b, y, r); + +// delay_in_usec(1000000); + + //---- main test + int count = 0; +// for(count = 0;count < 2;count++) + { + for(int fpu = 0;fpu < MAX_FPU;fpu++) + //for(int fpu = 3;fpu >= 0;fpu--) + { + printf("FPU: %s\n", fpu == 0 ? "ADD" : fpu == 1 ? "SUB" : fpu == 2 ? "MUL" : "DIV"); + + // generate kernel data + generate_kernel(fpu); + + // init data + for(int i = 0;i < ELEMENT_SIZE;i++) + { +#if USED_DATA + a[i] = (float)(i + 1 + count); // a sources + b[i] = (float)(i + 2 + count); // b sources +#else + a[i] = (float)rand() / RAND_MAX * 2000.0 - 1000.0; + b[i] = (float)rand() / RAND_MAX * 3000.0 - 2500.0; +#endif + r[i] = fpu == 0 ? a[i] + b[i] : // reference + fpu == 1 ? a[i] - b[i] : + fpu == 2 ? a[i] * b[i] : + a[i] / b[i] ; + } + + // run fpu + for(int core = START_CORE_ID;core < END_CORE_ID;core++) + { + printf("\tCORE: %d\n", core); + + // load kerneal + reg_write(KERNEL_OFFSET, (uint64_t)kbuf); + reg_write(KERNEL_SIZE, (uint64_t)klen); + reg_write(KERNEL_LOAD, (uint64_t)(1 << core)); + wait_done(core); + + // get load cycle + load_cycle[fpu][core] = reg_read(8 * (6 + core)); + + // run fpu + reg_write(RUN_FPU, (uint64_t)core); + wait_done(core); + + // get fpu cycles + fpu_cycle[fpu][core] = reg_read(8 * (6 + core)); + + // check result + for(int i = 0;i < ELEMENT_SIZE;i++) + { + int ok = (y[i] >= r[i] ? y[i] - r[i] : r[i] - y[i]) < 0.0000001; + volatile float *y_ptr; + volatile float y_tmp; + uint32_t *ptmp = (uint32_t *)&y_tmp; + + if(!ok) { + not_cok++; + y_ptr = &y[i]; + y_tmp = *y_ptr; + + printf("y_tmp: %08X\n", *ptmp); + ok = (y_tmp >= r[i] ? y_tmp - r[i] : r[i] - y_tmp) < 0.0000001; + // print_value(ok, i, r[i], y_tmp); + } + + print_value(ok, i, r[i], y[i]); + if(!ok) { + not_ok++; + } + } + delay_in_usec(1000); + } + } + } + + // print result + printf("[ Result ]\n"); + for(int fpu = 0;fpu < MAX_FPU;fpu++) + { + for(int core = 0;core < MAX_CORE;core++) + { + char *s = fpu == 0 ? "ADD" : fpu == 1 ? "SUB" : fpu == 2 ? "MUL" : "DIV"; + int lcyc = load_cycle[fpu][core]; + int fcyc = fpu_cycle[fpu][core]; + int tcyc = lcyc + fcyc; + printf("%s#%d:: load: %d cycles, fpu: %d cycles, total: %d cycles, time: %d us\n", s, core, lcyc, fcyc, tcyc, tcyc / FREQ); + } + } + +#if !_USE_VARIABLES_ + release_variables(); +#endif + printf("---- FPU Controller Test Finished(not_ok: %d, not_cok: %d)\n", not_ok, not_cok); + + return 0; +} + diff --git a/src/testdriver/fpc.h b/src/testdriver/fpc.h new file mode 100644 index 0000000..9666770 --- /dev/null +++ b/src/testdriver/fpc.h @@ -0,0 +1,44 @@ +/* + * fpc header file + */ + +#ifndef FPC_H +#define FPC_H + +// custom macros +#define STR1(x) #x +#ifndef STR +#define STR(x) STR1(x) +#endif + +#define CAT_(A, B) A##B +#define CAT(A, B) CAT_(A, B) + +#define ROCC_INSTRUCTION_R_R_R(x, rd, rs1, rs2, func7) \ + { \ + asm volatile( \ + ".insn r " STR(CAT(CUSTOM_, x)) ", " STR(0x7) ", " STR(func7) ", %0, %1, %2" \ + : "=r"(rd) \ + : "r"(rs1), "r"(rs2)); \ + } + +#define ROCC_INSTRUCTION_0_R_R(x, rs1, rs2, func7) \ + { \ + asm volatile( \ + ".insn r " STR(CAT(CUSTOM_, x)) ", " STR(0x3) ", " STR(func7) ", x0, %0, %1" \ + : \ + : "r"(rs1), "r"(rs2)); \ + } + +// commands +#define FPC_WRITE 0 +#define FPC_READ 1 + +// custom2 +#define FPC_CUSTOM 2 + +// macros +#define FpcWrite(adr, wd) ROCC_INSTRUCTION_0_R_R(FPC_CUSTOM, adr, wd, FPC_WRITE) +#define FpcRead(adr, rd) { uint64_t _placeholder = 0; ROCC_INSTRUCTION_R_R_R(FPC_CUSTOM, rd, adr, _placeholder, FPC_READ) } + +#endif // FPC_H diff --git a/src/testdriver/multi_NPU_RISCV_test.c b/src/testdriver/multi_NPU_RISCV_test.c old mode 100644 new mode 100755 index 7b697d3..fc5dc67 --- a/src/testdriver/multi_NPU_RISCV_test.c +++ b/src/testdriver/multi_NPU_RISCV_test.c @@ -4,18 +4,38 @@ #include #include -//#define DATA_SIZE 2048 // Data Size -#define DATA_SIZE 16 // Data Size -#define TEST_OP_TYPE "vadd.bf16" // Op Type for Test, Use "vadd.bf16", "vsub.bf16", "vmul.bf16", "vdiv.bf16" -#define NUMBER_OF_CORES 4 // Number f Cores used at the same time +#define MAX_DATA_SIZE 512 // Data Size +#define DATA_SIZE 512 // Data Size +#define MAX_NUMBER_OF_CORES 6 // Max. Number of Cores used at the same time +#define NUMBER_OF_CORES 6 // Number of Cores used at the same time -#define SYS_CLK 12500000 +#define LOOP_COUNT 1024 // Number of loop + +#define SYS_CLK 26000000 // RISC-V: 26MHz 26,000,000 #define KERNEL_WITH_LOAD_STORE 0 #define NPU_REG_ID_OFFSET 3 -#define LOAD_STORE_TEST 1 -#define __DEBUG_MODE__ +#define INPUT_A_SRAM_BASE_ADDRESS 0x80 // 128 // 0x40 // 0x200 +#define INPUT_B_SRAM_BASE_ADDRESS (INPUT_A_SRAM_BASE_ADDRESS + MAX_DATA_SIZE * 2) // 0x480 128 + 1024 // 0xC0 // 0x1200 +#define RESULT_SRAM_BASE_ADDRESS (INPUT_A_SRAM_BASE_ADDRESS + MAX_DATA_SIZE * 2 * 2) // 0x880 128 + 1024 * 2 *2 // 0x140 // 0x2200 + +#define NPU_LOAD_STORE_MICRO_DELAY 10000 + +#define NPU_COMPLETE_EXEC_INTERRUPT 1 +#define NPU_COMPLETE_EXEC_REG (NUMBER_OF_CORES * 3 + 1) // 19 +#define NPU_COMPLETE_INTERRUPT_RST (NPU_COMPLETE_EXEC_REG + 1) // 20 +#define NPU_COMPLETE_EXEC_TIMEOUT 10000000.00 +#define EPSILON 0.01 + +#define DDR_M 1 // 128 // DDR_ADDR_MAGNIFICATION +#define SRAM_M 1 // 4 // SRAM_ADDR_MAGNIFICATION +#define SIZE_M 16 // 4 // SIZE_MAGNIFICATION + +#define MAX_LOAD_STORE_CHUNK_SIZE 128 // must be >= 128 + +//#define _NPU_LOAD_STORE_TEST_MODE_ +//#define __DEBUG_MODE__ #ifdef __DEBUG_MODE__ #define trace_pc_position() printf("%s - %d \n", __func__, __LINE__); @@ -23,17 +43,98 @@ #define trace_pc_position() #endif -typedef union { - float f; - uint32_t i; -} FloatUnion; - typedef struct { uint16_t mantissa : 7; uint16_t exponent : 8; uint16_t sign : 1; } BF16; +// 4096 Size Input data +__attribute__ ((aligned (128))) volatile BF16 input_A[DATA_SIZE]; +__attribute__ ((aligned (128))) volatile BF16 input_B[DATA_SIZE]; + +__attribute__ ((aligned (128))) volatile BF16 output_npu_0[DATA_SIZE]; // NPU 0 Output +__attribute__ ((aligned (128))) volatile BF16 output_npu_1[DATA_SIZE]; // NPU 1 Output +__attribute__ ((aligned (128))) volatile BF16 output_npu_2[DATA_SIZE]; // NPU 2 Output +__attribute__ ((aligned (128))) volatile BF16 output_npu_3[DATA_SIZE]; // NPU 3 Output +__attribute__ ((aligned (128))) volatile BF16 output_npu_4[DATA_SIZE]; // NPU 4 Output +__attribute__ ((aligned (128))) volatile BF16 output_npu_5[DATA_SIZE]; // NPU 5 Output + +__attribute__ ((aligned (64))) volatile BF16 output_riscv_add[DATA_SIZE]; +__attribute__ ((aligned (64))) volatile BF16 output_riscv_sub[DATA_SIZE]; +__attribute__ ((aligned (64))) volatile BF16 output_riscv_mul[DATA_SIZE]; +__attribute__ ((aligned (64))) volatile BF16 output_riscv_div[DATA_SIZE]; + + // Kernel: need to align by 8bytes 0 1 2 3 4 5 6 7 8 9 + // 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 + // 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 + // 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 + // 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 + // 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 +#if KERNEL_WITH_LOAD_STORE +// With load/store, vadd.bf16 +__attribute__ ((aligned (128))) volatile uint8_t kernel_0[] = { + 0x00, 0x40, 0x20, 0x03, 0x20, 0x00, 0x10, 0x02, 0x00, 0x04, 0x30, 0x02, 0x00, 0x00, 0x20, 0x04, 0x20, 0x04, 0x10, 0x02, + 0x00, 0x30, 0x12, 0x07, 0x00, 0x00, 0x20, 0x04, 0x20, 0x40, 0x20, 0x03, 0x00, 0x30, 0x12, 0x07, 0x00, 0x04, 0x30, 0x02, + 0x20, 0x04, 0x20, 0x02, 0x20, 0x00, 0x10, 0x02, 0x00, 0x08, 0x40, 0x02, 0x20, 0x08, 0x30, 0x02, 0x40, 0x40, 0x10, 0x03, + 0x00, 0x24, 0x31, 0x09, 0x20, 0x08, 0x20, 0x02, 0x00, 0x00, 0x10, 0x04, 0x00, 0x30, 0x12, 0x08, 0x00, 0x04, 0x30, 0x02, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff}; +__attribute__ ((aligned (128))) volatile uint8_t kernel_1[] = { + 0x00, 0x40, 0x20, 0x03, 0x20, 0x00, 0x10, 0x02, 0x00, 0x04, 0x30, 0x02, 0x00, 0x00, 0x20, 0x04, 0x20, 0x04, 0x10, 0x02, + 0x00, 0x30, 0x12, 0x07, 0x00, 0x00, 0x20, 0x04, 0x20, 0x40, 0x20, 0x03, 0x00, 0x30, 0x12, 0x07, 0x00, 0x04, 0x30, 0x02, + 0x20, 0x04, 0x20, 0x02, 0x20, 0x00, 0x10, 0x02, 0x00, 0x08, 0x40, 0x02, 0x20, 0x08, 0x30, 0x02, 0x40, 0x40, 0x10, 0x03, + 0x00, 0x24, 0x31, 0x09, 0x20, 0x08, 0x20, 0x02, 0x00, 0x00, 0x10, 0x04, 0x00, 0x30, 0x12, 0x08, 0x00, 0x04, 0x30, 0x02, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff}; +__attribute__ ((aligned (128))) volatile uint8_t kernel_2[] = { + 0x00, 0x40, 0x20, 0x03, 0x20, 0x00, 0x10, 0x02, 0x00, 0x04, 0x30, 0x02, 0x00, 0x00, 0x20, 0x04, 0x20, 0x04, 0x10, 0x02, + 0x00, 0x30, 0x12, 0x07, 0x00, 0x00, 0x20, 0x04, 0x20, 0x40, 0x20, 0x03, 0x00, 0x30, 0x12, 0x07, 0x00, 0x04, 0x30, 0x02, + 0x20, 0x04, 0x20, 0x02, 0x20, 0x00, 0x10, 0x02, 0x00, 0x08, 0x40, 0x02, 0x20, 0x08, 0x30, 0x02, 0x40, 0x40, 0x10, 0x03, + 0x00, 0x24, 0x31, 0x09, 0x20, 0x08, 0x20, 0x02, 0x00, 0x00, 0x10, 0x04, 0x00, 0x30, 0x12, 0x08, 0x00, 0x04, 0x30, 0x02, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff}; +__attribute__ ((aligned (128))) volatile uint8_t kernel_3[] = { + 0x00, 0x40, 0x20, 0x03, 0x20, 0x00, 0x10, 0x02, 0x00, 0x04, 0x30, 0x02, 0x00, 0x00, 0x20, 0x04, 0x20, 0x04, 0x10, 0x02, + 0x00, 0x30, 0x12, 0x07, 0x00, 0x00, 0x20, 0x04, 0x20, 0x40, 0x20, 0x03, 0x00, 0x30, 0x12, 0x07, 0x00, 0x04, 0x30, 0x02, + 0x20, 0x04, 0x20, 0x02, 0x20, 0x00, 0x10, 0x02, 0x00, 0x08, 0x40, 0x02, 0x20, 0x08, 0x30, 0x02, 0x40, 0x40, 0x10, 0x03, + 0x00, 0x24, 0x31, 0x09, 0x20, 0x08, 0x20, 0x02, 0x00, 0x00, 0x10, 0x04, 0x00, 0x30, 0x12, 0x08, 0x00, 0x04, 0x30, 0x02, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff}; +__attribute__ ((aligned (128))) volatile uint8_t kernel_4[] = { + 0x00, 0x40, 0x20, 0x03, 0x20, 0x00, 0x10, 0x02, 0x00, 0x04, 0x30, 0x02, 0x00, 0x00, 0x20, 0x04, 0x20, 0x04, 0x10, 0x02, + 0x00, 0x30, 0x12, 0x07, 0x00, 0x00, 0x20, 0x04, 0x20, 0x40, 0x20, 0x03, 0x00, 0x30, 0x12, 0x07, 0x00, 0x04, 0x30, 0x02, + 0x20, 0x04, 0x20, 0x02, 0x20, 0x00, 0x10, 0x02, 0x00, 0x08, 0x40, 0x02, 0x20, 0x08, 0x30, 0x02, 0x40, 0x40, 0x10, 0x03, + 0x00, 0x24, 0x31, 0x09, 0x20, 0x08, 0x20, 0x02, 0x00, 0x00, 0x10, 0x04, 0x00, 0x30, 0x12, 0x08, 0x00, 0x04, 0x30, 0x02, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff}; +__attribute__ ((aligned (128))) volatile uint8_t kernel_5[] = { + 0x00, 0x40, 0x20, 0x03, 0x20, 0x00, 0x10, 0x02, 0x00, 0x04, 0x30, 0x02, 0x00, 0x00, 0x20, 0x04, 0x20, 0x04, 0x10, 0x02, + 0x00, 0x30, 0x12, 0x07, 0x00, 0x00, 0x20, 0x04, 0x20, 0x40, 0x20, 0x03, 0x00, 0x30, 0x12, 0x07, 0x00, 0x04, 0x30, 0x02, + 0x20, 0x04, 0x20, 0x02, 0x20, 0x00, 0x10, 0x02, 0x00, 0x08, 0x40, 0x02, 0x20, 0x08, 0x30, 0x02, 0x40, 0x40, 0x10, 0x03, + 0x00, 0x24, 0x31, 0x09, 0x20, 0x08, 0x20, 0x02, 0x00, 0x00, 0x10, 0x04, 0x00, 0x30, 0x12, 0x08, 0x00, 0x04, 0x30, 0x02, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff}; +#else + // Without load/store, vadd.bf16 +__attribute__ ((aligned (128))) volatile uint8_t kernel_0[] = { + 0x80, 0x04, 0x20, 0x02, 0x80, 0x00, 0x10, 0x02, 0x20, 0x00, 0x40, 0x02, 0x80, 0x08, 0x30, 0x02, 0x00, 0x00, 0x60, 0x02, + 0x00, 0x04, 0x50, 0x02, 0x01, 0x00, 0x60, 0x0d, 0x00, 0x24, 0x31, 0x09, 0x00, 0x00, 0x00, 0xff, 0xf8, 0xff, 0x56, 0x11}; +__attribute__ ((aligned (128))) volatile uint8_t kernel_1[] = { + 0x80, 0x04, 0x20, 0x02, 0x80, 0x00, 0x10, 0x02, 0x20, 0x00, 0x40, 0x02, 0x80, 0x08, 0x30, 0x02, 0x00, 0x00, 0x60, 0x02, + 0x00, 0x04, 0x50, 0x02, 0x01, 0x00, 0x60, 0x0d, 0x00, 0x24, 0x31, 0x09, 0x00, 0x00, 0x00, 0xff, 0xf8, 0xff, 0x56, 0x11}; +__attribute__ ((aligned (128))) volatile uint8_t kernel_2[] = { + 0x80, 0x04, 0x20, 0x02, 0x80, 0x00, 0x10, 0x02, 0x20, 0x00, 0x40, 0x02, 0x80, 0x08, 0x30, 0x02, 0x00, 0x00, 0x60, 0x02, + 0x00, 0x04, 0x50, 0x02, 0x01, 0x00, 0x60, 0x0d, 0x00, 0x24, 0x31, 0x09, 0x00, 0x00, 0x00, 0xff, 0xf8, 0xff, 0x56, 0x11}; +__attribute__ ((aligned (128))) volatile uint8_t kernel_3[] = { + 0x80, 0x04, 0x20, 0x02, 0x80, 0x00, 0x10, 0x02, 0x20, 0x00, 0x40, 0x02, 0x80, 0x08, 0x30, 0x02, 0x00, 0x00, 0x60, 0x02, + 0x00, 0x04, 0x50, 0x02, 0x01, 0x00, 0x60, 0x0d, 0x00, 0x24, 0x31, 0x09, 0x00, 0x00, 0x00, 0xff, 0xf8, 0xff, 0x56, 0x11}; +__attribute__ ((aligned (128))) volatile uint8_t kernel_4[] = { + 0x80, 0x04, 0x20, 0x02, 0x80, 0x00, 0x10, 0x02, 0x20, 0x00, 0x40, 0x02, 0x80, 0x08, 0x30, 0x02, 0x00, 0x00, 0x60, 0x02, + 0x00, 0x04, 0x50, 0x02, 0x01, 0x00, 0x60, 0x0d, 0x00, 0x24, 0x31, 0x09, 0x00, 0x00, 0x00, 0xff, 0xf8, 0xff, 0x56, 0x11}; +__attribute__ ((aligned (128))) volatile uint8_t kernel_5[] = { + 0x80, 0x04, 0x20, 0x02, 0x80, 0x00, 0x10, 0x02, 0x20, 0x00, 0x40, 0x02, 0x80, 0x08, 0x30, 0x02, 0x00, 0x00, 0x60, 0x02, + 0x00, 0x04, 0x50, 0x02, 0x01, 0x00, 0x60, 0x0d, 0x00, 0x24, 0x31, 0x09, 0x00, 0x00, 0x00, 0xff, 0xf8, 0xff, 0x56, 0x11}; +#endif + +uint64_t elapsedCsrrsCycle = 0; +unsigned long g_interrupt_mask = 0xF; +char* TEST_OP_TYPE = "vadd.bf16"; // Op Type for Test, Use "vadd.bf16", "vsub.bf16", "vmul.bf16", "vdiv.bf16" + static inline void npu_regSet(int idx, unsigned long data) { ROCC_INSTRUCTION_SS(3, data, idx, 0); @@ -63,54 +164,93 @@ static inline void npu_store() ROCC_INSTRUCTION(3, 4); } -uint64_t get_time() { +/* riscv issues store command to npu */ +static void load_command_to_npu(int npu, long unsigned int l_addr, long unsigned int r_addr, int size) { + + npu_regSet((npu * NPU_REG_ID_OFFSET + 1), (long unsigned int)((r_addr + DDR_M - 1) / DDR_M)); + npu_regSet((npu * NPU_REG_ID_OFFSET + 2), (int)((size + SIZE_M - 1) / SIZE_M)); + npu_regSet((npu * NPU_REG_ID_OFFSET + 3), (long unsigned int)((l_addr + SRAM_M - 1) / SRAM_M)); +} + +/* riscv issues store command to npu */ +static void store_command_to_npu(int npu, long unsigned int r_addr, long unsigned int l_addr, int size) { + + npu_regSet((npu * NPU_REG_ID_OFFSET + 1), (long unsigned int)((r_addr + DDR_M - 1) / DDR_M)); + npu_regSet((npu * NPU_REG_ID_OFFSET + 2), (int)((size + SIZE_M - 1) / SIZE_M)); + npu_regSet((npu * NPU_REG_ID_OFFSET + 3), (long unsigned int)((l_addr + SRAM_M - 1) / SRAM_M)); +} + +static inline uint64_t get_time() { uint64_t tmp; - asm volatile("csrr %0,time":"=r"(tmp)); + asm volatile("csrrs %0, cycle, x0":"=r"(tmp)); return tmp; } -BF16 float_to_bf16(float value) { +void delay_in_usec(int us) { - FloatUnion fu; - fu.f = value; - BF16 bf16; + uint64_t cycle_start; + uint64_t cycle_end; + int elapsedTime; - // Extract the sign bit. - bf16.sign = (fu.i >> 31) & 0x1; + cycle_start = get_time(); + cycle_end = get_time(); + elapsedTime = (cycle_end - cycle_start) / (SYS_CLK / 1000000); + while(elapsedTime < us) { + cycle_end = get_time(); + elapsedTime = (cycle_end - cycle_start) / (SYS_CLK / 1000000); + } +} - // Extract the biased exponent (8 bits). - int biased_exponent = (fu.i >> 23) & 0xFF; +BF16 swap_bf16_bytes(BF16 value) { - if (biased_exponent == 0) { - bf16.exponent = 0; - bf16.mantissa = (fu.i >> 16) & 0x7F; - } else { - bf16.exponent = biased_exponent - 127; + BF16 swap_value; + char *origin; + char *swap; - bf16.mantissa = (fu.i >> 16) & 0x7F; - } + origin = &value; + swap = &swap_value; + swap[0] = origin[1]; + swap[1] = origin[0]; - return bf16; + return swap_value; +} + +BF16 float_to_bf16(float value) { + + uint32_t f32_value_as_uint32; + uint16_t bf16_value; + + // Assuming little-endian architecture + memcpy(&f32_value_as_uint32, &value, sizeof(float)); + + // Extract the 16 most significant bits + bf16_value = (uint16_t)(f32_value_as_uint32 >> 16); + + BF16 bf16_result; + memcpy(&bf16_result, &bf16_value, sizeof(BF16)); + + // return swap_bf16_bytes(bf16_result); + return bf16_result; } float bf16_to_float(BF16 bf16) { - FloatUnion fu; - int biased_exponent; + BF16 swap_bf16; + uint32_t f32_value_as_uint32; + uint16_t zero_padding = 0; - fu.i = (bf16.sign << 31); + swap_bf16 = swap_bf16_bytes(bf16); + swap_bf16 = bf16; - if (bf16.exponent == 0) { - fu.i |= ((bf16.mantissa & 0x7F) << 16); - } else { - biased_exponent = bf16.exponent + 127; - fu.i |= (biased_exponent << 23); + // Assuming little-endian architecture + memcpy(&f32_value_as_uint32, &zero_padding, sizeof(uint16_t)); + memcpy(((uint8_t*)&f32_value_as_uint32) + sizeof(uint16_t), &swap_bf16, sizeof(uint16_t)); - fu.i |= ((bf16.mantissa & 0x7F) << 16); - } + float f32_result; + memcpy(&f32_result, &f32_value_as_uint32, sizeof(float)); - return fu.f; + return f32_result; } BF16 bf16_add(BF16 a, BF16 b) { @@ -150,10 +290,27 @@ static void resize_converted_data_size_kernel(uint8_t* kernel, int size) { #if KERNEL_WITH_LOAD_STORE // with-load-store kernel[8] = byte_low; kernel[9] = byte_high; - kernel[24] = byte_low; - kernel[25] = byte_high; - kernel[60] = byte_low; - kernel[61] = byte_high; + kernel[36] = byte_low; + kernel[37] = byte_high; + kernel[76] = byte_low; + kernel[77] = byte_high; +#endif +} + +static void kernel_loop_count_change(uint8_t* kernel, int count) { + + // Seperate Size Bytes Low and High + uint8_t byte_low; + uint8_t byte_high; + + // Devide Size + byte_low = count & 0xff; + byte_high = count >> 8; + +#if KERNEL_WITH_LOAD_STORE // with-load-store +#else + kernel[20] = byte_low; + kernel[21] = byte_high; #endif } @@ -168,11 +325,11 @@ static void resize_op_iteration_kernel(uint8_t* kernel, int size) { byte_high = size >> 8; #if KERNEL_WITH_LOAD_STORE // with-load-store - kernel[44] = byte_low; - kernel[45] = byte_high; + kernel[48] = byte_low; + kernel[49] = byte_high; #else - kernel[12] = byte_low; - kernel[13] = byte_high; + kernel[8] = byte_low; + kernel[9] = byte_high; #endif } @@ -191,9 +348,138 @@ static void kernel_op_change(uint8_t* kernel, char* op) { printf("Wrong OP!!!\n"); } #if KERNEL_WITH_LOAD_STORE // with-load-store - kernel[51] = opcode; + kernel[63] = opcode; +#else + kernel[31] = opcode; +#endif +} + +static void kernel_input_a_sram_addr_change(uint8_t* kernel, unsigned long addr) { + + uint8_t byte_low; + uint8_t byte_high; + + addr = (addr + 3)/4; + + // Devide Size + byte_low = addr & 0xff; + byte_high = (addr >> 8) & 0xFF; + +#if KERNEL_WITH_LOAD_STORE // with-load-store +#else + kernel[4] = byte_low; + kernel[5] = byte_high; +#endif +} + +static void kernel_input_b_sram_addr_change(uint8_t* kernel, unsigned long addr) { + + uint8_t byte_low; + uint8_t byte_high; + + addr = (addr + 3)/4; + + // Devide Size + byte_low = addr & 0xff; + byte_high = (addr >> 8) & 0xFF; + +#if KERNEL_WITH_LOAD_STORE // with-load-store #else - kernel[19] = opcode; + kernel[0] = byte_low; + kernel[1] = byte_high; +#endif +} + +static void kernel_output_c_sram_addr_change(uint8_t* kernel, unsigned long addr) { + + uint8_t byte_low; + uint8_t byte_high; + + addr = (addr + 3)/4; + + // Devide Size + byte_low = addr & 0xff; + byte_high = (addr >> 8) & 0xFF; + +#if KERNEL_WITH_LOAD_STORE // with-load-store +#else + kernel[12] = byte_low; + kernel[13] = byte_high; +#endif +} + +static void kernel_input_a_addr_change(uint8_t* kernel, BF16* data) { + + unsigned long addr; + uint8_t byte_low; + uint8_t byte_high; + + addr = (unsigned long)data; + addr = (addr + 127)/128; + + // Devide Size + byte_low = addr & 0xff; + byte_high = (addr >> 8) & 0xFF; + +#if KERNEL_WITH_LOAD_STORE // with-load-store + kernel[0] = byte_low; + kernel[1] = byte_high; + + byte_low = (addr >> 16) & 0xff; + byte_high = (addr >> 24) & 0xFF; + + kernel[12] = byte_low; + kernel[13] = byte_high; +#endif +} + +static void kernel_input_b_addr_change(uint8_t* kernel, BF16* data) { + + unsigned long addr; + uint8_t byte_low; + uint8_t byte_high; + + addr = (unsigned long)data; + addr = (addr + 127)/128; + + // Devide Size + byte_low = addr & 0xff; + byte_high = (addr >> 8) & 0xFF; + +#if KERNEL_WITH_LOAD_STORE // with-load-store + kernel[28] = byte_low; + kernel[29] = byte_high; + + byte_low = (addr >> 16) & 0xff; + byte_high = (addr >> 24) & 0xFF; + + kernel[24] = byte_low; + kernel[25] = byte_high; +#endif +} + +static void kernel_input_c_addr_change(uint8_t* kernel, BF16* data) { + + unsigned long addr; + uint8_t byte_low; + uint8_t byte_high; + + addr = (unsigned long)data; + addr = (addr + 127)/128; + + // Devide Size + byte_low = addr & 0xff; + byte_high = (addr >> 8) & 0xFF; + +#if KERNEL_WITH_LOAD_STORE // with-load-store + kernel[56] = byte_low; + kernel[57] = byte_high; + + byte_low = (addr >> 16) & 0xff; + byte_high = (addr >> 24) & 0xFF; + + kernel[68] = byte_low; + kernel[69] = byte_high; #endif } @@ -225,6 +511,36 @@ static void floatToString(float floatValue, char* strValue, int maxLength) { snprintf(strValue, maxLength, "%d.%03d", intPart, decimalPart); } +// void print_compared_bf16_recored(int s, int id, int total, char *op, char * a_data, char * b_data, char * r_data, char * n_data) { +static inline void print_compared_bf16_recored(int s, int id, int total, char *op, BF16 a, BF16 b, BF16 r, BF16 n) { + float f_a; + float f_b; + float f_r; + float f_n; + uint32_t i_a; + uint32_t i_b; + uint32_t i_r; + uint32_t i_n; + + f_a = bf16_to_float(a); + f_b = bf16_to_float(b); + f_r = bf16_to_float(r); + f_n = bf16_to_float(n); + + memcpy(&i_a, &f_a, sizeof(float)); + memcpy(&i_b, &f_b, sizeof(float)); + memcpy(&i_r, &f_r, sizeof(float)); + memcpy(&i_n, &f_n, sizeof(float)); + + if(s) { + printf("%s[%d/%d] - SUCCESS, A 0x%08x B 0x%08x = RISC-V 0x%08x - NPU 0x%08x\n", + op, id, total, i_a, i_b, i_r, i_n); + } else { + printf("%s[%d/%d] - FAIL, A 0x%08x B 0x%08x = RISC-V 0x%08x - NPU 0x%08x\n", + op, id, total, i_a, i_b, i_r, i_n); + } +} + static int compare_riscv_and_npu(int npu, char *op, BF16* out_risc_bf16, BF16* out_npu_bf16, int count) { // Check How Many Are Correct @@ -232,342 +548,943 @@ static int compare_riscv_and_npu(int npu, char *op, BF16* out_risc_bf16, BF16* o int error_cnt = 0; float out_risc_flt; float out_npu_flt; - char riscvStrValue[50]; - char npuStrValue[50]; - char diffStrValue[50]; + float diff; + for (int i = 0; i < count; i++) { out_risc_flt = bf16_to_float(out_risc_bf16[i]); out_npu_flt = bf16_to_float(out_npu_bf16[i]); - if (out_risc_flt != out_npu_flt) { + if(out_risc_flt > out_npu_flt) { + diff = out_risc_flt - out_npu_flt; + } else { + diff = out_npu_flt - out_risc_flt; + } + if (diff > EPSILON) { error_cnt += 1; -#ifdef __DEBUG_MODE__ - memset(riscvStrValue, 0, 50); - memset(npuStrValue, 0, 50); - memset(diffStrValue, 0, 50); - floatToString(out_risc_flt, riscvStrValue, sizeof(riscvStrValue)); - floatToString(out_npu_flt, npuStrValue, sizeof(npuStrValue)); - floatToString(out_risc_flt - out_npu_flt, diffStrValue, sizeof(diffStrValue)); - printf("[Test Case %d] %s - FAIL\nRISCV data[%d]: %s\nNPU%d data[%d]: %s\nRISCV data[%d] - NPU data[%d] = %s\n", - count, op, i, riscvStrValue, npu, i, npuStrValue, i, i, diffStrValue); +#if 1 // def __DEBUG_MODE__ + print_compared_bf16_recored( 0, i, count, op, input_A[i], input_B[i], out_risc_bf16[i], out_npu_bf16[i]); +#else + char * r_data; + char * n_data; + char * a_data; + char * b_data; + a_data = (char *)&input_A[i]; + b_data = (char *)&input_B[i]; + r_data = (char *)&out_risc_bf16[i]; + n_data = (char *)&out_npu_bf16[i]; + printf("%s[%d/%d] - FAIL, A 0x%02x%02x B 0x%02x%02x = RISC-V 0x%02x%02x - NPU 0x%02x%02x\n", + op, i, count, a_data[1], a_data[0], b_data[1], b_data[0], r_data[1], r_data[0], n_data[1], n_data[0]); #endif } else { check += 1; +#if 1 + print_compared_bf16_recored( 1, i, count, op, input_A[i], input_B[i], out_risc_bf16[i], out_npu_bf16[i]); +#else //def __DEBUG_MODE__ + char * r_data; + char * n_data; + char * a_data; + char * b_data; + a_data = (char *)&input_A[i]; + b_data = (char *)&input_B[i]; + r_data = (char *)&out_risc_bf16[i]; + n_data = (char *)&out_npu_bf16[i]; + printf("%s[%d/%d] - SUCCESS, A 0x%02x%02x B 0x%02x%02x = RISC-V 0x%02x%02x - NPU 0x%02x%02x\n", + op, i, count, a_data[1], a_data[0], b_data[1], b_data[0], r_data[1], r_data[0], n_data[1], n_data[0]); +#endif } } - printf("\n[Test Case %s, NPU%d] FAIL(Equal: %d, Not equal: %d)\n", op, npu, check, error_cnt); + printf("[Test Case %s, NPU%d] Result(matched: %d, unmatched: %d)\n", op, npu, check, error_cnt); return check; } -/* riscv issues store command to npu */ -static void store_command_to_npu(int npu, long unsigned int l_addr, long unsigned int r_addr, int size) { - - trace_pc_position() - npu_regSet((npu * NPU_REG_ID_OFFSET + 1), (long unsigned int)r_addr); - trace_pc_position() - npu_regSet((npu * NPU_REG_ID_OFFSET + 2), size); - trace_pc_position() - npu_regSet((npu * NPU_REG_ID_OFFSET + 3), (long unsigned int)l_addr); - trace_pc_position() - npu_load(); - trace_pc_position() +void init_variavles() { -} - -/* riscv issues store command to npu */ -static void load_command_to_npu(int npu, long unsigned int r_addr, long unsigned int l_addr, int size) { - - npu_regSet((npu * NPU_REG_ID_OFFSET + 1), (long unsigned int)r_addr); - npu_regSet((npu * NPU_REG_ID_OFFSET + 2), size); - npu_regSet((npu * NPU_REG_ID_OFFSET + 3), (long unsigned int)l_addr); - npu_store(); -} - -int main() { - // 4096 Size Input data - BF16 input_A[DATA_SIZE]; - BF16 input_B[DATA_SIZE]; - float f_val_A; float f_val_B; - BF16 output_riscv_add[DATA_SIZE]; - BF16 output_riscv_sub[DATA_SIZE]; - BF16 output_riscv_mul[DATA_SIZE]; - BF16 output_riscv_div[DATA_SIZE]; - - BF16 output_npu_0[DATA_SIZE]; // NPU 0 Output - BF16 output_npu_1[DATA_SIZE]; // NPU 1 Output - BF16 output_npu_2[DATA_SIZE]; // NPU 2 Output - BF16 output_npu_3[DATA_SIZE]; // NPU 3 Output - - printf("\n========Init========\n\n"); - printf("Multi NAU Test\n"); - printf("[%s Test] Using %d Cores\n", TEST_OP_TYPE, NUMBER_OF_CORES); - if(KERNEL_WITH_LOAD_STORE == 0) { - printf(" Kernel without load/store functions\n\n"); - } else { - printf(" Kernel with load/store functions\n\n"); - } - memset(output_riscv_add, 0, sizeof(output_riscv_add)); memset(output_riscv_sub, 0, sizeof(output_riscv_sub)); memset(output_riscv_mul, 0, sizeof(output_riscv_mul)); memset(output_riscv_div, 0, sizeof(output_riscv_div)); - trace_pc_position() memset(output_npu_0, 0, sizeof(output_npu_0)); memset(output_npu_1, 0, sizeof(output_npu_1)); memset(output_npu_2, 0, sizeof(output_npu_2)); memset(output_npu_3, 0, sizeof(output_npu_3)); + memset(output_npu_4, 0, sizeof(output_npu_4)); + memset(output_npu_5, 0, sizeof(output_npu_5)); - trace_pc_position() // Random Data Input for (int temp_count = 0; temp_count < DATA_SIZE; temp_count++) { + +#if 0 +//host.data[0x200000] = jnp.array([(v + 1) * 1.1 for v in range(2048)], dtype=jnp.bfloat16).tobytes() +//host.data[0x201000] = jnp.array([(v + 1) * 0.1 for v in range(2048)], dtype=jnp.bfloat16).tobytes() + uint16_t a_val; + uint16_t b_val; + + a_val = (temp_count + 1) * 1.1; + b_val = (temp_count + 1) * 0.1; + + memcpy(&input_A[temp_count], &a_val, sizeof(uint16_t)); + memcpy(&input_B[temp_count], &b_val, sizeof(uint16_t)); +#else + // f_val_A = (temp_count + 1) * 1.1; // (float)rand() / RAND_MAX * 2000.0 - 1000.0; + // f_val_B = (temp_count + 1) * 0.1; //(float)rand() / RAND_MAX * 2000.0 - 1000.0; f_val_A = (float)rand() / RAND_MAX * 2000.0 - 1000.0; f_val_B = (float)rand() / RAND_MAX * 2000.0 - 1000.0; + // f_val_A = (float)rand() / (float)(RAND_MAX ) * 5.0f; + // f_val_B = (float)rand() / (float)(RAND_MAX ) * 5.0f; input_A[temp_count] = float_to_bf16(f_val_A); input_B[temp_count] = float_to_bf16(f_val_B); +#endif } +} - printf("\ninput_A & input_B are filled with random data.\n"); +void riscv_calculate_result() { - trace_pc_position() + uint64_t cycle_start; + uint64_t cycle_end; + char elapsedTimeStrValue[50]; + + cycle_start = get_time(); riscv_calculate(output_riscv_add, input_A, input_B, "vadd.bf16", DATA_SIZE); + cycle_end = get_time(); + memset(elapsedTimeStrValue, 0, 50); + floatToString((cycle_end - cycle_start) / (SYS_CLK / 1000000), + elapsedTimeStrValue, sizeof(elapsedTimeStrValue)); + printf("Time spent by RISC-V calculating vadd.bf16: %s us.\n", elapsedTimeStrValue); + + cycle_start = get_time(); riscv_calculate(output_riscv_sub, input_A, input_B, "vsub.bf16", DATA_SIZE); - riscv_calculate(output_riscv_mul, input_A, input_B, "vmul.bf16", DATA_SIZE); - riscv_calculate(output_riscv_div, input_A, input_B, "vdiv.bf16", DATA_SIZE); + cycle_end = get_time(); + memset(elapsedTimeStrValue, 0, 50); + floatToString((cycle_end - cycle_start) / (SYS_CLK / 1000000), + elapsedTimeStrValue, sizeof(elapsedTimeStrValue)); + printf("Time spent by RISC-V calculating vsub.bf16: %s us.\n", elapsedTimeStrValue); - printf("\nThe result values of risc-v for each function were calculated using input_A & input_B.\n"); + cycle_start = get_time(); + riscv_calculate(output_riscv_mul, input_A, input_B, "vmul.bf16", DATA_SIZE); + cycle_end = get_time(); + memset(elapsedTimeStrValue, 0, 50); + floatToString((cycle_end - cycle_start) / (SYS_CLK / 1000000), + elapsedTimeStrValue, sizeof(elapsedTimeStrValue)); + printf("Time spent by RISC-V calculating vmul.bf16: %s us.\n", elapsedTimeStrValue); - trace_pc_position() -#if 1 // *** FAILED *** (tohost = 7) - // Memory Input - memcpy((BF16*)0x200000, input_A, sizeof(BF16) * DATA_SIZE); - memcpy((BF16*)0x201000, input_B, sizeof(BF16) * DATA_SIZE); -#endif + cycle_start = get_time(); + riscv_calculate(output_riscv_div, input_A, input_B, "vdiv.bf16", DATA_SIZE); + cycle_end = get_time(); + memset(elapsedTimeStrValue, 0, 50); + floatToString((cycle_end - cycle_start) / (SYS_CLK / 1000000), + elapsedTimeStrValue, sizeof(elapsedTimeStrValue)); + printf("Time spent by RISC-V calculating vdiv.bf16: %s us.\n", elapsedTimeStrValue); +} - trace_pc_position() - // Kernel: need to align by 8bytes 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 - // Kernel: need to align by 8bytes 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 - // 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 - // 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 - // 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 - // 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 -#if KERNEL_WITH_LOAD_STORE - // With load/store, vadd.bf16 - __attribute__ ((aligned (8))) volatile uint8_t kernel_0[] = {0x20, 0x00, 0x10, 0x02, 0x00, 0x40, 0x20, 0x02, 0x00, 0x04, 0x30, 0x02, 0x00, 0x30, 0x12, 0x07, 0x20, 0x04, 0x10, 0x02, - 0x20, 0x40, 0x20, 0x02, 0x00, 0x04, 0x30, 0x02, 0x00, 0x30, 0x12, 0x07, 0x20, 0x00, 0x10, 0x02, 0x20, 0x04, 0x20, 0x02, - 0x20, 0x08, 0x30, 0x02, 0x00, 0x08, 0x40, 0x02, 0x00, 0x24, 0x31, 0x09, 0x40, 0x40, 0x10, 0x02, 0x20, 0x08, 0x20, 0x02, - 0x00, 0x04, 0x30, 0x02, 0x00, 0x30, 0x12, 0x08, 0x00, 0x00, 0x00, 0xff}; - __attribute__ ((aligned (8))) volatile uint8_t kernel_1[] = {0x20, 0x00, 0x10, 0x02, 0x00, 0x40, 0x20, 0x02, 0x00, 0x04, 0x30, 0x02, 0x00, 0x30, 0x12, 0x07, 0x20, 0x04, 0x10, 0x02, - 0x20, 0x40, 0x20, 0x02, 0x00, 0x04, 0x30, 0x02, 0x00, 0x30, 0x12, 0x07, 0x20, 0x00, 0x10, 0x02, 0x20, 0x04, 0x20, 0x02, - 0x20, 0x08, 0x30, 0x02, 0x00, 0x08, 0x40, 0x02, 0x00, 0x24, 0x31, 0x09, 0x60, 0x40, 0x10, 0x02, 0x20, 0x08, 0x20, 0x02, - 0x00, 0x04, 0x30, 0x02, 0x00, 0x30, 0x12, 0x08, 0x00, 0x00, 0x00, 0xff}; - __attribute__ ((aligned (8))) volatile uint8_t kernel_2[] = {0x20, 0x00, 0x10, 0x02, 0x00, 0x40, 0x20, 0x02, 0x00, 0x04, 0x30, 0x02, 0x00, 0x30, 0x12, 0x07, 0x20, 0x04, 0x10, 0x02, - 0x20, 0x40, 0x20, 0x02, 0x00, 0x04, 0x30, 0x02, 0x00, 0x30, 0x12, 0x07, 0x20, 0x00, 0x10, 0x02, 0x20, 0x04, 0x20, 0x02, - 0x20, 0x08, 0x30, 0x02, 0x00, 0x08, 0x40, 0x02, 0x00, 0x24, 0x31, 0x09, 0x80, 0x40, 0x10, 0x02, 0x20, 0x08, 0x20, 0x02, - 0x00, 0x04, 0x30, 0x02, 0x00, 0x30, 0x12, 0x08, 0x00, 0x00, 0x00, 0xff}; - __attribute__ ((aligned (8))) volatile uint8_t kernel_3[] = {0x20, 0x00, 0x10, 0x02, 0x00, 0x40, 0x20, 0x02, 0x00, 0x04, 0x30, 0x02, 0x00, 0x30, 0x12, 0x07, 0x20, 0x04, 0x10, 0x02, - 0x20, 0x40, 0x20, 0x02, 0x00, 0x04, 0x30, 0x02, 0x00, 0x30, 0x12, 0x07, 0x20, 0x00, 0x10, 0x02, 0x20, 0x04, 0x20, 0x02, - 0x20, 0x08, 0x30, 0x02, 0x00, 0x08, 0x40, 0x02, 0x00, 0x24, 0x31, 0x09, 0xa0, 0x40, 0x10, 0x02, 0x20, 0x08, 0x20, 0x02, - 0x00, 0x04, 0x30, 0x02, 0x00, 0x30, 0x12, 0x08, 0x00, 0x00, 0x00, 0xff}; -#else - // Without load/store, vadd.bf16 - __attribute__ ((aligned (8))) volatile uint8_t kernel_0[] = {0x20, 0x00, 0x10, 0x02, 0x20, 0x04, 0x20, 0x02, 0x20, 0x08, 0x30, 0x02, 0x00, 0x08, 0x40, 0x02, 0x00, 0x24, 0x31, 0x09, - 0x00, 0x00, 0x00, 0xff}; - __attribute__ ((aligned (8))) volatile uint8_t kernel_1[] = {0x20, 0x00, 0x10, 0x02, 0x20, 0x04, 0x20, 0x02, 0x20, 0x08, 0x30, 0x02, 0x00, 0x08, 0x40, 0x02, 0x00, 0x24, 0x31, 0x09, - 0x00, 0x00, 0x00, 0xff}; - __attribute__ ((aligned (8))) volatile uint8_t kernel_2[] = {0x20, 0x00, 0x10, 0x02, 0x20, 0x04, 0x20, 0x02, 0x20, 0x08, 0x30, 0x02, 0x00, 0x08, 0x40, 0x02, 0x00, 0x24, 0x31, 0x09, - 0x00, 0x00, 0x00, 0xff}; - __attribute__ ((aligned (8))) volatile uint8_t kernel_3[] = {0x20, 0x00, 0x10, 0x02, 0x20, 0x04, 0x20, 0x02, 0x20, 0x08, 0x30, 0x02, 0x00, 0x08, 0x40, 0x02, 0x00, 0x24, 0x31, 0x09, - 0x00, 0x00, 0x00, 0xff}; -#endif +void adjust_kernel() { - trace_pc_position() resize_op_iteration_kernel(kernel_0, DATA_SIZE); resize_op_iteration_kernel(kernel_1, DATA_SIZE); resize_op_iteration_kernel(kernel_2, DATA_SIZE); resize_op_iteration_kernel(kernel_3, DATA_SIZE); - - trace_pc_position() + resize_op_iteration_kernel(kernel_4, DATA_SIZE); + resize_op_iteration_kernel(kernel_5, DATA_SIZE); + + kernel_loop_count_change(kernel_0, LOOP_COUNT); + kernel_loop_count_change(kernel_1, LOOP_COUNT); + kernel_loop_count_change(kernel_2, LOOP_COUNT); + kernel_loop_count_change(kernel_3, LOOP_COUNT); + kernel_loop_count_change(kernel_4, LOOP_COUNT); + kernel_loop_count_change(kernel_5, LOOP_COUNT); + // Change Kernel's Opcode kernel_op_change(kernel_0, TEST_OP_TYPE); kernel_op_change(kernel_1, TEST_OP_TYPE); kernel_op_change(kernel_2, TEST_OP_TYPE); kernel_op_change(kernel_3, TEST_OP_TYPE); + kernel_op_change(kernel_4, TEST_OP_TYPE); + kernel_op_change(kernel_5, TEST_OP_TYPE); + + // kernel_input_a_sram_addr_change(kernel_0, (unsigned long )(INPUT_A_SRAM_BASE_ADDRESS / 1)); + // kernel_input_a_sram_addr_change(kernel_1, (unsigned long )(INPUT_A_SRAM_BASE_ADDRESS / 1)); + // kernel_input_a_sram_addr_change(kernel_2, (unsigned long )(INPUT_A_SRAM_BASE_ADDRESS / 1)); + // kernel_input_a_sram_addr_change(kernel_3, (unsigned long )(INPUT_A_SRAM_BASE_ADDRESS / 1)); + // kernel_input_a_sram_addr_change(kernel_4, (unsigned long )(INPUT_A_SRAM_BASE_ADDRESS / 1)); + // kernel_input_a_sram_addr_change(kernel_5, (unsigned long )(INPUT_A_SRAM_BASE_ADDRESS / 1)); + + // kernel_input_b_sram_addr_change(kernel_0, (unsigned long )(INPUT_B_SRAM_BASE_ADDRESS / 1)); + // kernel_input_b_sram_addr_change(kernel_1, (unsigned long )(INPUT_B_SRAM_BASE_ADDRESS / 1)); + // kernel_input_b_sram_addr_change(kernel_2, (unsigned long )(INPUT_B_SRAM_BASE_ADDRESS / 1)); + // kernel_input_b_sram_addr_change(kernel_3, (unsigned long )(INPUT_B_SRAM_BASE_ADDRESS / 1)); + // kernel_input_b_sram_addr_change(kernel_4, (unsigned long )(INPUT_B_SRAM_BASE_ADDRESS / 1)); + // kernel_input_b_sram_addr_change(kernel_5, (unsigned long )(INPUT_B_SRAM_BASE_ADDRESS / 1)); + + // kernel_output_c_sram_addr_change(kernel_0, (unsigned long )(RESULT_SRAM_BASE_ADDRESS / 1)); + // kernel_output_c_sram_addr_change(kernel_1, (unsigned long )(RESULT_SRAM_BASE_ADDRESS / 1)); + // kernel_output_c_sram_addr_change(kernel_2, (unsigned long )(RESULT_SRAM_BASE_ADDRESS / 1)); + // kernel_output_c_sram_addr_change(kernel_3, (unsigned long )(RESULT_SRAM_BASE_ADDRESS / 1)); + // kernel_output_c_sram_addr_change(kernel_4, (unsigned long )(RESULT_SRAM_BASE_ADDRESS / 1)); + // kernel_output_c_sram_addr_change(kernel_5, (unsigned long )(RESULT_SRAM_BASE_ADDRESS / 1)); #if KERNEL_WITH_LOAD_STORE // with-load-store + // Change Kernel's input_A address + kernel_input_a_addr_change(kernel_0, input_A); + kernel_input_a_addr_change(kernel_1, input_A); + kernel_input_a_addr_change(kernel_2, input_A); + kernel_input_a_addr_change(kernel_3, input_A); + kernel_input_a_addr_change(kernel_4, input_A); + kernel_input_a_addr_change(kernel_5, input_A); + + // Change Kernel's input_A address + kernel_input_b_addr_change(kernel_0, input_B); + kernel_input_b_addr_change(kernel_1, input_B); + kernel_input_b_addr_change(kernel_2, input_B); + kernel_input_b_addr_change(kernel_3, input_B); + kernel_input_b_addr_change(kernel_4, input_B); + kernel_input_b_addr_change(kernel_5, input_B); + + // Change Kernel's input_A address + kernel_input_c_addr_change(kernel_0, output_npu_0); + kernel_input_c_addr_change(kernel_1, output_npu_1); + kernel_input_c_addr_change(kernel_2, output_npu_2); + kernel_input_c_addr_change(kernel_3, output_npu_3); + kernel_input_c_addr_change(kernel_4, output_npu_3); + kernel_input_c_addr_change(kernel_5, output_npu_3); + resize_converted_data_size_kernel(kernel_0, (int)((DATA_SIZE * 2 + 3)/4)); resize_converted_data_size_kernel(kernel_1, (int)((DATA_SIZE * 2 + 3)/4)); resize_converted_data_size_kernel(kernel_2, (int)((DATA_SIZE * 2 + 3)/4)); resize_converted_data_size_kernel(kernel_3, (int)((DATA_SIZE * 2 + 3)/4)); #endif +} - printf("\nKernel images for each NPU have been prepared.\n\n"); +void dump_data(char * data, int size) { + for(int id = 0; id < size; id++) { + printf("0x%02x, ", data[id] & 0xFF); + if(((id+1) % 16) == 0) { + printf("\n"); + } + } + printf("\n\n"); +} - trace_pc_position() - // Load kernel code at address 0 of npu - store_command_to_npu(0, (long unsigned int)0x00, (long unsigned int)kernel_0, (int)sizeof(kernel_0)); - store_command_to_npu(1, (long unsigned int)0x00, (long unsigned int)kernel_1, (int)sizeof(kernel_1)); - store_command_to_npu(2, (long unsigned int)0x00, (long unsigned int)kernel_2, (int)sizeof(kernel_2)); - store_command_to_npu(3, (long unsigned int)0x00, (long unsigned int)kernel_3, (int)sizeof(kernel_3)); +void load_kernel_into_npu(int npus) { + + long unsigned int sram_a; + long unsigned int ddr_a; + int remaining; + int loadSize; + int size; + + size = (int)sizeof(kernel_0); + for (int len = 0; len < size; len += MAX_LOAD_STORE_CHUNK_SIZE) { + remaining = size - len; + loadSize = remaining < MAX_LOAD_STORE_CHUNK_SIZE ? remaining : MAX_LOAD_STORE_CHUNK_SIZE; + sram_a = 0x00 + len; + + if(npus & 0x1) { + ddr_a = (long unsigned int)kernel_0 + len; + load_command_to_npu(0, sram_a, ddr_a, loadSize); + } + if(npus & 0x2) { + ddr_a = (long unsigned int)kernel_1 + len; + load_command_to_npu(1, sram_a, ddr_a, loadSize); + } + if(npus & 0x4) { + ddr_a = (long unsigned int)kernel_2 + len; + load_command_to_npu(2, sram_a, ddr_a, loadSize); + } + if(npus & 0x8) { + ddr_a = (long unsigned int)kernel_3 + len; + load_command_to_npu(3, sram_a, ddr_a, loadSize); + } + if(npus & 0x10) { + ddr_a = (long unsigned int)kernel_4 + len; + load_command_to_npu(4, sram_a, ddr_a, loadSize); + } + if(npus & 0x20) { + ddr_a = (long unsigned int)kernel_5 + len; + load_command_to_npu(5, sram_a, ddr_a, loadSize); + } + + npu_load(); + + //printf("%s - Offset: 0x%x, loadSize: %d\n", __func__, len, loadSize); + delay_in_usec(NPU_LOAD_STORE_MICRO_DELAY); + } +} + +void load_input_A_into_npu(int npus) { + + long unsigned int sram_a; + long unsigned int ddr_a; + int remaining; + int loadSize; + int size; + + size = (int)(sizeof(BF16) * DATA_SIZE); + + for (int len = 0; len < size; len += MAX_LOAD_STORE_CHUNK_SIZE) { + remaining = size - len; + loadSize = remaining < MAX_LOAD_STORE_CHUNK_SIZE ? remaining : MAX_LOAD_STORE_CHUNK_SIZE; + sram_a = INPUT_A_SRAM_BASE_ADDRESS + len; + + ddr_a = (long unsigned int)input_A + len; + if(npus & 0x1) { + load_command_to_npu(0, sram_a, ddr_a, loadSize); + } + if(npus & 0x2) { + load_command_to_npu(1, sram_a, ddr_a, loadSize); + } + if(npus & 0x4) { + load_command_to_npu(2, sram_a, ddr_a, loadSize); + } + if(npus & 0x8) { + load_command_to_npu(3, sram_a, ddr_a, loadSize); + } + if(npus & 0x10) { + load_command_to_npu(4, sram_a, ddr_a, loadSize); + } + if(npus & 0x20) { + load_command_to_npu(5, sram_a, ddr_a, loadSize); + } + + npu_load(); + + //printf("%s - Offset: 0x%x, loadSize: %d\n", __func__, len, loadSize); + delay_in_usec(NPU_LOAD_STORE_MICRO_DELAY); + } +} + +void load_input_B_into_npu(int npus) { + + long unsigned int sram_a; + long unsigned int ddr_a; + int remaining; + int loadSize; + int size; + + size = (int)(sizeof(BF16) * DATA_SIZE); + + for (int len = 0; len < size; len += MAX_LOAD_STORE_CHUNK_SIZE) { + remaining = size - len; + loadSize = remaining < MAX_LOAD_STORE_CHUNK_SIZE ? remaining : MAX_LOAD_STORE_CHUNK_SIZE; + sram_a = INPUT_B_SRAM_BASE_ADDRESS + len; + + ddr_a = (long unsigned int)input_B + len; + if(npus & 0x1) { + load_command_to_npu(0, sram_a, ddr_a, loadSize); + } + if(npus & 0x2) { + load_command_to_npu(1, sram_a, ddr_a, loadSize); + } + if(npus & 0x4) { + load_command_to_npu(2, sram_a, ddr_a, loadSize); + } + if(npus & 0x8) { + load_command_to_npu(3, sram_a, ddr_a, loadSize); + } + if(npus & 0x10) { + load_command_to_npu(4, sram_a, ddr_a, loadSize); + } + if(npus & 0x20) { + load_command_to_npu(5, sram_a, ddr_a, loadSize); + } + + npu_load(); + + //printf("%s - Offset: 0x%x, loadSize: %d\n", __func__, len, loadSize); + delay_in_usec(NPU_LOAD_STORE_MICRO_DELAY); + } +} + +void load_kernel_data_into_npu() { + + int npus; + + if(NUMBER_OF_CORES == 5) { + npus = 0x1F; + } else if(NUMBER_OF_CORES == 6) { + npus = 0x3F; + } else { + npus = 0x0F; + } + + // Load kernel code at address 0 of npu + load_kernel_into_npu(npus); printf("Kernel images are stored in each NPU.\n\n"); - trace_pc_position() #if !KERNEL_WITH_LOAD_STORE // without-load-store - // Load input_A at address 0x80 of npu - store_command_to_npu(0, (long unsigned int)0x80, (long unsigned int)0x200000, (int)(sizeof(BF16) * DATA_SIZE)); - store_command_to_npu(1, (long unsigned int)0x80, (long unsigned int)0x200000, (int)(sizeof(BF16) * DATA_SIZE)); - store_command_to_npu(2, (long unsigned int)0x80, (long unsigned int)0x200000, (int)(sizeof(BF16) * DATA_SIZE)); - store_command_to_npu(3, (long unsigned int)0x80, (long unsigned int)0x200000, (int)(sizeof(BF16) * DATA_SIZE)); - - printf("input_A is stored in all NPUs.\n\n"); - trace_pc_position() - // Load input_B at address 0x1080 of npu - store_command_to_npu(0, (long unsigned int)0x1080, (long unsigned int)0x201000, (int)(sizeof(BF16) * DATA_SIZE)); - store_command_to_npu(1, (long unsigned int)0x1080, (long unsigned int)0x201000, (int)(sizeof(BF16) * DATA_SIZE)); - store_command_to_npu(2, (long unsigned int)0x1080, (long unsigned int)0x201000, (int)(sizeof(BF16) * DATA_SIZE)); - store_command_to_npu(3, (long unsigned int)0x1080, (long unsigned int)0x201000, (int)(sizeof(BF16) * DATA_SIZE)); + load_input_A_into_npu(npus); + printf("input_A is stored in all NPUs.\n"); + load_input_B_into_npu(npus); printf("input_B is stored in all NPUs.\n\n"); #endif +} - trace_pc_position() - // sys-clk time_start, time_end; - //uint32_t time_start, time_end; - uint64_t time_start, time_end; +void store_result_into_ddr() { + + long unsigned int sram_a; + long unsigned int ddr_a; + int remaining; + int loadSize; + int size; + + size = (int)(sizeof(BF16) * DATA_SIZE); + + for (int len = 0; len < size; len += MAX_LOAD_STORE_CHUNK_SIZE) { + remaining = size - len; + loadSize = remaining < MAX_LOAD_STORE_CHUNK_SIZE ? remaining : MAX_LOAD_STORE_CHUNK_SIZE; + sram_a = RESULT_SRAM_BASE_ADDRESS + len; + + ddr_a = (long unsigned int)output_npu_0 + len; + store_command_to_npu(0, ddr_a, sram_a, loadSize); + ddr_a = (long unsigned int)output_npu_1 + len; + store_command_to_npu(1, ddr_a, sram_a, loadSize); + ddr_a = (long unsigned int)output_npu_2 + len; + store_command_to_npu(2, ddr_a, sram_a, loadSize); + ddr_a = (long unsigned int)output_npu_3 + len; + store_command_to_npu(3, ddr_a, sram_a, loadSize); + if(NUMBER_OF_CORES >= 5) { + ddr_a = (long unsigned int)output_npu_4 + len; + store_command_to_npu(4, ddr_a, sram_a, loadSize); + } + if(NUMBER_OF_CORES >= 6) { + ddr_a = (long unsigned int)output_npu_5 + len; + store_command_to_npu(5, ddr_a, sram_a, loadSize); + } - printf("\nRuns all NPUs.\n"); -#if 0 - // NPU Set - volatile uint32_t *npu_base = (uint32_t*)0x43C00000; // kernel offset - //Core 0 - npu_base[0] = (uint32_t)kernel_0;// the address of kernel in main memory - npu_base[1] = sizeof(kernel_0); // the size of kernel, also need to align by 8bytes + npu_store(); + + delay_in_usec(NPU_LOAD_STORE_MICRO_DELAY); + } +} + +void store_kernel_into_ddr() { + + long unsigned int sram_a; + long unsigned int ddr_a; + int remaining; + int loadSize; + int size; + + size = (int)sizeof(kernel_0); + + for (int len = 0; len < size; len += MAX_LOAD_STORE_CHUNK_SIZE) { + remaining = size - len; + loadSize = remaining < MAX_LOAD_STORE_CHUNK_SIZE ? remaining : MAX_LOAD_STORE_CHUNK_SIZE; + sram_a = 0x00 + len; + + ddr_a = (long unsigned int)output_npu_0 + len; + store_command_to_npu(0, ddr_a, sram_a, loadSize); + ddr_a = (long unsigned int)output_npu_1 + len; + store_command_to_npu(1, ddr_a, sram_a, loadSize); + ddr_a = (long unsigned int)output_npu_2 + len; + store_command_to_npu(2, ddr_a, sram_a, loadSize); + ddr_a = (long unsigned int)output_npu_3 + len; + store_command_to_npu(3, ddr_a, sram_a, loadSize); + if(NUMBER_OF_CORES >= 5) { + ddr_a = (long unsigned int)output_npu_4 + len; + store_command_to_npu(4, ddr_a, sram_a, loadSize); + } + if(NUMBER_OF_CORES >= 6) { + ddr_a = (long unsigned int)output_npu_5 + len; + store_command_to_npu(5, ddr_a, sram_a, loadSize); + } + + npu_store(); + + delay_in_usec(NPU_LOAD_STORE_MICRO_DELAY); + } +} + +void store_input_A_into_ddr() { + + long unsigned int sram_a; + long unsigned int ddr_a; + int remaining; + int loadSize; + int size; + + size = (int)(sizeof(BF16) * DATA_SIZE); - //XTime_GetTime(&time_start); // Start Measuring Time when core 0 is Start - time_start = get_time(); - npu_base[2] = 0; // Core Id - //Core 1 - npu_base[0] = (uint32_t)kernel_1; - npu_base[1] = sizeof(kernel_1); - npu_base[2] = 1; - //Core 2 - npu_base[0] = (uint32_t)kernel_2; - npu_base[1] = sizeof(kernel_2); - npu_base[2] = 2; - //Core 3 - npu_base[0] = (uint32_t)kernel_3; - npu_base[1] = sizeof(kernel_3); - npu_base[2] = 3; - - while(npu_base[3] & 0b1111) { // wait until operation is done (not busy) - //Xil_DCacheInvalidateRange(&npu_base[3], (uint32_t)sizeof(npu_base[3])); - //invalidate_data_cache(&npu_base[3], (uint32_t)sizeof(npu_base[3])); - }; - // XTime_GetTime(&time_end); // Get End Time + for (int len = 0; len < size; len += MAX_LOAD_STORE_CHUNK_SIZE) { + remaining = size - len; + loadSize = remaining < MAX_LOAD_STORE_CHUNK_SIZE ? remaining : MAX_LOAD_STORE_CHUNK_SIZE; + sram_a = INPUT_A_SRAM_BASE_ADDRESS + len; + + ddr_a = (long unsigned int)output_npu_0 + len; + store_command_to_npu(0, ddr_a, sram_a, loadSize); + ddr_a = (long unsigned int)output_npu_1 + len; + store_command_to_npu(1, ddr_a, sram_a, loadSize); + ddr_a = (long unsigned int)output_npu_2 + len; + store_command_to_npu(2, ddr_a, sram_a, loadSize); + ddr_a = (long unsigned int)output_npu_3 + len; + store_command_to_npu(3, ddr_a, sram_a, loadSize); + if(NUMBER_OF_CORES >= 5) { + ddr_a = (long unsigned int)output_npu_4 + len; + store_command_to_npu(4, ddr_a, sram_a, loadSize); + } + if(NUMBER_OF_CORES >= 6) { + ddr_a = (long unsigned int)output_npu_5 + len; + store_command_to_npu(5, ddr_a, sram_a, loadSize); + } + + npu_store(); + + delay_in_usec(NPU_LOAD_STORE_MICRO_DELAY); + } +} + +void store_input_B_into_ddr() { + + long unsigned int sram_a; + long unsigned int ddr_a; + int remaining; + int loadSize; + int size; + + size = (int)(sizeof(BF16) * DATA_SIZE); + + for (int len = 0; len < size; len += MAX_LOAD_STORE_CHUNK_SIZE) { + remaining = size - len; + loadSize = remaining < MAX_LOAD_STORE_CHUNK_SIZE ? remaining : MAX_LOAD_STORE_CHUNK_SIZE; + sram_a = INPUT_B_SRAM_BASE_ADDRESS + len; + + ddr_a = (long unsigned int)output_npu_0 + len; + store_command_to_npu(0, ddr_a, sram_a, loadSize); + ddr_a = (long unsigned int)output_npu_1 + len; + store_command_to_npu(1, ddr_a, sram_a, loadSize); + ddr_a = (long unsigned int)output_npu_2 + len; + store_command_to_npu(2, ddr_a, sram_a, loadSize); + ddr_a = (long unsigned int)output_npu_3 + len; + store_command_to_npu(3, ddr_a, sram_a, loadSize); + if(NUMBER_OF_CORES >= 5) { + ddr_a = (long unsigned int)output_npu_4 + len; + store_command_to_npu(4, ddr_a, sram_a, loadSize); + } + if(NUMBER_OF_CORES >= 6) { + ddr_a = (long unsigned int)output_npu_5 + len; + store_command_to_npu(5, ddr_a, sram_a, loadSize); + } + + npu_store(); + + delay_in_usec(NPU_LOAD_STORE_MICRO_DELAY); + } +} + +#ifdef _NPU_LOAD_STORE_TEST_MODE_ + +void compare_load_store_data(int npu, char *org, char *npu_ls, int size) { + + int mismatch = 0; + int idx; + + for(idx = 0;idx < size;idx++) { + if(org[idx] != npu_ls[idx]) { + printf("NPU%d Mismatch - origin[%4d]: 0x%02x, output_npu[%4d]: 0x%02x\n", + npu, idx, org[idx], idx, npu_ls[idx]); + mismatch = 1; + } +#if 0 // def __DEBUG_MODE__ + else { + printf("NPU%d Match - origin[%4d]: 0x%02x, output_npu[%4d]: 0x%02x\n", + npu, idx, org[idx], idx, npu_ls[idx]); + } #endif - trace_pc_position() - //time_start = get_time(); + } - trace_pc_position() - //time_end = get_time(); - - printf("\nAll NPUs have completed calculations.\n\n"); + if(mismatch) { + printf("[NPU%d] Fail to load & store\n", npu); + } else { + printf("[NPU%d] Success to load & store\n", npu); + } +} - trace_pc_position() +void load_store_test(int id) { -#if !KERNEL_WITH_LOAD_STORE // without-load-store -#if LOAD_STORE_TEST - // Load input_A at address 0x202000 of riscv - load_command_to_npu(0, (long unsigned int)0x202000, (long unsigned int)0x0080, (int)(sizeof(BF16) * DATA_SIZE)); - load_command_to_npu(1, (long unsigned int)0x203000, (long unsigned int)0x0080, (int)(sizeof(BF16) * DATA_SIZE)); - load_command_to_npu(2, (long unsigned int)0x204000, (long unsigned int)0x0080, (int)(sizeof(BF16) * DATA_SIZE)); - load_command_to_npu(3, (long unsigned int)0x205000, (long unsigned int)0x0080, (int)(sizeof(BF16) * DATA_SIZE)); + char *org; + char *npu_ls; + int size; + + printf("\n>>> %s(%d)\n\n", __func__, id); + + if(id == 0) { + load_kernel_into_npu(g_interrupt_mask); + } else if(id == 1) { + load_input_A_into_npu(g_interrupt_mask); + } else if(id == 2) { + load_input_B_into_npu(g_interrupt_mask); + } else { + return; + } + printf("\ncomplete npu_load\n\n"); + + if(id == 0) { + + store_kernel_into_ddr(); + + org = (char *)kernel_0; + npu_ls = (char *)output_npu_0; + compare_load_store_data(0, org, npu_ls, (int)sizeof(kernel_0)); + + org = (char *)kernel_1; + npu_ls = (char *)output_npu_1; + compare_load_store_data(1, org, npu_ls, (int)sizeof(kernel_1)); + + org = (char *)kernel_2; + npu_ls = (char *)output_npu_2; + compare_load_store_data(2, org, npu_ls, (int)sizeof(kernel_2)); + + org = (char *)kernel_3; + npu_ls = (char *)output_npu_3; + compare_load_store_data(3, org, npu_ls, (int)sizeof(kernel_3)); + if(NUMBER_OF_CORES >= 5) { + org = (char *)kernel_4; + npu_ls = (char *)output_npu_4; + compare_load_store_data(4, org, npu_ls, (int)sizeof(kernel_4)); + } + if(NUMBER_OF_CORES >= 6) { + org = (char *)kernel_5; + npu_ls = (char *)output_npu_5; + compare_load_store_data(5, org, npu_ls, (int)sizeof(kernel_5)); + } + } else if(id == 1) { + + size = (int)(sizeof(BF16) * DATA_SIZE); + store_input_A_into_ddr(); + + org = (char *)input_A; + compare_load_store_data(0, org, (char *)output_npu_0, size); + compare_load_store_data(1, org, (char *)output_npu_1, size); + compare_load_store_data(2, org, (char *)output_npu_2, size); + compare_load_store_data(3, org, (char *)output_npu_3, size); + if(NUMBER_OF_CORES >= 5) { + compare_load_store_data(4, org, (char *)output_npu_4, size); + } + if(NUMBER_OF_CORES >= 6) { + compare_load_store_data(5, org, (char *)output_npu_5, size); + } + } else if(id == 2) { + + size = (int)(sizeof(BF16) * DATA_SIZE); + store_input_B_into_ddr(); + + org = (char *)input_B; + compare_load_store_data(0, org, (char *)output_npu_0, size); + compare_load_store_data(1, org, (char *)output_npu_1, size); + compare_load_store_data(2, org, (char *)output_npu_2, size); + compare_load_store_data(3, org, (char *)output_npu_3, size); + if(NUMBER_OF_CORES >= 5) { + compare_load_store_data(4, org, (char *)output_npu_4, size); + } + if(NUMBER_OF_CORES >= 6) { + compare_load_store_data(5, org, (char *)output_npu_5, size); + } + } +} + +#endif + +static inline void init_complete_exec() { + + unsigned long value = 0x0; + + npu_regSet(NPU_COMPLETE_INTERRUPT_RST, (long unsigned int)0x01); // reset - active high + npu_regSet(NPU_COMPLETE_EXEC_REG, (long unsigned int)0x00); + npu_regSet(NPU_COMPLETE_INTERRUPT_RST, (long unsigned int)0x00); + + value = npu_regGet(NPU_COMPLETE_EXEC_REG); + if(value != 0) { + printf("%s - Fail, value: %lx\r", value); + } +} + +static uint64_t check_complete_exec(uint64_t start) { + + unsigned long value = 0x0; + uint64_t end; + + end = get_time(); +#if NPU_COMPLETE_EXEC_INTERRUPT + value = npu_regGet(NPU_COMPLETE_EXEC_REG); +#endif + + while(((value & g_interrupt_mask) != g_interrupt_mask) && + //while(((value) == 0) && + ((float)((end - start) / (SYS_CLK / 1000000)) < NPU_COMPLETE_EXEC_TIMEOUT)) { + end = get_time(); +#if NPU_COMPLETE_EXEC_INTERRUPT + value = npu_regGet(NPU_COMPLETE_EXEC_REG); #else - // Load output_C at address 0x202000 of riscv - load_command_to_npu(0, (long unsigned int)0x202000, (long unsigned int)0x2080, (int)(sizeof(BF16) * DATA_SIZE)); - load_command_to_npu(1, (long unsigned int)0x203000, (long unsigned int)0x2080, (int)(sizeof(BF16) * DATA_SIZE)); - load_command_to_npu(2, (long unsigned int)0x204000, (long unsigned int)0x2080, (int)(sizeof(BF16) * DATA_SIZE)); - load_command_to_npu(3, (long unsigned int)0x205000, (long unsigned int)0x2080, (int)(sizeof(BF16) * DATA_SIZE)); + printf("end: %016x\r", end); + fflush(stdout); #endif + } + printf("\nsrart: %016x\n", start); + printf("end: %016x\n", end); + printf("complete_exec_state: %lx\n", value); - printf("The calculated result values were loaded into external memory.\n\n"); + return end; +} + +void get_average_csrrs_cycle() { + + uint64_t cycle_start; + uint64_t cycle_end; + + cycle_start = get_time(); + for(int count = 0; count < 10; count++) { + cycle_end = get_time(); + } + elapsedCsrrsCycle = (cycle_end - cycle_start ) / 10; + printf("elapsedCsrrsCycle: 0x%lx\n", elapsedCsrrsCycle); +} + +int main_function() { + + uint64_t cycle_start; + uint64_t cycle_end; + int check[NUMBER_OF_CORES]; + char elapsedTimeStrValue[50]; + uint64_t elapsedCycle; + uint64_t flops; + float MFLOPS; + char megaFlopsStrValue[50]; + + printf("\n\n[%s Test] Using %d Cores\n", TEST_OP_TYPE, NUMBER_OF_CORES); + + memset(check, 0, NUMBER_OF_CORES * sizeof(int)); + + adjust_kernel(); + printf("\nKernel images for each NPU have been prepared.\n"); + +#ifdef __DEBUG_MODE__ + printf("[kernel_0]\n"); + dump_data((char *)kernel_0, (int)sizeof(kernel_0)); + printf("[kernel_1]\n"); + dump_data((char *)kernel_1, (int)sizeof(kernel_1)); + printf("[kernel_2]\n"); + dump_data((char *)kernel_2, (int)sizeof(kernel_2)); + printf("[kernel_3]\n"); + dump_data((char *)kernel_3, (int)sizeof(kernel_3)); + if(NUMBER_OF_CORES >= 5) { + g_interrupt_mask = 0x1F; + printf("[kernel_4]\n"); + dump_data((char *)kernel_4, (int)sizeof(kernel_4)); + } + if(NUMBER_OF_CORES >= 6) { + g_interrupt_mask = 0x3F; + printf("[kernel_5]\n"); + dump_data((char *)kernel_5, (int)sizeof(kernel_5)); + } #endif - trace_pc_position() -#if 1 // *** FAILED *** (tohost = 5) - // Memory Output - memcpy(output_npu_0, 0x202000, sizeof(BF16) * DATA_SIZE); - memcpy(output_npu_1, 0x203000, sizeof(BF16) * DATA_SIZE); - memcpy(output_npu_2, 0x204000, sizeof(BF16) * DATA_SIZE); - memcpy(output_npu_3, 0x205000, sizeof(BF16) * DATA_SIZE); + load_kernel_data_into_npu(); + + init_complete_exec(); + + printf("\nRuns all NPUs.\n"); + cycle_start = get_time(); + + npu_exec(); + + cycle_end = check_complete_exec(cycle_start); + if(((cycle_end - cycle_start) / (SYS_CLK / 1000000)) < NPU_COMPLETE_EXEC_TIMEOUT) { + printf("\nAll NPUs have completed calculations.\n\n"); + } else { + printf("\nTimeout - Waiting for all npu execs to finish.\n\n"); + return 0; + } - printf("Copy the result values loaded in external memory to local variables of risc-v.\n\n"); +#if !KERNEL_WITH_LOAD_STORE // without-load-store + store_result_into_ddr(); + printf("The calculated result values were loaded into external memory.\n\n"); #endif - trace_pc_position() +#ifdef __DEBUG_MODE__ + printf("[output_npu_0]\n"); + dump_data((char *)output_npu_0, (int)(sizeof(BF16) * DATA_SIZE)); + printf("[output_npu_1]\n"); + dump_data((char *)output_npu_1, (int)(sizeof(BF16) * DATA_SIZE)); + printf("[output_npu_2]\n"); + dump_data((char *)output_npu_2, (int)(sizeof(BF16) * DATA_SIZE)); + printf("[output_npu_3]\n"); + dump_data((char *)output_npu_3, (int)(sizeof(BF16) * DATA_SIZE)); + if(NUMBER_OF_CORES >= 5) { + printf("[output_npu_4]\n"); + dump_data((char *)output_npu_4, (int)(sizeof(BF16) * DATA_SIZE)); + } + if(NUMBER_OF_CORES >= 6) { + printf("[output_npu_5]\n"); + dump_data((char *)output_npu_5, (int)(sizeof(BF16) * DATA_SIZE)); + } +#endif printf("\nCompare the results calculated by risc-v and the results calculated by NPUs.\n"); // Check RISCV's Outpus & NPUs's Outputs Are Same, Input RISCV's OP Output Array in 1st Parameter - int check0 = 0; - int check1 = 0; - int check2 = 0; - int check3 = 0; if (TEST_OP_TYPE == "vadd.bf16") { - check0 = compare_riscv_and_npu(0, TEST_OP_TYPE, output_riscv_add, output_npu_0, DATA_SIZE); - check1 = compare_riscv_and_npu(1, TEST_OP_TYPE, output_riscv_add, output_npu_1, DATA_SIZE); - check2 = compare_riscv_and_npu(2, TEST_OP_TYPE, output_riscv_add, output_npu_2, DATA_SIZE); - check3 = compare_riscv_and_npu(3, TEST_OP_TYPE, output_riscv_add, output_npu_3, DATA_SIZE); + check[0] = compare_riscv_and_npu(0, TEST_OP_TYPE, output_riscv_add, output_npu_0, DATA_SIZE); + check[1] = compare_riscv_and_npu(1, TEST_OP_TYPE, output_riscv_add, output_npu_1, DATA_SIZE); + check[2] = compare_riscv_and_npu(2, TEST_OP_TYPE, output_riscv_add, output_npu_2, DATA_SIZE); + check[3] = compare_riscv_and_npu(3, TEST_OP_TYPE, output_riscv_add, output_npu_3, DATA_SIZE); + if(NUMBER_OF_CORES >= 5) { + check[4] = compare_riscv_and_npu(4, TEST_OP_TYPE, output_riscv_add, output_npu_4, DATA_SIZE); + } + if(NUMBER_OF_CORES >= 6) { + check[5] = compare_riscv_and_npu(5, TEST_OP_TYPE, output_riscv_add, output_npu_5, DATA_SIZE); + } } else if (TEST_OP_TYPE == "vsub.bf16") { - check0 = compare_riscv_and_npu(0, TEST_OP_TYPE, output_riscv_sub, output_npu_0, DATA_SIZE); - check1 = compare_riscv_and_npu(1, TEST_OP_TYPE, output_riscv_sub, output_npu_1, DATA_SIZE); - check2 = compare_riscv_and_npu(2, TEST_OP_TYPE, output_riscv_sub, output_npu_2, DATA_SIZE); - check3 = compare_riscv_and_npu(3, TEST_OP_TYPE, output_riscv_sub, output_npu_3, DATA_SIZE); + check[0] = compare_riscv_and_npu(0, TEST_OP_TYPE, output_riscv_sub, output_npu_0, DATA_SIZE); + check[1] = compare_riscv_and_npu(1, TEST_OP_TYPE, output_riscv_sub, output_npu_1, DATA_SIZE); + check[2] = compare_riscv_and_npu(2, TEST_OP_TYPE, output_riscv_sub, output_npu_2, DATA_SIZE); + check[3] = compare_riscv_and_npu(3, TEST_OP_TYPE, output_riscv_sub, output_npu_3, DATA_SIZE); + if(NUMBER_OF_CORES >= 5) { + check[4] = compare_riscv_and_npu(4, TEST_OP_TYPE, output_riscv_sub, output_npu_4, DATA_SIZE); + } + if(NUMBER_OF_CORES >= 6) { + check[5] = compare_riscv_and_npu(5, TEST_OP_TYPE, output_riscv_sub, output_npu_5, DATA_SIZE); + } } else if (TEST_OP_TYPE == "vmul.bf16"){ - check0 = compare_riscv_and_npu(0, TEST_OP_TYPE, output_riscv_mul, output_npu_0, DATA_SIZE); - check1 = compare_riscv_and_npu(1, TEST_OP_TYPE, output_riscv_mul, output_npu_1, DATA_SIZE); - check2 = compare_riscv_and_npu(2, TEST_OP_TYPE, output_riscv_mul, output_npu_2, DATA_SIZE); - check3 = compare_riscv_and_npu(3, TEST_OP_TYPE, output_riscv_mul, output_npu_3, DATA_SIZE); + check[0] = compare_riscv_and_npu(0, TEST_OP_TYPE, output_riscv_mul, output_npu_0, DATA_SIZE); + check[1] = compare_riscv_and_npu(1, TEST_OP_TYPE, output_riscv_mul, output_npu_1, DATA_SIZE); + check[2] = compare_riscv_and_npu(2, TEST_OP_TYPE, output_riscv_mul, output_npu_2, DATA_SIZE); + check[3] = compare_riscv_and_npu(3, TEST_OP_TYPE, output_riscv_mul, output_npu_3, DATA_SIZE); + if(NUMBER_OF_CORES >= 5) { + check[4] = compare_riscv_and_npu(4, TEST_OP_TYPE, output_riscv_mul, output_npu_4, DATA_SIZE); + } + if(NUMBER_OF_CORES >= 6) { + check[5] = compare_riscv_and_npu(5, TEST_OP_TYPE, output_riscv_mul, output_npu_5, DATA_SIZE); + } } else if (TEST_OP_TYPE == "vdiv.bf16") { - check0 = compare_riscv_and_npu(0, TEST_OP_TYPE, output_riscv_div, output_npu_0, DATA_SIZE); - check1 = compare_riscv_and_npu(1, TEST_OP_TYPE, output_riscv_div, output_npu_1, DATA_SIZE); - check2 = compare_riscv_and_npu(2, TEST_OP_TYPE, output_riscv_div, output_npu_2, DATA_SIZE); - check3 = compare_riscv_and_npu(3, TEST_OP_TYPE, output_riscv_div, output_npu_3, DATA_SIZE); + check[0] = compare_riscv_and_npu(0, TEST_OP_TYPE, output_riscv_div, output_npu_0, DATA_SIZE); + check[1] = compare_riscv_and_npu(1, TEST_OP_TYPE, output_riscv_div, output_npu_1, DATA_SIZE); + check[2] = compare_riscv_and_npu(2, TEST_OP_TYPE, output_riscv_div, output_npu_2, DATA_SIZE); + check[3] = compare_riscv_and_npu(3, TEST_OP_TYPE, output_riscv_div, output_npu_3, DATA_SIZE); + if(NUMBER_OF_CORES >= 5) { + check[4] = compare_riscv_and_npu(4, TEST_OP_TYPE, output_riscv_div, output_npu_4, DATA_SIZE); + } + if(NUMBER_OF_CORES >= 6) { + check[5] = compare_riscv_and_npu(5, TEST_OP_TYPE, output_riscv_div, output_npu_5, DATA_SIZE); + } } + printf("\n"); - trace_pc_position() // If All Pass, Print - if (check0 == DATA_SIZE) { + if (check[0] == DATA_SIZE) { printf("[NPU 0 Test Case %d] %s All Pass\n", DATA_SIZE, TEST_OP_TYPE); } - if (check1 == DATA_SIZE) { + if (check[1] == DATA_SIZE) { printf("[NPU 1 Test Case %d] %s All Pass\n", DATA_SIZE, TEST_OP_TYPE); } - if (check2 == DATA_SIZE) { + if (check[2] == DATA_SIZE) { printf("[NPU 2 Test Case %d] %s All Pass\n", DATA_SIZE, TEST_OP_TYPE); } - if (check3 == DATA_SIZE) { + if (check[3] == DATA_SIZE) { printf("[NPU 3 Test Case %d] %s All Pass\n", DATA_SIZE, TEST_OP_TYPE); } + if(NUMBER_OF_CORES >= 5) { + if (check[4] == DATA_SIZE) { + printf("[NPU 4 Test Case %d] %s All Pass\n", DATA_SIZE, TEST_OP_TYPE); + } + } + if(NUMBER_OF_CORES >= 6) { + if (check[5] == DATA_SIZE) { + printf("[NPU 5 Test Case %d] %s All Pass\n", DATA_SIZE, TEST_OP_TYPE); + } + } - char elapsedTimeStrValue[50]; - floatToString(2.0 * (time_end - time_start) / (SYS_CLK / 1000000), + // 1 cycle = 25ns + elapsedCycle = cycle_end - cycle_start - elapsedCsrrsCycle; + +#ifdef __DEBUG_MODE__ + floatToString(elapsedCycle / (SYS_CLK / 1000000), elapsedTimeStrValue, sizeof(elapsedTimeStrValue)); printf("\nRISC-V Time: %s us.\n", elapsedTimeStrValue); +#endif + + flops = NUMBER_OF_CORES * DATA_SIZE * LOOP_COUNT; + + printf("flops: %ld, elapsedCycle: %ld(1 cycle = 25ns)\n", flops, elapsedCycle); + MFLOPS = (flops * 1000) / (elapsedCycle * 25); + floatToString(MFLOPS, megaFlopsStrValue, sizeof(megaFlopsStrValue)); + printf(" = %s MFLOPS.\n\n", megaFlopsStrValue); + + return 0; +} + +int main() { + + uint64_t cycle_start; + uint64_t cycle_end; + int check[NUMBER_OF_CORES]; + char elapsedTimeStrValue[50]; + + printf("\n========Init========\n\n"); + printf("Multi NAU Test\n"); + if(KERNEL_WITH_LOAD_STORE == 0) { + printf(" Kernel without load/store functions\n\n"); + } else { + printf(" Kernel with load/store functions\n\n"); + } + + memset(check, 0, NUMBER_OF_CORES * sizeof(int)); + + init_variavles(); + printf("\ninput_A & input_B are filled with random data.\n"); + +#ifdef __DEBUG_MODE__ + printf("[input_A]\n"); + dump_data((char *)input_A, (int)(sizeof(BF16) * DATA_SIZE)); + printf("[input_B]\n"); + dump_data((char *)input_B, (int)(sizeof(BF16) * DATA_SIZE)); +#endif + + riscv_calculate_result(); + printf("\nThe result values of risc-v for each function were calculated using input_A & input_B.\n"); + + if(NUMBER_OF_CORES >= 5) { + g_interrupt_mask = 0x1F; + } + if(NUMBER_OF_CORES >= 6) { + g_interrupt_mask = 0x3F; + } + + get_average_csrrs_cycle(); + +#ifdef _NPU_LOAD_STORE_TEST_MODE_ + + load_store_test(0); + load_store_test(1); + load_store_test(2); + + return 0; +#endif + +#if 0 + TEST_OP_TYPE = "vadd.bf16"; + main_function(); +#endif + +#if 0 + TEST_OP_TYPE = "vsub.bf16"; + main_function(); +#endif #if 0 - // Test Case N's Total Cycles, NPU: 125MHz - printf("[#0 NPU] Total cycles: %d\tConvert Times: %.3fus\n", npu_base[4 + 0], npu_base[4 + 0] * 8.00 / 1000.00); - printf("[#1 NPU] Total cycles: %d\tConvert Times: %.3fus\n", npu_base[4 + 1], npu_base[4 + 1] * 8.00 / 1000.00); - printf("[#2 NPU] Total cycles: %d\tConvert Times: %.3fus\n", npu_base[4 + 2], npu_base[4 + 2] * 8.00 / 1000.00); - printf("[#3 NPU] Total cycles: %d\tConvert Times: %.3fus\n", npu_base[4 + 3], npu_base[4 + 3] * 8.00 / 1000.00); + TEST_OP_TYPE = "vmul.bf16"; + main_function(); +#endif + +#if 1 + TEST_OP_TYPE = "vdiv.bf16"; + main_function(); #endif printf("\n========Finish========\n\n");