Skip to content
code0.HDL

Vectors, bit selects and operators

Bundle bits into a vector, pull ranges out of it, join pieces with braces, and learn what each operator family builds and how wide its result is, from bitwise, logical and reduction operators to arithmetic, shifts, comparisons and precedence, before rearranging a byte in Practice.

In this chapter

  • Declare a vector with [MSB:LSB] and name the value of each bit position
  • Compute the width and value of a range select, a concatenation and a replication by hand
  • Predict what a narrower or wider assign target does to a value, and why Icarus stays silent
  • Choose between the bitwise, logical and reduction forms of an operator by the width the result needs
  • Read the width, wrap-around and signedness of arithmetic, shift and comparison results, and place parentheses where precedence differs from reading order
Concepts in this chapter
  • Vectors, bit select, concatenation, operators
Prerequisite concepts

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 bits

Without 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.

SyntaxResultWidth
data[7:4]Bits 7 down to 4, the high nibble4
data[0]Bit 0 alone1

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
SyntaxNameMeaningResult width
{a, b, c}Concatenationa, then b, then c, most significant firstThe sum of the item widths
{n{a}}Replicationa repeated n timesThe 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.

BitFields.v
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;
endmodule

Tb.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.

Tb.v
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
endmodule

Practice 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?

Your answer

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.

TargetBehavior1100_0101 assigned to it
[3:0], narrowerupper bits dropped (truncation)0101
[11:0], widerupper 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:

OutputCalculationValue
highBits 7..41100
lsbBit 01
framed10, then the 8 bits of data, then 010_11000101_0
repeatedThe low nibble 0101, twice01010101
narrowA 4-bit target keeps only the low four bits0101
wideA 12-bit target gets four leading zeros0000_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.

FamilyOperatorsWorks onResult
Bitwise&, |, ^, ~two vectors, bit by bitas wide as the operands
Logical&&, ||, !two whole values, each true when non-zeroone bit
Reduction&a, |a, ^aone vector, folding all its bitsone 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?

Your answer

5. Arithmetic operators build adders, and wrap at the width

OperatorMeaningHardware
a + b, a - baddition, subtractionan adder; subtraction adds the two's complement
a * bmultiplicationa multiplier array, much larger than an adder
a / b, a % bquotient, remainderby a constant power of two: a shift and a select, no gates; by anything else: a large divider
a ** bpowermeant 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

OperatorNameBits shifted in
a << nlogical left shift0 at the bottom
a >> nlogical right shift0 at the top
a <<< narithmetic left shift0 at the bottom, the same as <<
a >>> narithmetic right shiftcopies 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

OperatorQuestionResult
<, <=, >, >=is one value smaller or larger1 bit, or x if an operand has an x or z bit
==, !=are the values equal1 bit, or x if an x or z bit decides the answer
===, !==are the bit patterns identical, x and z includedalways 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.

LevelOperators
1unary + - ! ~, reduction & ~& | ~| ^ ~^
2**
3* / %
4binary + -
5<< >> <<< >>>
6< <= > >=
7== != === !==
8binary &
9binary ^ ~^
10binary |
11&&
12||
13?:

Three places where the table disagrees with how the expression reads:

WrittenRead by Verilog asUsually meant
a & b == ca & (b == c): a 1-bit comparison, then a bitwise AND(a & b) == c
a + b << 1(a + b) << 1: the sum doubleda + (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.

Operators.v
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
endmodule

Check

s is a signed 8-bit variable holding -1. What does s < 8'd1, the mixed comparison on the COMPARE line, print?

Your answer

Press Run and read the output line by line.

LineWhat to look at
ARITHsum4=1 lost the carry of 17, sum5=17 kept it; diff=9 is 5 - 12 wrapped in four bits
DIVa/4=3 and a%4=0 are the upper and lower bits of 12; a/0=x
SHIFTthree results are 00100101; only s>>>2, the arithmetic shift of a signed value, fills with 1
SHIFTNa shift by the variable n = 3; bits pushed past the end are gone
COMPAREthe 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-NOTeach pair is the expression as written, then with the parentheses most people meant
SELFa + 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?

Your answer

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

ElementMeaning
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 targetupper bits dropped, or filled with 0 for an unsigned value; Icarus warns about neither
& versus && versus &abitwise, 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
precedencebitwise operators bind more loosely than comparisons; + before <<

14. Check your understanding

  1. 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'hF is written first, so it sits at the most significant end, and the most significant bit is 1.

  2. 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.

  3. After deleting #1 from the testbench, some outputs printed x. Which part needs fixing, the design or the testbench?

    Show answer

    The testbench. The assign statements 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.

Checkpoint

Submit the Practice problem for grading

Grading runs on the server against hidden tests; the result is recorded in this browser only.

Sources

Licences and notices for the sources this site follows