Parity Check
Applications
Read aloud is unavailable in this browser.
Parity in Practice: Where Simplicity Shines
Despite its limitations, parity checking has found—and continues to find—practical applications across a surprising range of systems. From the earliest days of computing through modern memory systems, parity's combination of minimal overhead, trivial implementation, and guaranteed single-bit detection makes it the right choice in specific contexts.
In this final page of the module, we'll explore where parity is used in real systems, understand the engineering decisions that favor parity over more complex methods, and gain practical insight into implementing parity correctly in various environments.
By the end of this page, you will understand parity's role in serial communication, memory systems, storage interfaces, and embedded applications. You'll see how historical systems used parity and how modern systems leverage its concepts, enabling you to make informed choices in your own designs.
Serial Communication: RS-232, UART, and Beyond
Parity's most enduring application is in serial communication, where it has been a standard option since the earliest days of electronic data transmission.
UART (Universal Asynchronous Receiver-Transmitter):
The UART is the fundamental hardware component for serial communication. Almost every UART supports parity as a configuration option:
Frame Structure:
┌──────┬───────────────────┬────────┬──────────┬──────────┐
│Start │ Data Bits │ Parity │ Stop Bit │ Stop Bit │
│ Bit │ 5, 6, 7, or 8 │ (opt) │ (1) │ (opt) │
└──────┴───────────────────┴────────┴──────────┴──────────┘
1 5-8 bits 0-1 1 0-1
└─────────────────── Frame ───────────────────┘
Parity Options:
- None: No parity bit transmitted (most common today)
- Even: Parity bit makes total 1s even
- Odd: Parity bit makes total 1s odd
- Mark: Parity bit is always 1
- Space: Parity bit is always 0
Mark and Space parity are legacy options that waste a bit but maintain timing compatibility.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134
"""UART Parity implementation example. This demonstrates how parity would be computed and verifiedin a software UART implementation.""" from enum import Enumfrom dataclasses import dataclassfrom typing import Tuple class ParityMode(Enum): NONE = "none" EVEN = "even" ODD = "odd" MARK = "mark" # Always 1 SPACE = "space" # Always 0 @dataclassclass UARTConfig: data_bits: int = 8 # 5, 6, 7, or 8 parity: ParityMode = ParityMode.NONE stop_bits: int = 1 # 1 or 2 class UARTFrame: """Represents a UART frame with parity handling.""" def __init__(self, config: UARTConfig): self.config = config def compute_parity_bit(self, data: int) -> int: """Compute the parity bit for given data.""" if self.config.parity == ParityMode.NONE: return None elif self.config.parity == ParityMode.MARK: return 1 elif self.config.parity == ParityMode.SPACE: return 0 else: # Count 1s in data count = bin(data).count('1') if self.config.parity == ParityMode.EVEN: return count % 2 # 1 if odd count, 0 if even else: # ODD return (count + 1) % 2 # 0 if odd count, 1 if even def encode_frame(self, data: int) -> list[int]: """ Encode data into a bit stream for transmission. Returns list of bit values (0 or 1). """ bits = [] # Start bit (always 0) bits.append(0) # Data bits (LSB first for standard UART) for i in range(self.config.data_bits): bits.append((data >> i) & 1) # Parity bit (if configured) parity_bit = self.compute_parity_bit(data) if parity_bit is not None: bits.append(parity_bit) # Stop bit(s) (always 1) for _ in range(self.config.stop_bits): bits.append(1) return bits def verify_parity(self, data: int, received_parity: int) -> bool: """Verify that received parity matches expected.""" expected = self.compute_parity_bit(data) if expected is None: return True # No parity checking return expected == received_parity def demo_uart_parity(): """Demonstrate UART parity operation.""" print("UART Parity Demonstration") print("=" * 60) # Configure UART: 8 data bits, even parity, 1 stop bit config = UARTConfig(data_bits=8, parity=ParityMode.EVEN, stop_bits=1) uart = UARTFrame(config) # Example characters test_chars = [ (ord('A'), 'A'), # 0x41 = 01000001, 2 ones (even) (ord('B'), 'B'), # 0x42 = 01000010, 2 ones (even) (ord('C'), 'C'), # 0x43 = 01000011, 3 ones (odd) (ord('a'), 'a'), # 0x61 = 01100001, 3 ones (odd) ] print(f"Config: {config.data_bits} data bits, {config.parity.value} parity, {config.stop_bits} stop") print() print(f"{'Char':<6} | {'Binary':>12} | {'1s':>3} | {'Parity':>6} | Frame Bits") print("-" * 60) for data, char in test_chars: parity_bit = uart.compute_parity_bit(data) frame = uart.encode_frame(data) count_ones = bin(data).count('1') frame_str = ''.join(str(b) for b in frame) print(f"'{char}' ({data:3d}) | {bin(data):>12} | {count_ones:>3} | {parity_bit:>6} | {frame_str}") # Simulate error detection print("" + "=" * 60) print("Error Detection Simulation:") print() data = ord('A') # 01000001 correct_parity = uart.compute_parity_bit(data) print(f"Transmitted 'A' (0x{data:02X}) with even parity bit = {correct_parity}") # Simulate single-bit error in data corrupted_data = data ^ 0x04 # Flip bit 2: 01000101 print(f"Received corrupted: 0x{corrupted_data:02X}") print(f"Parity check: {'PASS' if uart.verify_parity(corrupted_data, correct_parity) else 'FAIL - Error detected!'}") if __name__ == "__main__": demo_uart_parity()| Application | Data Bits | Parity | Stop Bits | Notation |
|---|---|---|---|---|
| Modern general purpose | 8 | None | 1 | 8N1 |
| Legacy terminal | 7 | Even | 1 | 7E1 |
| Modem (historical) | 7 | Odd | 1 | 7O1 |
| Industrial/embedded | 8 | Even | 1 | 8E1 |
| High reliability | 8 | Even | 2 | 8E2 |
Serial communication transmits one character at a time at relatively low speeds. Single-bit errors are the dominant failure mode. The 1-bit overhead is acceptable (12.5% for 8N1 → 8E1), and hardware support is universal. For short cable runs and controlled environments, parity provides adequate protection.
Memory Systems: From Parity to ECC
Computer memory has used parity since the earliest electronic computers, though ECC (Error Correcting Code) has largely superseded simple parity in critical systems.
Historical Parity Memory:
In the PC era (1980s-1990s), memory modules included parity:
30-pin SIMM: 9 bits per byte (8 data + 1 parity) 72-pin SIMM: 36 bits for 32 data bits (32 + 4 parity) 168-pin SDRAM DIMM: 72 bits for 64 data bits (optional parity)
How Parity Memory Worked:
CPU Write Operation:
───────────────────
1. CPU sends 8-bit data to memory controller
2. Controller computes parity bit
3. 9 bits (8 data + 1 parity) stored in RAM
CPU Read Operation:
───────────────────
1. 9 bits read from RAM
2. Controller recomputes parity from 8 data bits
3. Computed parity compared to stored parity
4. If mismatch: Non-Maskable Interrupt (NMI) → system halt
5. If match: data returned to CPU
When parity failed:
The system would halt with the infamous message:
PARITY CHECK 1 or PARITY CHECK 2
xxxxxxxx
This was intentional—corrupted data in RAM could cause anything from calculation errors to uncontrolled machine behavior. Halting was the safe response.
Transition to ECC Memory:
Parity memory's limitation is that it can only detect, not correct. A parity error meant:
- System halts (data loss, downtime)
- User must power cycle the machine
- Work in progress is lost
ECC memory (using Hamming codes):
- Uses 8 check bits per 64 data bits (vs. 8 parity bits)
- Can correct any single-bit error silently
- Can detect (not correct) 2-bit errors
- System runs uninterrupted for single-bit errors
Modern Memory Architecture:
| Segment | Technology | Error Handling |
|---|---|---|
| Consumer desktop | Non-ECC | No protection |
| Consumer laptop | Non-ECC | No protection |
| Workstation | ECC | SEC-DED (Single Error Correct, Double Detect) |
| Server | ECC with scrubbing | SEC-DED + background checking |
| Data center | Chipkill/SDDC | Survives entire DRAM chip failure |
Memory errors from cosmic ray impacts (soft errors) occur at a rate of roughly 1 error per GB per month. For a server with 1TB of RAM, that's ~1000 single-bit errors per month. Without ECC, these cause silent data corruption. With parity, the system would halt 1000 times per month. ECC quietly corrects them all.
Storage Interfaces and Bus Systems
Parity has protected data on computer buses and storage interfaces throughout computing history.
PCI Bus (Conventional PCI):
The original PCI bus (1992) used parity for data integrity:
PCI Address/Data Phase:
AD[31:0] 32 address or data bits
C/BE#[3:0] 4 command/byte enable bits
PAR 1 parity bit (even parity over AD + C/BE#)
────────
37 bits total
Parity equation:
PAR = AD[0] ⊕ AD[1] ⊕ ... ⊕ AD[31] ⊕ C/BE#[0] ⊕ ... ⊕ C/BE#[3]
Parity Error Handling in PCI:
- PERR# (Parity Error) signal asserted on data phase errors
- SERR# (System Error) for address phase or severe errors
- Bus master receives error signal
- System may halt or retry transaction
SCSI (Small Computer System Interface):
Parallel SCSI used parity on its data bus:
- 8-bit SCSI: 8 data + 1 parity (DB0-DB7 + DBP)
- 16-bit Wide SCSI: 16 data + 2 parity (upper and lower bytes)
- Parity errors trigger retry or error reporting
| Interface | Era | Parity Configuration | Error Response |
|---|---|---|---|
| ISA Bus | 1981-2000 | Optional on some cards | System dependent |
| PCI | 1992-2010 | Mandatory, 37-bit even | PERR#/SERR# signals |
| PCI-X | 1998-2010 | Mandatory, enhanced | Error reporting |
| PCIe | 2004-present | CRC replaced parity | Link-level retry |
| SCSI-2 | 1990-2005 | Per-byte parity | Command retry |
| SAS | 2004-present | CRC + scrambling | Advanced ECC |
Modern high-speed interfaces like PCIe and SAS abandoned parity in favor of CRC. Higher speeds meant more errors, and serial links (with serializer/deserializer chips) naturally accommodate CRC calculation. The shift represents the industry's recognition of parity's limitations for high-reliability requirements.
RAID Storage Systems
RAID (Redundant Array of Independent Disks) systems use parity concepts at a higher level—protecting against entire disk failures rather than individual bit errors.
RAID Levels Using Parity:
RAID 3: Byte-level striping with dedicated parity disk
Disk 0: |B0_0|B1_0|B2_0|... (byte 0 of each stripe)
Disk 1: |B0_1|B1_1|B2_1|... (byte 1 of each stripe)
Disk 2: |B0_2|B1_2|B2_2|... (byte 2 of each stripe)
Disk 3: |P0 |P1 |P2 |... (parity bytes)
Pn = B(n,0) ⊕ B(n,1) ⊕ B(n,2)
RAID 4: Block-level striping with dedicated parity Same concept but with larger blocks (typically 64KB-512KB).
RAID 5: Block-level striping with distributed parity Parity rotates among all disks:
Disk 0: |D0 |D3 |D6 |P9 |...
Disk 1: |D1 |D4 |P7 |D10|...
Disk 2: |D2 |P5 |D8 |D11|...
Disk 3: |P3 |D6 |D9 |D12|...
Recovery Process:
When a disk fails, any missing block can be reconstructed:
Missing = Known_0 ⊕ Known_1 ⊕ ... ⊕ Known_n ⊕ Parity
Since: Parity = D0 ⊕ D1 ⊕ ... ⊕ Dn
Then: Dn = D0 ⊕ D1 ⊕ ... ⊕ D(n-1) ⊕ Parity (when Dn fails)
| RAID Level | Parity Disks | Data Disks | Fault Tolerance | Write Penalty |
|---|---|---|---|---|
| RAID 0 | 0 | N | None | 1x |
| RAID 1 | N/A (mirror) | N (×2) | 1 disk | 2x |
| RAID 3 | 1 (dedicated) | N-1 | 1 disk | Write all |
| RAID 4 | 1 (dedicated) | N-1 | 1 disk | 4 I/O per write |
| RAID 5 | 1 (distributed) | N-1 | 1 disk | 4 I/O per write |
| RAID 6 | 2 (distributed) | N-2 | 2 disks | 6 I/O per write |
RAID parity uses the same XOR operation as bit-level parity. The difference is scale: instead of XORing bits, we XOR entire disk blocks (4KB-256KB). The mathematics is identical—XOR is associative, commutative, and self-inverse at any scale.
Embedded Systems and IoT Applications
Resource-constrained embedded systems sometimes favor parity over more complex error detection due to its minimal overhead.
Why Parity in Embedded:
-
Minimal Code Size
- XOR parity: ~10 instructions
- CRC-16: ~100-200 instructions (table-driven) or ~500+ (bit-by-bit)
- For microcontrollers with 2KB-8KB flash, this matters
-
Minimal RAM
- Parity: 1 variable
- CRC table: 256-512 bytes
- Some MCUs have 128-512 bytes total RAM
-
Minimal Latency
- Parity: O(n) with low constant
- CRC: O(n) with higher constant
- For real-time systems, every cycle counts
Common Embedded Uses:
- UART communication: Hardware parity in MCU peripherals
- I²C bus: Some implementations add parity byte (PEC)
- Sensor data: Quick validity check before processing
- Flash storage: Parity per page in some bootloaders
- Inter-processor communication: Simple mailbox integrity
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114
/** * Minimal parity implementation for embedded systems. * Designed for 8-bit microcontrollers with limited resources. */ #include <stdint.h>#include <stdbool.h> /** * Compute even parity for a byte. * Returns 1 if odd number of 1s (parity bit needed to make even). * * Code size: ~10-15 instructions on AVR/PIC * Execution: 8 cycles on ARM Cortex-M0 */uint8_t compute_parity_byte(uint8_t data) { data ^= data >> 4; data ^= data >> 2; data ^= data >> 1; return data & 1;} /** * Compute parity for a buffer of bytes. * XORs all bytes together, then computes single-byte parity. * * This is a "longitudinal parity" or LRC (Longitudinal Redundancy Check). */uint8_t compute_parity_buffer(const uint8_t *data, uint16_t len) { uint8_t lrc = 0; while (len--) { lrc ^= *data++; } return lrc;} /** * Verify buffer with appended LRC byte. * Returns true if parity is correct (LRC of all bytes including LRC = 0). */bool verify_buffer_with_lrc(const uint8_t *data, uint16_t len) { uint8_t lrc = 0; while (len--) { lrc ^= *data++; } return lrc == 0;} /** * UART transmit with parity example. * Demonstrates hardware parity configuration. */typedef struct { volatile uint8_t *data_reg; volatile uint8_t *status_reg; volatile uint8_t *control_reg;} UART_TypeDef; // Parity error detection from hardware UART status register#define UART_STATUS_PE_MASK 0x04 // Parity Error bit bool uart_receive_with_parity_check(UART_TypeDef *uart, uint8_t *data) { // Wait for data (simplified - production code uses interrupts) while (!(*uart->status_reg & 0x01)) { // Wait for RX complete } // Check for parity error if (*uart->status_reg & UART_STATUS_PE_MASK) { // Parity error detected - data unreliable *data = *uart->data_reg; // Still read to clear buffer return false; // Indicate error } *data = *uart->data_reg; return true; // Data valid} /* * Example application: Simple sensor reading with parity */typedef struct { uint8_t sensor_id; int16_t temperature; // 2 bytes int16_t humidity; // 2 bytes uint8_t parity; // LRC of above} SensorPacket; // Total: 6 bytes bool validate_sensor_packet(const SensorPacket *pkt) { const uint8_t *bytes = (const uint8_t *)pkt; uint8_t lrc = 0; // LRC of all bytes including parity byte should equal 0 for (uint8_t i = 0; i < sizeof(SensorPacket); i++) { lrc ^= bytes[i]; } return lrc == 0;} void prepare_sensor_packet(SensorPacket *pkt, uint8_t id, int16_t temp, int16_t humidity) { pkt->sensor_id = id; pkt->temperature = temp; pkt->humidity = humidity; // Calculate LRC (XOR of all data bytes) const uint8_t *bytes = (const uint8_t *)pkt; uint8_t lrc = 0; for (uint8_t i = 0; i < sizeof(SensorPacket) - 1; i++) { lrc ^= bytes[i]; } pkt->parity = lrc;}Longitudinal Redundancy Check (LRC) XORs all bytes in a message to produce a single check byte. It's essentially parity applied at the byte level across a message, combining simplicity with slightly better protection than per-byte parity.
ASCII and Character Set Design
The design of ASCII itself reflects the importance of parity in early computing.
ASCII: 7 Data Bits + 1 Parity Bit = 8 Bits
ASCII was standardized in 1963 with exactly 7 bits for character encoding:
- 128 characters (2⁷)
- Control characters: 0x00-0x1F
- Printable characters: 0x20-0x7E
- Delete: 0x7F
Why 7 bits?
The eighth bit was explicitly reserved for parity! Early teleprinters, teletypes, and computer terminals transmitted 8 bits per character:
- 7 bits of ASCII data
- 1 bit of parity (usually even)
The Eighth Bit's Evolution:
| Era | Use of Bit 8 |
|---|---|
| 1960s-1970s | Parity for error detection |
| 1970s-1980s | Extended ASCII (IBM PC) |
| 1980s-1990s | ISO 8859 character sets |
| 1990s-present | UTF-8 multi-byte sequences |
Legacy Impact:
Many protocols still treat the high bit specially:
- Early email systems stripped bit 8 (7-bit clean requirement)
- Base64 encoding exists partly to survive 7-bit channels
- Some terminals interpret bit 8 as meta/alt key modifier
ASCII with Parity in Historical Systems
How the ASR-33 Teletype used parity
- 1
Step 1
Character 'A': ASCII 0x41 = 1000001 (binary, 7 bits)
- 2
Step 2
Bit count: 2 ones (even)
- 3
Step 3
Even parity bit: 0
- 4
Step 4
Transmitted: 01000001 (8 bits, MSB is parity)
- 5
Step 5
Character 'C': ASCII 0x43 = 1000011 (binary)
- 6
Step 6
Bit count: 3 ones (odd)
- 7
Step 7
Even parity bit: 1
- 8
Step 8
Transmitted: 11000011 (8 bits, MSB is parity)
Paper tape systems often used mark parity (always 1) because it ensured at least one hole per row—the hardware could distinguish 'no data' from 'all zeros'. This practical consideration influenced parity mode choices beyond pure error detection.
Educational and Conceptual Significance
Beyond practical applications, parity serves as a foundational concept in computer science education.
Gateway to Error Detection:
Parity introduces fundamental concepts that underlie all error detection:
- Redundancy — Adding bits that don't carry new information but enable verification
- Invariant Properties — Maintaining a mathematical relationship that errors violate
- Detection vs. Correction — Understanding the information requirements of each
- Hamming Distance — Parity creates distance-2 codes, introducing the concept
- Trade-offs — Overhead vs. protection strength
Conceptual Bridge:
Parity (d=2) → Hamming (d=3) → BCH/Reed-Solomon (d≥3)
↓ ↓ ↓
Detect 1 Correct 1 Correct multiple
Detect 2 Burst correction
Understanding parity makes Hamming intuitive:
- Hamming uses multiple parity checks
- Each check covers different bit subsets
- Failed checks identify error position
Interview and Exam Relevance:
Parity problems appear in:
- Computer architecture courses
- Digital logic design
- Networking fundamentals
- Coding theory introductions
- Reliability engineering
- •XOR properties — Associativity, commutativity, self-inverse (a ⊕ a = 0)
- •Modular arithmetic — Parity is counting mod 2
- •Code rate — Ratio of data bits to total bits (n/(n+1) for parity)
- •Detection probability — Analyzing what fraction of errors are caught
- •Hamming distance — Minimum flips between valid codewords
- •Syndrome — The parity check result indicates error presence
KEEP LEARNING
There’s more to understand.
You’re reading the introduction. Create a free account to read the complete lesson, save notes and keep track of your progress.