1. 结论先行:PYNQ-Z2 比上一版 ALINX 假设更紧,仍可做,但必须降目标
和上一版按 ALINX Zynq-7020 开发板的分析相比,PYNQ-Z2 的最关键差异是:只有 512MB DDR3,并且板上 DDR3 为 16-bit 总线、最高 1050Mbps;QSPI 只有 16MB;芯片为 XC7Z020-1CLG400C,通常比一些 -2 速度等级板卡更不适合追求高 PL 频率。PYNQ-Z2 官方/手册资料列出的资源包括 512MB DDR3、128Mbit/16MB QSPI、MicroSD、125MHz PL 参考时钟,以及 85K logic cells、630KB BRAM、220 DSP slices。[1][2]
最终建议目标:第一阶段把上下文限制在 512 或 1024 tokens;第二阶段做 2048 tokens;4096 tokens 只作为优化后目标;32K 上下文不适合 PYNQ-Z2。量化必须至少做到 W4 权重 + INT8 KV cache,默认 PYNQ Python/Jupyter 环境只能用于 bring-up,不适合作为最终运行环境。
目录
2. PYNQ-Z2 板级资源与影响
2.1 板卡资源
| 资源 | PYNQ-Z2 实际情况 | 对 Qwen2.5-0.5B 的影响 |
|---|---|---|
| SoC | XC7Z020-1CLG400C,Dual Cortex-A9 + 7-series PL | PL 资源和普通 Zynq-7020 一样,但 -1 速度等级建议先按 125–150MHz 做时序目标。 |
| PL 逻辑资源 | 约 85K logic cells、13,300 slices、220 DSP、630KB BRAM | 能实现一个复用型 GEMV/Attention 加速器;不能片上存模型权重。 |
| DDR3 | 512MB,16-bit bus @ 1050Mbps,连接 PS DDR controller | 理论峰值约 2.1GB/s,且与 ARM、Linux、PL 共享;INT4 权重可以放下,INT8 权重基本不可行。 |
| QSPI Flash | 16MB,Quad SPI,最高约 400Mbps 模式 | 只能放 FSBL/小 bitstream/配置,不可能放完整模型。 |
| MicroSD | 可启动系统,可做外部非易失存储;SDIO 0,最高 50MHz,支持 1-bit/4-bit | 适合存模型文件和系统镜像;运行时不能每 token 从 SD 流式读权重。 |
| 时钟 | PS 50MHz 输入,ARM 最高 650MHz;PL 外部 125MHz 参考时钟 | PL 可由 MMCM/PLL 产生工作时钟。建议第一版 125MHz,稳定后 150MHz,谨慎挑战 200MHz。 |
| 供电 | Micro-USB 或 7–15V 外部电源;手册提示高负载设计可能需要外部电源 | 高 DSP/DDR 活动时建议使用稳定 12V 外部供电,而不是只靠 USB。 |
DDR3 的 16-bit × 1050Mbps 理论带宽约为:
这个 2.1GB/s 是物理接口峰值,不等于 PL 能长期拿到的有效权重流带宽。Zynq-7000 的 AXI_HP 接口确实提供 PL master 到 DDR/OCM 的高带宽通路,但 DDR 仲裁、burst 效率、HP 时钟、cache coherency、Linux 干扰都会降低实际值。AMD UG585 明确说明 4 个 AXI_HP 接口用于给 PL bus masters 提供到 DDR/OCM 的高带宽路径,每个 HP 接口有读写 FIFO;PYNQ 文档也说明 PL 侧可通过 HP/ACP 访问 PS DRAM。[5][6]
2.2 和 ALINX 假设的关键差异
| 项目 | 上一版常见 ALINX 7020 假设 | PYNQ-Z2 修正 | 架构后果 |
|---|---|---|---|
| DDR 容量 | 常见为 1GB 级别 | 512MB | 从“勉强宽裕”变成“非常紧”。必须 W4;必须限制上下文;必须控制 Linux 内存。 |
| QSPI | 可能 32MB 或更高 | 16MB | 仍然只能做启动/配置;模型必须在 MicroSD,启动后搬到 DDR。 |
| PL 速度等级 | 部分板卡可能为 -2 | PYNQ-Z2 是 -1 | 不要一开始追求 200MHz;以 125/150MHz 打通数据流更实际。 |
| 软件环境 | 可定制裸机/Linux | 很多用户默认用 PYNQ Linux + Python/Jupyter | PYNQ 适合调试 overlay,不适合最终性能。最终应使用 C/C++ daemon、reserved-memory 或裸机。 |
3. Qwen2.5-0.5B 的模型尺寸与存储数学
Qwen2.5-0.5B-Instruct 是 causal LM,模型卡列出 0.49B 参数、0.36B 非 embedding 参数、24 层、GQA 为 14 个 Q heads 和 2 个 KV heads、完整上下文 32,768 tokens;架构包含 RoPE、SwiGLU、RMSNorm、QKV bias 和 tied word embeddings。[3] 配置文件给出 hidden_size=896、intermediate_size=4864、num_attention_heads=14、num_key_value_heads=2、vocab_size=151936、tie_word_embeddings=true、head_dim=64。[4]
3.1 主要模型参数
| 项目 | 数值 | 硬件含义 |
|---|---|---|
| hidden size | 896 | 片上 activation vector 很小,适合放 BRAM/寄存器。 |
| layers | 24 | 每 token 需要重复 24 层数据流;控制器需要层级地址表。 |
| attention heads | Q=14,KV=2,head_dim=64 | GQA 显著降低 KV cache,但 Q heads 仍有 14 个。 |
| intermediate size | 4864 | MLP 是每层最大计算/权重部分。 |
| vocab size | 151,936 | LM head 很大;每生成一个 token 需要做大词表 projection 或近似替代。 |
| 参数量 | 0.49B total,0.36B non-embedding | INT4 后仍需约 250MiB 级 DDR 存储。 |
3.2 权重尺寸估算
| 格式 | 理论权重体积 | 在 PYNQ-Z2 上的判断 |
|---|---|---|
| BF16/FP16 | 约 934.6MiB | 不可行 超过 512MB DDR。 |
| INT8 | 约 467.3MiB | 基本不可行 还没算 scale、KV cache、OS、workspace。 |
| INT4 raw | 约 233.7MiB | 勉强可行 还需加 scale/zero/metadata。 |
| INT4 + FP16 scale,group=64 | 约 248.3MiB | 推荐基线 若只存 scale,不存 zero-point。 |
| INT4 + scale + zero,group=64 | 约 262.9MiB | 现实可行 加上对齐和表,约 265–285MiB。 |
其中 tied embedding / LM head 的矩阵尺寸为:
仅这个矩阵,INT4 raw 就约 64.9MiB;而它在 decode 的最后一步通常每个 token 都要完整流过一次,用于得到 logits/top-k。这是 PYNQ-Z2 上被低估的瓶颈之一。
3.3 每层权重拆分
| 每层模块 | 近似权重数量 | INT4 raw 体积 | 说明 |
|---|---|---|---|
| Attention Q/K/V/O | 约 1.835M | 约 0.88MiB | Q/O 是 896×896,K/V 因 GQA 只有 896×128。 |
| MLP gate/up/down | 约 13.074M | 约 6.23MiB | 每层主要权重和计算都在 MLP。 |
| 每层合计 | 约 14.91M | 约 7.11MiB | 24 层约 171MiB raw INT4,加 scale/zero 后约 185–200MiB。 |
| Embedding / LM head | 约 136.13M | 约 64.9MiB | tied 权重,输入 embedding 只查一行,输出 head 需要扫全矩阵。 |
4. 数据分别存在哪里
4.1 总体存储分配
| 数据类别 | 推荐位置 | 原因与注意事项 |
|---|---|---|
| FSBL / U-Boot / bitstream / 小配置 | MicroSD 或 QSPI | PYNQ-Z2 支持 MicroSD、QSPI、JTAG 启动。默认 PYNQ 生态通常从 MicroSD 启动;QSPI 16MB 只适合启动介质/小配置。 |
| 量化模型文件 | MicroSD 非易失存储;启动后搬到 DDR | 模型包约 265–285MiB,远大于 16MB QSPI。MicroSD 可存放,但运行时不能每 token 从 SD 读取。 |
| INT4 权重包 | PS DDR3 的 reserved memory / CMA 大块物理连续区 | PL 通过 HP port 做长 burst 顺序读取。默认 Linux 普通虚拟内存不适合直接给 PL。 |
| KV cache | PS DDR3 reserved region | BRAM 不够放历史 KV。推荐 INT8 KV;上下文先限制到 1K/2K。 |
| 当前 hidden、norm 后向量、MLP tile、中间 partial sum | PL BRAM / distributed RAM / 寄存器 | hidden=896 很小,BRAM 足够。只把“当前 token 或小 block 的 working set”放片上。 |
| 权重 tile FIFO / DMA FIFO | PL BRAM | 用来隐藏 DDR burst 抖动,不做大容量缓存。 |
| 完整 logits | 不建议落 DDR | vocab=151936,完整 logits 写 DDR 会增加带宽和延迟。建议 PL 内流式 Top-K,只把 top-k token 和 score 返回 PS。 |
| Tokenizer / chat template / sampling | PS ARM DDR + cache | tokenizer 复杂但数据量小;采样和 UI 不适合放 PL。 |
| OCM 256KB | 小型控制结构/中断/低延迟 buffer,可选 | 容量太小,不参与模型权重或 KV cache。 |
4.2 KV cache 容量表
Qwen2.5-0.5B 的 KV cache 每 token 每层包含 K 和 V,各为 num_key_value_heads × head_dim = 2 × 64 = 128 个元素。24 层合计:
| 上下文长度 | INT8 KV cache | FP16 KV cache | PYNQ-Z2 建议 |
|---|---|---|---|
| 512 | 3MiB | 6MiB | 第一阶段推荐 |
| 1024 | 6MiB | 12MiB | 推荐基线 |
| 2048 | 12MiB | 24MiB | 第二阶段目标 |
| 4096 | 24MiB | 48MiB | 优化后可试 |
| 8192 | 48MiB | 96MiB | 容量可放,速度会明显下降 |
| 16384 | 96MiB | 192MiB | 不建议 |
| 32768 | 192MiB | 384MiB | 不适合 PYNQ-Z2 |
4.3 PYNQ-Z2 DDR3 的推荐内存布局
512MB DDR 要同时承载 Linux/PYNQ、模型、KV cache、workspace 和 DMA buffer。若使用默认 PYNQ 镜像、Jupyter、Python 进程和较小 CMA,可能无法一次性获得 300MiB 级连续物理区。PYNQ 文档说明,pynq.allocate 用于给 PL 连接到 HP/ACP 的 IP 分配 PS DRAM 中的物理地址可见内存;这类连续内存是有限资源。[6]
| 区域 | 最小 Linux 方案建议 | 默认 PYNQ 方案风险 |
|---|---|---|
| Linux kernel/rootfs/app | 80–140MiB | Jupyter/Python 服务会显著增加内存占用,留给模型的连续区变小。 |
| INT4 模型包 | 265–285MiB reserved | 如果模型放普通 mmap 文件页,物理地址不连续,PL 直接访问困难。 |
| KV cache | 8–32MiB,先按 1K/2K context | 4K 以上上下文会压缩 workspace 和 OS 空间。 |
| workspace / command ring / top-k / profiling | 16–40MiB | 如果 buffer 频繁分配释放,易碎片化。 |
| 安全余量 | 20–50MiB | 无余量时 Linux swap 到 SD 会使系统几乎不可用。 |
4.4 一个可执行的 DDR 切分示例
512MiB DDR3 物理空间概念切分:
0x0000_0000 ─────────────────────────────────────────────
Linux / bare-metal runtime / stack / heap
约 96–128MiB,尽量瘦身
0x0800_0000 ─────────────────────────────────────────────
Reserved model region
qwen2.5-0.5B W4 pack:约 265–285MiB
0x19D0_0000 ─────────────────────────────────────────────
KV cache region
INT8 KV:1K≈6MiB,2K≈12MiB,4K≈24MiB
0x1B80_0000 ─────────────────────────────────────────────
Activation/workspace/logits-topk/ring buffer
约 24–48MiB
0x2000_0000 ─────────────────────────────────────────────
End of 512MiB DDR
注:上面是概念布局,真实地址要根据 PYNQ-Z2 的 device tree、kernel memory map、CMA/reserved-memory 和 Vivado address map 决定。
5. PS-PL 总线与 DDR 数据流设计
5.1 推荐总线分工
| 接口 | 方向/用途 | 设计建议 |
|---|---|---|
| AXI-Lite / M_AXI_GP0 | PS 控制 PL 寄存器 | 写 base address、layer table 地址、seq_len、token_id、start、interrupt enable;读 status、cycle counter、error flags。 |
| S_AXI_HP0 | PL master 读权重 | 最重要通道。长 burst、顺序地址、64-bit 数据宽度、尽量只读。 |
| S_AXI_HP1 | PL master 读/写 KV cache | decode 中每层写当前 K/V,attention 读历史 K/V。与权重流分离,减少互相阻塞。 |
| S_AXI_HP2 | PL master 读写 workspace / top-k / 可选第二权重流 | 可用于 LM head 单独 streamer,或预取下一 tile。 |
| S_AXI_HP3 | 可选 profiling / 双缓冲 / prefill block | 不是必须。PYNQ-Z2 DDR 总带宽有限,多 HP 口提升调度弹性,不会突破 DDR 物理峰值。 |
| Interrupt | PL 通知 PS | 每 token 完成、中断错误、DMA underrun、top-k ready。 |
UG585 对 AXI_HP 的定位是让 PL bus master 获得到 DDR/OCM 的高带宽路径,4 个 HP 接口内部带 FIFO,并通向 DDR/OCM;另一个 UG585 页面也强调 4 个 64-bit HP 接口提供最大的聚合接口带宽。[5]
5.2 总体数据流图
5.3 DDR 访问原则
| 原则 | 具体做法 | 原因 |
|---|---|---|
| 顺序化权重布局 | 按执行顺序存:layer0 norm/QKV/O/MLP,layer1...,最后 LM head;每个矩阵按 tile 连续对齐。 | 让 HP read 发长 burst,降低随机访问开销。 |
| 避免完整 logits 写 DDR | LM head 边读权重边算 dot,PL 内维护 top-k heap。 | vocab 151,936,写完整 logits 再由 PS 采样浪费带宽。 |
| 双缓冲 | weight tile FIFO A/B,当前 tile 计算时预取下一 tile。 | 隐藏 DDR burst latency。 |
| KV cache 分层连续 | 推荐 layout:layer → kv_type → token → kv_head → head_dim,每层每 token 对齐。 | attention 读历史 KV 时可以按 layer/head 顺序扫。 |
| PS 缓存一致性 | 共享 buffer 用 non-cacheable、dma_alloc_coherent、reserved-memory 或严格 flush/invalidate。 | 避免 PS 写 token/配置后 PL 读到旧数据,或 PL 写 top-k 后 PS 读到旧缓存。 |
| 降低 Linux 干扰 | 推理时关闭 Jupyter、大量网络服务和 swap;固定 CPU governor。 | DDR 是主瓶颈,PS 服务会抢 DDR 和 cache。 |
6. PL 内部计算架构
6.1 推荐模块划分
| 模块 | 职责 | 实现建议 |
|---|---|---|
| Control Sequencer | 执行每层微码、发起 DMA、控制 GEMV/Attention/MLP 阶段 | 类似 gateGPT 的 sequencer 思路,但地址和维度来自 DDR 中的 layer descriptor table。 |
| Weight DMA Engine | 从 DDR 顺序读 INT4 权重、scale、zero | 64-bit AXI master,长 burst,2–4 个 outstanding,BRAM FIFO。 |
| W4 Unpack / Dequant | 把 nibble 权重解包,与 group scale/zero 结合 | 建议 group=64 或 128;scale 可 FP16 存储、内部转 fixed-point。 |
| Vector Buffer | 保存当前 hidden、norm 后向量、partial output | hidden=896,INT16 双缓冲只需几 KB;BRAM 足够。 |
| MAC Array | 矩阵-向量主计算 | 第一版 32–64 lanes;成熟后 64–96 lanes。decode 被 DDR 限制,盲目堆 128+ lanes 意义不大。 |
| RMSNorm Engine | 平方和、rsqrt、scale 乘 | INT32/FP32 累加,rsqrt 可 LUT + Newton 一步。 |
| RoPE Engine | 对 Q/K 做旋转位置编码 | sin/cos 表放 BRAM 或 DDR 小表;因为 head_dim=64,开销不大。 |
| Attention Engine | QK dot、causal mask、softmax、V 加权求和 | 流式 softmax:第一遍 max 或在线 max/sum;KV cache INT8 读入后乘 scale。 |
| SwiGLU / SiLU Engine | MLP gate 激活和逐元素乘 | SiLU 用 LUT/PWL 近似;MLP 的 GEMV 是大头。 |
| Top-K Engine | LM head 输出时保留 top-k token/score | 堆或分段比较树。只返回 top-k,PS 做 temperature/top-p/repetition penalty。 |
6.2 为什么 decode 不需要太大的 MAC 阵列
一个 token 的总权重访问约 265–285MiB,PYNQ-Z2 DDR3 峰值只有约 2.1GB/s,现实有效读带宽更低。即使 PL 计算阵列很强,只要权重必须每 token 从 DDR 扫一遍,就会被内存带宽限制。
因此第一版推荐 32–64 lanes,主要目标是稳定吞吐、少 stall、易时序收敛。更大 MAC 阵列适用于 block prefill,因为 block prefill 可以让同一份权重同时服务多个 token,计算/带宽比上升。
6.3 片上 BRAM 粗分配
| BRAM 用途 | 建议容量 | 说明 |
|---|---|---|
| Weight tile FIFO A/B | 64–128KB | 用于 DDR burst 缓冲和预取。 |
| Scale/zero FIFO | 8–32KB | 与权重 group 对齐读取。 |
| Activation vectors | 16–32KB | hidden、norm hidden、attention output、residual。 |
| MLP tile / partial sums | 64–128KB | down_proj 和 gate/up 的中间 tile。 |
| Attention score/softmax buffer | 32–128KB | 若完全流式可更少;为了简化可按 head 分块缓存。 |
| Top-K heap / compare buffer | <16KB | 保存 top-32/top-64 token 和 score。 |
| 控制表 / debug counters | 8–16KB | 微码、状态、profiling。 |
合计控制在 300–500KB 较合理,给 AXI FIFO、时序调试和未来 prefill block 留余量。
7. Prefill 与 Decode 两种流程
7.1 Decode:最优先实现
PS:
prompt 已经有 KV cache
写入 current_token_id, seq_len, base addresses
start PL
PL for each generated token:
1. embedding lookup -> hidden[896]
2. for layer in 0..23:
RMSNorm(hidden)
Q/K/V GEMV + bias
RoPE(Q, K)
write current K,V to DDR KV cache
read historical K,V from DDR
attention(Q, K_cache, V_cache) -> attn_out
O_proj GEMV + residual
RMSNorm
gate_proj/up_proj/down_proj + SwiGLU + residual
3. final RMSNorm
4. LM head streaming projection over 151,936 vocab rows
5. Top-K only -> DDR small buffer / register
PS:
read top-k, apply sampling, append token, repeat
Decode 是每 token 扫一次模型权重,数据复用低,但控制简单,是第一阶段最应该打通的闭环。
7.2 Prefill:真正影响交互体验的难点
如果 prefill 也按 token-by-token decode 方式做,128 token prompt 在 1 token/s 时就要 2 分钟以上,用户体验不可接受。因此有三种选择:
| 方案 | 实现难度 | 效果 | 建议 |
|---|---|---|---|
| Naive token-by-token prefill | 低 | 极慢 | 第一版可用于验证,prompt 限制到几十 token。 |
| 固定系统 prompt 的 KV 预缓存 | 中 | 减少重复系统提示词开销 | 很适合 demo,例如固定中文助手 system prompt。 |
| Block prefill,B=4/8/16 | 高 | 权重读一次服务多个 token,显著提升 prefill | 第二阶段必须做,否则只能短 prompt。 |
Block prefill 的基本思想是把 GEMV 变成小型 GEMM:同一个权重 tile 读入后,对 B 个 token 的 activation 同时做乘加。这样每个 token 摊到的权重带宽下降,但 MAC 需求上升。PYNQ-Z2 的 64-lane 阵列在 decode 可能吃不满 DDR,在 block prefill 反而能更充分利用。
7.3 推荐 prompt 策略
- 第一版演示:固定 system prompt + 用户 prompt ≤64 tokens,上下文 512。
- 第二版演示:prompt ≤256 tokens,上下文 1024/2048,加入 block prefill。
- 不要以 Qwen2.5 官方 32K context 作为 PYNQ-Z2 目标。模型支持 32K,不代表这块板能实用地跑 32K。
8. 性能预估与瓶颈
8.1 带宽主导的 decode 估算
每 token 的主数据量近似:
| 上下文 | W4 模型包 | INT8 KV 读量/步 | 合计粗估 | 1.0GB/s 有效带宽理论上限 |
|---|---|---|---|---|
| 512 | 约 280MiB | 约 3MiB | 约 283MiB | 约 3.4 tok/s |
| 1024 | 约 280MiB | 约 6MiB | 约 286MiB | 约 3.3 tok/s |
| 2048 | 约 280MiB | 约 12MiB | 约 292MiB | 约 3.3 tok/s |
| 4096 | 约 280MiB | 约 24MiB | 约 304MiB | 约 3.1 tok/s |
| 8192 | 约 280MiB | 约 48MiB | 约 328MiB | 约 2.9 tok/s |
上表是“只看 DDR 读”的乐观估算,还没扣除 DDR 仲裁、scale 读取、写 KV、softmax、非线性、PS 采样、AXI stall、地址切换等开销。因此更现实的端到端 decode 目标是:
| 实现成熟度 | 有效 DDR 流带宽假设 | 端到端 decode 速度预估 | 备注 |
|---|---|---|---|
| 早期 PYNQ/Python 调试版 | 很不稳定 | 0.1–0.8 tok/s | Python 控制、内存分配、DMA stall 会拖垮结果。 |
| C/C++ + 单 HP + 基本双缓冲 | 0.4–0.8GB/s | 0.8–1.8 tok/s | 较现实的第一版目标。 |
| 多 HP 分流 + 长 burst + 预取 + 精简 Linux | 0.8–1.2GB/s | 1.5–2.8 tok/s | 需要认真做内存布局和 AXI profiling。 |
| 高度优化 bare-metal / block prefill / 专用调度 | 接近板级可持续上限 | decode 2–3+ tok/s,prefill 明显改善 | 是否超过 3 tok/s 取决于实测 DDR 和时序。 |
8.2 计算能力是否够
若使用 64-lane MAC 阵列、150MHz:
单 token 近似要扫 0.49B 权重,即约 0.49G 级乘加。若权重都在片上,9.6GMAC/s 的计算时间约 51ms;但 DDR 读 280MiB 在 1GB/s 下已经约 280–300ms。因此 decode 主要是 DDR 带宽瓶颈,不是 DSP 数量瓶颈。220 个 DSP slice 足够做一个复用阵列,但不足以改变“每 token 必须从 DDR 流过整个模型”的事实。
8.3 LM head 是特殊瓶颈
由于 vocab_size=151,936,LM head / tied embedding 矩阵约 136M 权重,INT4 raw 约 64.9MiB。也就是说,每个 token 仅为了算输出 logits,就要流过约 65–75MiB 的权重包。缓解方式如下:
| 方法 | 是否改变模型语义 | 建议 |
|---|---|---|
| PL 内流式 Top-K,不写完整 logits | 不改变 | 必须做 |
| LM head 权重连续布局、单独 HP streamer | 不改变 | 推荐 |
| 只算候选词表 shortlist | 会改变 | 适合封闭领域 demo,不适合通用 Qwen。 |
| 词表裁剪/重训 tokenizer | 改变模型 | 工程量大,已经不是原始 Qwen2.5-0.5B。 |
| 在 PS 上算 LM head | 不改变 | 不推荐 ARM A9 太慢,且仍要读大矩阵。 |
9. 主要风险与规避策略
| 风险 | 表现 | 规避策略 |
|---|---|---|
| 512MB DDR 容量不足 | 模型能放但 OS/KV/workspace 没空间;PYNQ allocate 失败 | W4 all weights;INT8 KV;context 1K/2K;精简 Linux;reserved-memory;避免默认 Jupyter 常驻。 |
| DDR 带宽不足 | MAC 阵列等待权重,token/s 上不去 | 长 burst;按执行顺序重排权重;多 HP 分流;双缓冲;profiling HP stall;减少 PS DDR 活动。 |
| Prefill 太慢 | 输入稍长就等待数十秒到数分钟 | 固定 prompt KV cache;限制 prompt;第二阶段实现 block prefill。 |
| INT4 精度下降 | 输出重复、乱码、中文能力下降 | 优先用 AWQ/GPTQ 类校准量化;group=64;保留 RMSNorm/softmax 高精度;必要时首尾层或 LM head 用更高精度但要重算内存。 |
| 时序收敛困难 | 150/200MHz 无法过 timing | 先 125MHz;MAC array 分区;AXI 和 compute 分 clock domain;pipeline W4 unpack 和 accumulation。 |
| 缓存一致性 bug | PL 读旧数据、PS 读旧 top-k | non-cacheable buffer;flush/invalidate;使用 dma_alloc_coherent 或 reserved-memory driver。 |
| MicroSD 被误用为运行时权重流 | 速度极慢,甚至卡死 | MicroSD 只在启动/加载时使用。模型必须搬进 DDR 后再推理。 |
| USB 供电不足 | 高负载下重启/不稳定 | 使用稳定外部 12V 供电,尤其是高 DDR/PL 活动测试时。 |
10. 推荐实现路线图
Phase 0:板级基准测试
- 不要先写 Qwen 全链路。先做 HP port DDR read bandwidth benchmark。
- 测试 64-bit HP、不同 burst 长度、不同 outstanding、单 HP/多 HP 下的可持续读带宽。
- 测试在默认 PYNQ、关闭 Jupyter、精简 Linux、bare-metal 下的差异。
- 目标:至少证明能从 DDR 给 PL 持续读出 0.5GB/s 以上;否则 Qwen decode 会非常慢。
Phase 1:单矩阵 GEMV 加速器
- 实现 W4 unpack + group scale + INT16 activation × W4 的 GEMV。
- 先跑 Qwen 的一个矩阵,例如
q_proj或mlp.down_proj,和 Python reference 对齐。 - 验证 tile 布局、scale 布局、AXI burst、定点误差。
Phase 2:单 decoder layer
- 实现 RMSNorm、QKV、RoPE、attention、O_proj、MLP、residual。
- 只跑 1 层、1 token,对齐参考输出。
- 记录每个模块 cycle:权重读、MAC、KV 读、softmax、写回。
Phase 3:24 层 decode 闭环
- 实现 layer descriptor table,权重按层连续布局。
- 上下文先 128/512,KV cache INT8。
- 先不追性能,优先确保数值稳定和不会 DMA underrun。
Phase 4:LM head streaming Top-K
- 实现 tied embedding 的输出 projection。
- PL 只输出 top-32 或 top-64 token/score。
- PS 完成 temperature、top-p、repetition penalty 和采样。
Phase 5:优化 prefill 与系统软件
- 实现固定 system prompt KV cache。
- 实现 block prefill,先 B=4,再 B=8。
- 把 Python 调度替换成 C/C++ 或 bare-metal 控制。
- 重新训练/微调量化参数,测试中文输出质量。
11. 最终建议
在 PYNQ-Z2 上继续考虑 Qwen2.5-0.5B 是可以的,但要把目标定义清楚:
| 目标定义 | 可行性 | 说明 |
|---|---|---|
| 全片上硬化,权重放 BRAM | 不可行 | BRAM 只有约 630KB,权重 INT4 后仍约 250MiB 级。 |
| QSPI 存模型,PL 运行时读取 | 不可行 | QSPI 只有 16MB,远小于模型。 |
| MicroSD 存模型,每 token 从 SD 读 | 不可取 | SDIO 带宽远低于 DDR,推理速度会不可接受。 |
| MicroSD 存模型,启动后加载到 DDR,PL 从 DDR 流式推理 | 推荐 | PYNQ-Z2 上唯一现实路线。 |
| 默认 PYNQ Python/Jupyter 端到端高性能推理 | 仅适合调试 | Python 适合 bring-up,不适合最终 token loop。 |
| 精简 Linux/bare-metal + W4 + INT8 KV + PL 数据通路 | 工程可做 | 推荐路线,现实目标 0.5–2.5 tok/s。 |
一句话总结:PYNQ-Z2 仍然能做 Qwen2.5-0.5B 的“FPGA 硬化数据通路 + DDR 流式权重”的研究型 demo,但比 1GB DDR 的 Zynq-7020 板子更吃紧;必须牺牲上下文长度、使用 INT4 权重、INT8 KV、精简系统软件,并把性能预期放在低 token/s 区间。
参考资料
- AMD AUP PYNQ-Z2 页面:列出 XC7Z020-1CLG400C、512MB DDR3、128Mbit QSPI、MicroSD、125MHz PL clock、85K logic cells、630KB BRAM、220 DSP 等。
https://www.amd.com/en/corporate/university-program/aup-boards/pynq-z2.html - PYNQ-Z2 Reference Manual v1.0:DRAM 为 Micron 256Mx16 DDR3,512MB,16-bit,最高 525MHz/1050Mbps;QSPI 为 Spansion S25FL128S,16MB;MicroSD 支持 SDIO 1-bit/4-bit,最高 50MHz;供电和时钟说明。
https://www.mouser.com/datasheet/2/744/pynqz2_user_manual_v1_0-1525725.pdf - Qwen/Qwen2.5-0.5B-Instruct 模型卡:0.49B 参数、0.36B non-embedding、24 层、GQA 14Q/2KV、context length 等。
https://huggingface.co/Qwen/Qwen2.5-0.5B-Instruct - Qwen2.5-0.5B-Instruct config.json:hidden_size=896、intermediate_size=4864、num_attention_heads=14、num_key_value_heads=2、vocab_size=151936、tie_word_embeddings=true。
https://huggingface.co/Qwen/Qwen2.5-0.5B-Instruct/blame/main/config.json - AMD UG585, AXI_HP Interfaces / PL DMA via AXI HP:4 个 AXI_HP 接口为 PL bus masters 提供到 DDR/OCM 的高带宽通路,4 个 64-bit 接口提供最大聚合接口带宽。
https://docs.amd.com/r/en-US/ug585-zynq-7000-SoC-TRM/AXI_HP-Interfaces
https://docs.amd.com/r/en-US/ug585-zynq-7000-SoC-TRM/PL-DMA-via-AXI-High-Performance-HP-Interface - PYNQ allocate 文档:PL 中连接 HP/ACP 的 IP 访问 PS DRAM 前,需要分配/保留物理地址可见的内存,并把地址传给 IP。
https://pynq.readthedocs.io/en/v2.7.0/pynq_libraries/allocate.html