Add feature request from - RV32E/EC configuration #64 - branch: Dev/rv32e#162
Open
lcapossio wants to merge 13 commits intoolofk:mainfrom
Open
Add feature request from - RV32E/EC configuration #64 - branch: Dev/rv32e#162lcapossio wants to merge 13 commits intoolofk:mainfrom
lcapossio wants to merge 13 commits intoolofk:mainfrom
Conversation
Adds a WITH_RV32E parameter (default 0, fully backwards-compatible)
that configures SERV to use 16 GPRs (x0-x15) instead of 32,
halving the register file RAM depth for ASIC area savings.
Address space with RV32E+CSR uses 5-bit addresses (0-19):
GPRs 0-15, then mscratch=16, mtvec=17, mepc=18, mtval=19
Changes:
- serv_rf_ram: add gpr_regs param, depth formula uses it
- serv_rf_ram_if: add gpr_regs param, raw = $clog2(gpr_regs+csr_regs)
- serv_rf_if: add WITH_RV32E param, update port widths and
use generate blocks for CSR address encoding
- serv_rf_top: add WITH_RV32E param, update RF_L2D, wire widths,
propagate gpr_regs/WITH_RV32E to sub-modules
- serv_top: add WITH_RV32E param, update port widths,
propagate to serv_rf_if
- serv.core: expose WITH_RV32E as a top-level parameter
The x0 zeroing logic in serv_rf_ram automatically adjusts since
it derives the register index field from $clog2(depth).
- Remove [0:0] width constraint from WITH_RV32E parameter so it can be used in arithmetic port dimension expressions like WITH_CSR - Use (|WITH_RV32E) reduction OR for boolean/ternary contexts to avoid WIDTHTRUNC warnings when testing a 32-bit parameter as a condition - Remove nested generate keyword inside generate block in serv_rf_if.v - Add WITH_RV32E to lint target parameters in serv.core - Update verilator_waiver.vlt: fix line numbers (shifted +1 by new param), update UNUSED→UNUSEDSIGNAL for Verilator 5.x rule names, add waivers for unused i_rs1_raddr[4]/i_rs2_raddr[4] bits in RV32E mode All four lint checks now pass (RV32I W=1, RV32I W=4, RV32E W=1, RV32E W=4)
- servant.v: add with_rv32e parameter; adjust rf_l2d and gpr_regs for 16-register file; propagate to servile and serv_rf_ram - servile.v: add with_rv32e parameter; adjust regs formula for 16-GPR count; propagate gpr_regs to serv_rf_ram_if and WITH_RV32E to serv_top - bench/servant_sim.v: add with_rv32e parameter; pass to servant - servant.core: expose with_rv32e as a FuseSoC vlogparam; add to verilator_tb target parameter list - sw/hello_uart_rv32e.hex: add RV32E firmware (word-per-line format for $readmemh, compiled from hello_uart.S with rv32e march)
- Makefile: add specific rules for RV32E targets using -march=rv32e
(-mabi=ilp32e) so the toolchain enforces x0-x15 register constraint:
* hello_uart_rv32e.elf: builds from hello_uart.S with rv32e march
* trap_test_rv32e.elf: builds from trap_test_rv32e.S with rv32e_zicsr
- trap_test_rv32e.S: new firmware that exercises the RV32E CSR address
paths (mtvec at RF[17], mepc at RF[18]) through a complete trap cycle:
la t0, trap_handler / csrw mtvec, t0 -- set trap vector
ecall -- trigger trap
[trap_handler]: csrr/addi/csrw mepc -- advance past ecall
mret -- return; PC = ecall+4
sw zero, 0(HALT_ADDR) -- exit simulation
Simulation reaches 'Test complete' well within 500000 cycles, proving
mtvec and mepc RF addresses are correct for the RV32E register layout.
Adds a self-contained RISCOF configuration that runs the riscv-arch-test
RV32E suite against SERV with WITH_RV32E=1 and compares signatures with
the SAIL reference model.
New files:
- config_rv32e.ini: top-level RISCOF config; DUT=serv_rv32e,
reference=sail_cSim (same reference model as RV32I run)
- plugin-serv/serv_isa_rv32e.yaml: ISA spec for RV32E_Zicsr;
misa reset-val=0x40000010 (MXL=01, E-bit=1)
- plugin-serv/riscof_serv_rv32e.py: DUT plugin derived from
riscof_serv.py with three key changes:
* Builds servant with --with_rv32e=1 into servant_test_rv32e/
(separate work root to avoid clobbering the RV32I build)
* Uses -mabi=ilp32e and constructs isa as rv32e[_zicsr]
* Includes env_rv32e/ instead of env/
- plugin-serv/env_rv32e/link.ld: identical to env/link.ld (same
SERV memory layout, no address changes needed for RV32E)
- plugin-serv/env_rv32e/model_test.h: RV32E-safe version of the
compliance test macros. The RV32I version uses a6 (x16) as a
scratch register in RVMODEL_HALT to hold the constant 10 for
hex-nibble comparison. In RV32E mode x16 maps silently to x0
(always 0), so 'blt a5, a6' would never branch and every nibble
would be mis-encoded. Fixed by replacing:
li a6, 10 / blt a5, a6, target
with:
slti t1, a5, 10 / bnez t1, target
using t1 (x6), which is within the x0-x15 RV32E register file.
Run with:
riscof run --config=$SERV/verif/config_rv32e.ini --suite=riscv-arch-test/riscv-test-suite/rv32e_m/E --env=riscv-arch-test/riscv-test-suite/env
Five issues resolved while running the compliance suite against SAIL: - serv_isa_rv32e.yaml: fix ISA canonical ordering (RV32E_Zicsr → RV32EZicsr); the underscore must only appear between consecutive Z-extensions, not before the first one - riscof_serv_rv32e.py: increase memsize 8 MiB → 16 MiB to accommodate jal-01 (the JAL test binary is ~14.7 MiB due to ±1 MiB jump offsets) - riscof_serv_rv32e.py: fix DUT signature filename from DUT-serv.signature to DUT-serv_rv32e.signature to match the plugin class name that RISCOF uses when looking up the result - riscof_sail_cSim.py: use ilp32e ABI when the ISA contains the E extension; -mabi=ilp32 is incompatible with -march=rv32e - plugin-sail_cSim/env/model_test.h: replace t5 (x30) with t0 (x5) in RVMODEL_HALT; x30 is outside the RV32E register file and is rejected by the assembler under -march=rv32e With all five fixes applied, all 37 rv32e_m/E tests pass against the SAIL reference model.
Add a short section describing the WITH_RV32E parameter, how to simulate and lint with RV32E enabled, and a pointer to the RISCOF compliance setup in verif/.
Enable the C extension alongside RV32E for ~25% code size reduction:
- serv_isa_rv32e.yaml: ISA -> RV32ECZicsr, misa reset-val 0x40000014
- riscof_serv_rv32e.py: detect C in ISA spec, build servant with --compressed=1
- sw/Makefile: add hello_uart_rv32ec.elf target (-march=rv32ec -mabi=ilp32e)
- sw/hello_uart_rv32ec.{elf,hex}: pre-built RV32EC firmware
- README: document --compressed=1 usage with RV32E
No RTL changes needed: COMPRESSED and WITH_RV32E parameters already compose
independently; serv_compdec.v delegates RV32E register checks to the main decoder.
Fix 10 issues identified during code review:
1. rtl/serv_compdec.v: remove false claim that x16-x31 traps on RV32E;
document as known limitation (aliasing to x0-x15, no trap raised)
2. verif/plugin-serv/env_rv32e/model_test.h: replace x31 (outside RV32E
range) with x15 in LOCAL_IO_WRITE_STR
3. sw/hello_uart_rv32ec.elf: remove binary from git; add .gitignore
covering *.elf, build/, riscof_work/ and other generated outputs
4. verif/plugin-serv/riscof_serv_rv32e.py: move servant build from
initialise() to build() so --compressed=1 is only passed when C
appears in the ISA spec yaml
5. rtl/{serv_rf_if,serv_rf_top,serv_top}.v, servant/servant.v,
servile/servile.v, bench/servant_sim.v: declare WITH_RV32E/with_rv32e
as [0:0] for consistent 1-bit typing (prevents silent misbehaviour
if a non-0/1 value is passed)
6. rtl/serv_rf_top.v: add comment explaining why RF_L2D repeats the
(|WITH_RV32E)?16:32 ternary instead of using the GPR_REGS localparam
7. .github/workflows/lint.yml: add lint runs with --WITH_RV32E=1 and
--WITH_RV32E=1 --WITH_CSR=0 for both W=1 and W=4
8. .github/workflows/ci.yml: add RV32EC compliance test step using
config_rv32e.ini against the C test suite
9. sw/Makefile: add trap_test_rv32ec.elf target (same source as
trap_test_rv32e.S, compiled with -march=rv32ec_zicsr); add hex
10. README.md: clarify that FPGA targets see no area reduction from
RV32E (RF fits in one BRAM block); ASIC/DFF targets save ~36% cells
Add hardware detection of illegal register access (x16-x31) in RV32E mode, triggering an illegal instruction trap (mcause=2) with the faulting instruction written to mtval. The detection covers all instruction formats including compressed instructions via the expanded 32-bit form. Key changes: - serv_compdec: expose o_illegal for truly illegal compressed encodings (gated to exclude normal 32-bit instructions passing through) - serv_csr: set mcause=2 on illegal instruction, with interrupt priority preserved (o_new_irq takes precedence) - serv_rf_if: route illegal flag and faulting instruction to mtval - serv_state: include i_illegal in trap condition - serv_top: RV32E register decoder, illegal instruction pipeline registers, and mtval shift register for serializing the faulting instruction
- bench/servant_tb.v: add missing with_rv32e and compressed parameters, wire them through to servant_sim (fixes FuseSoC sim target for RV32E) - servant.core: add compressed parameter to sim target; add with_rv32e to all FPGA board targets for consistent RV32E support - sw/trap_test_rv32e.S: add .balign 4 before trap_handler to ensure mtvec is 4-byte aligned (compressed builds placed the handler at 0x1A, causing SERV to jump to 0x18 instead) - sw/trap_test_rv32ec.hex: rebuilt with alignment fix - README.md: document illegal instruction trapping behavior and Icarus sim commands for RV32E/RV32EC
…arnings Replace 5-bit instr_rs1/rs2/rd wire declarations (where only bit[4] was used) with direct single-bit indexing into i_wb_rdt. Fixes three Verilator -Wall warnings on the RV32E register-range check logic.
serv_rf_if: In RV32E mode the RF only addresses x0-x15, so bit[4] of i_rs1_raddr/i_rs2_raddr is intentionally unused here — illegal register detection (x16-x31 trap) happens upstream in serv_top. Add a local _unused_rv32e_rs_msb wire; the _unused_ prefix suppresses UNUSEDSIGNAL and UNUSED across all Verilator versions (4.x and 5.x). ci.yml: rv32i_m/C tests require the I base ISA and selected no tests against RV32ECZicsr. Switch to rv32e_m/C (the correct E+C suite) and add rv32e_m/E to cover the base integer tests.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Add RV32E and RV32EC support
Implements the RV32E ISA variant (16-register subset, x0–x15 only) and the RV32EC extension (RV32E + compressed 16-bit instructions).
New RV32E and RV32EC RISCOF steps added to CI. Local Icarus simulations of trap_test_rv32e and trap_test_rv32ec confirm correct trap entry, mcause/mtval values, and mret resume.
Passes the existing RV32I/IM/IC/Zifencei/Privilege RISCOF suites unchanged
Addressing issue #64