← 에세이 /Post · 01 of 21 · Embedded Dev

LLM PCB 배선: 외주 엔지니어가 가르친 것

에이전트 등록부에 있는 PCB 배선 기법 16개 중 7개는 에이전트가 실행하지 못한다. 대부분 외주 엔지니어에게서 배운 것들이다.

·12 min read · · · #pcb#kicad#ai-agents#hardware#spoton
LLM PCB 배선: 외주 엔지니어가 가르친 것
On this page

PCB 배선을 LLM한테 맡겨봤다. 기판 위에 부품을 놓고 그 사이를 선으로 잇는 일이다. 잘 안 돼서 외주를 썼고, 돌아온 기판을 이전 것과 Net마다 비교해서 다음에 에이전트가 쓸 배선 기법 목록으로 정리했다.

기법 16개가 들어갔다. 항목마다 어느 편집기가 실행하는지 적는 칸이 있는데, 그중 7개에 gui_card가 적혀 있다. 그런 함수가 없어서 사람이 손으로 해야 할 때 쓰는 값이다. 이 기판을 살린 그 수가 일곱 중 하나다.

LLM은 숙련된 사람처럼 기판을 그리지 못한다. 외주 엔지니어와 두 라운드를 돌며 20만원으로 산 건 배선 실력이 아니라 어디서 못 하는지 적힌 목록이고, 목록은 기판보다 오래 남는다.

모델은 Fable 5다.

어떤 기판인가

지름 21mm 원형에 4층. CR1632 코인셀 위에 얹힌다. 안쪽 첫 층은 통째로 접지다. 세대별로 비교할 때 추적하는 Net은 15개.

쓸 용어는 세 개다. Net은 회로도에서 같은 신호로 묶인 선 한 가닥. 소프트웨어의 변수 하나쯤. 층을 갈아타려 뚫는 구멍이 via, 이대로 공장에 넘겨도 되는지 훑어주는 검사가 DRC다. 린터라고 보면 얼추 맞다.

그리고 이 기판은 마우스로 그리지 않는다. 파이썬 코드가 만든다. 규칙 파일의 상수는 이렇게 생겼다.

TRACK_MIN_MM = 0.127   # 5 mil
CLEARANCE_MM = 0.1     # tightened (still JLCPCB-capable) to open routing room
VIA_DRILL_MM = 0.3
VIA_DIA_MM   = 0.5

두 번째 줄 주석을 보라. 배선할 자리를 벌려고 간격을 좁혔다고 스스로 적어놨다. 나중에 벌어질 일의 절반이 이 한 줄에서 출발한다.

LLM이 뽑아낸 기판

처음 나온 기판의 DRC 위반은 143건. 진단 문서가 그중 약 120건, 84%를 “기계적 내용이 없는” 위반으로 분류했다. 잘못된 via 템플릿 하나에서 나온 90건(via 30개마다 규칙 3개씩), 미세 피치 간격 규칙 불일치 16건, 실크 화장 18건, 플레인 채움 3건. 나머지 8건은 진짜였다. 부품 점유 영역이 겹친 것들이다.

via 규격을 올리자 90건이 한 번에 0이 됐다. 원인을 진단 문서가 한 줄로 짚어놨다. 자동 배선기의 via 템플릿이 보드 via 규칙을 무시한다는 것. 그 템플릿이 뽑아낸 패드는 annular ring이 0.075mm라 우리 보드 규칙 0.10mm에 미달이고, 진단의 결론은 이 기판으로는 공장에 못 들어간다는 것이었다.

눈여겨볼 대목은 그 다음이다. DRC는 그 90건을 처음부터 정직하게 보고했다. 초록불이 거짓말을 했을까. 읽는 사람이 없었을 뿐이다.

그 수정에는 대가가 붙었다. via를 규격에 맞추자 굵어진 via 밭이 밀도 위반 약 34건을 새로 만들었고 연결이 안 끝난 Net도 6개에서 13개로 늘었다. DRC 위반 건수는 143에서 64로 내려갔는데 판정은 DENSITY_LIMITED가 됐다. 룰만 만져서 닿을 수 있는 바닥이 64였고, 처음 잡은 목표 20 근처에도 못 갔다.

