Intuition The big picture
Combinational logic has no memory : its output depends only on the current inputs. Sequential logic has memory : its output depends on inputs and on the past, which is stored in flip-flops . In Verilog, we describe this stored, clock-driven behaviour using an always block that "wakes up" on a clock edge. The always block is not a loop — it is a sensitivity-driven process that models hardware that reacts to events.
A pure combinational circuit can compute f ( inputs ) f(\text{inputs}) f ( inputs ) but cannot count , wait , remember a state , or sequence steps in time . Every useful digital system — CPUs, counters, FSMs, UARTs — needs to know "where it was." To remember, we need an element that holds a value until told to change , and to keep everyone in sync we change values only at a shared clock edge . That shared drumbeat is what makes millions of gates cooperate reliably.
always block
An always block is a Verilog procedural construct that continuously re-executes whenever a signal in its sensitivity list changes. It is used to model either combinational or sequential logic depending on how it is triggered.
always @(posedge clk) → sequential logic (flip-flops), reacts to a clock edge .
always @(*) → combinational logic, reacts to any input change.
A flip-flop is a 1-bit memory element that samples its input D at the active clock edge and holds it on output Q until the next active edge.
There are two assignment operators and two block styles. Choosing correctly is 80% of avoiding bugs.
Intuition Why two operators?
Real flip-flops all sample at the same instant using their old input values, then update together. If code updated variables one-by-one in program order, a later line would see the new value of an earlier line — which is not how parallel flip-flops behave.
Blocking = executes immediately, in order (like normal software). Good for combinational chains where order = data flow.
Non-blocking <= evaluates all right-hand sides first (using old values), then assigns all left-hand sides simultaneously. This exactly models a rank of flip-flops clocking together.
Worked example Shift register — why
<= matters
Goal: q1 → q2 → q3 shift on each clock (a 3-stage shift register).
always @( posedge clk) begin
q2 <= q1; // uses OLD q1
q3 <= q2; // uses OLD q2
end
Why non-blocking? All RHS (q1, q2) are read before any update. So q3 gets the old q2, giving a true shift.
If we wrote blocking =:
q2 = q1; // q2 now equals q1
q3 = q2; // q3 = q2 = q1 --> WRONG, collapses two stages
Why wrong? Blocking updates q2 first, so line 2 sees the new q2. The two flip-flops merge into one; the shift register loses a stage.
Step 1 — WHAT do we want? A box where Q becomes D only at a rising clock edge, else holds.
Step 2 — HOW in Verilog? "At the rising edge, copy D into Q." That is literally:
always @( posedge clk)
q <= d;
Why posedge? It fires the block only on the 0 → 1 0\to1 0 → 1 transition, so q updates once per cycle.
Why <=? So that if many such flops share clk, they all use pre-edge values — correct parallel behaviour.
Worked example D flip-flop with asynchronous reset
always @( posedge clk or posedge rst) begin
if (rst) q <= 1'b0 ; // Why? reset dominates, forces 0 immediately
else q <= d; // Why? normal capture of D
end
Why put rst in the sensitivity list? Because it is asynchronous — it must act the moment rst rises, not wait for a clock edge. Why check if (rst) first? Priority: reset must override normal operation.
Worked example Synchronous reset (contrast)
always @( posedge clk) begin // rst NOT in list
if (rst) q <= 1'b0 ;
else q <= d;
end
Why different? Here reset only takes effect on a clock edge . Cheaper timing, but reset is ignored until the next clock tick. Choose based on your design's requirement.
Worked example A 2:1 mux, three equivalent ways
always @( * ) begin // Why @(*)? auto-includes ALL read signals
if (sel) y = b; // Why '='? combinational, order = logic flow
else y = a;
end
Why no clock? A mux has no memory; it must respond instantly to any input, so we trigger on any change.
Danger: if an always @(*) path fails to assign y in some branch, the synthesizer infers a latch to "remember" the old value — usually a bug. Fix: assign every output in every branch, or give a default at the top.
Common mistake Steel-manning the common errors
Mistake 1: "always @(posedge clk) is a loop that runs forever fast."
Why it feels right: the word "always" sounds like while(1). Reality: it is event-driven — it executes once per triggering edge , then sleeps. Fix: read it as "whenever this event happens, do the body once."
Mistake 2: "Use = inside clocked blocks, it's simpler."
Why it feels right: blocking behaves like normal code; simulation may even look correct for a single flop. Reality: with multiple registers you create race conditions and simulation/synthesis mismatch. Fix: clocked → <= always.
Mistake 3: "Forgetting a signal in @(a or b) is fine."
Why it feels right: code still compiles and simulates. Reality: simulation ignores the missing signal (stale output) while synthesis includes it → mismatch . Fix: use always @(*) for combinational logic.
Mistake 4: driving the same reg from two always blocks.
Why it feels right: looks like OR-ing behaviours. Reality: two drivers = a race / multiple-driver error. Fix: one register, one always block.
Mnemonic Remember the pairing
"CLOCK gives you a NON-blocking hug (<=); COMB does it BLOCK by block (=)."
Clock ↔ <=, Combinational ↔ =.
Recall Feynman: explain to a 12-year-old
Imagine a class where everyone writes an answer on a mini-whiteboard. A combinational kid shouts the answer the instant they see the question. A sequential kid only flips their board around when the teacher rings a bell (the clock ) — until then they hold their last answer. Now, the tricky part: when the bell rings, all kids must show what they had written before the bell — not peek at their neighbour's new board. Non-blocking <= is the rule "read everyone's old board first, then flip together." Blocking = is "do it one by one," which is fine for the shout-instantly kids but chaos for the bell kids.
Recall Quick self-test (predict before revealing)
Which operator for clocked logic, and why?
What hardware does an unassigned branch in always @(*) accidentally create?
Why must async reset be in the sensitivity list?
What triggers a sequential always block? A clock edge, e.g. always @(posedge clk).
Which assignment for clocked/sequential logic? Non-blocking <=.
Which assignment for combinational logic? Blocking =.
Why does non-blocking <= model flip-flops correctly? All RHS are evaluated using old values, then all LHS update simultaneously — matching parallel flops clocking together.
Why does blocking = break a shift register? Later lines see the just-updated value, collapsing stages instead of shifting.
Is always @(posedge clk) a loop? No — it is event-driven; it runs once per triggering edge then sleeps.
What is an inferred latch and when does it happen? An unwanted memory element created when an always @(*) output isn't assigned in every branch.
How to avoid inferred latches? Assign every output in every branch, or set defaults at the top of the block.
Difference between async and sync reset? Async reset is in the sensitivity list and acts immediately; sync reset only acts on a clock edge.
Why put rst first in the if of a reset flop? To give reset priority over normal data capture.
What does always @(*) mean? Combinational logic sensitive to any signal read inside the block.
What error occurs if two always blocks drive the same reg? Multiple-driver / race condition.
reads old values then assigns
Intuition Hinglish mein samjho
Dekho, digital circuit do type ke hote hain. Combinational logic ki koi memory nahi hoti — jaise ek calculator jo turant answer de deta hai, output sirf current input pe depend karta hai. Sequential logic ke paas memory hoti hai — yeh apni "past state" yaad rakhta hai flip-flops mein. Aur sab flip-flops ek common clock ke edge pe hi apna value update karte hain, taaki poora chip sync mein rahe. Verilog mein hum isko always block se likhte hain.
Sabse important cheez yaad rakhni hai: always @(posedge clk) matlab sequential logic, aur yahan hamesha non-blocking <= use karo. always @(*) matlab combinational logic, aur yahan blocking = use karo. Mantra: "clock ke saath <=, comb ke saath =." Ek common galti — log samajhte hain always ek loop hai jo baar-baar fast chalta hai. Galat! Yeh event-driven hai: jab clock ka edge aata hai, tabhi ek baar body chalti hai, phir so jaata hai.
Non-blocking <= kyun zaroori hai? Socho ek shift register — q1 -> q2 -> q3. <= mein saare right-hand side pehle purane values se read hote hain, phir sab ek saath update hote hain — bilkul real flip-flops jaisa. Agar = use karoge, toh ek line agli line ka naya value dekh legi aur shifting toot jaayegi. Isliye clocked block mein hamesha <=.
Ek aur trap: always @(*) block mein agar kisi branch mein output assign nahi kiya, toh synthesizer ek latch bana deta hai (galti se memory add ho jaati hai). Fix — har branch mein har output ko assign karo, ya top pe default de do. In do rules ko pakka kar lo, aur sequential logic ke 80% bugs khatam.