Bit widths, signed arithmetic and overflow
How many bits a result needs, how negative numbers work, and what happens when a value does not fit.
On this page
How many bitsUnsigned and signedWriting signed arithmeticA worked example: MAC-8OverflowIn the waveformIn a testMost arithmetic bugs in hardware are width bugs. Software hides them behind 32- and 64-bit integers. Hardware does not.
How many bits#
| Operation | Width of the full result |
|---|---|
| N bits + N bits | N + 1 |
| N bits x M bits | N + M |
| Adding K values of N bits | N + the number of bits needed to count to K |
If the signal you assign to is narrower than the full result, the top bits are thrown away without warning.
Unsigned and signed#
The same 8 bits mean different things.
| Bits | Unsigned | Signed (two's complement) |
|---|---|---|
0000_0001 | 1 | 1 |
0111_1111 | 127 | 127 |
1000_0000 | 128 | -128 |
1111_1110 | 254 | -2 |
1111_1111 | 255 | -1 |
An 8-bit signed value runs from -128 to 127.
Writing signed arithmetic#
Declare the signals signed. If any operand in an expression is unsigned, the whole expression is treated as unsigned.
input logic signed [7:0] a, b;
logic signed [15:0] product;
assign product = a * b;A worked example: MAC-8#
The MAC-8 starter multiplies two signed 8-bit numbers and adds the product to a running total. Its specification works the widths out:
- One product is between -16256 (127 x -128) and 16384 (-128 x -128). That needs 16 bits.
- The accumulator is 20 bits, from -524288 to 524287.
- So at least 32 worst-case products fit before it wraps.
To add a 16-bit signed product to a 20-bit total, the product is sign-extended: its top bit is copied into the new upper bits.
acc <= acc + {{4{product[15]}}, product};Overflow#
When a result does not fit, something has to happen. Decide which, and write it in the specification.
| Behaviour | What happens | Cost |
|---|---|---|
| Wrap | The top bits are dropped. 127 + 1 becomes -128 | None. This is what hardware does by default |
| Saturate | The value sticks at the largest or smallest it can hold | A comparator and a multiplexer |
| Cannot happen | The width is large enough for every case | A proof, written down |
MAC-8 wraps, and says so: "Wrap-around is not detected."
In the waveform#
The waveform shows values in hexadecimal. -2 in 8 bits appears as fe, and -3 as fd. Most viewers can switch a signal to signed decimal.
In a test#
Compare like with like. Read a signed output with .value.to_signed(), or mask your Python result to the output's width: (a - b) & 0xFF.
Something unclear or missing? Send feedbackSign in and use Feedback to tell us.