Skip to content

§ 4.6.1. Operand Forwarding

Forwarding supplies an older instruction's result before its register-file write becomes visible to a younger instruction. It does not read back the younger instruction's own writes.

Component Description Structural Behavioral
RV32IForwardingUnit Two register comparisons with newest-producer priority Yes Yes
RV32IDecodeStage Decode/register read with WB bypass and load-use detection Yes Yes
RV32IExecuteStage Forwarded operands around the shared execution datapath Yes Yes
RV32IWritebackBypass Internal Decode selector for a matching, valid WB destination Yes Via enclosing stage
RV32IForwardedOperands Internal selector and two operand muxes Yes Via enclosing stage
RV32IExecutePacketStatus Internal terminal/redirect qualification gates Yes Via enclosing stage

Forwarding-unit IO pins

Pin Direction Width Description
ID_EX_RS1, ID_EX_RS2 Input 5 each Execute's source register numbers
EX_MEM_RD, MEM_WB_RD Input 5 each Producer destinations
EX_MEM_VALID, MEM_WB_VALID Input 1 each Producer slot contains an instruction
EX_MEM_REG_WRITE, MEM_WB_REG_WRITE Input 1 each Producer writes a register
EX_MEM_RESULT_READY Input 1 EX/MEM has a usable ALU/link result, not a pending load
FORWARD_A, FORWARD_B Output 2 each 0: captured operand; 1: EX/MEM; 2: MEM/WB; 3 unused

Behavior

For each source, a matching valid EX/MEM writer wins over MEM/WB. If that newer producer is not ready, select the captured operand—not an older WB value for the same register. The load-use interlock prevents the consumer from using that unavailable value in normal execution. Destination x0 never forwards.

The same selected RS2 feeds both ALU input selection and store data. Forwarded operands also feed branch comparisons and JALR target calculation. Decode uses a dedicated WB bypass: two source-register comparisons and two 2:1 data muxes. WB's value can therefore reach an instruction entering ID/EX on the very edge that commits WB.

C++ example

Producer.ready matters only for EX/MEM. Packet, the binary ALU, target selection and status checks come from 4.5.4. Captured x0 operands are zero by the register-file contract.

struct Producer { bool valid,write,ready; unsigned rd; uint32_t value; };
inline unsigned forward_select(unsigned source,Producer ex,Producer wb) {
    source&=31;
    if(ex.valid&&ex.write&&(ex.rd&31)&&((ex.rd&31)==source)) return ex.ready?1:0;
    if(wb.valid&&wb.write&&(wb.rd&31)&&((wb.rd&31)==source)) return 2;
    return 0;
}
inline uint32_t forwarded(unsigned source,uint32_t captured,Producer ex,Producer wb) {
    switch(forward_select(source,ex,wb)) {
        case 1:return ex.value; case 2:return wb.value; default:return captured;
    }
}
inline baseline_example::StageResult execute(Packet p,Producer ex,Producer wb) {
    using namespace rv32i::pipeline_encoding;
    p.rs1_value=forwarded(rs1(p.addresses),p.rs1_value,ex,wb);
    p.rs2_value=forwarded(rs2(p.addresses),p.rs2_value,ex,wb);
    return baseline_example::execute(p); // The same ALU/target/status contract as 4.5.4.
}

Stage-interface changes

The common packet inputs/outputs retain the baseline Decode and Execute field meanings. TERMINAL on the advanced Execute interface is named PRETERMINAL; the baseline's CONTROL_FLOW output is replaced by the more specific signals below.

Stage Additional or changed pins Width Meaning
Decode input WB_VALID, WB_REG_WRITE, WB_REGISTER_WRITE 1 each WB packet/write intent for bypass; qualified edge write for the register file
Decode input EX_VALID, EX_CONTROL, EX_REGISTER_ADDRESSES 1, 32, 32 Current Execute producer for load-use detection
Decode output LOAD_USE_HAZARD, TERMINAL 1 each Interlock and valid early-terminal instruction
Execute input EX_MEM_VALID, EX_MEM_REG_WRITE, EX_MEM_RESULT_READY, MEM_WB_VALID, MEM_WB_REG_WRITE 1 each Forwarding qualifications
Execute input EX_MEM_RD, MEM_WB_RD 5 each Producer destinations
Execute input EX_MEM_VALUE, MEM_WB_VALUE 32 each Forwardable values
Execute output IS_BRANCH, BRANCH_TAKEN 1 each Conditional-branch kind and actual condition; validity is qualified by recovery
Execute output REDIRECT, PRETERMINAL 1 each Valid selected control transfer; valid early-terminal instruction
Execute output REDIRECT_PC 32 Actual next PC, including fallthrough
Execute output CONTROL_TARGET, CONTROL_TAKEN 32, 1 Candidate branch/jump destination and its selection, before the next-PC mux; paired with OUT_PC_PLUS_4 for parallel recovery comparisons

