← Essays /Post · 07 of 28 · Methods

LLM PCB Routing: What I Learned From a Human's Fixes

I handed a 96% auto-routed PCB to a contract engineer. He redrew finished routes, not only the last 4%. I kept that difference as 16 rules for my agent.

· updated ·16 min read · · · #pcb#kicad#ai-agents#hardware#build-in-public
LLM PCB Routing: What I Learned From a Human's Fixes
On this page

Lately I have been designing a 21mm circular PCB with an LLM and code.

Placement and auto-routing got me to about 96% of the connections. The last 4% was where it stuck. A few nets never connected, and some DRC violations remained. So I paid a PCB engineer KRW 200,000, about USD 145, to finish it.

The board that came back was not quite what I expected. He did not just connect the remaining 4%. He deleted routing the auto-router considered finished and redrew it, moved nets to other layers, added vias, and reorganized the whole topology.

So I laid the two PCBs side by side and compared them net by net. I wanted to find what he had done differently and turn that difference into routing rules the agent could reuse on the next board. Sixteen techniques have come out of it so far. Seven of them still cannot run automatically.

This is not a story about an LLM laying out a good board. It is a record of how far the automation got and where human judgement was still required.

I used Claude Fable 5 for the routing decisions.

The board

The board is a 21mm circular four-layer PCB. It sits on top of a CR1632 coin cell, so there is very little room to route. One inner layer is a GND plane, and I followed 15 key nets to compare the revisions.

Three terms come up often here, so I will set them down first for anyone who does not work with PCBs.

  • Net is a set of points that must be electrically connected. The SPI_SCK net, for instance, includes the MCU’s SCK pin, the sensor’s SCK pin, and every trace and via between them.
  • Via is a plated hole used to carry a connection from one copper layer to another.
  • DRC (Design Rule Check) checks whether the PCB obeys the design rules you configured. It is close to a linter.

One thing about DRC matters for everything that follows. DRC only checks the rules that are registered. It does not judge design intent. Several of the problems in this post come out of that gap.

This PCB is not drawn by hand in KiCad. Python code produces the placement and routing data, and an auto-router generates the traces. The main routing rules I was using then looked like this.

TRACK_MIN_MM = 0.127
CLEARANCE_MM = 0.1
VIA_DRILL_MM = 0.3
VIA_DIA_MM   = 0.5

CLEARANCE_MM = 0.1 in particular was a value I tightened on purpose, to buy any routing room at all inside 21mm. When I traced problems back later, several trade-offs led to that one decision.

How far the auto-router got

The first auto-routing run came back with 143 DRC violations. The count alone looked like total failure. Looking closer, most of it was one cause flagged over and over.

The biggest cause was the via template. The auto-router produced vias with a 0.075mm annular ring, while the board rules required at least 0.10mm. That mismatch repeated across many vias, and this one item generated 90 violations. The cause was simple. The via spec the auto-router used and KiCad’s board rules did not match.

Bringing the vias in line with the board rules cleared those 90. Tidying other rules along the way brought the total down to 64. Up to that point it was an improvement.

Then the larger vias caused a different problem. They took up more of the space inside 21mm, routing density went up, and the nets that never finished connecting went from 6 to 13.

It was hard to say the board was better off. The DRC count fell while the unconnected nets rose. I classified this state internally as DENSITY_LIMITED.

It means nudging rule values further will not solve this. That was the first boundary the auto-router ran into.

A better DRC count can mean a worse board

Before the handoff I tried one more setting. I widened the global clearance from 0.1mm to 0.16mm and ran the auto-router again. The 11 boundary-related DRC violations left at the time went to zero. Unconnected nets, meanwhile, went from 6 to 15.

The DRC count improved, and a better design was not the reason. It improved because more routing was abandoned. If DRC had been my only KPI, 11 down to 0 would have looked like a clear win. So I did not use that setting.