숫자가 좋아지는 동안 판정이 나빠진다. 이 조합이 나온 자리가 자동 배선기의 끝이다.

DRC 0건을 만들 수 있었는데 안 만들었다

외주 의뢰서를 쓰면서 시험 삼아 해본 게 있다. 앞의 코드에서 0.1mm로 좁혀뒀던 전역 간격을 0.16mm로 도로 넓히면 넘기기 전 기판의 경계 위반 11건이 0이 된다. 대신 연결이 안 끝난 Net이 6개에서 15개로 뛴다. 다만 의뢰서는 그 숫자를 이전 리비전에서 쟀다.

배선을 포기해서 DRC를 통과시키는 셈이다. 안 썼고, 의뢰서에 그 이유를 그렇게 적어뒀다.

숫자 하나만 보고 있었으면 이 교환은 개선으로 읽힌다. 11건이 0건이 됐으니까.

전원선과 클럭선이 같은 굵기였다

제일 조용한 결함이 여기 있었다. 배터리 전원 Net VBAT, 접지 GND, SPI 클럭 SPI_SCK. 세 Net의 선 폭이 넘기기 전 기판에서 전부 0.2mm로 같았다.

코인셀 전원을 SPI 클럭과 같은 굵기로 그은 것이다. DRC는 이걸 잡지 못한다. 규칙을 어긴 게 없으니까. 돌아온 기판에서 VBAT는 0.25에서 1.0mm 사이로 계단이 생겼고 GND는 0.3에서 0.7 사이로 벌어졌다.

코드가 나보다 훨씬 잘한 것

부품 회전 조합을 전수로 훑는 일에서 코드는 나를 상대도 안 해준다. 여기서 일한 건 LLM이 아닌 for 문이다. 2의 14제곱, 16384가지를 다 돌려서 전체 Net 길이를 128.62mm에서 124.38mm로 줄였다. 3.3% 감소다.

사람은 저 탐색이 불가능하다. 대신 사람은 이 기판이 공장에 갈 수 있는 물건인지를 안다. 무식한 탐색에는 초인적이고 적법성에는 무력하다. 이 대비가 이 글 전체의 모양이다.

외주는 두 번 나갔고, 엄밀한 쪽이 빈손이었다

첫 번째는 제대로 설계한 실험이었다. 외부 엔지니어에게 우리 기판을 안 보여주고 독립적으로 레이아웃을 시켰다. 브리프에 못을 박았다. 숨긴 레이아웃을 맞추려 들지 말라고.

채점 기준은 일을 시작하기 전에 확정해서 봉인했다. 결과를 본 뒤에 기준을 쓰면 마음에 드는 쪽으로 기준이 휘니까, 먼저 적어놓고 나중에 손댔는지 확인할 수 있게 해뒀다. 파일 내용으로 지문 같은 문자열을 뽑아 따로 적어두는 방식이다. 한 글자만 고쳐도 그 문자열이 달라지니 나중에 대조하면 손댔는지 바로 드러난다. 우리 기판 파일도 같은 식으로 지문을 떠서 따로 보관했다.

돌아온 건 범위 밖의 회로도 재작성. 같은 날 추가된 범위 게이트가 채점 불가로 판정했다.

엄밀한 실험은 아무 비교도 만들지 못했다.

두 번째는 그냥 외주다. 배치는 확정이고 자동 배선으로 96%까지 간 상태이니 남은 Net 5개와 DRC 위반 11건을 손으로 마감하고 우리가 놓친 설계 문제를 지적해달라는, 공개된 유료 의뢰였다. 그쪽은 우리 기판을 봤다. 이건 블라인드가 아니고 실험이라고 불러선 안 된다.

성과가 나온 쪽은 두 번째다. 20만원을 지불했고, 엔지니어는 익명으로 둔다.

돌아온 걸 나란히 놓았다

Net 하나씩 뺐다.