Decode retains CLK, RST, WB_RD<5> and WB_VALUE<32>. Its register-file write input is WB_REGISTER_WRITE, not the baseline's REGISTER_WRITE. Execute remains combinational. The core uses prediction mismatch, not Execute's compatibility REDIRECT output, to decide whether to flush.

Implementation

Structural implementation

Execute's main groups are FORWARDED_OPERANDS, DATAPATH, NEXT_PC, and PACKET_STATUS. DATAPATH is the same selectable single-cycle execution component used by the baseline: one ALU32 for arithmetic, address calculation, comparison, and JAL/JALR targets. The separate PC+immediate adder is needed alongside a conditional-branch comparison; PC+4 uses an incrementer. There is no duplicate RS1+immediate target adder.

The forwarding selector uses five-bit equality networks, nonzero checks and priority gates. Decode's WB_BYPASS child is RV32IWritebackBypass: it compares RS1 and RS2 with WB_RD (five bits each) and asserts its one-bit SELECT_A/SELECT_B outputs only when WB_VALID, WB_REG_WRITE, a nonzero destination, and the corresponding match hold. The two 32-bit muxes then select WB_VALUE. Muxes select values without storing them. Packet/status rewiring stays separate from the datapath.

Behavioral implementation

The selector computes the same priority equations; the stage evaluates forwarded operands through the equivalent ALU and next-PC rules. The two internal grouping components have no separately registered behavioral variant: selecting the enclosing stage's behavior replaces their assembly.

Examples

The selector rows cover both sources, competing producers, x0, invalid producers, and a younger unready producer hiding WB. Execute rows also show forwarded branch operands and shared-ALU JAL/JALR targets.

All 7 recorded rows of RV32IForwardingUnitTest, in checkpoint order. Outputs are test expectations verified against the simulation. Time is the absolute settled checkpoint time in simulator units, not milliseconds or CPU cycles.

Default scenario profile: rv32i-component-preview-preview / structural (a5a83c6c721cd53c). Changing fidelity or the circuit can change these times.

Checkpointzero-based number · timeline label Timesimulator units EX_MEM_RDInput · 5 bit EX_MEM_REG_WRITEInput · 1 bit EX_MEM_RESULT_READYInput · 1 bit EX_MEM_VALIDInput · 1 bit ID_EX_RS1Input · 5 bit ID_EX_RS2Input · 5 bit MEM_WB_RDInput · 5 bit MEM_WB_REG_WRITEInput · 1 bit MEM_WB_VALIDInput · 1 bit FORWARD_AExpected output · 2 bit FORWARD_BExpected output · 2 bit
#0no-producer7 0x00 0 0 0 0x01 0x02 0x00 0 0 0x0 0x0
#1ex-forwards-both17 0x05 1 1 1 0x05 0x05 0x00 0 0 0x1 0x1
#2mem-forwards-both26 0x00 0 0 0 0x07 0x07 0x07 1 1 0x2 0x2
#3newest-producer-wins36 0x08 1 1 1 0x08 0x08 0x08 1 1 0x1 0x1
#4unready-newest-blocks-stale-value40 0x09 1 0 1 0x09 0x09 0x09 1 1 0x0 0x0
#5independent-operands50 0x0A 1 1 1 0x0A 0x0B 0x0B 1 1 0x1 0x2
#6x0-never-forwards59 0x00 1 1 1 0x00 0x00 0x00 1 1 0x0 0x0

Scroll within the table for all rows and pins. Buses use hexadecimal; values containing X/Z use MSB-first binary. Inputs show the declared values in effect at each checkpoint, including retained inputs. Sequential tests also replay preparation changes between checkpoints, such as lowering the clock. Use the matching checkpoint label in the simulation below.

All 7 recorded rows of RV32IExecuteStageTest, in checkpoint order. Outputs are test expectations verified against the simulation. Time is the absolute settled checkpoint time in simulator units, not milliseconds or CPU cycles.

Default scenario profile: rv32i-component-preview-preview / structural (1766858467d25ef7). Changing fidelity or the circuit can change these times.

