Level 54: A Loop on BNE
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:
- Pack the
bneword in "Word from fields" and take apart the inverted condition: forbeqthe branch isBranch & zero, forbneit isBranch & not(zero). The B-bit map is the same as beq's from level 53. - Build the loop from
addiandbnein the Assembler tab and run it (Step/Run in the "Execution" block). - Make sure the loop terminates: x5 reaches the bound 10 and the last bne does not jump.
- 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.