← 목록
기타 2026-06-19 12KB 읽기 13분

FPGA 세션1 — 한선씨 합성 파이프라인 (yosys/nextpnr → 실 비트스트림)

개요

세션1-FPGA 브리프의 첫 단계 완료: 한선씨로 기술한 회로 → 합성가능 Verilog → yosys → nextpnr-ice40 → icepack → 실 .bin 비트스트림. 오픈소스 툴체인을 끝까지 결선하고, 한선씨 에미터가 손-레퍼런스와 바이트 동일 비트스트림을 생성함을 왕복검증했다.

무엇을 했는지

1. 툴체인 설치 + E2E 증명

  • brew install yosys nextpnr-ice40 icestorm (GHDL은 기설치). Yosys 0.66 / nextpnr-ice40 0.10 / icestorm 1.1.
  • 손으로 쓴 합성가능 Verilog 카운터(blinky)로 전체 플로우 증명: 29 LC / 1280, Max freq 191 MHz (PASS @12MHz), 32,220 B 비트스트림 (iCE40 싱크마커 ff00 00ff 7eaa 997e 확인).

2. 회로합성.한선 — 순수 한선씨 Verilog 에미터 (신규 라이브러리)

  • libs/회로합성.한선. 문자열 누적만으로 합성가능 Verilog 생성 (VM 65535 캡 이하, 나눗셈/큐브설정 회피).
  • API: 폭표기 v입력/v입력버스/v출력/v출력버스 포트조립 v모듈/v모듈끝 v레지스터/v신호 v항상상승 v대입 v슬라이스 + 고수준 블링키생성(모듈명,클럭명,LED명,카운터폭,LED수).
  • 함정: 신호는 hanseonc_high 예약어 → 매개변수명 불가(버스로 회피).

3. 제약파일(.pcf) 생성기 (한선씨)

  • 같은 라이브러리에 pcf핀 pcf버스핀 pcf블링키_icestick 추가. iCEstick(HX1K-TQ144): clk=21, LED=99/98/97/96/95.

4. 원클릭 파이프라인 드라이버 + 회귀

  • rtl/fpga/합성.sh {emit|build|all|test} + 동반 합성플로우.한선(4단계 한선씨 기술).
  • ./합성.sh testPASS: 한선씨생성 .bin == 손-레퍼런스 .bin (바이트 동일, md5 6e874aa8...).

왕복검증 결과

산출물결과
한선씨생성 .v vs 손-Verilog공백 1칸 차이만(의미 동일)
한선씨생성 .v → 비트스트림손-레퍼런스와 바이트 동일
한선씨생성 .v + .pcf → 비트스트림손-레퍼런스와 바이트 동일

관련 파일

  • /Users/ef/CrownyOS/crownyc/libs/회로합성.한선 — 에미터 라이브러리 (.v + .pcf)
  • /Users/ef/CrownyOS/crownyc/rtl/fpga/블링키.한선 — 데모 (RTL → .v/.pcf 출력)
  • /Users/ef/CrownyOS/crownyc/rtl/fpga/합성.sh + 합성플로우.한선 — 파이프라인 드라이버
  • /Users/ef/CrownyOS/crownyc/rtl/fpga/verilog/, pcf/, build/ — 산출물
  • 손-레퍼런스: verilog/blinky_ref.v, pcf/icestick.pcf

확장 (2026-06-20)

에미터 순차로직 확장 (회로합성.한선)

  • v항상상승블록(begin/end), v논블로킹(<=), v만약/v아니면(if/else), v접합({a,b} 비트접합) 추가.
  • 고수준 합성기 2종: 카운터생성(동기리셋 업카운터), 시프트레지스터생성(SIPO).

