// See LICENSE for license details. #ifndef SRC_MAIN_C_GEMMINI_TESTUTILS_H #define SRC_MAIN_C_GEMMINI_TESTUTILS_H #undef abs #include #include #include #include #include #include #include "include/gemmini_params.h" #include "include/gemmini.h" #ifdef BAREMETAL #undef assert #define assert(expr) \ if (!(expr)) { \ printf("Failed assertion: " #expr "\n " __FILE__ ":%u\n", __LINE__); \ exit(1); \ } #endif // #define GEMMINI_ASSERTIONS // Matmul utility functions static void matmul(elem_t A[DIM][DIM], elem_t B[DIM][DIM], elem_t D[DIM][DIM], full_t C_full[DIM][DIM]) { for (size_t r = 0; r < DIM; r++) for (size_t c = 0; c < DIM; c++) { C_full[r][c] = D[r][c]; for (size_t k = 0; k < DIM; k++) C_full[r][c] += A[r][k]*B[k][c]; } } static void matmul_short(elem_t A[DIM][DIM], elem_t B[DIM][DIM], elem_t D[DIM][DIM], elem_t C[DIM][DIM]) { for (size_t r = 0; r < DIM; r++) for (size_t c = 0; c < DIM; c++) { C[r][c] = D[r][c]; for (size_t k = 0; k < DIM; k++) C[r][c] += A[r][k]*B[k][c]; } } static void matmul_full(elem_t A[DIM][DIM], elem_t B[DIM][DIM], full_t D[DIM][DIM], full_t C_full[DIM][DIM]) { // Identical to the other matmul function, but with a 64-bit bias for (size_t r = 0; r < DIM; r++) for (size_t c = 0; c < DIM; c++) { C_full[r][c] = D[r][c]; for (size_t k = 0; k < DIM; k++) C_full[r][c] += A[r][k]*B[k][c]; } } static void matmul_A_transposed(elem_t A[DIM][DIM], elem_t B[DIM][DIM], elem_t D[DIM][DIM], full_t C_full[DIM][DIM]) { for (size_t r = 0; r < DIM; r++) for (size_t c = 0; c < DIM; c++) { C_full[r][c] = D[r][c]; for (size_t k = 0; k < DIM; k++) C_full[r][c] += A[k][r]*B[k][c]; } } static void matmul_short_A_transposed(elem_t A[DIM][DIM], elem_t B[DIM][DIM], elem_t D[DIM][DIM], elem_t C[DIM][DIM]) { for (size_t r = 0; r < DIM; r++) for (size_t c = 0; c < DIM; c++) { C[r][c] = D[r][c]; for (size_t k = 0; k < DIM; k++) C[r][c] += A[k][r]*B[k][c]; } } static void matmul_full_A_transposed(elem_t A[DIM][DIM], elem_t B[DIM][DIM], full_t D[DIM][DIM], full_t C_full[DIM][DIM]) { for (size_t r = 0; r < DIM; r++) for (size_t c = 0; c < DIM; c++) { C_full[r][c] = D[r][c]; for (size_t k = 0; k < DIM; k++) C_full[r][c] += A[k][r]*B[k][c]; } } static void matmul_B_transposed(elem_t A[DIM][DIM], elem_t B[DIM][DIM], elem_t D[DIM][DIM], full_t C_full[DIM][DIM]) { for (size_t r = 0; r < DIM; r++) for (size_t c = 0; c < DIM; c++) { C_full[r][c] = D[r][c]; for (size_t k = 0; k < DIM; k++) C_full[r][c] += A[r][k]*B[c][k]; } } static void matmul_short_B_transposed(elem_t A[DIM][DIM], elem_t B[DIM][DIM], elem_t D[DIM][DIM], elem_t C[DIM][DIM]) { for (size_t r = 0; r < DIM; r++) for (size_t c = 0; c < DIM; c++) { C[r][c] = D[r][c]; for (size_t k = 0; k < DIM; k++) C[r][c] += A[r][k]*B[c][k]; } } static void matmul_full_B_transposed(elem_t A[DIM][DIM], elem_t B[DIM][DIM], full_t D[DIM][DIM], full_t C_full[DIM][DIM]) { for (size_t r = 0; r < DIM; r++) for (size_t c = 0; c < DIM; c++) { C_full[r][c] = D[r][c]; for (size_t k = 0; k < DIM; k++) C_full[r][c] += A[r][k]*B[c][k]; } } static void matmul_AB_transposed(elem_t A[DIM][DIM], elem_t B[DIM][DIM], elem_t D[DIM][DIM], full_t C_full[DIM][DIM]) { for (size_t r = 0; r < DIM; r++) for (size_t c = 0; c < DIM; c++) { C_full[r][c] = D[r][c]; for (size_t k = 0; k < DIM; k++) C_full[r][c] += A[k][r]*B[c][k]; } } static void matmul_short_AB_transposed(elem_t A[DIM][DIM], elem_t B[DIM][DIM], elem_t D[DIM][DIM], elem_t C[DIM][DIM]) { for (size_t r = 0; r < DIM; r++) for (size_t c = 0; c < DIM; c++) { C[r][c] = D[r][c]; for (size_t k = 0; k < DIM; k++) C[r][c] += A[k][r]*B[c][k]; } } static void matmul_full_AB_transposed(elem_t A[DIM][DIM], elem_t B[DIM][DIM], full_t D[DIM][DIM], full_t C_full[DIM][DIM]) { for (size_t r = 0; r < DIM; r++) for (size_t c = 0; c < DIM; c++) { C_full[r][c] = D[r][c]; for (size_t k = 0; k < DIM; k++) C_full[r][c] += A[k][r]*B[c][k]; } } static void matadd(full_t sum[DIM][DIM], full_t m1[DIM][DIM], full_t m2[DIM][DIM]) { for (size_t r = 0; r < DIM; r++) for (size_t c = 0; c < DIM; c++) sum[r][c] = m1[r][c] + m2[r][c]; } // THIS IS A ROUNDING SHIFT! It also performs a saturating cast static void matshift(full_t full[DIM][DIM], elem_t out[DIM][DIM], int shift) { for (size_t r = 0; r < DIM; r++) for (size_t c = 0; c < DIM; c++) { // Bitshift and round element full_t shifted = ROUNDING_RIGHT_SHIFT(full[r][c], shift); // Saturate and cast element #ifndef ELEM_T_IS_FLOAT full_t elem = shifted > elem_t_max ? elem_t_max : (shifted < elem_t_min ? elem_t_min : shifted); out[r][c] = elem; #else out[r][c] = shifted; // TODO should we also saturate when using floats? #endif } } static void matscale(full_t full[DIM][DIM], elem_t out[DIM][DIM], acc_scale_t scale) { for (size_t r = 0; r < DIM; r++) for (size_t c = 0; c < DIM; c++) { // Bitshift and round element full_t scaled = ACC_SCALE(full[r][c], scale); // Saturate and cast element #ifndef ELEM_T_IS_FLOAT full_t elem = scaled > elem_t_max ? elem_t_max : (scaled < elem_t_min ? elem_t_min : scaled); out[r][c] = elem; #else out[r][c] = scaled; // TODO should we also saturate when using floats? #endif } } static void matrelu(elem_t in[DIM][DIM], elem_t out[DIM][DIM]) { for (size_t r = 0; r < DIM; r++) for (size_t c = 0; c < DIM; c++) out[r][c] = in[r][c] > 0 ? in[r][c] : 0; } static void transpose(elem_t in[DIM][DIM], elem_t out[DIM][DIM]) { for (size_t r = 0; r < DIM; r++) for (size_t c = 0; c < DIM; c++) out[c][r] = in[r][c]; } int rand() { static uint32_t x = 777; x = x * 1664525 + 1013904223; return x >> 24; } #ifdef ELEM_T_IS_FLOAT double rand_double() { double a = (double)(rand() % 128) / (double)(1 + (rand() % 64)); double b = (double)(rand() % 128) / (double)(1 + (rand() % 64)); return a - b; } #endif static void printMatrix(elem_t m[DIM][DIM]) { for (size_t i = 0; i < DIM; ++i) { for (size_t j = 0; j < DIM; ++j) #ifndef ELEM_T_IS_FLOAT printf("%d ", m[i][j]); #else printf("%x ", elem_t_to_elem_t_bits(m[i][j])); #endif printf("\n"); } } static void printMatrixAcc(acc_t m[DIM][DIM]) { for (size_t i = 0; i < DIM; ++i) { for (size_t j = 0; j < DIM; ++j) #ifndef ELEM_T_IS_FLOAT printf("%d ", m[i][j]); #else printf("%x ", acc_t_to_acc_t_bits(m[i][j])); #endif printf("\n"); } } static int is_equal(elem_t x[DIM][DIM], elem_t y[DIM][DIM]) { for (size_t i = 0; i < DIM; ++i) for (size_t j = 0; j < DIM; ++j) { #ifndef ELEM_T_IS_FLOAT if (x[i][j] != y[i][j]) #else bool isnanx = elem_t_isnan(x[i][j]); bool isnany = elem_t_isnan(y[i][j]); if (x[i][j] != y[i][j] && !(isnanx && isnany)) #endif return 0; } return 1; } static int is_equal_transposed(elem_t x[DIM][DIM], elem_t y[DIM][DIM]) { for (size_t i = 0; i < DIM; ++i) for (size_t j = 0; j < DIM; ++j) { #ifndef ELEM_T_IS_FLOAT if (x[i][j] != y[j][i]) #else bool isnanx = elem_t_isnan(x[i][j]); bool isnany = elem_t_isnan(y[j][i]); if (x[i][j] != y[j][i] && !(isnanx && isnany)) #endif return 0; } return 1; } // This is a GNU extension known as statment expressions #define MAT_IS_EQUAL(dim_i, dim_j, x, y) \ ({int result = 1; \ for (size_t i = 0; i < dim_i; i++) \ for (size_t j = 0; j < dim_j; ++j) { \ if (x[i][j] != y[i][j]) { \ result = 0; \ break; \ } \ } \ result;}) static uint64_t read_cycles() { uint64_t cycles; asm volatile ("rdcycle %0" : "=r" (cycles)); return cycles; // const uint32_t * mtime = (uint32_t *)(33554432 + 0xbff8); // const uint32_t * mtime = (uint32_t *)(33554432 + 0xbffc); // return *mtime; } #undef abs #endif // SRC_MAIN_C_GEMMINI_TESTUTILS_H