RISC-V path

RISC-V: from binary word to pipeline

The site’s three RISC-V tools answer three different questions about the same instruction: how it is encoded, what it does when it runs and how long it takes when overlapped with others. Here we go through them with a three-line loop.
li   t0, 3
loop:
  addi t0, t0, -1
  bnez t0, loop

The loop loads 3 into t0 and subtracts 1 until t0 is 0. It looks trivial, but it contains all three topics: an instruction with a negative immediate, a backward conditional branch and a dependency between consecutive instructions.

1. Encoding: bnez is a bne with a negative offset

bnez does not exist in the ISA: it is a pseudo-instruction the assembler turns into bne t0, zero, loop. Since loop is 4 bytes before the branch, the offset is −4 and the resulting word is 0xFE029EE3. In the B format the immediate is split into four pieces and its bit 0 is not stored, because targets are always even:

0xFE029EE3 · B-Type
FieldBitsBinaryMeaning
imm[12][31]1piece of the immediate
imm[10:5][30:25]111111piece of the immediate
rs2[24:20]00000register x0 (zero)
rs1[19:15]00101register x5 (t0)
funct3[14:12]001selects the operation within the group
imm[4:1][11:8]1110piece of the immediate
imm[11][7]1piece of the immediate
opcode[6:0]1100011group BRANCH
imm-4 (0xFFFFFFFC)rebuilt immediate (signed)
Instructionbne t0, zero, -4

Joining imm[12], imm[11], imm[10:5] and imm[4:1] and appending the final 0 gives 1 1 111111 1110 0 = −4. Open it in the decoder.

The six formats at a glance

FormatFields (bit 31 → bit 0)Used for
Rfunct7 · rs2 · rs1 · funct3 · rd · opcoderegister-register operations (add, sub, and, sll…)
Iimm[11:0] · rs1 · funct3 · rd · opcodeimmediate operations, loads and jalr
Simm[11:5] · rs2 · rs1 · funct3 · imm[4:0] · opcodestores (sw, sh, sb)
Bimm[12|10:5] · rs2 · rs1 · funct3 · imm[4:1|11] · opcodeconditional branches (beq, bne, blt…)
Uimm[31:12] · rd · opcodelui and auipc
Jimm[20|10:1|11|19:12] · rd · opcodejal

Notice that rs1, rs2 and rd always sit in the same positions. It is a RISC-V design decision: the hardware can start reading registers before knowing which instruction it is, at the cost of scattering the immediate bits.

2. Execution: follow the PC

When running, each instruction changes registers and the PC. bnez writes no register: it only decides whether the PC goes back to loop or falls through. The body runs 3 times, so 1 + 3 × 2 = 7 instructions execute and t0 ends at 0. In the debugger, press “Step” and watch the PC jump from 0x00000008 back to 0x00000004 twice.

3. Pipeline: what a branch costs

In a 5-stage pipeline the branch is not resolved until EX. By then the next two instructions have already been fetched, and if the branch is taken they must be discarded. The simulator treats every branch as taken, so the diagram shows exactly that 2-cycle penalty. The dependency of bne on addi (t0) costs nothing thanks to forwarding.

Stage12345678910
IFI0I1I2I3I2I3————
ID—I0I1I2FLUSHI2I3———
EX——I0I1FLUSHFLUSHI2I3——
MEM———I0I1FLUSHFLUSHI2I3—
WB————I0I1FLUSHFLUSHI2I3
With forwarding: 10 cycles, 0 bubbles, 2 flushed instructions. I0 = addi t0, t0, -1 · I1 = bne t0, zero, loop · I2 = addi a0, a0, 1 · I3 = addi a1, a1, 1

Open this example in the pipeline simulator.

Suggested path

  1. Guide: how to decode an RV32I instruction and the decoding exercises and formats.
  2. Guide: from machine code to assembly, which translates a whole routine with branches and labels.
  3. Debugger and the register tracing and loop exercises.
  4. Guide: the 5-stage pipeline and RAW hazards, forwarding and stalls, with the load-use exercise.

References