Skip to content
code0.HDL

Literals, signed values and expression width

Write sized literals, know that an unsized one is at least 32 bits, read one bit pattern as signed or unsigned, and give an addition a target wide enough to keep its carry.

In this chapter

  • Read and write sized literals in any radix, and state the width of an unsized one in Icarus
  • Explain what x and z mean, how x spreads through an operator, and how %d, %0d, %b and %h show the same bits
  • Predict what a narrower or wider target does to an unsigned and to a signed value
  • Decide at what width an expression is evaluated and rewrite one so that its carry survives
Concepts in this chapter
  • Literals, signed, expression width
Prerequisite concepts

1. A literal carries its own width

A number in Verilog is written as a width, an apostrophe, a radix letter and the digits. The width is part of the value: 8'hA5 is eight bits, 4'b1010 is four.

Form

<width>'<radix><digits>     // 8'hA5, 4'b1010, 8'd7
Radix letterBaseExampleValue
bBinary4'b101010
oOctal6'o1715
dDecimal8'd77
hHexadecimal8'hA5165

A few more rules let you read any literal precisely.

  • An underscore _ between digits is ignored. It only makes long numbers easier to read, as in 8'b1100_0101.
  • x and z are allowed as digits. 4'b10x1 is a 4-bit value whose bit 1 is unknown.
  • A literal without a width, such as 12 or 'hA5, is an unsized literal. The standard says it is at least 32 bits wide. Icarus uses 32 for these small values but expands larger unsized constants as needed to preserve their value.

Note. Inside an expression an unsized literal is extended to the width the context needs, and its own width of at least 32 bits takes part in deciding the result width. That is why one careless + 1 can widen a whole calculation (section 6). In circuit code, make a habit of writing the width, as in 8'd7.

2. Every bit is 0, 1, x or z

Each bit in Verilog holds one of four values.

ValueMeaningWhere you see it
0Logic 0
1Logic 1
xUnknownA variable that has never been assigned, or a signal with conflicting drivers
zHigh impedance: nothing drives the signalAn unconnected wire

x does not mean "0 or 1, I don't care". It means "the simulator cannot tell what this bit is right now". When x or z reaches the input of an operator, every result bit that depends on it also becomes x; a bit the other operand already decides stays known, so 0 & x is 0 and 1 | x is 1.

3. Format specifiers choose how the bits are shown

The format string of $display uses specifiers similar to C's.

SpecifierPrints as8'hA5 prints
%bBinary10100101
%hHexadecimala5
%dDecimal, padded to the largest number of digits the width can need165
%0dDecimal, with no leading padding165

A format specifier only chooses how the same bits are shown. The stored value does not change.

4. Run: one byte in three radixes, padding, and x

Literals.v exercises literals, format specifiers, and the behavior of x in one go. Each output line starts with a marker such as RADIX or PAD, so you can tell at once which $display produced it. As you read the code, look for three things:

  • value is assigned 8'hA5.
  • nibble is assigned 4'b10x1, whose bit 1 is x.
  • never_set is declared but never assigned.
