Home > EulerNPU > Tutorials > 08. KWS NPU on XC7Z020 Silicon

08. KWS NPU on XC7Z020 Silicon

This page is the silicon canonical for eulernpu. It walks the full flow of putting the Keyword Spotting NPU onto a QMTECH XC7Z020 board. Both subsequent silicon demos — 09 / 10 — are variations on this pattern.

Two silicon patterns covered together:

PatternResultMeaning
L2 (matmul only)11/11 MATCH, NPU 2.5× slowerDMA dominates, "data-path proof"
L3 (full graph)11/11 MATCH, NPU 2.11M cyc vs ARM 17M cyc = 8.07× speedupA real NPU

The Model

examples/silicon_kws/'s DS-CNN + GRU keyword spotter (11 commands).

Training + quantization + golden generation all finish inside examples/silicon_kws/. P06 only takes that model to silicon.

7-Stage Silicon Flow (Full)

Project location: examples/silicon_kws/

Step 1 — Host CPU smoke

Before silicon, compile the same C with host gcc and verify accuracy. If math breaks on host, it breaks on silicon (the reverse is not true).

bash examples/silicon_kws/tests/build_host_smoke.sh

Expected: 11/11 PASS — every keyword answered correctly.

Step 2 — HLS C-synth

bash examples/silicon_kws/scripts/run_hls_synth.sh

This step instantiates eulernpu/hls/kernels/ templates (matmul_systolic.h, conv2d_sliding.h, recurrent.h, ...) into an RTL IP.

L2 synthesis report:

L3 synthesis report:

L3 is heavier because the full graph + weights ROM are all on-chip.

Step 3 — Vivado bitstream

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

The TCL autogenerates the 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 channel, P05 pattern)

IP (kws_npu_top or matmul_systolic)

Vivado impl result:

Step 4 — Vitis platform + ELF

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

This Python script:

  1. Creates an SDT (System Device Tree) platform from the XSA
  2. Compiles the ARM standalone BSP
  3. Applies the EMIO UART MIO 48/49 routing patch (P05 X-1 carry-over)
  4. Builds two ELFs (demo1, demo2)

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

Step 5 — Silicon run (xsdb + UART)

Board must be in just-reset state (P07 X-10 trap):

bash examples/silicon_kws/scripts/run_npu_test.sh

This harness:

  1. xsdb connect + fpga -file the bitstream
  2. Source ps7_init.tcl + re-route EMIO UART
  3. dow the ELF + con
  4. Simultaneously pyserial captures /dev/ttyUSB1 (P06 X-8 trap: cat loses bursts)
  5. Auto-grade RESULT: PASS / FAIL

Step 6 — Results

examples/silicon_kws/docs/results/ contains capture files and SUMMARY.md.

demo1 (accuracy gate):

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-frame stream):

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× speedup — Apple-ANE pattern)

Step 7 — Cycle model regression lock

The P06 measurement matches eulernpu/hls/cycle_model.py's matmul_systolic lambda within ±1 % (predicted 456 vs measured 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

This lock-test is in CI; CI fails if the cycle model formula drifts in the future. The P06 silicon measurement is the trust anchor for every subsequent npu_sim profile.

Two Silicon Traps (P05/P06 X-items)

X-8 — cat /dev/ttyUSB1 loses bursts

At UART 115200 baud with fast burst printf from the IP, cat drops lines.

→ Fix: pyserial-based capture_uart.py. run_npu_test.sh auto-prefers pyserial with a fallback.

X-10 — JTAG DAP sticky error

If the previous demo left the board halted, the next xsdb attach fails (DAP status 0x30000021).

→ Fix: press the board RESET button (or cycle USB power). test_xsdb.tcl detects this state and prints explicit RESET advice.

Reproduce — One Line

# (board just-reset)
bash examples/silicon_kws/scripts/run_npu_test.sh

Expected: ~2–3 minutes later

demo1 : PASS
demo2 : PASS

Videos + SUMMARY:

Why P06 is Canonical

The three-stage silicon ladder:

  1. P06 (KWS) — first silicon, 8.07× speedup, cycle model locked. The cleanest "this is possible" proof.
  2. P07 (Bearing) — same L3 pattern, deeper graph. 11.13× speedup. Shows how to recover at a resource ceiling (II=64).
  3. P08 (LLM) — deliberately departs to L2 hybrid (the big model doesn't fit BRAM). Honest result of NPU being slower than CPU.

P06 is the clearest "FPGA NPU has meaning" proof; P07/P08 show how far the pattern stretches.

Next