Skip to content

§ 4.6.3. Branch Prediction

Fetch predicts a next PC while Execute remains responsible for the actual outcome. Prediction changes throughput, never the architectural meaning of an instruction.

Component Description Structural Behavioral
RV32IPredictionCounter Stateless counter-update circuit Yes Yes
RV32IBranchPredecode B/J instruction recognition and target calculation Yes Yes
RV32IBranchPredictor Static policy or indexed FF table with next-PC selection Yes Yes
RV32IPredictionEntry Internal row: one FF in one-bit mode, two in two-bit mode Yes Via predictor
RV32IPredictionTable<N> Internal update decoder and read mux tree Yes Via predictor

Policies and configuration

prediction Conditional branch prediction Reset / training
not-taken PC+4 Default; no table
taken Encoded target No table
backward-taken Target when the signed immediate is negative; otherwise PC+4 No table
one-bit Most recent outcome at this index Reset not-taken; taken writes 11, untaken writes 00
two-bit Taken when counter is 10 or 11 Reset 00; saturating increment/decrement

prediction_entries is a power of two from 1 to 256, default 16. The table index is (PC >> 2) & (N - 1). It has no address tags: different branches can intentionally alias. Static policies ignore table contents but still validate the supplied size.

All policies predict a direct JAL's encoded target. JALR uses PC+4 until Execute resolves its register-dependent target. There is no target cache, BTB, return-address stack or instruction-history predictor. Switching policy means constructing/reloading a configured core, not changing a pin while a program runs. Fidelity is an independent structural/behavioral choice.

PredictionCounter

IO pins

Pin Direction Width Description
CURRENT Input 2 Current state
TAKEN, ONE_BIT Input 1 each Observed outcome and update mode
NEXT Output 2 Next state; no clock or storage in this block

Behavior and C++ example

Two-bit states are strongly not-taken, weakly not-taken, weakly taken, strongly taken in numeric order. Saturation prevents wraparound. One-bit mode reports 00 or 11 through this two-bit interface, but each table row stores only one outcome bit.

inline unsigned next_counter(unsigned state,bool taken,bool one_bit) {
    state&=3;
    if(one_bit) return taken?3:0;
    return unsigned(std::clamp(int(state)+(taken?1:-1),0,3));
}

Implementation

The structural implementation uses increment/decrement Boolean equations and direction/mode muxes. The behavioral implementation computes the same next-state function. Storage belongs to RV32IPredictionEntry, not this component.

Examples

All 16 recorded rows of RV32IPredictionCounterTest, 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: prediction-preview-preview / structural (c00b720a1b70c77a). Changing fidelity or the circuit can change these times.

Checkpointzero-based number · timeline label Timesimulator units CURRENTInput · 2 bit ONE_BITInput · 1 bit TAKENInput · 1 bit NEXTExpected output · 2 bit
#0mode-0-state-0-taken-06 0x0 0 0 0x0
#1mode-0-state-0-taken-111 0x0 0 1 0x1
#2mode-0-state-1-taken-016 0x1 0 0 0x0
#3mode-0-state-1-taken-121 0x1 0 1 0x2
#4mode-0-state-2-taken-029 0x2 0 0 0x1
#5mode-0-state-2-taken-134 0x2 0 1 0x3
#6mode-0-state-3-taken-042 0x3 0 0 0x2
#7mode-0-state-3-taken-147 0x3 0 1 0x3
#8mode-1-state-0-taken-053 0x0 1 0 0x0
#9mode-1-state-0-taken-157 0x0 1 1 0x3
#10mode-1-state-1-taken-062 0x1 1 0 0x0
#11mode-1-state-1-taken-166 0x1 1 1 0x3
#12mode-1-state-2-taken-073 0x2 1 0 0x0
#13mode-1-state-2-taken-177 0x2 1 1 0x3
#14mode-1-state-3-taken-084 0x3 1 0 0x0
#15mode-1-state-3-taken-188 0x3 1 1 0x3

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.

BranchPredecode

IO pins

Pin Direction Width Description
PC, INSTRUCTION Input 32 each Current fetch address and instruction bits
CONDITIONAL, DIRECT_JUMP, BACKWARD Output 1 each Recognized branch, JAL, negative selected immediate
TARGET, PC_PLUS_4 Output 32 each Candidate target and sequential address

Behavior and C++ example

Recognize branch opcode 0x63 with funct3 other than reserved 010/011, and JAL opcode 0x6f. Reassemble and sign-extend the B/J immediate, then add it to PC modulo 2³². For other instructions, the selected immediate is zero, TARGET=PC, and BACKWARD=0. Full legality and target-alignment checks still happen later; predecode never raises a trap or authorizes a side effect.

