From a0b47dec99693c5cfe76470eee7d7bddf263784e Mon Sep 17 00:00:00 2001 From: Saurav Singh Date: Thu, 18 Jun 2026 05:39:23 +0000 Subject: [PATCH 1/5] :bug: [valu] Start reductions only when result queue is empty A reduction whose init state is entered while the VALU result queue still holds valid entries deadlocks the lane: the reduction datapath and the result queue contend for the same sequential state and neither makes progress. This reproduces with vredsum following a widening op that leaves results queued. Gate the reduction-init transition on result_queue_cnt_d == '0 so a new reduction only starts once previous results have drained. Refs #451 --- hardware/src/lane/valu.sv | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/hardware/src/lane/valu.sv b/hardware/src/lane/valu.sv index 623f3c40a..2c7fa6941 100644 --- a/hardware/src/lane/valu.sv +++ b/hardware/src/lane/valu.sv @@ -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); From d0d7fc5a36b1dc153f2ae52d52ab6bc1e87c6479 Mon Sep 17 00:00:00 2001 From: Saurav Singh Date: Thu, 18 Jun 2026 07:18:14 +0000 Subject: [PATCH 2/5] :white_check_mark: [apps] Add vredsum_deadlock differential reproducer Self-contained reproducer for the reduction deadlock (#451): a widening op (vwaddu.wv) that leaves results in the VALU result queue followed by vredsum.vs. Verified on the Verilated model (4 lanes, VLEN=4096): * with the valu.sv fix: PASS (reduction result = 384, ~0xb9c cycles) * without the fix: hangs (testbench timeout) -- deadlock reproduced Refs #451 --- apps/vredsum_deadlock/main.c | 63 ++++++++++++++++++++++++++++++++++++ 1 file changed, 63 insertions(+) create mode 100644 apps/vredsum_deadlock/main.c diff --git a/apps/vredsum_deadlock/main.c b/apps/vredsum_deadlock/main.c new file mode 100644 index 000000000..407090ace --- /dev/null +++ b/apps/vredsum_deadlock/main.c @@ -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 + +#ifdef SPIKE +#include "util.h" +#include +#elif defined ARA_LINUX +#include +#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; +} From 6c3a897fde086ef5474ba0615bbdb1b501734698 Mon Sep 17 00:00:00 2001 From: Saurav Singh Date: Thu, 18 Jun 2026 05:44:53 +0000 Subject: [PATCH 3/5] :bug: [vmfpu] Use non-comp latency for FP comparisons Floating-point comparisons (vmfeq/vmfne/vmflt/vmfle/vmfgt/vmfge) are non-computational CVFPU operations, in the same family as vfmin/vfmax/vfsgnj* which already map to LatFNonComp. They were missing from the latency function and fell through to the arithmetic, sew-based default latency. The wrong latency mis-aligns the mask-routing tag that travels alongside the result, so under pipeline pressure a comparison result is written to the VRF instead of being routed to the mask unit. Map [VMFEQ:VMFGE] to LatFNonComp. The enum range is contiguous and isolated (FP reductions before, integer comparisons after). NOTE: this is a pipeline-timing change and must be confirmed against the FP-compare regression suite in simulation before merging upstream. Refs #447 --- hardware/src/lane/vmfpu.sv | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/hardware/src/lane/vmfpu.sv b/hardware/src/lane/vmfpu.sv index c12598649..e783dcc52 100644 --- a/hardware/src/lane/vmfpu.sv +++ b/hardware/src/lane/vmfpu.sv @@ -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; From e06d03e60613fe9f6ad74b6eacf6d4683ecb27c5 Mon Sep 17 00:00:00 2001 From: Saurav Singh Date: Thu, 18 Jun 2026 07:40:26 +0000 Subject: [PATCH 4/5] :white_check_mark: [apps] Add vmfeq_route differential reproducer Self-checking reproducer for the FP-compare mask-routing desync (#447): long-latency FP ops (vfdiv + vfmacc) kept in flight across a vmfge that produces v0, then a masked vfadd consuming v0.t. Verified on the Verilated model (4 lanes, VLEN=4096): * with the vmfpu.sv fix: PASS (mask {0,0,1,1} applied -> {100,100,12,16}) * without the fix: FAIL (mask mis-routed; op runs unmasked -> {4,8,12,16}) Refs #447 --- apps/vmfeq_route/main.c | 73 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 73 insertions(+) create mode 100644 apps/vmfeq_route/main.c diff --git a/apps/vmfeq_route/main.c b/apps/vmfeq_route/main.c new file mode 100644 index 000000000..dd9b08dc9 --- /dev/null +++ b/apps/vmfeq_route/main.c @@ -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 + +#ifdef SPIKE +#include "util.h" +#include +#elif defined ARA_LINUX +#include +#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; +} From feac73116a2578b9eda800b0b114fd9be2cd62c2 Mon Sep 17 00:00:00 2001 From: Saurav Singh Date: Sat, 20 Jun 2026 09:36:13 +0000 Subject: [PATCH 5/5] :memo: [changelog] Document pr/lane-exec-fixes --- CHANGELOG.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 542eb153d..58dd5603e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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