Binary as you've used it so far only counts up from zero. Real designs also need −3 °C and negative error terms, but a wire carries only high or low; there is no third voltage for a minus sign. Two's complement solves this without adding a single wire, and the price it pays, fixed-width wrap-around, is exactly where overflow comes from. Both halves of that trade are this lesson.
Two's complement changes one thing about positional notation: the top bit's weight becomes negative. For 4 bits, the weights are no longer 8, 4, 2, 1 but −8, 4, 2, 1. That's the whole definition. Reading patterns is the same weighted sum as before:
0111 = 4 + 2 + 1 = +7 (top bit 0: plain positive)
1011 = -8 + 2 + 1 = -5
1111 = -8 + 4 + 2 + 1 = -1
1000 = -8 = -8
Two consequences fall out immediately. First, the top bit doubles as the sign bit: 1 means negative, 0 means zero-or-positive, always. Second, the range is lopsided: 4 bits span −8 to +7. Sixteen patterns, but one of them is zero, so the negatives get the extra value.
To negate, invert every bit, then add one. For 3:
0011 +3
1100 invert every bit
1101 add 1 -> -8 + 4 + 1 = -3 (check against the weights)
The same recipe works in both directions (1101 → 0010 → 0011 = +3). It has one famous edge: negating −8 gives 1000 again, because +8 doesn't fit in 4 bits: your first taste of why fixed width bites.
Nothing on the bus marks it as signed. The pattern 1011 is 11 if you read it unsigned and is −5 if you read it two's complement: same four wires, same voltages. The choice of reading lives in your head and, later, in the types you give your signals Signals and Data Types. What makes two's complement the universal choice is that the same adder circuit produces correct results under both readings, with no special signed hardware needed.
Moving a 4-bit value onto an 8-bit bus is a wiring job, but the obvious wiring is wrong for signed values. The correct rule is sign extension: copy the sign bit into every new position. 1101 becomes 11111101, and the weights confirm it: −128 + 64 + 32 + 16 + 8 + 4 + 1 = −3. All those extra ones cancel down to the same value, for any width.
CAUTION
Common Mistake: Padding a signed value with zeros. 1101 (−3) becomes 00001101, which reads as 8 + 4 + 1 = +13. The bit that carried the −8 weight now sits in a position with positive weight. Zero-padding is correct only for unsigned values.
To wire this yourself you need two syntax fragments this lesson hands you (they get full treatment in Signals and Data Types and Operators and Expressions). You can pick out one wire of a bundle, and you can glue bits into a wider bundle. To extend a 2-bit signed value n to 4 bits, copy the top wire of n into both new positions:
wide <= n(1) & n(1) & n; -- & glues bits left-to-right
VHDL spells wide extensions out with repeated & (a resize function exists, but it belongs to a later topic).
An adder's output bus is as fixed-width as everything else. Add two 4-bit values whose true sum needs 5 bits, and the top bit of the truth has no wire to land on. Watch 5 + 6:
0101 +5
+ 0110 +6
------
1011 true sum 11 needs 5 bits (01011); the leading 0 is lost,
and 1011 reads as -5
The result isn't garbage; it's exactly 11 − 16 = −5, the sum wrapped around the 16-pattern circle. That's overflow: a correct mod-16 answer presented as the wrong signed number.
You don't need arithmetic to detect it, only signs. Adding two numbers of the same sign must produce that sign, so if the result's sign differs, the sum overflowed. Opposite-sign operands can never overflow: their sum always sits between the two operands, safely in range. In the 5 + 6 example: two sign bits of 0, result sign 1, overflow.
The example panel on the right (Overflow in Action) is a 4-bit signed adder with an ovf flag. The testbench feeds it a mix of sums that fit and sums that wrap: two that fit, then three that wrap, then one final fit. ovf traces exactly that order. The example detects overflow by a second correct method. It extends both operands by one bit (the sign-extension trick from this lesson), then adds on 5 wires, where the true sum always fits. It flags overflow when the wide sum disagrees with the 4-bit result. (It uses the + operator, which you'll meet properly in Operators and Expressions; adders themselves are built gate-by-gate in Arithmetic Circuits.)
The wider sum5 declaration, the slice that takes back its low four bits, and the signed(...) wrappers are also machinery ahead of this topic. Read the wrappers as this lesson's two-readings idea in code: they tell the tools to treat those wires as two's complement. Declarations, slices, and casts get their full story in Signals and Data Types.
In the waveform, watch sum5 next to sum: on 5 + 6 the wide sum reads 11 while sum shows the wrapped 1011, and ovf goes high. Then check -8 + 7, where opposite signs keep ovf low exactly as the sign rule predicts.
sync-content --examples to generate.