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
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,15 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.

## [Unreleased]

### Added

- Add `vredsum_deadlock` app: differential reproducer for the reduction deadlock (#451)
- Add `vmfeq_route` app: differential reproducer for the FP-compare mask-routing desync (#447)

### Fixed

- Use non-computational FPU latency for floating-point comparisons, fixing a mask-routing desync (#447)
- Start integer reductions only when the VALU result queue is empty, fixing a `vredsum` deadlock (#451)
- 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
73 changes: 73 additions & 0 deletions apps/vmfeq_route/main.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
// 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.

// Differential reproducer for issue #447 (FP-compare mask-routing desync).
//
// FP comparisons are non-computational FPU ops, but without the vmfpu.sv fix
// they use the arithmetic (sew-based) latency. Under pipeline pressure (a
// preceding FP op still in flight) the mask-routing tag is sampled at the wrong
// cycle, so the comparison result is written to the VRF instead of being routed
// to the mask unit. A subsequent masked FP op then uses a wrong mask.
//
// Sequence (mirrors the report): filler vfadd -> vmfge producing v0 ->
// masked vfadd using v0.t. Self-checks the masked output.

#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 float src[4] = {2.0f, 4.0f, 6.0f, 8.0f};
static volatile uint32_t out[4];

int main() {
uint64_t vl;
float thresh = 5.0f;

// mask-undisturbed so inactive elements keep their old (defined) value.
asm volatile("vsetivli %0, 4, e32, m1, ta, mu" : "=r"(vl));

asm volatile("vle32.v v6, (%0)" ::"r"(src));
asm volatile("vfmv.v.f v4, %0" ::"f"(100.0f)); // base for masked op
asm volatile("vfmv.v.f v24, %0" ::"f"(7.0f)); // filler operands
asm volatile("vfmv.v.f v26, %0" ::"f"(3.0f));

// Heavy, long-latency pressure: keep the FPU pipe busy across the compare.
asm volatile("vfdiv.vv v24, v24, v26"); // long latency in flight
asm volatile("vfmacc.vv v24, v24, v26");
asm volatile("vfmacc.vv v24, v24, v26");
asm volatile("vmfge.vf v0, v6, %0" ::"f"(thresh)); // mask = src >= 5 -> {0,0,1,1}
asm volatile("vfadd.vv v4, v6, v6, v0.t"); // v4[i] = mask? 2*src : 100.0
asm volatile("vse32.v v4, (%0)" ::"r"(out) : "memory");

// Expected: {100.0, 100.0, 12.0, 16.0}
uint32_t e0 = 0x42C80000u; // 100.0f
uint32_t e2 = 0x41400000u; // 12.0f
uint32_t e3 = 0x41800000u; // 16.0f
if (out[0] == e0 && out[1] == e0 && out[2] == e2 && out[3] == e3) {
printf("vmfeq_route: PASS (out[2]=0x%x out[3]=0x%x)\n", out[2], out[3]);
return 0;
}
printf("vmfeq_route: FAIL (out={0x%x,0x%x,0x%x,0x%x})\n",
out[0], out[1], out[2], out[3]);
return 1;
}
63 changes: 63 additions & 0 deletions apps/vredsum_deadlock/main.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
// 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.

// Differential reproducer for issue #451 (vredsum deadlock).
//
// A reduction (vredsum.vs) issued right after a widening op (vwaddu.wv) that
// leaves results in the VALU result queue deadlocks the lane: the reduction-init
// state and the result queue contend for the same sequential state. With the fix
// in valu.sv (gate reduction-init on result_queue_cnt_d == '0) the sequence
// completes; without it, the simulation hangs (caught by the testbench timeout).
//
// Reaching the final print => PASS (no deadlock).

#include <stdint.h>

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

int main() {
uint64_t vl;
uint64_t res;

// Initialize the wide destination group v8 (e64, m2 -> v8..v9).
asm volatile("vsetvli %0, zero, e64, m2, ta, ma" : "=r"(vl));
asm volatile("vmv.v.i v8, 1");

// Narrow source v11 (e32, m1).
asm volatile("vsetvli %0, zero, e32, m1, tu, ma" : "=r"(vl));
asm volatile("vmv.v.i v11, 2");

// Widening add: wide v8 += narrow v11. Leaves results queued in the VALU.
asm volatile("vwaddu.wv v8, v8, v11");

// Reduction immediately after, on the wide group.
asm volatile("vsetvli %0, zero, e64, m2, ta, ma" : "=r"(vl));
asm volatile("vmv.s.x v10, zero");
asm volatile("vredsum.vs v8, v8, v10");

// If we get here, no deadlock occurred.
asm volatile("vmv.x.s %0, v8" : "=r"(res));
printf("vredsum_deadlock: PASS (reduction result = %lu)\n", res);

return 0;
}
8 changes: 6 additions & 2 deletions hardware/src/lane/valu.sv
Original file line number Diff line number Diff line change
Expand Up @@ -786,8 +786,12 @@ module valu import ara_pkg::*; import rvv_pkg::*; import cf_math_pkg::idx_width;
if (is_reduction(vinsn_commit.op)) alu_red_complete_d = 1'b1;

// Initialize counters and alu state if needed by the next instruction
// After a reduction, the next instructions starts after the reduction commits
if (is_reduction(vinsn_queue_q.vinsn[vinsn_queue_d.issue_pnt].op) && (vinsn_queue_d.issue_cnt != '0)) begin
// After a reduction, the next instructions starts after the reduction commits.
// Only enter the reduction-init state once the ALU result queue has drained:
// starting a new reduction while results are still queued deadlocks the unit,
// because the reduction datapath and the result queue contend for the same state.
if (is_reduction(vinsn_queue_q.vinsn[vinsn_queue_d.issue_pnt].op) && (vinsn_queue_d.issue_cnt != '0)
&& (result_queue_cnt_d == '0)) begin
// Initialize reduction-related sequential elements
first_op_d = 1'b1;
reduction_rx_cnt_d = reduction_rx_cnt_init(NrLanes, lane_id_i);
Expand Down
6 changes: 6 additions & 0 deletions hardware/src/lane/vmfpu.sv
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,12 @@ module vmfpu import ara_pkg::*; import rvv_pkg::*; import fpnew_pkg::*;
[VFREDMIN:VFREDMAX]: fpu_latency = LatFNonComp;
[VFCVTXUF:VFCVTFF]: fpu_latency = LatFConv;
[VFMIN:VFSGNJX]: fpu_latency = LatFNonComp;
// FP comparisons are non-computational FPU ops (CVFPU NONCOMP group),
// like VFMIN..VFSGNJX above. Without this case they fall through to the
// arithmetic (sew-based) latency, which mis-aligns the mask-routing tag
// under pipeline pressure and sends the comparison result to the VRF
// instead of the mask unit.
[VMFEQ:VMFGE]: fpu_latency = LatFNonComp;
default: begin
case (sew)
EW64: fpu_latency = LatFCompEW64;
Expand Down