Immediate Generator
The IMMGEN is a small Verilog module in a single-cycle RISC-V core whose sole job is to pull out the immediate (constant) field from a 32-bit instruction and sign-extend it to 32 bits. It centralizes all of the different RISC-V immediate formats (I, S, B, U, J) into one place.
What Does it Look Like?
We can have a big picture at the start so it is easier to visualize it. You can visualize the immgen as just a box that takes instruct and imm_sel and outputs imm_out.
┌──────────┐
instr[31:0]─►│ │
imm_sel ─--─►│ IMMGEN │─► imm_out[31:0]
└──────────┘In verilog, it is described as something like:
module immgen (
input wire [31:0] instr,
input wire [2:0] imm_sel,
output reg [31:0] imm_out
);
// Precompute all formats
wire [31:0] i_imm = {{20{instr[31]}}, instr[31:20]};
wire [31:0] s_imm = {{20{instr[31]}}, instr[31:25], instr[11:7]};
wire [31:0] b_imm = {{19{instr[31]}}, instr[31], instr[7],
instr[30:25], instr[11:8], 1'b0};
wire [31:0] u_imm = {instr[31:12], 12'b0};
wire [31:0] j_imm = {{11{instr[31]}}, instr[31], instr[19:12],
instr[20], instr[30:21], 1'b0};
// Selecting and passing the correct format
always @(*) begin
case (imm_sel)
3'b000: imm_out = i_imm; // I-type
3'b001: imm_out = s_imm; // S-type
3'b010: imm_out = b_imm; // B-type
3'b011: imm_out = u_imm; // U-type (LUI/AUIPC)
3'b100: imm_out = j_imm; // J-type (JAL)
default: imm_out = 32'b0;
endcase
end
endmoduleWhat Exactly Does it Do?
But you can just recap it as 2 stages:
- Precomputing all formats:
- Take the instruction via
instrthis is the main data we will be manipulating. - It is sliced so that the immediate part is extracted from the instruction and saved to
i_imm,s_imm,b_imm,u_imm, andj_imm. - Now we have the bits that we want. But since we sliced them, it will be shorter than our original instruction. So we sign-extend them - which means we make it 32 bits (as long as the original instruction) and make sure the sign stays the same.
- Take the instruction via
- Selecting and passing the correct format
- We also received the
imm_sel, we can just write acasethat we pass our precomputed immediate.
- We also received the
What is Its Role?
Okay, so now we know what it is, what it looks like, and what it does. Now why do we even need it? How does it work with the rest of the CPU? Well, in short, everywhere that needs an immediate. But below are some examples:
ALU operations: For arithmetic-immediate (
ADDI,SLTI, etc.) and logic-immediate (ANDI,ORI), the ALU’s second operand comes from IMMGEN.Memory access: Load/store instructions use IMMGEN’s output as the address offset (base register + immediate).
Branches & jumps: The branch-target and jump-target calculations add the sign-extended offset from IMMGEN to the PC.
PC-relative instructions:
AUIPCandLUIrely on the U-type immediate for building large constants or PC offsets.
Connections
Inputs:
instr[31:0]from the instruction memory.imm_sel[2:0]from the main control unit, decoded from the instruction’s opcode/funct fields.
Output:
imm_out[31:0]which then fans out to the ALU, branch-adder, or PC-update logic.
More about Immediate Formats:
| Type | Encoding bits | Immediate bits (before sign-extend) | Used for |
|---|---|---|---|
| I-type | [31:20] | imm[11:0] | load/store offsets, ALU immediates, jalr |
| S-type | [31:25] | [11:7] | imm[11:5] | imm[4:0] | store offsets |
| B-type | [31] | [7] | [30:25] | [11:8] | imm[12] | imm[11] | imm[10:5] | imm[4:1] | 0 | branch offsets (×2) |
| U-type | [31:12] | imm[31:12] | 12’b0 | upper immediates (lui, auipc) |
| J-type | [31] | [19:12] | [20] | [30:21] | imm[20] | imm[19:12] | imm[11] | imm[10:1] | 0 | jump offsets (jal) (×2) |
There is also R-type instructions but they do not have any immediate bits.
See also: Instruction Formats
References:
https://www.cs.sfu.ca/~ashriram/Courses/CS295/assets/notebooks/RISCV/RISCV_CARD.pdf