Level 51: A Constant Inside the Instruction

Open level →

Task

Goal: build sign-extend: imm[11:0] expands to 32 bits by copying the sign bit. This is the heart of the I-format addi — the constant lives inside the instruction itself. Eight ready-made I-modules (slti32.v…srai32.v) sit in the project read-only — they will join your core later; the Assembler tab already packs words.

Not every instruction takes both operands from registers. The command addi x1, x0, -1 adds x0 to a number stored directly in the machine word. Such a number is called an immediate. In the instruction it occupies 12 bits, while the ALU works with 32-bit numbers. Your task is the SignExt32 block: extend the immediate to 32 bits while preserving the sign.

The project is multi-file:

  • signext.v — editable: interface imm[11:0] → ext[31:0]. Your work.
  • slti32.v … srai32.v — eight ready-made I-modules, read-only. They are the future executors of I-instructions in your core; for now you only need to see which module corresponds to which operation code.

The checker tests sign-extend at the boundaries of the 12-bit range: 0, 1, 0x3FF/0x400 (the top-bits boundary), 0x7FF (2047), 0x800 (−2048), 0xFFF (−1) and others.

The second half of the level is the Assembler tab, which already works at this level: it turns a mnemonic into a machine word and back. Assemble addi x1, x0, -1 there and find the I-format fields in the resulting word FFF00093: imm[11:0] in the top bits, rs1, funct3, rd and opcode below.

Solution for self-check

First implement SignExt32 yourself. The solution below will help you check it.

What you should get: SignExt32 is correct on the range edges: 0x7FF → 2047, 0x800 → −2048, 0xFFF → −1 (the top 20 bits are ones); the Assembler packs addi x1, x0, -1 into FFF00093.

Step 1 — parse the 12-bit number. The top bit is bit 11. In two's complement it determines the sign: for 0x7FF (2047) it is zero, for 0x800 (−2048) it is one.

Step 2 — build the circuit from buffers. The low 12 bits of ext are direct copies of imm[0..11]. The top 20 bits are all connected to imm[11]: the sign bit is replicated. Copying a bit is the buffer or(out, in, 1'b0), the idiom from level 50. Thirty-two copy wires in total, no flip-flops: sign-extend is a purely combinational block.

Step 3 — check the boundaries. The level table contains all edge cases. Compare the two most telling: 0x7FF → 2047 (top bits are zeros), 0xFFF → 0xFFFFFFFF, that is −1 (top bits are ones).

Step 4 — check the assembler. addi x1, x0, -1: imm = FFF, rs1 = x0 = 00000, funct3 = 000, rd = x1 = 00001, opcode OP-IMM = 0010011. Put the fields together — you get FFF00093. If you forget sign-extend, the constant −1 becomes 4095: the level prediction offers −1, 2047 and 4095, and 4095 is exactly what you get when the top 20 bits stay zero.

Principle: the immediate is not a separate register but part of the instruction itself. The processor extracts it from the instruction and extends it to the required width right in the datapath.

Check yourself

What happens to the constant −1 without sign-extend?

The top 20 bits would stay zero and −1 would turn into 4095. The sign bit is copied into the upper positions precisely to preserve the value: the 12 bits honestly become 32.