회로방언.한선 — .회로 → 합성가능 Verilog 트랜스파일러 (순수 한선씨, 신규)

  • 브리프 핵심 문구 "회로방언 → 넷리스트 → 비트스트림" 실현. 기존 hanseonc_dialect.c.회로시뮬레이션으로만 변환했으나, 이건 합성가능 Verilog 생성.
  • examples/카운터.회로(모듈/입력/출력/신호/항상@상승/만약-아니면/신호읽기·신호설정) 파싱 → counter 모듈 Verilog.
  • yosys 한글 식별자 거부이름맵(클럭→clk, 리셋→rst, 값→val…)으로 ASCII 변환. 시뮬레이션 블록은 합성 비대상으로 무시.
  • 함정: 이 hanseonc_high인라인 ; 주석을 코드로 파싱(구버전) → 인라인 주석/한 줄 다중문장 금지.

방언경로 == 직접경로 동등성 (왕복검증)

.회로 → Verilog(방언)와 직접 한선씨 카운터생성동일 하드웨어로 합성: 둘 다 2 SB_CARRY + 4 SB_DFFSR + 4 SB_LUT4 = 10 셀.

통합 회귀 (./합성.sh regress)

4종 전부 비트스트림 PASS ✅: 블링키(29 LC) · 카운터(7 LC) · 시프트(5 LC) · 방언카운터(7 LC). 전부 32,220 B .bin, 모두 191~626 MHz PASS @12MHz.

추가 파일: rtl/fpga/{카운터.한선, 회로방언.한선, 시프트.한선}, verilog/{카운터_gen,카운터회로_gen,시프트_gen}.v, pcf/{카운터_gen,시프트_gen}.pcf.

2단계: 합성가능성 보장 (2026-06-20)

합성검사.한선 — 합성가능 서브셋 가드 (순수 한선씨, 신규)

  • yosys/nextpnr 넘기기 전 비합성 구조 검출. 오류(나눗셈 / , 초월함수 루트/사인/코사인/로그, always 내 동안(while)) + 경고(yosys 거부하는 한글 식별자, 이름맵 필요).
  • ./합성.sh 검사 <.회로>. 카운터.회로 → PASS(0/0), 나쁜회로.회로 → FAIL(오류 3, 경고 2).
  • 함정: 오류는 hanseonc_high 예약어(시도/오류 catch절) → 변수명 불가(오류수로 회피). 한글 env명은 bash 미전달 → ASCII(CIRCUIT).

룩업.한선 / 룩업생성 — 정적 case LUT (나눗셈/√ 합성가능 대안)

  • 가드가 잡은 나눗셈/초월함수의 합성가능 대안. 미리 계산한 값 배열 → 조합 ROM(always @(*) case).
  • 데모: ÷3 LUT(4비트 입력 → floor(i/3), 16엔트리) → 5 LC 비트스트림. v출력reg버스/v케이스라인/룩업생성 추가.

통합 회귀 5종 (./합성.sh regress)

블링키·카운터·시프트·룩업·방언카운터 전부 비트스트림 PASS ✅.

추가 파일: rtl/fpga/{합성검사.한선, 룩업.한선}, examples/나쁜회로.회로, verilog/룩업_gen.v, pcf/룩업_gen.pcf.

방언 확장 + 전단 가드 자동연결 (2026-06-20)

회로방언.한선 v2 — 블록스택 트랜스리터레이션

  • 줄단위 의미재구성 → 블록스택 기반 트랜스리터레이션 파서로 일반화. 다중 always, 중첩 선택(case)/만약(if), 조합 @* 지원.
  • always @*에서 대입되는 출력 = output reg 자동 판정(reg출력스캔, 브레이스 깊이).
  • 신규 .회로: ALU(4연산 조합 case, 47 LC), FSM(3상태 순차+중첩 case/if, 5 LC), 이중카운터(다중 always, 11 LC) — 전부 비트스트림 PASS.

