jk
Blog

A circuit board check that passed without reading my rules

I wrote rules for the USB traces on a circuit board, and the design check passed for 16 days. It had never read them.

3 November 20265 min readTooling

In September I was designing a circuit board in KiCad, an open-source program for circuit board design. One USB cable connects the board to a computer, and the two copper traces that carry its data have to be drawn with care. So I wrote rules for them into the design, and KiCad’s check said the board broke none of them: 0 violations.

It kept saying 0 for 16 days, while I swapped USB parts and rerouted those two traces more than once. Then, as I got the board ready to order, I found that KiCad had never read my rules. Once it did, the same board broke them. The cause was one line in the rules file.

Two traces that have to stay together

USB 2.0 at high speed sends its data over two wires as a pair, and the receiver reads the difference between them. On a circuit board those wires are two copper traces, and they have to run side by side at a set gap for nearly their whole length. Where they spread apart, or run apart for too long, the signal degrades.

On this board the pair runs from the USB connector, past a small chip that guards the lines against static, into a USB hub chip. The hub passes the connection on to four small processors, so one cable can program all of them.

ComputerUSB cable
USB connector
The pair of tracespast a static guard chip
USB hub chip
Four processors
One cable reaches four processors through a hub chip. My rules cover the two USB traces between the connector and the hub.

KiCad’s design rule check measures every piece of copper on the board against a list of limits. They say how close two traces may get, how thin a trace may be and how far copper must stay from the edge. The check lists every place that breaks one. Those general limits come built in. What KiCad can’t know is that these two traces must stay 0.14 to 0.30 mm apart, a gap about one to three sheets of paper thick. They may also run apart for no more than 8 mm in all. That allowance goes to the few spots where they fan out to reach the pins of the connector and the chips. A project adds rules like that in a rules file of its own.

A finished board should come back with 0 violations. Anything else is a list of things to fix.

Zero violations, with the new rules

I added the two rules on 7 September. The check came back clean, and the commit that added them records it as passing, with no violations of any kind, “with the new rules”.

Over the next 16 days I kept changing the board. I went through a manufacturing review, swapped the USB connectors and the hub chip, and rerouted the pair more than once. The check kept coming back clean.

7 SeptemberRules added0 violations, recorded as a pass with the new rules.
8 SeptemberManufacturing review0 violations again.
8 to 11 SeptemberNew USB parts, and the pair reroutedStill clean on 11 September.
23 SeptemberThe rules loadThree violations on the pair, fixed the same day.
From the day I added the rules until 23 September, KiCad never read them. Through every change to the pair, the check came back clean.

One comment switched off the whole file

On 23 September, while I got the board ready to order from a factory, I found that KiCad had never read the rules file.

A KiCad rules file is a list of rules in parentheses, and a line that starts with # is a comment. Each rule has a name, a condition that picks the traces it applies to, and the limits. My condition picks the pair by name, since KiCad treats two traces whose names end in + and -, like HOST_D+ and HOST_D-, as a pair. A script writes my rules file along with the rest of the design, and the gap rule ended with a comment on the same line. It noted that where the traces spread out to meet a chip’s pins, the gap grows to 0.27 mm, still under the 0.30 mm maximum. Here is the rule, shortened, with the comment in plain words:

(rule "usb_hs_pair_gap"
  (condition "A.inDiffPair('HOST_D')")
  (constraint diff_pair_gap (min 0.14mm) (max 0.30mm)))   # 0.27 mm where the pair spreads out

KiCad 10.0.6 dropped the whole file over that comment, without a message, and ran the check with its built-in limits only. The board passed those. So the report said 0 violations, the same thing it would have said if both of my rules had run and passed.

The rules found three violations

The fix for the file was to move the comment onto a line of its own. I did that, and added a warning to the script that writes the file. It says to keep comments on their own lines, because KiCad silently drops the whole rules file when a comment follows a rule.

The next check read the rules and found three violations on the pair, and the fix for them was small. After the static guard chip, each trace drops through a via, a plated hole that carries a trace to another copper layer. One trace now turns earlier on the way, so the pair runs apart for a shorter stretch. Its via also moved a tenth of a millimeter closer to its partner’s, which keeps the traces leaving the two vias within the gap. After that the check read my rules, and the board passed them.

What I learned

Zero violations looks the same whether the rules ran or not. For 16 days the check measured the board against KiCad’s built-in limits only and called it clean. The commit on 7 September even recorded the pass as “with the new rules”. A pass told me something only once I could see that the check had read what I gave it.

A rules file fails as a whole. KiCad didn’t skip the one rule with a comment after it. It dropped the file, so one comment switched off every rule in it, with no message. That same day I added the factory’s limits for plated holes to the file, and with the comment still there they would have gone too.

Where it stands

The USB pair now passes its rules, and this time the check has read them. Since 23 September the board’s design checks have been clean, and it’s ready for a quote from the factory.

The only guard against a repeat is the warning in the script, and it covers only this one way to break the file. If KiCad drops the file for some other reason, the check will report 0 violations again, the same way it did for 16 days.

The board isn’t proven to work either. Nothing has been ordered or built yet, so the pair has passed every check on screen and never carried a signal. The tests that come next happen on a real board, with a USB cable plugged in.

Read next