From 0f7df203b616d045eb1171741d7c94a1bf6a4cb2 Mon Sep 17 00:00:00 2001 From: Saurav Singh Date: Thu, 18 Jun 2026 19:03:55 +0000 Subject: [PATCH 1/4] :bug: [addrgen] Report misaligned indexed access as LD/ST_ADDR_MISALIGNED An indexed memory element whose effective address is misaligned to its EEW must raise a load/store address-misaligned exception (RVV spec) and report the faulting effective address in [ms]tval. The addrgen instead reported `riscv::ILLEGAL_INSTR` with tval=0, so software saw the wrong mcause and lost the faulting address. Set the exception cause to LD_ADDR_MISALIGNED / ST_ADDR_MISALIGNED based on the access direction and forward the faulting virtual address (idx_final_vaddr_q) as tval. The cause/tval already propagate to CVA6 via ara_resp.exception. The load/store still has to be drained, so widen the addrgen_illegal_load_o/store_o kill condition to also fire on the misaligned cause (it is really a "kill faulted op" signal for the VLDU/ VSTU drain, not specific to illegal instructions). Verified against Spike with a trap probe: unmasked vluxei32.v with a base+1 index now traps with mcause=4 (LD_ADDR_MISALIGNED) and mtval=faulting address (Ara previously reported mcause=2, mtval=0). Refs #457 --- apps/vlxe_misalign_cause/main.c | 71 +++++++++++++++++++++++++++++++++ hardware/src/vlsu/addrgen.sv | 20 +++++++--- 2 files changed, 85 insertions(+), 6 deletions(-) create mode 100644 apps/vlxe_misalign_cause/main.c diff --git a/apps/vlxe_misalign_cause/main.c b/apps/vlxe_misalign_cause/main.c new file mode 100644 index 000000000..401a8d76f --- /dev/null +++ b/apps/vlxe_misalign_cause/main.c @@ -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 +#ifdef SPIKE +#include "util.h" +#include +#elif defined ARA_LINUX +#include +#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; +} diff --git a/hardware/src/vlsu/addrgen.sv b/hardware/src/vlsu/addrgen.sv index 1ba67f650..5ecc2269f 100644 --- a/hardware/src/vlsu/addrgen.sv +++ b/hardware/src/vlsu/addrgen.sv @@ -515,10 +515,14 @@ module addrgen import ara_pkg::*; import rvv_pkg::*; #( word_lane_ptr_d = '0; // Raise an error if necessary if (idx_op_error_q) begin - // In this case, we always get EEW-misaligned exceptions + // An indexed element whose effective address is misaligned to its + // EEW raises a load/store address-misaligned exception (RVV spec), + // reporting the faulting effective address in [ms]tval - not an + // illegal-instruction exception with tval=0 as before. (#457) addrgen_exception_o.valid = 1'b1; - addrgen_exception_o.cause = riscv::ILLEGAL_INSTR; - addrgen_exception_o.tval = '0; + addrgen_exception_o.cause = is_load(pe_req_q.op) ? riscv::LD_ADDR_MISALIGNED + : riscv::ST_ADDR_MISALIGNED; + addrgen_exception_o.tval = idx_final_vaddr_q; end // Propagate the exception from the MMU (if any) // NOTE: this would override idx_op_error_q @@ -543,10 +547,14 @@ module addrgen import ara_pkg::*; import rvv_pkg::*; #( end endcase - // Immediately kill the load/store if the instruction was illegal + // Immediately kill the load/store if the addrgen detected a fault + // (illegal access or an element address-misaligned exception). Both must + // drain the load/store unit so the faulting instruction retires. (#457) if (addrgen_exception_o.valid && addrgen_ack_o) begin - addrgen_illegal_load_o = is_load(pe_req_q.op) && (addrgen_exception_o.cause == riscv::ILLEGAL_INSTR); - addrgen_illegal_store_o = !is_load(pe_req_q.op) && (addrgen_exception_o.cause == riscv::ILLEGAL_INSTR); + addrgen_illegal_load_o = is_load(pe_req_q.op) && + (addrgen_exception_o.cause inside {riscv::ILLEGAL_INSTR, riscv::LD_ADDR_MISALIGNED}); + addrgen_illegal_store_o = !is_load(pe_req_q.op) && + (addrgen_exception_o.cause inside {riscv::ILLEGAL_INSTR, riscv::ST_ADDR_MISALIGNED}); end end : addr_generation From 31320682b7203b0d88658dd6ef5c6b7aec166e1c Mon Sep 17 00:00:00 2001 From: Saurav Singh Date: Fri, 19 Jun 2026 03:21:40 +0000 Subject: [PATCH 2/4] :bug: [addrgen] Skip misalignment exception for masked-off indexed elements A masked indexed load/store must not raise a misalignment exception for an element that is masked off: per the RVV spec, masked-off elements generate neither exceptions nor architectural accesses. Ara trapped because the address generator checked is_addr_error() per indexed element without any visibility into the per-element mask. Route the mask (peek-only; the load/store units still own the handshake) into the addrgen. For a misaligned indexed element whose mask bit is 0, suppress the exception, align the address down to the element width, and emit a normal beat. The VLDU/VSTU already zero the byte strobes of masked-off elements, so the element is dropped exactly like any other masked-off element and the element accounting stays in sync. The mask is resolved in the addrgen only when the whole vector fits within a single mask chunk (vl <= elements-per-chunk); otherwise the conservative trapping behaviour is kept, so there is no regression on multi-chunk vectors. Verified against the Spike golden model (vlxe_masked_misalign, e16/e32/e64): masked-off misaligned element no longer traps and keeps its old value, while an active misaligned element still traps with LD_ADDR_MISALIGNED (cause=4). Refs #456 --- apps/vlxe_masked_misalign/main.c | 96 ++++++++++++++++++++++++++++++++ hardware/src/vlsu/addrgen.sv | 74 ++++++++++++++++++++++-- hardware/src/vlsu/vlsu.sv | 3 + 3 files changed, 168 insertions(+), 5 deletions(-) create mode 100644 apps/vlxe_masked_misalign/main.c diff --git a/apps/vlxe_masked_misalign/main.c b/apps/vlxe_masked_misalign/main.c new file mode 100644 index 000000000..b0473df8c --- /dev/null +++ b/apps/vlxe_masked_misalign/main.c @@ -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 +#ifdef SPIKE +#include "util.h" +#include +#elif defined ARA_LINUX +#include +#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; +} diff --git a/hardware/src/vlsu/addrgen.sv b/hardware/src/vlsu/addrgen.sv index 5ecc2269f..0f44f954c 100644 --- a/hardware/src/vlsu/addrgen.sv +++ b/hardware/src/vlsu/addrgen.sv @@ -22,6 +22,8 @@ module addrgen import ara_pkg::*; import rvv_pkg::*; #( parameter type exception_t = logic, // Dependant parameters. DO NOT CHANGE! localparam type axi_addr_t = logic [AxiAddrWidth-1:0], + localparam int unsigned MaskStrbWidth = $bits(elen_t)/8, + localparam type strb_t = logic [MaskStrbWidth-1:0], localparam type vlen_t = logic[$clog2(VLEN+1)-1:0] ) ( input logic clk_i, @@ -71,7 +73,13 @@ module addrgen import ara_pkg::*; import rvv_pkg::*; #( input logic [NrLanes-1:0] addrgen_operand_valid_i, output logic addrgen_operand_ready_o, // Indexed LSU exception support - input logic lsu_ex_flush_i + input logic lsu_ex_flush_i, + // Interface with the Mask unit (peek-only). + // The load/store units own the mask handshake (ready/ack). The addrgen only + // reads the mask to decide whether a misaligned *indexed* element is + // masked-off (see #456): a masked-off element must not raise an exception. + input strb_t [NrLanes-1:0] mask_i, + input logic [NrLanes-1:0] mask_valid_i ); localparam unsigned DataWidth = $bits(elen_t); @@ -817,6 +825,56 @@ module addrgen import ara_pkg::*; import rvv_pkg::*; #( automatic logic [12:0] num_bytes; // Cannot consume more than 4 KiB automatic vlen_t remaining_bytes; + // Indexed masked-off misalignment handling (#456) + // ----------------------------------------------- + // For a masked indexed access, an element whose own mask bit is 0 must + // not raise a misalignment exception (RVV: masked-off elements generate + // neither exceptions nor architectural accesses). The addrgen does not + // own the mask stream, so it can only resolve this when the whole vector + // fits within a single mask chunk (vl <= elements-per-chunk); otherwise + // it keeps the conservative (trapping) behaviour, avoiding any regression. + // For a masked-off misaligned element we align the address down and emit + // a normal beat: the load/store unit's mask already zeroes its byte + // strobes, exactly as it does for any other masked-off element, so the + // VLDU/VSTU element accounting stays in sync. + automatic logic idx_misalign; + automatic vlen_t idx_elem_per_chunk; + automatic logic idx_single_chunk; + automatic vlen_t idx_cur_elem; + automatic int unsigned idx_seq_byte; + automatic logic [$clog2(8*MaxNrLanes)-1:0] idx_vrf_byte; + automatic int unsigned idx_mask_lane; + automatic logic idx_mask_bit; + automatic logic idx_mask_valid_elem; + automatic logic idx_suppress; + automatic logic idx_stall_mask; + automatic logic idx_trap; + automatic axi_addr_t idx_eff_vaddr; + + idx_misalign = is_addr_error(idx_final_vaddr_q, axi_addrgen_q.vew[1:0]); + idx_elem_per_chunk = vlen_t'((NrLanes * 8) >> axi_addrgen_q.vew); + idx_single_chunk = (pe_req_q.vl <= idx_elem_per_chunk); + idx_cur_elem = pe_req_q.vl - (axi_addrgen_q.len >> axi_addrgen_q.vew); + idx_seq_byte = idx_cur_elem << axi_addrgen_q.vew; + idx_vrf_byte = shuffle_index(idx_seq_byte, NrLanes, rvv_pkg::vew_e'(axi_addrgen_q.vew)); + // The mask bit for this element lives in one specific lane. Only that + // lane's mask beat needs to be valid: requiring *all* lanes + // (&mask_valid_i) deadlocks whenever vl does not span every lane (e.g. + // vl < NrLanes), because idle lanes never produce a mask beat. + idx_mask_lane = idx_vrf_byte >> 3; + idx_mask_valid_elem = mask_valid_i[idx_mask_lane]; + idx_mask_bit = mask_i[idx_mask_lane][idx_vrf_byte[2:0]]; + // Suppress the exception only when we are certain the element is masked-off + idx_suppress = idx_misalign && !pe_req_q.vm && idx_single_chunk && idx_mask_valid_elem && !idx_mask_bit; + // Wait for this element's (single-chunk) mask lane before deciding + idx_stall_mask = idx_misalign && !pe_req_q.vm && idx_single_chunk && !idx_mask_valid_elem; + // Trap on a genuine (unmasked, or undecidable multi-chunk) misalignment + idx_trap = idx_misalign && !idx_suppress && !idx_stall_mask; + // Address actually issued: aligned-down for a masked-off misaligned element + idx_eff_vaddr = idx_suppress + ? (idx_final_vaddr_q & ~((axi_addr_t'(1) << axi_addrgen_q.vew) - 1)) + : idx_final_vaddr_q; + // Pre-calculate the next_2page_msb. This should not require much energy if the addr // has zeroes in the upper positions. next_2page_msb_d = aligned_next_start_addr_q[AxiAddrWidth-1:12] + 1; @@ -974,7 +1032,12 @@ module addrgen import ara_pkg::*; import rvv_pkg::*; #( // Check if the virtual address generates an exception // NOTE: we can do this even before address translation, since the // page offset (2^12) is the same for both physical and virtual addresses - if (is_addr_error(idx_final_vaddr_q, axi_addrgen_q.vew[1:0])) begin : eew_misaligned_error + if (idx_stall_mask) begin : wait_for_mask + // Masked indexed element is misaligned but the (single-chunk) + // mask is not available yet: stall until we can tell whether + // this element is masked-off. Do not issue or acknowledge. + end : wait_for_mask + else if (idx_trap) begin : eew_misaligned_error // Generate an error idx_op_error_d = 1'b1; // Forward next vstart info to the dispatcher @@ -983,8 +1046,9 @@ module addrgen import ara_pkg::*; import rvv_pkg::*; #( axi_addrgen_state_d = AXI_ADDRGEN_IDLE; end : eew_misaligned_error else begin : aligned_vaddress - // Mux target address - idx_final_paddr = (en_ld_st_translation_i) ? mmu_paddr_i : idx_final_vaddr_q; + // Mux target address (use the aligned-down address for a + // masked-off misaligned element, see #456) + idx_final_paddr = (en_ld_st_translation_i) ? mmu_paddr_i : idx_eff_vaddr; // AR Channel if (axi_addrgen_q.is_load) begin @@ -1073,7 +1137,7 @@ module addrgen import ara_pkg::*; import rvv_pkg::*; #( // Check if the virtual address generates an exception // NOTE: we can do this even before address translation, since the // page offset (2^12) is the same for both physical and virtual addresses - if (!is_addr_error(idx_final_vaddr_q, axi_addrgen_q.vew[1:0])) begin : aligned_vaddress + if (!idx_trap && !idx_stall_mask) begin : aligned_vaddress // We consumed a word idx_vaddr_ready_d = 1'b1; diff --git a/hardware/src/vlsu/vlsu.sv b/hardware/src/vlsu/vlsu.sv index 27397fa35..5f058704f 100644 --- a/hardware/src/vlsu/vlsu.sv +++ b/hardware/src/vlsu/vlsu.sv @@ -195,6 +195,9 @@ module vlsu import ara_pkg::*; import rvv_pkg::*; #( .ldu_axi_addrgen_req_ready_i(ldu_axi_addrgen_req_ready ), .stu_axi_addrgen_req_ready_i(stu_axi_addrgen_req_ready ), .lsu_ex_flush_i (lsu_ex_flush_i ), + // Interface with the Mask unit (peek-only; #456) + .mask_i (mask_i ), + .mask_valid_i (mask_valid_i ), // CSR input .en_ld_st_translation_i, From de1d288c89cb738901391b6a827ddfd52e9b9ab7 Mon Sep 17 00:00:00 2001 From: Saurav Singh Date: Fri, 19 Jun 2026 04:56:55 +0000 Subject: [PATCH 3/4] :bug: [sequencer] Track all readers per register for correct WAR under OoO retire Ara issues in order but retires out of order. The main sequencer's read table recorded only the *last* instruction reading each vector register, so a writer built a WAR hazard against just that last reader. A slow earlier reader could then be overtaken: once the last (fast) reader retired, the writer was allowed to overwrite the register while the slow reader had not finished reading it, so the slow reader observed the new value. Example (the writer to v2 only waited for the fast vadd, not the slow vdivu): vdivu.vv v6, v4, v2 ; slow, reads v2 vadd.vv v8, v2, v2 ; fast, reads v2 -> overwrote the read-table entry vadd.vv v2, v12, v12 ; writes v2 -> WAR only vs the fast vadd (bug) Replace the "last reader id" per register with a per-register bitmask of every in-flight reader (one bit per instruction id). The WAR build then ORs the whole reader mask into the writer's hazards, so the writer waits for all pending readers to retire. This is the maintainer's preferred fix (issue #434): it only affects writers (no reader-reader serialization), so it does not slow down read-heavy code such as matmul. The mask decays with vinsn_running, like the previous valid bit. Writes still track only the last writer (serialized by WAW). Verified against the Spike golden model with the rar_retire_hazard reproducer (256-element e32/m2 divide + clobber): unfixed Ara returned 168/256 wrong elements (sum=1040), the fixed model matches Spike (sum=2048, bad=0). Regressions: dotproduct (reductions) and fmatmul both still pass. Refs #434 --- apps/rar_retire_hazard/main.c | 75 +++++++++++++++++++++++++++++++++++ hardware/src/ara_sequencer.sv | 36 +++++++++++------ 2 files changed, 98 insertions(+), 13 deletions(-) create mode 100644 apps/rar_retire_hazard/main.c diff --git a/apps/rar_retire_hazard/main.c b/apps/rar_retire_hazard/main.c new file mode 100644 index 000000000..5702b6172 --- /dev/null +++ b/apps/rar_retire_hazard/main.c @@ -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 +#ifdef SPIKE +#include "util.h" +#include +#elif defined ARA_LINUX +#include +#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; +} diff --git a/hardware/src/ara_sequencer.sv b/hardware/src/ara_sequencer.sv index 9cd0cc405..613948f57 100644 --- a/hardware/src/ara_sequencer.sv +++ b/hardware/src/ara_sequencer.sv @@ -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); @@ -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; @@ -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 @@ -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 @@ -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 @@ -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; @@ -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; From 9f3e448102386a869a1c8eb4284816ff49c023c8 Mon Sep 17 00:00:00 2001 From: Saurav Singh Date: Sat, 20 Jun 2026 09:36:13 +0000 Subject: [PATCH 4/4] :memo: [changelog] Document pr/vlsu-sequencer-fixes --- CHANGELOG.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 542eb153d..e2c723793 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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