Question bank — Verilog - VHDL syntax basics
The one idea underneath everything: HDL text describes hardware that all exists at once, not a program that runs line by line. Almost every trap below is a disguised version of forgetting that.
First, the vocabulary these traps use (build it before you test it)
Every trap on this page is built from a small set of Verilog/VHDL tokens. If any of these feels unfamiliar, read this whole section first — you cannot debug a symbol you have never been shown. Each token below is drawn as a piece of real hardware in the figures.
The five structural keywords (what the boxes and wires are called)
The behaviour keywords
The picture that ties the structural tokens together

Figure s01 (alt text, in case the image does not load): a large rectangle labelled module. On its left edge, two arrows point inward labelled input a and input b. On its right edge, one arrow points outward labelled output y. At the bottom edge a double-headed arrow labelled inout io carries a small switch symbol showing it can drive or release to Z. Inside the box sits an AND gate fed by a and b, its output continuously driving y — this is the assign y = a & b; wire.
wire versus reg, side by side

Figure s02 (alt text): two panels. LEFT — a bare copper trace labelled wire, driven only by an assign gate, with a note "no memory: means nothing if the driver is cut." RIGHT — a name labelled reg, shown twice: once inside always @(*) collapsing into pure combinational logic (no storage box), and once inside always @(posedge clk) becoming an actual flip-flop with a clock triangle. Caption: reg is only "assignable in a procedural block" — the block decides logic vs storage.
Why <= models a bank of flip-flops (read before the swap trap)

Figure s03 (alt text): a rising clock edge (0→1) on the left triggers two flip-flops a and b. Crossed arrows show a sampling old b and b sampling old a at the same instant, then both flipping together — a genuine swap. A red note shows that with blocking = instead, a changes first so b copies the new a and the swap collapses.
The delta-cycle race — why <= in a combinational block is a trap

Figure s04 (alt text): a timeline of one simulation instant split into tiny "delta cycles." A chain always @(*) b <= a; always @(*) c <= b; is drawn. Because <= defers updates, c uses the OLD b in this delta, so a value that should ripple through in zero time takes several deltas — meaning simulation shows stale values and needs extra iterations, while the synthesized hardware is just plain wires. The mismatch is highlighted in burnt orange. The fix use = for combinational is shown in teal, where the update ripples in one pass.
Concurrent <= in VHDL is a wire, not a register