Literals.v
module literals;
  reg [7:0] value;
  reg [3:0] nibble;
  reg [7:0] never_set;

  initial begin
    value = 8'hA5;
    // One stored pattern, printed in three radixes.
    $display("RADIX hex=%h bin=%b dec=%0d", value, value, value);
    // %d pads to the widest decimal an 8-bit value can need.
    $display("PAD [%d] [%0d]", 8'd7, 8'd7);
    nibble = 4'b10x1;
    $display("FOUR-STATE nibble=%b", nibble);
    // A variable nobody has assigned yet holds x in every bit.
    $display("UNSET never_set=%b", never_set);
    // Any unknown input bit makes the whole sum unknown.
    $display("X-ADD %b", nibble + 4'd1);
    $display("SIZES %0d %0d", $bits(8'hA5), $bits('hA5));
    $finish;
  end
endmodule

Predict before you run.

  1. On the RADIX line, what are the hexadecimal, binary, and decimal forms of 8'hA5?
  2. On the PAD line, how many spaces appear in front of the 7 printed with %d?
  3. Does the never-assigned never_set print as 0?
  4. Only bit 1 of nibble is x. Is only one bit of nibble + 4'd1 unknown as well?

Check

On the PAD line, 8'd7 is printed with %d. How many spaces come before the 7?

Check

The SIZES line prints $bits(8'hA5) and then $bits('hA5). What is the second number in Icarus?

Check the result. Press Run and check each line against this table.

Output lineWhat it shows
RADIXThe same eight bits print as a5, 10100101, and 165
PADThe largest 8-bit value, 255, has three digits, so %d reserves three columns and prints 7. %0d prints just 7
FOUR-STATEThe x is printed in bit 1, exactly where it was written
UNSETA variable nobody assigned is not 0; it is eight x characters
X-ADDVerilog addition produces an all-x result if either operand contains x or z
SIZES$bits returns a width: 8 for 8'hA5, 32 for the unsized 'hA5

Also read UNSET: an unassigned variable is x in every bit, not 0. On X-ADD, one unknown operand bit makes the entire sum unknown. This is Verilog's arithmetic rule, not a bit-by-bit analysis of which sum bits a circuit could determine.

Try it yourself.

ChangeWhat happens
4'b10x1 to 4'b1001No bit is unknown any more, so X-ADD prints 1010
4'b10x1 to 4'b10z1A floating bit is just as unusable in arithmetic, so the sum is xxxx again

5. The same bits read as signed or unsigned

5.1 A signed declaration

A pattern has no sign of its own. The declaration says how to read it. reg [3:0] u reads 1111 as 15; reg signed [3:0] s reads the same 1111 as two's complement, which is −1, and %d prints whichever reading the declaration gives.

reg        [3:0] u;   // 1111 prints as 15
reg signed [3:0] s;   // 1111 prints as -1

Two rules follow the declaration into every expression.

RuleEffect
A binary addition is signed only if both operands are signedone unsigned operand makes the addition unsigned
A wider target extends the right-hand sidean unsigned value is zero-extended; a signed value is sign-extended

So s assigned to an 8-bit target becomes 1111_1111, keeping the value −1, while u assigned to the same target becomes 0000_1111, keeping 15. Mixing s + u drops the sign reading entirely. You have already run both rules: in the operators example, the same bits in a plain and in a signed vector gave different results for >>> and for <. Every other runnable example in this course is unsigned; these two rules are all the signed arithmetic you need to read them.

Check

s is declared reg signed [3:0] and holds 1111. What does %0d print for s?

5.2 Assigning between different widths

When the value and the target of an assignment have different widths, Verilog makes them match. The adjustment is applied to the operands before the expression is evaluated, which is why a wide target also widens the arithmetic feeding it (section 6).

CaseBehaviorExample: assign the 8-bit 1100_0101
Target is narrowerThe upper bits are dropped (truncation)A 4-bit target keeps 0101
Target is wider, value unsignedThe upper bits are filled with 0 (zero extension)A 12-bit target holds 0000_1100_0101
Target is wider, value signedThe upper bits copy the sign bit (sign extension)A 12-bit target holds 1111_1100_0101

Neither case is a compile error, and Icarus prints no warning for either by default; lint tools and synthesis tools usually do warn about a truncation. The width you declare for a signal is therefore part of how the circuit behaves. The narrow and wide outputs of Vectors, bit selects and operators were the first two rows of this table.

6. An expression is computed at the width of its widest participant

The width of an addition is not decided by the +. Verilog looks at the whole assignment: the operands and the target. The widest of them sets the width at which the expression is evaluated, and a carry survives only if that width has room for it.

6.1 Context-determined and self-determined widths

Most expressions are evaluated at a width chosen from the whole assignment, not from each operator alone. Which rule applies depends on where an operand sits.

KindOperand positionsEvaluated at
Context-determinedOperands of arithmetic and bitwise operators, both branches of ?:, the right-hand side of an assignmentThe widest of those operands and the assignment target
Self-determinedEach item inside { }, a shift amount, a comparison result, a conditionIts own width only
wire [7:0] a, b;
wire [8:0] total;
wire [7:0] y;
assign total = a + b;          // evaluated at 9 bits; the carry lands in bit 8
assign y     = (a + b) >> 1;   // evaluated at 8 bits; the carry is gone before the shift

In the first assignment the 9-bit target makes the addition 9 bits wide. In the second, every participant is 8 bits, so bit 8 of the sum is never produced. {a + b} with 8-bit operands is 8 bits for the same reason: an item inside braces is self-determined.

6.2 Widening on purpose, narrowing by accident

ExpressionAddition widthWhy
{1'b0, a} + b9 bitsOne 9-bit operand widens the whole expression
a + b + 0At least 32 bitsThe unsized 0 widens the addition; Icarus can further expand unsized expressions
{a + b}8 bitsAn item inside braces is self-determined

Note. A sum written inside braces is computed at its operands' width, however wide the assignment target is. The concatenation may look as if it widens the result, but the carry was already dropped before the braces were applied.

6.3 Run: averaging at two widths

Average.v takes its width from #(parameter W = 8) and writes every port width in terms of W; how a parameter is declared and overridden is the subject of Instances, hierarchy and parameters, and here it only means "the same module at two widths". It averages the same two inputs in two ways and also outputs their sum.

OutputComputationAddition width
avg_short(a + b) >> 1W
avg_full({1'b0, a} + b) >> 1W+1
totala + b assigned to a W+1-bit outputW+1

avg_short adds at W bits and shifts; avg_full puts a 1'b0 in front of one operand, which widens the whole addition to W+1 bits; total is assigned to a W+1-bit output, so the target widens the addition.

Average.v
// Average of two W-bit values, computed at two different widths.
module average #(parameter W = 8) (
  input  [W-1:0] a,
  input  [W-1:0] b,
  output [W-1:0] avg_short,
  output [W-1:0] avg_full,
  output [W:0]   total
);
  // Every operand here is W bits, so the carry of a + b is lost.
  assign avg_short = (a + b) >> 1;
  // One W+1-bit operand widens the whole expression.
  assign avg_full  = ({1'b0, a} + b) >> 1;
  // A W+1-bit target also widens the addition.
  assign total     = a + b;
endmodule

Tb.v places two instances. average #(.W(8)) wide keeps the default, and average #(.W(4)) narrow overrides W to 4, which gives that instance 4-bit inputs and a 5-bit total. Both instances first get a pair whose sum fits, then a pair whose sum overflows.

Tb.v
module tb;
  reg  [7:0] a8, b8;
  wire [7:0] short8, full8;
  wire [8:0] total8;
  reg  [3:0] a4, b4;
  wire [3:0] short4, full4;
  wire [4:0] total4;

  average #(.W(8)) wide   (.a(a8), .b(b8), .avg_short(short8), .avg_full(full8), .total(total8));
  average #(.W(4)) narrow (.a(a4), .b(b4), .avg_short(short4), .avg_full(full4), .total(total4));

  initial begin
    a8 = 8'd100; b8 = 8'd50;  a4 = 4'd6;  b4 = 4'd4;
    #1 $display("W8 %0d,%0d short=%0d full=%0d total=%0d", a8, b8, short8, full8, total8);
    $display("W4 %0d,%0d short=%0d full=%0d total=%0d", a4, b4, short4, full4, total4);
    a8 = 8'd200; b8 = 8'd100; a4 = 4'd12; b4 = 4'd10;
    #1 $display("W8 %0d,%0d short=%0d full=%0d total=%0d", a8, b8, short8, full8, total8);
    $display("W4 %0d,%0d short=%0d full=%0d total=%0d", a4, b4, short4, full4, total4);
    $finish;
  end
endmodule

Practice this concept

Use this concept in a short Verilog problem as your next step.

Predict before you run. The second 8-bit pair is 200 + 100 = 300, and 300 needs nine bits.

  1. Is avg_short 22 or 150?
  2. What are avg_full and total?
  3. For the 4-bit pair 12 + 10, what are short and full?

Check

For the 8-bit instance with 200 and 100, what does short print?

Your answer

Check

For the 4-bit instance with 12 and 10, what does full print?

Check the result. Press Run and compare the two W8 and two W4 lines with your answers.

Output lineWhat it shows
The first W8 and W4 linesThe sums fit, so short and full agree
W8 200,100short is 22: 300 truncated to 8 bits is 44, shifted once. full keeps 300 in 9 bits and prints 150. total is 300
W4 12,1022 loses 16 in four bits and becomes 6, so short is 3, while full is 11

The + operator did not lose the carry. The operands and the target were all too narrow, so nothing gave the addition a wider context. One definition produced two differently sized circuits, and each instance followed the same rule at its own W.

7. Change one thing

In Average.v, change avg_short to (a + b + 0) >> 1 and run again. Decide first what the unsized 0 does to the width of the addition.

Check

At what width is a + b + 0 computed when a and b are 8 bits?

Your answer

ChangeWhat happens
avg_short to (a + b + 0) >> 1The unsized 0 widens the addition to at least 32 bits, and the second-pair short values become 150 and 11
avg_full to {a + b} >> 1The braced sum is evaluated at W bits, so full drops to 22 and 3, the same as short

8. Common mistakes

8.1 Losing upper bits in a narrow target

wire [3:0] low4;
assign low4 = 8'hC5;   // low4 is 4'h5; the upper four bits vanish silently

There is no error and, in Icarus, no warning, so when a result looks wrong, check the width of every signal the data passes through.

8.2 Leading spaces from %d

%d reserves as many columns as the largest value of that width could need, so an 8-bit 7 prints as 7. Use %0d when you want no padding.

8.3 An unsized literal widening an expression

wire [7:0] a, b, y;
assign y = (a + b + 1) >> 1;    // 1 is 32 bits in Icarus, so the whole addition is 32 bits
assign y = (a + b + 8'd1) >> 1; // an 8-bit addition; the carry is lost

Which line is right depends on what you want, but you should know that one literal changes the result. Write sized literals in width-sensitive expressions, and when you need a wider result, widen an operand or the target explicitly.

9. Practice: keep the carry

The checkpoint is the Practice problem carry-out. A 4-bit adder drives a 5-bit sum, yet every result above 15 prints exactly 16 too small. Its assign writes {1'b0, a + b}: the addition sits inside braces, so it is self-determined at four bits, and the carry is gone before the leading zero is attached. The 5-bit target cannot help from outside the braces. Find where the width of the result is decided, rewrite the line so that all five bits of the sum appear, and check that 9 + 8 prints 17 and 15 + 15 prints 30.

10. Summary

ElementMeaning
8'hA5a sized literal: width, radix (b, o, d, h), digits; _ is ignored, x and z are digits
12, 'hA5unsized: 32 bits for these values in Icarus; larger constants can need more, and the width participates in the expression
0, 1, x, zthe four values of a bit; a variable that was never assigned is x, and an x input spreads to every result bit it can reach
%b, %h, %d, %0dhow the same bits are printed; %d pads to the widest decimal the width can need
reg signed [3:0]the same bits read as two's complement; a binary addition is signed only if both operands are signed
narrower or wider targettruncates the upper bits, or extends with 0 for an unsigned value and with the sign bit for a signed one; Icarus warns about neither by default, lint and synthesis tools usually do
total = a + b into a wider totalthe widest of the operands and the target sets the width: the target widens the addition and keeps the carry
{a + b}, a shift amount, a comparisonself-determined: items inside braces keep their own width, so the carry is lost

11. Check your understanding

  1. Only one bit of nibble is x. Why are all four bits of nibble + 4'd1 x?

    Show answer

    Verilog defines the entire addition result as x if any operand bit is x or z. This conservative simulation rule also marks bits unknown that a bit-by-bit circuit analysis could determine; unknown carry propagation alone does not explain the lowest result bit becoming x.

  2. In average #(.W(4)) narrow, how wide is total, and how wide is the addition that drives avg_short?

    Show answer

    total is declared [W:0], so it is 5 bits. The addition for avg_short has 4-bit operands and a 4-bit target, so it is evaluated at 4 bits.

  3. Why does {1'b0, a} + b keep the carry while {a + b} does not?

    Show answer

    {1'b0, a} is a W+1-bit operand, so the whole addition runs at W+1 bits. {a + b} is an item inside braces, so it is self-determined at W bits and the carry bit is never produced.

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