Skip to main content...
S0 · CMOS Fundamentals + Digital Logic
35 min

Day 30: Project: the gate-level logic simulator in Python (part 1 — netlist + events)

Building a tiny logic simulator teaches you what tools like Verilator actually do — and gives you a real, testable artifact for your portfolio.

The Stage 0 capstone project begins

Your headline Stage 0 artifact is a gate-level logic simulator in Python: it parses a netlist of gates (AND, OR, NOT, DFF), builds the connectivity, and propagates events — changes on nets — through the combinational gates until the circuit settles. Today you build the data model and the combinational evaluator; tomorrow you add clocking and waveforms.

sim.py (part 1): net/gate model + event-driven combinational settle
from dataclasses import dataclass, field

@dataclass
class Gate:
    op: str                 # "AND","OR","NOT","DFF"
    inputs: list             # net names
    output: str              # net name

class Circuit:
    def __init__(self):
        self.gates: list[Gate] = []
        self.values: dict[str, int] = {}     # net -> 0/1
        self.fanout: dict[str, list[Gate]] = {}

    def add(self, g: Gate):
        self.gates.append(g)
        for n in g.inputs:
            self.fanout.setdefault(n, []).append(g)
        self.values.setdefault(g.output, 0)
        for n in g.inputs:
            self.values.setdefault(n, 0)

    def eval_comb(self, g: Gate) -> int:
        a = [self.values[n] for n in g.inputs]
        if g.op == "AND": return int(all(a))
        if g.op == "OR":  return int(any(a))
        if g.op == "NOT": return int(not a[0])
        return self.values[g.output]        # DFF: not combinational (Day 31)

    def settle(self, changed_nets):
        """Event-driven: re-evaluate only gates whose inputs changed, repeat."""
        queue = list(changed_nets)
        while queue:
            net = queue.pop(0)
            for g in self.fanout.get(net, []):
                if g.op == "DFF":
                    continue                 # sequential: handled on clock edge
                new = self.eval_comb(g)
                if new != self.values[g.output]:
                    self.values[g.output] = new
                    queue.append(g.output)   # its change may ripple onward

This is a toy Verilator

Event-driven propagation — re-evaluate only what changed, ripple until stable — is the core algorithm of real simulators. When you meet Verilator in Stage 2 (it *compiles* your RTL for speed) you'll appreciate exactly what problem it's solving, because you built the naive version first.

Key terms

Netlist
A list of gates and the nets connecting them — the structural description of a circuit.
Event-driven simulation
Re-evaluating only the gates whose inputs changed, propagating changes until the circuit settles.
Fan-out map
A lookup from each net to the gates it drives, used to find what to re-evaluate on a change.
Combinational settle
Iterating gate evaluations until no net value changes further.

Progress for Day 30

In the event-driven simulator, why re-evaluate only the gates driven by a changed net, rather than every gate each time?

We use cookies

We use cookies to enhance your browsing experience, serve personalized content, and analyze our traffic. By clicking "Accept All", you consent to our use of cookies. Learn more

    Day 30: Project: the gate-level logic simulator in Python (part 1 — netlist + events) | RBTechIconX