Some of the numbers in that comparison come from an earlier revision, so the direction of the trade-off says more than the absolute values. One thing was clear all the same. A DRC count alone cannot evaluate routing quality.

Some problems never show up in DRC at all

A more important problem never showed up in DRC at all. On the board I handed off, three nets, VBAT, GND, and SPI_SCK, all had a 0.2mm trace width. Battery power, ground, and the SPI clock were all on the same routing policy. The number 0.2mm is not necessarily wrong on its own, since the right width depends on current, trace length, copper thickness, and allowed voltage drop. What I saw as the problem was that the auto-router did not distinguish between the roles of the nets.

On the engineer’s revised board, VBAT widened to between 0.25 and 1.0mm depending on the segment, and GND used 0.3 to 0.7mm.

The original 0.2mm routing broke no DRC rule, so nothing was reported. DRC finds rule violations. It does not decide whether a net carries power or a clock. That was the second boundary.

What code did far better

Humans were not better at everything, though. Searching component orientations is something code did far better.

With 14 components and two rotations each, there are 2^14 = 16,384 combinations. Python computed every one and picked the placement with the shortest total net length. Estimated routing length fell from 128.62mm to 124.38mm, about 3.3%.

Nobody is going to compare 16,384 arrangements by hand. For that kind of search, code has an overwhelming advantage.

Still, the placement with the shortest total net length is not automatically the best PCB. Whether the power routing is sound, whether the board can be manufactured, whether an already-connected net would be better ripped out and sent to another layer: those are separate judgements. Working through this made the division of labour a little clearer to me. Code was good at search, and the human was better at judgement that changes topology.

Handing off at 96% auto-routed

By then placement was frozen and auto-routing was about 96% done. I asked a PCB engineer to finish the remaining 5 nets and 11 DRC violations, and to point out any design problems I had missed. The cost was KRW 200,000.

I gave him my PCB as it was. So this was never a blind test or a human-versus-AI benchmark. It was ordinary contract work to finish a real board. Looking at what came back, though, it was worth comparing.

He did not just connect the last 4%

I parsed both PCB files and compared trace length, via count, and layer usage for each net. Here are a few of the nets with the largest differences.

NetBeforeAfterTrace length change
IMU interrupt 226.8mm, 2 vias9.8mm, 0 viasdown 63%
IMU interrupt 125.5mm11.8mmdown 54%
Module reset39.4mm14.4mmdown 63%
SWDIO21.3mm14.6mmdown 31%

What matters here is something else. All four of these nets were already connected before the handoff. As far as the auto-router was concerned, they were done. He did not see it that way. He deleted the existing traces and redrew them, a move I later classified in the routing techniques as rip-up & reroute.

3D view of the board the LLM routed. A 21mm round green board with long traces sweeping around the edge.
Before, the auto-routed result
3D view of the board the contract engineer redrew. Same board, but the traces are short and the silkscreen is tidied.
After, the engineer’s revision

So the engineer did more than finish the last 4%. He went back through part of the 96% I thought was done.

Layer usage was different too

The difference is hard to see from the front of the board. Putting the inner routing layers side by side makes it clearer.

Inner layer 2 as the LLM routed it. Six long straight runs cross the board.
Before, inner layer
Inner layer 2 as the contract engineer redrew it. Short traces packed far more densely.
After, inner layer

Before the handoff, 6 nets used this layer, across 12 trace segments. Afterwards, 3 of those 6 had dropped out and 5 new ones had come in, for 8 nets on the layer. Segments went from 12 to 79.

Across the whole PCB, vias went from 37 to 58 and trace segments from 151 to 282. At a glance it had become more complicated. Yet the actual paths of the key nets above had become much shorter.

He was not trying to minimize via count or segment count. He spent more vias and more segments where they were needed while rebuilding the whole topology around which net belongs on which layer. The auto-router and the human had been optimizing for different things.

How those 11 hole clearance violations went away