전단 합성가드 자동연결 (합성가드.한선 라이브러리)

  • 가드 로직을 libs/합성가드.한선(검사→오류수)으로 추출. 회로방언.한선이 변환 전에 자동 호출.
  • 오류>0 → ⛔ 차단, Verilog 미생성 → 잘못된 .회로가 yosys까지 못 감. 검증: 나쁜회로.회로 → 차단(/tmp/bad.v 미생성), 정상 → 변환.
  • 합성검사.한선도 같은 라이브러리 래퍼로 단일화.

드라이버 확장 (합성.sh)

  • regress(9종: 직접 에미터 4 + 방언 4 + 카운터회로), dialect <.회로> <top> <base> [pcf](원클릭, 가드 자동), flash <base>(iceprog 실보드), 검사.
  • README.md 추가(빠른시작·두 경로·실보드 워크플로·함정).

상태: "실보드 꽂고 디버깅만"

.회로 → (가드) → Verilog → yosys → nextpnr → icepack → .bin 전부 자동. iCEstick USB 연결 후 ./합성.sh flash <base>만 남음. iceprog 설치됨(보드 미연결 시 "Can't find iCE FTDI USB device" 정상).

보드 생태계 + 원스탑 구동 (2026-06-20, 무하드웨어 사전구축)

하드웨어 미보유 상태에서 "보드 꽂으면 한 줄로 구동" 되도록 사전 구축. flash만 보드 대기, 나머지 전부 검증.

보드 프로파일 레지스트리 (libs/보드.한선)

  • 보드 → 디바이스/패키지/클럭핀/클럭MHz/LED핀들/리셋핀/프로그래머. icestick(hx1k/tq144/clk21/LED5)·icebreaker(up5k/sg48/clk35/LED2)·hx8k(ct256)·upduino(up5k). 보드별 보드pcf() 생성.
  • 새 보드 = if-체인 한 줄 추가.

브링업.한선 — 보드별 blinky 생성기

  • BOARD env → 보드 LED수에 맞춘 blinky + 보드 pcf + META device pkg programmer ledn 라인(shell 파싱용).

구동.sh — 원스탑 오케스트레이터

  • doctor(툴체인+보드 준비도+다음할일), boards(프로파일 목록), probe(iceprog -t 보드탐지: 0=연결/1=미연결/127=툴없음), bringup <board>(blinky→비트스트림→보드있으면 자동flash), flash <board>, run <design> <board>(.한선/.회로 원스탑, 가드 자동).
  • 하드웨어연결.md: 보드선택·물리연결·원스탑·디버깅 가이드.

무하드웨어 retarget 증명

  • icestick(HX1K) 32,220B / icebreaker(UP5K, 다른 칩) 104,090B 비트스트림 — 크기 다름=진짜 보드별 합성. 칩 패밀리 retarget 동작.
  • 보드 도착 시: USB 연결 → ./구동.sh probe./구동.sh bringup icestick → LED 깜빡이면 끝.

UART + ECP5 타깃 (2026-06-21)

UART 송신기 (회로합성.한선: 송신생성 / 송신헬로생성)

  • 8N1 UART TX(DIV=클럭/보드레이트). 송신생성(모듈명, DIV) + 보드데모 송신헬로생성(주기적 'A' 전송).
  • iverilog 기능검증 PASS: 송신 0x41 == 수신 0x41 (시리얼 프레임 디코딩). 함정: output reg(tx/busy) initial 초기화 없으면 시뮬 x 전파.
  • uart_hello → icestick 비트스트림 79 LC. 보드선 tx핀→USB시리얼, 터미널 9600 baud로 'A' 확인.

