아케이드 CRT를 살리기 위한 광기의 엔지니어링: RP2040에서 STM32까지


엔지니어로서 우리는 종종 “왜 굳이?”라는 질문을 받습니다. 이미 잘 작동하는 VGA 어댑터가 시장에 널려 있는데, 왜 바닥부터 다시 만드는 걸까요? 오늘 소개할 이야기는 바로 그 질문에 대한 가장 완벽한 대답입니다. Recurse Center의 아케이드 머신을 살리기 위해 시작된 이 프로젝트는, 단순한 디스플레이 연결을 넘어 RP2040의 한계, 리눅스 커널 모듈, 그리고 STM32 하드웨어 설계 의 심연까지 파고드는 여정입니다.

이 글은 단순한 프로젝트 소개가 아닙니다. 하드웨어와 소프트웨어의 경계에서 엔지니어들이 겪는 시행착오, 그리고 그 속에서 발견하는 “진짜” 기술에 대한 이야기입니다.

시작은 언제나 ‘가벼운 마음’으로

이 프로젝트의 발단은 단순했습니다. 친구가 가져온 아케이드 머신(RCade)의 CRT 모니터를 라즈베리 파이 대신 더 강력한 PC에 연결하고 싶다는 것이었죠. 문제는 이 CRT가 336x262 라는 변태적인 해상도를 요구한다는 점입니다. 일반적인 VGA 어댑터는 640x480 밑으로 내려가는 것을 거부합니다. 게다가 기존 파이의 VGA 보드는 18비트 컬러만 지원해서 밴딩(banding) 현상이 심각했습니다.

“그거 내가 USB로 연결되는 어댑터 하나 만들어줄게.”

모든 재앙은 이 한마디로 시작됩니다.

1라운드: RP2040과 PIO의 마법 (그리고 수학적 실수)

저자는 가성비의 제왕 RP2040(Raspberry Pi Pico)을 선택했습니다. 여기서 주목할 기술은 PIO(Programmable I/O) 입니다. PIO는 메인 CPU와 독립적으로 돌아가는 작은 스테이트 머신으로, 사이클 단위의 정밀한 타이밍 제어가 가능합니다. VGA 신호(R, G, B, HSYNC, VSYNC)를 만들어내기에 이보다 완벽한 도구는 없죠.

위 코드는 Rust로 작성된 PIO 어셈블리입니다. HSYNC와 VSYNC 펄스를 생성하고, DMA로 픽셀 데이터를 쏘아주는 로직이 아주 우아하게 구현되어 있습니다. 실제로 이 코드는 CRT에 화면을 띄우는 데 성공했습니다.

하지만 치명적인 문제가 있었습니다. 바로 대역폭 입니다.

저자는 RP2040의 USB Full Speed가 11 MBps 라고 착각했습니다. 실제로는 11 Mbps 입니다. 비트와 바이트의 차이, 초보적인 실수 같지만 시니어들도 가끔 저지르는 실수죠. 320x240 해상도에 16비트 컬러를 60fps로 쏘려면 약 73Mbps가 필요한데, 12Mbps 대역폭으로는 턱도 없습니다. 결과적으로 프레임 레이트는 10fps 미만으로 떨어졌습니다.

여기서 엔지니어로서의 판단력이 요구됩니다. 최적화를 할 것인가, 아키텍처를 갈아엎을 것인가? 저자는 과감하게 하드웨어 변경을 선택합니다.

2라운드: GUD 프로토콜과 리눅스 커널

하드웨어를 바꾸기 전에 짚고 넘어갈 소프트웨어적인 발견이 있습니다. 바로 GUD(Generic USB Display) 프로토콜입니다.

보통 이런 프로젝트를 하면 커스텀 드라이버를 짜느라 고생하기 마련인데, GUD는 이미 리눅스 커널에 업스트림되어 있는 표준 프로토콜입니다. 호스트(PC)는 그냥 꽂기만 하면 되고, 디바이스(MCU)는 압축된 델타 프레임만 받아 처리하면 됩니다. 문서화가 거의 안 되어 있어 커널 모듈을 리버스 엔지니어링해야 했지만, 이 선택은 신의 한 수였습니다.

3라운드: STM32와 하드웨어의 쓴맛

USB High Speed(480Mbps)가 필요해진 저자는 STM32H7 시리즈로 눈을 돌립니다. 이 칩은 LTDC(LCD-TFT Display Controller) 라는 주변장치를 내장하고 있습니다. 쉽게 말해, 칩 자체가 VGA 신호를 하드웨어적으로 생성해준다는 뜻입니다. 이제 PIO로 비트뱅잉을 할 필요도 없습니다.

