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

Day 31: Project: the gate-level logic simulator (part 2 — DFFs + waveform dump)

Add a clock and your simulator can run sequential circuits — including the 101 detector you designed on Day 29.

Adding sequential behavior and waveforms

Combinational settling isn't enough for memory. A DFF samples its input at the clock edge: on each tick, capture every flip-flop's current input, then commit them to outputs *simultaneously* (matching real edge-triggered behavior — no flip-flop sees another's new value until the next settle). Then re-settle the combinational logic. Wrap that in a clock loop and record every net each cycle as a waveform.

sim.py (part 2): clocked DFF update, cycle loop, ASCII waveform dump
def tick(circ):
    # 1) sample all DFF inputs BEFORE committing (edge-triggered semantics)
    next_q = {g.output: circ.values[g.inputs[0]]
              for g in circ.gates if g.op == "DFF"}
    # 2) commit all flip-flops together, then ripple combinational logic
    changed = [q for q, v in next_q.items() if v != circ.values[q]]
    circ.values.update(next_q)
    circ.settle(changed)

def run(circ, stimulus, watch):
    """stimulus: list of {net: value} to force each cycle. watch: nets to log."""
    trace = {w: [] for w in watch}
    for step in stimulus:
        for net, val in step.items():
            if val != circ.values[net]:
                circ.values[net] = val
                circ.settle([net])          # inputs ripple first
        tick(circ)                           # then the clock edge
        for w in watch:
            trace[w].append(circ.values[w])
    return trace

def dump(trace):
    for net, bits in trace.items():
        print(f"{net:>8} | " + "".join("_-"[b] for b in bits))

# e.g. feed the 101-detector its input stream and watch 'y' assert on 1-0-1

Ship: the gate-level logic simulator repo

Finish the simulator with a README, unit tests (a mux, a full adder, a DFF, and your 101 detector), and a committed waveform screenshot. Run your Day-29 sequence detector on it and confirm it detects the overlapping pattern. This is the flagship Stage 0 deliverable.

Sample-then-commit, or you get a race

If you update flip-flops one at a time, a later DFF may read an earlier DFF's *already-updated* value in the same edge — a shift register would collapse into one stage. Capturing all inputs first, then committing together, reproduces true edge-triggered behavior. This is the software echo of the hold-time and non-blocking-assignment issues you'll meet in Stage 2 RTL.

Key terms

Clock tick
One simulated clock edge: sample all flip-flop inputs, commit outputs, then re-settle logic.
Waveform / VCD
A record of each net’s value over time; VCD is the standard format viewers like GTKWave read.
Testbench
Code that drives stimulus into a circuit and checks its outputs automatically.
Sample-then-commit
Reading all sequential inputs before writing any output, to model simultaneous edge-triggered updates.

Ship for Day 31

Why must the simulator capture all flip-flop inputs before committing any of their outputs on a clock edge?

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 31: Project: the gate-level logic simulator (part 2 — DFFs + waveform dump) | RBTechIconX