The Workhorse of Behavioral Verilog
assign describes single-equation combinational logic. As soon as you need if/else, case, or memory, you reach for always. An always block is a piece of procedural code that re-runs whenever specific signals change. The signals that trigger the re-run are the sensitivity list.
There are two shapes of always that you'll see most often:
always @(*)- re-runs whenever any signal read in the block changes. Builds combinational logic.always @(posedge clk)- re-runs only on the rising edge ofclk. Builds clocked sequential logic (flip-flops).
Other forms exist (@(a or b), @(negedge clk), @(posedge clk or negedge reset_n)) but the two above account for almost every synthesizable block you'll write.
Combinational always @(*)
Three things to notice:
outisreg. Anything assigned insidealwaysmust bereg. The keyword doesn't mean "this is a flip-flop"; here it just means "I'm written from a procedural block".always @(*). The*says "wake up whenever anything I read changes". The simulator automatically figures out the sensitivity list. You can write the list manually -always @(sel)- but@(*)is safer because missing a signal is a classic source of bugs.- No clock. This block describes combinational logic. The synthesizer produces a piece of logic that computes
outfromseldirectly - no flip-flops, no clock pin needed.
The default case isn't optional in spirit even though it's optional in syntax. Omit it and any unspecified input value leaves out holding its previous value - which synthesizes to an unintended latch. Always include the default.
Sequential always @(posedge clk)
The key differences from the combinational version:
always @(posedge clk). The block re-runs only on the rising edge ofclk. Nothing happens between edges.- Non-blocking assignment
<=. Inside a clocked block, this is the right operator. It says "schedulecountto take on its new value at the end of the time step", which is exactly how a flip-flop behaves. The why and the alternative are in Blocking vs Non-blocking. - No
defaultneeded. Theifcovers both branches (reset and not-reset). No latch risk.
The synthesizer sees this shape - clocked sensitivity, non-blocking assignment - and produces a 4-bit register (four flip-flops) plus the combinational logic that computes count + 1 and the mux that picks between reset and increment.
The Synthesis Distinction
The same module source can describe two completely different pieces of hardware depending on the always block's shape:
| Block | Hardware |
|---|---|
always @(*) y = expr; | Pure combinational logic. No memory. |
always @(posedge clk) y <= expr; | Flip-flop. Captures expr once per clock cycle. |
always @(*) if (en) y = expr; | Latch - usually a bug. The "else" case keeps the old value. |
always @(posedge clk) if (en) y <= expr; | Flip-flop with enable. Captures only when en is high. |
The third case is the latch trap. A latch is a transparent memory cell that holds its output when the input isn't asserted - useful in specific designs, almost always a bug when accidental. Most synthesis tools warn loudly when they infer a latch you didn't ask for. Treat the warning as an error.
Sensitivity List Variants
You'll see a few less-common sensitivity lists:
always @(a or b or c)- explicit list. Verilog-2001 added the,separator:always @(a, b, c). Either works.always @(posedge clk or negedge reset_n)- asynchronous reset. The block runs on a rising clock edge or a falling reset edge. Used when reset has to take effect immediately, not wait for the next clock.always @(negedge clk)- falling-edge clocking. Rare; some designs use it for "negative-edge-triggered" flip-flops that capture on the falling edge instead of rising.
For new designs, prefer always @(*) for combinational and always @(posedge clk) for sequential. Reach for asynchronous reset only when the design genuinely needs it.
Two Blocks Are Two Pieces of Hardware
Multiple always blocks in the same module are independent - each becomes its own piece of hardware:
The clocked block produces a flip-flop register. The combinational block produces an XOR gate. They live side by side; neither knows about the other. The two outputs change on completely different schedules.
What always Blocks Can't Do
A few things that look tempting but aren't allowed:
- Assigning to a
wire: target must bereg. Compiler enforces. - Assigning to the same
regfrom two differentalwaysblocks: produces undefined behavior in simulation and won't synthesize. One driver per signal. - Reading and writing the same signal in the same combinational block in a way that creates a feedback loop:
always @(*) x = x + 1;is a zero-delay loop the simulator can't resolve.
The first two the compiler catches. The third sometimes only shows up at simulation time as a hang.
What Comes Next
The next doc - Initial Block - covers the sibling of always: a block that runs exactly once at simulation start. It's the workhorse of testbenches. After that, the rules for blocking vs non-blocking assignment that decide whether your clocked block does what you meant.
Frequently Asked Questions
What is an always block in Verilog?
always introduces a procedural block that re-runs whenever the signals in its sensitivity list change. There are two flavors: always @(*) builds combinational logic (re-runs whenever any input changes), and always @(posedge clk) builds sequential logic (re-runs on each rising edge of clk). The body of an always block can contain if, case, for, and procedural assignments.
What is the difference between always @(*) and always @(posedge clk)?
always @(*) is sensitive to any signal read in the block; it produces combinational logic with no memory. always @(posedge clk) is sensitive only to the rising edge of clk; it produces flip-flops that capture state once per clock cycle. The first has no clock and no register; the second has both.
What is a sensitivity list in Verilog?
The list of signals after @ that determines when an always block re-runs. @(*) is shorthand for 'every signal read in the block'. @(posedge clk) runs only on the rising edge of clk. @(posedge clk or negedge reset_n) runs on either event - used for asynchronous resets. Getting the sensitivity list wrong is one of the most common sources of simulation/synthesis mismatch.
Can you assign to a wire inside an always block?
No. always blocks can only assign to reg (or logic in SystemVerilog). The compiler enforces this. If you want a wire to be the output of procedural logic, declare an intermediate reg, drive it inside always, and assign the wire from the reg outside - or just change the wire to reg.