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_sew_eew` app: Spike-vs-Ara differential probe for the indexed SEW!=EEW hang (#455)
- Add `vslideup_mask` app: Spike-vs-Ara differential probe for the large-stride masked vslideup bug (#459)
- Add `vle_vstart_mask`/`vse_vstart_mask2`/`vlseg_mask` apps: differential probes for the masked memory op `vstart >= NrLanes` bug (#462)

### Fixed

- Fix indexed load/store hang/stuck-valid when data SEW != index EEW by not rescaling the index fetch length (#455)
- Skip the correct number of whole mask rows for `vslideup` with a large stride, fixing the MASKU reading mask bits `0..stride-1` instead of the active window above the stride (#459)
- Use the bit-packed mask-row index for the masked load/store mask operand, fixing masked memory ops with `vstart >= NrLanes` (notably segment-load micro-ops) that dropped active elements (#462)
- 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
38 changes: 38 additions & 0 deletions apps/vle_vstart_mask/main.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
// Copyright 2026 ETH Zurich and University of Bologna.
// SPDX-License-Identifier: Apache-2.0
//
// Isolation probe for issue #462: a masked vle16.v with an explicit
// vstart=4 (>= NrLanes), vl=5, mask bit 4 active. This mimics the segment
// micro-op (vstart=s, vl=s+1, masked). Element 4 should be loaded; elements
// 0..3 stay undisturbed (below vstart). If Ara drops element 4, the bug is in
// the masked-load + vstart path, independent of segments.

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

static volatile uint16_t in_data[6] = {0x1000, 0x1001, 0x1002,
0x1003, 0x1004, 0x1005};
static volatile uint8_t mask_data[1] = {0x10}; // bit 4 active
static volatile uint16_t out[6];

int main() {
asm volatile("fence" ::: "memory");
asm volatile("vsetivli x0, 5, e16, m1, ta, ma");
asm volatile("vmv.v.x v13, %0" ::"r"((uint64_t)0x1111));
asm volatile("vlm.v v0, (%0)" ::"r"(mask_data));
asm volatile("csrw vstart, 4");
asm volatile("vle16.v v13, (%0), v0.t" ::"r"(in_data));
asm volatile("vse16.v v13, (%0)" ::"r"(out) : "memory");
asm volatile("fence" ::: "memory");

// Expected: out[4] = 0x1004 = 4100; out[0..3] = 0x1111 = 4369 (below vstart)
for (int i = 0; i < 6; i++) printf("e%d=%d\n", i, (int)out[i]);
return 0;
}
44 changes: 44 additions & 0 deletions apps/vlseg_mask/main.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
// Copyright 2026 ETH Zurich and University of Bologna.
// SPDX-License-Identifier: Apache-2.0
//
// Spike-vs-Ara differential probe for issue #462 (masked vlseg2e16.v ignores an
// active mask bit and leaves that lane undisturbed). vl=6, e16, only mask bit 5
// active. v13/v14 preinit to 0x1111/0x2222. Lane 5 source = (0x1005, 0x2005).
// Only lane 5 should be loaded; all other lanes stay undisturbed.
// Static .data init + fence; read v13/v14 back with vse16.

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

// 6 segments x 2 fields, element-major: seg i -> {0x1000+i, 0x2000+i}.
static volatile uint16_t seg_data[12] = {0x1000, 0x2000, 0x1001, 0x2001,
0x1002, 0x2002, 0x1003, 0x2003,
0x1004, 0x2004, 0x1005, 0x2005};
// Mask: only bit 5 active (0x20).
static volatile uint8_t mask_data[1] = {0x20};
static volatile uint16_t out13[6], out14[6];

int main() {
asm volatile("fence" ::: "memory");
asm volatile("vsetivli x0, 6, e16, m1, ta, ma");
asm volatile("vmv.v.x v13, %0" ::"r"((uint64_t)0x1111));
asm volatile("vmv.v.x v14, %0" ::"r"((uint64_t)0x2222));
asm volatile("vlm.v v0, (%0)" ::"r"(mask_data));
asm volatile("vlseg2e16.v v13, (%0), v0.t" ::"r"(seg_data));
asm volatile("vse16.v v13, (%0)" ::"r"(out13) : "memory");
asm volatile("vse16.v v14, (%0)" ::"r"(out14) : "memory");
asm volatile("fence" ::: "memory");

// Expected (Spike): lane 5 loaded, all others undisturbed.
// out13 = {0x1111 x5, 0x1005}; out14 = {0x2222 x5, 0x2005}
printf("c0=%d c5=%d d0=%d d5=%d\n", (int)out13[0], (int)out13[5],
(int)out14[0], (int)out14[5]);
return 0;
}
61 changes: 61 additions & 0 deletions apps/vlxe_sew_eew/main.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
// Copyright 2026 ETH Zurich and University of Bologna.
//
// SPDX-License-Identifier: Apache-2.0
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// Spike-vs-Ara differential probe for issue #455 (indexed load/store hang when
// data SEW != index EEW).
//
// Index EEW (16) > data SEW (8): the index-operand fetch length is mis-scaled
// by the data SEW, so the address generator under-fetches and waits forever.
// On a buggy Ara this hangs (caught by the sim timeout); Spike completes.
// Each value line lets the harness diff the loaded elements.

#include <stdint.h>

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

static volatile uint8_t mem[128];
static volatile uint16_t idx[64];
static volatile uint8_t out[64];

int main() {
uint64_t vl;

for (int i = 0; i < 128; i++) mem[i] = (uint8_t)(i & 0xff);
for (int i = 0; i < 64; i++) idx[i] = (uint16_t)(63 - i); // reverse order

// Load 64 indices as e16 (index EEW = 16).
asm volatile("vsetvli %0, %1, e16, m1, ta, ma" : "=r"(vl) : "r"((uint64_t)64));
asm volatile("vle16.v v8, (%0)" ::"r"(idx));

// Indexed load with data SEW = e8 (SEW < index EEW) -> the failing case.
// 64 e16 indices span multiple NrLanes*64b words, so a mis-scaled (too small)
// fetch length starves the address generator and hangs a buggy Ara.
asm volatile("vsetvli %0, %1, e8, m1, ta, ma" : "=r"(vl) : "r"((uint64_t)64));
asm volatile("vluxei16.v v16, (%0), v8" ::"r"(mem));
asm volatile("vse8.v v16, (%0)" ::"r"(out) : "memory");

// Expected: out[i] = mem[idx[i]] = mem[63-i] = (63 - i) & 0xff
for (int i = 0; i < 64; i++)
printf("o%d=%d\n", i, (int)out[i]);
return 0;
}
34 changes: 34 additions & 0 deletions apps/vse_vstart_mask2/main.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
// Copyright 2026 ETH Zurich and University of Bologna.
// SPDX-License-Identifier: Apache-2.0
//
// Store-side validation for the #462 fix: masked vse16.v with vstart=4, vl=5,
// mask bit 4 active. Only element 4 should be stored; the rest of the output
// buffer keeps its preset sentinel. Mirrors the load probe on the store path.

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

static volatile uint16_t out[6] = {0x9990, 0x9991, 0x9992, 0x9993, 0x9994, 0x9995};
static volatile uint8_t mask_data[1] = {0x10}; // bit 4 active

int main() {
asm volatile("fence" ::: "memory");
asm volatile("vsetivli x0, 5, e16, m1, ta, ma");
asm volatile("vmv.v.x v13, %0" ::"r"((uint64_t)0x2000)); // v13[i] = 0x2000
asm volatile("vlm.v v0, (%0)" ::"r"(mask_data));
asm volatile("csrw vstart, 4");
asm volatile("vse16.v v13, (%0), v0.t" ::"r"(out) : "memory");
asm volatile("fence" ::: "memory");

// Expected: out[4] = 0x2000 = 8192 (stored); out[0..3] keep sentinels
// (below vstart), out[5] = 0x9995 (beyond vl).
for (int i = 0; i < 6; i++) printf("f%d=%d\n", i, (int)out[i]);
return 0;
}
37 changes: 37 additions & 0 deletions apps/vslideup_mask/main.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
// Copyright 2026 ETH Zurich and University of Bologna.
// SPDX-License-Identifier: Apache-2.0
//
// Spike-vs-Ara differential probe for issue #459 (masked vslideup.vx with a
// large stride uses the wrong mask window). vl=300 (e8), slide by 256, mask
// active only for elements 256..299. Static .data init + fence.

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

// Mask bits 256..299 active: bytes 32..36 = 0xFF (bits 256..295), byte 37 = 0x0F
// (bits 296..299). All other bits inactive.
static volatile uint8_t mask_data[40] = {[32 ... 36] = 0xFF, [37] = 0x0F};
static volatile uint8_t out[320];

int main() {
uint64_t vl;
asm volatile("vsetvli %0, %1, e8, m1, ta, ma" : "=r"(vl) : "r"((uint64_t)300));
asm volatile("vmv.v.x v1, %0" ::"r"(0xaaUL)); // background 0xaa
asm volatile("vid.v v2"); // v2[i] = i (mod 256)
asm volatile("vlm.v v0, (%0)" ::"r"(mask_data));
asm volatile("vslideup.vx v1, v2, %0, v0.t" ::"r"((uint64_t)256));
asm volatile("vse8.v v1, (%0)" ::"r"(out) : "memory");
asm volatile("fence" ::: "memory");

// Expected: e255 stays 0xaa(170); e256=src0=0; e257=src1=1; e299=src43=43
printf("o255=%d o256=%d o257=%d o299=%d\n",
(int)out[255], (int)out[256], (int)out[257], (int)out[299]);
return 0;
}
37 changes: 30 additions & 7 deletions hardware/src/lane/lane_sequencer.sv
Original file line number Diff line number Diff line change
Expand Up @@ -533,7 +533,14 @@ module lane_sequencer import ara_pkg::*; import rvv_pkg::*; import cf_math_pkg::
eew : EW64,
vtype : pe_req.vtype,
vl : pe_req.vl / NrLanes / ELEN,
vstart : vfu_operation_d.vstart,
// The mask is bit-packed (NrLanes*ELEN bits per VRF row) and fetched
// with eew=EW64, so the operand requester adds `vstart` directly to
// the row address (vstart >> (EW64-EW64)). The element-scaled
// vfu_operation_d.vstart (= vstart/NrLanes) over-counts and skips
// mask rows that still hold active bits, dropping masked elements
// with vstart >= NrLanes (e.g. segment-load micro-ops). Use the
// bit-packed mask-row index instead. (#462)
vstart : pe_req.vstart / (NrLanes * ELEN),
hazard : pe_req.hazard_vm | pe_req.hazard_vd,
target_fu : ALU_SLDU,
conv : OpQueueConversionNone,
Expand All @@ -554,7 +561,11 @@ module lane_sequencer import ara_pkg::*; import rvv_pkg::*; import cf_math_pkg::
conv : pe_req.conversion_vs2,
target_fu: MFPU_ADDRGEN,
vl : pe_req.vl / NrLanes,
scale_vl : pe_req.scale_vl,
// The index-operand vl above is already the index element count.
// Do not let the operand requester rescale it by the data SEW
// (vl<<vsew>>eew), which only matches when SEW==EEW and otherwise
// under-/over-fetches the indices, hanging the addrgen (#455).
scale_vl : 1'b0,
vstart : vfu_operation_d.vstart,
vtype : pe_req.vtype,
hazard : pe_req.hazard_vs2 | pe_req.hazard_vd,
Expand Down Expand Up @@ -599,7 +610,8 @@ module lane_sequencer import ara_pkg::*; import rvv_pkg::*; import cf_math_pkg::
eew : EW64,
vtype : pe_req.vtype,
vl : pe_req.vl / NrLanes / ELEN,
vstart : vfu_operation_d.vstart,
// Bit-packed mask row index, see the VFU_LoadUnit note above. (#462)
vstart : pe_req.vstart / (NrLanes * ELEN),
hazard : pe_req.hazard_vm | pe_req.hazard_vd,
target_fu : ALU_SLDU,
conv : OpQueueConversionNone,
Expand All @@ -612,6 +624,7 @@ module lane_sequencer import ara_pkg::*; import rvv_pkg::*; import cf_math_pkg::
operand_request[MaskM].vl += 1;
operand_request_push[MaskM] = !pe_req.vm;

// Store indexed
// Store indexed
// TODO: add vstart support here
operand_request[SlideAddrGenA] = '{
Expand All @@ -621,7 +634,9 @@ module lane_sequencer import ara_pkg::*; import rvv_pkg::*; import cf_math_pkg::
conv : pe_req.conversion_vs2,
target_fu: MFPU_ADDRGEN,
vl : pe_req.vl / NrLanes,
scale_vl : pe_req.scale_vl,
// See the load-indexed note above: the index-operand vl is already
// the index element count and must not be rescaled by the data SEW (#455).
scale_vl : 1'b0,
vstart : vfu_operation_d.vstart,
vtype : pe_req.vtype,
hazard : pe_req.hazard_vs2 | pe_req.hazard_vd,
Expand Down Expand Up @@ -726,9 +741,17 @@ module lane_sequencer import ara_pkg::*; import rvv_pkg::*; import cf_math_pkg::
operand_request[MaskM].vl += 1;

// SLIDEUP only uses mask bits whose indices are > stride
// Don't send the previous (unused) ones to the MASKU
if (pe_req.stride >= NrLanes * 64)
operand_request[MaskM].vstart += ((pe_req.stride >> NrLanes * ELEN) << NrLanes * ELEN) / 8;
// Don't send the previous (unused) ones to the MASKU.
// The mask is bit-packed: one VRF row across all lanes holds
// NrLanes*ELEN mask bits. The MaskM operand is fetched with
// eew=EW64, so the operand_requester adds `vstart` directly to the
// VRF row address (vstart >> (EW64-EW64) == vstart). Advance by the
// number of whole mask rows that lie entirely below the stride.
// The original code shifted by the value NrLanes*ELEN (=256) instead
// of $clog2(NrLanes*ELEN), and worked in bytes, both of which zeroed
// or mis-scaled the skip, so the MASKU read mask bits 0..stride-1. (#459)
if (pe_req.stride >= NrLanes * ELEN)
operand_request[MaskM].vstart += pe_req.stride / (NrLanes * ELEN);
end
VSLIDEDOWN: begin
// Since this request goes outside of the lane, we might need to request an
Expand Down