1. A multiplexer picks one of its inputs
A multiplexer has data inputs, a select input and one output. The select decides which data input reaches the output; the others are ignored. Nothing is stored: change the select and the output switches at once. Operate the two-input version below until the rule is obvious.
Concept experiment · mux2
A browser model of the circuit, not a compiled design. The real toolchain runs in the examples.
Inputs
Outputs
- y
- 0
| sel | a | b | y | current inputs |
|---|---|---|---|---|
| 0 | 0 | 0 | 0 | ◂ current inputs |
| 0 | 0 | 1 | 0 | |
| 0 | 1 | 0 | 1 | |
| 0 | 1 | 1 | 1 | |
| 1 | 0 | 0 | 0 | |
| 1 | 0 | 1 | 1 | |
| 1 | 1 | 0 | 0 | |
| 1 | 1 | 1 | 1 |
Check
In the mux2 circuit, sel is 1, a is 0 and b is 1. What is y?
This chapter writes that multiplexer in Verilog as an assign and as procedural blocks, then runs
all three forms side by side. The forms assign different kinds of signal, so the kinds come first.
2. Nets and variables are two kinds of signal
Verilog signals come in two kinds, nets and variables. They differ in how they receive a value and in whether they keep it.
| Kind | Typical declaration | How it gets a value | Does it keep the value? |
|---|---|---|---|
| Net | wire. A port is a wire unless you say otherwise | assign, or the output of another module | No. It always shows whatever drives it |
| Variable | reg, integer | Assignment statements inside initial or always | Yes. It remembers the last value assigned |
A good first picture: a net is a wire in the circuit, and a variable is a value that the simulation
keeps between the procedural assignments that write it. That is not the same as hardware storage: a
variable assigned in always @* describes combinational logic, and only a variable assigned on a
clock edge becomes a register, as Registers and
nonblocking assignment shows.
Note. The name reg suggests a hardware register, but declaring something reg does not create
a flip-flop. The hardware you get is decided by how the signal is assigned, not by the keyword that
declares it.
3. Continuous and procedural assignment
Matching the two kinds of signal, there are two kinds of assignment.
| Assignment | Syntax | Where it appears | Target |
|---|---|---|---|
| Continuous | assign target = expression; | Module body | A net |
| Procedural | target = expression; or target <= expression; | Inside initial or always | A variable |
A continuous assignment is a connection that holds at every moment. A procedural assignment happens at one particular moment, and its target has to keep the value until the next assignment. A net cannot keep a value, so a procedural assignment to a net is a compile error.
The opposite case, assign to a variable, depends on the language version.
Language version (iverilog option) | assign to a reg |
|---|---|
Verilog-2005 (-g2005) | An error: reg y; cannot be driven by primitives or continuous assignment. |
SystemVerilog (-g2012) | Allowed, as long as nothing else drives it |
The examples in this course are compiled with -g2012.
Tip. SystemVerilog's logic is a four-state data type, not a replacement for the net/variable
distinction: logic y; declares a variable, while wire logic y; declares a net. This course uses
wire and reg to keep that distinction visible.
4. The first way: assign with ?:
The conditional operator ?: is the multiplexer written as an expression. cond ? x : y is x
when the condition is true and y otherwise, and the whole thing is a wire.
module mux2 (
input sel,
input [3:0] a,
input [3:0] b,
output [3:0] y
);
assign y = sel ? b : a;
endmoduleBoth branches are evaluated at the context width of Literals, signed values and expression width, so a and b should
be the same width as y. For a two-way choice this is the shortest and clearest form.
5. The second way: always @* with case
When there are several choices, a case inside a procedural block reads better than nested ?:.
always @* means "run this block again whenever any signal it reads changes", which is exactly the
behavior of combinational logic; it is how you write combinational logic as procedural code. The
output must be declared reg, because only a variable can be assigned inside always.
module mux4 (
input [1:0] sel,
input [3:0] a, b, c, d,
output reg [3:0] y
);
always @* begin
case (sel)
2'd0: y = a;
2'd1: y = b;
2'd2: y = c;
default: y = d;
endcase
end
endmoduleThe word reg here does not create storage. The block recomputes y from the current inputs every
time one of them changes, so y is a combinational output exactly like the assign version. What
makes a variable a register is how it is assigned, which is the next chapter's subject.
Note. The examples compile with iverilog -g2012. always @* is Verilog-2001 and is what this
course uses. SystemVerilog adds always_comb for the same purpose; it is not used here.
Check
In mux4, sel is 2'd2. Which input reaches y?
6. Three ways, one multiplexer, compared by simulation
A third legal form is an if/else if chain inside always @*. Mux4.v writes the same 4-to-1
multiplexer of 1-bit inputs all three ways: mux4_assign with nested ?:, mux4_if with an
if/else chain and mux4_case with a case and a default. There is no clock anywhere in the
file, yet two of the three outputs are declared output reg.
// One 4-to-1 multiplexer, written three legal ways.
module mux4_assign (
input [1:0] sel,
input a, b, c, d,
output y
);
assign y = (sel == 2'd0) ? a :
(sel == 2'd1) ? b :
(sel == 2'd2) ? c : d;
endmodule
module mux4_if (
input [1:0] sel,
input a, b, c, d,
output reg y
);
// reg here is a variable, not a flip-flop: there is no clock.
always @* begin
if (sel == 2'd0) y = a;
else if (sel == 2'd1) y = b;
else if (sel == 2'd2) y = c;
else y = d;
end
endmodule
module mux4_case (
input [1:0] sel,
input a, b, c, d,
output reg y
);
always @* begin
case (sel)
2'd0: y = a;
2'd1: y = b;
2'd2: y = c;
default: y = d;
endcase
end
endmoduleCheck
Mux4.v declares y as output reg in mux4_if and mux4_case. How many flip-flops does the whole file describe?
Tb.v places all three modules on the same inputs. It first holds a=0 b=1 c=1 d=0 and steps
sel from 0 to 3, printing one SEL line per value with the three outputs side by side. Then it
runs through all 64 combinations of sel and the four data bits, compares mux4_if and
mux4_case against mux4_assign with !==, and prints how many combinations disagreed.
module tb;
reg [1:0] sel;
reg a, b, c, d;
wire y_assign, y_if, y_case;
integer i, mismatches = 0;
mux4_assign u_assign (.sel(sel), .a(a), .b(b), .c(c), .d(d), .y(y_assign));
mux4_if u_if (.sel(sel), .a(a), .b(b), .c(c), .d(d), .y(y_if));
mux4_case u_case (.sel(sel), .a(a), .b(b), .c(c), .d(d), .y(y_case));
initial begin
// One data pattern, every select value: a=0 b=1 c=1 d=0.
{a, b, c, d} = 4'b0110;
for (i = 0; i < 4; i = i + 1) begin
sel = i;
#1 $display("SEL %0d assign=%b if=%b case=%b", sel, y_assign, y_if, y_case);
end
// Every combination of sel and data: 4 x 16 = 64.
for (i = 0; i < 64; i = i + 1) begin
{sel, a, b, c, d} = i;
#1;
if (y_if !== y_assign || y_case !== y_assign) mismatches = mismatches + 1;
end
$display("COMPARED 64 combinations mismatches=%0d", mismatches);
$finish;
end
endmodulePredict before you run. For each SEL line, which input is selected and what is its value? Do
the three columns ever differ?
Check
With a=0 b=1 c=1 d=0, what does the SEL 3 line print?
Check the result. The SEL lines read 0, 1, 1, 0, the values of a, b, c, d, and
the three columns agree on every line. The last line is COMPARED 64 combinations mismatches=0: over
all 64 binary input combinations, the three descriptions agree. This comparison does not test
select values containing x or z.
Change one thing. In mux4_case, delete the line default: y = d; and run again. Predict the
SEL 3 line first.
Check
Without the default, what does the SEL 3 line print?
The run now ends with mismatches=8. During the 16 combinations with sel = 3, mux4_case keeps
the 1 it had for the last sel = 2 combination, so it is wrong exactly where d is 0. Put the
default back before you go on. Why a held value is a problem is the next section.
7. Every path through the block must assign the output
Suppose the case in mux4 had only the 2'd0 and 2'd1 branches and no default. For sel equal
to 2 or 3, nothing in the block assigns y. In simulation y then keeps whatever it had before,
which means the description now needs memory of a previous value, and a combinational block was not
supposed to have any. A synthesis tool reads it the same way and has to build a storage element, a
latch, to hold the old value; most tools warn about it, and the circuit is not the multiplexer you
meant. Icarus itself prints no warning; the only sign is the held value in the output.
LatchMux.v is a 4-to-1 multiplexer whose case has branches for 0, 1 and 2 but none for 3 and
no default. Tb.v prints the multiplexer's y next to full, the value a complete multiplexer
would give, after each of five input changes.
// The case has no branch for sel = 3 and no default.
module mux4_missing (
input [1:0] sel,
input a, b, c, d,
output reg y
);
always @* begin
case (sel)
2'd0: y = a;
2'd1: y = b;
2'd2: y = c;
endcase
end
endmodulemodule tb;
reg [1:0] sel;
reg a = 0, b = 0, c = 0, d = 0;
wire y;
// What a complete multiplexer would output, for comparison.
wire full = (sel == 2'd0) ? a : (sel == 2'd1) ? b : (sel == 2'd2) ? c : d;
mux4_missing dut (.sel(sel), .a(a), .b(b), .c(c), .d(d), .y(y));
task show(input integer n);
#1 $display("STEP %0d sel=%0d a=%b d=%b -> y=%b full=%b", n, sel, a, d, y, full);
endtask
initial begin
sel = 3; d = 1; show(1);
sel = 0; a = 1; show(2);
sel = 3; d = 0; show(3);
a = 0; show(4);
sel = 1; show(5);
$finish;
end
endmodule| Step | Inputs set | A complete mux gives |
|---|---|---|
| 1 | sel=3, d=1 | 1 |
| 2 | sel=0, a=1 | 1 |
| 3 | sel=3, d=0 | 0 |
| 4 | a=0, still sel=3 | 0 |
| 5 | sel=1, b=0 | 0 |
Predict before you run. Write the y column yourself. At step 1 nothing has ever assigned y;
at steps 3 and 4 the block runs again, because an input it reads changed, but no branch matches.
Check
At STEP 3, sel is 3 again and d is 0. What does y print?
Check the result.
| Line | y | full | What it shows |
|---|---|---|---|
STEP 1 | x | 1 | y has never been assigned, so it holds its starting value x |
STEP 2 | 1 | 1 | A matching branch: y follows a |
STEP 3, STEP 4 | 1 | 0 | The held value: y remembers STEP 2 although d and a are 0 |
STEP 5 | 0 | 0 | A matching branch again |
Fix it. Add default: y = d; after the 2'd2 branch and run again. Every y now equals
full, STEP 1 included: no path leaves y unassigned, so nothing has to be remembered.
The rule is simple to apply. Either give the case a default branch, or assign the output once
before the case so that every path has a value. The same applies to if without else.
Check
Which of these infers a latch in a combinational block?
8. Blocking = is the assignment for combinational blocks
Inside a combinational block use the blocking assignment =. It updates its target at once, so the
next statement in the block already reads the new value. That is what a chain of combinational steps
needs: compute an intermediate, then use it.
always @* begin
tmp = a & b; // tmp has its new value from here on
y = tmp | c; // reads the new tmp
endThe other assignment, <=, delays the update and is for registers in clocked blocks. Mixing the two
in one block is where most beginners' bugs come from; Registers
and nonblocking assignment draws the line.
Check
In the block above, a = 1, b = 1 and c = 0. After the block runs, what is y?
9. Run a block that fails on purpose
The next example fails to compile on purpose. Majority.v is a majority circuit: its output is 1
when at least two of its three inputs are 1. It is written as an always @* block, but its output y
is declared only as output y, which makes it a net (wire). Yet the code assigns y inside the
block. Decide before running: does this fail when iverilog compiles it, or when vvp simulates it?
module majority (
input a,
input b,
input c,
output y
);
// y is a net, so a procedural block cannot assign it.
always @* begin
y = (a & b) | (a & c) | (b & c);
end
endmodulePractice this concept
Use this concept in a short Verilog problem as your next step.
Check
At which stage does Majority.v fail?
Check the result. Press Run and read the two lines of the message.
| Item | Content |
|---|---|
| Faulty code | y = ... inside an always @* block, while y is a net |
| Failing stage | Compilation in iverilog. vvp never runs |
| Error message | y is not a valid l-value in majority. followed by y is declared here as wire. |
| Fix | Declare output reg y, or replace the block with assign y = ...; |
An l-value is anything that may appear on the left of an assignment. The second line of the message
points at the place where y was declared a wire, so the declaration is the first thing to check.
Then change one thing: make it output reg y and run again. It compiles now. Nothing is printed,
because the file has no testbench; the exit code of 0 is all you get. The other repair is to delete
the block and write the same expression after assign. Both fixes produce the same majority
circuit. For logic that is a single expression, as here, assign is easier to read. For logic with
several branches, a reg target in an always @* block is usually clearer.
10. Common mistakes
10.1 Assigning a net from procedural code
// Wrong: y is a wire
output y;
always @* y = a & b;
// Right, option 1: declare a variable
output reg y;
always @* y = a & b;
// Right, option 2: use a continuous assignment
output y;
assign y = a & b;Anything you assign inside always or initial must be a variable: reg, integer, or, with
-g2012, logic. The error message for this mistake is ... is not a valid l-value.
10.2 Reading a variable before assigning it
reg [7:0] total;
initial $display("%d", total); // prints x, not 0A variable starts as x, not 0. When you see x in the output, check whether the variable was
assigned before it was used, and whether any input of the calculation contains x or z.
10.3 Reading an output right after changing an input
data = 8'hFF;
$display("%b", high); // may still show the old value or x
#1;
$display("%b", high); // shows the new valueAt the instant an input changes, the assign statements that read it may not have been re-evaluated
yet. In a testbench, change the input, wait one time unit with something like #1, and then read
the output.
11. Summary
| Element | Meaning |
|---|---|
net (wire) and variable (reg, integer) | a net is driven by assign and keeps nothing; a variable is assigned in procedural blocks and keeps its value |
assign y = sel ? b : a; | a continuous assignment: a two-way multiplexer as a wire |
always @* … case | combinational logic as procedural code; the target is reg |
?:, if/else, case with default | three legal forms of the same multiplexer; an exhaustive comparison finds 0 mismatches |
reg | a variable, not storage; the assignment style decides |
| every path assigns | otherwise the old value must be kept, and that is a latch |
= in always @* | blocking: the next statement sees the new value |
y = … on a net | a compile error: not a valid l-value |
12. Check your understanding
-
Why must the target of an assignment inside an
alwaysblock be a variable?Show answer
A procedural assignment happens at one moment, and the target has to keep the value until the next assignment. A net cannot keep a value; it always shows whatever drives it, so it cannot be the target of a procedural assignment.
-
output reg [3:0] yis assigned insidealways @*. Does theregmakeya register?Show answer
No. The block recomputes
yfrom the current inputs whenever one of them changes, soyis a combinational output. A variable becomes a register only when it is assigned on a clock edge; the keyword says nothing about storage.
The Practice problem select-op applies this chapter: a 2-bit sel chooses which of four bitwise
operations of two 4-bit inputs drives y, with no clock. Its starter is a case skeleton inside
always @* whose default gives y 0; fill in the four operations so that every select value, and
every path through the block, assigns y.