UART · Module 7
Ready/Busy Handshake and Back-to-Back Frames
The write-side contract stated in cycles — what ready promises, how it differs from busy, when payload ownership transfers, and how accepting at the final boundary lets frames run with no gap and no shortened stop bit.
Two chapters have used the word accepted without defining it. Chapter 7.1 said capture happens "at acceptance"; Chapter 7.3 measured latency from "the accepting clock edge". Neither said what that means.
This chapter says it, and then uses it to answer the question the module's registry blurb poses: no gaps the producer did not ask for.
The two halves are connected. A transmitter that can only accept a byte when it is completely idle inserts an idle interval between every pair of frames, and the link runs at a fraction of its rated throughput through no fault of the wire. Avoiding that is not a buffering problem — it is a question of exactly when tx_ready_o may rise, and it is settled in the handshake.
1. The Transaction
A transaction is accepted on a rising clk edge at which
tx_valid_i and tx_ready_o are BOTH high.
At that edge — and at no other time — the transmitter captures
tx_data_i and parity_mode_i, and the producer's obligation ends.That is the whole contract, and three consequences follow that a producer must know without reading the implementation.
Before acceptance, the producer owns the offered value and must hold it stable. While tx_valid_i is high and tx_ready_o is low, tx_data_i and parity_mode_i must not change. This is the standard ready/valid source rule, and it exists because the transmitter may capture on any cycle where both are high — it cannot be asked to capture a value that is being changed.
After acceptance, the producer owns nothing. The transmitter has its own copy (Chapter 7.1 §4), so tx_data_i may change on the very next cycle.
tx_ready_o must not depend combinationally on tx_valid_i. The transmitter's readiness is a function of its own state, and it is legal for a producer to hold tx_valid_i high continuously. A tx_ready_o computed from tx_valid_i creates a combinational loop the moment the producer computes tx_valid_i from tx_ready_o, which is exactly what a naive bridge does.
2. Ready Is Not Busy
These are routinely used interchangeably and mean different things. Both exist in this transmitter, and they are not complements.
tx_ready_o | tx_busy_o | |
|---|---|---|
| Means | I can accept a byte now | a frame is in progress or queued |
| Asks | about capacity | about activity |
| For | the producer, to know when to offer | software, to know when the line is free |
| High during idle | yes | no |
High during S_DATA | no | yes |
High during S_STOP | yes — if nothing is queued | yes |
The S_STOP row is the whole of §5. During the final interval the transmitter is still transmitting — busy — and able to accept the next byte, because the shift register has been emptied. Ready and busy are simultaneously high, which is impossible if they are treated as complements.
A design needs tx_ready_o. tx_busy_o is a convenience for a different consumer — typically software asking "has everything I queued actually gone out?", which readiness cannot answer. This transmitter provides both because they are one gate each and they answer different questions; a design that needs only one should expose only one.
What this transmitter deliberately does not provide is tx_done. A completion pulse is a third signal describing the same state transition as tx_busy_o falling, and a consumer that needs an event can derive it. Adding it would mean three signals for two facts.
3. When Ready May Rise
// Synthesizable SystemVerilog — the readiness condition, stated once.
// Ready exactly where the capture register is free: idle, or the stop
// interval, where the payload has already been shifted out.
assign tx_ready_o = (state_q == S_IDLE || state_q == S_STOP) && !pending_q;
assign accept = tx_valid_i && tx_ready_o;
assign tx_busy_o = (state_q != S_IDLE) || pending_q;tx_ready_o is derived from capacity, not from convenience. The shift register is the capture register, so readiness means precisely the shift register is free and nothing is already queued:
| State | shift register | ready? |
|---|---|---|
S_IDLE | free | yes, unless already queued |
S_START | loaded, about to be used | no |
S_DATA | in use, shifting | no |
S_PARITY | emptied, but... | no — see below |
S_STOP | emptied | yes, unless already queued |
The S_PARITY row is a deliberate choice rather than a necessity. The register is technically free there, so readiness could rise one interval earlier. It does not, because the gain is zero: a byte accepted during S_PARITY still cannot launch until the tick that ends S_STOP, which is exactly when a byte accepted during S_STOP launches. Asserting readiness earlier would widen the window in which the producer can present a byte without changing when any byte is transmitted, in exchange for one more term in the condition.
Asserting readiness where capacity does not exist is the defect to avoid. A tx_ready_o that is high during S_DATA promises the producer that a byte will be taken, and the byte is then dropped or corrupts the frame in flight. The rule is that readiness is a promise about the next edge, and a transmitter that cannot keep it must not make it.
4. Requests That Are Not Accepted
A producer may assert tx_valid_i while tx_ready_o is low. Nothing happens — and that is correct behaviour, not data loss.
tx_valid_i high, tx_ready_o low -> no transaction occurred.The transmitter did not drop a byte, because it never accepted one. The obligation to keep offering is the producer's, and it is the other half of the stability rule in §1: hold the value and keep valid high until the handshake completes.
A producer that pulses tx_valid_i for a single cycle while tx_ready_o is low has simply not made a request. This is verified explicitly in Chapter 7.5's matrix — drive a one-cycle valid pulse mid-frame and confirm no extra frame appears — and it is worth testing precisely because the instinct on seeing a lost byte is to blame the transmitter.
This is also where the layer boundary matters. The transmitter can backpressure its local producer through tx_ready_o, and that is a real and useful mechanism. It has nothing to do with flow control on the wire: Chapter 6.5 §4 established that a UART receiver cannot tell the far end to wait, and the same is true in reverse. RTS/CTS is a separate mechanism on separate wires and belongs to Module 10.
5. Back-to-Back Frames
The policy question, with three defensible answers:
| Architecture | Accept next byte | Cost |
|---|---|---|
| Conservative | only in S_IDLE | one idle interval between every pair of frames |
| Boundary-ready (this design) | in S_IDLE or S_STOP | one comparison |
| Prefetch buffer | any time, into a second register | a buffer — Module 10's subject |
The conservative form's cost is not small. An 8N1 frame is 10 intervals; adding a mandatory idle interval makes it 11, so the link delivers 10/11 of its rated throughput — a 9.1% loss — on a continuous stream, for no reason the protocol requires. Chapter 3.4 was explicit that the stop interval provides the required separation; nothing demands an additional idle bit.
Boundary-ready costs one comparison and eliminates the gap entirely. tx_ready_o rises during S_STOP, the producer presents the next byte, it is captured into the freed shift register, and at the tick that ends S_STOP the machine goes directly to S_START — Chapter 7.2's transition 7.
Extracted from simulation, two frames with tx_valid_i held high:
| interval | state | tx_o | note |
|---|---|---|---|
| 1 | S_START | 0 | frame A begins |
| 2–9 | S_DATA | 1 1 0 0 1 0 1 0 | 0x53, LSB first |
| 10 | S_STOP | 1 | full stop interval — and tx_ready_o rises here |
| 11 | S_START | 0 | frame B begins — no idle interval |
| 12–19 | S_DATA | 0 1 1 0 0 1 0 1 | 0xA6, LSB first |
| 20 | S_STOP | 1 | frame B's stop |
| 21 | S_IDLE | 1 | nothing queued |
Frame A occupies intervals 1–10 and frame B occupies 11–20 — ten each, exactly. The stop interval is not shortened; the idle interval is simply absent, which is what "no gaps the producer did not ask for" means.
0x53 then 0xA6, zero gap
21 cycles6. The Same-Edge Case
The corner the architecture must answer explicitly: a frame completes and a new request is accepted on the same clock edge.
Concretely, the tick that ends S_STOP arrives on the very edge at which tx_valid_i && tx_ready_o first holds. The naive implementation tests only the registered pending_q:
// WRONG — reads only the REGISTERED pending flag.
S_STOP: if (pending_q) state_q <= S_START;
else state_q <= S_IDLE;At that edge pending_q is still 0 — it is being set by the acceptance happening now, and a non-blocking assignment does not take effect until after the edge. So the machine goes to S_IDLE, and the newly accepted byte launches one interval later. The producer did everything right and still gets an idle interval, intermittently, depending on exactly when its offer aligned with the frame's end. That is the worst kind of defect: correct most of the time, and phase-dependent.
The fix is to include the acceptance that is happening on this edge:
// CORRECT — pending_q OR the acceptance occurring on this very edge.
S_STOP: if (pending_q || accept) begin
state_q <= S_START;
pending_q <= 1'b0; // wins over the accept branch's <= 1
bit_idx_q <= '0;
tx_o <= 1'b0;
end else begin
state_q <= S_IDLE;
tx_o <= 1'b1;
endThe ordering of the two writes to pending_q matters and is deliberate. The acceptance branch sets pending_q <= 1'b1; this branch sets it back to 1'b0. Both are non-blocking assignments in the same always_ff, so the later one wins — and because the byte is launching immediately, it must not also remain queued. Reversing the order would leave pending_q set and cause the frame to be transmitted a second time.
The same construction appears in S_IDLE, where a request accepted on the same edge as a tick launches immediately rather than waiting a full interval — which is why Chapter 7.3 §5's latency table has a minimum of one clock rather than a full interval.
7. Verification
Sweep the offer's phase relative to the frame's end. §6's defect is invisible unless the offer sometimes lands exactly on the completing edge. Presenting a byte at every offset across a bit interval, with the previous frame ending in that window, is the test that finds it.
Assert that back-to-back frames have no gap and a full stop interval. Both halves matter and they fail differently: a missing stop interval means a frame started early, an extra idle interval means §6's defect. Counting intervals between successive start bits — 10 for 8N1, every time — checks both at once.
Drive a one-cycle tx_valid_i while tx_ready_o is low and assert that no extra frame appears. §4's case.
Hold tx_valid_i high continuously with changing data and confirm exactly one frame per accepted transaction. This is the test that would have caught the §1 producer defect, and it requires the bench to count acceptances independently rather than assuming one per call.
Change tx_data_i immediately after acceptance and confirm the emitted frame carries the captured value (Chapter 7.1 §4).
// Assertion — ready is never asserted where the capture register is in use.
property p_ready_only_when_free;
@(posedge clk) disable iff (!rst_n)
tx_ready_o |-> (state_q == S_IDLE || state_q == S_STOP);
endproperty
assert property (p_ready_only_when_free);
// Assertion — an accepted transaction is always followed by a frame.
// Nothing accepted may be silently dropped.
property p_accept_leads_to_start;
@(posedge clk) disable iff (!rst_n)
(tx_valid_i && tx_ready_o) |-> ##[1:$] (state_q == S_START);
endproperty
assert property (p_accept_leads_to_start);
// Assertion — a byte is never queued twice. pending_q rises only on an
// acceptance, so it cannot be set while already set.
property p_no_double_queue;
@(posedge clk) disable iff (!rst_n)
(tx_valid_i && tx_ready_o) |-> !pending_q;
endproperty
assert property (p_no_double_queue);The second is the one worth having: it states the contract that acceptance is a commitment, which is exactly what a tx_ready_o asserted without capacity would violate.
8. What This Means on an FPGA
tx_ready_o is a two-term decode of the state register. One LUT. The back-to-back capability is not a feature that costs area; it is the absence of an unnecessary restriction.
Do not derive tx_valid_i combinationally from tx_ready_o in the producer. §1's rule, and it is where a quickly-written bus bridge goes wrong. The symptom is a combinational loop reported somewhere that points at neither block clearly.
Probe tx_valid_i, tx_ready_o and tx_o together. The three of them separate the common failures immediately: valid high with ready never rising is a stuck transmitter; both toggling with nothing on the line is a datapath problem; ready rising twice per frame means the producer is about to double-send.
Measure throughput, do not assume it. A link that should deliver 11,520 bytes per second at 115,200 baud 8N1 and delivers about 10,473 is losing exactly one interval per frame — the conservative-handshake signature from §5, and a 9.1% shortfall that is easy to attribute to the far end.
9. Understanding Check
10. Summary
A transaction is accepted on a rising edge where tx_valid_i and tx_ready_o are both high, and nothing else marks one. At that edge the payload and the configuration are captured and ownership transfers — before it the producer must hold its offer stable, after it the bus is free.
A producer that treats rising readiness as confirmation will double-send, because readiness rises again during the stop interval. The transmitter is correct; the producer asked twice. This was a real defect in this module's own testbench.
Ready is not busy. Readiness asks about capacity, busy asks about activity, and during the stop interval both are high — which is impossible if they are treated as complements, and is precisely what makes back-to-back transmission work.
Readiness is derived from capacity: the shift register is free in S_IDLE and S_STOP, and nowhere else. Asserting it without capacity turns a promise into a dropped byte.
An unaccepted request is not a lost byte. The obligation to keep offering belongs to the producer, and this backpressure is local — it says nothing about the wire, where neither direction can ask the other to wait.
Accepting during the stop interval eliminates the inter-frame gap at the cost of one comparison. The conservative alternative costs 9.1% of throughput on a continuous 8N1 stream. Simulation confirms two frames of exactly ten intervals each, back to back, with a full stop interval and no idle.
And the same-edge case needs pending_q || accept, because the registered flag has not been set yet at the moment the decision is made — with the two writes ordered so the launch clears the queue rather than leaving it set for a duplicate frame.
11. What Comes Next
Four chapters have specified a transmitter completely: a datapath with a capture discipline, a state machine with eight enumerated transitions and a registered output, a timing contract with a measured launch cost, and a handshake with a stated back-to-back policy.
Chapter 7.5 assembles them. One synthesizable module, preceded by the assumptions block that says exactly what it does and does not support, walked block by block with the invariant each part maintains — and then the subject the registry names for it: how a configuration change is applied safely between frames rather than mid-frame, which is the transmit-side counterpart of the receiver's status-ownership problem. It closes with a design review, a verification matrix, and an honest report of what the compiler and the simulator actually established.
Browse the full path on the UART tutorials index. For the receive-side interface this one mirrors — and the overrun that follows when its consumer is late — read Chapter 6.5.
Continue learning
Related tutorials
- Related topic
Data-Valid Generation and the Receiver Handshake
A receiver cannot tell the far end to wait. That one fact decides the shape of the output interface, forces a policy for the frame that arrives while the last is still held, and explains why the answer is not a FIFO.
- Related topic
The TX FSM and Frame Sequencing
Five states, eight transitions, and one rule that keeps the line honest: tx_o is a register written only inside the bit-boundary branch. Includes the off-by-one that emits nine data bits, and the state table the RTL was checked against.
- Related topic
Parity Generation, Checking and Error Detection
One interval, one XOR reduction, and a detection guarantee with a sharp edge: parity catches every corruption that flips an odd number of protected bits and provably misses every even-numbered one — demonstrated, not asserted.
- Related topic
Frame Configurations: 8N1 and the Configuration Space
8N1 names three of the four choices a UART link depends on and omits the one most likely to be wrong. Reading the shorthand, computing what each configuration costs in intervals and line time, and why a longer frame spends timing margin as well as throughput.
Where this fits
Part of the UART curriculum.
