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 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/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/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/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; diff --git a/hardware/src/vlsu/addrgen.sv b/hardware/src/vlsu/addrgen.sv index 1ba67f650..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); @@ -515,10 +523,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 +555,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 @@ -809,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; @@ -966,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 @@ -975,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 @@ -1065,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,