RV64: csr_gen.py generates non-compiling BSV for PMP configs with more than 8 entries
Affected: csrbox master HEAD d9cedf0 (verified live, not a stale local pin)
File: csr_gen.py — the pmpaddr WARL branch and the mv_pmpcfg aggregator
Scope: RV64 with pmpentries > 8 only. RV32 and RV64 ≤ 8 entries are byte-identical (no behavior change).
Summary
On RV64 with more than 8 PMP entries (e.g. pmpentries: 16), csrbox emits BSV that does not compile (bsc T0004, then S0015). Two sites compute a per-register index/slice from the global PMP-entry number, which is only correct while every entry lives in pmpcfg0. RV64 packs 8 entries per pmpcfg and the pmpcfg registers are even-numbered (pmpcfg0 holds pmp0..7cfg, pmpcfg2 holds pmp8..15cfg), so entries 8..15 are mis-addressed.
Bug 1 — pmpaddr WARL references a non-existent pmpcfg register (bsc T0004)
csr_gen.py:1331:
pmpcfgnum = int(pmpaddrnum / int(xlen/8)) # RV64: pmpaddr8 -> 8/8 = 1 -> "pmpcfg1"For RV64 the divisor is 8, so entries 8..15 map to pmpcfg1 — which does not exist in RV64 (only the even-numbered pmpcfg0/2/... are defined). The generated pmpaddr8 NAPOT read-mask then references rg_pmpcfg1_pmp8cfg, and bsc fails: (T0004) Unbound variable rg_pmpcfg1_pmp8cfg. Correct for ≤ 8 (index 0); broken for ≥ 8.
Bug 2 — mv_pmpcfg slices the cfg register with a global bit offset (bsc S0015)
' lv_pmpcfg[{0}] = {1}.mv_csr_pmpcfg{2}[{3}:{4}];'.format(count, cfg_grp, cfg_reg, cfgnum*8+7, cfgnum*8)pmpcfg2 is a separate Bit#(xlen) holding entries 8..15 at bits [(n-8)*8 ...], but the slice uses the global cfgnum*8 → pmpcfg2[71:64] .. [127:120] on a 64-bit value → bsc (S0015) index 127 out-of-range (bit extraction). The slice must be register-local.
Reproduce
(1) git clone https://gitlab.com/shaktiproject/cores/csrbox (HEAD d9cedf0). (2) Drive csrbox from a 16-entry RV64IMAC C-Class config (pmpentries: 16, xlen: 64). (3) Generate the CSR BSV and run bsc on the resulting core. Observed: (T0004) Unbound variable rg_pmpcfg1_pmp8cfg; after patching Bug 1 only, then (S0015) index 127 out-of-range (bit extraction).
Fix
# Bug 1: RV64 pmpcfg registers are even-numbered -> *2 on RV64 (RV32 unchanged)
pmpcfgnum = int(pmpaddrnum / int(xlen/8)) * (2 if int(xlen) == 64 else 1)
# Bug 2: register-local slice (cfgnum % entries-per-reg); entries 0..7 unchanged
_epr = xlen // 8 # 4 (RV32), 8 (RV64)
_loc = cfgnum % _epr
' lv_pmpcfg[{0}] = {1}.mv_csr_pmpcfg{2}[{3}:{4}];'.format(count, cfg_grp, cfg_reg, _loc*8+7, _loc*8)No change to existing, building configurations. On RV32, *1 and cfgnum % 4 make the arithmetic bit-identical to the original. On RV64 with ≤ 8 entries, all entries live in pmpcfg0 (index 0, local offset == global offset), so the output is byte-identical. The change takes effect only for entry numbers ≥ 8 on RV64, which previously could not be generated at all (the build failed).
Verify
Reproduced on a 16-entry RV64IMAC SHAKTI C-Class config: stock csrbox → bsc T0004, then (after Bug 1) S0015. With the patch, csrbox reports "programmable pmp_entries 16", bsc generates mkccore_axi4.v cleanly, the SoC elaborates and boots (smoke PASS). RV32 and RV64-≤8 regressions are unchanged.
A ready patch (clean git apply --check at HEAD d9cedf0) is available — happy to open an MR if preferred.