> EulerNPU > 튜토리얼 > 08. KWS NPU — XC7Z020 실리콘 검증

08. KWS NPU — XC7Z020 실리콘 검증

이 페이지가 eulernpu 의 silicon 정석입니다. 음성 인식 (Keyword Spotting) NPU를 QMTECH XC7Z020 보드에 실제로 올린 전체 흐름을 다룹니다. 이후 09 / 10 silicon 데모는 모두 이 패턴의 변형입니다.

두 가지 silicon 패턴을 함께 다룹니다:

패턴결과의미
L2 (matmul만)11/11 MATCH, NPU 2.5× 느림DMA dominate, "데이터 경로 증명"
L3 (풀 그래프)11/11 MATCH, NPU 2.11M cyc vs ARM 17M cyc = 8.07× 가속진짜 NPU

모델

examples/silicon_kws/ 의 DS-CNN + GRU 키워드 스포터 (11 commands).

학습 + 양자화 + golden 생성은 examples/silicon_kws/ 에서 끝나 있고, P06 는 그 모델을 silicon으로 가져가는 일 만 합니다.

7-단계 silicon 흐름 (전체)

프로젝트 위치: examples/silicon_kws/

Step 1 — Host CPU smoke

silicon 가기 전에 host gcc로 같은 C 코드를 컴파일해서 정확도가 맞는지 봅니다. 수학이 host에서 깨지면 silicon에서도 깨집니다 (그 역은 아님).

bash examples/silicon_kws/tests/build_host_smoke.sh

기대: 11/11 PASS — 모든 키워드 정답.

Step 2 — HLS C-synth

bash examples/silicon_kws/scripts/run_hls_synth.sh

이 단계가 eulernpu/hls/kernels/ 의 템플릿 (matmul_systolic.h, conv2d_sliding.h, recurrent.h 등)을 instantiate 해서 RTL IP를 만듭니다.

L2 모드의 합성 보고서:

L3 모드의 합성 보고서:

L3가 더 무거운 이유는 전체 그래프 + 가중치 ROM 모두 onchip 이라.

Step 3 — Vivado bitstream

vivado -mode batch \
  -source examples/silicon_kws/scripts/01_create_vivado_project.tcl

이 TCL이 Block Design을 자동으로 만듭니다:

processing_system7_0
├── M_AXI_GP0  →  IP s_axilite (control)
├── S_AXI_HP0  ←  IP m_axi (data, if any)
└── EMIO UART1 → external pin (printf 채널, P05 패턴)

IP (kws_npu_top 또는 matmul_systolic)

Vivado impl 결과:

Step 4 — Vitis platform + ELF

vitis -s examples/silicon_kws/scripts/02_create_app.py

이 Python script가:

  1. XSA 로부터 SDT (System Device Tree) 플랫폼 생성
  2. ARM standalone BSP 컴파일
  3. EMIO UART MIO 48/49 routing patch 적용 (P05 X-1 carry-over)
  4. demo1 / demo2 ELF 2개 빌드

ELF 위치: ~/Developments/vitis_ws/kws_npu/llm_demo{1,2}/build/*.elf

Step 5 — Silicon run (xsdb + UART)

보드가 just-reset 상태여야 합니다 (P07 X-10 함정):

bash examples/silicon_kws/scripts/run_npu_test.sh

이 harness가:

  1. xsdb connect + 비트스트림 fpga -file
  2. ps7_init.tcl source + EMIO UART 재라우팅
  3. ELF dow + con
  4. 동시에 pyserial/dev/ttyUSB1 캡처 (P06 X-8 함정: cat은 burst loss)
  5. RESULT: PASS / FAIL 자동 grading

Step 6 — 결과 측정

examples/silicon_kws/docs/results/ 에 캡쳐 파일 + SUMMARY.md.

demo1 (정확성 게이트):

demo1: 11/11 CPU=NPU agreement
   "yes"   pred=yes   conf=0.998  cyc_cpu=17,492,310  cyc_npu=2,114,832  → MATCH
   "no"    pred=no    conf=0.999  cyc_cpu=17,489,221  cyc_npu=2,114,977  → MATCH
   ... (× 11)
   RESULT: PASS

demo2 (200 프레임 스트림):

demo2: 200 frames / 182 keywords detected / 5,166 ms wall
       NPU avg 25.83 ms per frame, CPU avg 209 ms per frame
       (8.07× 가속 — Apple-ANE 패턴)

Step 7 — Cycle model 회귀 잠금

P06 측정값이 eulernpu/hls/cycle_model.pymatmul_systolic lambda 와 ±1% 일치합니다 (예측 456 vs 측정 460):

def test_matmul_systolic_matches_p06_silicon():
    cycles = matmul_systolic_cycles(M=32, K=48, N=8, T=8)
    assert 414 <= cycles <= 506   # ±10% tolerance

이 lock-test가 CI에 들어가 있어서, cycle model 식이 미래에 drift 하면 CI가 fail합니다. P06 silicon 측정값이 모든 후속 npu_sim profile 의 신뢰성 anchor입니다.

두 silicon 함정 (P05/P06 X-항)

X-8 — cat /dev/ttyUSB1 은 burst loss

UART 115200 baud + IP가 빠른 burst printf 시 cat 는 일부 라인을 놓칩니다.

→ Fix: pyserial 기반 capture_uart.py 사용. run_npu_test.sh 가 자동으로 pyserial 우선 + fallback 처리.

X-10 — JTAG DAP sticky-error

직전 데모가 보드를 halted 상태로 남기면 다음 xsdb attach 가 fail합니다 (DAP status 0x30000021).

→ Fix: 보드 RESET 버튼 한 번 push (또는 USB 전원 cycle). test_xsdb.tcl 가 이 상황을 감지하면 명시적 RESET 안내 출력.

재현 — 한 줄

# (보드가 just-reset 상태)
bash examples/silicon_kws/scripts/run_npu_test.sh

기대: 약 2-3분 후

demo1 : PASS
demo2 : PASS

상세 비디오 + SUMMARY:

왜 P06이 정석인가

세 silicon 단계의 ladder:

  1. P06 (KWS) — 첫 silicon, 8.07× 가속, cycle model 잠금. 가장 깔끔한 "이게 가능하다" 증명.
  2. P07 (Bearing) — 같은 L3 패턴, 더 깊은 그래프. 11.13× 가속. 리소스 한계 (II=64) 에서 어떻게 회복하는지 보여줌.
  3. P08 (LLM) — L2 hybrid 로 deliberately 떠남 (큰 모델 BRAM 안 들어감). NPU가 CPU보다 느린 정직한 결과.

P06이 "FPGA NPU 가 의미 있다" 의 가장 분명한 증명이고, P07/P08은 그 패턴이 어디까지 변형 가능한가의 확장입니다.

다음 단계