Altium PCB 배치
배치는 두 종류가 섞여 있다. 섞어서 다루면 둘 다 망한다.
| 성격 | 처리 | |
|---|---|---|
| 회전·사이즈 | 데이터에서 유도된다. 정답이 하나다 | 계산해서 근거와 함께 제시 |
| 어느 변에 뭘 둘까 | 사용자 취향·기구 제약. 정답이 없다 | 물어보고 그림으로 수렴 |
하나만 지킨다면 이것
가안은 반드시 그림에서 수렴시킨 다음 Altium 에 넣는다. 그림은 싸고 PcbDoc 은 비싸다.
가안은 2~4회 바뀐다고 보고 시작한다. 한 번에 맞지 않는다. 주로 바뀌는 것은 이 넷이고, 전부 §0 에서 미리 물으면 줄일 수 있다.
- 큰 커넥터를 어느 변에 둘지
- 커넥터를 한 변에 몰지, 기능별로 흩을지
- 주요 IC 의 회전 기준을 무엇으로 잡을지
- 보드 크기 목표
배치 하나하나에 이유를 댈 수 없으면 그건 아직 가안이 아니다. "왜 여기?" 에 "딱히" 라고 답할 부품이 있으면 그만큼 보드가 크다.
0. 사용자 제약을 먼저 캔다 — 여기서 회차가 갈린다
계산 전에 물어본다. 나중에 알면 전부 다시 그린다.
- 큰 커넥터는 어느 변인가. 방향이 보드 종횡비를 통째로 결정한다
- 커넥터를 한 변에 몰 것인가, 기능별로 흩을 것인가
- 결합 상대가 있는가 — 상대 보드가 위로 겹치는지 옆으로 나가는지. 라이트앵글이면 대개 평면 연장이라 높이 간섭이 없다. 앵글/스트레이트를 확인한다
- 기구 제약 — 케이스, 고정 홀, 높이 제한
- 보드 크기 목표가 있는가
1. 제약 수집
python scripts/connectivity_matrix.py <SchDoc> --ref <기준부품>
- 부품쌍 넷 수 — 존 분할의 1차 근거. 16넷으로 묶인 두 부품은 붙여 놓는다
- TP 소속 — 테스트포인트를 어느 부품 옆에 둘지
- 기준부품 핀별 상대 — §2 회전 결정의 입력값
파이썬은 altium_monkey 가 설치된 3.12 venv 인터프리터를 쓴다
(이 패키지가 Python <3.13 을 요구한다). 아래에서는 그걸 python 이라고 쓴다.
치수는 라이브러리에서 잰다. PCB 는 필요 없다
python scripts/measure_from_lib.py <SchDoc> --libs <라이브러리폴더>
회로도가 참조하는 풋프린트 이름을 뽑아 .PcbLib 에서 bbox 를 잰다.
PcbDoc 도, Update PCB 도 필요 없다. 외주 PCB 설계업체에 넘길 가안처럼
애초에 PCB 문서가 없는 경우가 오히려 흔하다.
| 출처 | 얻는 것 | 비고 |
|---|---|---|
라이브러리 .PcbLib |
풋프린트 bbox·패드·홀 — 원본 | 기본 경로 |
| PcbDoc (있으면) | 같은 값 + 현재 배치·회전 | 복사본이다 (아래) |
| 벤더 도면 PDF | 몸체, 레버 스윙, 높이, 볼트홀 — 풋프린트에 없는 것 | altium-library measure_drawing.py |
PcbDoc 은 풋프린트를 복사해 들고 있다. 라이브러리를 고쳐도
Tools ▸ Update From PCB Libraries 전까지 PcbDoc 은 옛 모양 그대로다.
실측 17건 중 2건이 이것 때문에 0.2~0.5mm 어긋났다. 라이브러리가 원본이다.
라이브러리가 없으면 데이터시트로 간다 — 추정으로 가지 않는다
PCB 외주 설계업체를 쓸 때는 풋프린트 라이브러리를 아예 안 만드는 게 흔하다. 그 경우가 배치 가안이 가장 필요한 경우이기도 하다. 라이브러리 없음은 정상 상황이지 포기 사유가 아니다.
부품마다 이 사다리를 위에서부터 내려간다. 맨 아래로 떨어지지 마라.
| # | 출처 | 얻는 것 | 표시 |
|---|---|---|---|
| 1 | 라이브러리 .PcbLib |
실제 랜드+실크 | 실측 |
| 2 | 데이터시트 권장 랜드패턴 / PCB layout 뷰 | 랜드 실치수 → 코트야드만 더함 | 실측 |
| 3 | 데이터시트 몸체 치수표 (D/E/e/b/L) | 랜드를 유도 → 코트야드 | 유도값 |
| 4 | 패키지 이름만 (QFN32 4x4) |
IPC-7351 표준 랜드 | 유도값 |
| 5 | 추정 | — | 금지 |
라이브러리 경로 찾는 순서: 프로젝트 폴더 → Altium 워크스페이스
(%APPDATA%\Altium\Altium Designer {GUID}\LastWorkspace\*.DsnWrk 안에 .LibPkg
절대경로가 그대로 있다) → 사용자에게 묻는다. 전체 드라이브 find 는 타임아웃 난다.
몸체 치수를 그대로 쓰지 마라
데이터시트 치수표의 D/E 는 몸체다. 보드가 먹는 면적이 아니다.
랜드패턴이 몸체 밖으로 나오고, 그 위에 코트야드와 실크가 얹힌다.
실측 대조 — 몸체 대비 실제 풋프린트가 얼마나 커지는가:
| 부품 | 몸체 | 라이브러리 풋프린트 |
|---|---|---|
| QFN32 | 4.00 × 4.00 | 7.05 × 7.00 |
| SOT-223 | 6.50 × 3.50 | 8.83 × 7.25 |
| 0805 | 2.00 × 1.25 | 3.90 × 1.90 |
| 0603 | 1.60 × 0.80 | 2.30 × 1.10 |
일정한 배수가 없다. 라이브러리·업체마다 실크 여유가 달라서 단일 계수 환산은 틀린다. 그러니 3~4단계로 잡은 값은:
- 랜드패턴을 먼저 유도하고, 그 위에 코트야드를 더한다 (유도 규칙은 IPC-7351 확인)
- 가안
label에 「유도값」 이라고 적는다 - 배치 여유를 더 준다. 나중에 실물 풋프린트가 크면 다시 그려야 한다
공칭·기억으로 잡은 값이 실측과 얼마나 벌어지는지 (실측 대조):
| 부품 | 공칭으로 잡기 쉬운 값 | 실제 풋프린트 |
|---|---|---|
| QFN32 | 4 × 4 (몸체/다이 크기) | 7.05 × 7.00 — 43% 과소 |
| DC 잭 | 14 × 11 | 9.90 × 14.40 — 폭·깊이가 뒤집힌다 |
| DIP 스위치 4연 | 10 × 6 | 12.34 × 10.90 |
크기보다 나쁜 건 방향이다. 실측을 미루면 핀이 어느 쪽으로 나오는지까지 기억으로 추론하게 되고, 그건 재보면 반대인 경우가 있다. 30초짜리 측정이다.
벤더 도면 실측이 정확했다고 나머지도 괜찮을 거라 넘기지 마라 — 그게 이 사고의 경로였다.
몸체와 금지구역은 다르다. 테스트 소켓은 레버가 도는 범위가 몸체보다 크고, 그 밑에는 테스트포인트를 못 깐다(프로브가 안 들어간다). 실측 예: 몸체 36×36, 레버 포함 46.2, 높이 31.7 — 이 세 값이 전부 다른 제약이다.
2. 회전 결정 — 이 스킬의 핵심
QFN/QFP 는 핀번호가 변을 결정하므로 회전이 순수 계산으로 나온다.
python scripts/pin_side_map.py --pins-per-side 16 --start left --dir ccw \
--group "MII=1-12,61-68" --group "BUS=14-22,25,32,33" \
--want "MII=NE" --want "BUS=W"
--start / --dir 는 본드 도면이나 패키지 도면으로 확인해서 넣는다.
추정하면 결과가 통째로 뒤집힌다. 자세한 절차와 실증 예는 references/rotation-decision.md.
출력은 회전 0/90/180/270 각각에서 각 신호 무리가 어느 방향으로 나오는지다. 코너(NE/NW/SE/SW)로 나온 무리는 그 대각에 상대 부품을 놓으면 교차가 0 이 된다.
커넥터는 이 방법으로 안 나온다. 커넥터에 필요한 건 핀 매핑이 아니라 개구부가 어디를 보는가 다. 「패드 bbox 대비 실크가 더 튀어나온 쪽」이 개구부고, 그 방향이 보드 바깥을 향해야 한다.
python scripts/connector_facing.py <SchDoc> --libs <라이브러리폴더>
bbox 는 180° 회전으로 안 변하므로 겹침·외곽 검사에 안 걸린다. 배치를 끝낸 뒤 이 표와 실제 회전을 따로 대조한다. 안 하면 개구부가 보드 안쪽을 향한 채로 넘어간다.
이건 사용자에게 물어볼 일이 아니라 계산 결과를 근거와 함께 제시할 일이다. 비교표를 그대로 보여준다 — 왜 다른 3개가 아닌지가 같이 보여야 납득된다.
3. 보드 사이즈
축별로 치수를 누적해서 역산한다. 어림하지 않는다.
X: 커넥터 온보드 깊이 + 라우팅 채널 + 소켓존(금지구역+TP뱅크) + 주변IC + 여유
Y: (그 축을 지배하는 부품 하나) + 양끝 여유
한 축은 대개 부품 하나가 강제한다(긴 커넥터 등). 그 축은 못 줄인다.
다른 축이 자유롭다 — 거기서 낭비를 찾는다. 자세한 건 references/board-sizing.md.
모든 배치에 이유를 대본다. "왜 여기?" 에 답이 "없다" 인 부품이 하나라도 있으면 그만큼 보드가 크다. 커넥터 하나를 이유 없이 대각선에 두면 그 축이 30mm 커진다.
4. 가안 도면 — 반복하는 단계
python scripts/plan_svg.py plan.json -o <출력경로>
plan.json 스키마는 references/plan-schema.md,
동작하는 예제는 examples/plan.example.json — 거기서 시작하는 게 빠르다.
- 반드시 1:1 축척. 눈대중은 "들어갈 것 같다" 를 만들고 그건 늘 틀린다
- 금지구역·테스트포인트 뱅크·주요 신호 흐름·NOTE 를 같이 그린다
- NOTE 에 판단 근거를 적는다. 나중에 "왜 이렇게 했지" 를 푸는 유일한 기록이다
- 생성 후 PNG 를 직접 읽어서 겹침을 확인한다. 라벨이 부품을 가리거나 부품이 홀과 겹치는 건 코드로 안 잡히고 그림을 봐야 보인다
사용자 피드백이 오면 JSON 만 고쳐 다시 렌더한다. 이 왕복이 이 스킬의 정상 동작이다. 2~4회는 당연하다고 보고 진행한다.
테스트포인트 뱅크
- 피치를 먼저 정하고 span 을 계산한다. 뱅크 span 이 부품 몸체보다 크면 안 된다
- 금지구역이 있는 변은 몸체가 아니라 금지구역 바깥에 붙인다
- 실제 패드 지름을 모르면 그렇게 적는다. 피치가 패드보다 작으면 다시 그려야 한다
5. 겹침 검사
배치를 넣은 뒤(또는 넣기 전 시뮬레이션으로) 물리 충돌을 본다.
python scripts/overlap_check.py <PcbDoc> --pads-only --gap 0.3
--pads-only 는 실크를 빼고 패드만으로 본다 (실크 겹침은 무해할 때가 많다).
--gap 은 요구 최소 간격 mm — 0 이면 실제 교차만 잡는다.
출력은 네 갈래이고 심각도가 다르다.
| 항목 | 뜻 |
|---|---|
| 같은 면 겹침 | 진짜 충돌. 고친다 |
| 반대면 관통홀 충돌 | THT 핀·기구홀이 반대면 부품을 뚫는다. 고친다 |
| 패드가 보드 밖 | 제조 불가. 고친다 |
| 실크·하우징만 보드 밖 | 엣지 커넥터면 정상. 눈으로만 확인 |
반대면끼리 XY 가 겹치는 것은 겹침이 아니다
디커플링은 반대면에 놓는 게 정석이다. 소켓·대형 IC 는 몸체가 주변을 덮어서 같은 면에서는 핀 가까이 못 간다. 실측 예 — 36mm 소켓에서 같은 면 최근접이 핀에서 15.5mm, 반대면으로 내리면 2.3mm 였다.
그래서 레이어를 안 보는 겹침 검사는 이 배치에서 통째로 오탐이 된다.
overlap_check.py 는 면을 갈라 판정한다.
단 관통홀은 예외다. 홀은 판을 뚫으므로 반대면 부품과 진짜로 부딪친다. 관통홀은 하나씩 비교한다 — 네 모서리에 기구홀이 있는 소켓은 홀들을 합친 bbox 가 몸체 전체가 되어, 그 아래 반대면 부품을 전부 오탐으로 잡는다.
이 검사는 커넥터 개구부 방향을 못 잡는다. bbox 가 180° 회전으로 안 변하기
때문이다 — §2 의 connector_facing.py 로 따로 대조한다.
6. 좌표 투입 — 승인 후 1회
사용자가 "이대로 확정" 이라고 말하기 전에는 하지 않는다.
python scripts/plan_to_placements.py plan.json > placements.json
→ altium-mcp place_components 에 그대로 넣는다 (배치 전용 배치 API, 1 undo 단계).
단위·기준이 다르다. 그대로 넣으면 어긋난다.
| 가안 | Altium |
|---|---|
| mm | mils |
| bbox 좌하단 | 컴포넌트 원점 (풋프린트에 따라 pad1 이거나 몸체 중심) |
| — | rotation 은 반시계(CCW) 도 |
그래서 2패스다. 1패스로 넣고, 실제 bbox 를 읽어 의도한 중심과의 차를 구해
--offsets 로 2패스를 돌린다. 함정은 references/injection.md.
place_components 는 반환값에 Altium 이 보고하는 최종 x/y/rotation 이 들어 있다.
그걸 읽어서 대조한다. 넣고 끝내면 어긋난 걸 모른다.
어디까지 자동으로 할 것인가 — 이게 이 스킬의 경계다
| 자동 | 이유 | |
|---|---|---|
| 보드 사이즈·외곽·고정홀 | ○ | 치수 누적. 정답이 하나 |
| 주요 IC 회전 | ○ | 핀→변 매핑에서 유도된다 |
| 큰 부품(커넥터·소켓·IC) 배치 | ○ | 변 제약·상대 거리로 풀린다 |
| 테스트포인트 뱅크 | ○ | 핀 순서대로 규칙 격자 |
| 겹침·외곽밖 검사 | ○ | 순수 기하 |
| 패시브 배치 | ✕ 사람이 한다 | 아래 |
| 라우팅·자동배선 | ✕ | MII·차동쌍은 손으로 |
| DRC 판정 / 넷클래스·룰 | ✕ | 별개 작업 |
패시브를 자동으로 놓지 마라
패시브 자리는 라우팅 의도가 정한다 — 디커플링 루프 면적, GND 리턴 경로, 트레이스가 IC 어느 변으로 빠지는지, 어느 패드가 비아로 내려가는지. 규칙이 아는 건 "누구와 넷을 공유하나" 와 "안 겹치나" 둘뿐이라 그 위 정보가 없다.
결과: 겹치지 않는 배치는 만들어도 좋은 배치는 못 만든다.
규칙을 네 번 고쳐 겹침 0 까지 만들어도 결론은 같다 — 큰 부품은 쓸 만하고 패시브는 수동이 낫다.
그러니 패시브는 「주인 IC 근처 · 겹침 0」 까지만 만들고 넘긴다. 그 상태가 손으로 다듬기 좋은 출발점이다. 규칙을 더 정교하게 만들려 하지 마라 — 그 방향으로는 답이 안 나온다.
참고 파일
| 파일 | 언제 |
|---|---|
references/rotation-decision.md |
§2 회전. 핀→변 매핑 확인법과 실증 예 |
references/board-sizing.md |
§3 사이즈. 축 누적법 |
references/plan-schema.md |
§4 plan.json 스키마 |
references/injection.md |
§6 투입 전에. 단위·원점 함정 |
| 스크립트 | 용도 |
|---|---|
scripts/apply_outline.py |
보드 외곽(모서리 라운드) + 대칭 고정홀. Altium 이 문서를 닫은 상태에서 |
scripts/measure_from_lib.py |
§1 회로도+라이브러리만으로 풋프린트 실측 |
scripts/connector_facing.py |
커넥터 개구부 방향 → 변별 회전값. 배치 후 반드시 대조 |
scripts/connectivity_matrix.py |
§1 부품쌍 넷 수 · TP 소속 · 핀별 상대 |
scripts/pin_side_map.py |
§2 회전 4안 비교 |
scripts/plan_svg.py |
§4 가안 도면 |
scripts/plan_to_placements.py |
§6 mm/bbox → mils/원점 변환 |
관련: 회로도 검토 altium-schematic-review · 라이브러리 제작 altium-library.