Master the fundamental concepts of digital logic & boolean algebra 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 worksA hardware simulator evaluates gates and wires in topological order. When an input toggles, affected gates recompute and propagate new values downstream. Tools like Logisim, Digital, and Verilog testbenches all follow this pattern.
cLoading…
Your simulator needs a netlist: gates as nodes, wires as edges. Each gate type (NAND, MUX, DFF) registers an evaluation function.
For example, feeding A=1, B=0 into a half-adder should eventually show sum=1, carry=0 on the output probes after one evaluation pass.
For this exercise, you will wire every component built so far into one runnable simulator. You will need to step clocks, load registers, and trace signals through the 4-bit ALU you constructed, mirroring how verification engineers smoke-test RTL before synthesis.
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.
Write a small gate-level hardware simulator, in the spirit of the HDL simulators used in nand2tetris or by chip designers. It reads a netlist, named wires driven by gates, checks it, puts the gates in dependency order and evaluates the circuit for given inputs. It also reports how big and how deep the circuit is. Depth matters because the longest path through the gates sets the clock speed.
cLoading…
Gate lines may appear in any order, e.g. a gate can use a wire that is defined further down. eval, table and stats use every declaration that comes before them.
Run the checks before each eval/table/stats. If one fails, that command prints only the error:
error: undefined wire X: the first wire, scanning outputs in order then gate inputs (gates in definition order, inputs left to right), that is neither an input nor driven by a gate.error: combinational loop through X: run a depth-first search from each gate in definition order, following its inputs left to right. X is the first wire reached that is already on the current DFS path.eval: a=1 b=0 -> out1=V out2=V. With the wrong number of values: error: eval needs N values.table: header IN1 IN2 ... | OUT1 OUT2 ..., then one row per combination, counting up in binary with the first input as the most significant bit.stats: gates=G depth=D critical path: IN -> W1 -> ... -> OUT. A gate's depth is 1 plus the largest depth among its inputs, and inputs have depth 0. D is the largest depth among the outputs (the first such output wins a tie). The path is traced back from that output, each time stepping to the input with the largest depth (the leftmost wins a tie), until an input wire is reached.Input:
cLoading…
Output:
cLoading…
Hidden tests cover the full truth table of a circuit, the buf/nand/nor/xnor gates, an undefined wire, a combinational loop, and a wrong eval count.