struct Predecode { bool conditional,direct_jump,backward; uint32_t target,pc_plus_4; };
inline Predecode predecode(uint32_t pc,uint32_t instruction) {
    const auto opcode=instruction&127,funct3=(instruction>>12)&7;
    const bool branch=opcode==0x63&&funct3!=2&&funct3!=3,jump=opcode==0x6f;
    uint32_t imm=0;
    if(branch) {
        imm=((instruction>>31)<<12)|(((instruction>>7)&1)<<11)|
            (((instruction>>25)&63)<<5)|(((instruction>>8)&15)<<1);
        if(imm&0x1000) imm|=0xffffe000u;
    } else if(jump) {
        imm=((instruction>>31)<<20)|(instruction&0xff000)|
            (((instruction>>20)&1)<<11)|(((instruction>>21)&1023)<<1);
        if(imm&0x100000) imm|=0xffe00000u;
    }
    return {branch,jump,bool(imm&0x80000000u),pc+imm,pc+4};
}

Implementation

Structural opcode matchers, field/sign-extension rewiring, muxes and adders implement the calculation. Behavioral predecode performs the same bit extraction and unsigned arithmetic. These are Fetch adders: prediction must run concurrently with Execute, so they cannot use Execute's busy ALU.

Examples

All 8 recorded rows of RV32IBranchPredecodeTest, 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: prediction-preview-preview / structural (c00b720a1b70c77a). Changing fidelity or the circuit can change these times.

Checkpointzero-based number · timeline label Timesimulator units INSTRUCTIONInput · 32 bit PCInput · 32 bit BACKWARDExpected output · 1 bit CONDITIONALExpected output · 1 bit DIRECT_JUMPExpected output · 1 bit PC_PLUS_4Expected output · 32 bit TARGETExpected output · 32 bit
#0forward-branch22 0x00208463 0x00000100 0 1 0 0x00000104 0x00000108
#1backward-branch48 0xFE208EE3 0x00000100 1 1 0 0x00000104 0x000000FC
#2branch-to-fallthrough71 0x00000263 0x00000100 0 1 0 0x00000104 0x00000104
#3jal83 0x008000EF 0x00000100 0 0 1 0x00000104 0x00000108
#4jalr-needs-register94 0x00008067 0x00000100 0 0 0 0x00000104 0x00000100
#5reserved-branch101 0x00002063 0x00000100 0 0 0 0x00000104 0x00000100
#6ordinary106 0x00108093 0x00000100 0 0 0 0x00000104 0x00000100
#7wraparound133 0x008000EF 0xFFFFFFFC 0 0 1 0x00000000 0x00000004

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.

BranchPredictor

IO pins

Pin Direction Width Description
CLK, RST Input 1 each Rising-edge training; reset clears table immediately
PC, TARGET, PC_PLUS_4 Input 32 each Lookup PC and predecoded candidate addresses
CONDITIONAL, DIRECT_JUMP, BACKWARD Input 1 each Predecode results
UPDATE, UPDATE_TAKEN Input 1 each Qualified conditional-branch training and actual outcome
UPDATE_PC Input 32 Branch's own PC, not current Fetch PC
NEXT_PC Output 32 Predicted target or PC+4
TAKEN Output 1 Selected target; always true for DIRECT_JUMP
COUNTER Output 2 Current indexed state; zero for static policies

Behavior and C++ example

Lookup is combinational. Training updates one row on the rising edge when UPDATE=1 and reset is inactive. Reset clears every row to zero regardless of UPDATE. Low PC bits are ignored for indexing. Static policies never modify state; ordinary instructions always select PC+4 even if the indexed counter predicts taken.

In the code, call evaluate for state updates, then lookup for settled outputs. Capturing a prediction at the same edge as a colliding update uses the old table value; PredictionPath makes that ordering explicit.

enum class Policy { NotTaken,Taken,BackwardTaken,OneBit,TwoBit };
struct Predictor {
    Policy policy;
    std::vector<unsigned> table;
    bool previous_clock=false;
    Predictor(Policy mode,size_t entries):policy(mode) {
        if(!entries||entries>256||(entries&(entries-1))) throw std::invalid_argument("table size");
        table.assign(entries,0);
    }
    unsigned counter(uint32_t pc) const {return table[(pc>>2)&(table.size()-1)];}
    struct Output {uint32_t next_pc;bool taken;unsigned counter;};
    Output lookup(uint32_t pc,Predecode d) const {
        const bool direction=policy==Policy::Taken ||
            (policy==Policy::BackwardTaken&&d.backward) ||
            ((policy==Policy::OneBit||policy==Policy::TwoBit)&&counter(pc)>=2);
        const bool taken=d.direct_jump||(d.conditional&&direction);
        return {taken?d.target:d.pc_plus_4,taken,counter(pc)};
    }
    void evaluate(bool clk,bool reset,bool update,uint32_t update_pc,bool taken) {
        if(reset) std::fill(table.begin(),table.end(),0);
        else if(!previous_clock&&clk&&update&&(policy==Policy::OneBit||policy==Policy::TwoBit)) {
            auto& state=table[(update_pc>>2)&(table.size()-1)];
            state=next_counter(state,taken,policy==Policy::OneBit);
        }
        previous_clock=clk;
    }
};

