Skip to content
VLSI Mentor

UART · Module 6

Complete RX RTL Architecture

One synthesizable receiver assembled from the module's five preceding chapters — assumptions stated first, walked block by block with the invariant each maintains, then reviewed the way a reviewer would, including the defects found during its own development.

Five chapters have specified a receiver completely. Chapter 6.1 partitioned it and settled the timing origin; Chapter 6.2 enumerated eight transitions; Chapter 6.3 derived the counters and stated their semantics; Chapter 6.4 added validation and the publish discipline; Chapter 6.5 chose the output interface and the overrun policy.

This chapter assembles them into one module and then does the thing that separates working RTL from reviewable RTL: it challenges its own design.

That is not a rhetorical device. Two genuine defects were found while developing this receiver — one by reasoning about a path no test covered, one by a numeric check contradicting a published claim — and both are described where they occurred rather than quietly corrected. A design review chapter that presents only successes teaches the wrong thing.

1. What Was Actually Verified

Stated first, because the rest of the chapter depends on knowing it — and because "the RTL is correct" means nothing without saying what checked it.

HDL compiler availableIcarus Verilog 13.0 (stable)
Commandiverilog -g2012 -o sim uart_rx.sv tb_uart_rx.sv
Elaborationclean — no errors, no warnings except that unique qualities are ignored
Simulation73 checks, 0 failures
Every figure, table and trace in Module 6extracted from this simulation, not computed by hand

What that does and does not prove. It proves the design elaborates as SystemVerilog-2012, that its behaviour matches an independent reference model across the cases in §8's matrix, and that every published trace is the design's real behaviour.

It does not prove synthesisability. Icarus is a simulator; it does not run inference rules, it does not report latches, and it does not know whether a construct maps to hardware. The RTL is written to the constraints Chapter 6.2 and Chapter 6.3 set out — single clock, enables not generated clocks, every branch assigning, no combinational loops — and that is a reviewed claim, not a tool-verified one. A synthesis run is the missing evidence, and Module 12 is where it belongs.

2. Assumptions

Before the code, what this receiver is — and is not.

Clockone fabric clock, clk. No generated clock anywhere.
Resetrst_n, asynchronous assert, active low. Synchronous-release discipline is Module 12's.
RX synchronisationtwo stages, reset to the idle level. rx_i fans out to nothing else.
Timing sourceos_tick_i, a one-cycle enable, OVERSAMPLE per bit interval. Module 8 generates it.
Oversampling factorOVERSAMPLE parameter, default 16, any value ≥ 2.
Data widthDATA_W parameter, default 8. Elaboration-time.
Bit orderLSB first, per Chapter 3.2.
Parityruntime input: none / even / odd / mark.
Stop bitsone, validated at its centre sample.
Samplingsingle sample at the interval centre.
Output interfaceheld valid with rx_ready_i accept.
Error policycorrupt frames are delivered with their flags.
Overrun policypreserve the held byte, drop the new frame, raise rx_overrun_o.
Bufferingone holding register. No FIFO — Module 10.
Break handlingdeferred — Module 9.
Idle detectiondeferred — Module 9. See §7's mis-framing case.
Sticky statusdeferred — Module 9. Flags here are strictly per-frame.
Two stop bitsnot supported. Only the first stop interval is validated.
Majority votingnot implemented. Chapter 5.4 showed it is a trade, not an upgrade.

The last four matter. Unsupported behaviour must look deliberate, and a receiver whose assumptions block is silent about two stop bits is one where a reviewer cannot tell whether the omission was a decision or an oversight.

3. The Assembled Receiver

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// ============================================================================
//  Synthesizable SystemVerilog — complete UART receiver.
//
//  Compiled and simulated with Icarus Verilog 13.0 (-g2012); 73 checks, 0
//  failures. Every trace published in Module 6 was extracted from this code.
//
//  Assumptions are stated in §2 and are not repeated here. Chapter references
//  point at the derivation of each decision rather than restating it.
// ============================================================================
package uart_parity_pkg;
    typedef enum logic [1:0] {
        PARITY_NONE  = 2'b00,
        PARITY_EVEN  = 2'b01,
        PARITY_ODD   = 2'b10,
        PARITY_MARK  = 2'b11        // constant 1 — no detection, Chapter 3.3
    } parity_mode_t;
