1. A vector is several bits under one name
The ports of the last chapter were one bit each. A signal that carries several bits is a vector, and its declaration says how many bits it has and how they are numbered. The brackets in front of the name give the number of the most significant bit (MSB) and of the least significant bit (LSB).
Form
wire [MSB:LSB] name;
reg [MSB:LSB] name;wire [7:0] data; // 8 bits, bit 7 down to bit 0
reg [3:0] count; // 4 bitsWithout brackets, a signal is one bit wide. The number on the left of the colon is the most
significant bit; the number on the right is the least significant. [7:0] is the usual way to write
a byte: bit 7 is worth 128, bit 0 is worth 1, and each hex digit of the value covers four of the
bits. In the fundamentals you called this a bus; in Verilog it is a vector, and a port can be one
just as well as an internal wire: input [7:0] data.
2. Selects take bits out; braces put them together
Writing a range after a vector's name selects those bits, and the result is exactly as wide as the range. Braces join values into one wider value, and the first item inside the braces lands at the most significant end. None of these builds a gate. A select is a choice of wires, and a concatenation is wires laid side by side.
2.1 Range selects
To take some of the bits of a vector, put a range or a single bit number in brackets after the name.
| Syntax | Result | Width |
|---|---|---|
data[7:4] | Bits 7 down to 4, the high nibble | 4 |
data[0] | Bit 0 alone | 1 |
2.2 Concatenation and replication
Braces { } join several values into one wider vector. The first item you write ends up at the
most significant end.
{2'b10, data, 1'b0} // 2 + 8 + 1 = 11 bits
{2{data[3:0]}} // data[3:0] twice, 8 bits| Syntax | Name | Meaning | Result width |
|---|---|---|---|
{a, b, c} | Concatenation | a, then b, then c, most significant first | The sum of the item widths |
{n{a}} | Replication | a repeated n times | The width of a times n |
With data eight bits wide, {2'b10, data, 1'b0} is 10, then the eight bits of data, then 0:
2 + 8 + 1 = 11 bits. {2{data[3:0]}} is the low nibble twice, 4 × 2 = 8 bits.
2.3 Read BitFields.v and its testbench
BitFields.v is a combinational circuit that derives six outputs from one 8-bit input, data. Each
output has its own assign, so read the width of each output port first, then its right-hand side.
module bit_fields (
input [7:0] data,
output [3:0] high,
output lsb,
output [10:0] framed,
output [7:0] repeated,
output [3:0] narrow,
output [11:0] wide
);
// A range selects bits; the result is as wide as the range.
assign high = data[7:4];
assign lsb = data[0];
// Braces join pieces left to right, most significant first.
assign framed = {2'b10, data, 1'b0};
assign repeated = {2{data[3:0]}};
// A wider value loses its top bits; a narrower one is zero-extended.
assign narrow = data;
assign wide = data;
endmoduleTb.v is a testbench for bit_fields, a module without ports that feeds inputs into the design and
looks at its outputs, as in A module, its ports, and
assign. It puts 1100_0101 on data, waits #1 so the assign lines settle, prints three
lines, then changes data to 8'hFF and prints once more. The instance and its named connections
are explained fully in Instances, hierarchy
and parameters.
module tb;
reg [7:0] data;
wire [3:0] high;
wire lsb;
wire [10:0] framed;
wire [7:0] repeated;
wire [3:0] narrow;
wire [11:0] wide;
bit_fields dut (
.data(data), .high(high), .lsb(lsb), .framed(framed),
.repeated(repeated), .narrow(narrow), .wide(wide)
);
initial begin
data = 8'b1100_0101;
// Give continuous assignments a time step to settle before printing.
#1;
$display("SELECT high=%b lsb=%b", high, lsb);
$display("CONCAT framed=%b repeated=%b", framed, repeated);
$display("RESIZE narrow=%b wide=%b", narrow, wide);
data = 8'hFF;
#1;
$display("FOLLOW high=%b framed=%b", high, framed);
$finish;
end
endmodulePractice this concept
Use this concept in a short Verilog problem as your next step.
Predict before you run. With data = 1100_0101, work out high, framed and repeated by
hand.
Check
data is 1100_0101. What does high, declared as data[7:4], hold?
Check
How many bits wide is {2'b10, data, 1'b0} when data is 8 bits?
3. A wider or narrower target changes the value silently
When the value and the target of an assign differ in width, Verilog makes them match without an
error. A narrower target drops the upper bits; a wider target fills the upper bits with 0. Icarus
prints no warning for either, so the width you declare is part of the behavior, not a comment.
| Target | Behavior | 1100_0101 assigned to it |
|---|---|---|
[3:0], narrower | upper bits dropped (truncation) | 0101 |
[11:0], wider | upper bits filled with 0 (zero extension) | 0000_1100_0101 |
The outputs narrow and wide of BitFields.v are exactly these two cases. The full rule has one
more row, for signed values, and a consequence for arithmetic: a wide target also widens the
addition that feeds it. Both are in Literals, signed values and expression width.
Check
8'hC5 is assigned to a 4-bit wire. Which hex digit does the wire hold?
Press Run. Compare the SELECT, CONCAT and RESIZE lines with your three answers.
Check the result. Every output for data = 1100_0101:
| Output | Calculation | Value |
|---|---|---|
high | Bits 7..4 | 1100 |
lsb | Bit 0 | 1 |
framed | 10, then the 8 bits of data, then 0 | 10_11000101_0 |
repeated | The low nibble 0101, twice | 01010101 |
narrow | A 4-bit target keeps only the low four bits | 0101 |
wide | A 12-bit target gets four leading zeros | 0000_1100_0101 |
Then look at the last line, FOLLOW. The testbench changed only data, to 8'hFF, yet high and
framed show new values. An assign is not a statement that runs once and is done; it is a
connection, so its output follows its input for as long as the simulation runs.
4. Three families of operators give results of different widths
Verilog has three kinds of operators that look alike and behave differently. Which family you use decides how wide the result is.
| Family | Operators | Works on | Result |
|---|---|---|---|
| Bitwise | &, |, ^, ~ | two vectors, bit by bit | as wide as the operands |
| Logical | &&, ||, ! | two whole values, each true when non-zero | one bit |
| Reduction | &a, |a, ^a | one vector, folding all its bits | one bit |
With a = 4'b1010 and b = 4'b0101: a & b is 0000, because no bit position has a 1 in both;
a && b is 1, because both values are non-zero; |a is 1, because at least one bit of a is 1;
&a is 0, because not every bit of a is 1. The reduction operators are the Verilog spelling of
"any bit set" and "all bits set".
Check
What is |4'b0100, the reduction OR of 0100?
Check
a = 4'b1010 and b = 4'b0101. Which pair of results is right?
5. Arithmetic operators build adders, and wrap at the width
| Operator | Meaning | Hardware |
|---|---|---|
a + b, a - b | addition, subtraction | an adder; subtraction adds the two's complement |
a * b | multiplication | a multiplier array, much larger than an adder |
a / b, a % b | quotient, remainder | by a constant power of two: a shift and a select, no gates; by anything else: a large divider |
a ** b | power | meant for constants, such as 2 ** W in a parameter |
An arithmetic result has no width of its own. It is computed at the width of the widest operand and the assignment target, and whatever does not fit is dropped, again without a warning. So 12 + 5 is 17 in a 5-bit target, but in a 4-bit target the carry is lost and 1 remains. Unsigned subtraction wraps the same way: 5 - 12 in four bits is not -7 but 16 - 7 = 9. That is not a bug in the simulator; it is what a 4-bit subtractor without a borrow output does. If you need the carry or the borrow, make the target one bit wider. The full width rules are in Literals, signed values and expression width.
Division is where the hardware cost shows. For the unsigned a used here, a / 4 and a % 4
need only wiring: dividing by 4 drops the
low two bits, and the remainder is exactly those two bits. a / b with a variable b describes a
complete divider in one clock cycle, which synthesis builds as a large, slow circuit or refuses; real
designs use a shift or a multi-cycle divider instead. In simulation, dividing by zero gives x, not
an error and not 0.
6. Shifts move bits; only >>> on a signed value copies the sign
| Operator | Name | Bits shifted in |
|---|---|---|
a << n | logical left shift | 0 at the bottom |
a >> n | logical right shift | 0 at the top |
a <<< n | arithmetic left shift | 0 at the bottom, the same as << |
a >>> n | arithmetic right shift | copies of the sign bit if a is signed; otherwise 0 |
A shift's width comes from its left operand and, in an assignment, any wider target context.
The shift amount does not widen the result; bits pushed past either end are gone. The trap is
>>>. It copies the sign bit only when the operand is declared signed. On an ordinary vector it is
exactly >>, so 8'b1001_0110 >>> 2 is 0010_0101, and only a reg signed [7:0] holding the same
bits gives 1110_0101, which keeps the value negative: dividing -106 by 4 and rounding down gives
-27.
Shifting by a constant builds no gates; it is the same wiring as a concatenation, a << 2 being
{a[5:0], 2'b00} for an 8-bit a. Shifting by a variable amount, a << n, builds a barrel shifter:
a stack of multiplexers that choose, for every output bit, which input bit arrives there.
7. Comparisons give one bit; == and === differ on x
| Operator | Question | Result |
|---|---|---|
<, <=, >, >= | is one value smaller or larger | 1 bit, or x if an operand has an x or z bit |
==, != | are the values equal | 1 bit, or x if an x or z bit decides the answer |
===, !== | are the bit patterns identical, x and z included | always 0 or 1 |
A relational comparison is signed only when both operands are signed. If one of them is an
ordinary vector, both are read as unsigned. A signed variable holding -1 is then the bit pattern
1111_1111, which is 255, so s < 8'd1 is 0 while s < 8'sd1 is 1. The rule is the same one that
makes a whole expression unsigned, from Literals, signed values and expression width.
== compares values the way hardware can. If a known bit already differs, the answer is 0; if the
only doubt is an x bit, the answer is x, because a real circuit could see either level there.
=== treats x and z as values of their own and asks whether the patterns match symbol for
symbol. A real wire is never x, so === has no gate to become: use == in a design, and ===
in a testbench when you want to check that an output really is x, or really is not.
8. The conditional operator chooses between two values
cond ? x : y is x when cond is non-zero and y otherwise: a two-way multiplexer written as an
expression. Its result width comes from the wider of x and y and any wider assignment context.
It is taught, with nested choices and
the case alternative, in Nets, variables and the combinational always.
9. Precedence: when in doubt, add parentheses
From highest to lowest, as in IEEE 1364-2005. Operators on the same row bind equally and group from
left to right, except ?:, which groups from right to left.
| Level | Operators |
|---|---|
| 1 | unary + - ! ~, reduction & ~& | ~| ^ ~^ |
| 2 | ** |
| 3 | * / % |
| 4 | binary + - |
| 5 | << >> <<< >>> |
| 6 | < <= > >= |
| 7 | == != === !== |
| 8 | binary & |
| 9 | binary ^ ~^ |
| 10 | binary | |
| 11 | && |
| 12 | || |
| 13 | ?: |
Three places where the table disagrees with how the expression reads:
| Written | Read by Verilog as | Usually meant |
|---|---|---|
a & b == c | a & (b == c): a 1-bit comparison, then a bitwise AND | (a & b) == c |
a + b << 1 | (a + b) << 1: the sum doubled | a + (b << 1) |
!a == b | (!a) == b: the one-bit NOT of a, compared with b | !(a == b), or a != b |
The bitwise operators sit below the comparisons, which is the opposite of what most people expect. Parentheses cost nothing in hardware; write them whenever a bitwise operator meets a comparison.
10. Run: every family in one testbench
Operators.v declares a = 12 and b = 5 as 4-bit vectors, plus an 8-bit pattern 1001_0110
stored twice, once in a plain vector u and once in a reg signed called s. Each $display line
is labelled with the family it shows.
module operators;
reg [3:0] a, b;
reg [3:0] sum4, diff;
reg [4:0] sum5;
reg [7:0] pow, u, wide, grouped;
reg signed [7:0] s;
reg [3:0] q;
reg [2:0] n;
initial begin
a = 4'd12;
b = 4'd5;
// A 4-bit target keeps the low four bits of 17; a 5-bit target keeps the carry.
sum4 = a + b;
sum5 = a + b;
// Unsigned 5 - 12 wraps around modulo 16.
diff = b - a;
pow = b ** 2;
$display("ARITH sum4=%0d sum5=%0d diff=%0d pow=%0d", sum4, sum5, diff, pow);
// Dividing by a power of two is a shift; dividing by zero gives x.
$display("DIV a/b=%0d a%%b=%0d a/4=%0d a%%4=%0d a/0=%0d", a / b, a % b, a / 4, a % 4, a / 4'd0);
// The same bits, unsigned and signed: only >>> on the signed copy repeats the sign bit.
u = 8'b1001_0110;
s = 8'sb1001_0110;
n = 3;
$display("SHIFT u>>2=%b u>>>2=%b s>>2=%b s>>>2=%b", u >> 2, u >>> 2, s >> 2, s >>> 2);
$display("SHIFTN n=%0d u<<n=%b u>>n=%b", n, u << n, u >> n);
// One unsigned operand makes the comparison unsigned: -1 is read as 255.
s = -1;
$display("COMPARE signed=%b mixed=%b", s < 8'sd1, s < 8'd1);
// == returns x only when equality is undecidable; === compares x as a value.
q = 4'b10x1;
$display("EQUAL q==10x1:%b q===10x1:%b q==0000:%b", q == 4'b10x1, q === 4'b10x1, q == 4'b0000);
// Each pair: as written, then with the parentheses the writer meant.
$display("PREC-AND %b %b", a & b == 4'd4, (a & b) == 4'd4);
wide = a + b << 1;
grouped = a + (b << 1);
$display("PREC-SHIFT %0d %0d", wide, grouped);
$display("PREC-NOT %b %b", !a == b, !(a == b));
// A $display argument has no target, so a + b stays 4 bits wide.
$display("SELF a+b=%0d", a + b);
$finish;
end
endmoduleCheck
s is a signed 8-bit variable holding -1. What does s < 8'd1, the mixed comparison on the COMPARE line, print?
Press Run and read the output line by line.
| Line | What to look at |
|---|---|
ARITH | sum4=1 lost the carry of 17, sum5=17 kept it; diff=9 is 5 - 12 wrapped in four bits |
DIV | a/4=3 and a%4=0 are the upper and lower bits of 12; a/0=x |
SHIFT | three results are 00100101; only s>>>2, the arithmetic shift of a signed value, fills with 1 |
SHIFTN | a shift by the variable n = 3; bits pushed past the end are gone |
COMPARE | the same -1 is below 1 when both sides are signed, and above it as soon as one is not |
EQUAL | == answers x when only the x bit is in doubt, and 0 when a known bit differs; === answers 1 |
PREC-AND, PREC-SHIFT, PREC-NOT | each pair is the expression as written, then with the parentheses most people meant |
SELF | a + b inside $display has no target to widen it, so it is 4 bits wide and prints 1 |
Change one thing. Delete signed from the declaration of s and run again. Decide first which
entries of SHIFT and COMPARE change: s>>>2 becomes 00100101, and signed turns from 1 to
0, because -1 is now just 255 on both sides.
11. Change one thing
In the lab, change the concatenation in BitFields.v to {data, 2'b10, 1'b0} and run again. The
width is still 11 bits, but data has moved to the top. Decide what the last three bits of framed
will be on the FOLLOW line before you look.
Check
With framed = {data, 2'b10, 1'b0}, what are the lowest three bits of framed?
A second change is in the testbench. Delete the first #1; in Tb.v and run again. Some of the
first three lines now contain x. At the instant data changes, the assign statements have not
been re-evaluated yet. Which outputs show x depends on the order in which the simulator happens to
schedule the updates. The design is correct; the testbench read the outputs too early. The same
mistake, and the #1 that fixes it, is listed under the common mistakes of
Nets, variables and the combinational
always.
12. Practice: route a byte
The checkpoint is the Practice problem nibble-swap: an 8-bit input whose two halves must trade
places on the output. Everything you need is in this chapter. Name the two halves with range selects,
join them in braces in the opposite order, and notice that the fix adds no gate at all, only a
different routing of the same eight wires. The grader checks every input value, not just the three
the public testbench prints.
The second problem, byte-merge, is the other side of section 3. Its assign lines are right and
one declared width is wrong, so bits disappear before the final assign can combine them. Predict
all three rows from the declared widths: the first is correct, but the other two lose selected
upper bits. Both problems ask
you to complete a small module that works on bytes: you rearrange bits with range selects and
concatenation, and see for yourself how a vector's width affects the result.
13. Summary
| Element | Meaning |
|---|---|
wire [7:0] d; | an 8-bit vector, bit 7 most significant; without brackets a signal is one bit |
d[7:4], d[0] | a range select or a single bit, as wide as the selection |
{a, b}, {n{a}} | concatenation and replication, first item most significant; the width is the sum, or n times the width of a |
| narrower or wider target | upper bits dropped, or filled with 0 for an unsigned value; Icarus warns about neither |
& versus && versus &a | bitwise, logical, reduction |
+ - * / % | computed at the width of the operands and target; the excess wraps; / by a variable is a large divider, by zero x |
<< >> <<< >>> | width from the left operand and assignment context, not the shift amount; >>> copies the sign bit only for a signed operand |
< == === | one bit; unsigned unless both sides are signed; == can give x, === cannot |
| precedence | bitwise operators bind more loosely than comparisons; + before << |
14. Check your understanding
-
What is the width of
{4'hF, 1'b0, data[2:0]}, and what is its most significant bit?Show answer
The width is 4 + 1 + 3 = 8 bits.
4'hFis written first, so it sits at the most significant end, and the most significant bit is 1. -
An 8-bit value is assigned to a 12-bit
wire. Which bits become 0, and is there a compile error?Show answer
The upper four bits, bits 11..8, are filled with 0 because the value is unsigned. Assignments between different widths are resized automatically, so there is no compile error and no warning.
-
After deleting
#1from the testbench, some outputs printedx. Which part needs fixing, the design or the testbench?Show answer
The testbench. The
assignstatements in the design are correct, but the testbench read the outputs at the very instant it changed the input, before they had been recomputed.
The x and z digits a literal may contain, and the padding %d added to the output you saw, are
the subject of Literals, signed values and expression
width.