Net넘기기 전돌아온 것비고
관성 센서 인터럽트 226.8mm, via 2개9.8mm, via 0개63% 감소
관성 센서 인터럽트 125.5mm11.8mm
모듈 리셋39.4mm14.4mm
디버그 데이터 (SWDIO)21.3mm14.6mm

이 Net 네 개 모두 넘기기 전 기판에서 이미 연결이 끝나 있었다. 자동 배선기 입장에서 손댈 이유가 없는 Net이다. 사람은 그걸 지우고 다시 그었다.

LLM이 배선을 끝낸 기판의 3D 뷰. 지름 21mm 원형 초록 기판에 긴 선들이 가장자리를 크게 돌아간다
LLM 작업본(Fable 5)
외주 엔지니어가 다시 그린 기판의 3D 뷰. 같은 기판인데 선이 짧게 끊겨 있고 실크스크린도 정리됐다
외주 엔지니어 수정본

둘 다 실제 보드 파일에서 KiCad 3D 뷰어로 렌더했다.

앞면만 보면 반쪽이다. 진짜 변화는 안쪽 2층에서 일어났다.

LLM이 배선한 기판의 안쪽 2층. 긴 직선 여섯 가닥이 판을 가로지른다
LLM 작업본, 안쪽 2층
외주 엔지니어가 다시 그린 기판의 안쪽 2층. 짧은 선이 훨씬 빽빽하게 들어차 있다
외주 엔지니어 수정본, 안쪽 2층

보드 파일을 파싱해서 세면 이 층을 쓰는 Net이 6개에서 8개, 선분이 12개에서 79개로 늘었다. 원래 여섯 중 셋만 나갔고 다섯이 새로 들어왔다. 층을 덜 쓴 게 아니라 다르게 썼다. 여기서 나간 셋에 디버그 Net 둘이 들어 있다.

전체 via는 37개에서 58개, 선분은 151개에서 282개가 됐다. 선이 짧아지는 동안 개수는 오히려 늘었다.

위반 11건이 사라진 이유, 그리고 내가 먼저 틀린 것

넘길 때 있던 hole_clearance 위반 11건은 돌아온 기판에서 0이 됐다. 저장소의 기법 문서는 그 공로를 via 지름 통일에 돌린다. 드릴은 그대로 두고 패드만 0.5mm에서 0.6mm로 키운 조치다.

나는 그게 틀렸다고 봤다. 파싱한 건 이렇다.

  • 위반 11건의 출처는 via 5개다. 같은 via가 여러 번 잡힌 것이다.
  • 실측 간격은 요구치 0.25mm에 0.0077에서 0.0483mm씩 모자란다. 의뢰서에 그대로 적혀 있다.
0.2017  0.2018  0.2092  0.2092  0.2112
0.2245  0.2248  0.2248  0.2280  0.2300  0.2423

걸린 지점은 C_SC1 패드↔INT_IMU via ×2, TP5 패드↔SWDIO via ×2, NRESET_MOD 트랙↔SWDCLK via ×3, VBAT 트랙↔INT_IMU via ×2, SPI_SCK/SPI_MISO 트랙↔GND via ×2.

  • 드릴 지름은 세 보드 모두 0.3mm다. 바뀐 건 패드뿐이다.
  • 문제의 via 5개는 돌아온 두 리비전 어디에도 없다. 좌표 0.05mm 안에 아무것도 없다.

여기서 패드만 키워선 안 된다고 결론냈다. hole_clearance는 구멍과 구리 사이 거리이니 구멍이 그대로면 달라질 게 없다고 본 것이다.

그 추론 자체는 이미 앉아 있던 11건에 대해서는 맞다. 틀린 건 그다음 걸음이다. 11건이 사라진 게 지우고 다시 그은 덕분이라고 넘겨짚은 것이다. 애초에 패드 크기가 왜 문제가 되는지는 내가 쓴 의뢰서가 설명한다. 이물 구리가 떨어져 있는 기준은 구멍이 아닌 패드 가장자리이고, 구리↔구리 규칙만큼 벌어져 있다. 그래서 구멍 기준 거리는 클리어런스에 annular ring을 더한 값이다.

