HomeRISC-V from ScratchDay 8
DAY 8 · PHASE 2 — BUILD THE CPU

Instruction Memory & the Fetch Stage

By EcrioniX · Updated Jun 11, 2026

Day 7 gave you the full datapath map and the Program Counter. Now we build the other half of the Fetch stage: Instruction Memory (IMEM) — the ROM that holds your program. Wire PC + IMEM together and you have a stage that can continuously fetch instructions. That's the first piece of a working CPU.

1. What instruction memory does

IMEM is the simplest module in the CPU: give it an address, get back a 32-bit instruction. It is purely combinational — no clock, no write port. Think of it as a lookup table: index = address, value = machine code word.

PC (addr) IMEM ROM — program stored here instr[31:0] combinational — no clock needed
IMEM: address in, 32-bit instruction word out. No clock — it's a read-only lookup.

2. Port table

PortDirWidthMeaning
addrinput32byte address of the instruction to fetch (from PC)
instroutput3232-bit machine code word at that address

We word-index into the array using addr[31:2] (shift right by 2 = divide by 4) because instructions are 4-byte aligned and we store one word per entry.

3. imem.v — the instruction memory

imem.v — instruction memory (ROM)
// Instruction Memory — combinational ROM, word-addressed
// Holds up to 256 instructions (1 KB). Pre-load with $readmemh.
module imem #(parameter DEPTH = 256) (
    input  wire [31:0] addr,   // byte address from PC
    output wire [31:0] instr   // 32-bit instruction word
);
    reg [31:0] mem [0:DEPTH-1];

    // Load program at simulation start.
    // Replace "program.hex" with your assembled .hex file.
    initial $readmemh("program.hex", mem);

    // Word-aligned read: divide byte address by 4
    assign instr = mem[addr[31:2]];
endmodule

4. The Fetch stage — PC + IMEM wired together

if_stage.v — Instruction Fetch stage
// Instruction Fetch stage: PC register + IMEM
// pc_next is driven by the branch/jump logic (just PC+4 for now)
module if_stage (
    input  wire        clk,
    input  wire        rst,
    input  wire [31:0] pc_next,   // next PC (PC+4 or branch target)
    output wire [31:0] pc,        // current PC
    output wire [31:0] instr      // fetched instruction
);
    pc   pc_reg  (.clk(clk), .rst(rst), .pc_next(pc_next), .pc(pc));
    imem imem_mod(.addr(pc), .instr(instr));
endmodule

5. Testbench — fetch a real program

We pre-load IMEM with a small program encoded as hex words, then check that each instruction fetches in the right order:

program.hex — 4 instructions as machine code
00500093  // addi x1, x0, 5    (x1 = 5)
00A00113  // addi x2, x0, 10   (x2 = 10)
002081B3  // add  x3, x1, x2   (x3 = 15)
00000013  // nop (addi x0,x0,0)
tb_if_stage.v — testbench
`timescale 1ns/1ps
module tb_if_stage;
    reg        clk=0, rst=1;
    reg [31:0] pc_next;
    wire[31:0] pc, instr;

    if_stage dut(.clk(clk),.rst(rst),.pc_next(pc_next),.pc(pc),.instr(instr));
    always #5 clk=~clk;

    integer errors=0;
    task check(input [31:0] epc, input [31:0] einstr);
        if(pc!==epc||instr!==einstr)begin
            $display("FAIL pc=%0h instr=%0h (exp pc=%0h instr=%0h)",pc,instr,epc,einstr);
            errors=errors+1;
        end else $display("ok   pc=%0h instr=%0h",pc,instr);
    endtask

    initial begin
        pc_next=0; @(posedge clk); #1; check(32'h0,32'h00500093); // reset, fetch instr 0
        rst=0;
        pc_next=32'h4; @(posedge clk); #1; check(32'h4,32'h00A00113);
        pc_next=32'h8; @(posedge clk); #1; check(32'h8,32'h002081B3);
        pc_next=32'hC; @(posedge clk); #1; check(32'hC,32'h00000013);
        if(errors==0) $display("ALL TESTS PASSED"); else $display("%0d FAILED",errors);
        $finish;
    end
endmodule
expected output
ok   pc=00000000 instr=00500093
ok   pc=00000004 instr=00a00113
ok   pc=00000008 instr=002081b3
ok   pc=0000000c instr=00000013
ALL TESTS PASSED

✅ Day 8 in one line

IMEM is a combinational ROM: addr in → instr out, no clock. Word-index with addr[31:2]. Wired with pc.v it becomes the complete Fetch stage — the CPU can now continuously fetch instructions from a stored program.

🎯 Day 8 takeaways

FAQ

What is instruction memory?

A read-only memory (ROM) holding the program. The PC supplies the address; IMEM returns the 32-bit instruction word at that address.

Why combinational?

We only ever read IMEM (never write during execution). A combinational ROM returns the instruction the same cycle the address is presented — no clock edge needed.

What is $readmemh?

A Verilog system task that loads a memory array from a hex file at simulation time — used to pre-load the program into IMEM.

Previous
← Day 7: Datapath overview

← Full roadmap