The 11 DRC violations left at handoff were all hole_clearance. Only 5 vias caused them, and the same via colliding with several pieces of nearby copper added up to 11. The required hole clearance was 0.25mm, and the measured values ran from about 0.2017 to 0.2423mm.

On the returned board, all of them were gone. The via spec had changed too. The drill stayed at Ø0.3mm, but the pad grew from Ø0.5 to Ø0.6mm.

At first I read the relationship wrong. hole_clearance is the distance between the drill hole and other copper, so with the drill unchanged, growing the pad alone cannot clear an existing violation. That much I had right.

Pad size does, however, affect whether the same violation gets created again on the next routing pass. The original via looked like this.

pad Ø0.5
drill Ø0.3

annular ring = (0.5 - 0.3) / 2 = 0.10mm

If the auto-router only guarantees 0.1mm copper-to-copper clearance, the distance from the drill edge to other copper works out like this.

0.10mm annular ring + 0.10mm copper clearance = 0.20mm

That is 0.05mm short of the 0.25mm KiCad asked for. Grow the pad to Ø0.6mm and the annular ring becomes (0.6 - 0.3) / 2 = 0.15mm. Add the 0.1mm clearance and it comes to exactly 0.25mm.

So growing the via pad does mean something, as a way to stop producing the same class of violation when routing new copper. But copper already sitting 0.20mm away does not move because a pad got bigger. To clear the existing 11, the copper in those spans had to be placed again as well.

When I compared the actual PCB files, the routing geometry at all five offending locations had changed. The files cannot tell me whether the engineer deleted and redrew those spans by hand, or whether they changed during a reroute under the new via rule. The data goes that far and no further.

Our own brief was wrong too

Of the 13 DRC problems the engineer reported first, 10 started with the requirements we sent rather than with the PCB. The battery pad itself sits inside a particular keep-out area, and we had written that no copper may go there. We never said via-in-pad was an allowed exception, so taken literally, the requirement could not be met. The other 3 came from his environment lacking the same component and footprint files we use.

Some of what the human “found” was not a flaw in the design but a flaw in the specification and the environment. When this gets automated, that distinction has to be kept as well.

Turning his fixes into the next set of rules

The contract work finished the PCB itself. After that my interest moved somewhere else. What will the agent do when it meets the same problem on the next board?

So I did not stop at storing his revision as an answer key. I parsed the before and after PCBs and pulled out, for each net, how the trace length, via count, layer used, and routing topology had changed.

From those differences I built reusable routing techniques. There were 12 at first, and later work has brought it to 16. Two of them matter most as examples.

  • T1: via-size-normalization. Bring the via spec the auto-router uses in line with KiCad and the fab rules.
  • T2: rip-up-and-reroute. Even for an already-connected net, if the topology is bad, delete the existing routing and route it again. All four nets with the large length reductions above are T2.

The trouble is that the agent cannot execute T2 itself. The technique registry records which function performs each operation. An operation with no function to run yet gets the value gui_card, which hands it to a human. Seven of the 16 registered techniques are in that state.

And rip-up-and-reroute, the technique that did the most in this whole engagement, is one of the seven. Described honestly, the current level of automation comes to this. It could learn them, but close to half of them it still cannot execute.

I added one more principle. When the agent’s confidence in a judgement falls below a threshold, it does not force a routing technique. It hands off to a human instead of guessing and modifying the PCB. In hardware, knowing where to stop automating sometimes matters more than automating wrong.

Testing whether the learned rules get reused

The question after that was this one. Do the rules extracted from the contract work end as a one-time explanation, or can the agent reuse them?

To find out, I had the agent handle three problems of the same kind without showing it the engineer’s final revision. I fixed the pass conditions in advance.

CheckThresholdResult
Does a clock trace run too close between padsedge clearance at least 0.15mm0.105 / 0.115mm, handed to a human
Are the vias around SWD/UART too denseat least 1.0mm centre to centrepass, 1.031mm at the closest
Do clock/data intrude on another via’s clearance zoneat least 0.58mm from the via centrepass, zero violations

