UART Framing Protocol Library: Wire Protocol Specification

This document specifies the binary wire format, byte offsets, checksum algorithms, and transport state machines for the Fuchsia UART Framing Protocol Library (uart_fpl).

For the Rust crate software API reference, method contracts, and integration examples, see API.md. For a high-level overview of the library and channel multiplexing topology, see README.md.


1. Frame Layout & Wire Format

Every packet transmitted over the serial link follows a structured binary layout with sync words, session tracking, logical channel multiplexing, sequence tracking, length-prefixed payload, an 8-bit header checksum, and a 32-bit CRC checksum:

+-----------+------------+------------+-----+------------+-------------+---------+----------------+---------------+
| Sync Word | Session ID | Channel ID | Seq | Frame Type | Payload Len | Hdr Chk | Payload (data) | CRC32 Checksum|
|   (2B)    |  (4B BE)   |  (2B BE)   | (1B)|    (1B)    |   (2B BE)   |  (1B)   |   (N Bytes)    |    (4B BE)    |
+-----------+------------+------------+-----+------------+-------------+---------+----------------+---------------+
|  Offset 0 |  Offset 2  |  Offset 6  |Off 8|  Offset 9  |  Offset 10  |  Off 12 |   Offset 13    |  Offset 13+N  |
+-----------+------------+------------+-----+------------+-------------+---------+----------------+---------------+

Field Definitions

FieldOffsetSizeTypeDescription
Sync Word02 bytes[u8; 2]Canonical preamble (0xAA 0x55) used for frame boundary delineation and continuous stream resynchronization.
Session ID24 bytesu32 (BE)Unique random identifier generated per connection session. Prevents sequence collisions from stale in-flight FIFO buffers across host or target restarts.
Channel ID62 bytesu16 (BE)Logical channel identifier for multiplexing multiple independent streams over a single serial link (Channel 0 = Control/Handshake, Channels 1..=65535 = Data).
Sequence Number81 byteu8Packet sequence number (0 to 255) for Go-Back-N sliding-window flow control in ResendSP. Wraps modulo 256.
Frame Type91 byteu8Semantic frame indicator (0x01 = DATA, 0x02 = ACK, 0x03 = CLOSE, 0x04 = RESET, 0x10 = NEGOTIATE_REQ, 0x11 = NEGOTIATE_RESP).
Payload Length102 bytesu16 (BE)Length $N$ of the subsequent payload in bytes (Big-Endian, maximum 1024 bytes).
Header Checksum121 byteu88-bit CRC checksum (HdrChk, polynomial 0x07) calculated over the 10 header bytes (offsets 2 through 11).
Payload13$N$ bytes[u8]Raw frame payload data ($0 \le N \le 1024$).
CRC32 Checksum13+$N$4 bytesu32 (BE)IEEE 802.3 CRC-32 checksum (Big-Endian) calculated over header fields, header checksum, and payload (offsets 2 through 12+$N$).

2. Checksum Algorithms

2.1 1-Byte Header Checksum (HdrChk)

Calculated over offsets 2 through 11 (Session ID, Channel ID, Seq, Frame Type, Payload Len) using polynomial 0x07 (x^8 + x^2 + x + 1), matching Pigweed pw_ulink:

fn crc8(data: &[u8]) -> u8 {
    let mut crc: u8 = 0x00;
    for &byte in data {
        crc ^= byte;
        for _ in 0..8 {
            if (crc & 0x80) != 0 {
                crc = (crc << 1) ^ 0x07;
            } else {
                crc <<= 1;
            }
        }
    }
    crc
}
  • Purpose: Guarantees that corrupted Payload Length headers are rejected immediately before the parser attempts to allocate memory or buffer trailing payload bytes.

2.2 32-Bit Frame Checksum (IEEE 802.3 CRC-32)

Calculated over all bytes from offset 2 up to the end of the payload (offsets 2 through 12+$N$):

$$\text{CRC-32} = \text{CRC32}(\text{Header}[2..12] \parallel \text{HdrChk} \parallel \text{Payload})$$

  • Polynomial: Standard IEEE 802.3 polynomial (0xEDB88320 reversed / 0x04C11DB7 normal).
  • Wire Representation: 4 bytes, Big-Endian.

3. Dynamic Protocol Negotiation (Channel 0)

Before client data exchange begins, the host and target negotiate the framing protocol across Channel ID = 0.

3.1 Negotiation Request (NEGOTIATE_REQ = 0x10)

The host transmits a prioritized list of proposed protocol IDs:

+------------+------------------+------------------+-----+
| Count (1B) | Protocol ID 0    | Protocol ID 1    | ... |
|            | (4B BE)          | (4B BE)          |     |
+------------+------------------+------------------+-----+
  • Count: Number of proposed protocols ($1 \le \text{Count} \le 255$).
  • Protocol ID: 32-bit big-endian integer. Currently defined protocols:
    • 1 = ResendSP (Sliding-window Go-Back-N ARQ with CRC-32).
    • Future proposals are preserved forward-compatibly as ProtocolId::Unknown(id).