endpackage

module uart_rx
    import uart_parity_pkg::*;
#(
    parameter int unsigned DATA_W     = 8,
    parameter int unsigned OVERSAMPLE = 16
) (
    input  logic              clk,
    input  logic              rst_n,
    input  logic              rx_i,            // the only asynchronous input
    input  logic              os_tick_i,       // enable, M per bit — Module 8
    input  parity_mode_t      parity_mode_i,

    output logic [DATA_W-1:0] rx_data_o,
    output logic              rx_valid_o,
    input  logic              rx_ready_i,
    output logic              rx_parity_err_o,
    output logic              rx_frame_err_o,
    output logic              rx_overrun_o
);
    // ---- derived constants — Chapter 6.3 §2, §5 ----------------------------
    localparam int unsigned CENTER = OVERSAMPLE / 2;
    localparam int unsigned PH_W   = (OVERSAMPLE <= 1) ? 1 : $clog2(OVERSAMPLE);
    localparam int unsigned BI_W   = (DATA_W     <= 1) ? 1 : $clog2(DATA_W);

    // ---- parameter legality — §6 of this chapter ---------------------------
    initial begin
        if (OVERSAMPLE < 2)
            $fatal(1, "uart_rx: OVERSAMPLE = %0d has no centre to sample", OVERSAMPLE);
        if (DATA_W < 1)
            $fatal(1, "uart_rx: DATA_W = %0d is not a frame", DATA_W);
    end

    typedef enum logic [2:0] {
        S_IDLE   = 3'd0,
        S_START  = 3'd1,
        S_DATA   = 3'd2,
        S_PARITY = 3'd3,
        S_STOP   = 3'd4
    } rx_state_e;

    rx_state_e         state_q;
    logic              rx_meta_q, rx_sync_q, rx_sync_d_q;
    logic [PH_W-1:0]   phase_q;
    logic [BI_W-1:0]   bit_idx_q;
    logic [DATA_W-1:0] shreg_q;
    logic              par_acc_q;
    logic              par_err_q;
    logic              start_cand;
    logic              sample_now;

    // ---- input boundary — Chapter 5.1 §3 -----------------------------------
    // Reset to the IDLE level, never to zero: a receiver leaving reset must
    // not present a fabricated departure from idle to the edge detector.
    always_ff @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin
            rx_meta_q   <= 1'b1;
            rx_sync_q   <= 1'b1;
            rx_sync_d_q <= 1'b1;
        end else begin
            rx_meta_q   <= rx_i;
            rx_sync_q   <= rx_meta_q;
            rx_sync_d_q <= rx_sync_q;
        end
    end

    // ---- the two named events — Chapters 5.2 §1 and 6.3 §3 -----------------
    assign start_cand = rx_sync_d_q && !rx_sync_q;
    assign sample_now = os_tick_i && (phase_q == PH_W'(CENTER - 1));

    // ---- the receiver's expectation — Chapter 6.4 §2 -----------------------
    function automatic logic expected_parity(input parity_mode_t m, input logic acc);
        case (m)
            PARITY_EVEN: expected_parity = acc;
            PARITY_ODD:  expected_parity = ~acc;
            default:     expected_parity = 1'b1;   // MARK
        endcase
    endfunction

    always_ff @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin
            state_q         <= S_IDLE;
            phase_q         <= '0;
            bit_idx_q       <= '0;
            shreg_q         <= '0;
            par_acc_q       <= 1'b0;
            par_err_q       <= 1'b0;
            rx_data_o       <= '0;
            rx_valid_o      <= 1'b0;
            rx_parity_err_o <= 1'b0;
            rx_frame_err_o  <= 1'b0;
            rx_overrun_o    <= 1'b0;
        end else begin
            // Accept releases the holding register and ALL of its status
            // together — Chapter 6.5 §3. Written first so that a delivery in
            // the same cycle overrides it, which is what lets a byte be
            // accepted and replaced on one edge.
            if (rx_valid_o && rx_ready_i) begin
                rx_valid_o      <= 1'b0;
                rx_parity_err_o <= 1'b0;
                rx_frame_err_o  <= 1'b0;
                rx_overrun_o    <= 1'b0;
            end

            if (state_q == S_IDLE) begin
                // The only state that does not wait for the timing event.
                if (start_cand) begin
                    state_q   <= S_START;
                    phase_q   <= '0;      // THE origin — Chapter 6.1 §4
                    bit_idx_q <= '0;
                    par_acc_q <= 1'b0;
                    par_err_q <= 1'b0;    // status is per-frame: clear at entry
                end
            end else if (os_tick_i) begin
                // Explicit wrap. Natural overflow is correct only when
                // OVERSAMPLE is a power of two — Chapter 6.3 §2.
                phase_q <= (phase_q == PH_W'(OVERSAMPLE - 1)) ? '0 : phase_q + 1'b1;

                if (sample_now) begin
                    case (state_q)
                        S_START: begin
                            // The sampled level here is the QUALIFICATION
                            // result, not payload — Chapter 6.2 §1.
                            if (rx_sync_q) state_q <= S_IDLE;     // false start
                            else           state_q <= S_DATA;
                        end
                        S_DATA: begin
                            // Right shift, MSB insert: LSB-first arrival order
                            // reconstructed without a write address — 6.3 §6.
                            shreg_q   <= {rx_sync_q, shreg_q[DATA_W-1:1]};
                            par_acc_q <= par_acc_q ^ rx_sync_q;
                            bit_idx_q <= bit_idx_q + 1'b1;
                            // Compared against DATA_W-1: bit_idx_q still holds
                            // the index of the bit arriving now, and the
                            // increment above is concurrent — Chapter 6.3 §4.
                            if (bit_idx_q == BI_W'(DATA_W - 1)) begin
                                // The ONLY place configuration steers control.
                                if (parity_mode_i == PARITY_NONE) state_q <= S_STOP;
                                else                              state_q <= S_PARITY;
                            end
                        end
                        S_PARITY: begin
                            // Held INTERNALLY. Writing rx_parity_err_o here
                            // would let this frame rewrite the status of a
                            // byte still held from the previous one — the
                            // defect described in Chapter 6.4 §4.
                            par_err_q <= (rx_sync_q != expected_parity(parity_mode_i, par_acc_q));
                            state_q   <= S_STOP;
                        end
                        S_STOP: begin
                            // Unconditional. Waiting for the line to return to
                            // mark wedges the receiver on a break — 6.2 §4.
                            state_q <= S_IDLE;
                            if (rx_valid_o && !rx_ready_i) begin
                                // Preserve the held frame, drop this one, and
                                // record the loss — Chapter 6.5 §4.
                                rx_overrun_o <= 1'b1;
                            end else begin
                                // THE publish point. Data and both status
                                // flags cross to the consumer on one edge.
                                rx_data_o       <= shreg_q;
                                rx_valid_o      <= 1'b1;
                                rx_parity_err_o <= par_err_q;
                                rx_frame_err_o  <= !rx_sync_q;
                            end
                        end
                        default: state_q <= S_IDLE;   // recovery — 6.2 §4
                    endcase
                end
            end
        end
    end
endmodule
The complete UART receiver as assembled. The asynchronous receive input enters a two-stage synchroniser that is reset to the idle level. Its output feeds an edge detector producing a one-cycle start candidate pulse, and also feeds the sampling logic directly. A single phase counter, loaded with zero at the start candidate, counts oversample ticks and produces one named sampling event at each bit interval centre. A five-state control machine consumes that event and decides which field of the frame is arriving. Under the machine's direction, a shift register assembles the payload least significant bit first, a bit counter tracks the arriving index, and a parity accumulator runs an exclusive-or over the stored bits. At the stop sample a single publish event transfers the assembled payload, the parity result and the framing result together into the holding register and status flags, which the consumer reads using a held valid and ready handshake.sync 2FFCh 5.1 — idle resetstart_candCh 5.2 — evidencephase_qCh 6.1 — one originstate_qCh 6.2 — 8 arcsshreg_qCh 6.3 — LSB firstpar_acc_qCh 6.4 — XORpublishCh 6.4 — one edgevalid / readyCh 6.5 — heldsyncph=0samplestoreaccstopbyte12
Figure 1 — the assembled receiver, with each block annotated by the chapter that derived it. Compare against Chapter 6.1 Figure 1: the partition is unchanged, which is the point. The module was specified before it was written, and the assembled code adds no block the plan did not contain and moves no boundary the plan did not draw.

4. The Walkthrough

Each part, and the invariant it maintains.

Parameters and interface. Invariant: the hardware's shape is fixed at elaboration and its behaviour is selected at runtime. DATA_W and OVERSAMPLE change register widths; parity_mode_i selects a path. Chapter 6.1 §5 gives the test that separates them.

Derived constants. Invariant: no position in this design is a literal. CENTER, PH_W and BI_W all follow the parameters, so changing OVERSAMPLE from 16 to 8 moves every sampling position correctly. Chapter 5.3 §6 showed what a hard-coded 7 costs.

Parameter legality. Invariant: a parameter for which the arithmetic is undefined fails at elaboration, not in silicon. Both guards mark genuinely broken configurations rather than merely unusual ones — see §6.

State type. Invariant: the machine's states are named and enumerable. Five values in three bits, with three encodings unreachable and a default that recovers from them.

Synchronisation boundary. Invariant: exactly one representation of the line exists, and rx_i reaches nothing else. Chapter 6.1 §9 explains why two synchronisers would let the receiver hold contradictory beliefs about the wire.

Named events. Invariant: start_cand and sample_now are each produced in exactly one place. Scattering phase_q == 7 through the design is the defect Chapter 6.3 §3 named; one assignment means one thing to change.

Phase counter. Invariant: phase zero is the start candidate, and the sample fires on the tick that carries the count to CENTER. This is Chapter 6.1 §4's single origin, and the reason all eleven samples in Chapter 6.3 §7's trace read phase_q == 7.

Bit counter. Invariant: bit_idx_q is the index of the data bit arriving now, and nothing outside S_DATA reads it. That second clause is what makes the wrap from 7 to 0 harmless at DATA_W = 8.

Shift register. Invariant: after n stores, the first n arrived bits occupy the top n positions, and after DATA_W stores they are correctly placed. Verified by the 00, 80, C0, 60, 30, 98, 4C, A6 trace.

Parity accumulator. Invariant: it holds the XOR of the payload bits stored so far, and nothing else. Cleared at the candidate; the received parity bit never enters it.

Stop validation. Invariant: the framing result describes the stop interval alone. One comparison, at the stop sample, published in the same branch as the data.

Holding register and status. Invariant: data and all three flags describe one frame and move together. The single publish point, and Chapter 6.5 §3's stability property.

Control and recovery. Invariant: the machine is always in a legal state and always leaves every state. No conditional exits except the qualification test; default covers the rest.

5. Review Checklist

The questions a reviewer should ask, with this design's answers. Every no would be a finding.

QuestionHere
Does raw rx_i fan out anywhere?No — only into rx_meta_q
One clock domain?Yes — one clk, no generated clock
Are timing events clock enables?Yes — os_tick_i and sample_now
What is phase count zero?The start candidate, stated in 6.3 §1
Is the centre sample derived or magic?Derived: CENTER - 1 from OVERSAMPLE
Is the bit-counter convention documented?Yes — index of the bit arriving now
Is LSB-first reconstruction correct?Verified against 0xA6 and eight other patterns
Is the parity accumulator cleared at the right time?At the candidate, with par_err_q
Does the received parity bit enter the accumulator?No
Does disabled parity skip the state cleanly?Yes — one branch, one place
Is stop validated at the intended sample?Yes — 10.5 UI in 8E1, verified in simulation
Are error flags tied to the correct frame?Yes — via par_err_q and a single publish point
Can rx_valid_o be missed?No — held until accepted
What happens if the consumer stalls?Held byte preserved, new frame dropped, overrun raised
Back-to-back frames?Received at full rate, verified
Reset mid-frame?In-flight byte discarded; see §7
Illegal state?default returns to S_IDLE
Can any configuration create a zero-width counter?No — both guards, §6
Are parameters checked?Yes, at elaboration
Is any status sticky across frames?No — deliberately, Module 9 owns that

6. Parameter Legality, and Not Over-Engineering It

Two checks, and both mark configurations where the arithmetic above is undefined:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
if (OVERSAMPLE < 2) $fatal(1, ...);   // CENTER = 0, so CENTER-1 underflows
if (DATA_W < 1)     $fatal(1, ...);   // a zero-width shift register

OVERSAMPLE = 1 gives CENTER = 0, and CENTER - 1 on an unsigned localparam is a very large number — the comparison never matches and the receiver never samples. That is worth a fatal rather than a comment.

What is deliberately not checked is as informative:

Odd OVERSAMPLE. CENTER truncates, placing the sample half a tick before the true centre. That is a real but tiny asymmetry, and Chapter 5.3's standalone module warned about it. Here it is legal.

DATA_W outside 5–9. UART configurations conventionally span 5 to 9 data bits, and this receiver works for any width its parameters can express. Restricting it to the conventional range would reject a valid use of the block for no engineering reason.

Two stop bits. Not a parameter at all — the assumptions block says one, which is more honest than a parameter that silently ignores its second value.

7. Corner Cases

Reset released onto a live line. The receiver returns to S_IDLE correctly and the in-flight byte is discarded — and then it mis-frames on the remainder and delivers a byte that was never sent. Simulation produced 0xFA from an interrupted 0xA6. Nothing malfunctioned: the next mark-to-space transition it saw was a data edge, and nothing in a UART frame identifies itself as a continuation. Chapter 6.2 §7 develops it. A testbench must allow an idle period after a mid-frame reset, and a system resetting a UART during traffic should expect one spurious byte.

Line stuck at space. The receiver validates a stop of 0, reports rx_frame_err_o, returns to S_IDLE, and then — correctly — does nothing, because a candidate needs a mark-to-space transition and there is no mark to leave. It resumes the moment a real frame arrives. This is the behaviour Chapter 6.2 §4's unconditional stop exit exists to produce; the conditional version wedges here permanently.

Consumer never reads. One byte held indefinitely, rx_overrun_o set at the second frame and remaining set. The receiver does not escalate or give up. This is intentional: escalation policy belongs to the system, and Module 9 is where it is built.

Accept and delivery on the same cycle. rx_valid_o && rx_ready_i clears the register set, and the S_STOP branch may write it in the same cycle. The clear is written first in the always_ff, so the delivery's non-blocking assignments take precedence — the byte is accepted and replaced on one edge with no gap and no overrun. Verified by the back-to-back test with a prompt consumer.

Glitch narrower than half a bit. Rejected by qualification: the sample at 0.5 UI finds mark and the machine returns to S_IDLE. Chapter 5.2 established the exact class this rejects — disturbances shorter than the validation delay, and nothing more.

8. Verification Matrix and Results

The receiver-focused matrix, with what each axis is for:

AxisValues exercisedCatches
Data patterns00 FF 55 AA 01 80 A5 A6 53see below
Parity modenone / even / oddmode swap, accumulator convention
Parity corruptioninverted parity bit, both modescheck polarity, status ownership
Stop corruptionstop driven to spaceframing detection, wedging
False startglitch of 1/8 UIqualification
Back-to-backno idle gapdelivery latency, return-to-idle
Consumer stallready held low across a frameoverrun policy, status ownership
Reset timingasserted mid-S_DATAreset semantics, mis-framing
Clock mismatch±1%, ±3%, both signssample placement, 5.5's model

Why these data patterns, since a list without reasons is not a plan:

PatternPurpose
0x00, 0xFFlongest runs — expose sampling that drifts without transitions to re-anchor on. Both are palindromic, so neither can detect a reversed shift register.
0x55, 0xAAmaximum transition density — expose setup/hold and synchroniser issues. Reverse into each other.
0x01, 0x80single bit at each end — expose bit-order reversal unambiguously, since 0x01 reverses to 0x80.
0xA5, 0xA6asymmetric, differing in one bit — 0xA6 reverses to 0x65, and the pair differs in parity-relevant weight.
0x53asymmetric and odd-looking — reverses to 0xCA, the classic reversal signature.

Note the parity consequence from Chapter 6.4 §2: seven of these nine bytes have even weight, so the odd-parity branch is exercised only by 0x01 and 0x80. A pattern set chosen for visual variety rather than by weight leaves half the parity logic untested.

Results, from the run in §1:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
TOTAL CHECKS: 73   FAILURES: 0
RESULT: ALL TESTS PASSED

What the matrix does not cover, stated so it is not mistaken for completeness: randomised start phase against a free-running clock (the testbench drives on the clock grid); mismatch beyond ±3%; DATA_W other than 8 in simulation, though the arithmetic was checked across 5–9; OVERSAMPLE other than 16 in simulation; and any form of constrained-random or coverage-driven verification, which is Modules 14–16.

9. Debugging: Symptom to Mechanism

10. What This Means on an FPGA

Thirty-five flip-flops at DATA_W = 8, OVERSAMPLE = 16. Nothing here needs optimising, and anything that appears to want it is a sign a parameter has been pushed somewhere a UART does not go.

The synchroniser is the only structure needing a constraint, and it is Module 12's to specify. Its placement is fixed here: adjacent to the pin, with nothing tapping rx_i.

Expose state_q, sample_now, rx_valid_o and rx_ready_i during bring-up — four signals that resolve most of §9's table at a glance. They are debug aids, not interface: a production instantiation need not carry them, and a bring-up build should.

Check the synthesis report for a latch. There should be none; every branch in both always_ff blocks either assigns or holds a register. This is a reviewed claim, per §1 — an actual synthesis run is what would turn it into a verified one.

11. Understanding Check

12. Summary

One synthesizable receiver, assembled from five chapters of specification, compiled with Icarus Verilog 13.0 and simulated to 73 checks with 0 failures — and every trace published across Module 6 extracted from that simulation rather than computed by hand. MDX compiling and SystemVerilog compiling are different facts, and both were established; synthesisability is a reviewed claim awaiting Module 12.

The assumptions block states what the receiver is not: no FIFO, no break handling, no idle detection, no sticky status, no second stop bit, no majority voting — each deferred to a named module, because unsupported behaviour must look deliberate.

Every part maintains a stated invariant, and the walkthrough names them: one representation of the line, one origin, one publish point, one place where configuration steers control flow, and a counter whose value is read only where it is meaningful.

Two parameters and one runtime input, and even that small space has corners — DATA_W = 8 wraps the bit counter, a non-power-of-two OVERSAMPLE exposes the natural-wrap defect. Every parameter multiplies the verification burden, which is why the design has two rather than eight.

The corner cases are documented rather than discovered: mis-framing after reset onto a live line, correct inaction on a line stuck at space, indefinite holding when the consumer never reads, and accept-plus-delivery on one edge.

Two real defects were found during development — status ownership, found by reasoning about a path no nominal test reaches, and a flip-flop count contradicted by computing it. Both are reported where they occurred.

13. Where Module 6 Leaves You

Six chapters have turned Module 5's timing mechanisms into a receiver.

Chapter 6.1 partitioned it, wrote the interface before the implementation, and settled the timing origin that Module 5 deliberately left open. Chapter 6.2 built the control machine as a table of eight transitions and showed which three implementations get wrong. Chapter 6.3 derived both counters from the parameters, stated every semantic before its RTL, and traced a byte through the shift register. Chapter 6.4 added validation and the publish discipline that keeps status attached to the frame it describes. Chapter 6.5 chose the output interface from the fact that a receiver cannot tell the wire to wait. This chapter assembled, compiled, simulated and reviewed the result.

An engineer holding this chain can design a receiver from a specification, derive its counter widths and semantics, defend its interface, review someone else's, and diagnose a failure from a symptom — without memorising the final RTL, which is the test Chapter 6.1 §1 set for the interface and which applies equally to the module.

What remains is the other half of the link, and everything the receiver deferred.

14. What Comes Next

Module 7 builds the transmitter — the simpler half, done properly. It has no start detection, no qualification and no sampling: it executes a schedule it wrote. What it does have is a producer-facing contract that is easy to get subtly wrong, a frame sequencer whose shape must match this receiver's exactly, and a back-to-back behaviour that decides whether the link runs at its rated throughput or at some fraction of it.

After that: Module 8 builds the os_tick both halves have been consuming; Module 9 takes the per-frame status this module produces and builds the error, break and recovery architecture on top of it — including the idle detection that would suppress §7's fabricated byte; Module 10 adds the buffering that moves the overrun threshold; and Modules 11 onward assemble an IP, integrate it, and verify it properly.

Browse the full path on the UART tutorials index. For the sampling mechanisms this receiver assembles, read back to Chapter 5.3; for the budget that decides whether it works at a given rate, Chapter 4.5.

Continue learning

Where this fits

Part of the UART curriculum.