freerouting은 구리↔구리 클리어런스(0.1mm)만 보고 KiCad의 홀↔구리 규칙(0.25mm)은 보지 않습니다. 비아는 패드 Ø0.5 / 드릴 Ø0.3이라 annular ring이 0.1mm뿐이어서, 구리를 패드 엣지에서 0.1mm 띄우면 드릴 기준으로는 0.2mm가 되어 0.05mm 모자랍니다.

패드를 Ø0.6으로 키우면 드릴이 0.3 그대로여도 annular ring이 0.15mm가 된다. 클리어런스까지 더하면 0.25mm다.

다만 이건 스윕 이후에 라우터가 새로 놓는 구리에만 성립한다. 패드를 키운다고 이미 기판에 앉아 있는 남의 구리가 움직이지는 않으니, 0.2017mm짜리 위반은 그대로 0.2017mm다. 스윕은 앞으로 이 부류가 다시 생기는 걸 막는다. 이미 있던 11건은 구리도 같이 움직여야 했고, 두 기전은 같은 재작업에서 함께 돌았다.

내 파싱이 증명한 건 그 다섯 자리의 구리가 움직였다는 것이다. 크기만 바뀌었을 뿐 위치는 그대로다. 그러니 스윕 하나로 11건이 사라졌을 수는 없다. 다만 누가 구리를 움직였는지는 부재로 갈리지 않는다. 일부러 지우고 다시 그은 것과, 새 via 규칙 아래 전체를 다시 배선한 것이 같은 흔적을 남긴다.

틀린 쪽을 지우지 않고 위에 남겨둔다. 틀린 모양이 흥미로운 부분이라서다. 실측은 진짜였고, 그게 받쳐주지 않는 인과를 끌어냈고, 기전을 설명하는 문단은 내 의뢰서에서 grep 한 번 거리에 있었다.

우리가 보낸 의뢰서도 틀려 있었다

외주가 r1에서 보고한 DRC 13건 중 10건은 우리 잘못이었다. 배터리 패드가 금지 구역 안에 앉아 있는데 “선 넣지 말 것”을 요구했으니 애초에 만족이 불가능한 주문을 낸 셈이다. 패드 안 via를 허용한다고 우리가 적어두지 않았다. 3건은 우리 저장소에 없던 부품 파일 탓으로 나왔다.

사람이 “찾아낸” 것 일부는 우리 주문서의 결함이었다. 기판은 멀쩡했다.

기법으로 뽑았다

기판은 이미 완성됐다. 이 보드를 위해 더 할 건 없었다. 목적은 하나였다. 다음 시제품에서 에이전트가 같은 실수를 반복하지 않게 만드는 것.

그래서 LLM에 완성된 기판을 주지 않았다. 위의 표를 줬다. Net마다 길이와 구멍 수와 층을 두 기판에서 뺀 차이. 돌이켜보면 이 글에서 실제로 작동한 부분은 거기다.

넘기기 전에서 돌아온 것으로 가는 구간 하나를 훑어 PCB 배선 기법 12개가 나왔다. 에이전트가 되읽는 등록부에 들어갔고, 이후 실행에서 더 나와 지금은 131줄에 기법 16개다. 다 늘어놓진 않겠다. 두 개면 된다.

T2, 지우고 다시 긋기. 위 표의 Net 네 개가 전부 이것의 결과다. 이 글에서 가장 값어치 있는 수인데, 목록에서는 두 번째이고 바로 위가 T1: via-size-normalization이다. 방금 내가 잘못 무너뜨리려 했던 그 항목이다.

T2의 editor 칸에는 gui_card가 적혀 있다. 파일에서 제일 값어치 있는 수를 에이전트가 실행하지 못한다는 뜻이다.

이걸 기계가 자기에 대해 뭔가 인정한 것으로 읽고 싶어진다. 아니다. 스키마 주석이 그 칸이 뭔지 못 박아뒀다. editors MUST name real routing_editors functions. 구현된 함수 이름만 들어가는 디스패치 표고, gui_card는 그런 함수가 없을 때 적는 값이다. 같은 값이 붙은 나머지 여섯에 silk-nudge가 있다. 실크스크린 글자를 미는 작업이다. 기계 능력의 경계에 대해 심오한 고백이 이뤄지는 게 아니라, 그냥 아무도 그 함수를 안 짠 것이다.

