Repository navigation
Description
A config_ex between matmul.preload and its compute, even one that repeats the current configuration, gives a wrong C for that matmul in both WS and OS. No assertion fires. Is this sequence supposed to work? If not, an assertion or a README note would help.
Environment: Gemmini 8c3f9923 (Chipyard 1.15.0), Verilator 5.022, GemminiRocketConfig SoC with Gemmini DIM = 4 and DIM = 8 (INT8/INT32, Dataflow.BOTH). Tests built like the bareMetalC tests and run with make -C sims/verilator CONFIG=<config> LOADMEM=1 RANDOM_SEED=1 run-binary-fast BINARY=<elf>. Reference: Spike with libgemmini.
Reproducer
The test runs two back-to-back DIM x DIM matmuls with raw preload/compute commands, once as usual and once with a config_ex that repeats the current configuration between the second preload and its compute, in WS and in OS. A single matmul (gemmini_fence(); preload(...); config_ex(...); compute_preloaded(...)) also gives a wrong C.
config_between_preload_compute.c
#include <stdint.h>
#include <stdio.h>
#include "include/gemmini_testutils.h"
static elem_t A[2][DIM][DIM] __attribute__((aligned(64)));
static elem_t B[2][DIM][DIM] __attribute__((aligned(64)));
static acc_t OUT[2][DIM][DIM] __attribute__((aligned(64)));
static acc_t REF[2][DIM][DIM];
static void run(int df, int extra_cfg) {
const uint32_t a0 = 0, a1 = DIM, b0 = 2 * DIM, b1 = 3 * DIM;
const uint32_t c0 = 0x80000000u, c1 = 0x80000000u | DIM; // accumulator rows 0 and DIM
gemmini_flush(0);
gemmini_extended3_config_ex(df, 0, 0, ACC_SCALE_IDENTITY, 1, 1, 0, 0, false);
gemmini_extended3_config_ld(DIM * sizeof(elem_t), MVIN_SCALE_IDENTITY, false, 0);
gemmini_extended_mvin(&A[0][0][0], a0, DIM, DIM);
gemmini_extended_mvin(&A[1][0][0], a1, DIM, DIM);
gemmini_extended_mvin(&B[0][0][0], b0, DIM, DIM);
gemmini_extended_mvin(&B[1][0][0], b1, DIM, DIM);
if (df == WEIGHT_STATIONARY) {
gemmini_extended_preload(b0, c0, DIM, DIM, DIM, DIM);
gemmini_extended_compute_preloaded(a0, GARBAGE_ADDR, DIM, DIM, DIM, DIM);
gemmini_extended_preload(b1, c1, DIM, DIM, DIM, DIM);
if (extra_cfg) gemmini_extended3_config_ex(df, 0, 0, ACC_SCALE_IDENTITY, 1, 1, 0, 0, false);
gemmini_extended_compute_preloaded(a1, GARBAGE_ADDR, DIM, DIM, DIM, DIM);
} else {
gemmini_extended_preload(GARBAGE_ADDR, c0, DIM, DIM, DIM, DIM);
gemmini_extended_compute_preloaded(a0, b0, DIM, DIM, DIM, DIM);
gemmini_extended_preload(GARBAGE_ADDR, c1, DIM, DIM, DIM, DIM);
if (extra_cfg) gemmini_extended3_config_ex(df, 0, 0, ACC_SCALE_IDENTITY, 1, 1, 0, 0, false);
gemmini_extended_compute_preloaded(a1, b1, DIM, DIM, DIM, DIM);
}
gemmini_fence();
gemmini_config_st(DIM * sizeof(acc_t));
gemmini_extended_mvout((uintptr_t)&OUT[0][0][0], 0xA0000000u, DIM, DIM);
gemmini_extended_mvout((uintptr_t)&OUT[1][0][0], 0xA0000000u | DIM, DIM, DIM);
gemmini_fence();
int bad[2] = {0, 0};
for (int t = 0; t < 2; t++)
for (int i = 0; i < DIM; i++)
for (int j = 0; j < DIM; j++) bad[t] += OUT[t][i][j] != REF[t][i][j];
printf("%s, extra config_ex=%d: wrong elements C0=%d C1=%d\n", df == WEIGHT_STATIONARY ? "WS" : "OS", extra_cfg, bad[0], bad[1]);
if (bad[1])
for (int i = 0; i < DIM; i++) {
printf(" C1 row %d:", i);
for (int j = 0; j < DIM; j++) printf(" %d", (int)OUT[1][i][j]);
printf(" (C0 row %d:", i);
for (int j = 0; j < DIM; j++) printf(" %d", (int)OUT[0][i][j]);
printf(")\n");
}
}
int main() {
for (int t = 0; t < 2; t++)
for (int i = 0; i < DIM; i++)
for (int j = 0; j < DIM; j++) {
A[t][i][j] = (elem_t)(((i * 5 + j * 3 + t * 7 + i * j) % 11) - 5);
B[t][i][j] = (elem_t)(((i * 3 + j * 7 + t * 5 + 2 * i * j) % 13) - 6);
}
for (int t = 0; t < 2; t++)
for (int i = 0; i < DIM; i++)
for (int j = 0; j < DIM; j++) {
acc_t s = 0;
for (int k = 0; k < DIM; k++) s += (acc_t)A[t][i][k] * B[t][k][j];
REF[t][i][j] = s;
}
run(WEIGHT_STATIONARY, 0);
run(WEIGHT_STATIONARY, 1);
run(OUTPUT_STATIONARY, 0);
run(OUTPUT_STATIONARY, 1);
return 0;
}- Expected (Spike):
wrong elements C0=0 C1=0in all four runs. - Observed (RTL),
DIM = 4:WS, extra config_ex=0: wrong elements C0=0 C1=0 WS, extra config_ex=1: wrong elements C0=0 C1=16 C1 row 0: -24 14 0 -27 (C0 row 0: 48 -7 29 0) C1 row 1: -24 14 0 -27 (C0 row 1: -9 33 23 13) C1 row 2: -24 14 0 -27 (C0 row 2: -33 -15 -49 -18) C1 row 3: -24 14 0 -27 (C0 row 3: -24 14 0 -27) OS, extra config_ex=0: wrong elements C0=0 C1=0 OS, extra config_ex=1: wrong elements C0=0 C1=16 C1 row 0: 0 0 0 0 (C0 row 0: 48 -7 29 0) C1 row 1: 0 0 0 0 (C0 row 1: -9 33 23 13) C1 row 2: 0 0 0 0 (C0 row 2: -33 -15 -49 -18) C1 row 3: 0 0 0 0 (C0 row 3: -24 14 0 -27)DIM = 8shows the same pattern (WS:C1=64, every row equal to the last row of C0; OS:C1=62, all zeros). - Controls: a
fenceor a CPU delay at the same place, or aconfig_exbetween two preload/compute pairs, gives the right result.
Cause (from reading the RTL; consistent with the values above, not checked with waveforms)
- The execute controller overlaps a compute with the following preload as soon as a third command is queued (
ExecuteController.scala:600checkscmd.valid(2)), so theconfig_exlets the second preload be issued together with the first compute. - With a matmul in flight the
config_excannot execute (ExecuteController.scala:541), so the controller flushes the array first (:624); the flush lands between the preload and its compute. - The flush advances
matmul_id(MeshWithDelays.scala:114-117) but enqueues no output tag (:223,:238), so the C address of the pending preload (:219-226) is paired with the wrong rows: in WS the flush's rows are written to C1 and the compute's rows are dropped; in OS the flush flips the accumulators (ExecuteController.scala:665-667) and C1 receives the freshly preloaded zeros.
Activity