Checkpointzero-based number · timeline label Timesimulator units EX_MEM_RDInput · 5 bit EX_MEM_REG_WRITEInput · 1 bit EX_MEM_RESULT_READYInput · 1 bit EX_MEM_VALIDInput · 1 bit EX_MEM_VALUEInput · 32 bit IN_CONTROLInput · 32 bit IN_FETCH_STATUSInput · 32 bit IN_IMMEDIATEInput · 32 bit IN_INSTRUCTIONInput · 32 bit IN_PCInput · 32 bit IN_REGISTER_ADDRESSESInput · 32 bit IN_RS1_VALUEInput · 32 bit IN_RS2_VALUEInput · 32 bit IN_VALIDInput · 1 bit MEM_WB_RDInput · 5 bit MEM_WB_REG_WRITEInput · 1 bit MEM_WB_VALIDInput · 1 bit MEM_WB_VALUEInput · 32 bit BRANCH_TAKENExpected output · 1 bit CONTROL_TAKENExpected output · 1 bit CONTROL_TARGETExpected output · 32 bit IS_BRANCHExpected output · 1 bit OUT_ALU_RESULTExpected output · 32 bit OUT_CONTROLExpected output · 32 bit OUT_EXECUTION_STATUSExpected output · 32 bit OUT_FETCH_STATUSExpected output · 32 bit OUT_INSTRUCTIONExpected output · 32 bit OUT_NEXT_PCExpected output · 32 bit OUT_PCExpected output · 32 bit OUT_PC_PLUS_4Expected output · 32 bit OUT_REGISTER_ADDRESSESExpected output · 32 bit OUT_STORE_DATAExpected output · 32 bit OUT_VALIDExpected output · 1 bit PRETERMINALExpected output · 1 bit REDIRECTExpected output · 1 bit REDIRECT_PCExpected output · 32 bit
#0addi36 0x00 0 0 0 0x00000000 0x20001283 0x00000000 0x00000005 0x00508193 0x00000100 0x00000CA1 0x00000007 0x00000000 1 0x00 0 0 0x00000000 0 not checked not checked 0 0x0000000C not checked 0x00000000 not checked not checked 0x00000104 0x00000100 0x00000104 not checked 0x00000000 1 0 0 not checked
#1ex-forwarding78 0x01 1 1 1 0x00000014 0x20001283 0x00000000 0x00000005 0x00508193 0x00000100 0x00000CA1 0x00000007 0x00000000 1 0x00 0 0 0x00000000 not checked not checked not checked not checked 0x00000019 not checked not checked not checked not checked not checked not checked not checked not checked not checked not checked not checked not checked not checked
#2taken-branch121 0x00 0 0 0 0x00000000 0x00403181 0x00000000 0x00000008 0x00208463 0x00000200 0x00002041 0x00000004 0x00000004 1 0x00 0 0 0x00000000 not checked not checked not checked not checked not checked not checked not checked not checked not checked 0x00000208 not checked not checked not checked not checked not checked 0 1 0x00000208
#3wb-branch-forwarding173 0x00 0 0 0 0x00000000 0x00403181 0x00000000 0x00000008 0x00208463 0x00000200 0x00002041 0x00000004 0x00000009 1 0x01 1 1 0x00000009 1 not checked not checked not checked not checked not checked not checked not checked not checked not checked not checked not checked not checked not checked not checked not checked not checked not checked
#4younger-unready-blocks-old-wb217 0x01 1 0 1 0x00000000 0x00403181 0x00000000 0x00000008 0x00208463 0x00000200 0x00002041 0x00000004 0x00000009 1 0x01 1 1 0x00000009 0 not checked not checked not checked not checked not checked not checked not checked not checked 0x00000204 not checked not checked not checked not checked not checked not checked not checked not checked
#5jal-shares-alu248 0x00 0 0 0 0x00000000 0x28011603 0x00000000 0x00000008 0x008000EF 0x00000100 0x00000500 0x00000000 0x00000000 1 0x00 0 0 0x00000000 not checked not checked not checked 0 0x00000108 not checked not checked not checked not checked 0x00000108 not checked 0x00000104 not checked not checked not checked not checked not checked not checked
#6jalr-shares-alu-and-clears-bit-zero276 0x00 0 0 0 0x00000000 0x20021683 0x00000000 0x00000001 0x001080E7 0x00000100 0x00000421 0x00000208 0x00000000 1 0x00 0 0 0x00000000 not checked not checked not checked not checked 0x00000209 not checked not checked not checked not checked 0x00000208 not checked not checked not checked not checked not checked 0 not checked not checked

Scroll within the table for all rows and pins. Buses use hexadecimal; values containing X/Z use MSB-first binary. Inputs show the declared values in effect at each checkpoint, including retained inputs. Sequential tests also replay preparation changes between checkpoints, such as lowering the clock. Use the matching checkpoint label in the simulation below.

Tests

RV32IForwardingUnitTest, RV32IDecodeStageTest, and RV32IExecuteStageTest compare both fidelities. Program lockstep additionally checks arithmetic chains, loads, stores, branches and JALR against the architectural oracle. RV32IAdvancedExamplesTest compiles and checks the displayed selector example.