개수가 말해주는 쪽이 더 값어치 있다. 16개 중 7개, 절반 가까이가 사람이 손으로 여는 카드로 가고 최고가치 기법이 그 안에 있다. 두 개는 편집기 이름이 아예 없는데, 이유가 반대다. 판단을 멈추는 것도 배치 입력도 편집 자체를 하지 않는다.

T12, 모르는 경계에서는 찍지 말고 손을 뗄 것. 신뢰도 하한이 0.6으로 걸려 있는데 어떤 항목은 0.5로 매겨진다. 하한을 못 넘으면 분류 단계가 기법을 고르는 대신 멈추고, 발주 사양을 들고 있는 사람에게 넘긴다.

정답지 없이 다시 시켰다

PCB 배선이 전이되는지 보는 시험이다. 출발점은 외주 엔지니어의 1차 기판에 우리 수정 하나를 얹은 것, 이미 배선이 끝나고 DRC도 깨끗한 상태다. 거기서 2차 재작업을 정답지 없이 재현시키고 채점한다.

채점 기준 셋은 정답지에서 미리 뽑아 기법 12개에서 일부러 빼놨다. 가르친 걸 따라 하는 건 시험이 아니니까.

기준얼려둔 임계실측판정
1번. 클럭 선이 칩 패드 줄 사이를 파고듦가장자리 간격 0.15mm 미만 금지패드 두 곳에서 0.105 / 0.115mm사람에게 넘김
2번. 디버그·시리얼 구멍 기둥이 서로 파고듦SWD·UART 구멍 구역 안에서 신호 구멍 중심 간 1.0mm 이상가장 가까운 쌍 1.031mm기준은 통과
3번. 클럭·데이터가 남의 절연 영역을 스침구멍 중심에서 0.58mm 이상위반 0건정답 없이 해결

2번과 3번은 정답을 안 보고 풀렸다. 다만 2번의 통과는 그대로 믿으면 안 된다. 가장 가까운 쌍이 1.031mm라 얼려둔 기준 1.0은 넘겼는데, 같은 검토가 두 절연 영역이 물리적으로 붙지 않으려면 1.25mm가 필요하다고 유도해놨다. 채점표는 통과, 물리는 미달이다. 잔여분은 사람 GUI 작업으로 갭 목록에 올라가 있다.

1번은 사람 손으로 넘어갔다. 분류 단계는 패드 밀집을 T7에 대응시키는데, T7gui_card 항목이라 편집기 함수가 없는 쪽으로 기준이 향한다. 결과 문서는 이 대응이 처음부터 잡혀 있었다고 적는다. 나는 그걸 확인하지 못한다. git에 가장 먼저 커밋된 등록부가 채점 이후 것이고, 그 안의 추천 목록에 T15가 들어 있다. 채점 때 정답지에서 캐낸 기법이다. 그러니 대응은 그럴듯하지만 확인되지 않았고, 에스컬레이션은 나중에 적혔다.

gui_card 7개는 그냥 없는 함수 목록이다. 결과 문서도 이 대응이 결과와 부합한다고만 적어놨고, 기록상으로는 거기까지다.

첫 기판과 견줘보면 차이가 있다. 위반 90건이 내 화면에 열려 있는 DRC 리포트 안에 앉아 있었고, 나는 몇 주 뒤 진단을 돌려서야 찾았다. 이번엔 못 하는 것이 이름을 달고 나왔다.

정답 안 보고 2/3. GUI 작업 지시 한 장이 나왔고 나는 그걸 손대지 않았다. 이후 실행이 그 카드를 헤드리스로 처리하고 세 조건을 다 통과했는데, 돌아온 기판을 본 뒤라 블라인드가 깨져서 세지 않는다. 내 0시간은 사람의 효율과 무관하다. 에이전트가 답을 본 순간 경계가 움직여서 생긴 숫자다.

진 것과 모르는 것

