Every line of the warm-up Verilog, explained from zero hardware background — with each question you asked sitting next to the line that provoked it.
Several of your questions have the same root. Read this once and the line-by-line notes will land much harder.
Verilog Silicon
┌───────────┐ ┌───────────┐
│ what you │ synth │ what gets │
│ write │ ───────▶ │ printed │
└───────────┘ └───────────┘
a DESCRIPTION transistors,
of a circuit wires, cells
not instructions that "run"
It looks like one, and that is the single biggest source of confusion. You are not writing instructions that execute in order. You are writing a description of a physical circuit that a tool called a synthesizer will build out of real transistors.
Every line either describes some wires, some gates, or some memory. There is no "run". The whole thing exists simultaneously and continuously, like plumbing — if you change an input, outputs start changing a fraction of a nanosecond later, forever, without anything being "called".
COMBINATIONAL SEQUENTIAL ┌────────┐ ┌────────┐ │ gates │ │ flops │ └────────┘ └────────┘ output = f(inputs now) remembers no memory needs a clock AND, adder, compare registers, counters
Almost every Verilog rule you are about to meet (wire vs reg, = vs <=, assign vs always) is really just "which of these two are you describing?"
┌─────────┐
D ──▶│ │──▶ Q
│ DFF │
CLK ──▶│ │
└─────────┘
with a reset pin:
┌───────────┐
D ──▶│ │──▶ Q
│ DFF w/ │
CLK ──▶│ reset │
RESET_B▶│ │
└───────────┘
A DFF is a D Flip-Flop: a one-bit memory cell, and the answer to "what is a DFF" in Q5.
Its entire behaviour is one sentence: at the instant the clock goes from 0 to 1, whatever is on D is captured and appears on Q. The rest of the time Q holds steady no matter what D does.
That's it. That is the whole basis of digital state — a CPU register, a counter, RAM, all just piles of these. "D" is historical, for data (or delay).
RESET_B forces Q to 0 immediately, regardless of the clock. The _B means "bar", i.e. inverted, i.e. active-low: the reset happens when the pin is 0. Same convention as rst_n.
A physical reason: on power-up, an undriven wire tends to drift toward ground. Active-low reset means a chip powering up with nothing driving it defaults to being held in reset rather than running on garbage.
sky130_fd_sc_hd__dfrtp_2 └──┬──┘ └┘ └┘ └┘ └─┬─┘ │ │ │ │ │ │ └── drive strength │ │ │ │ └────── the cell itself │ │ │ └─────────── high density │ │ └────────────── standard cell │ └───────────────── foundry └─────────────────────── SkyWater 130nm
sky130_fd_sc_hd__dfrtp_2 \sr_a/_16_ (.CLK(clknet_1_1__leaf_clk),
.D(\sr_a/_00_ ),
.RESET_B(rst_n),
.Q(\a_reg[0] ));
Chip designers do not lay out transistors by hand. The foundry ships a standard cell library: a catalogue of pre-drawn, pre-verified building blocks — an AND gate, a NOR gate, a flip-flop — each already drawn as polygons ready to print on silicon. Synthesis converts your Verilog into a shopping list of these cells plus a wiring diagram.
| Piece | Meaning |
|---|---|
| sky130 | SkyWater 130 nanometre process — the open-source PDK, the one TinyTapeout uses |
| fd | "foundry" — SkyWater's own cells |
| sc | standard cell |
| hd | high density variant of the library (versus high-speed, low-power…) |
| dfrtp | the cell: d-flipflop, reset, true output (gives Q, not Q̄), positive-edge clock |
| _2 | drive strength — a "size 2" version with bigger transistors that push harder into heavier loads. Same logic, physically larger. |
The second block is a real instance from your netlist. Read it as: place one flip-flop cell, name this copy sr_a/_16_, hook CLK to the clock net, D to the wire sr_a/_00_, reset to rst_n, and let its Q drive the wire a_reg[0].
93 tapvpwrvgnd_1 ← filler 58 decap_3 ← filler 16 mux2_1 ← 2-input multiplexers 16 dfrtp_2 ← flip-flops 8 nor2_2 7 and2_2 5 xor2_2 5 or2_2 5 a31o_2 4 nand2_2 3 xnor2_2 3 clkbuf_16 ← clock buffers 2 and4bb_2 1 o21bai_2 and3_2 a21o_2 1 a21boi_2 a21bo_2
This is just a count of cell types in the netlist — a bill of materials for the chip. I produced it by grepping 01_netlist.v and tallying.
When reverse-engineering, this is usually the very first move. You do not yet know what the circuit does, but "there are 16 flip-flops" instantly tells you the design has 16 bits of state, which massively constrains what it could possibly be.
Ignore the top two rows. tapvpwrvgnd cells tie the silicon substrate to power to prevent a failure mode called latch-up; decap cells are little capacitors that steady the power supply. Neither does any logic. They just fill empty space.
The source says parallel_out is [7:0] — 8 bits of memory — and shift_register is instantiated twice. 8 × 2 = 16 one-bit memory cells. The netlist has exactly 16 dfrtp_2. Source and silicon agree. That is what the "✔" meant.
shift_register — the piece with memoryInstantiated twice later, so the netlist contains two complete copies of everything in here. This is where Q1 through Q5 live.
module shift_register (
input wire clk,
input wire rst_n,
input wire en,
input wire serial_in,
output reg [7:0] parallel_out
);
clk — the clock input. See Q1.rst_n — active-low reset. The _n suffix is a naming convention, not syntax; the polarity is established by how it is used on L9.en — enable. Nothing happens on a clock edge unless this is high. This one innocent signal is responsible for 16 extra cells; see Q5.serial_in — the one-bit-per-cycle data input.output reg [7:0] parallel_out — an 8-bit output, declared reg rather than wire. This is the module's state: 8 flip-flops. See Q2 for what reg actually means, and Q9 for what [7:0] means.module shift_register (
input wire clk,
clk ──▶[buf]──┬──▶[buf]──┬──▶ flop
│ ├──▶ flop
│ ├──▶ flop
│ └──▶ flop
└──▶[buf]──┬──▶ flop
└──▶ ...
a balanced tree, so every flop
sees the edge at the same instant
What is fanout, fat routing? What is a clock net?
Fanout is how many input pins a single output drives. An output feeding 3 gates has a fanout of 3.
Why it matters: every input pin is a tiny capacitor. Driving one is easy; driving fifty means charging fifty capacitors through one small transistor, which is slow — like one person pushing fifty shopping trolleys. So high-fanout signals get buffers inserted: a tree of amplifier cells that splits the load. The 3 × clkbuf_16 in the histogram are exactly that (_16 = very high drive strength).
A clock net is the wire carrying clk. It is special because it must reach every single flip-flop — all 16 here — and, critically, arrive at all of them at nearly the same instant. If one flop sees the edge noticeably later than its neighbour, it may capture the new value instead of the old one, and a shift register quietly corrupts itself. That spread is called clock skew, and a whole place-and-route stage ("clock tree synthesis") exists purely to minimise it. That is where clknet_1_1__leaf_clk in the netlist came from: the tool replaced your raw clk with the output of a balanced buffer tree.
Fat routing means physically wider metal wires. Wider metal has lower resistance, so less delay and less skew. Since the clock is the most timing-critical net on the chip, it is usually drawn wider than ordinary signal wires and often on a dedicated upper metal layer.
Open puzzle.gds in KLayout and look for the one net that is visibly wider than everything else and branches in a tree to a huge number of cells. That is the clock, found in ten seconds. Same trick for reset. Identifying those two first removes an enormous amount of visual noise.
output reg [7:0] parallel_out
);
always @(posedge clk or negedge rst_n) begin
output wire [8:0] sum
);
assign sum = a + b;
What is the difference between a wire output and a reg output? What is an always block?
Verilog has two ways to describe hardware, and the wire/reg choice simply records which one you used.
Continuous assignment — for combinational logic:
An assign is not "compute a+b once". It means there is permanently a physical adder here whose output is welded to sum. If a changes at 3pm, sum changes at 3pm plus a few hundred picoseconds. Forever. Targets of assign must be declared wire.
Procedural block — an always block is a chunk of behaviour that re-evaluates whenever something in its trigger list changes. Inside it you may use if/else/case, which reads like software — but the synthesizer's job is to work out what circuit would produce that behaviour. Anything assigned inside an always block must be declared reg.
reg does not mean "register". It is a badly-named leftover meaning only "assigned procedurally". Whether you actually get flip-flops depends entirely on the sensitivity list:
always @(posedge clk) → clocked, real flip-flops.
always @(*) → combinational, just gates, no memory at all.
SystemVerilog fixed this by introducing logic, which covers both cases and spares everyone the confusion.
So on L6, parallel_out is reg purely because L10 and L12 assign it inside an always. It does also become real registers — but because of L8, not because of the word reg. On L19, sum is a wire because it is driven by assign.
always @(posedge clk or negedge rst_n) begin
if (!rst_n)
parallel_out <= 8'b0;
What is a sensitivity list? What does "sky130_fd_sc_hd__dfrtp_2 — a D-Flip-Flop with Reset, True output (Q), Positive-edge clock. There are 16 of them: 8 per shift register × 2 instances ✔" even mean?
The sensitivity list is the @(...) part of an always block: the list of events that wake the block up. Here it reads "re-evaluate this block when clk rises or when rst_n falls."
This exact shape is a template. The synthesizer does not reason about your code in general — it pattern-matches known idioms and swaps in the matching cell:
| Sensitivity list | What you get |
|---|---|
| @(posedge clk) | plain DFF |
| @(posedge clk or negedge rst_n) | DFF with asynchronous active-low reset → dfrtp |
| @(*) | pure combinational gates, no memory |
Async vs sync reset. Mentioning rst_n in the sensitivity list means reset acts immediately, without waiting for a clock edge — the reset wire goes physically into the flip-flop's own RESET_B pin. Had you instead written @(posedge clk) and tested rst_n inside, reset would only take effect at the next clock edge (synchronous) and would be built from a gate on the D input. Different silicon, from a one-word difference.
As for the cell name and the "16": the name decodes to d-flipflop, reset, true output, positive edge — the breakdown table is in Foundations §4. The "16" was a sanity check: your source declares 8 bits of state and instantiates the module twice, so the design must contain 8 × 2 = 16 one-bit memory cells. The netlist contains exactly 16 dfrtp_2. The ✔ meant source and silicon agree — which is the fundamental move of this whole puzzle, just run in the forward direction.
always @(posedge clk or negedge rst_n) begin
if (!rst_n)
parallel_out <= 8'b0;
else if (en)
parallel_out <= {parallel_out[6:0], serial_in};
end
endmodule
// non-blocking — both read OLD values a <= b; b <= a; // a and b SWAP // blocking — top-to-bottom, like software a = b; b = a; // both end up as old b
What is the 8'b0 syntax? What is a non-blocking assignment?
The form is <width>'<base><value>.
| Literal | Meaning |
|---|---|
| 8'b0 | 8 bits, binary, value 0 → 00000000 |
| 9'd496 | 9 bits, decimal, 496 → 111110000 |
| 8'hFF | 8 bits, hex, 255 → 11111111 |
| 1'b1 | single bit, value 1 |
The width is mandatory in good style because in hardware every signal has a fixed physical bit-width — there is no such thing as an unsized integer when it is made of wires. Writing 8'b0 rather than 0 says plainly: eight wires, all held at ground.
= vs non-blocking <=This is the classic beginner cliff, so here is the concrete version. Take two flops currently holding a=1, b=0, and a clock edge arrives.
Non-blocking (<=) means: read all right-hand sides first using the OLD values, then update everything at once. So a gets old b (0) and b gets old a (1) — they swap. This is exactly what real flip-flops do: they all sample simultaneously on the edge, before any of them have updated.
Blocking (=) executes top-to-bottom like software: a becomes 0, then b reads the already-updated a and also becomes 0. No swap. Not what the hardware does.
In parallel_out <= {parallel_out[6:0], serial_in}, every bit must simultaneously take the old value of its neighbour. With <=, all 8 bits sample the old state at once and shift cleanly. With =, bit 1 would grab bit 0's already-shifted value, and the whole register would collapse into eight copies of serial_in.
The rule: <= in clocked blocks, = in combinational blocks. Follow it mechanically.
if (!rst_n)
parallel_out <= 8'b0;
else if (en)
parallel_out <= {parallel_out[6:0], serial_in};
end
┌──── mux2 ────┐
shifted value ────▶│A1 │
│ X ─┼─▶ D ┌─────┐
Q (feedback) ───▶│A0 │ │ DFF │──┬──▶ Q
│ S │ └─────┘ │
en ────────▶└──────────────┘ ▲ │
└─────┘
feedback
sky130_fd_sc_hd__mux2_1 \sr_a/_08_ (.A0(\a_reg[0] ),
.A1(A),
.S(en),
.X(\sr_a/_00_ ));
sky130_fd_sc_hd__mux2_1 \sr_a/_09_ (.A0(\a_reg[1] ),
.A1(\a_reg[0] ),
.S(en),
.X(\sr_a/_01_ ));
What is a DFF? What is the histogram here? I don't understand the explanation of L11 at all.
DFF and histogram are covered in Foundations §3 and §5. Here is L11 properly, because it is genuinely the subtlest thing in the file.
Enumerate the three cases at a clock edge:
rst_n == 0 → load zerorst_n == 1, en == 1 → load the shifted valuerst_n == 1, en == 0 → the code says nothingNotice there is no else clause at all — the if chain just ends at L13.
In software, "says nothing" means do nothing. In hardware, "the value must not change" means you have to physically build something that remembers — which is the definition of state. Verilog's rule is: if a reg is not assigned on some path through the block, it must hold its previous value.
But look at the DFF again. It has D, CLK, Q. There is no "freeze" pin. The clock is still ticking — you must never casually gate a clock, that is how you get skew and glitch bugs — so the flop will capture whatever sits on D at the next edge, unavoidably.
The only way to make Q come out unchanged is to feed Q back into D. If D already equals Q, the capture is a no-op. So the synthesizer inserts a multiplexer — a two-way switch, X = S ? A1 : A0 — in front of every flop, as in the diagram.
With en=1, D gets the new shifted value. With en=0, D gets Q back, so the flop reloads what it already had. Held.
And that is precisely the 16 mux2_1 cells — one per flop. The third block is the real thing from your netlist. You can read the shift register straight off the page: bit 0's mux takes external A, bit 1's takes a_reg[0], bit 2's takes a_reg[1]… each stage fed by its neighbour, with each flop's own output wired to A0 as the hold path. That chain is "enabled shift register" in netlist form.
A mux whose A0 comes from its own flop's Q is control logic — a clock enable — not datapath. When you extract a netlist from puzzle.gds, recognising and filtering out this pattern strips away a large pile of cells that compute nothing, and the actual logic gets much easier to see.
adder8 — pure combinational logicmodule adder8 (
input wire [7:0] a,
input wire [7:0] b,
output wire [8:0] sum
);
assign sum = a + b;
endmodule
assign onto a wire, no clock anywhere. Note Verilog's width rules — because the left-hand side is 9 bits, both operands are zero-extended to 9 bits before the addition, so the carry is captured correctly. Had sum been declared [7:0], the carry would be silently dropped.Synthesis expands this one line into a ripple-carry-style chain of xor2, and2, nand2, or2, a21o and a31o cells: the XORs generate the sum bits, the AND/OR cells propagate the carry.
A chain of XOR2 cells with AND/OR cells interleaved between them is the classic signature of an adder in an unknown netlist.
assign sum = a + b;
a_reg ─┐
├─▶[ adder gates ]─▶ sum
b_reg ─┘ ▲
│
garbage while the carry
ripples; correct only
after it settles
flops sample AFTER settling.
that is what the clock is for.
When add is assigned as you call 'purely combinational', why is a clock even needed?
The adder itself needs no clock — you are right. It is a lump of gates, permanently computing. If you could wiggle a_reg and b_reg directly with your fingers, sum would follow with no clock anywhere in sight.
The clock is needed for the parts around it:
a and b steady. The adder's inputs come from flip-flops. Those flops are what need clocking. The adder is a passive consumer of whatever they present.A is one wire delivering 8 bits over time. "Over time" requires a heartbeat that defines when one bit ends and the next begins. That is the clock.a_reg changes, the carry ripples through the adder gate by gate, and for a few hundred picoseconds sum shows meaningless intermediate garbage before settling. Flip-flops paper over this: they only sample at clock edges, and the clock period is chosen to exceed the worst-case settling time.That third point is the fundamental discipline of synchronous design: let combinational logic settle, then latch it. Combinational logic does the computing, flip-flops do the remembering, and the clock says when it is safe to move from one to the other.
comparator496 — the magic constantmodule comparator496 (
input wire [8:0] val,
output wire eq
);
assign eq = (val == 9'd496);
endmodule
9'd496 = 1 1 1 1 1 0 0 0 0
│ │ │ │ │ └─┴─┴─┴── must be 0
└─┴─┴─┴─┴────────── must be 1
b8 b0
L28 — 9'd496 is a 9-bit decimal literal, binary 1_1111_0000. So the comparison is: bits [8:4] must all be 1, and bits [3:0] must all be 0.
Equality against a constant is far cheaper than a general comparator. It collapses into one big AND of the true bits and the inverted false bits — which is what the 8 × nor2_2 (checking that groups of low bits are zero) and 2 × and4bb_2 (AND with two inverted inputs) are doing in the netlist.
A wide AND/NOR tree converging to a single output bit is the signature of a comparison against a magic constant. Finding that constant is very often the goal of a puzzle like this one — so on the real puzzle.gds, work backwards from the success pin and this is roughly what you should expect to run into.
adder_demo — the top levelThe only module that becomes anything physical at the chip boundary. Q7, Q8 and Q9 live here.
module adder_demo (
input wire clk,
input wire rst_n,
input wire A,
input wire B,
output wire S,
input wire en
);
6 pins 16 bits of state ──────── ───────────────── clk a_reg [7:0] rst_n b_reg [7:0] A ──serial──▶ B ──serial──▶ 8 cycles to fill S en
How do you know only module adder_demo is the one whose ports survive as physical I/O pins in the GDS? And what does "6 pins but 16 bits of internal state" mean?
The hierarchy points at it. adder_demo instantiates the other three (L42, L47, L52, L56); nothing instantiates adder_demo. That makes it the top module — the root of the tree, the outermost box. Its ports are the boundary of the design; everything else is internal plumbing between sub-boxes. In a real flow you also explicitly tell the tool -top adder_demo.
And hierarchy is fictional anyway. Module boundaries are an organisational convenience for humans. Synthesis flattens them: the two shift_register instances get copied out and 32 loose cells end up in one flat pile. Open 01_netlist.v and there is exactly one module keyword — the sub-modules survive only as name prefixes like \sr_a/_16_. Even those prefixes are stripped by the GDS stage.
Physical pins exist only where silicon meets the outside world: bond pads wired out to package pins. There is simply nowhere for adder8's ports to physically be.
Pins are genuinely scarce and expensive. Each needs a bond pad, a package pin and a board trace, and pads are enormous compared to logic cells. This design has six: clk, rst_n, A, B, S, en.
But the circuit needs 16 bits of input data — 8 for a_reg, 8 for b_reg. With six pins you cannot have 16 data wires. Two options:
A, B), one bit each per clock cycle, 8 cycles to fill both registers. This is what the design does.Serialising is trading time for pins. The shift registers are the mechanism: they accept a dribble of bits over 8 cycles and turn it into a wide parallel value the adder can consume in one go. Same trade-off as SPI, I²C or USB versus a 64-bit parallel bus.
Your only lever on puzzle.gds is toggling a handful of pins over a sequence of clock cycles — exactly what example_inputs.vcd shows. The answer is therefore not a static value; it is a waveform: which pins are high on which cycles.
input wire A,
input wire B,
output reg [7:0] parallel_out
│ │
│ └── LSB
└────── MSB
[7:0] = indices 7 down to 0
= 8 bits
What is the shape of A and B, how do you conclude that they are 8 bits in size, from type inference? Is the .clk(clk) syntax how you pass arguments to shift_register? What is dfrtp?
Verilog does no type inference. Widths are always explicit or defaulted — and here is the correction worth internalising: A and B are not 8 bits. They are 1 bit each.
On L34, input wire A has no [x:y], so it defaults to a single bit — one physical wire, carrying one bit per clock cycle.
The 8 comes from the register, on L6: [7:0] declares a vector — indices 7 down to 0, so 8 bits, MSB at 7 and LSB at 0. The convention is [MSB:LSB], descending. And sum is [8:0], one wider, for the carry.
So the shape is: A and B are each one wire carrying a serial bit-stream. The eight-bit-ness lives in the shift register that accumulates them, not in the pin.
Which direction the stream fills the register is decided by the concatenation on L12 — worth working out yourself from first principles, since it determines your bit ordering and therefore your answer.
.clk(clk) is the connection syntaxIt is called a named port connection. Read .clk(clk) as "connect the port named clk to my local signal named clk." Those are two different namespaces that happen to share a name; .serial_in(A) shows the general case. The breakdown is in the next block.
The alternative is positional — shift_register sr_a (clk, rst_n, en, A, a_reg); — which depends on port order and goes silently wrong the moment anyone reorders the port list. Always use named.
d-flip-flop, reset, true output, positive edge. Related cells in the same library follow the same scheme:
| Cell | What differs |
|---|---|
| dfrtp | reset, true output, positive edge — the one you have |
| dfstp | set instead of reset |
| dfxtp | plain — no reset, no set |
| dfbbn | both set and reset, both outputs, negative edge |
The full catalogue is the SkyWater PDK cell reference — worth bookmarking, you will be looking up cell names constantly while decoding puzzle.gds.
wire [7:0] a_reg, b_reg;
wire [8:0] sum;
module adder_demo (
input wire clk, ┐
... ├ PORTS
input wire en ┘
); ◀── port list ends here
wire [7:0] a_reg; ┐
wire [8:0] sum; ┘ INTERNAL
...
endmodule
How do you know a_reg, b_reg and sum are internal nets? What is a net btw?
A net is a physical electrical connection — a wire, or more precisely a set of pins all shorted together at the same voltage. "Net" is the netlist word for it; wire is the Verilog word for the same thing. A netlist is literally the list of nets: which cell pins connect to which. That is all a circuit fundamentally is — cells plus nets.
As for how I know these are internal: it is pure scoping. Ports are the names listed between the ( on L31 and the ) on L38, each tagged input or output. a_reg, b_reg and sum appear after the port list closes, with no direction keyword — so they are local declarations, visible only inside adder_demo. Same rule as local variables inside a function.
Their job is to connect one instance's output to another's input: L44 has sr_a driving a_reg, and L53 has add0 consuming it. Purely internal plumbing. In silicon they are real copper traces between cells, but they never reach a pad.
01_netlist.v still calls them \a_reg[0], \sum[3] and so on. By 04_final.gds those names are deleted. You get anonymous geometry and have to re-derive what each net means from structure alone.
shift_register sr_a (
.clk(clk), .rst_n(rst_n), .en(en),
.serial_in(A), .parallel_out(a_reg)
);
shift_register sr_b (
.clk(clk), .rst_n(rst_n), .en(en),
.serial_in(B), .parallel_out(b_reg)
);
shift_register sr_a ( .clk(clk), ... ); │ │ │ │ │ │ │ └─ signal in adder_demo │ │ └────── port inside shift_register │ └────────────── instance name └───────────────────────────── module being instantiated
Two instances of the same module, using named port connections. They share clk, rst_n and en, and differ only in their serial input and output bus.
Nothing is invoked here. shift_register sr_a (...) means place a physical copy of this circuit here and wire it up. sr_a and sr_b are two separate piles of silicon existing simultaneously — not two calls to one function. The instance name is a label so you can refer to that specific copy, which is exactly why the netlist has \sr_a/_16_ and \sr_b/_16_ as distinct cells.
Because they are identical and share control signals, place-and-route often lays them out as two visually similar rows — another handy cue when you are staring at an unfamiliar layout.
adder8 add0 (
.a(a_reg), .b(b_reg), .sum(sum)
);
comparator496 cmp0 (
.val(sum), .eq(S)
);
endmodule
The whole design in one line of dataflow:
A ─▶[sr_a]─▶ a_reg ─┐
├─▶[add0]─▶ sum ─▶[cmp0]─▶ S
B ─▶[sr_b]─▶ b_reg ─┘
Serial bits in on two pins, accumulated into two 8-bit registers, added, compared against a constant, and the single-bit verdict goes out on S.
That last hop — combinational logic converging on one output pin — is structurally the same thing you are looking for in the real puzzle, where the pin is called success.
Everything introduced above, in one place.
| Term | Meaning |
|---|---|
| combinational | Logic with no memory; output is a function of inputs right now. Gates. |
| sequential | Logic with memory; needs a clock. Flip-flops. |
| DFF | D flip-flop. One bit of memory. Captures D onto Q at a clock edge. |
| net | An electrical connection — a set of pins shorted together. A wire. |
| netlist | The list of cells and the nets connecting them. What a circuit fundamentally is. |
| standard cell | A pre-drawn, pre-verified logic block from the foundry's library. |
| synthesis | Converting Verilog into a netlist of standard cells. |
| place & route | Deciding where each cell physically sits and drawing the wires between them. |
| GDS / GDSII | The final layout file: pure polygons per layer, ready to manufacture. Names stripped. |
| fanout | How many input pins one output drives. |
| drive strength | Transistor sizing of a cell; the _1, _2, _16 suffix. Bigger drives heavier loads. |
| buffer | An amplifier cell inserted to help a high-fanout signal reach everything in time. |
| clock skew | The spread in arrival time of a clock edge across different flops. Must be tiny. |
| clock tree | The balanced buffer network that distributes the clock with minimal skew. |
| active-low | A signal that acts when it is 0. Marked _n or _b. |
| async reset | Reset that acts immediately, not at a clock edge. Wired to the flop's own reset pin. |
| sensitivity list | The @(...) events that wake an always block. Determines what cell you get. |
blocking = | Executes top-to-bottom. For combinational blocks. |
non-blocking <= | All right-hand sides read old values, then all update at once. For clocked blocks. |
| vector | A multi-bit signal, declared [MSB:LSB], e.g. [7:0] for 8 bits. |
| mux | Multiplexer. A switch: X = S ? A1 : A0. |
| clock enable | Holding a flop's value by muxing its own Q back into its D. Not a real flop pin. |
| flattening | Synthesis dissolving module boundaries into one flat pile of cells. |
| top module | The module nothing else instantiates. Its ports become physical pins. |
| serialising | Sending a wide value one bit at a time over few pins. Trading time for pins. |
| tap cell | Filler that ties the substrate to power, preventing latch-up. No logic. |
| decap cell | Filler capacitor that steadies the power supply. No logic. |
| PDK | Process Design Kit — everything the foundry gives you to design for their process. |
| VCD | Value Change Dump — a waveform file recording how signals changed over time. |
puzzle.gds00_source.v ← you are here
↓ synthesis
01_netlist.v ← trace sr_b next
↓ power
02_..._power_rails.v
↓ place & route
03_post_place_...def
↓ stream out
04_final.gds ← names gone
the puzzle runs this backwards
sr_b yourself in 01_netlist.v, the same way sr_a is traced in Q5. Find its 8 muxes and 8 flops and confirm the chain hangs together.xor2 cells and see whether you can pick out the adder's bit-slice structure by hand.04_final.gds in KLayout alongside 03_post_place_and_route.def. The DEF still has names, so you can check your reading of the geometry against ground truth — which is the one chance you get to calibrate before the real puzzle, where there is no answer key.Once the warm-up netlist reads naturally, the jump to extracting one from GDS is mostly a tooling problem rather than a conceptual one.