UART · Module 1
The UART Link: TX, RX, Idle and Full Duplex
A UART link is two independent one-way conductors, not one bidirectional bus — which removes arbitration, turnaround and direction control from the design, makes the naming endpoint-relative, and means full duplex guarantees simultaneity and nothing else.
Chapter 1.2 settled where a receiver's sampling reference comes from. This chapter settles something more basic that the previous two have been quietly assuming: what the link physically consists of, and who owns each part of it.
The answer decides more than it looks like it should. It determines whether the two devices ever have to take turns, whether either can be interrupted, what the line means when nothing is happening, and which wiring mistake will consume the first hour of your first bring-up. It also produces the most common false inference in the subject — that because a link is full duplex, the two directions are somehow related to each other. They are not, and the rest of this chapter is largely about why that matters.
1. Two Signals, Not One Bus
Start with what is actually between the two devices. In the direct, logic-level arrangement — two devices wired to each other with no transceiver in between — a UART link is two signal conductors plus a common reference, and the two conductors are not interchangeable:
- One is driven by device A and read by device B.
- The other is driven by device B and read by device A.
In this arrangement each conductor has one driver for its whole life. It is never driven by two devices and never left undriven. That is a property of the arrangement rather than of UART framing — insert a different electrical layer between the two devices and the connectivity can change, which is Chapter 1.4's subject. Everything in this chapter is about the direct case, because it is the one the framing was designed around and the one every later RTL chapter assumes.
That single property removes an entire category of machinery that shared-bus interfaces are obliged to have. There is no arbitration, because there is never a contest for a conductor. There is no bus turnaround, because direction never changes. There is no direction-control signal, no output-enable timing, no driver-overlap window to design around, and no contention state to detect. A conductor whose driver never changes cannot have any of those problems.
2. Naming Is Endpoint-Relative, and That Is Where Bring-Up Fails
The names TX and RX describe a signal from the point of view of one device. tx is what this device drives; rx is what this device reads. Both names refer to the same pair of conductors from opposite ends, which means the correct connection is a crossover:
Device A's transmit output connects to device B's receive input. Device B's transmit output connects to device A's receive input.
This is obvious written down and is nonetheless the single most reliable way to lose an afternoon, because the failure is silent and symmetric. Connect transmit to transmit and receive to receive and you have two outputs driving each other and two inputs reading nothing they will ever recognise. Neither device reports an error. Neither sees traffic. Both are working correctly.
Read the figure as two unrelated rows. They are drawn together because they share a connector, not because they share anything functional.
3. Idle: The Line Has to Mean Something When Nothing Is Happening
A conductor is always at some level. It is never "off". So a link that is not currently carrying data still presents a level to the receiver, and the protocol has to say what that level means.
This is not a formality. Chapter 1.2 established that the receiver has no forwarded clock and must therefore detect, in the data signal itself, the event that starts a transmission. An event is only detectable as a departure from something. Without a defined resting level there is no departure to detect, and the receiver has nothing to trigger on.
So a UART link defines an idle level: the level a transmitter holds when it has nothing to send. Conventionally this is the mark level, and on a logic-level link that is normally the high level — though what that level physically is depends entirely on the electrical layer, which is Chapter 1.4's subject. The framing-level fact that matters here is narrower and is not a convention at all: the resting level and the level that signals the start of a transmission must be different, or the start of transmission would be undetectable.
Three consequences follow immediately, and all three reappear later in the curriculum.
A transmitter is responsible for the line even when it is silent. Idle is something the transmitter actively drives, not something that happens when it stops. A transmitter that releases its output between transmissions leaves the receiver reading an undefined level.
Idle is indistinguishable from several faults. A disconnected conductor with a pull-up, a device held in reset that drives its output to the idle level, and a perfectly healthy link with nothing to say all look identical. The receiver cannot tell them apart, because there is nothing in the resting level to tell apart. This is exactly why the opposite condition — the line held at the non-idle level for longer than a whole transmission — is given a name and detected deliberately; Module 9 covers it.
The gap between transmissions is unconstrained. Because idle is a valid steady state of arbitrary length, a transmitter may send back-to-back transmissions with no gap, or one transmission an hour. Neither is exceptional, and a receiver cannot infer anything from how long the line has been idle.
4. Full Duplex: What It Guarantees
Both conductors exist at once and neither depends on the other, so both directions can carry traffic at the same time. That is the entire content of full duplex here: simultaneity. Device A can be transmitting while device B is transmitting, and neither has to wait, request permission, or finish first.
The waveform below is what that looks like on the two conductors.
Two conductors, two idle states, one overlap
10 cyclesNote what the figure does not contain. There is no clock track, because there is no clock on this link to draw — the columns are bit periods, a unit both endpoints approximate independently, which is precisely Chapter 1.2's subject. There is also no marker relating an event in one row to an event in the other, because no such relationship exists.
5. The Four Things Full Duplex Does Not Guarantee
This is the part worth slowing down for, because "full duplex" is routinely over-read. Simultaneity is all it provides. In particular:
It does not mean the two directions are synchronised. The two conductors are driven by two devices using two independent timebases. A transmission starting on one has no defined relationship to anything on the other. The overlap in Figure 2 is a coincidence of when each device happened to have something to send.
It does not mean traffic in one direction is a response to traffic in the other. There is no request/response structure anywhere in UART framing. If a system treats received bytes as replies to transmitted ones, that pairing is a convention implemented in software or in a higher-level protocol, and the link neither enforces nor is aware of it.
It does not mean anything is acknowledged. A transmitter finishes transmitting and knows only that it drove the conductor. It receives no indication that the far end was listening, was powered, was correctly configured, or recovered anything at all. Whatever confidence a system has about delivery comes from a layer above.
It does not mean either side can be stopped. Nothing in the two-conductor arrangement lets a receiver tell a transmitter to pause. If a receiver cannot keep up, the transmitter has no way to know and no reason to stop. Adding that capability means adding either extra conductors or in-band signalling, both of which are genuine additions rather than parts of this picture — Module 10 builds them.
6. What This Produces in RTL
The interface contract falls directly out of §1. A UART core's serial data interface is one input and one output — not a bidirectional port, and not a port carrying a direction control. (A design driving a half-duplex transceiver may well have an enable output for that device; it is a transceiver control signal and not part of the serial data path. Chapter 1.4.)
// Synthesizable SystemVerilog — interface contract only.
// The TX and RX engines themselves are Modules 6 and 7; this is the
// boundary they sit behind, and it already encodes this chapter.
module uart_core (
input logic clk, // this device's own system clock
input logic rst_n, // active-low reset
// ── Serial link: two independent one-way signals ──────────────
input logic rx_i, // driven by the FAR end, read here
output logic tx_o, // driven HERE, read by the far end
// ── Transmit side: local parallel interface ───────────────────
input logic [7:0] tx_data_i,
input logic tx_valid_i,
output logic tx_ready_o,
// ── Receive side: local parallel interface ────────────────────
output logic [7:0] rx_data_o,
output logic rx_valid_o
);Read the port list as the chapter in condensed form.
rx_i is an input and tx_o is an output, permanently. Neither is inout. There is no tx_oe or dir port on the data path, because each conductor's direction is fixed and nothing needs to be told which way it faces.
The two parallel interfaces are separate and both may be active in the same clock cycle. tx_valid_i and rx_valid_o have no relationship. Nothing in the module needs to arbitrate between them, and a correct implementation contains no logic that couples them — which is worth stating because an incorrect one often does, usually by sharing a state register between the two halves.
clk is singular and local. One clock runs both halves; it is this device's clock and has nothing to do with the far end's. The serial rate is a separate matter, derived from this clock by logic that Module 8 builds.
There is no configuration in this port list yet. Data width, rate and framing options all belong here eventually, and they arrive with the chapters that define them. Adding them now would import Modules 3 and 4 into a chapter that has not earned them.
The shape to avoid
The most common way to get this wrong is to carry a mental model from a shared-bus interface and model the link as one bidirectional signal:
// Tempting but wrong — models the link as one shared conductor.
module uart_core_wrong (
input logic clk,
input logic rst_n,
inout wire serial_io, // ← the mistake
input logic dir_sel // ← and its inevitable consequence
);Four things go wrong at once, and they are worth separating because only the first is obvious.
It infers a tristate buffer at a pin that never needs one. A conductor with a single permanent driver does not require a driver that can be turned off. On an FPGA this consumes an I/O primitive capability for nothing; on an ASIC it puts a pad cell in the design that has no functional reason to exist.
It invents dir_sel, which has no source. Something must now decide which way the conductor faces, and no such information exists in UART framing. Whatever drives that port will be a fabrication — and it will be wrong, because the correct answer is "both, at once".
It destroys full duplex. One conductor can face one way at a time, so simultaneous transmission becomes impossible by construction. A design built this way works in testing that exercises one direction at a time and fails the first time both directions are busy — which, on a console link, means it fails the first time a person types while output is being printed.
It creates a contention window that did not previously exist. Two devices that can both drive one conductor can both drive it at once. The design now needs a turnaround rule to prevent that, and a turnaround rule needs timing, and the timing has nothing in the protocol to anchor it to.
The fix is not to add arbitration. It is to recognise that the problem was imported rather than discovered: the link was never shared, so none of this machinery belongs.
7. What This Means for Verification
Two independent channels have a direct and often-missed consequence: verifying one direction tells you nothing about the other. They are separate datapaths that happen to share a module boundary, a clock and a reset.
That produces a concrete plan even at this stage of the curriculum.
Two observers, not one. Each conductor needs its own monitor with its own state. A single checker that tries to follow both will either serialise them — and miss the overlap in Figure 2 entirely — or need two independent state machines inside it, at which point it is two monitors wearing one name.
Concurrency is a stimulus requirement, not an edge case. A test that transmits, waits, then receives will pass on a design that cannot do both at once. The interesting stimulus is precisely the Figure 2 overlap: both directions active, with the second starting partway through the first. A design that couples the two halves internally fails only under that pattern.
The shared resources are where the coupling bugs live. The two directions share a clock, a reset and — in every real implementation — some configuration. Those are the places where an implementation can accidentally connect two things the protocol says are independent. A useful early question for a verification plan is simply: what state does this design hold that both directions can reach? Anything on that list deserves a test that exercises both directions against it.
Loopback proves less than it appears to. Tying tx_o back to rx_i is a genuinely useful bring-up aid and a weak functional test: it exercises both halves against each other, so it is insensitive to any error the two halves share. Two halves that agree on a wrong rate, or a wrong bit ordering, pass loopback perfectly and fail against every other device. Module 11 treats loopback as the IP feature it is; the caution belongs here, where the temptation first arises.
Assertions become genuinely useful once framing is defined, which is Module 3 onward. The one property this chapter can already state precisely is the independence itself — that activity on one conductor never causes activity on the other — and that is better expressed as a stimulus and coverage requirement than as a temporal property, because it is a statement about the absence of a relationship.
8. What This Means on an FPGA
The link appears in a design as exactly two pins, and the asymmetry between them is the thing to internalise.
tx_o is an ordinary registered output. It is driven by fabric running on the local clock, and its timing is entirely the design's own business.
rx_i is an input whose transitions originate in another device, with no relationship to the local clock. That makes it a genuinely asynchronous input to the synchronous design, and it must be treated as one before any logic makes a decision from it. This is an ordinary, well-understood digital-design obligation with a standard answer — and it is a different concern from the asynchronous serial timing of Chapter 1.2, which is a protocol-level fact about where sampling information comes from. Module 12 treats the input-synchronisation obligation properly.
Two further points matter at bring-up specifically.
An unconnected rx_i is not a neutral state. A floating input has no defined level, and a receiver watching a floating pin may see transitions that resemble the start of a transmission. Giving the pin a defined level when nothing is connected is a board-level decision, and it is the reason a design can appear to receive traffic from a device that is not plugged in.
Both pins are worth bringing out to a header even when they are not needed. The two conductors are the only place in the system where both endpoints' behaviour is visible at once, and an instrument on them answers questions that no amount of internal state inspection can. This is the practical form of the observation that Module 17 builds on.
9. Understanding Check
10. Summary
A UART link is two one-way conductors, not one bidirectional bus. Each has a single permanent driver and a single permanent reader, which removes arbitration, turnaround, direction control and contention from the design entirely — and is most of the reason a UART core is small.
The names are relative to the device you are standing on, so a correct connection crosses transmit at one end to receive at the other. Getting this wrong produces a silent, symmetric, error-free failure, because nothing in UART acknowledges anything.
Each conductor rests at a defined idle level that its transmitter actively drives. A resting state must exist because the receiver has no forwarded clock and can only detect the start of a transmission as a departure from something known. The framing-level requirement is only that the resting level and the start-of-transmission level differ; what those levels physically are belongs to the electrical layer in Chapter 1.4. Idle is also indistinguishable from a disconnected cable or a device in reset, and it may last for any length of time.
Full duplex means simultaneity and nothing else. It does not make the directions synchronised, does not pair transmissions with replies, does not acknowledge delivery, and provides no way for a receiver to slow a transmitter down. Each of those is a layer built on top, and mistaking one for a property of the link produces a bug that presents as something else.
In RTL this becomes a permanent input and a permanent output, two parallel interfaces that may be active in the same cycle, and one local clock. In verification it becomes two independent monitors, concurrency as a stimulus requirement rather than an edge case, and suspicion of any state both directions can reach. On an FPGA it becomes one ordinary registered output and one genuinely asynchronous input.
11. What Comes Next
The link is now defined as a logical arrangement — two conductors, a resting level, two independent directions. Chapter 1.4 asks what those conductors physically are: the difference between the framing a UART controller implements and the electrical layer that carries it, why the same controller can sit behind a plain logic-level connection, an RS-232 transceiver or an RS-485 transceiver, and why connectivity and topology follow from that choice rather than from UART itself. Chapter 1.5 then places the whole arrangement inside real systems, and Chapter 1.6 compares it against the alternatives.
Browse the full path on the UART tutorials index. For the two-independent-directions model as a verification component rather than a wiring diagram, Case Study — A UART Agent builds a monitor that recovers one direction's traffic without reference to the other.
Continue learning
Related tutorials
- Related topic
What a UART Actually Is
Two digital systems need to exchange a small amount of data over very few wires, and no clock travels with it. A UART is the logic that answers that problem — it converts between locally meaningful parallel data and timed activity on a single line, and the timing agreement it depends on is what the rest of the curriculum builds.
- Related topic
Synchronous vs Asynchronous Serial Links
A forwarded clock is a sampling reference generated by the same source as the data. Remove it and the receiver must assemble one from a configured rate, an observable event in the signal, and its own local clock — the responsibility shift that turns a receiver into a state machine and shapes every UART design decision that follows.
- Related topic
UART vs RS-232, RS-485 and the Physical Layer
A UART controller decides the bit pattern; a transceiver decides what those bits physically are, how far they reach and how many devices can share the medium. Same controller, different transceiver, different network — and why topology is never a property of the framing.
- Related topic
Where UART Lives: Debug Consoles, Boot and Bring-Up
UART survives for a structural reason: its requirements are nearly zero, so it works at the moments when nothing else does yet. The FPGA debug link, the SoC boot console and board bring-up — and the two-pin integration surface a debug UART actually costs.
Where this fits
Part of the UART curriculum.
