By the end of this chapter you should be able to:
- Distinguish weighted from unweighted codes, and recover a decimal digit from an 8421, 2421 or 5211 pattern by applying the weights.
- Define the self-complementing property, test a code for it, and explain why it simplifies 9's-complement decimal subtraction.
- Construct the Gray code by reflection, and convert between binary and Gray in both directions using exclusive-OR.
- Explain the shaft-encoder ambiguity that motivates the Gray code, and state what the worst-case reading error becomes once it is used.
- Generate and check a parity bit, determine the number of Hamming check bits required for a given data width, and locate and correct a single error from the syndrome.
Chapters 2 and 3 treated a bit pattern as a number. That is not the only thing a bit pattern can be. The same four wires that carry a binary weight can instead carry a decimal digit, a position on a rotating shaft, a letter of the alphabet, or a number together with enough redundancy to survive a corrupted bit. A code is simply an agreed assignment of bit patterns to things, and the assignment is chosen to make some particular job easy.
This chapter works through the codes an EEE graduate actually meets: the decimal codes that make a seven-segment display straightforward, the reflected code that makes a shaft encoder trustworthy, the character code that every serial link uses, and the two error codes — parity and Hamming — that separate noticing a fault from repairing it. In each case the code exists because a natural binary representation made something awkward.
1 Weighted Codes: 8421, 2421 and 5211
A code is weighted if each bit position carries a fixed numerical weight and the decimal digit is the sum of the weights where a 1 appears. This is the same weighted-sum idea as Chapter 2, except that the weights no longer have to be powers of a base — they are chosen by whoever designed the code.
The most familiar is 8421 BCD, whose weights are the ordinary binary ones, so each decimal digit is written exactly as it would be in four-bit binary. Only 0000 through 1001 are used; the six patterns 1010 to 1111 are invalid and, if they appear, indicate a fault or an uncorrected arithmetic result of the kind Chapter 3 dealt with. A reading of 947 is stored as three separate groups, 1001 0100 0111, and not as the binary number 947, which is 1110110011. The two are different things.
| Decimal | 8421 (BCD) | 2421 | 5211 | Excess-3 | Gray |
|---|---|---|---|---|---|
| 0 | 0000 | 0000 | 0000 | 0011 | 0000 |
| 1 | 0001 | 0001 | 0001 | 0100 | 0001 |
| 2 | 0010 | 0010 | 0011 | 0101 | 0011 |
| 3 | 0011 | 0011 | 0101 | 0110 | 0010 |
| 4 | 0100 | 0100 | 0111 | 0111 | 0110 |
| 5 | 0101 | 1011 | 1000 | 1000 | 0111 |
| 6 | 0110 | 1100 | 1001 | 1001 | 0101 |
| 7 | 0111 | 1101 | 1011 | 1010 | 0100 |
| 8 | 1000 | 1110 | 1101 | 1011 | 1100 |
| 9 | 1001 | 1111 | 1111 | 1100 | 1101 |
Two other weighted codes appear in syllabuses and are worth being able to decode on sight. The 2421 code (also called the Aiken code) has weights 2, 4, 2 and 1. The digit 5 is 1011, since \(2+2+1 = 5\); the digit 8 is 1110, since \(2+4+2 = 8\). Because the weights sum to 9 rather than 15, some digits could be written more than one way — 5 is also \(4+1\), which is 0101 — and the code fixes the ambiguity by a rule: digits 0 to 4 take the pattern with a 0 in the leading position and digits 5 to 9 take the pattern with a 1 there. Section 2 shows what that rule buys.
The 5211 code has weights 5, 2, 1 and 1. Here 7 is 1011, since \(5+1+1 = 7\), and 3 is 0101, since \(2+1 = 3\). Its practical attraction is that the leading bit is 1 for exactly the digits 5 to 9, so a single wire divides the decade in half — useful in a counter that must round or divide by two.
Apply the weights to every row and check that you recover the digit. For 5211 and 1101: \(5+2+0+1 = 8\). For 2421 and 1100: \(2+4+0+0 = 6\). A code whose weights fail on even one row is not a weighted code.
Packed BCD stores two digits in one byte, which is why a real-time clock returns 0x59 for fifty-nine seconds rather than 0x3B. Reading that register as ordinary binary gives 89.
2 Unweighted Codes and the Self-Complementing Property
An unweighted code assigns patterns to digits by a rule that is not a weighted sum at all. The two that matter here are Excess-3 and Gray.
Excess-3 is the plain binary value of the digit plus three, so 0 is 0011 and 9 is 1100. No set of four weights reproduces that table — in particular any weighted code must give 0000 for zero, and Excess-3 does not. The offset looks arbitrary until you notice what it does to complements.
A code is self-complementing if inverting every bit of the pattern for a digit \(D\) produces the pattern for \(9-D\). Test Excess-3 on \(D = 2\): the pattern is 0101, its bitwise inverse is 1010, and 1010 is the Excess-3 pattern for 7, which is indeed \(9-2\). The reason is algebraic rather than lucky. The stored value is \(D+3\); inverting a four-bit value \(v\) gives \(15-v\), so the inverse holds \(15-(D+3) = 12-D\), and \(12-D = (9-D)+3\), which is precisely the Excess-3 pattern for \(9-D\). The offset of 3 is what makes the arithmetic close.
The 9's complement of a decimal digit — needed for decimal subtraction in exactly the way the 1's complement was needed for binary subtraction in Chapter 3 — is obtained from a self-complementing code by a row of inverters, with no arithmetic and no delay. In 8421 the same operation needs a subtractor.
The test fails immediately for 8421. Take \(D = 4\), pattern 0100; its inverse is 1011, which is not a valid BCD pattern at all, let alone the pattern for 5. It succeeds for 2421 on every row, and that is the whole reason the awkward duplicate-pattern rule of the previous section was chosen the way it was: \(D=4\) is 0100, whose inverse 1011 is the 2421 pattern for 5. The 5211 code passes on some digits and fails on others — \(D=1\) is 0001, whose inverse 1110 is not the 5211 pattern for 8, which is 1101 — and a code that works on only some rows is of no use, because the hardware cannot know which row it is holding.
Converting between codes is a small combinational design problem. Excess-3 from BCD is simply “add 0011” — a four-bit adder with a constant, or four gates once minimised. Chapter 20 designs these converters properly, using the technique of Chapter 8, where the six unused BCD patterns become don't-care conditions that shrink the result considerably.
3 The Gray Code and Its Conversions
The Gray code, also called the reflected binary code, orders the \(2^n\) patterns so that any two consecutive entries differ in exactly one bit. That single property is the whole point of it; the code carries no useful weights and you cannot do arithmetic in it.
It is built by reflection, which is where the other name comes from. Start with the one-bit list 0, 1. To extend to \(n+1\) bits: write the \(n\)-bit list, then write it again underneath in reverse order, and prefix 0 to every entry in the first half and 1 to every entry in the second. The mirror line guarantees that the two entries straddling it differ only in the new leading bit, and every other adjacent pair inherits its single-bit difference from the shorter list. Three bits give 000, 001, 011, 010, 110, 111, 101, 100 — and the last entry differs from the first in one bit too, so the sequence is cyclic, which is what a rotating shaft needs.
That ordering will reappear in Chapter 8: the rows and columns of a Karnaugh map are labelled in Gray order precisely so that physically adjacent cells differ in one variable, which is what makes grouping them a valid simplification.
Reflection is fine for building a table but useless for converting one number. For that there are two formulas, and both are pure exclusive-OR. Writing the binary word as \(B_{n-1}\ldots B_0\) and the Gray word as \(G_{n-1}\ldots G_0\):
The two look almost identical but behave very differently in hardware. In the first, every output depends only on the original inputs, so all the gates work at once and the conversion costs one gate delay. In the second, each output depends on the output above it, so the gates form a chain \(n-1\) deep.
(a) Binary 10110110 to Gray. Copy the MSB, then exclusive-OR each bit with the one to its left. Taking the pairs in order — \(1{\oplus}0\), \(0{\oplus}1\), \(1{\oplus}1\), \(1{\oplus}0\), \(0{\oplus}1\), \(1{\oplus}1\), \(1{\oplus}0\) — gives 1, 1, 0, 1, 1, 0, 1, so the Gray word is 11101101.
(b) Gray 11101001 to binary. The MSB is copied: \(B_7 = 1\). Then each binary bit is the previous binary bit exclusive-ORed with the next Gray bit: \(B_6 = 1{\oplus}1 = 0\), \(B_5 = 0{\oplus}1 = 1\), \(B_4 = 1{\oplus}0 = 1\), \(B_3 = 1{\oplus}1 = 0\), \(B_2 = 0{\oplus}0 = 0\), \(B_1 = 0{\oplus}0 = 0\), \(B_0 = 0{\oplus}1 = 1\). The binary word is 10110001, which is 177 in decimal.
Check (b) by running it forward: converting 10110001 to Gray gives 11101001, the word we started with. Always close the loop like this — a single mis-copied bit in the cascade corrupts every bit below it, so an error in the second method is never isolated.
4 Why the Gray Code Exists: The Shaft Encoder
An absolute shaft encoder reports the angular position of a rotating shaft. A disc fixed to the shaft carries \(n\) concentric tracks of conducting and insulating sectors, and \(n\) brushes or optical sensors read one bit from each. Three tracks divide the disc into eight sectors.
Pattern the tracks in natural binary and the encoder works perfectly — as long as the shaft never moves. The trouble is at the boundaries. Going from position 3 to position 4, the code goes from 011 to 100 and all three bits change. No mechanical assembly aligns three brushes to better than a fraction of a sector, so for a brief interval one track has already switched and another has not. The reading during that interval is neither 3 nor 4 but whatever combination the misalignment produces — possibly 000, which is 0, or 111, which is 7.
That is not a small inaccuracy but a wrong answer at the far end of the range, arriving briefly and repeatedly at one particular angle. A control loop reading such an encoder receives a violent spurious error every revolution, in the same place each time, and will act on it.
If only one track changes at a boundary, then a misaligned brush can only be early or late on that one track. The reading is therefore either the position being left or the position being entered — never anything else. The error is bounded by one least significant step, which is the resolution of the encoder anyway.
Figure 4.1 makes the comparison directly. At the 3 → 4 boundary the binary tracks change from 011 to 100, three simultaneous transitions; the Gray tracks change from 010 to 110, one transition on the outermost track. Every boundary behaves that way, including the wrap from 7 back to 0.
The price is that the encoder delivers a number the rest of the system cannot use directly, so a Gray-to-binary converter sits immediately after it — the cascade in the lower half of Figure 4.2, developed as a standard code converter in Chapter 20. The same single-bit-change argument recurs later: it is why the Johnson counter of Chapter 26 decodes without glitches, and it underlies the hazards of Chapter 11, where a circuit misbehaves precisely because two inputs did not change at quite the same instant.
5 ASCII and Error Detection by Parity
Text needs a code too. ASCII, the American Standard Code for Information Interchange, assigns a seven-bit pattern to each of 128 characters: 95 printable ones and 33 control codes — enough for the Latin alphabet in both cases, the digits, punctuation, and the control characters a teleprinter needed.
Three regularities make the table worth understanding rather than memorising. The digits 0 to 9 occupy 0x30 to 0x39, so the low four bits of a digit character are its BCD value and converting between the two is a matter of adding or subtracting 0x30. Upper-case letters run from 0x41 (A) to 0x5A (Z) and lower-case from 0x61 (a) to 0x7A (z), so the two cases differ in exactly one bit, \(b_5\); changing case is one XOR with 0x20, not a table lookup. And the control codes occupy 0x00 to 0x1F, which is why carriage return is 0x0D and line feed 0x0A.
A seven-bit character sits awkwardly in an eight-bit byte, and the spare bit was traditionally used for a parity bit. In even parity it is chosen so that the total number of 1s in the transmitted word is even; in odd parity, odd. Chapter 17 builds the generator and the checker — both are a tree of exclusive-OR gates, because an XOR of many inputs is 1 exactly when an odd number of them are 1.
| Character | Decimal | Hex | Seven-bit ASCII | 1s | Parity bit | Word sent |
|---|---|---|---|---|---|---|
| H | 72 | 0x48 | 1001000 | 2 | 1 | 11001000 |
| i | 105 | 0x69 | 1101001 | 4 | 1 | 11101001 |
| ! | 33 | 0x21 | 0100001 | 2 | 1 | 10100001 |
Each character happens to contain an even number of 1s, so each needs a parity bit of 1 to make the total odd. A receiver that counts an even number of 1s knows something is wrong.
What parity cannot do is as important as what it can. A single parity bit detects any odd number of bit errors and is completely blind to any even number: if two bits flip, the count changes by zero, two or minus two, and its parity is unaltered. Nor can parity say which bit is wrong, so the only recovery is to ask for the message again. Where retransmission is impossible — a memory read, a signal already in flight from a spacecraft — detection is not enough, and the next section shows what more redundancy buys.
6 The Hamming Code: Locating a Single Error
Parity gives a single yes-or-no answer. Hamming's idea is to use several parity bits, each covering a different overlapping subset of the word, so that the pattern of failed checks identifies the guilty bit — and if the subsets are chosen well, that pattern is the bit's position number written in binary.
Start with how many check bits are needed. With \(k\) check bits there are \(2^k\) possible patterns of check results. One of them must mean “no error”, and each of the \(m+k\) positions in the code word needs one of the rest. So
For \(m=4\) data bits, \(k=3\) satisfies it (\(8 \ge 8\)), giving the classic (7, 4) code. For \(m=8\), \(k=3\) fails (\(8 < 12\)) but \(k=4\) works (\(16 \ge 13\)), giving a 12-bit word. For \(m=16\), \(k=5\); for \(m=32\), \(k=6\) — the overhead falls as the word grows, which is why memory error correction is applied to wide words.
The placement is what makes the scheme work. Number the positions of the code word from 1 on the left, and put the check bits at the positions that are powers of two — 1, 2, 4, 8 — leaving every other position for data. Check bit \(P_p\) then covers every position whose number has a 1 in bit \(p\), including its own. So \(P_1\) covers 1, 3, 5, 7, 9, 11; \(P_2\) covers 2, 3, 6, 7, 10, 11; \(P_4\) covers 4, 5, 6, 7, 12; and \(P_8\) covers 8, 9, 10, 11, 12. Each check bit is set so that its group has even parity.
Now suppose exactly one bit is corrupted, at position \(e\). Every check whose group contains position \(e\) fails; every check whose group does not, passes. By construction the checks that contain \(e\) are exactly those whose bit is 1 in the binary expansion of \(e\). Assemble the results as \(C_8C_4C_2C_1\), taking 1 for a failed check, and the number you have written is \(e\) itself. That word is the syndrome. A syndrome of zero means no single error was present.
Encode. The data byte is 10110011. Its bits go into positions 3, 5, 6, 7, 9, 10, 11, 12 in order, giving \(D_3=1\), \(D_5=0\), \(D_6=1\), \(D_7=1\), \(D_9=0\), \(D_{10}=0\), \(D_{11}=1\), \(D_{12}=1\). Each check bit is the even-parity bit of its group:
- \(P_1\) over positions 3, 5, 7, 9, 11: \(1{\oplus}0{\oplus}1{\oplus}0{\oplus}1 = 1\).
- \(P_2\) over positions 3, 6, 7, 10, 11: \(1{\oplus}1{\oplus}1{\oplus}0{\oplus}1 = 0\).
- \(P_4\) over positions 5, 6, 7, 12: \(0{\oplus}1{\oplus}1{\oplus}1 = 1\).
- \(P_8\) over positions 9, 10, 11, 12: \(0{\oplus}0{\oplus}1{\oplus}1 = 0\).
The transmitted code word, positions 1 to 12, is therefore 1 0 1 1 0 1 1 0 0 0 1 1.
Corrupt. Suppose noise inverts position 9. The receiver sees 1 0 1 1 0 1 1 0 1 0 1 1.
Repair. Recompute each parity over its full group, this time including the check bit itself, and record 1 for a failure:
| Check | Positions covered | Bits received | Parity | Result |
|---|---|---|---|---|
| \(C_1\) | 1, 3, 5, 7, 9, 11 | 1, 1, 0, 1, 1, 1 | odd | fail — 1 |
| \(C_2\) | 2, 3, 6, 7, 10, 11 | 0, 1, 1, 1, 0, 1 | even | pass — 0 |
| \(C_4\) | 4, 5, 6, 7, 12 | 1, 0, 1, 1, 1 | even | pass — 0 |
| \(C_8\) | 8, 9, 10, 11, 12 | 0, 1, 0, 1, 1 | odd | fail — 1 |
The syndrome, written \(C_8C_4C_2C_1\), is 1001, which is 9. Inverting position 9 restores 1 0 1 1 0 1 1 0 0 0 1 1, and stripping positions 1, 2, 4 and 8 recovers the data 10110011. The syndrome pointed at the error with nobody knowing in advance where it was, and the repair needed no retransmission.
The limits follow from counting. A Hamming code has a minimum distance of 3 between valid code words, so it corrects one error or detects two, but not both at once — two errors produce a non-zero syndrome that points confidently at an innocent third position. Adding one more overall parity bit over the whole word raises the distance to 4 and gives the SECDED code (single error correction, double error detection) used in server memory, where the extra bit distinguishes “one error, corrected” from “two errors, give up and report”. Chapter 27 returns to this when it discusses what a memory subsystem does about soft errors.
7 Summary and Key Results
| Code | Rule | What it is for |
|---|---|---|
| 8421 BCD | Four bits per decimal digit, values 0000–1001 | Direct decimal display; six patterns per digit wasted |
| 2421 (Aiken) | Weights 2, 4, 2, 1 with 0–4 leading 0 and 5–9 leading 1 | Weighted and self-complementing |
| 5211 | Weights 5, 2, 1, 1 | Leading bit splits the decade at 5; not self-complementing |
| Excess-3 | Binary value of the digit plus 3 | Self-complementing, so 9's complement is a row of inverters |
| Gray | \(G_i = B_{i+1} \oplus B_i\); one bit changes per step | Shaft encoders, K-map labelling, glitch-free decoding |
| ASCII | Seven bits per character, 128 codes | Text; digits at 0x30, case differs in bit \(b_5\) |
| Parity | One extra bit making the count of 1s even or odd | Detects any odd number of errors; corrects none |
| Hamming | \(k\) checks at positions \(2^p\), with \(2^k \ge m+k+1\) | Syndrome gives the position of a single error |
8 Common Mistakes
The decimal number 947 in BCD is 1001 0100 0111, twelve bits holding three separate digits. The binary number 947 is 1110110011, ten bits holding one value. They are different patterns, of different lengths, and only one of them can be added with a plain binary adder. The same confusion in reverse is what makes a real-time clock register read 89 when it means 59.
Binary to Gray is \(G_i = B_{i+1} \oplus B_i\), where every term is an original input. Gray to binary is \(B_i = B_{i+1} \oplus G_i\), where the first term is a bit you have just computed, not an input. Writing \(B_i = G_{i+1} \oplus G_i\) is the standard slip and it gives the wrong answer on any word with more than two 1s. It also hides a real engineering difference: the first circuit is one gate deep, the second is \(n-1\) gates deep.
With a minimum distance of 3 the code corrects one error or detects two, never both. If two bits flip, the syndrome is the exclusive-OR of the two positions and points at a third, undamaged bit, which the decoder then cheerfully inverts — leaving three wrong bits instead of two. A code that must tolerate double errors needs the extra overall parity bit of SECDED, and that is a design decision taken before the first bit is stored.
9 Chapter Review
1. Write the decimal number 947 in 8421 BCD and in Excess-3, and state how many bits each needs.
BCD encodes each digit separately: 9 is
1001, 4 is0100and 7 is0111, giving1001 0100 0111. Excess-3 adds three to each digit before encoding: 12 is1100, 7 is0111and 10 is1010, giving1100 0111 1010. Both need twelve bits, four per digit; twelve bits of straight binary would reach 4095, so both codes leave three quarters of the range unused.2. Convert the binary word
1101101to Gray, and the Gray word1011010back to binary.For the first, copy the MSB (1) and exclusive-OR each remaining bit with the one to its left: \(1{\oplus}1=0\), \(1{\oplus}0=1\), \(0{\oplus}1=1\), \(1{\oplus}1=0\), \(1{\oplus}0=1\), \(0{\oplus}1=1\), giving
1011011. For the second, copy the MSB (1), then \(B_5 = 1{\oplus}0 = 1\), \(B_4 = 1{\oplus}1 = 0\), \(B_3 = 0{\oplus}1 = 1\), \(B_2 = 1{\oplus}0 = 1\), \(B_1 = 1{\oplus}1 = 0\), \(B_0 = 0{\oplus}0 = 0\), giving1101100, which is 108. Converting 108 forward returns1011010, confirming the result.3. Show that 8421 BCD is not self-complementing but 2421 is, using the digit 4 in each case.
In 8421 the digit 4 is
0100. Inverting every bit gives1011, which is not a valid BCD pattern at all and certainly not 5, so the code fails the test. In 2421 the digit 4 is also0100, but the inverse1011is the 2421 pattern for 5, since \(2+2+1 = 5\), and \(9-4 = 5\). The test succeeds, and it succeeds on all ten digits, which is why 2421 can form a 9's complement with a row of inverters.4. A system transmits eight data bits. How many Hamming check bits are needed, where do they sit, and which positions does the check bit at position 4 cover?
The requirement is \(2^k \ge m+k+1\) with \(m=8\). With \(k=3\), \(8 \ge 12\) is false; with \(k=4\), \(16 \ge 13\) is true, so four check bits are needed and the code word is twelve bits long. They sit at positions 1, 2, 4 and 8 — the powers of two — so that each owns one bit of the position number. The check bit at position 4 covers every position whose number has a 1 in the fours place: 4, 5, 6, 7 and 12.
5. A 12-bit Hamming word with even parity arrives as
011010010010. Find and correct the error, then state the eight data bits.Number the received bits 1 to 12: 0, 1, 1, 0, 1, 0, 0, 1, 0, 0, 1, 0. Check \(C_1\) over positions 1, 3, 5, 7, 9, 11 sees 0, 1, 1, 0, 0, 1 — three 1s, odd, so it fails and \(C_1 = 1\). \(C_2\) over 2, 3, 6, 7, 10, 11 sees 1, 1, 0, 0, 0, 1 — three 1s, so \(C_2 = 1\). \(C_4\) over 4, 5, 6, 7, 12 sees 0, 1, 0, 0, 0 — one 1, so \(C_4 = 1\). \(C_8\) over 8, 9, 10, 11, 12 sees 1, 0, 0, 1, 0 — two 1s, even, so \(C_8 = 0\). The syndrome \(C_8C_4C_2C_1\) is
0111= 7, so bit 7 is wrong. Inverting it gives011010110010, and stripping the check bits at positions 1, 2, 4 and 8 leaves the data11010010.