rtl/register_file.sv — 32×32 Register File
The RV32 integer register file: 31 physical 32-bit registers (x1–x31), with x0 hardwired to zero and never stored. Synchronous write, asynchronous read with same-cycle write-then-read bypass forwarding.
Where it fits
Instantiated once by riscv_core.sv as rf_unit, read in ID (rs1/rs2) and written in WB (rd/wd). Testbenches reach it directly for register assertions via the hierarchical path dut.cpu_chiplet_inst.cpu_core_inst.rf_unit.rf[i].
Ports
| Direction | Name | Width | Description |
|---|---|---|---|
| in | clk |
1 | Clock |
| in | reg_write |
1 | Write enable (from WB stage) |
| in | rs1, rs2 |
5, 5 | Read register indices |
| in | rd |
5 | Write register index |
| in | wd |
32 | Write data |
| out | rd_data1, rd_data2 |
32, 32 | Read data for rs1/rs2 |
Functionality
Storage is logic [31:0] rf [31:1] — index 0 is never allocated; both read ports return 32'b0 unconditionally when the requested index is 0.
Bypass forwarding: if the same cycle writes rd and reads that same index (reg_write && rd == rs1), the read port returns wd directly instead of the pre-write rf[rs1] value — i.e. write-then-read same-cycle semantics, avoiding a structural hazard that would otherwise need an extra forwarding path in riscv_core.sv.
Write: synchronous, posedge clk, gated on reg_write && rd != 0.
Example
cycle N: reg_write=1, rd=x9, wd=42, rs1=x9 (WB writing x9, ID reading x9 same cycle)
rd_data1 = 42 (bypass — not the stale rf[9])
cycle N+1: rf[9] = 42 (committed to storage)
rd_data1 (if rs1=x9 again) = rf[9] = 42
Related
- riscv_core.md — the only instantiation site
- cpu.md — prose writeup with WB-stage context