Skip to content
VLSI Mentor

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

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
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_otx_busy_o
MeansI can accept a byte nowa frame is in progress or queued
Asksabout capacityabout activity
Forthe producer, to know when to offersoftware, to know when the line is free
High during idleyesno
High during S_DATAnoyes
High during S_STOPyes — if nothing is queuedyes

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

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// 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:

Stateshift registerready?
S_IDLEfreeyes, unless already queued
S_STARTloaded, about to be usedno
S_DATAin use, shiftingno
S_PARITYemptied, but...no — see below
S_STOPemptiedyes, 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.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
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:

ArchitectureAccept next byteCost
Conservativeonly in S_IDLEone idle interval between every pair of frames
Boundary-ready (this design)in S_IDLE or S_STOPone comparison
Prefetch bufferany time, into a second registera 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_STARTChapter 7.2's transition 7.

Extracted from simulation, two frames with tx_valid_i held high:

intervalstatetx_onote
1S_START0frame A begins
2–9S_DATA1 1 0 0 1 0 1 00x53, LSB first
10S_STOP1full stop interval — and tx_ready_o rises here
11S_START0frame B begins — no idle interval
12–19S_DATA0 1 1 0 0 1 0 10xA6, LSB first
20S_STOP1frame B's stop
21S_IDLE1nothing 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 cycles
A trace of twenty-one bit intervals showing two consecutive UART frames transmitted with no gap between them. The first frame carries 53 hexadecimal: a start bit at the space level, eight payload bits least significant bit first, and a stop bit at the mark level, occupying ten intervals. Readiness rises during that stop interval while the transmitter is still busy, allowing the next byte to be accepted. At the boundary ending the stop interval the second frame begins immediately with its own start bit, carrying A6 hexadecimal across a further ten intervals. No idle interval appears between the two frames and neither stop interval is shortened. After the second frame's stop interval, with nothing further queued, the line returns to idle.frame A — 10 intervalsframe A — 10 intervalsframe B — 10 intervalsframe B — 10 intervalsstop A — ready HIGH, busy HIGHstop A — ready HIGH, busyHIGHframe B starts — NO gapframe B starts — NO gapidle — nothing queuedidle — nothing queuedfieldSTd0d1d2d3d4d5d6d7SPSTd0d1d2d3d4d5d6d7SPidletx_otx_ready_otx_busy_ot0t1t2t3t4t5t6t7t8t9t10t11t12t13t14t15t16t17t18t19t20
Figure 1 — two frames with no gap, extracted from simulation. Columns are BIT INTERVALS, one baud tick apart. tx_ready_o is sampled at the START of each interval, before any acceptance inside it — it falls again mid-interval once the next byte is taken, which one value per column cannot show. Frame A occupies intervals 1 to 10 and frame B occupies 11 to 20, ten each, with A's stop interval emitted at full length and no idle interval between them.
A handshake sequence between a producer and a UART transmitter across two consecutive frames. The producer offers the first byte with valid asserted. The transmitter, being idle and therefore ready, accepts it on a clock edge and captures the payload, after which readiness falls and the frame begins at the next bit boundary. While frame A is being transmitted the transmitter is not ready and the producer waits, holding its next offer stable. During frame A's final stop interval the shift register is free again, so readiness rises even though the transmitter is still busy, and the producer's second byte is accepted at that point. At the bit boundary ending the stop interval the transmitter moves directly into the second frame's start bit with no idle interval between the two frames, and the stop interval of the first frame is not shortened.ProducerTransmittertx_ovalid + 0x53 —offeredready high (idle) —ACCEPTEDnext tick: frame Astartsready LOW — nocapacityholds 0xA6 stable,valid highS_STOP: ready HIGH,busy HIGH — ACCEPTEDtick ends stop:frame B starts, nogap
Figure 2 — the handshake across two back-to-back frames. Time runs downward. The producer's second offer is accepted during frame A's stop interval, while the transmitter is still busy, which is the only reason frame B can begin at the very next boundary. Note that readiness and busy are both high at that moment — they answer different questions.

6. 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:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// 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:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// 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;
        end

The 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).

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// 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

Where this fits

Part of the UART curriculum.