Digital Design / Module 1: Numbers and logic
1. Binary, hex and two's complement
Read and convert the number formats every waveform uses.
See it move
The idea
Wires carry only two voltage levels, so everything a chip handles is a pattern of bits. The pattern has no meaning of its own. The same eight bits, 11110011, are 243 if you read them as unsigned and -13 if you read them as two's complement. The hardware doesn't know which you intended. You do, and your design has to treat the bits consistently.
Reading the formats
Each binary position is worth a power of two. With N bits you get 2^N patterns. Hex is shorthand: one hex digit is exactly four bits, which is why waveforms and memory dumps use it. 1111 0011 is F3.
Two's complement gives the top bit a negative weight. In 8 bits, that top bit is worth -128 rather than +128, so the signed range is -128 to +127. In general it is -2^(N-1) to 2^(N-1)-1. There is one more negative value than positive, because zero uses up a non-negative pattern.
Why chips use two's complement
To negate a number, invert every bit and add one. The real prize is that subtraction becomes addition: A - B equals A + ~B + 1. One adder handles signed and unsigned values, and zero has only one pattern. The older sign-magnitude format has both +0 and -0, and it needs separate logic to subtract.
Worked example: -13 in 8 bits
- Start with +13:
00001101 - Invert every bit:
11110010 - Add one:
11110011, which isF3in hex
Check it by adding the weights: -128 + 64 + 32 + 16 + 2 + 1 = -13.
Common mistake
Widening a signed number by padding with zeros. -13 in 8 bits is F3, but in 16 bits it is FFF3, not 00F3. Copy the sign bit into the new positions. This is called sign extension. In Verilog, mixing signed and unsigned operands quietly makes the whole expression unsigned, which gives the same kind of bug.
Key ideas
- An N-bit value holds 2^N patterns; meaning comes from how we interpret them.
- Two's complement negates by inverting and adding one.
- An N-bit signed range is -2^(N-1) to 2^(N-1)-1.
Your exercise
Interview questions
coreWhat is the range of a 6-bit signed number?
-32 to +31.
coreWhy does two's complement dominate?
Addition and subtraction use the same adder, and zero has one representation.
basicHow many different patterns can a 10-bit bus carry, and what range does it cover when read as unsigned?
Each extra bit doubles the count, so 10 bits give 2^10 = 1024 patterns. Read as unsigned, they run from 0 up to 1023. The top value is always one less than the pattern count, because zero takes one of the patterns.
basicWrite -1 as an 8-bit and as a 16-bit two's complement value, in hex.
In 8 bits it is FF and in 16 bits it is FFFF: every bit is 1. One way to see it is that adding 1 to an all-ones pattern wraps round to zero, which is exactly what -1 + 1 should give. It is also a quick sign-extension check, since copying the sign bit of FF into eight new positions gives FFFF.
coreThe byte 0xB6 appears on a bus. What is it in binary, as an unsigned number and as an 8-bit signed number?
B is 1011 and 6 is 0110, so the byte is 10110110. As unsigned it is 128 + 32 + 16 + 4 + 2 = 182. As two's complement the top bit weighs -128 instead of +128, giving -128 + 54 = -74, which is also 182 - 256. The bits are the same in both cases; only the reading changes.
coreApply the invert-and-add-one rule to -128 in 8 bits. What do you get, and why?
-128 is 10000000. Inverting gives 01111111, and adding one gives 10000000 again, so the result is -128. The true answer, +128, does not fit, because the 8-bit signed range stops at +127. The range has one more negative value than positive, so negating the most negative number always overflows.
coreA designer widens a signed 8-bit sample to 16 bits as shown. The sample holds -13. What does w hold, and how do you fix it?
wire [7:0] a = 8'hF3; // -13
wire [15:0] w = a; // zero-padded
wire [15:0] s = {{8{a[7]}}, a}; // sign-extendedThe plain assignment pads with zeros because a is unsigned, so w is 00F3, which reads as +243. The value should be FFF3, made by copying the sign bit into the eight new positions, as s does with {{8{a[7]}}, a}. Declaring the source signed also makes Verilog sign-extend when the whole expression is signed.
coreWhat is the smallest two's complement width that can hold every integer from -100 to +100?
Eight bits. A width of N covers -2^(N-1) to 2^(N-1)-1, so 7 bits only reach -64 to +63, which is too small. Eight bits cover -128 to +127, which contains the whole range.
advancedIn this Verilog, sa holds -3 and ub holds 5. Is sa < ub true or false, and why?
reg signed [7:0] sa = -8'sd3; reg [7:0] ub = 8'd5; // what does (sa < ub) give?
It is false. One operand is unsigned, so Verilog evaluates the whole comparison as unsigned, and -3 is read as its 8-bit pattern 253. 253 is not less than 5. Writing sa < $signed(ub) makes both sides signed and gives true; the general fix is to keep both operands signed (or both unsigned) on purpose.
advancedYou must narrow a 16-bit signed value x to 8 bits and raise a flag when it does not fit. How do you test whether it fits?
wire fits = (x[15:7] == 9'h000) ||
(x[15:7] == 9'h1FF);
wire [7:0] x8 = x[7:0];A value fits in 8 signed bits only if bits 15 down to 7 are all copies of the sign, so they must be all zeros or all ones. That gives fits = (x[15:7] == 9'h000) || (x[15:7] == 9'h1FF). Bit 7 has to be included, because a value like 0x0080 (+128) has zeros above it but would read as -128 after narrowing.