3.2 Negotiation Response (NEGOTIATE_RESP = 0x11)

The target evaluates the host's proposals against its supported protocols, selects the highest-priority mutual match, and responds:

+-------------+-----------------------+
| Status (1B) | Selected Protocol ID  |
|             | (4B BE)               |
+-------------+-----------------------+
  • Status Codes:
    • 0x00 = Success: Mutually supported protocol agreed upon.
    • 0x01 = NoCommonProtocol: Target supports none of the proposed protocols.
    • 0x02 = MalformedRequest: Request payload was truncated or malformed.
  • Selected Protocol ID: 32-bit big-endian integer of the agreed protocol (or 0 on failure).

3.3 Idempotent Handshake Retransmission Contract

Over lossy or noisy UART channels, the target's NEGOTIATE_RESP may be corrupted or dropped.

  • The host will retransmit its NEGOTIATE_REQ carrying the identical session_id.
  • Contract: The target must treat repeated NEGOTIATE_REQ frames bearing the active session_id as idempotent retransmissions. It re-emits its cached NEGOTIATE_RESP without tearing down active channels or resetting sequence state.
  • If a NEGOTIATE_REQ arrives with a new session_id, it indicates a host restart, prompting a complete state and channel reset.

4. Sliding-Window Flow Control (ResendSP)

ResendSP (ProtocolId = 1) implements Go-Back-N sliding-window flow control:

4.1 Sequence Space & Window Bounds

  • Sequence numbers are 8-bit unsigned integers (0..=255), wrapping modulo 256.
  • Default window size $W = 64$ frames (maximum permissible window $W \le 128$).
  • In-flight capacity invariant: $$\text{in_flight} = (\text{next_seq} - \text{base}) \pmod{256} < W$$

4.2 Cumulative Acknowledgments (TYPE_ACK = 0x02)

  • ACKs are cumulative: receiving an ACK for sequence $K$ acknowledges all outstanding frames from base through $K$ inclusive.
  • A receiver only emits ACKs for in-order sequence delivery. Out-of-order or duplicate frames prompt re-emission of the current cumulative ACK.
  • ACKs are coalesced in memory via AckTracker: if frames 0 through 7 arrive contiguously, only a single cumulative ACK for frame 7 is transmitted across the wire.

4.3 Timeout & Retransmission

  • When the retransmission timer fires (default 1,000 ms) and unacknowledged frames remain in flight, the sender retransmits all frames in the active window $[\text{base}, \text{next_seq})$.
  • Retransmission limit: default 60 consecutive timeout attempts before declaring transport failure.
  • Karn's Algorithm: Retransmitted frames are flagged. ACKs matching retransmitted frames do not generate round-trip time (RTT) samples, preventing RTT estimator pollution.

5. Protocol Design Decisions & Rationale

5.1 Why 1-Byte Header Checksum (HdrChk) Prevents Corrupted Length Stalls

A trailing CRC-32 can only be verified after reading the entire declared frame length. If line noise flips bits in the Payload Length field (e.g. corrupting 16 bytes to 1024 bytes), a receiver without a header checksum must wait for 1024 bytes of incoming data before detecting CRC failure. This wedges the parser and drops subsequent valid frames. HdrChk verifies the header integrity at offset 12, allowing immediate rejection and $O(N)$ sync recovery.

5.2 Why 32-Bit Session IDs Prevent Stale FIFO Collisions Across Reboots

UART FIFOs, USB-to-serial adapters (e.g. FTDI FT4232H), and kernel TTY buffers frequently hold unread bytes across target reboots or host tool restarts. Because sequence numbers always begin at 0, stale in-flight frames or delayed ACKs from a prior session could be accepted as valid data in the new session. Random 32-bit session IDs ensure instant rejection of stale frames.

5.3 Why Channel 0 is Reserved as the Dedicated Control Plane

  • Control vs Data Separation: User client connections are allocated dynamic channels ($1 \le \text{Channel ID} \le 65535$). Channel 0 permanently serves as the control plane.
  • In-Band Reboot Detection: When a target reboots, it emits a NEGOTIATE_REQ (with a fresh session ID) directly into Channel 0 without corrupting active client framing.
  • Queue Independence: Control signaling operates outside client Go-Back-N data window queues, preventing handshakes and resets from blocking behind saturated data streams.

5.4 Why Go-Back-N Was Selected Over Stop-and-Wait and Selective Repeat

  • Stop-and-Wait: Incurs a full round-trip delay per packet. On USB-serial bridges or remote tunnels with 20-50 ms RTT, throughput is capped at 10-25 KB/s.
  • Selective Repeat: Requires complex out-of-order reassembly queues, dynamic heap allocations, and SACK bitmaps, which risk OOM crashes on embedded targets.
  • Go-Back-N: Fully saturates 1 MBaud serial bandwidth (~98 KB/s) with static buffers and zero receiver-side reassembly queueing.