하지만 여기서부터 진짜 “하드웨어의 늪”이 시작됩니다.

Datasheet의 함정

저자는 처음에 STM32H723을 선택했습니다. 스펙 시트에는 분명 USB High Speed 지원이라고 적혀 있었으니까요. 하지만 데이터시트 구석에 작은 글씨로 이렇게 적혀 있었습니다.

“USB HS 모드를 사용하려면 외부 PHY가 필요합니다.”

내장 PHY는 Full Speed까지만 지원한다는 사실을 뒤늦게 깨달은 겁니다. 결국 보드를 다시 설계해야 했습니다. 이런 경험, 다들 한 번쯤 있으시죠? 스펙 시트의 첫 페이지만 보고 부품을 골랐다가 낭패를 보는 경우 말입니다.

PCB 설계와 1M 옴의 기적

결국 외부 PHY(ULPI 인터페이스)와 SDRAM을 달기 위해 핀 수가 많은 STM32H750IBT(BGA 패키지급의 176핀 LQFP)를 사용하게 됩니다. 여기서 Trace Length Matching 이 등장합니다.

SDRAM과 같은 고속 메모리는 배선 길이가 다르면 신호 도달 시간이 달라져 데이터가 깨집니다. 저 구불구불한 배선들이 바로 그 길이를 맞추기 위한 처절한 노력의 산물입니다.

더 재미있는 에피소드는 보드 브링업(Bring-up) 과정에서 일어났습니다. USB가 불안정하게 동작했는데, 알고 보니 크리스털 발진기에 달아야 할 1M 옴 저항을 빼먹은 겁니다. 저자는 손가락을 갖다 대니(인체 저항 약 1M 옴) 신호가 안정되는 것을 발견하고, 결국 저항을 보드에 땜질해 넣었습니다. 이런 게 바로 현장의 노하우죠.

결과: 24비트의 영광

수많은 삽질 끝에 완성된 보드는 완벽하게 작동했습니다. 24비트 컬러는 밴딩 없이 매끄러웠고, 60Hz 주사율은 부드러웠습니다. 오픈 소스 3D 모델링 툴인 OpenSCAD로 케이스까지 만들어 씌우니 그럴싸한 제품이 탄생했습니다.

물론, 마지막에 USB PHY가 의문사하는 사고가 있었지만, 저자는 멈추지 않고 개선을 예고했습니다.

Hacker News의 반응과 나의 생각

Hacker News의 댓글들을 보면 재미있는 논쟁이 있습니다. 어떤 유저는 RP2040의 PIO를 두고 “쓸데없는 기능(wart)“이라며 폄하하기도 했습니다. FPGA나 전용 칩을 쓰면 그만이라는 거죠. 하지만 저는 반대입니다. $1짜리 칩에서 소프트웨어만으로 커스텀 하드웨어 프로토콜을 구현할 수 있다는 건 엄청난 유연성입니다. 저자가 결국 STM32로 넘어갔지만, PIO로 개념 증명을 빠르게 해냈다는 점을 잊지 말아야 합니다.

또한, 이 프로젝트에서 Mini-USB 를 고집한 저자의 태도가 인상적이었습니다. “USB-C는 복잡하고 싫다, Mini-USB가 귀엽다”는 주장은 비합리적으로 보일지 몰라도, 개인 프로젝트에서는 ‘내 마음대로’가 가장 중요한 스펙입니다.

마치며

이 프로젝트는 “돌아가는 쓰레기(Working Garbage)” 에서 시작해 “제대로 된 엔지니어링” 으로 발전하는 과정을 가감 없이 보여줍니다. 대역폭 계산 실수, 데이터시트 정독 실패, PCB 설계 오류 등은 부끄러운 일이 아니라 엔지니어라면 누구나 겪는 성장통입니다.

요즘 AI가 코드를 짜주는 시대라고 하지만, 오실로스코프를 찍어가며 신호 타이밍을 맞추고, 데이터시트의 각주를 해석하며 하드웨어의 물리적 한계와 싸우는 이런 경험은 AI가 대체할 수 없는 엔지니어의 진짜 실력입니다. 여러분도 오늘, 편안한 추상화 계층을 벗어나 밑바닥(bare-metal)을 건드려보는 건 어떨까요?