Figure s05 (alt text): two panels for the same VHDL symbol <=. LEFT — outside a process, y <= a and b; is drawn as a permanent AND gate wire (identical hardware to Verilog assign), captioned "concurrent signal assignment = combinational wire." RIGHT — inside process(clk) with if rising_edge(clk), q <= d; becomes a flip-flop with a clock triangle, captioned "same symbol, edge-triggered register." The point: in VHDL the context (concurrent vs process), not the <= symbol, decides gate versus register.
True or false — justify
"Two assign statements written in different orders produce different hardware."
assign is a separate gate soldered on simultaneously, so page order is irrelevant; only inside one always block with blocking = does order matter."Inside always @(posedge clk) the lines execute strictly top-to-bottom like C."
<= all right-hand sides are sampled at the edge first, then all left-hand sides update together; there is no sequential overwrite between them."A wire can be assigned inside an always block."
always must be reg, because it must hold its value between events; a wire has no storage and would be a syntax error."Declaring a signal reg means the synthesizer always builds a flip-flop for it."
reg only means "assignable in a procedural block"; inside always @(*) it synthesizes to pure combinational logic, not a register."Non-blocking <= is 'better' so you should use it everywhere to be safe."
<= in combinational always @(*) still works in synthesis but creates the delta-cycle races drawn in figure s04, where chained values read stale copies; the rule pairs <= with clocks and = with combinational for a reason."In VHDL a concurrent y <= a and b; uses the same <= as a clocked register, so they mean the same thing."
process the <= is a concurrent signal assignment (a permanent combinational wire), while inside a clocked process it schedules an edge-triggered update; same symbol, different context."An assign statement can describe a flip-flop if you just add a clock to the expression."
assign is continuous combinational logic with no notion of an edge; storage requires an edge-sensitive always/process, so there is no way to "add a clock" to assign."A VHDL entity with no architecture is a complete, usable design."
entity is only the interface (ports); without an architecture describing behaviour there is nothing to synthesize."8'hA5 and 8'b10100101 describe two different values."
hA5 in hex equals 10100101 in binary, just a different base notation for identical wires."inout ports are just a fancy way to write two ports in one line."
inout is a genuinely bidirectional physical line that at any instant is either driving a value or in high-impedance Z so someone else can drive; it is not shorthand for a separate in and out."On a shared bus, two drivers outputting 1 and 0 simply give whichever came last."
X (contention); the whole point of Z is that only one driver is active while the rest float, so there is never a "last one wins.""always @(posedge clk) and always @(posedge clk or posedge rst) build the same flip-flop."
rst to the sensitivity list making it asynchronous (reset acts the instant rst rises, no clock needed).Spot the error
Error: always @(posedge clk) begin a = b; b = a; end — meant to swap a and b.
= executes in order: line 1 makes a equal b, then line 2 copies the new a back into b, so both end up b (see figure s03). Use <= so both read old values first and genuinely swap.Error: always @(a) y = a & b; — a 2-input AND gate.
b, so y won't re-evaluate when b changes — simulation diverges from the real gate. Use always @(*) to catch every input automatically.Error: always @(*) begin if (sel) y = d0; end — an intended combinational mux path.
y is unassigned when sel is 0, so the tool must remember the old value and infers an unwanted latch. Give y a default before the if, or add an else.Error: wire y; assign y = a & b; assign y = a | b; — two drivers on one wire.
y); the result is x (unknown) in simulation. One wire, one driver — or a tri-state discipline where only one drives at a time.Error: module m(a, b, y); assign y = a & b; endmodule with no direction on ports.
input/output direction, so the tool can't tell which cross the boundary which way. Every port needs a declared direction.Error: always @(*) begin b <= a; c <= b; end — chained combinational logic with <=.
c reads the old b (figure s04); the value takes extra simulation passes to settle and mismatches the plain-wire hardware. Use = for combinational so it ripples in one pass.Error: always @(posedge clk) q = d; inside a design with several chained registers.
= on a clock edge lets one register's new value feed the next in the same edge, silently collapsing a pipeline into fewer stages (a shift-register bug). Clocked logic must use <=.Error: assign bus = 4'b1010; where bus is declared wire [7:0] bus;.
8'b00001010 or a concatenation.Error: always @(posedge clk or posedge rst) if (rst) q <= 0; else q <= d; but rst is checked after other logic.
if; otherwise the tool cannot map it to the flip-flop's dedicated async-reset pin and may synthesize incorrectly. Always write if (rst) ... else ... at the top.Error: A bidirectional pin driven as assign io = data; with no enable, meant to be a shared bus.
data permanently, so no other device can ever put a value on the line. The correct idiom is assign io = oe ? data : 1'bz; so it releases to Z when not enabled.Why questions
Why does HDL force every literal to carry a width and base like 8'hA5?
Why do we even need two assignment operators instead of one?
=) and an edge-triggered register that updates all at once (<=) — and each operator models the timing of its flavour correctly.Why does non-blocking <= correctly model a bank of flip-flops sharing one clock?
<= reads every right-hand side first and updates every left-hand side together, matching that simultaneity (figure s03).Why is "HDL is not software" the single biggest beginner warning?
Why does an incomplete combinational path infer a latch rather than defaulting to 0?
Why must a signal assigned inside always be reg even when it ends up combinational?
reg is Verilog's procedural-storage type used for any assignment inside a procedural block; whether it becomes a register or pure logic is decided by the block's sensitivity, not the keyword (figure s02).Why does VHDL split an interface (entity) from behaviour (architecture) at all?
Why do we put a signal into high-impedance Z instead of just not writing to it?
Z is the explicit "I am letting go, drive me elsewhere" state that lets multiple devices share one physical line without fighting, which plain "no assignment" cannot express.Why does an asynchronous reset appear in the sensitivity list but a synchronous one does not?
posedge clk block.Why does the same VHDL <= symbol build a wire in one place and a register in another?
<= outside a process is a permanent combinational wire, while a <= inside a clocked process schedules an edge-triggered register update.Edge cases
What hardware results from assign y = a; when a is a 1-bit input and y a 1-bit output?
What happens on the very first clock edge of a flip-flop before any reset runs?
q starts as x (unknown) in simulation and an undefined power-up value in hardware; that's why real designs assert reset first.What does always @(posedge clk) do if clk never toggles in simulation?
x forever — the design appears "frozen," which is a clue your clock stimulus is missing, not a syntax bug.What is the meaning of an empty sensitivity list vs always @(*)?
always @(*) auto-includes every signal read inside; an empty or hand-written list that omits signals is the classic source of missing-input latches and simulation/hardware mismatch.What is the visible symptom of using <= in a chained combinational always @(*) block?
What happens if you concatenate mismatched widths, e.g. {a, b} feeding a wider destination?
width(a)+width(b) bits; if that total is smaller than the destination the upper bits zero-extend, if larger it truncates — both silent, so always count bits.What does a synchronous reset (if (rst) q <= 0; inside posedge clk) do when rst is high but no clock edge occurs?
posedge clk.What does an asynchronous reset (always @(posedge clk or posedge rst)) do when rst rises between two clock edges?
q to its reset value immediately, without waiting for a clock edge — that instantaneous action is exactly the trade-off (fast, but reset timing is not clock-aligned).What is the resolved value on a tri-state bus when no driver is active (all in Z)?
Z and reads as unknown unless a pull-up/pull-down resistor pins it to a defined 1 or 0 — floating inputs are a real hardware hazard, so shared buses usually add a weak pull.What happens when two tri-state drivers on one bus are both enabled with opposite values?
X (in simulation) and real current flows between drivers (a short) in hardware — the enable logic must guarantee at most one driver is active at a time.Recall One-line self-test before you move on
Cover the answers and ask: "For each trap, could I explain it to a robot builder using wires and a bell, without the words blocking or concurrent?" If yes, you own the concept, not just the rule.
Connections
- Combinational vs Sequential Logic — the two behaviours every trap here maps onto.
- Flip-Flops and Latches — inferred-latch, sync vs async reset, and
Z-state edge cases live here. - Synthesis vs Simulation — why
=in a clocked block causes silent mismatches. - Finite State Machines in HDL — combines both styles, so both trap families apply.
- Number Systems and Bit-Widths — width/base and concatenation-width traps.
- Testbenches — the one place procedural code does run sequentially.