외주 엔지니어는 디버그 선을 구멍 없이 다시 그어 안쪽 2층에서 빼냈다. 내 재현본은 그걸 하지 못했다. 낮을수록 좋은 참조 커버리지 지표에서 재현본 100 대 사람 92로 8점 뒤졌다.

대조군이 없다. 같은 에이전트를 이 기법 없이 한 번 더 돌린 실행이 없다. “비교 대상을 줬더니 되더라”의 인과를 가르는 실험이 정확히 그건데 하지 않았다.

기법을 뽑은 기판과 재현을 증명한 기판이 같다는 것도 걸린다. 둘 다 같은 시제품이다. 저장소의 남은 구멍 목록이 이걸 “같은 보드 계열 과적합”으로 적어뒀고, 진짜 이전 증명은 다음 시제품의 첫 배선 세션이라고 못 박았다.

여기까지 확인된 건 같은 기판에서 재현된다는 것뿐이다.

헛발질도 이 목록에 있다

커밋 로그에서 몇 개 옮긴다.

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

배선 스크립트가 애초에 실행조차 안 되고 있었다. 일찍 죽은 스크립트의 출력을 계속 읽고 있었던 건데, 언제부터였는지는 모른다. 에이전트가 “Ø21에는 자리가 없다”고 단언한 제약은 재보니 틀렸다. 라우팅 근거를 다시 유도한 커밋에는 계측기가 리포에 없었고 채택 근거가 바뀐다고 적혀 있다.

지금 상태

기판 5매를 4층 FR-4 0.8mm, 지름 21mm, ENIG로 발주했고 아직 아무것도 안 왔다. 첫 발주는 아예 틀렸다. 0.5mm 피치 14패드 LGA를 밑면 단자로 시켰는데 내 책상에서 납땜이 안 되는 물건을 시켰다.

게다가 발주본은 이 스킬이 생기기 전 산출물이다. 스킬의 첫 실전은 아직 오지도 않았다.

여기까지는 배선 과정의 기록이고, 기판이 동작한다는 증거는 아직 없다.

어디서 사람을 불러야 하나

DRC 위반 건수만 보면 놓친다. 그 숫자가 내려가는 동안 연결이 안 끝난 Net이 늘어나면 거기가 경계다. 앞에 적은 0.16mm 교환이 그 자리다. 위반 11건이 0이 되는 대신 미배선이 6개에서 15개로 뛴다(이전 리비전 기준). 그 경계를 넘기는 데 든 값이 20만원이다.

집계에 아예 안 뜨는 경계도 있다. 전원 Net과 클럭 Net이 같은 0.2mm 폭으로 그어져 있던 것. 규칙을 어긴 게 없으니 DRC는 조용하다. 어느 대시보드에도 뜨지 않는다.

AI로 하드웨어를 해보는 중이라면, 초록불을 공장 문으로 쓰지 마라. 위의 두 경계가 DRC가 못 잡는 걸 잡아내는 방법이다.

이 일에서 기판 말고 남은 게 둘이다. 등록부, 3월의 나한테 없던 물건이다. 그리고 첫 기판 뒤에 적은 하드웨어 체크리스트의 한 줄. 거짓 초록불로는 절대 출하하지 않는다. 돈이 나가는 주문 전에 fab.gate_ok를 교차 확인한다. DRC 통과는 필요조건이지 충분조건이 아니다. 한동안 초록불을 출하 문으로 쓰고 있었으니 생긴 규칙이다.

외주를 줄 거면 결과물을 이전 버전과 나란히 놓을 수 있는 형태로 받아라. Net마다. 등록부가 바로 그 표에서 나왔고, 표가 없으면 되먹일 것도 없다.

다음 시제품에서 저 일곱이 줄어드는지, 내가 볼 숫자는 그거 하나다.

ecro
글쓴이 ecro

LLM 펌웨어 벤치마크를 만들었습니다. EmbedEval →

프로젝트에 맞는 Claude Code / Cursor / Codex 하네스를 생성하는 도구를 만듭니다. harness-maker →

Datasheet을 읽는 터미널을 만들고 있습니다. NeuroTerm →

댓글

댓글 불러오는 중...