Master the fundamental concepts of cpu design through this focused micro-challenge.
You have read the whole brief, and the concepts above stay free on every task. Writing and running the code needs a plan.
Three hints are available for this task, revealed one at a time inside the code workspace so you can struggle productively before seeing them.
Every task includes starter code, theory, and hidden tests so you can implement and verify locally in the browser.
How it worksPipelining overlaps instruction phases. A two-stage design fetches the next instruction while the current one executes. Throughput can approach one instruction per cycle when the pipeline stays full.
cLoading…
A hazard occurs when execute reads a register that the previous instruction has not yet written. Example:
cLoading…
Without forwarding or stalls, the SUB stage reads stale R1.
For this exercise, you will split your emulator into IF and EX stages and log hazard stalls. This task asks you to count how many bubbles a tight read-after-write chain injects, foreshadowing the five-stage pipeline ahead.
Keep the relevant datasheet, ISA manual, or architecture textbook chapter open while you implement. When your output disagrees with the reference trace on the same program, the bug is usually a mis-decoded opcode, a stale register read, or a flag bit left unchanged after arithmetic.
For this exercise, you will use those habits while implementing the requirement in the starter code. Microarchitectural product names change across CPU generations, but the control ideas (fetch, bypass, cache lines, vector lanes) stay stable enough to debug from first principles.
Pipeline the toy CPU into two stages. FD fetches, decodes and reads registers. EX executes and writes the result back at the end of the cycle. While one instruction executes, the next is being fetched, so a full pipeline completes one instruction per cycle. Two things break that: a data hazard (FD needs a register that the instruction in EX hasn't written yet → stall) and a taken branch (the instruction already fetched is the wrong one → flush). Simulate the pipeline cycle by cycle and compare it with the unpipelined CPU.
Registers R0: R7 (integers, start at 0). Labels are name: at the start of a line. Programs are well-formed.
| Instruction | Reads | Writes | Effect |
|---|---|---|---|
LI Rd, imm | - | Rd | Rd = imm |
ADD Rd, Rs, Rt / SUB Rd, Rs, Rt | Rs, Rt | Rd | Rd = Rs ± Rt |
BNEZ Rs, label | Rs | - | if Rs != 0, branch to label |
HALT | - | - | stop |
Instructions are named I0, I1, … by their position in the program (labels and blank lines don't count).
BNEZ, the instruction in FD is flushed (EX gets a bubble), and FD fetches the target next cycle. This takes priority over a stall.HALT (and restarts if a taken branch redirects it). Fetching past the last instruction yields nothing.HALT is in EX, or when both stages are empty. Give up after 200 cycles.One line per cycle, with -- for an empty stage or bubble. Add stall Rn (the first conflicting source register, in operand order) or flush when that rule applies:
cLoading…
Then:
cLoading…
N counts completed instructions including HALT. Unpipelined, each instruction takes both stages back to back. Print the speedup 2N / C with 2 decimals. If the cycle limit is hit, print cycle limit reached in place of the retired and unpipelined lines.
Input:
cLoading…
Output:
cLoading…
Hidden tests cover a countdown loop (BNEZ taken several times, then not taken), and a chain of back-to-back dependent instructions in a program that has no HALT.