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 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; +} 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; +} 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); 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;