Menu

Verilog Always Block: Combinational and Sequential Logic

How always blocks work, the difference between combinational always @(*) and clocked always @(posedge clk), and the rules that decide what hardware each produces.

This page includes runnable editors - edit, run, and see output instantly.

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:

  1. always @(*) - re-runs whenever any signal read in the block changes. Builds combinational logic.
  2. always @(posedge clk) - re-runs only on the rising edge of clk. 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:

  • out is reg. Anything assigned inside always must be reg. 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 out from sel directly - 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 of clk. Nothing happens between edges.
  • Non-blocking assignment <=. Inside a clocked block, this is the right operator. It says "schedule count to 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 default needed. The if covers 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:

BlockHardware
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 be reg. Compiler enforces.
  • Assigning to the same reg from two different always blocks: 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.

Coddy programming languages illustration

Learn to code with Coddy

GET STARTED