2.7 Hierarchy, always_comb and multiplexers
Why this matters
No real design is one module. A processor, a game controller and a lock are each built by connecting smaller modules, and half of the skill is deciding where the boundaries go. This section shows how modules connect, how to describe a choice between two or more values, and which constructs the tools reject. It ends with a first attempt at a small guessing game, which puts instances, signals and a comparator together.
Learning goals
Section titled “Learning goals”- Instantiate a module with named connections, with implicit connections, and with
.*. - Describe a choice with
ifandcaseinalways_comb, and recognize the multiplexer it makes. - Predict which assignment wins when a block assigns the same variable several times.
- Explain why an
ifwithout anelsemakes a latch, and fix it. - Recognize three illegal arrangements of instances and drivers, and read the tool's message for each.
Intuition first
Section titled “Intuition first”A module is like a function you write once and use many times. The difference is what "use" means. Calling a function runs its code at one moment; instantiating a module places a copy of its hardware in the design, and that copy exists and works for as long as the circuit does. Two instances of a module are two separate pieces of hardware, each with its own wires, working at the same time.
Think of a kitchen. A module is a recipe card for a small appliance, say a toaster. An instance is a toaster actually standing on the counter. Wiring up a design means placing appliances and running cords between their sockets, and the card itself never moves. The picture breaks where a function call would be convenient: you cannot place an appliance in the middle of a decision, because the appliances are already on the counter before anyone decides anything. That is why an instance can never appear inside a procedural block.
A multiplexer, or mux, is the circuit form of a decision. A mux has several data inputs and a select input, and it passes along one of the data inputs, the one the select names. All the data inputs are always there, each with its own value, and the select only decides which one is let through. An if in a circuit has the same shape: both branches exist as hardware, and a mux chooses between them.
Instances and connections
Section titled “Instances and connections”An instance is one copy of a module placed inside another. It is written as the module's name, then a name for this copy, then a list of connections that join each of the module's ports to a signal of the surrounding module: majority_three u1(.A(x), .B(y), .C(z), .M(out));. The surrounding module is the parent. Instances appear at the level of the module body, next to assign statements, and never inside a procedural block.
In SystemVerilog
Section titled “In SystemVerilog”Connection forms
Section titled “Connection forms”There are three ways to write the connections. Naming each port, .A(x), connects port A to the signal x; the order of the list does not matter. An implicit connection, .A, is short for .A(A), and works when the signal has the same name as the port. The wildcard .* connects every port that is not listed to the signal of the same name in the parent. Each connection should join a port to a signal of the same width. With a named connection, .A(x), a signal of a different width is still legal and the tools warn about it (the compiler front end and Verilator both do). With .A or .* the language asks for matching types, so a width mismatch is an error.
// Three ways to connect the same instance: by name, by implicit name, and with .* for every port that matches.module connect_forms(input logic A, B, C, output logic m_named, m_implicit, m_star); majority_three u_named(.A(A), .B(B), .C(C), .M(m_named)); majority_three u_implicit(.A, .B, .C, .M(m_implicit)); majority_three u_star(.*, .M(m_star));endmoduleThe three instances are the same hardware. A separate test applies all eight rows of inputs and compares each output with the majority of the three inputs.
Multiplexers from gates and from code
Section titled “Multiplexers from gates and from code”A multiplexer, or mux, passes one of its data inputs to its output. A 2:1 mux has two data inputs and one select input; the output is the data input numbered by the select. A mux with data inputs has a -bit select. In a diagram it is a trapezoid with the data input numbers written inside and the select entering the slanted side.
A 2:1 mux needs y = d1 when the select s is 1 and y = d0 when it is 0. Two AND terms do that: d1 with s, and d0 with the inverse of s. An AOI cell computes the NOR of two such terms, which is the inverse of what we want, so an inverter after the cell fixes it. The module below instantiates the AOI cell and adds two inverters.
// A 2:1 multiplexer built from an AOI cell and two inverters: y is d1 when s is 1, and d0 when s is 0.module mux2_aoi(input logic s, d0, d1, output logic y); logic s_n, y_n;
not g1(s_n, s); aoi_cell core(.A(d1), .B(s), .C(d0), .D(s_n), .F(y_n)); // y_n is 0 when d1 and s are 1, or d0 and s_n are 1 not g2(y, y_n);endmoduleThe cell is a module of its own, listed with the file:
// An AND-OR-INVERT cell, used below as a building block.module aoi_cell(input logic A, B, C, D, output logic F); assign F = ~((A & B) | (C & D));endmoduleFollowing the signals: s_n is the inverse of s, the cell's output y_n is 0 when either d1 and s are both 1 or d0 and s_n are both 1, and the last inverter turns that into y. The module is compared with the specification of a 2:1 mux on all eight input rows.
| s | y | ||
|---|---|---|---|
| 0 | 0 | 0 | 0 |
| 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 |
Describing a choice in code
Section titled “Describing a choice in code”A circuit built from gates is correct but long. Most of the time you describe the choice itself and let the tool build the mux. Three more modules describe the same 2:1 mux: with if and else, with a case, and with the conditional operator.
// The same multiplexer written with if and else.module mux2_if(input logic s, d0, d1, output logic y); always_comb begin if (s) y = d1; else y = d0; endendmodule// The same multiplexer written with a case statement and a default arm.module mux2_case(input logic s, d0, d1, output logic y); always_comb begin case (s) 1'b0: y = d0; default: y = d1; endcase endendmodule// The same multiplexer as a conditional expression.module mux2_cond(input logic s, d0, d1, output logic y); assign y = s ? d1 : d0;endmoduleAll four mux modules pass the same check, on every row.
An always_comb block is a group of statements that computes one or more variables from the signals it reads. It always works, like an assign: when an input of the block changes, its results follow. Inside it, assignments use =, and the statements may be if, else, case and plain assignments. A variable that a block assigns must be assigned in no other block and in no assign.
ReadingTwo ways to read the same code
Chapters 1 and 2 say that nothing runs: an assign or an always_comb describes hardware that is always connected and always working. Later pages, and the tools' own manuals, say that a block "runs". Both are right, about different things. The hardware reading is that the code describes gates and wires. The simulator reading is that a simulator computes that hardware's behavior on an ordinary computer, so it executes an always_comb block once at the start and again whenever a signal the block reads changes. When Chapter 4 says that an always_ff block runs at each rising clock edge, that is the simulator reading of the same kind of description. "Runs" always means the simulator carrying out the description, never a step taken by the circuit.
An always_comb block holds exactly one statement, and so does each arm of an if or case. The words begin and end turn several statements into that one statement. You need them whenever a body has more than one statement, and they are optional around a single statement, which is why some listings in this book have them around one case and others do not. The usual slip is adding a second line under an if and forgetting them: the new line is then not part of the if and runs whatever the condition says, and after an always_comb or always_ff header it is a syntax error.
In design code, if and case statements live inside a procedural block such as this one, and the block can describe more than a mux. The rule that matters is how several assignments to one variable combine. The statements are read from top to bottom, and when more than one of them applies, the last one wins.
// Several assignments to one variable in a block: when more than one if is true, the last assignment decides.module last_wins(input logic a, b, output logic [1:0] level); always_comb begin level = 2'd0; if (a) level = 2'd1; if (b) level = 2'd2; endendmoduleThe level is 0 unless a or b is 1, and when both are, the second if wins: 2'b10 in simulation. Here the order matters, because the order says which assignment is last. This is a rule about the statements inside one block, and it does not conflict with the idea that circuits have no order: the block is one piece of logic, and its text is the recipe for how the choices are ranked.
Every path must assign
Section titled “Every path must assign”Every variable that a block assigns must receive a value on every path through it. If some path assigns nothing, the variable keeps its old value on that path, which means the circuit has to remember it. That memory is called a latch: a circuit that follows its input while an enable is on and holds its last value while the enable is off. Here the enable is the condition of the if. Designs in this book never use one, and a latch written by accident is a bug.
// A lamp that should be on when the door is open and the key is out. The else case is missing.module latch_demo(input logic door_open, key_out, output logic lamp); always_comb begin if (door_open & key_out) lamp = 1'b1; endendmodulehdl/ch02/s07/latch_demo.sv:4:30: warning: latch inferred for 'lamp' because it is not assigned on all control paths [-Winferred-latch]
if (door_open & key_out) lamp = 1'b1;
^~~~There is a cure with two forms. Either end the if with an else, or assign a default value on the first line and let later statements override it, as last_wins did above with level = 2'd0.
// The lamp with a value for every case: the first assignment is the default, and a later one overrides it.module no_latch(input logic door_open, key_out, output logic lamp); always_comb begin lamp = 1'b0; if (door_open & key_out) lamp = 1'b1; endendmoduleA case statement needs the same care: a value of the select that no item names leaves the variable unassigned, which is a latch for the same reason. End the case with a default arm, as the earlier case did, so that every value has an assignment even after someone edits the items. The default is also where you decide what the circuit does for the values you did not list. If the input can truly never occur and any output is acceptable, as for the unused codes of a decoder, assigning X says so (the decoder of Section 3.1 assigns 7'bX). If you must say what happens, as for the unused codes of a state machine, assign a named safe value; Section 5.3 says what that does and does not promise once a synthesis tool has handled the code.
A circuit built from instances: variable majority
Section titled “A circuit built from instances: variable majority”The majority of three bits is 1 when at least two of them are 1. The case below lists the four patterns of {A, B, C} for which that holds, and the default arm covers the other four. Writing the three bits as one value with a concatenation is what lets a single case look at all of them.
// Majority of three: M is 1 when at least two of A, B and C are 1.module majority_three(input logic A, B, C, output logic M); always_comb begin case ({A, B, C}) 3'b011, 3'b101, 3'b110, 3'b111: M = 1'b1; default: M = 1'b0; endcase endendmoduleNow a circuit that takes the majority of up to three inputs, with a 2-bit count that says how many of them take part: 0 gives 0, 1 gives A, 2 gives the majority of A and B, and 3 gives the majority of A, B and C. The count-2 case is a majority module whose third input is the constant 0, and a majority of three with one input stuck at 0 needs the other two inputs to be 1.
Illustration a block diagram that explains an idea; its behavior is not checked
A block diagram with inputs A, B, C and count (2 bits) and output M.
Constant 0 drives n1.
Block "maj" maj2: A is A, B is B, C is n1, M is n2.
Block "maj" maj3: A is A, B is B, C is C, M is n3.
Constant 0 drives n4.
4-to-1 multiplexer sel: selects among n4 as input 0, A as input 1, n2 as input 2 and n3 as input 3 using count; output M.
This diagram is illustrative: its behavior is not checked.
Description: A block diagram. Inputs A and B each go to two maj blocks, one above the other. The upper maj block has its third input tied to 0, and the lower one takes C as its third input. A multiplexer with four inputs, numbered 00, 01, 10 and 11, has the constant 0 on input 00, A on input 01, the output of the upper maj block on input 10 and the output of the lower maj block on input 11. The 2-bit input count enters the select side of the multiplexer, and the multiplexer output is M.
The drawing marks the structure: two instances of the same module feeding a mux that picks one of four values. The code reads the same way.
// Vote among the first `count` of the inputs A, B and C: no inputs gives 0, one input gives A, two inputs give their majority (with C held at 0), three give the full majority.module variable_majority(input logic A, B, C, input logic [1:0] count, output logic M); logic vote_pair, vote_trio;
majority_three pair_vote(.A(A), .B(B), .C(1'b0), .M(vote_pair)); majority_three trio_vote(.A(A), .B(B), .C(C), .M(vote_trio));
always_comb begin case (count) 2'd0: M = 1'b0; 2'd1: M = A; 2'd2: M = vote_pair; default: M = vote_trio; endcase endendmoduleThe case on count is the mux. Its arms compare count with constants as wide as count: 2'd0, 2'd1, 2'd2, and the default arm covers 3. The two instances are named connections, and the third input of the first one is the literal 1'b0. A shorter way to write the same two instances uses an implicit connection and .*:
// The same voter with abbreviated connections: `.A` stands for `.A(A)`, and `.*` fills in every port that has a same-named signal here.module variable_majority_short(input logic A, B, C, input logic [1:0] count, output logic M); logic vote_pair, vote_trio;
majority_three pair_vote(.A, .B, .C(1'b0), .M(vote_pair)); majority_three trio_vote(.*, .M(vote_trio));
always_comb begin case (count) 2'd0: M = 1'b0; 2'd1: M = A; 2'd2: M = vote_pair; default: M = vote_trio; endcase endendmoduleBoth modules were run through all 32 combinations of A, B, C and count and compared with a reference that counts the 1s among the inputs that take part instead of using majority_three. The testbench below shows a few of them:
// Tries several combinations of inputs and count on variable_majority.module variable_majority_tb(); logic A, B, C, M; logic [1:0] count;
variable_majority dut(.*);
initial begin A = 1; B = 0; C = 1; count = 2'd0; #10; // expect: M=0 count = 2'd1; #10; // expect: M=1 count = 2'd2; #10; // expect: M=0 $display("DD m_101_count2=%0d'b%b", $bits(M), M); count = 2'd3; #10; // expect: M=1 A = 1; B = 1; C = 0; count = 2'd2; #10; // expect: M=1 count = 2'd3; #10; // expect: M=1 $display("DD m_110_count3=%0d'b%b", $bits(M), M); A = 0; B = 1; C = 0; count = 2'd1; #10; // expect: M=0 count = 2'd3; #10; // expect: M=0 A = 0; B = 1; C = 1; count = 2'd2; #10; // expect: M=0 count = 2'd3; #10; // expect: M=1 $stop; endendmoduleFor example, with A, B and C equal to 1, 0 and 1 and a count of 2, only A and B take part, one of them is 1, and the majority is 1'b0. With the count at 3 and the inputs 1, 1 and 0 it is 1'b1.
Two small slips the tools catch
Section titled “Two small slips the tools catch”Compare count with a constant of its own width. The compiler front end says nothing about a 3-bit constant against a 2-bit signal, but Verilator warns about each comparison, and the book's checks count any warning as a failure:
// A 2-bit count compared with 3-bit constants.module count_wide_bad(input logic [1:0] count, input logic A, B, output logic M); always_comb begin if (count == 3'd1) M = A; else if (count == 3'd2) M = B; else M = 1'b0; endendmodule%Warning-WIDTHEXPAND: hdl/ch02/s07/count_wide_bad.sv:5:20: Operator EQ expects 3 bits on the LHS, but LHS's VARREF 'count' generates 2 bits.
: ... note: In instance 'count_wide_bad'
5 | else if (count == 3'd2) M = B;
| ^~
... For warning description see https://verilator.org/warn/WIDTHEXPAND?v=5.055
... Use "/* verilator lint_off WIDTHEXPAND */" and lint_on around source to disable this message.
%Warning-WIDTHEXPAND: hdl/ch02/s07/count_wide_bad.sv:4:15: Operator EQ expects 3 bits on the LHS, but LHS's VARREF 'count' generates 2 bits.
: ... note: In instance 'count_wide_bad'
4 | if (count == 3'd1) M = A;
| ^~A comma after the last port is also an error:
// A comma after the last port.module trailing_comma_bad(input logic A, B, output logic M,); assign M = A & B;endmodulehdl/ch02/s07/trailing_comma_bad.sv:2:59: error: misplaced trailing ',' [-Wmisplaced-trailing-separator]
module trailing_comma_bad(input logic A, B, output logic M,);
^What not to write
Section titled “What not to write”Three arrangements are wrong in hardware and rejected by the tools. The first puts an instance inside an always_comb block. The instance is a piece of hardware, not a step, so it cannot depend on a condition.
// A module instance placed inside an always_comb block, which is not allowed.module bad_in_always(input logic A, B, C, input logic [1:0] count, output logic M); logic vote_trio;
always_comb begin if (count == 2'd3) begin majority_three trio_vote(.A(A), .B(B), .C(C), .M(vote_trio)); M = vote_trio; end else begin M = A & B & C; end endendmodulehdl/ch02/s07/bad_in_always.sv:3:9: warning: variable 'vote_trio' is never assigned a value [-Wunassigned-variable]
logic vote_trio;
^~~~~~~~~
hdl/ch02/s07/bad_in_always.sv:7:7: error: 'majority_three' is a module definition but is used as a value
majority_three trio_vote(.A(A), .B(B), .C(C), .M(vote_trio));
^~~~~~~~~~~~~~
hdl/ch02/s07/majority_three.sv:2:8: warning: module definition is unused [-Wunused-def]
module majority_three(input logic A, B, C, output logic M);
^~~~~~~~~~~~~~The error is the line saying that majority_three is a module definition used as a value. The two warnings around it follow from it: with no valid instance, nothing drives vote_trio, and nothing uses the module majority_three.
The second assigns an input of the module. The message calls the input a net, as in Section 2.5: an input is driven by the parent module, and a procedural block may not assign it.
// An input port assigned inside an always_comb block, which is not allowed.module bad_assign_input(input logic A, B, C, input logic [1:0] count, output logic M); logic vote_pair;
majority_three pair_vote(.A(A), .B(B), .C(C), .M(vote_pair));
always_comb begin if (count == 2'd2) begin C = 1'b0; M = vote_pair; end else begin M = A; end endendmodulehdl/ch02/s07/bad_assign_input.sv:9:7: error: cannot assign to a net within a procedural context
C = 1'b0;
^The third drives one output from two places: here the output of an instance and an always_comb block.
// An output that an instance and an always_comb block both try to drive.module bad_two_drivers(input logic A, B, C, input logic [1:0] count, output logic M); majority_three trio_vote(.A(A), .B(B), .C(C), .M(M));
always_comb begin if (count == 2'd1) M = A; else if (count == 2'd2) M = B; else M = 1'b0; endendmodulehdl/ch02/s07/bad_two_drivers.sv:6:24: error: cannot mix continuous and procedural assignments to variable 'M' [-Wmixed-var-assigns]
if (count == 2'd1) M = A;
^
hdl/ch02/s07/bad_two_drivers.sv:3:52: note: also assigned here
majority_three trio_vote(.A(A), .B(B), .C(C), .M(M));
^The message names the variable and points at the other driver. Each signal has one driver, as in Section 2.5; whichever way the second driver sneaks in, the rule is the same.
A first-attempt guessing game
Section titled “A first-attempt guessing game”To finish, a small game. A secret number is fixed inside the circuit. The player sets three switches as a guess and a fourth switch to say "check my guess". When the check switch is on, one of three lights shows whether the guess is greater than, equal to or less than the secret, with all the numbers read as signed values, as in Section 2.6.
The game needs a comparator. The first attempt uses subtraction: compute the 3-bit difference a - b, then read the answer from it. If the difference is all zeros, the numbers are equal. If its leftmost bit is 1, the difference looks negative, so a is less than b. Otherwise a is greater.
// A first attempt at a comparator for 3-bit signed numbers, built on subtraction.module compare3(input logic [2:0] a, b, output logic lt, eq, gt); logic [2:0] diff;
assign diff = a - b; assign eq = ~|diff; // every bit of the difference is 0 assign lt = diff[2]; // the difference looks negative assign gt = ~eq & ~lt;endmoduleThe game instantiates the comparator with the guess on one port and the constant secret on the other. Order matters here, because subtraction is not commutative: a is the guess and b is the secret.
Illustration a block diagram that explains an idea; its behavior is not checked
A block diagram with inputs guess (3 bits) and check and outputs led_gt, led_eq and led_lt.
Constant 2 drives n4.
Block "compare3" cmp: a is guess, b is n4, gt is n1, eq is n2, lt is n3.
AND gate g_gt: inputs n1 and check; output led_gt.
AND gate g_eq: inputs n2 and check; output led_eq.
AND gate g_lt: inputs n3 and check; output led_lt.
This diagram is illustrative: its behavior is not checked.
Description: A block diagram. The 3-bit input guess goes to the a input of a compare3 block, and the constant 2 goes to its b input. The block has three outputs, gt, eq and lt, from top to bottom. Each output goes to its own AND gate, and the check input goes to the other input of all three AND gates. The three AND gates drive the outputs labeled led_gt, led_eq and led_lt, the lights for greater, equal and less.
// A guessing game with a fixed secret. SW[2:0] is the guess and SW[3] says "check my guess".// LEDR[0] lights when the guess is greater than the secret, LEDR[1] when it is equal, LEDR[2] when it is less.module guessing_game(input logic [3:0] SW, output logic [2:0] LEDR); logic guess_lt, guess_eq, guess_gt;
compare3 judge(.a(SW[2:0]), .b(3'b010), .lt(guess_lt), .eq(guess_eq), .gt(guess_gt)); // the secret is 3'b010
assign LEDR[0] = guess_gt & SW[3]; assign LEDR[1] = guess_eq & SW[3]; assign LEDR[2] = guess_lt & SW[3];endmoduleThe first light, LEDR[0], shows greater, the second shows equal and the third shows less. The secret is 2. The testbench below plays six guesses.
// Plays six guesses against the secret 2, with the check switch on except in the fourth.module guessing_game_tb(); logic [3:0] SW; logic [2:0] LEDR;
guessing_game dut(.*);
initial begin SW = 4'b1011; #10; // expect: LEDR=3'b001 SW = 4'b1010; #10; // expect: LEDR=3'b010 SW = 4'b1001; #10; // expect: LEDR=3'b100 SW = 4'b0011; #10; // expect: LEDR=3'b000 SW = 4'b1000; #10; // expect: LEDR=3'b100 SW = 4'b1111; #10; // expect: LEDR=3'b100 $stop; endendmoduleThe first guess is 3, the pattern 011, and the greater light comes on. Guess 2 lights the equal light, and guess 1 lights the less light. In the fourth line the check switch is off, so no light comes on. The last two guesses are 0 and then the pattern 111, which is when read as a signed number; both are less than 2, and both light the less light.
The rule is not right for every pair: some guesses the testbench does not try light the wrong lamp. Section 3.2 tries every guess and finds them.
This is a first attempt, and six friendly guesses do not make a tested comparator: they show that it works on those guesses and say nothing about the other pairs of numbers. Chapter 3 shows how to try every input in a testbench and read the result, and Chapter 4 returns to the arithmetic behind the comparator. Until then, take this section's version as an example of how modules connect, not as a finished design.
Course conventions
Section titled “Course conventions”How CSE 369 writes this
In this book's code, instances use named connections, implicit connections or .*, and never appear inside an always_comb block. A multiplexer is drawn as a trapezoid with its data inputs numbered, and port lists have no trailing comma. Constants compared with a bus are sized to the bus.
Pitfalls
Section titled “Pitfalls”Common pitfall
- Writing an instance inside
always_comb, anifor acase. Instances go at the top level of the module body, and anifchooses between values, not between pieces of hardware. - Assigning an input of the module, or driving a signal from both an instance and a procedural block. Each signal has one driver, and inputs are driven by the parent module.
- Leaving a path of an
always_combblock without an assignment. The missingelseordefaultis a latch; give a default first or finish everyifandcase. - Comparing a 2-bit signal with a 3-bit constant. The comparison still comes out right here, but Verilator warns about it; write
2'd1. - Putting a comma after the last port of a module.
- Trusting a design because a few inputs worked. Six friendly guesses do not test a comparator; when the number of inputs is small, try all of them.
Check your understanding
Section titled “Check your understanding”Take the quiz (6 questions)
In majority_three u1(.*, .M(x));, which ports are connected by .*?
A, B and C must therefore exist in the parent, and M is connected explicitly to x.Which of these is not legal?
In the testbench of variable_majority, A, B and C are 1, 0 and 1 and the count is 2. What is M?
In last_wins, both a and b are 1. What is level?
level is 2. The value 1 is what you get if you take the first one.An always_comb block assigns a variable only inside an if that has no else. What does the tool report?
How many copies of majority_three does variable_majority contain, and how do they relate?
case chooses which result reaches M.Exercises
Section titled “Exercises”Here is a block with three if statements that each assign code. What is code when x and y are 1 and z is 0? What is it when x and z are 1 and y is 0?
// Exercise: three ifs assign the same variable; when several are true, the last one decides.module last_of_three(input logic x, y, z, output logic [1:0] code); always_comb begin code = 2'd0; if (x) code = 2'd1; if (y) code = 2'd2; if (z) code = 2'd3; endendmoduleHint
Solution
When x and y are 1, the first two ifs are true. The second is the last true one, so code is 2'b10, which is 2. When x and z are 1, the first and the third are true, and the third wins, so code is 2'b11, which is 3. A simulation of these two cases printed the values.
Build xor_aoi, with inputs a and b and output y, from one instance of aoi_cell and two inverters (gate primitives). Name the two inverted signals a_n and b_n.
Hint
A and B are both 1, or when C and D are both 1. The inputs are equal when both are 1, or when both are 0.Solution
The cell's output is 0 when A and B are both 1, or when C and D are both 1. That is the condition "a and b are both 1, or a and b are both 0". Feed a and b to the first pair and the inverse of each to the second pair, a_n and b_n: the second pair is both 1 exactly when a and b are both 0. The cell's output is then 0 when a and b are equal and 1 when they differ, which is XOR, so it connects directly to y.
// Exercise solution: an XOR from one AOI cell and two inverters. The cell's output is 0 when a and b are both 1 or both 0.module xor_aoi(input logic a, b, output logic y); logic a_n, b_n;
not g1(a_n, a); not g2(b_n, b); aoi_cell core(.A(a), .B(b), .C(a_n), .D(b_n), .F(y));endmoduleThe module is compared with an XOR on all four input rows.
Write a module logic_unit with inputs a, b and a 2-bit op, and one output y. When op is 00, y is a AND b; when it is 01, OR; when it is 10, XOR; and for 11, NAND. Use an always_comb block with a case.
Hint
default arm for the last.Solution
Each arm assigns y with one gate's expression, and the default arm covers 11, so every value of op assigns y and no latch is inferred.
// Exercise solution: a case on a two-bit operation code chooses which gate's result reaches y.module logic_unit(input logic a, b, input logic [1:0] op, output logic y); always_comb begin case (op) 2'b00: y = a & b; 2'b01: y = a | b; 2'b10: y = a ^ b; default: y = ~(a & b); endcase endendmoduleAll 16 combinations of op, a and b are checked against the expected result.
An alarm should sound when it is hot, or when there is smoke and the alarm is not muted. The tools warn about this module, and the book's checks treat that warning as a failure. Explain the message and write a correct version.
// Exercise: the alarm should sound when it is hot, or when there is smoke and the alarm is not muted.module alarm_latch(input logic hot, smoke, muted, output logic alarm); always_comb begin if (hot) alarm = 1'b1; else if (smoke & ~muted) alarm = 1'b1; endendmoduleHint
alarm get when hot is 0 and the second condition is false?Solution
The tool reports:
hdl/ch02/s07/ex/alarm_latch.sv:4:14: warning: latch inferred for 'alarm' because it is not assigned on all control paths [-Winferred-latch]
if (hot) alarm = 1'b1;
^~~~~When hot is 0 and the second condition is false, no statement assigns alarm, so it would have to hold its old value. Add the missing last else, which gives that path the value 0.
// Exercise solution: every path through the block assigns alarm.module alarm_fixed(input logic hot, smoke, muted, output logic alarm); always_comb begin if (hot) alarm = 1'b1; else if (smoke & ~muted) alarm = 1'b1; else alarm = 1'b0; endendmoduleThe corrected module is compared with the function hot OR (smoke AND NOT muted) on all eight input rows.
Write a module at_least with inputs a, b, c and a 2-bit k, and one output y. The output is 1 when at least k of the three inputs are 1. So with k equal to 0 it is always 1, and with k equal to 3 it needs all three inputs. Use an always_comb block with a case on k.
Hint
k on its own: "at least one" is an OR, and "at least two" is the majority. Use the default arm for 3.Solution
Each arm of the case is the condition for one value of k. For 0 the answer is the constant 1. For 1, one input being 1 is enough, so it is a | b | c. For 2, some pair must be 1, which is (a & b) | (a & c) | (b & c). The default arm covers the one value left, 3, which needs all three inputs, a & b & c. Every value of k assigns y, so no latch is inferred.
// Exercise solution: y is 1 when at least k of the three inputs are 1; a case on k picks the condition.module at_least(input logic a, b, c, input logic [1:0] k, output logic y); always_comb begin case (k) 2'd0: y = 1'b1; 2'd1: y = a | b | c; 2'd2: y = (a & b) | (a & c) | (b & c); default: y = a & b & c; endcase endendmoduleAll 32 combinations of k, a, b and c are compared with a reference that adds up the three inputs and compares the sum with k.