UART · Module 15
UART Properties Worth Asserting
Which UART rules deserve a continuous assertion and which a scoreboard already covers, with a test that separates them — and the properties that look valuable until you ask what would notice them failing.
An assertion is a claim that something is true on every clock, forever, and that is a strong thing to say. Most of what a verification environment checks is not like that at all: a scoreboard compares one transaction against one prediction, once, after the fact. Both are useful. Confusing them produces suites that assert things no design could violate and miss the rules that no scoreboard can see.
This chapter is the sorting exercise. It ends with a catalogue of the properties this UART actually has, and with three that look excellent and are not.
1. The Test: Would Anything Else Notice?
A rule deserves a continuous assertion when both halves of this are true:
- It holds on every clock, not at the end of a transaction.
- No scoreboard would see it break.
The second half is the one that does the work, and it is worth applying ruthlessly.
| Rule | Continuous? | Would a scoreboard see it fail? | Verdict |
|---|---|---|---|
the byte 0xA5 is delivered as 0xA5 | no — one transaction | yes, immediately | scoreboard |
| the line is MARK whenever the transmitter is idle | yes | no — nothing is delivered to compare | assertion |
| a frame is exactly ten bit intervals long | yes | no — a frame one bit long delivers the right byte | assertion |
| parity is computed over the data bits | no — per frame | yes, via the reference model | scoreboard |
| the FIFO level never exceeds its depth | yes | no — it would look like data loss somewhere else | assertion |
ready and busy follow their handshake contract | yes | no — the data still arrives | assertion |
The pattern in the right-hand column is the whole chapter. Every row that earns an assertion is a rule whose violation produces no wrong byte. It produces a link that works and then stops working, or a queue that quietly loses one entry a week, or a transmitter that is fine until someone connects a second master.
2. The Catalogue
These are the properties this UART has. They are grouped by the block they belong to, and each names the interface signals it watches — a property that needs a signal the block does not expose is not a property of that block.
Transmitter
| # | Property | Why it is not a scoreboard check |
|---|---|---|
| T1 | the line is MARK whenever idle | §1 — a stuck-low line delivers nothing to compare |
| T2 | no two acceptances on consecutive cycles | the data still arrives; the second byte is silently lost |
| T3 | an accepted handshake is followed by busy | a handshake that vanishes drops a byte with no error |
| T4 | ready returns only at the final bit interval | promising capacity that does not exist corrupts the next frame |
| T5 | a frame is exactly the configured number of bit intervals | a frame one interval long still carries the right byte |
| T6 | the frame opens with SPACE | a start bit at MARK is not a frame at all |
| T7 | the line moves only on a baud tick | a line that moves between ticks is a timing bug, not a data bug |
| T8 | busy does not drop mid-frame | truncation, which looks like a framing error at the far end |
Synchronous FIFO
| # | Property | Why it is not a scoreboard check |
|---|---|---|
| P1 | the level never exceeds the depth | overflow shows up as loss somewhere else entirely |
| P2 | empty is true exactly when the level is zero | a flag that lies makes every consumer wrong |
| P3 | full is true exactly when the level is the depth | the same, in the other direction |
| P4 | the level follows accepted pushes and pops | the FIFO property: nothing lost, nothing invented |
| P5 | overflow is raised exactly on a rejected push | a silent drop is the worst failure a queue has |
| P6 | underflow is raised exactly on a rejected pop | reading a queue that has nothing in it |
| P7 | empty and full are never asserted together | a contradiction the consumer cannot resolve |
| P8 | the level moves by at most one per clock | catches pointer corruption before data does |
Receiver and status
| # | Property | Watches |
|---|---|---|
| R1 | a start candidate is only ever taken from the synchronised line | 12.2 |
| R2 | sticky error flags are set-dominant against a clearing write | 9.6 |
| R3 | a parity error never suppresses delivery of the byte | 6.4 |
| R4 | IRQ_STAT is exactly IRQ_RAW & IRQ_EN, with no state | 13.3 |
| R5 | a DMA request never outlives its cause | 13.4 |
R2, R4 and R5 were each checked as counted invariants in Modules 13 and 14 rather than as assertions, and that was the right call there: those suites drove the blocks directly and could make an arithmetic claim at the end. Written as assertions they become reusable — bindable to the block wherever it is instantiated, including inside the assembled IP where no directed suite is driving it.
3. Three Properties Worth Not Writing
Every assertion has a cost: it is read, maintained, and eventually debugged by someone who did not write it. These three look valuable and are not.
"The transmitter never asserts busy without a preceding handshake."
True, and unfalsifiable by any stimulus the environment can produce. busy is driven from a state machine whose only entry is the handshake; there is no input sequence that separates them. An assertion nothing can violate is documentation with a runtime cost — and worse, it inflates an assertion count that someone will quote as evidence.
"The received byte equals the transmitted byte in loopback."
This is a scoreboard check wearing an assertion's clothes. It is a claim about one transaction, it needs the reference model to evaluate, and writing it as a continuous property means evaluating it on every clock in order to be true vacuously on almost all of them.
"The baud tick period is exactly CLK_HZ / BAUD_HZ clocks."
Tempting, and wrong in a way that takes a while to see: with a fractional divider it is deliberately not constant. Chapter 8.3 builds an accumulator whose whole purpose is to alternate between two periods so the average is right. An assertion demanding a constant period fails on the correct design — which is the failure mode Chapter 15.2 spends most of its length on.
4. Where the Three Languages Diverge
The properties are language-independent. Writing them is not, and the differences on this toolchain are larger and stranger than expected.
| Verilog-2001 | SystemVerilog (Icarus 13.0) | VHDL-2008 + PSL (NVC 1.23) | |
|---|---|---|---|
| assertion construct | none at all | immediate only | full PSL |
temporal operators (next, until) | no | no — concurrent assertions rejected | yes |
implication (a -> b) | no | no | yes |
| sequences | no | no | yes |
| coverage directives | no | no | yes (cover) |
The middle column is the surprise. SystemVerilog has SVA — it is the language's headline verification feature — and Icarus Verilog 13.0 answers every concurrent assertion with sorry: concurrent_assertion_item not supported. Inline properties, named properties, properties without disable iff, purely boolean properties: each was tried, each refused.
So on this toolchain the oldest of the three languages has the most expressive assertion support, by a distance. Chapter 15.2 §4 shows what that looks like in practice and what the other two have to do instead.
5. Understanding Check
6. Summary
An assertion claims something holds on every clock; a scoreboard compares one transaction once. Both are necessary and they answer different questions.
The test for a property is whether anything else would notice it failing. Every UART rule that earns an assertion is one whose violation produces no wrong byte — a stuck-low line, a frame of the wrong length, a flag that lies, a level that exceeds its depth.
Sixteen properties across the transmitter, the FIFO and the receiver, each named, each watching interface signals only.
P4 — the FIFO accounting property — is the one that would survive a purge, and the other seven exist to localise a failure rather than to detect one.
Three properties are worth not writing: one nothing can violate, one that is a scoreboard check in disguise, and one that is false on a correct fractional baud generator.
And a confidently wrong assertion is more expensive than a missing one, because the usual response to a checker that argues with a correct design is to stop reading it.
7. What Comes Next
Chapter 15.2 writes this catalogue as code, in all three languages, bound to the published transmitter and FIFO — and records the four properties that were wrong on the first attempt, with the number of false failures each one produced.
Browse the full path on the UART tutorials index. For the scoreboard side of the boundary drawn in §1, read back to Chapter 14.5.
Continue learning
Related tutorials
- Related topic
Writing UART Assertions for TX, RX and FIFOs
The transmitter and FIFO property checkers written in three languages and bound to published designs, the sampling pitfalls that make an assertion argue with a correct design, and what each toolchain will actually run.
- Related topic
UART Design Review: The Questions a Reviewer Asks
Twenty-five review questions across RTL, timing, integration, verification and debug, five of them implemented as a synthesisable probe in three HDLs and the same properties written in PSL, where they run as real concurrent assertions.
- Related topic
UCIe Assertions
Writing SVA that describes UCIe architectural contracts rather than implementation details — triggers that mean the right event, reset and disable scoping that does not sleep through the bug, overlapping transactions that outgrow local variables, liveness with its assumptions written down, and the four wrong properties that pass a regression while checking nothing.
- Related topic
Assertions — Executable Statements About Ownership Over Time
A PCIe assertion is not a syntax exercise. It is a claim about who owns an item, what must stay true while they own it, and which event transfers it — and the hardest part is proving the assertion was ever reached.
Where this fits
Part of the UART curriculum.
