BUG-010: test_rv32m_control_flow_regression.asm had a wrong MUL encoding and no halt sentinel
| Field | Value |
|---|---|
| Severity | LOW |
| Component | Testbench |
| File(s) | tb/test_rv32m_control_flow_regression.asm |
| Status | Fixed |
| Date | 2026-07-23 |
Symptom
This file is not wired into the standard 15-test regression suite — it's only reachable via a manual ASM_TEST= override — so it appears to have never actually been run end to end. Running it (as part of validating BUG-008/BUG-009 against a second control-flow test) hung indefinitely. After adding the missing halt word (below), it ran but failed its own check: x19 actual=9 expected=20.
Root Cause
Two independent hand-encoding mistakes in the pipe-format hex, neither caught because nothing ever exercised them:
- No halt sentinel. The file ends with
addi x0, x0, 0 | 00000013 | cpu | x19==20and nothing after it. The core has no notion of "end of program" other than fetching the literaljal x0, 0(HALT_WORD) bit pattern — without it,if_pcruns past the end of the loaded program and the testbench'swait_for_halt()blocks forever. - Wrong
mulencoding.mul s3, s1, s2was hand-encoded as0x012489B3, which hasfunct7=0x00. Real RV32MMULrequiresfunct7=0x01(the R-type M-extension marker) — withfunct7=0x00this decodes as plainADDinstead.s1=5, s2=4:ADDgivess3=9(5+4), matching the observed failure exactly;MULshould have givens3=20(5×4). The file's own check annotation (x19==20) was written assuming themulencoding was correct, so nothing flagged the mismatch — the author's expected value and the (wrong) hex were never cross-checked against each other by anything.
Fix
Rather than hand-patch two more hex values in a file that already had two hand-encoding bugs, the file was regenerated from real assembly source using the new assembler (scripts/riscv_asm.py, see toolchain.md):
addi s1, x0, 5 # check: s1==5
addi s2, x0, 4 # check: s2==4
mul s3, s1, s2 # check: s3==20
j path_a # Path A: unconditional jump, skip the poison line
addi s3, x0, 99 # skipped — if executed, s3 becomes 99 (fail)
path_a:
bne s1, s2, path_b # s1(5) != s2(4) -> must branch
addi s3, x0, 77 # skipped — if executed, s3 becomes 77 (fail)
path_b:
addi x0, x0, 0 # check: s3==20
halt
tb/test_rv32m_control_flow_regression.s is the new source; tb/test_rv32m_control_flow_regression.asm is regenerated from it (python3 scripts/riscv_asm.py tb/test_rv32m_control_flow_regression.s -o tb/test_rv32m_control_flow_regression.asm) rather than hand-edited. All 3 checks now pass, and this also re-validates the BUG-009 fix against a second control-flow shape (JAL immediately followed by a poison instruction, then BNE).
Notes
This file — plus the INT16 NPU block in cpu_peak_tests.asm (see BUG-011) — is the second and third instance found in this session of a hand-computed hex value silently disagreeing with the comment/mnemonic next to it. This class of bug is structural: nothing re-derives the hex from the mnemonic, so nothing can catch the two drifting apart. The assembler exists specifically to remove this failure mode going forward — see toolchain.md.