Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,17 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.

## [Unreleased]

### Added

- Add `vlxe_misalign_cause` app: Spike-vs-Ara trap probe for the indexed-misalignment exception class/tval (#457)
- Add `vlxe_masked_misalign` app: Spike-vs-Ara trap probe for the masked-off misaligned indexed element (#456)
- Add `rar_retire_hazard` app: Spike-vs-Ara differential reproducer for the read-after-read out-of-order-retire hazard (#434)

### Fixed

- Report `LD/ST_ADDR_MISALIGNED` with the faulting effective address (instead of `ILLEGAL_INSTR` with tval=0) for indexed memory elements misaligned to their EEW (#457)
- Do not raise a misalignment exception for a *masked-off* element of a masked indexed load/store: the address generator now peeks the mask and, when the whole vector fits in a single mask chunk, aligns the masked-off element's address down and lets the load/store unit's byte strobes drop it (#456)
- Track *all* in-flight readers of each vector register (per-register bitmask) in the main sequencer's read table instead of only the last one, so a writer builds a WAR hazard against every pending reader. This prevents a later writer from overtaking a slow earlier reader under Ara's out-of-order retirement (#434)
- Fix dump vtrace script for vsetvli instructions without x0 (ideal dispatcher)
- Fix Pathfinder and FFT performance
- Stall Ara and wait for ara_idle upon CSR write/read
Expand Down
75 changes: 75 additions & 0 deletions apps/rar_retire_hazard/main.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
// Copyright 2026 ETH Zurich and University of Bologna.
// SPDX-License-Identifier: Apache-2.0
//
// Spike-vs-Ara differential reproducer for issue #434: a Read-After-Read that
// turns into a correctness bug under Ara's out-of-order retirement.
//
// The main sequencer's read table only remembered the *last* instruction
// reading each vector register, so a later writer built a WAR hazard against
// only that last reader. A slow earlier reader could then be overtaken:
//
// vdivu v6, v4, v2 ; SLOW, reads v2 (the value we care about)
// vadd v8, v2, v2 ; fast, reads v2 -> overwrites the read table entry
// vadd v2, v12, v12 ; writes v2 -> WAR only vs the fast vadd (bug!)
//
// Once the fast vadd retires, the writer to v2 was allowed to proceed while the
// slow vdivu had not finished reading v2, so vdivu's tail elements divided by
// the *new* v2. Correct hardware (and Spike) must keep v6 == num/old_a for all
// elements.
//
// old_a = 1000, num = 8000 -> v6 = 8 everywhere (correct)
// new_a = 4000 -> 8000/4000 = 2 on corrupted tail elements (buggy)
//
// Expected (Spike, and Ara after the #434 fix): sum=2048 bad=0 (256 * 8).

#include <stdint.h>
#ifdef SPIKE
#include "util.h"
#include <stdio.h>
#elif defined ARA_LINUX
#include <stdio.h>
#else
#include "printf.h"
#endif

#define N 256

static volatile uint32_t arr_a[N]; // old value of "a" = 1000
static volatile uint32_t arr_num[N]; // numerator = 8000
static volatile uint32_t arr_new[N]; // half of new "a" = 2000 (new a = 4000)
static volatile uint32_t result[N];

int main() {
for (int i = 0; i < N; i++) {
arr_a[i] = 1000;
arr_num[i] = 8000;
arr_new[i] = 2000;
}
asm volatile("fence" ::: "memory");

uint64_t vl;
asm volatile("vsetvli %0, %1, e32, m2, ta, ma" : "=r"(vl) : "r"((uint64_t)N));

asm volatile("vle32.v v2, (%0)" ::"r"(arr_a)); // v2 = 1000 (old a)
asm volatile("vle32.v v4, (%0)" ::"r"(arr_num)); // v4 = 8000 (num)
asm volatile("vle32.v v12, (%0)" ::"r"(arr_new)); // v12 = 2000

// Slow reader of v2.
asm volatile("vdivu.vv v6, v4, v2");
// Fast reader of v2 (used to clobber the single-entry read table).
asm volatile("vadd.vv v8, v2, v2");
// Writer of v2: must wait for *both* readers above to retire.
asm volatile("vadd.vv v2, v12, v12"); // v2 <- 4000

asm volatile("vse32.v v6, (%0)" ::"r"(result));
asm volatile("fence" ::: "memory");

uint32_t sum = 0;
int bad = 0;
for (int i = 0; i < N; i++) {
sum += result[i];
if (result[i] != 8) bad++;
}
printf("R: sum=%d bad=%d\n", (int)sum, bad);
return 0;
}
96 changes: 96 additions & 0 deletions apps/vlxe_masked_misalign/main.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
// Copyright 2026 ETH Zurich and University of Bologna.
// SPDX-License-Identifier: Apache-2.0
//
// Spike-vs-Ara differential probe for issue #456: a masked indexed load whose
// *masked-off* element has a misaligned effective address must NOT trap (per
// the RVV spec, masked-off elements generate neither exceptions nor accesses).
//
// vsetivli 4, e32, m1, ta, mu
// v0 = 0b1110 (element 0 masked-off; elements 1..3 active)
// v4 = idx{1,4,8,12} ; element 0 idx=1 -> base+1 is misaligned for e32
// vluxei32.v v8, (base), v4, v0.t
//
// Element 0 is masked off, so its misaligned address must be ignored: no trap.
// v8 is pre-filled with 0xdead and the policy is mask-undisturbed, so v8[0]
// keeps 0xdead while v8[1..3] receive mem[base+4/8/12].
//
// Expected (Spike, and Ara after the #456 fix):
// cause=0 o0=57005 o1=8738 o2=13107 o3=17476
// (0xdead=57005, 0x2222=8738, 0x3333=13107, 0x4444=17476)

#include <stdint.h>
#ifdef SPIKE
#include "util.h"
#include <stdio.h>
#elif defined ARA_LINUX
#include <stdio.h>
#else
#include "printf.h"
#endif

// g_cause stays 0 if no trap occurs (the expected, correct behaviour).
volatile uint64_t g_cause = 0;
volatile uint64_t g_tval = 0;
volatile uint64_t g_epc = 0;

// Ara runtime (apps/common/crt0.S) jumps to a weak mtvec_handler with no saved
// context. Record the trap CSRs and resume at trap_resume. Present only so an
// (incorrect) trapping run terminates instead of hanging.
asm(".global mtvec_handler\n"
".align 2\n"
"mtvec_handler:\n"
" addi sp, sp, -16\n"
" sd t0, 0(sp)\n"
" sd t1, 8(sp)\n"
" csrr t0, mcause\n la t1, g_cause\n sd t0, 0(t1)\n"
" csrr t0, mtval\n la t1, g_tval\n sd t0, 0(t1)\n"
" csrr t0, mepc\n la t1, g_epc\n sd t0, 0(t1)\n"
" la t0, trap_resume\n csrw mepc, t0\n"
" ld t0, 0(sp)\n ld t1, 8(sp)\n addi sp, sp, 16\n"
" mret\n");

// Spike runtime (riscv-tests benchmarks crt.S) saves context and calls
// handle_trap(mcause, mepc, sp), then sets mepc to the return value.
extern char trap_resume[];
uintptr_t handle_trap(uintptr_t cause, uintptr_t epc, uintptr_t regs) {
uint64_t tval;
asm volatile("csrr %0, mtval" : "=r"(tval));
g_cause = cause;
g_tval = tval;
g_epc = epc;
return (uintptr_t)trap_resume;
}

static volatile uint32_t mem_data[8] = {0x1111, 0x2222, 0x3333, 0x4444,
0, 0, 0, 0};
static volatile uint32_t idx_data[4] = {1, 4, 8, 12};
static volatile uint8_t mask_data[1] = {0x0E}; // bit0=0, bits1..3=1
static volatile uint32_t result[4] = {0, 0, 0, 0};

int main() {
asm volatile("fence" ::: "memory");

// Pre-fill v8 with 0xdead (mask-undisturbed keeps masked-off lanes).
asm volatile("vsetivli x0, 4, e32, m1, ta, mu\n"
"li t0, 0xdead\n"
"vmv.v.x v8, t0\n" ::: "t0");

// Load the mask register v0 = 0b1110.
asm volatile("vsetivli x0, 1, e8, m1, ta, ma");
asm volatile("vle8.v v0, (%0)" ::"r"(mask_data));

// Load the index vector and perform the masked indexed load.
asm volatile("vsetivli x0, 4, e32, m1, ta, mu");
asm volatile("vle32.v v4, (%0)" ::"r"(idx_data));
asm volatile("vluxei32.v v8, (%0), v4, v0.t\n"
".global trap_resume\n"
"trap_resume:\n" ::"r"(mem_data));

// Store v8 back to memory for read-out.
asm volatile("vse32.v v8, (%0)" ::"r"(result));
asm volatile("fence" ::: "memory");

printf("R: cause=%d o0=%d o1=%d o2=%d o3=%d\n", (int)g_cause,
(int)result[0], (int)result[1], (int)result[2], (int)result[3]);
return 0;
}
71 changes: 71 additions & 0 deletions apps/vlxe_misalign_cause/main.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
// Copyright 2026 ETH Zurich and University of Bologna.
// SPDX-License-Identifier: Apache-2.0
//
// Spike-vs-Ara differential trap probe for issue #457: an unmasked misaligned
// indexed load must raise LD_ADDR_MISALIGNED (mcause=4) and report the faulting
// effective address in mtval - not ILLEGAL_INSTR (mcause=2) with mtval=0.
//
// idx_data = {1,4,8,12}; element 0 uses base+1, misaligned for vluxei32.
// We trap, record mcause/mtval, resume past the load, and print:
// cause = mcause
// tval_off = mtval - base (link-independent; expect 1)
// Expected (Spike): cause=4, tval_off=1.

#include <stdint.h>
#ifdef SPIKE
#include "util.h"
#include <stdio.h>
#elif defined ARA_LINUX
#include <stdio.h>
#else
#include "printf.h"
#endif

volatile uint64_t g_cause = 0xbad;
volatile uint64_t g_tval = 0xbad;
volatile uint64_t g_epc = 0xbad;

// Ara runtime (apps/common/crt0.S) jumps to a weak mtvec_handler with no saved
// context. Record the trap CSRs and resume at trap_resume.
asm(".global mtvec_handler\n"
".align 2\n"
"mtvec_handler:\n"
" addi sp, sp, -16\n"
" sd t0, 0(sp)\n"
" sd t1, 8(sp)\n"
" csrr t0, mcause\n la t1, g_cause\n sd t0, 0(t1)\n"
" csrr t0, mtval\n la t1, g_tval\n sd t0, 0(t1)\n"
" csrr t0, mepc\n la t1, g_epc\n sd t0, 0(t1)\n"
" la t0, trap_resume\n csrw mepc, t0\n"
" ld t0, 0(sp)\n ld t1, 8(sp)\n addi sp, sp, 16\n"
" mret\n");

// Spike runtime (riscv-tests benchmarks crt.S) saves context and calls
// handle_trap(mcause, mepc, sp), then sets mepc to the return value.
extern char trap_resume[];
uintptr_t handle_trap(uintptr_t cause, uintptr_t epc, uintptr_t regs) {
uint64_t tval;
asm volatile("csrr %0, mtval" : "=r"(tval));
g_cause = cause;
g_tval = tval;
g_epc = epc;
return (uintptr_t)trap_resume;
}

static volatile uint32_t idx_data[4] = {1, 4, 8, 12};
static volatile uint32_t mem_data[8] = {0x11111111, 0x22222222, 0x33333333,
0x44444444, 0, 0, 0, 0};

int main() {
asm volatile("fence" ::: "memory");
asm volatile("vsetivli x0, 4, e32, m1, ta, ma");
asm volatile("vle32.v v4, (%0)" ::"r"(idx_data));
asm volatile("vluxei32.v v8, (%0), v4\n"
".global trap_resume\n"
"trap_resume:\n" ::"r"(mem_data));
asm volatile("fence" ::: "memory");

uint64_t tval_off = g_tval - (uint64_t)(uintptr_t)mem_data;
printf("cause=%d tval_off=%d\n", (int)g_cause, (int)tval_off);
return 0;
}
36 changes: 23 additions & 13 deletions hardware/src/ara_sequencer.sv
Original file line number Diff line number Diff line change
Expand Up @@ -233,8 +233,15 @@ module ara_sequencer import ara_pkg::*; import rvv_pkg::*; import cf_math_pkg::i
vid_t vid;
logic valid;
} vreg_access_t;
vreg_access_t [31:0] read_list_d, read_list_q;
// Writes: only the last writer of each register matters, since writes to the
// same register are serialized by the WAW hazard.
vreg_access_t [31:0] write_list_d, write_list_q;
// Reads: a per-register bitmask of *every* in-flight instruction currently
// reading the register (bit i == instruction id i is a reader). Tracking all
// readers - not just the last one - is required for correct WAR hazards under
// Ara's out-of-order retirement: a later writer must wait for all earlier
// readers to retire, otherwise a slow reader could observe the new value (#434).
logic [31:0][NrVInsn-1:0] read_mask_d, read_mask_q;

// This function determines the VFU responsible for handling this operation.
function automatic vfu_e vfu(ara_op_e op`ifndef SYNTHESIS = VADD `endif);
Expand Down Expand Up @@ -356,7 +363,7 @@ module ara_sequencer import ara_pkg::*; import rvv_pkg::*; import cf_math_pkg::i
// Default assignments
state_d = state_q;
pe_vinsn_running_d = pe_vinsn_running_q;
read_list_d = read_list_q;
read_mask_d = read_mask_q;
write_list_d = write_list_q;
global_hazard_table_d = global_hazard_table_o;

Expand All @@ -376,7 +383,8 @@ module ara_sequencer import ara_pkg::*; import rvv_pkg::*; import cf_math_pkg::i

// Update vector register's access list
for (int unsigned v = 0; v < 32; v++) begin
read_list_d[v].valid &= vinsn_running_q[read_list_q[v].vid] ;
// Drop readers/writers that have retired
read_mask_d[v] &= vinsn_running_q;
write_list_d[v].valid &= vinsn_running_q[write_list_q[v].vid];
end

Expand Down Expand Up @@ -416,11 +424,12 @@ module ara_sequencer import ara_pkg::*; import rvv_pkg::*; import cf_math_pkg::i
if (!ara_req_i.vm) pe_req_d.hazard_vm[write_list_d[VMASK].vid] |=
write_list_d[VMASK].valid;

// WAR
// WAR - wait for *all* in-flight readers of vd, not only the last
// one, so a later writer cannot overtake a slow earlier reader (#434)
if (ara_req_i.use_vd) begin
pe_req_d.hazard_vs1[read_list_d[ara_req_i.vd].vid] |= read_list_d[ara_req_i.vd].valid;
pe_req_d.hazard_vs2[read_list_d[ara_req_i.vd].vid] |= read_list_d[ara_req_i.vd].valid;
pe_req_d.hazard_vm[read_list_d[ara_req_i.vd].vid] |= read_list_d[ara_req_i.vd].valid;
pe_req_d.hazard_vs1 |= read_mask_d[ara_req_i.vd];
pe_req_d.hazard_vs2 |= read_mask_d[ara_req_i.vd];
pe_req_d.hazard_vm |= read_mask_d[ara_req_i.vd];
end

// WAW
Expand Down Expand Up @@ -519,10 +528,11 @@ module ara_sequencer import ara_pkg::*; import rvv_pkg::*; import cf_math_pkg::i
// Mark that this vector instruction is writing to vector vd
if (ara_req_i.use_vd) write_list_d[ara_req_i.vd] = '{vid: vinsn_id_n, valid: 1'b1};

// Mark that this loop is reading vs
if (ara_req_i.use_vs1) read_list_d[ara_req_i.vs1] = '{vid: vinsn_id_n, valid: 1'b1};
if (ara_req_i.use_vs2) read_list_d[ara_req_i.vs2] = '{vid: vinsn_id_n, valid: 1'b1};
if (!ara_req_i.vm) read_list_d[VMASK] = '{vid: vinsn_id_n, valid: 1'b1};
// Mark that this loop is reading vs (set this reader's bit; keep
// the bits of any other instructions still reading the register)
if (ara_req_i.use_vs1) read_mask_d[ara_req_i.vs1][vinsn_id_n] = 1'b1;
if (ara_req_i.use_vs2) read_mask_d[ara_req_i.vs2][vinsn_id_n] = 1'b1;
if (!ara_req_i.vm) read_mask_d[VMASK][vinsn_id_n] = 1'b1;
end
end else ara_req_ready_o = 1'b0; // Wait until the PEs are ready
end
Expand Down Expand Up @@ -581,7 +591,7 @@ module ara_sequencer import ara_pkg::*; import rvv_pkg::*; import cf_math_pkg::i
if (!rst_ni) begin
state_q <= IDLE;

read_list_q <= '0;
read_mask_q <= '0;
write_list_q <= '0;

pe_req_o <= '0;
Expand All @@ -596,7 +606,7 @@ module ara_sequencer import ara_pkg::*; import rvv_pkg::*; import cf_math_pkg::i
end else begin
state_q <= state_d;

read_list_q <= read_list_d;
read_mask_q <= read_mask_d;
write_list_q <= write_list_d;

pe_req_o <= pe_req_d;
Expand Down
Loading