Draft preview. Lessons are being written and reviewed.

Digital Design / Module 3: Verilog that means hardware

14. assign and always blocks

Pick the right construct for each kind of logic.

Lecture coming soon

See it move

The idea

There are three common ways to describe hardware, and each has a clear purpose.

ConstructDescribesAssignment
assignSimple combinational logic=
always @(*) or always_combLarger combinational logic=
always @(posedge clk) or always_ff<=

assign

An assign is a permanent connection. The left side follows the right side whenever it changes. This is the simplest form, and it suits one-line expressions.

always blocks

For logic with case or several conditions, an always block reads better. @(*) means "re-evaluate whenever any input changes", so the block behaves as combinational logic.

// The same 2:1 mux, written two ways
assign y = sel ? b : a;

always_comb begin
  if (sel) y = b;
  else     y = a;
end

Why always_comb and always_ff are better

They state your intent, and tools check it. Accidentally create a inside always_comb, and you get a warning or an error. A plain always @(*) accepts it silently. always_comb also runs once at time zero, which avoids some start-up simulation mismatches. Use these forms whenever your tools support SystemVerilog.

Sequential logic

always_ff @(posedge clk)
  q <= d;

This describes a flop for each bit of q. Everything inside happens at the clock edge.

Common mistake

Writing a sensitivity list by hand, such as always @(a) when the logic also reads b. Simulation won't react to b, whereas the synthesized gates will. The two then disagree, and it is painful to debug. Use @(*) or always_comb.

Key ideas

  • assign is continuous.
  • always @(*) describes combinational logic; always @(posedge clk) describes registers.
  • SystemVerilog always_comb and always_ff state your intent.

Your exercise

Write the same mux with assign and with always_comb.

Find the bug

This code has one deliberate bug. Read the symptom, then click the line you think is wrong.

What goes wrong: In simulation y ignores changes on b until a changes. The synthesized gates would respond to b straight away.

Interview questions

coreWhy prefer always_comb over always @(*)?

basicWhich construct would you use for a one-line expression, a decode written with case, and a register?

basicA 2:1 mux is written once with assign and once with always @(*) and if-else. Is the hardware different?

coreWhat hardware do these two blocks describe?

always @(*)          y = a & b;

always @(posedge clk) q <= a & b;

coreThis block reads b but has only a in its sensitivity list. What goes wrong?

always @(a)
  y = a & b;

coreWhy should a signal not be assigned from two different always blocks?

coreHow does adding posedge rst to the event list change this flop?

always @(posedge clk or posedge rst)
  if (rst) q <= 1'b0;
  else     q <= d;

advancedWhat is the value of k in simulation, and why?

reg k;
always @(*) k = 1'b1;

advancedWhy does a hand-written sensitivity list cause bugs that @(*) avoids, even when the list is complete on the day it is written?

Practise these 9 questions with animations