ECP5 타깃 (보드 패밀리 분기)

  • 보드.한선에 패밀리(ice40/ecp5) + ECP5 보드(ulx3s/orangecrab/ecp5-evn) + .lpf 생성기(LOCATE/IOBUF SITE). 프로그래머 openFPGALoader + 로더플래그.
  • 구동.sh 패밀리 분기: ice40=synth_ice40/nextpnr-ice40/icepack/iceprog(.bin), ecp5=synth_ecp5/nextpnr-ecp5/ecppack/openFPGALoader(.bit).
  • 툴: synth_ecp5(yosys)·ecppack(prjtrellis)·openFPGALoader=brew. nextpnr-ecp5는 brew 미제공 → 소스빌드(cmake -B build -DARCH=ecp5 -DTRELLIS_INSTALL_PREFIX=/opt/homebrew) → /opt/homebrew/bin 설치.
  • 무하드웨어 ECP5 풀 비트스트림 증명: ULX3S blinky → synth_ecp5 → nextpnr-ecp5(335MHz) → ecppack → 1.9MB .bit. doctor 전부 그린.
  • boards 7종(iCE40 4 + ECP5 3). bringup ulx3s(.bit)·bringup icestick(.bin) 둘 다 동작.

UART RX + 에코 + 기능 시뮬 회귀 (2026-06-21)

UART 수신기 + 에코 (회로합성.한선: 수신생성 / 에코생성)

  • 8N1 UART RX(중간샘플 DIV/2, case FSM idle/start/data/stop) + 에코 top(rx→tx 루프백, uart_rx+uart_tx 인스턴스).
  • iverilog 양방향 기능검증: 에코 5/5 바이트 PASS(0x55·0xA3·0x00·0xFF·0x41 입력==에코). RX 디코딩+TX 재인코딩 왕복.
  • uart_echo 비트스트림: iCE40(icestick 134 LC, .bin) + ECP5(ulx3s 160MHz, 1.9MB .bit) 둘 다.

시뮬.sh — 기능 시뮬 회귀 (iverilog)

  • 합성("만들어지는가")에 더해 동작("올바로 동작하는가")을 상시 검증. uart_tx(송수신 일치)·uart_echo(5/5) 테스트벤치 자동 실행. 하드웨어 없이 가능한 최강 검증.
  • 하드웨어연결.md에 시리얼 에코 테스트(screen/minicom 9600 8N1) 워크플로 추가.

PWM + 호흡등 (회로합성.한선: 변조생성 / 호흡생성)

  • PWM(카운터<duty → out=1) + 호흡등(duty 자동 0..max..0 램프 → LED 밝기 숨쉬듯). iverilog 기능검증: duty=64 → 64/256 high(25%) PASS. breathe 합성 74 LC.
  • 시뮬.sh 기능 회귀 3종(uart_tx·uart_echo·pwm) 전부 PASS.

BRAM 블록 메모리 (회로합성.한선: 메모리생성)

  • 단일포트 동기 RAM(write→sync read). yosys 블록RAM 추론 확인: 256x8 → 1× SB_RAM40_4K(LUT 아님). write→read 3/3 PASS(addr 5/10/200). iCE40 .bin + ECP5 .bit.
  • 시뮬.sh 기능 회귀 4종(uart_tx·uart_echo·pwm·bram) 전부 PASS.

자동 재개 (레이트리밋 대비)

  • 세션에 60초 자동 깨어남 설정 — 레이트리밋 시 백오프 후 다음 미완 작업(.회로 폭추론/orangecrab DFU) 자동 이어감.

잔여 이슈 (다음 단계)

  1. 회로방언(.회로) → 회로합성.한선 연결: 현재 hanseonc_dialect.c.회로시뮬레이션(하드웨어.한선)으로만 트랜스파일. .회로 → 합성가능 Verilog 경로 추가 필요(카운터.회로를 에미터로 흡수).
  2. 합성가능 서브셋 보장: 나눗셈/뉴턴랩슨 → 정적 LUT, 클럭/리셋 규약 강제, 회귀 확장.
  3. 실보드 검증: iCEstick에 iceprog.bin 플래싱 → LED blink 육안 확인 (하드웨어 필요).
  4. ECP5/UP5K 타겟: nextpnr-ecp5, UP5K(sg48) 핀맵 추가.
  5. 더 큰 회로(UART/ALU/FSM): 에미터 always-블록/case 문 확장.