Implementation

Structural dynamic policies instantiate TABLE, with numbered ROW_n entries and update-address decoding. A one-bit row stores one FF and uses one read-selection tree; the public two-bit counter output duplicates that bit as 00 or 11. A two-bit row stores two FFs and includes the saturating-counter update network and two read trees. Static policies omit the table entirely. The behavioral implementation replaces the selected bank with a bounded array and follows the same indexing, edge/reset, saturation, and aliasing rules.

Examples

The embedded preview starts with a four-entry two-bit predictor. The registered contract test additionally covers all five policies at 1, 4 and 16 entries. Every preview checkpoint, including its time and number, appears below.

All 21 recorded rows of RV32IBranchPredictorTest, 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: prediction-preview-preview / structural (c00b720a1b70c77a). Changing fidelity or the circuit can change these times.

Checkpointzero-based number · timeline label Timesimulator units BACKWARDInput · 1 bit CLKInput · 1 bit CONDITIONALInput · 1 bit DIRECT_JUMPInput · 1 bit PCInput · 32 bit PC_PLUS_4Input · 32 bit RSTInput · 1 bit TARGETInput · 32 bit UPDATEInput · 1 bit UPDATE_PCInput · 32 bit UPDATE_TAKENInput · 1 bit COUNTERExpected output · 2 bit NEXT_PCExpected output · 32 bit TAKENExpected output · 1 bit
#0reset15 0 0 1 0 0x00000000 0x00000004 1 0x00000064 0 0x00000000 0 0x0 0x00000004 0
#1train-034 0 1 1 0 0x00000000 0x00000004 0 0x00000064 1 0x00000000 1 0x1 0x00000004 0
#2train-151 0 1 1 0 0x00000000 0x00000004 0 0x00000064 1 0x00000000 1 0x2 0x00000064 1
#3train-263 0 1 1 0 0x00000000 0x00000004 0 0x00000064 1 0x00000000 1 0x3 0x00000064 1
#4train-369 0 1 1 0 0x00000000 0x00000004 0 0x00000064 1 0x00000000 1 0x3 0x00000064 1
#5train-489 0 1 1 0 0x00000000 0x00000004 0 0x00000064 1 0x00000000 0 0x2 0x00000064 1
#6train-5106 0 1 1 0 0x00000000 0x00000004 0 0x00000064 1 0x00000000 0 0x1 0x00000004 0
#7train-6118 0 1 1 0 0x00000000 0x00000004 0 0x00000064 1 0x00000000 0 0x0 0x00000004 0
#8train-7124 0 1 1 0 0x00000000 0x00000004 0 0x00000064 1 0x00000000 0 0x0 0x00000004 0
#9train-8126 0 1 1 0 0x00000000 0x00000004 0 0x00000064 1 0x00000000 0 0x0 0x00000004 0
#10train-9145 0 1 1 0 0x00000000 0x00000004 0 0x00000064 1 0x00000000 1 0x1 0x00000004 0
#11train-10162 0 1 1 0 0x00000000 0x00000004 0 0x00000064 1 0x00000000 0 0x0 0x00000004 0
#12train-11181 0 1 1 0 0x00000000 0x00000004 0 0x00000064 1 0x00000000 1 0x1 0x00000004 0
#13train-12198 0 1 1 0 0x00000000 0x00000004 0 0x00000064 1 0x00000000 1 0x2 0x00000064 1
#14backward-direction200 1 0 1 0 0x00000000 0x00000004 0 0x00000064 0 0x00000000 1 0x2 0x00000064 1
#15disabled-update205 1 1 1 0 0x00000000 0x00000004 0 0x00000064 0 0x00000000 1 0x2 0x00000064 1
#16independent-row217 1 0 1 0 0x00000004 0x00000004 0 0x00000064 0 0x00000000 1 0x0 0x00000004 0
#17index-alias229 1 0 1 0 0x00000010 0x00000004 0 0x00000064 0 0x00000000 1 0x2 0x00000064 1
#18jal-always-target232 1 0 0 1 0x00000010 0x00000004 0 0x00000064 0 0x00000000 1 0x2 0x00000064 1
#19ordinary-fallthrough237 1 0 0 0 0x00000010 0x00000004 0 0x00000064 0 0x00000000 1 0x2 0x00000004 0
#20reset-clears-training253 1 0 1 0 0x00000010 0x00000004 1 0x00000064 0 0x00000000 1 0x0 0x00000004 0

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

RV32IPredictionCounterTest exhausts all 16 mode/state/outcome combinations. RV32IBranchPredecodeTest covers targets, wraparound, reserved encodings and JALR fallback. RV32IBranchPredictorTest checks saturation, reset, disabled training, independent rows, aliasing and all policies in both fidelities. RV32IAdvancedExamplesTest replays their actions through these exact C++ examples and rejects malformed configuration values.