By the thresholds fixed in advance, two of the three passed. I still find it hard to call that 2/3.

The second check cleared the 1.0mm pre-set threshold at 1.031mm. When I recomputed the geometry afterwards, though, the two clearance zones turned out to need about 1.25mm to actually stay apart. The test passed, and the test itself had not been strict enough.

The first check the agent did not fix at all. It handed it to a human, and I cannot call that a clean success either.

Something had changed from before, though. On the first auto-routing run, the problem sat right there in the DRC report and I only understood what it meant much later. This time the condition the agent could not solve surfaced explicitly as manual work. I think that difference counts for something.

There is a limit here too. For some of the techniques and classification rules, git history alone cannot fully show that they existed before the test. So I did not count that part as evidence of learning.

The human is still better at some of it

When the engineer redrew the SWD traces, he removed the vias and used less of the inner layer. My replay did not get that far.

There is a more important limit. There is no control group. I never re-ran the same agent without the new routing techniques, so I cannot causally prove that the improvement came from them.

The PCB the techniques came from and the PCB the replay ran on are also the same product family. So what I have confirmed is that some judgements reproduce within the same PCB family. I do not yet know whether the techniques work on a different board. The real transfer test has to happen on the next prototype.

The automation itself was wrong several times

The routing was not the only thing that went wrong. At one point the routing script itself was not running at all. The commit log still carries this line.

route.py did not run at all — SWIG ownership trap, plus two clobber hazards

Because of a SWIG object ownership problem, route.py never ran, and for a while I was analyzing leftovers from an earlier run as if they were current results. I was analyzing a stale artifact. The agent had also asserted that “there is no more room inside Ø21mm”, and that turned out to be wrong when I measured the actual geometry again.

Looking back, I needed more than PCB automation. I also needed instrumentation that verifies the automation actually ran, and that the basis for a judgement matches the real geometry.

These failures went into the routing techniques and the checklist as well. If I record only what worked, nothing stops the same failure from happening twice.

No evidence the board works yet

Five PCBs are on order: four-layer FR-4, 0.8mm, Ø21mm, ENIG. None has arrived yet.

The first order also skipped an assembly review. I picked a 0.5mm pitch 14-pad LGA package while planning to assemble the board myself. Routing aside, that is a part my bench cannot handle.

There is a more important fact. The boards on order were made before the routing technique registry reached its current form. So this workflow has not yet built a new PCB from start to finish.

What I have confirmed so far is the routing process, and only that. Whether the board boots on power-up, whether the sensor works, whether RF and power are clean: none of it is known yet. Those answers will come off the bench once the boards arrive.

So where do you call a human

In this work, a few signals told me more than the DRC count did.

  • DRC falling while unrouted nets rise. Widening clearance cleared 11 DRC violations, but unrouted nets went from 6 to 15. The number improved and the routing got worse.
  • Nothing wrong in DRC, but net roles not reflected. VBAT, GND, and the SPI clock all routed on the same 0.2mm policy is the example.
  • The agent not confident enough about which technique to use. In that case it does not guess and edit; it hands off to a human.

So I no longer use DRC as the final verdict. Passing DRC is necessary but not sufficient. Before fabrication I check a separate manufacturability gate alongside it.

If you use a contractor, getting the result in a form you can diff against the previous revision is worth far more than receiving a finished PCB file. The routing techniques here came out of exactly that difference.

Seven of the 16 techniques still need a human working in a GUI. The number I most want to see on the next PCB is not DRC zero. It is how far those seven come down. That number will tell me whether this automation is actually getting better.

ecro
Written by ecro

Created an LLM benchmark for firmware. EmbedEval →

Building a project-shaped agent harness for Claude Code, Cursor, and Codex. harness-maker →

Building a serial and SSH terminal where coding agents work on the board beside you. NeuroTerm →

Comments

Loading comments...