1. A conditional expression is a multiplexer
condition ? a : b picks a when the Bool condition is True and b otherwise. In hardware
it is the two-input multiplexer from the fundamentals: both a and b are computed, and the
condition selects which one reaches the output. Both branches must have the same type, so a
Bit#(8) on one side and a Bit#(4) on the other is the T0020 error of the last chapter;
the operator's form and type rule are in
Bool, Bit and pure functions.
Bit#(8) magnitude = negative ? 0 - value : value;Chaining conditionals selects among more than two values, with the first true condition winning. That order is a priority, and later chapters use it on purpose.
2. Blocks and the if statement
2.1 begin and end
begin and end group several statements where the syntax expects one.
Form
begin
statement1;
statement2;
end2.2 if and else
if chooses between statements according to a Bool condition.
Form
if (condition) statement
if (condition) statement else statementUInt#(8) result = 0;
if (valid) begin
result = payload;
end| Part | Meaning |
|---|---|
UInt#(8) result = 0; | a default value for every path |
if (valid) | a runtime Bool signal |
result = payload; | on the true path, result names payload |
When valid is a runtime signal, both paths become hardware: the if turns into a multiplexer
that chooses the value of result. Elaboration does not pick one path in advance. In an action
context, a branch may also contain register writes and method calls; those writes then happen
only when the condition is true.
Caution. Give every path a value. If a path leaves a variable unassigned, bsc first warns
with P0042, Assignment to `result' missing from some paths, and once a rule or method
uses that value, elaboration stops with error G0028, `result' uses uninitialized value.
No latch is created to remember an older value; the design does not compile at all. Initialize
the variable before the if, or assign it in every branch.
2.3 A begin/end block as an expression
A begin/end block in parentheses computes one value with several statements. The last
expression is the block's value.
Form
(begin
statements;
resultExpression;
end)Bit#(8) normalized = (begin
Bit#(8) masked = raw & 8'h7f;
Bit#(8) result = masked | 8'h80;
result;
end);With raw = 8'h35, masked is 8'h35, and normalized is 8'hb5. The names inside are
combinational intermediates; they create no registers.
3. case compares one value against several
case compares one expression against several values and runs the first matching item.
default catches everything else. Inside a function, each item can return its result; in a
plain statement list, each item assigns a variable.
Form
case (expression)
value1: statement
value2, value3: statement
default: statement
endcaseBit#(8) code;
case (op)
2'b00: code = 8'h10;
2'b01: code = 8'h20;
default: code = 8'hff;
endcase| Item | Meaning |
|---|---|
2'b00: ... | runs when op equals 2'b00 |
value2, value3: statement | one item may list several values |
default: ... | runs when no earlier item matched, so every path assigns code |
A case whose items are constants is a decoder followed by a multiplexer: the comparisons
produce one-hot selects, and the selects choose the result. It is still combinational; no clock
is involved, and the result is available in the same cycle as op. The rule of section 2.2
applies here too: a case without default that assigns on only some items leaves the value
undefined on the missing items, and using it stops elaboration with the same G0028 error.
Pattern matching extends case so that items can test
the shape of structured values.
4. The don't-care value says the value does not matter
4.1 What ? means
A lone question mark ? is a value of whatever type the context expects, with no content. It
says "any bit pattern is acceptable here", and the compiler may pick whatever value makes the
circuit simplest.
Form
?Bit#(8) unused = ?;
case (code)
2'd0: return 4'h3;
// ... every other value handled as well
default: return ?; // a branch that is never reached
endcase? is not Verilog's x, it is not a random number generator, and it does not promise x
behaviour: a given build chooses one pattern and keeps it. Use it only where every possible
choice gives the same observable behaviour, such as a branch no input can reach or a field of
an inactive union member.
Note. The value of ? is unspecified. Never put it where it can reach an output, an
interface, or another computation; mkReg(?) does not give a predictable reset value either.
Caution. Initializing visible state with ? leaves part of the design unspecified;
simulation and synthesis may then legitimately differ.
4.2 Example: an unreachable default branch
decode in DontCare.bsv maps a 2-bit code to a 4-bit value. The case covers all four
values 0 to 3, and its default branch returns ?. No input can reach that item, so what ?
becomes cannot change any output.
package DontCare;
// The default branch is unreachable: code is two bits and every value is
// covered above it. ? tells the compiler no particular value is required.
function Bit#(4) decode(Bit#(2) code);
case (code)
2'd0: return 4'h3;
2'd1: return 4'h7;
2'd2: return 4'hB;
2'd3: return 4'hF;
default: return ?;
endcase
endfunction
(* synthesize *)
module mkTop(Empty);
rule show;
$display("decode(0) = %h", decode(2'd0));
$display("decode(3) = %h", decode(2'd3));
$finish(0);
endrule
endmodule
endpackagePractice this concept
Use this concept in a short BSV problem as your next step.
Predict before you run: how many values can a 2-bit argument take, and can any of them reach
default? decode(0) and decode(3) are printed in hexadecimal; read the two matching items.
Check
If a third line printed decode(2'd2), which hexadecimal digit would it show?
Press Run and compare the two lines with the items 2'd0 and 2'd3.
| Output line | What it shows |
|---|---|
decode(0) | 3, from an explicit branch |
decode(3) | f, from an explicit branch |
All four 2-bit values have explicit branches, so default is unreachable, and whatever value
? becomes cannot affect any output. Adding $display("decode(2) = %h", decode(2'd2)); prints
b, the answer you predicted.
5. Change one thing
Delete the 2'd3 item in the lab and run again. The value 3 now falls through to default.
Check
With the 2'd3 item removed, what does the decode(3) line show?
The rule that follows from this: ? belongs in branches you can prove unreachable, or in data
that a separate flag marks as meaningless. The graded problem is the second case.
6. A flag can make a value unobservable
A struct groups named fields into one value. The problem below uses
typedef struct {
Bool valid;
Bit#(8) payload;
} Decoded deriving (Bits, Eq);and builds values with Decoded { valid: True, payload: 8'h11 }, reading them back as
d.valid and d.payload. The deriving clause gives the type a bit representation and ==;
User-defined types has the rest.
The contract of such a pair is a protocol: payload carries information only while valid is
True. When valid is False, no correct caller reads payload, so its value is unobservable
by agreement, and ? is the idiomatic thing to put there. A constant such as 8'h00 is equally
correct; what makes either safe is the flag and the callers that honour it, not the spelling.
Check
decodeCommand(3'd4) is an illegal code. Which result follows the protocol?
7. Apply it: mark the illegal codes invalid
In the graded problem, decodeCommand maps codes 0 to 3 to four payloads and its default
item returns valid: True for the four illegal codes 4 to 7. Change only that item so every
illegal code reports valid: False, and leave the four legal items as they are. The testbench
prints the legal code 3 with its payload and the illegal code 4 with its flag only; grading
checks all eight codes.
8. Common mistakes
8.1 ? in a reachable branch
// Wrong: with a 3-bit code, values 4 to 7 reach default and return an unspecified value
default: return ?;
// Right: a reachable branch returns a concrete value
default: return 4'h0;Use ? only when you can show its value is never observed. First check that the explicit
branches cover every possible input; with a 3-bit code and four explicit items they do not,
and the example of section 4 would print the compiler's choice for four of the eight codes.
9. Summary
| Element | Meaning |
|---|---|
c ? a : b | a two-input multiplexer; branches share one type; a chain is a priority |
begin … end | groups statements; in parentheses, computes one value whose names are combinational intermediates |
if (c) … else … | a runtime condition becomes a multiplexer; in an action, the writes in a branch happen only when it is true |
case (x) … default: … endcase | compare against constants; first match wins; a decoder followed by a multiplexer |
| every path assigns | otherwise P0042 (for if) and error G0028 once the value is used; no latch is created |
? | don't-care: the compiler's choice, safe only where unobservable; do not use it for a required reset value |
Type { field: value }, v.field | build and read a struct; a valid flag can make a payload unobservable by protocol |