Project 5, Part 2: Diagnosing Each Violation From the Reports Alone
In the previous article we collected the evidence: three reports, three failures. In this article we reason from each report to a cause. Two of the three turn out to share one, which is how real bugs behave: a single shortcut, taken once outside the normal flow, tends to leave more than one fingerprint.
The DRC violation
The first question for any DRC violation, from Module 6, is whether it is a placement problem (cells overlapping or off their sites) or a routing problem (the wires themselves). Placement is cheaper to rule out:
% check_placement -verbose
No overlaps and no illegal placements. Whatever is wrong is in the routing geometry, not in where any cell sits.
Now look again at the geometry. The detailed router works by finding rule-correct paths through a grid of tracks, jogging between tracks and layers to keep clearance. When a real router run does leave a violation, it is usually a near miss around an obstacle, reported by the router itself at the end of routing. This run’s router log reported zero violations. A perfectly straight wire, ignoring a neighbour 0.11 µm away, that the router did not report, fits one explanation far better than any router bug: the wire was added directly to the routed design, by hand or by a script, after the router had finished, and nothing checked its clearance to the tx_busy segment already there.
Cause: rx_valid_fix_net was drawn into the routed design by hand, after routing, without any spacing check against the neighbouring net.
The LVS mismatch
Module 8.2 taught the signature to look for. An instance present in the layout but not in the netlist, together with a net that has one more pin in the layout than in the netlist, is the classic sign of a change made to the physical design that never reached the netlist LVS compares against.
The instance’s name, _eco_buf_9, is not something Yosys or OpenROAD would generate; it looks like a manual ECO. And the extra pin on rx_valid is exactly what you get when a buffer is spliced onto that net in the layout: its input pin now hangs on rx_valid physically, and its output drives a new net, the extra one in the count, which carries the signal on to wherever the buffer was meant to drive.
Cause: a buffer, _eco_buf_9, was inserted by hand into the routed layout on the rx_valid net, probably as a hold fix like the one in Project 2, but the netlist was never rewritten, so the layout is ahead of the netlist it is checked against.
Now put the two diagnoses side by side. They are not two unrelated bugs. The hand-drawn wire, rx_valid_fix_net, is the new net from the buffer’s output: the wiring of the same manual edit. DRC caught the careless geometry of the edit, and LVS caught the fact that the netlist was never updated. One event, two symptoms, seen through two different tools. Figure 1 shows the causes behind all three reports.
Compare this with Project 2. There, the hold buffer was added by the resizer inside the flow, the router routed it, and the final netlist was written afterwards, so DRC and LVS were clean. Here the same kind of fix was made outside the flow, and both checks caught it.
The timing violation
This one is unrelated to the other two, and its evidence is the fanout column. A path that fails only at the slow corner, while passing comfortably at typical and fast, is the signature from Modules 7 and 10’s Project 4 of a delay dominated by load: a weak driver charging a large capacitance gets disproportionately slower at the slow corner. A long logic chain slows down more evenly across corners; a heavy load hurts most where the drive is weakest.
A fanout of 42 on a dfxtp_1, the smallest flop, confirms it. baud_tick enables the transmitter’s shift register, its bit counter and its state logic, and in this netlist it fans out to 42 flop-enable inputs spread across the design. A normal OpenLane run would never leave a net like that: the resizer’s design repair step, run after placement, enforces a maximum fanout (10 by default) and a maximum load and slew, and would have buffered baud_tick into a small tree. So the next thing to look at is the configuration:
grep -nE "FANOUT|DESIGN_REPAIR" designs/uart/config.json
12: "MAX_FANOUT_CONSTRAINT": 64,
13: "RUN_POST_GPL_DESIGN_REPAIR": false,
The fanout limit was raised to 64 and the post-placement design repair was switched off (variable names from OpenLane 2). With both changed, nothing in the flow was obliged to buffer the net, and it reached signoff as one weak flop driving 42 loads and a long wire.
Cause: the maximum fanout constraint was raised and design repair was disabled in the configuration, so baud_tick was never buffered; the resulting load only becomes a violation at the slow corner, where the driving flop is weakest.
The mistake to avoid is assuming three reports must mean three independent problems with three unrelated fixes. Sometimes they do; the timing violation here really is separate. But looking for a shared cause first, as we did with the DRC and LVS reports, saves real effort, and it is the kind of cross-checking experienced physical designers do by habit.
Practice Exercise
Suppose the LVS report had shown the mismatch in the opposite direction: the netlist has an instance and a pin on rx_valid that the layout does not. Would “manual edit to the layout, netlist not rewritten” still fit? If not, what would fit better?
Solution
No, it points the other way. If the netlist has a connection the layout lacks, something was added to the netlist after the layout was built and never went through placement and routing: for example, a late netlist or RTL edit applied to the final netlist without re-running the back end, or a layout taken from an older run than the netlist.
The general rule works in both directions: whichever side of an LVS mismatch has the extra instance or pin is usually the side that changed more recently. The direction of the mismatch is itself evidence. It tells you whether to look for an untracked physical edit or an unimplemented logical one.
All three causes are now specific: a manual ECO outside the flow, which explains both the DRC and the LVS failures, and a configuration change that left a heavily loaded net unbuffered. In the next article we fix each at its cause, re-run every check, and close the course with the one habit that matters most at each stage of the flow.