Level 54: A Loop on BNE

Open level →

Task

Goal: build the classic three-instruction for-loop (addi init, addi step, bne backward jump) in the Assembler tab, run it cycle by cycle, and predict the number of cycles to halt — before the first Check. The CPU schematic does not change: bne is a twin of level-53 beq with the condition inverted, and the reference core already has it. This is the first pure-programming level. The halt is .end: jal x0, 0 jumping to itself, it adds no cycle.

The processor circuit does not change at this level: you already have everything you need. bne is the twin of beq from level 53: the branch is taken when zero = 0, that is, the registers are not equal — and the reference core already has it. What changes is what you write — this is the first level of pure programming on your own machine: programs run on the single-cycle risc-run core, integration of your blocks into top.v is level 58.

Your program is a classic for loop (the canon of the counter golden program):

    addi x5, x0, 0      # counter = 0
    addi x6, x0, 10     # bound = 10
loop:
    addi x5, x5, 1      # counter += 1
    bne  x5, x6, loop   # repeat while not equal
.end                   # halt: jal x0, 0 to itself

Level tasks:

  1. Pack the bne word in "Word from fields" and take apart the inverted condition: for beq the branch is Branch & zero, for bne it is Branch & not(zero). The B-bit map is the same as beq's from level 53.
  2. Build the loop from addi and bne in the Assembler tab and run it (Step/Run in the "Execution" block).
  3. Make sure the loop terminates: x5 reaches the bound 10 and the last bne does not jump.
  4. Predict the cycle count before running — the first "Check" will ask for it and compare it with the trace.

This is the first complete golden run: the counter program is assembled and executed in full. Predicting cycles before the run is a mandatory part of the level: count yourself how many instructions execute over 10 iterations, not forgetting the bne on every iteration and the .end halt.

Solution for self-check

First count the cycles on paper. The solution below will help you check it.

What you should get: the counter loop finishes: x5 reaches the bound 10, the last bne no longer jumps; the predicted 22 cycles (2 init + 10 iterations × 2) match the trace, and the PC rests at address 16.

Step 1 — invert the condition. For beq the branch is Branch & zero, for bne it is Branch & not(zero). Everything else — offset, PCSrc, addressing — is the same. A common mistake: forgetting to invert — and the loop either never runs or never terminates.

Step 2 — anatomy of the loop. Two init instructions, the loop body is addi, the condition is bne, and a negative offset leads back to the loop label (in bytes from the bne's own address: 8 − 12 = −4).

Step 3 — count cycles. A single-cycle machine executes one instruction per cycle. Two inits + 10 iterations × (addi + bne) = 2 + 20 = 22 cycles. The last, tenth bne executes but no longer jumps (x5 = 10 = x6). The .end halt is jal x0, 0, a jump to itself: the core sees an unchanged PC and stops without spending a cycle — the PC freezes at address 16. Any other number (20 "no init", 21 "count down", 23 ".end is a separate cycle") is a reason to re-check your model.

Step 4 — compare with the trace. Open the trace and find: every taken bne returns the PC to 8; the last bne leaves PC = 16; the .end halt record does not advance the cycle counter. Compare your prediction with the actual cycle count — a match means you control the machine, not just assemble it.

Check yourself

Why does the .end halt add no cycle?

.end is jal x0, 0 jumping to itself: the core sees the PC did not change and halts without spending a cycle on the jump. The last bne was already executed in the count — it simply does not jump.