Where this shows up
Every CPU datapath — even a tiny one — needs a place to hold its general-purpose registers, and needs to read up to two operands (for a typical two-input ALU instruction) in the same cycle it decodes the instruction, while writeback from a previous instruction commits to a (possibly different) register in that same cycle. That’s exactly this module’s shape: 2 read ports for operand fetch, 1 write port for writeback, single-cycle (combinational) reads so the decode stage doesn’t stall waiting on a register value.
Interface
| Signal | Direction | Width | Description |
|---|---|---|---|
clk/rst |
input | — | Clock and synchronous, active-high reset (clears all registers to 0). |
ra1, ra2 |
input | $clog2(NUM_REGS) |
Read addresses for the two read ports. |
rd1, rd2 |
output | DW |
Combinational read data for ra1/ra2. |
we |
input | 1 | Write-enable for the single write port. |
wa |
input | $clog2(NUM_REGS) |
Write address. |
wd |
input | DW |
Write data. |
Read semantics: combinational, not registered
Unlike the RAM problems in this category, reads here are combinational (assign rd1 = regs[ra1];), not registered — this is what “single-cycle read” means in the spec: present an address and see the value in the very same cycle, with no extra latency. This is the opposite convention from a block-RAM-style memory, and it’s a deliberate, common choice for register files feeding directly into combinational ALU logic within one pipeline stage.
The write-then-read-same-register hazard
Because reads are combinational and writes are synchronous, if a read address matches the currently in-flight write address in the same cycle, the read sees the old value throughout that cycle — the new value only becomes visible starting the cycle after the write’s clock edge. This module does not implement same-cycle write-to-read forwarding/bypassing; if a consuming pipeline needs a value to be visible to a dependent read in the very same cycle it’s written, that forwarding logic belongs in the surrounding pipeline (this is exactly the “RAW hazard” a real CPU’s hazard-detection/forwarding unit handles), not inside the register file itself.
Correctness constraints
- All
NUM_REGSregisters must reset to0synchronously. - Both read ports must be able to read any two (possibly identical) registers independently and combinationally in the same cycle.
- A write must take effect on the following clock edge and be visible to all subsequent reads of that register.
- The write port must not interfere with unrelated registers — writing
wamust never disturb any register other thanregs[wa].