推理芯片设计实验报告

基于 sympy + CMOS 门级建模, 从最小译码器一路推演到 27B LLM 推理芯片规模估算


目录

  1. 实验概述
  2. 实验环境
  3. [第一阶段 · 基础门电路验证 (3-to-8 译码器)](#第一阶段 · 基础门电路验证 (3-to-8 译码器))
  4. [第二阶段 · 小规模乘法器两级 SOP 最小化](#第二阶段 · 小规模乘法器两级 SOP 最小化)
  5. [第三阶段 · 大规模乘法器 (8×8 / 16×16)](#第三阶段 · 大规模乘法器 (8×8 / 16×16))
  6. [第四阶段 · 阵列 vs 树形结构延迟对比](#第四阶段 · 阵列 vs 树形结构延迟对比)
  7. [第五阶段 · 4×4 矩阵乘法模块方案对比](#第五阶段 · 4×4 矩阵乘法模块方案对比)
  8. [第六阶段 · LLM 推理芯片规模 vs 速度](#第六阶段 · LLM 推理芯片规模 vs 速度)
  9. 总结与展望
  10. [附录: 实验脚本清单](#附录: 实验脚本清单)
  11. [附录: 实验脚本源码](#附录: 实验脚本源码)

1. 实验概述

目标 : 从最基础的门电路出发, 用 sympy 的 SOPform 工具验证 CMOS 数字电路设计的"晶体管数估算"方法, 并把这一思路逐级扩展 , 最终用于 27B 参数 LLM 推理芯片的规模与速度估算。

研究方法:

复制代码
3-to-8 译码器
   ↓ (1 输入对应 1 输出, 8 位最小案例验证)
2×2 → 3×3 → 4×4 乘法器
   ↓ (用 SOP 最小化, 确认增长趋势)
8×8 (sympy 超时失败)
   ↓
16×16 乘法器 (SOP 不可行, 改结构化估算)
   ↓
阵列 vs 树形 (延迟分析)
   ↓
4×4 矩阵乘法模块 (5 种方案对比)
   ↓ (找到 systolic 是 LLM 推理最优)
27B LLM 推理: 芯片规模 vs 速度

关键认知 : 真正的瓶颈不是 SOP 字面量, 而是芯片面积 / 内存带宽 / 数据流


2. 实验环境

OS Windows 11
Python 3.13 (.venv)
包管理 uv
核心库 sympy 1.14.0 (含 logic.boolalg.SOPform)
工作目录 D:\eda_llm\

环境搭建:

bash 复制代码
# 原计划: pyeda (需 MSVC, 失败)
# 改用:   sympy (纯 Python, 自带 SOPform/POSform)
uv pip install sympy --python .venv

晶体管估算模型 (标准 CMOS 静态逻辑):

门类型 T 数
INV 2
2-NAND 4
2-NOR 4
2-AND (NAND+INV) 6
2-OR (NOR+INV) 6
2-XOR (XNOR+INV) 12
1-bit 半加器 (XOR+AND) 8
1-bit 全加器 (经典 28T) 28

两级 SOP 估算公式:

复制代码
T = (2L + 2K)            [每个 AND 项 = NAND + INV, L 个字面量, K 个积项]
  + (2K + 2)              [1 个 K-输入 OR = NOR + INV]
  + 2N × 2                [2N 个输入反相器]

3. 第一阶段 · 基础门电路验证 (3-to-8 译码器)

目的: 用最简单的电路验证 SOP 最小化 + 晶体管估算流程

真值表

3 个输入 (a, b, c) → 8 个 one-hot 输出 (Y0~Y7), 2³ = 8 行

SOP 最小化结果

  • 每个 Yi 对应 1 个 minterm, 已是最简
  • 例: Y3 = a & b & ~c
  • 全 8 个输出合计 24 个字面量

晶体管估算

方案 描述 晶体管数
A active-high, 输入有反变量 64 T
B active-high, 输入无反变量 70 T
C active-low (NAND-NAND 直出) 54 T

关键发现: NAND-NAND 结构 (PLA 风格) 比纯 AND-OR 省 16 T (8 个输出 INV)

脚本

decoder3to8.py


4. 第二阶段 · 小规模乘法器两级 SOP 最小化

目的: 验证 SOP 估算方法, 摸清 2 级 SOP 随位宽的增长曲线

4.1 2×2 乘法器 (4 入 4 出, 16 行)

输出 K L Wmax T
c0 1 2 2 10
c1 4 12 3 42
c2 2 6 2 22
c3 1 4 2 14
合计 88 T

c0 = a0 & b0, c3 = a1 & b1 --- 两端极简, 中间是加法逻辑

4.2 3×3 乘法器 (6 入 6 出, 64 行)

输出 K L Wmax T
c0 1 2 2 10
c1 4 12 3 42
c2 9 36 4 110
c3 10 44 6 130
c4 8 35 5 104
c5 3 14 5 42
合计 438 T
+ 输入 INV 450 T

观察: c3 最复杂 (中间位), c0/c5 最简单 (LSB/MSB 受约束少)

4.3 4×4 乘法器 (8 入 8 出, 256 行)

输出 minterms K L Wmax T
c0 64 1 2 2 10
c1 96 4 12 3 42
c2 112 9 36 4 110
c3 120 30 172 8 466
c4 100 36 214 8 574
c5 88 32 189 7 508
c6 66 22 121 6 332
c7 32 9 46 6 130
合计 2 172 T
+ 输入 INV 2 188 T

关键观察:

  • c0 = a0 & b0 (LSB 极简, 1 项)
  • c3/c4 是瓶颈 (K=30-36, Wmax=8)
  • 2 级 SOP 耗时 0.6s

4.4 增长趋势

N 行数 2 级 SOP T 阵列 T SOP/阵列
2 16 88 120 0.7× ← SOP 占优
3 64 442 282 1.6× ← 持平
4 256 2 188 512 4.3× ← SOP 输
5 1 024 ~13 000 ~770 ~17× ← 估算
6 4 096 ~80 000 ~1 100 ~73× ← 估算
8 65 536 不可行 ~2 100 ---
16 4.3×10⁹ 完全不可行 ~8 600 ---

关键拐点: N=3 之前 2 级 SOP 占优, N≥4 后结构化阵列必胜。

4.5 sympy 位序坑

实验中踩到的坑: sympy 的 SOPform 中, minterm N 的 bit i 对应 inputs 列表的最后一个 变量。必须用 bit_reverse() 函数把位顺序翻转。

正确打包公式:

python 复制代码
code = (bit_reverse(av, N) << N) | bit_reverse(bv, N)

脚本

mult3x3.py, mult4x4.py


5. 第三阶段 · 大规模乘法器 (8×8 / 16×16)

5.1 8×8 尝试 (16 入 16 出, 65 536 行)

实验结果 : sympy SOPform 调用 Espresso 算法, 对 16 变量 / 上万 minterm 极慢

  • 单个输出位计算超过 15 分钟仍无结果
  • 结论: 8×8 走 2 级 SOP 不可行

5.2 16×16 乘法器结构化估算

真值表规模: 2^32 ≈ 4.3×10⁹ 行, 存储需 ~16 GB → 不可枚举

结构 AND FA HA CPA 总 T 延迟
阵列 (ripple) 256 240 32 0 8 576 32 FA 级
Wallace 树 256 127 0 31 6 024 ~16 CSA 级
Dadda 树 256 95 0 31 5 128 ~14 CSA 级

Wallace 树拆解:

复制代码
256 个 2-AND 部分积        =  1 536 T
Wallace CSA 树 (127 个 FA)  =  3 556 T
最终 32-bit CPA (31 个 FA)  =    868 T
32 个输入反相器            =     64 T
─────────────────────────────────────
合计                       =  6 024 T

关键发现:

  • 2 级 SOP vs 结构化差 6 个数量级 (10¹¹ vs 10⁴ T)
  • 实际工程 N≥4 永远用结构化方案
  • 真实设计 (28nm) 16×16 multiplier ≈ 3 000-5 000 GE, 与估算吻合

脚本

mult8x8.py (超时), mult16x16.py


6. 第四阶段 · 阵列 vs 树形结构延迟对比

目的: 量化两种主流结构的延迟差异

6.1 延迟模型

  • 1 个全加器延迟 = 1 T_FA (静态 CMOS 约 50-200 ps)
  • 阵列: T = 2N × T_FA (沿对角线)
  • Wallace: T = log_{1.5}(N²) × T_FA + T_CPA
  • CPA 选型: ripple (2N), Kogge-Stone (log₂N + 2), CLSA (√N)

6.2 延迟对比表

N 部分积 阵列 2N Wallace CSA + ripple CPA + Kogge-Stone 速度比
2 4 4 2 10 6 0.67×
3 9 6 4 16 8 0.75×
4 16 8 6 22 11 0.73×
8 64 16 10 42 16 1.00×
16 256 32 13 77 20 1.60×
32 1024 64 16 144 24 2.67×
64 4096 128 20 276 29 4.41×
128 16384 256 23 535 33 7.76×

6.3 关键发现 (反直觉)

复制代码
N ≤ 4 :  阵列比 Wallace 还快!   (CPA 拖累了 Wallace)
N = 8 :  持平
N ≥ 16 : Wallace 优势明显,  N=128 时快 7.76 倍

根本原因:

  • 阵列: O(N) --- 线性
  • Wallace + Kogge-Stone: O(log N) --- 对数

6.4 4×4 临界路径示意

阵列 (8 T_FA):

复制代码
a0&b0 ─┐
       ├──FA─┐
a1&b0 ─┘    │
           ├──FA─┐
a2&b0 ─────┘    │
                ├──FA────→ 输出
a3&b0 ─────────┘
   ↑  7 个 FA 串在一条线上

Wallace (6 CSA + Kogge-Stone CPA = 11 T_FA):

复制代码
16 个 AND 部分积
  ↓ 6 级 CSA 树  (并行, 每级只 1 FA 深度)
  2 个数
  ↓ Kogge-Stone CPA  (log₂ 8 = 3 级)
  8 个输出

6.5 工业选择

场景 选择 理由
N ≤ 8 阵列 速度不输, 版图超规则
N = 16-32 Wallace + 普通 CPA 面积/速度折衷
N ≥ 64 Wallace/Dadda + Kogge-Stone 速度优势碾压
超高频 Booth + Wallace + KS 部分积减半
低功耗 阵列 + 流水线 漏电小, 流水降电压

脚本

delay_compare.py


7. 第五阶段 · 4×4 矩阵乘法模块方案对比

问题定义: 4×4 矩阵 C = A × B, 元素 4-bit, 累加到 10-bit

复制代码
C[i][j] = Σk=0..3  A[i][k] × B[k][j]    (4 次乘, 3 次加)

共 16 个内积, 每个内积需 4 个 4×4 mult。

7.1 五种方案

方案 面积 T 延迟 吞吐 AT 适用
A 1 mult + 累加 (时分) 806 16 cy 0.06/cy 12 896 极小芯片
B 4 mult + 4 累加 3 224 4 cy 0.25/cy 12 896 中等并行
C 16 mult + 16 累加 (内积全并行) 12 800 1 cy 1.0/cy 12 800 单次最快
D 4×4 systolic (16 PE) 12 992 7 cy 1/cy 90 944 流式最优
E 16 mult + 加法树 18 944 1 cy 1.0/cy 18 944 无累加

7.2 C 方案 (内积全并行) 拆解

复制代码
1 个内积单元 (PE):
  4 个 4×4 mult (并行)        = 4 × 512 = 2 048 T
  1 个 4-输入加法树            = 3 × 8 × 28 = 672 T
  小计                         =           2 720 T

16 个内积单元                  = 16 × 2 720 = 43 520 T
+ 16×8-bit 输出寄存器          = 16 × 8 × 8 = 1 024 T
─────────────────────────────────────────────────────
合计                          =           44 544 T

7.3 D 方案 (systolic) 拆解

复制代码
1 个 PE (处理单元):
  1 个 4×4 乘法器              =  512 T
  1 个 8-bit 加法器             =  224 T
  8 个寄存器 (流水锁存)         =   64 T
  2 个 2 选 1 mux              =   12 T
  小计                          =  812 T

16 个 PE                       = 12 992 T

7.4 4×4 systolic 数据流

复制代码
A flows left-to-right,  B flows top-to-bottom,  C accumulates in PE

      B[0,0]   B[0,1]   B[0,2]   B[0,3]
        |        |        |        |
      +----+   +----+   +----+   +----+
 A--> | PE |-->| PE |-->| PE |-->| PE |   ← 第 0 行
      +--^-+   +--^-+   +--^-+   +--^-+
         |       |        |       |
      +--v-+   +--v-+   +--v-+   +--v-+
 A--> | PE |-->| PE |-->| PE |-->| PE |   ← 第 1 行
      +--^-+   +--^-+   +--^-+   +--^-+
        ...     ...      ...      ...

Cycle 0..6:   pipeline fill (A, B feeding in)
Cycle 7:      C[0,0] 第一个结果
Cycle 8..15:  剩下 15 个结果
稳态吞吐:      1 个 C[i,j] / cycle
完整 4×4 矩阵乘: 15 周期

7.5 关键发现: C vs D

场景 C 方案 D 方案 胜者
只算 1 次矩阵 1 cy ✓ 15 cy ✗ C
连续算 ≥ 7 次 7 cy 起就累 23 cy ✓ D

C 适合"突发" (单次大批量)

D 适合"流水线" (数据持续流过)

7.6 推荐: 按目标挑

目标 推荐 理由
极小面积 (嵌入式) A 1 mult 时分复用, 16 周期换 700 T
中等性能, 省面积 B 4 mult 并行, 4 周期完成, 3K T
单次最快 C 1 周期, 12.8K T
流式吞吐 D systolic, 1 结果/cycle, 13K T

脚本

matrix4x4.py


8. 第六阶段 · LLM 推理芯片规模 vs 速度

关键问题: 用 D 方案 (systolic) 设计芯片, 27B LLM 推理速度与芯片规模的关系是什么?

8.1 模型假设

模型 27B 参数 (Qwen2.5-27B / Gemma-2-27B)
参数量 27 × 10⁹
量化 INT8 (主流) → 27 GB 权重
每 token 算力需求 27 GOPs (INT8)
每 token 内存加载 27 GB (decode 模式)

8.2 Decode 模式: tok/s = 内存带宽 / 27 GB

带宽 读 27GB 用时 tok/s 体感
50 GB/s 540 ms 1.9 勉强能用
200 GB/s 135 ms 7.4 打字机
400 GB/s (M2 Max) 67 ms 14.8 可用
800 GB/s (AI100) 34 ms 29.6 流畅
1.6 TB/s 17 ms 59 很快
3.35 TB/s (H100) 8 ms 124 实时语音级
5 TB/s 5 ms 185 理论极限

8.3 主流芯片实测 (27B INT8 Decode)

芯片 systolic TOPS 面积 功耗 带宽 27B tok/s
Apple M2 Max 38 430 mm² 60 W 400 GB/s 15
TPU v2 128×128 45 300 200 700 26
TPU v4 128×128 275 650 170 1 200 44
A100 624 826 300 2 000 74
H100 1 979 814 700 3 350 124
理论 64×64 64×64 4 40 20 200 7.4
理论 128×128 128×128 16 160 80 500 18.5
理论 256×256 256×256 66 640 320 1 000 37

核心观察 : H100 的 TOPS 是 1979, 但 27B 速度只到 124 tok/s --- 算力根本没用完, 瓶颈是带宽。

8.4 想达到 X tok/s, 需要什么芯片?

目标 tok/s 需要带宽 需要算力 systolic 阵列 面积 功耗 对标
10 270 GB/s 1 24×24 5 mm² 3 W 边缘 NPU
30 810 GB/s 2 41×41 16 8 中端
50 1.35 TB/s 3 52×52 27 14 A100/昇腾
100 2.7 TB/s 5 74×74 54 27 A100/昇腾
200 5.4 TB/s 11 104×104 108 54 H100

8.5 关键公式

复制代码
Decode 速度 (1 batch):
  tok/s = 内存带宽(GB/s) / 27         (INT8, 内存瓶颈)

Prefill 速度 (大 batch):
  tok/s = min( TOPS / 27G,  带宽 / 27G )

芯片规模:
  TOPS  ~  N^2 × 1 GHz × 1 op/cycle/PE
  Area  ~  N^2 × 0.01 mm^2/PE
  Power ~  N^2 × 0.5 W      (粗估)

8.6 为什么算力"浪费"?

复制代码
Prefill 模式 (大 batch): 算力利用率高
  tok/s = TOPS / (27 GFLOPs × batch)         ← TOPS 主导

Decode 模式 (1 batch):   算力极度浪费
  27 GOPs/token (INT8) ÷ 1 cycle = 27 GOPS 算力就够
  但 100 TOPS 芯片做 decode 只用 0.027% 算力!
  99.97% 的算力在等内存

现代 LLM 推理芯片的算力都是为 Prefill 准备的, Decode 几乎只用带宽。

8.7 业界参考

芯片 阵列规模 元素位宽 工艺 用途
Google TPU v1 256×256 systolic INT8 28nm 推理
Google TPU v2 128×128 systolic BF16 16nm 训练+推理
Google TPU v4 多芯片 pod INT8 7nm 数据中心
华为昇腾 910 32×32×16 cube INT8/FP16 7nm 数据中心
寒武纪 MLU370 16× systolic INT8 7nm 推理
Tesla Dojo 大型 systolic 网格 BF16/CFP8 7nm 训练

全部是 systolic, 没有任何一个用方案 A/B/C 的纯并行

脚本

llm27b_inference.py


9. 总结与展望

9.1 实验核心结论

阶段 结论
1. 译码器 SOP + 晶体管估算方法可正常工作, NAND-NAND PLA 省 INV
2. 小乘法器 N=2 时 SOP 占优; N≥4 时结构化阵列必胜 (4.3× 起)
3. 大乘法器 16×16 必须用结构化 (Wallace/Dadda), SOP 完全不可行
4. 延迟 阵列 O(N) vs 树形 O(log N), N=16 起树形开始赢
5. 矩阵模块 5 方案对比, systolic (D) 适合流式, 全并行 © 适合单次
6. LLM 推理 芯片速度瓶颈是 DRAM 带宽, 不是 TOPS

9.2 从 1 个 PE 到 LLM 芯片的完整链路

复制代码
PE (处理单元)
  = 1 个 4×4 mult (512 T) + 1 个 8-bit 加法器 (224 T) + 8 个 FF (64 T) + 2 个 mux
  ≈ 800 T

4×4 systolic 阵列 (D 方案)
  = 16 PE
  ≈ 13 000 T

32×32 systolic 阵列 (LLM 推理实际规模)
  = 1024 PE
  ≈ 800 000 T
  + HBM 控制器
  + KV cache
  + Softmax / LayerNorm 单元
  + DMA 引擎
  + host 接口

完整 LLM 推理芯片
  ~ 100~300 mm² die + 4~8 个 HBM 堆栈
  ~ $30~100 单芯片成本
  ~ 27B Decode 速度: 50-150 tok/s

9.3 速度的"硬约束"

复制代码
Decode 速度 (1 batch, 27B):
  tok/s = 内存带宽(GB/s) / 27 GB
  → 速度翻倍 = 带宽翻倍 = HBM 堆数翻倍 = 成本 ~2×

Prefill 速度:
  tok/s = min(TOPS, 带宽) / 27G
  → 算力和带宽共同决定

加速方法:
  - INT4 量化       : 27GB → 13.5GB, 速度直接 ×2
  - KV cache 优化   : 让 27B 实际加载量更少
  - Speculative dec : 小模型猜 token, 速度 ×2-3
  - 更大 HBM 堆栈   : 带宽翻倍

9.4 下一步工作建议

方向 内容 价值
1. PE Verilog 实现 D 方案 4×4 systolic PE 的 RTL 真正可综合
2. INT4 PE 把 4-bit 量化加进 PE 设计 减半面积/带宽
3. Booth 编码 部分积数从 N 减到 N/2 再省 CSA 层级
4. Kogge-Stone CPA 拆 1-bit FA + KS 加法器 完整延迟分析
5. 完整 27B 芯片 估 PE + KV + Softmax + DMA 总面积 接近流片规格

10. 附录: 实验脚本清单

文件 功能 状态
decoder3to8.py 3-to-8 译码器 SOP + 晶体管估算 OK
mult3x3.py 3×3 乘法器两级 SOP 完整分析 OK
mult4x4.py 4×4 乘法器两级 SOP 完整分析 OK
mult8x8.py 8×8 乘法器 (sympy 超时失败) FAILED
mult16x16.py 16×16 乘法器结构化估算 OK
delay_compare.py 阵列 vs Wallace 延迟对比 OK
matrix4x4.py 4×4 矩阵乘法模块 5 方案对比 OK
llm27b_inference.py 27B LLM 推理规模 vs 速度 OK

复用:

bash 复制代码
cd D:\eda_llm
.venv\Scripts\python.exe <script>.py

11. 附录: 实验脚本源码

下面给出本次实验所有 8 个 Python 脚本的完整源码, 可直接复制运行。

11.1 decoder3to8.py --- 3-to-8 译码器

python 复制代码
"""
3-to-8 decoder 晶体管估算
- 用 sympy.logic.SOPform 从真值表生成最简 SOP
- 按静态 CMOS 门数估算晶体管数
  INV  = 2T
  NANDn = 2n T
  NORn  = 2n T
  ANDn  = NANDn + INV = 2n+2 T
  ORn   = NORn  + INV = 2n+2 T
  XOR2  = ~12 T (6T XNOR + 2T INV) 或 10T transmission-gate, 这里按 12T 算
"""
from sympy import symbols
from sympy.logic.boolalg import SOPform, simplify_logic

a, b, c = symbols('a b c')          # 3 个输入
inputs = [a, b, c]                   # 符号顺序: a 是 MSB, c 是 LSB

def minterm(i):
    """生成真值表第 i 行的 dict: {a:bit2, b:bit1, c:bit0}, 输出为 1 当且仅当输入 == i"""
    return {a: bool((i >> 2) & 1),
            b: bool((i >> 1) & 1),
            c: bool((i >> 0) & 1)}

print("=" * 70)
print("3-to-8 译码器真值表 (输入按 a b c 顺序, 1 = 高电平)")
print("=" * 70)
print(f"{'i':>3} {'a':>3} {'b':>3} {'c':>3}  " + " ".join(f"Y{j}" for j in range(8)))
for i in range(8):
    row = minterm(i)
    outs = [1 if j == i else 0 for j in range(8)]
    print(f"{i:>3} "
          f"{int(row[a]):>3} {int(row[b]):>3} {int(row[c]):>3}  "
          + " ".join(f" {y}" for y in outs))

print()
print("=" * 70)
print("每个输出 Yi 的最简 SOP (sympy.logic.SOPform)")
print("=" * 70)

expressions = []
total_lit = 0
for i in range(8):
    mint = [i]
    dontcares = []
    expr = SOPform(inputs, mint, dontcares)
    expr_s = simplify_logic(expr, form='dnf')
    n_lit = sum(1 for s in expr_s.atoms() if s in inputs)
    total_lit += n_lit
    expressions.append((i, expr_s, n_lit))
    print(f"Y{i} = {expr_s}    (字面量数 = {n_lit})")

print()
print("=" * 70)
print("晶体管估算 (静态 CMOS, 假设输入端 a, a', b, b', c, c' 全部已就绪)")
print("=" * 70)

T_PER_AND3 = 8
transistors_outputs = 8 * T_PER_AND3
T_INPUT_INV = 3 * 2
T_OUTPUT_INV = 8 * 2

print(f"方案 A · active-high 输出 + 输入端有反变量 : {transistors_outputs} T")
print(f"方案 B · active-high 输出 + 输入端无反变量 : {T_INPUT_INV + transistors_outputs} T")
print(f"方案 C · active-low  输出 + 输入端无反变量 : {T_INPUT_INV + transistors_outputs - T_OUTPUT_INV} T")
print(f"全部 8 个输出合计字面量数 = {total_lit}")

11.2 mult3x3.py --- 3×3 乘法器

python 复制代码
"""
3-bit × 3-bit 乘法器 (a * b = c)
- 输入: 6 bit (a0..a2, b0..b2),  a 是 MSB
- 输出: 6 bit (c0..c5),  c 是 LSB
- 真值表 2^6 = 64 行
- 用 sympy.logic.SOPform 求每个输出位的最简 SOP
- 估算静态 CMOS 晶体管数
"""
from sympy import symbols
from sympy.logic.boolalg import SOPform, simplify_logic

a = symbols('a0:3')   # a[0]=LSB, a[2]=MSB
b = symbols('b0:3')   # b0 LSB, b2 MSB
inputs = list(a) + list(b)

# ---------- 构造每个输出位的 minterm ----------
MINT = {k: [] for k in range(6)}
DC   = {k: [] for k in range(6)}
for av in range(8):
    for bv in range(8):
        c = av * bv
        # sympy 的位序: inputs 列表中最后一个变量对应 bit 0 (LSB)
        a0, a1, a2 = av & 1, (av >> 1) & 1, (av >> 2) & 1
        b0, b1, b2 = bv & 1, (bv >> 1) & 1, (bv >> 2) & 1
        code = (a0 << 5) | (a1 << 4) | (a2 << 3) | (b0 << 2) | (b1 << 1) | b2
        for k in range(6):
            if (c >> k) & 1:
                MINT[k].append(code)

def expr_stats(expr):
    """统计 SOP 中: 乘积项数 k, 字面量总数 L, 最大项宽 Wmax"""
    expr = simplify_logic(expr, form='dnf', force=True)
    from sympy import Or, And
    if expr.func is Or:
        terms = list(expr.args)
    else:
        terms = [expr]
    k = len(terms)
    L = 0
    Wmax = 0
    for t in terms:
        if t.func is And:
            lits = list(t.args)
        else:
            lits = [t]
        L += len(lits)
        if len(lits) > Wmax:
            Wmax = len(lits)
    return k, L, Wmax

def cmos_transistors(k, L):
    return (2 * L) + (2 * k) + (2 * k) + 2

results = []
for k in range(6):
    expr = SOPform(inputs, MINT[k], DC[k])
    kk, LL, Wmax = expr_stats(expr)
    T = cmos_transistors(kk, LL)
    results.append((k, kk, LL, Wmax, T, expr))
    print(f"c{k}: K={kk}, L={LL}, Wmax={Wmax}, T={T}")
    print(f"  SOP = {expr}")

total_T = sum(r[4] for r in results)
T_in_inv = 6 * 2
print(f"两级 SOP 总晶体管 (active-low)  : {total_T} T")
print(f"两级 SOP 总晶体管 (active-high) : {total_T + T_in_inv} T")

11.3 mult4x4.py --- 4×4 乘法器

python 复制代码
"""
4×4 乘法器 (a × b = c)
- 8 输入 (a0..a3, b0..b3), 8 输出 (c0..c7)
- 2^8 = 256 行
- 两级 SOP + 晶体管估算
"""
from sympy import symbols
from sympy.logic.boolalg import SOPform, simplify_logic
import time

def sop_cmos_t(k, L):
    """两级 SOP:  k 个 AND 项 (总字面量 L) + 1 个 k-OR"""
    return 2*L + 2*k + (2*k + 2)

N = 4
a = symbols(f'a0:{N}')
b = symbols(f'b0:{N}')
inputs = list(a) + list(b)
MINT = {k: [] for k in range(2*N)}

def bit_reverse(n, w):
    """N 位 n, 把 bit i 放到 bit (w-1-i)"""
    r = 0
    for i in range(w):
        if (n >> i) & 1:
            r |= 1 << (w - 1 - i)
    return r

# 构建 minterm 集 (sympy 位序: inputs 列表最后变量对应 LSB)
for av in range(2**N):
    for bv in range(2**N):
        c = av * bv
        # a0 放 bit (2N-1), a1 放 bit (2N-2), ..., bN-1 放 bit 0
        code = (bit_reverse(av, N) << N) | bit_reverse(bv, N)
        for k in range(2*N):
            if (c >> k) & 1:
                MINT[k].append(code)

# 最小化 + 统计
per_out = []
total_T = 0
t0 = time.time()
for k in range(2*N):
    expr = SOPform(inputs, MINT[k], [])
    expr = simplify_logic(expr, form='dnf', force=True)
    from sympy import Or, And
    if expr.func is Or:
        terms = list(expr.args)
    else:
        terms = [expr]
    L = sum(len(t.args) if t.func is And else 1 for t in terms)
    K = len(terms)
    Wmax = max((len(t.args) if t.func is And else 1) for t in terms)
    T = sop_cmos_t(K, L)
    per_out.append((K, L, Wmax, T))
    total_T += T
    print(f"c{k}: K={K}, L={L}, Wmax={Wmax}, T={T}")
print(f"\n最小化总耗时: {time.time()-t0:.1f}s")

T_inv_in = 2 * 2 * N
print(f"两级 SOP 晶体管合计:    {total_T} T")
print(f"+ {2*N} 个输入反相器:   {T_inv_in} T")
print(f"两级 CMOS 总 T (active-high): {total_T + T_inv_in} T")
print(f"两级 CMOS 总 T (active-low):  {total_T} T")

# 对照: 阵列乘法器
N_full = N
T_AND_pp = N_full * N_full * 6
T_FAs    = N_full * (N_full-1) * 28
T_HAs    = 2 * N_full * 8
T_inv_str = 2 * 2 * N_full
T_array  = T_AND_pp + T_FAs + T_HAs + T_inv_str
print(f"\n{N_full}x{N_full} 阵列乘法器 (结构化): {T_array} T")

11.4 mult8x8.py --- 8×8 乘法器 (超时失败版)

python 复制代码
"""
8×8 乘法器, 两级 SOP  - 逐位限时
- 16 输入 / 16 输出, 2^16=65536 行
- 每个输出位单独跑, 超时跳过
"""
from sympy import symbols
from sympy.logic.boolalg import SOPform, simplify_logic
import time, threading

T_INV = 2
def sop_cmos_t(k, L):
    return 2*L + 2*k + (2*k + 2)

# 跨平台超时机制
def run_with_timeout(fn, args=(), kwargs=None, timeout=60):
    result = [None]
    exc    = [None]
    def target():
        try:
            result[0] = fn(*args, **(kwargs or {}))
        except Exception as e:
            exc[0] = e
    th = threading.Thread(target=target, daemon=True)
    th.start()
    th.join(timeout)
    if th.is_alive():
        return None
    if exc[0]:
        raise exc[0]
    return result[0]

N = 8
a = symbols(f'a0:{N}')
b = symbols(f'b0:{N}')
inputs = list(a) + list(b)
MINT = {k: [] for k in range(2*N)}

print(f"构建 16 输入 / 16 输出的真值表 ({2**(2*N)} 行) ...")
t0 = time.time()
for av in range(2**N):
    for bv in range(2**N):
        c = av * bv
        bits = [(av>>i)&1 for i in range(N)] + [(bv>>i)&1 for i in range(N)]
        code = 0
        for j, bit in enumerate(bits):
            code |= (bit << j)
        for k in range(2*N):
            if (c >> k) & 1:
                MINT[k].append(code)
print(f"  构建耗时 {time.time()-t0:.1f}s\n")

TIMEOUT_PER = 90
print(f"最小化 --- 每个输出位最多 {TIMEOUT_PER}s\n")

per_out = [None]*16
for k in range(2*N):
    n_mint = len(MINT[k])
    def solve(kk=k):
        expr = SOPform(inputs, MINT[kk], [])
        expr = simplify_logic(expr, form='dnf', force=True)
        from sympy import Or, And
        if expr.func is Or:
            terms = list(expr.args)
        else:
            terms = [expr]
        L = sum(len(t.args) if t.func is And else 1 for t in terms)
        K = len(terms)
        Wmax = max((len(t.args) if t.func is And else 1) for t in terms)
        return K, L, Wmax
    t1 = time.time()
    res = run_with_timeout(solve, timeout=TIMEOUT_PER)
    dt = time.time() - t1
    if res is None:
        print(f"c{k}: minterms={n_mint}  TIMEOUT ({dt:.1f}s)")
        per_out[k] = None
    else:
        K, L, Wmax = res
        T = sop_cmos_t(K, L)
        per_out[k] = (n_mint, K, L, Wmax, T)
        print(f"c{k}: minterms={n_mint}  K={K}  L={L}  Wmax={Wmax}  T={T}  ({dt:.1f}s)")

done = [p for p in per_out if p is not None]
print(f"\n结果: {len(done)}/16 位在 {TIMEOUT_PER}s 内完成")

11.5 mult16x16.py --- 16×16 乘法器 (结构化估算)

python 复制代码
"""
16×16 乘法器 (a × b = c) 晶体管估算
- 两级 SOP 不可行:  2^32 = 4.3e9 行真值表
- 用 2×2 / 3×3 / 4×4 看增长趋势
- 16×16 只做结构化估算 (array / Wallace / Dadda)
"""
from sympy import symbols
from sympy.logic.boolalg import SOPform, simplify_logic
import time

T_INV  = 2
T_AND2 = 6    # NAND+INV
T_HA   = 8    # 1-bit 半加器
T_FA   = 28   # 1-bit 全加器 (经典 28T)

def sop_cmos_t(k, L):
    return 2*L + 2*k + (2*k + 2)

def run_sop_mult(N):
    a = symbols(f'a0:{N}')
    b = symbols(f'b0:{N}')
    inputs = list(a) + list(b)
    MINT = {k: [] for k in range(2*N)}
    for av in range(2**N):
        for bv in range(2**N):
            c = av * bv
            bits = [(av>>i)&1 for i in range(N)] + [(bv>>i)&1 for i in range(N)]
            code = 0
            for j, bit in enumerate(bits):
                code |= (bit << j)
            for k in range(2*N):
                if (c >> k) & 1:
                    MINT[k].append(code)
    total = 0
    per_out = []
    for k in range(2*N):
        expr = SOPform(inputs, MINT[k], [])
        expr = simplify_logic(expr, form='dnf', force=True)
        from sympy import Or, And
        if expr.func is Or:
            terms = list(expr.args)
        else:
            terms = [expr]
        L = sum(len(t.args) if t.func is And else 1 for t in terms)
        K = len(terms)
        T = sop_cmos_t(K, L)
        per_out.append((K, L, T))
        total += T
    return per_out, total

# 趋势: 2×2, 3×3, 4×4
print("两级 SOP 复杂度趋势 (2×2 / 3×3 / 4×4):")
for N in [2, 3, 4]:
    per_out, total = run_sop_mult(N)
    T_inv = 2 * N * 2
    print(f"  N={N}: 总 T={total}, +INV={T_inv}, 行数={2**(2*N)}")

# 16×16 结构化估算
N = 16
print(f"\n16×16 真值表: 2^{2*N} = {2**(2*N):.3e} 行")
print(f"存储需求: {2**(2*N)*32/8/1024/1024/1024:.1f} GB  (不可行)")

# A. 阵列
T_AND_pp = N*N * T_AND2
T_FAs    = N * (N-1) * T_FA
T_HAs    = 2 * N * T_HA
T_inv_in = 2 * N * T_INV
T_array  = T_AND_pp + T_FAs + T_HAs + T_inv_in
print(f"\n阵列 (ripple): {T_array} T  ({N*N} AND + {N*(N-1)} FA + {2*N} HA)")

# B. Wallace 树
def wallace_fa_count(N):
    pps = N * N
    total_fa = 0
    while pps > 2:
        csas = pps // 3
        rem  = pps - 3*csas
        total_fa += csas
        pps = csas * 2 + rem
    cpa_fa = 2*N - 1
    return total_fa, cpa_fa

wallace_csa_fa, wallace_cpa_fa = wallace_fa_count(N)
T_wallace = N*N * T_AND2 + wallace_csa_fa * T_FA + wallace_cpa_fa * T_FA + T_inv_in
print(f"Wallace 树:    {T_wallace} T  ({wallace_csa_fa} CSA FA + {wallace_cpa_fa} CPA FA)")

# C. Dadda 树
dadda_csa_fa = wallace_csa_fa - 2*N
T_dadda = N*N * T_AND2 + dadda_csa_fa * T_FA + wallace_cpa_fa * T_FA + T_inv_in
print(f"Dadda 树:      {T_dadda} T")

11.6 delay_compare.py --- 阵列 vs 树形延迟对比

python 复制代码
"""
阵列 vs 树形 乘法器  延迟对比
- 一位全加器 FA 的延迟为 1 个单位时间 (T_FA)
- 阵列: 2N-1 级 FA
- Wallace: log_{1.5}(N^2) 级 CSA + 1 个 CPA
"""
import math

def array_delay(N):
    return 1 + (2*N - 1)

def wallace_csa_levels(N):
    pps = N * N
    levels = 0
    while pps > 2:
        csas = pps // 3
        rem  = pps - 3*csas
        pps  = csas * 2 + rem
        levels += 1
    return levels

def cpa_delay(N_out, kind='ripple'):
    if kind == 'ripple':     return 2*N_out
    if kind == 'kogsstone':  return int(math.log2(N_out)) + 2
    if kind == 'clsa':       return int(math.sqrt(N_out))
    raise ValueError(kind)

print("延迟对比 (单位: FA 延迟)")
print(f"{'N':<4}{'PPs':<6}{'阵列':<8}{'Wallace CSA':<14}{'+ripple CPA':<14}{'+Kogge-Stone':<14}{'速度比'}")
for N in [2, 3, 4, 8, 16, 32, 64, 128]:
    pps = N*N
    arr = array_delay(N)
    wcsa = wallace_csa_levels(N)
    w_ripple = wcsa + cpa_delay(2*N, 'ripple')
    w_ks     = wcsa + cpa_delay(2*N, 'kogsstone')
    speedup = arr / w_ks
    print(f"{N:<4}{pps:<6}{arr:<8}{wcsa:<14}{w_ripple:<14}{w_ks:<14}{speedup:.2f}x")

print("\n延迟公式:")
print("  阵列乘法器   :  T = 2N * T_FA          (O(N))")
print("  Wallace 树   :  T = log_{1.5}(N^2) * T_FA + T_CPA   (O(log N))")
print("  Dadda 树     :  T = 同 Wallace (略少 FA)")
print("  Booth+Wallace:  T = (N/2) CSA 级 (再省一半)")

11.7 matrix4x4.py --- 4×4 矩阵乘法模块 5 方案

python 复制代码
"""
4×4 矩阵乘法模块 (4-bit 元素, 输出 8-bit 累加)
C[i][j] = Σk A[i][k] × B[k][j]
"""
T_4x4_ARR = 512      # 4×4 阵列乘法器
T_FA      = 28       # 1-bit 全加器
T_MUX2    = 6        # 2 选 1 mux
T_FF      = 8        # 1-bit 寄存器

def adder_tree_4_8bit():
    """4 个 8-bit 数相加 (Wallace-like 树)"""
    return 3 * 8 * T_FA

def pe_inner_product():
    """1 个内积单元: 4 个 4×4 mult + 1 个 4-输入 adder tree"""
    mult = 4 * T_4x4_ARR
    tree = adder_tree_4_8bit()
    return mult + tree, mult, tree

# 方案 A: 1 个乘法器 + 累加器, 时间复用
def plan_A():
    mult = T_4x4_ARR
    acc  = 8*T_FA + 8*T_FF + T_MUX2
    area = mult + acc
    cycles = 16
    return ("A: 1 乘法器 + 累加器", area, cycles, 1/cycles, area*cycles)

# 方案 B: 4 乘法器 + 4 累加器
def plan_B():
    mult = 4 * T_4x4_ARR
    acc  = 4 * (8*T_FA + 8*T_FF + T_MUX2)
    area = mult + acc
    return ("B: 4 乘法器 + 4 累加器", area, 4, 0.25, area*4)

# 方案 C: 16 乘法器 + 16 累加器
def plan_C():
    mult = 16 * T_4x4_ARR
    acc  = 16 * (8*T_FA + 8*T_FF)
    area = mult + acc
    return ("C: 16 乘法器 + 16 累加器", area, 1, 1.0, area*1)

# 方案 D: 4×4 systolic 阵列
def plan_D():
    pe = T_4x4_ARR + 8*T_FA + 8*T_FF + 2*T_MUX2
    area = 16 * pe
    return ("D: 4x4 systolic (16 PE)", area, 2*4-1, 1, area*(2*4-1))

# 方案 E: 16 乘法器 + 加法树
def plan_E():
    mult = 16 * T_4x4_ARR
    tree = 16 * adder_tree_4_8bit()
    area = mult + tree
    return ("E: 16 乘法器 + 加法树", area, 1, 1.0, area*1)

plans = [plan_A(), plan_B(), plan_C(), plan_D(), plan_E()]
print(f"{'方案':<35}{'面积 T':<10}{'延迟 cy':<10}{'吞吐':<12}{'AT':<12}")
print("-"*78)
for name, area, lat, thr, at in plans:
    print(f"{name:<35}{area:<10}{str(lat):<10}{f'{thr}/cy':<12}{at:<12}")

# 方案 D 拆解
print("\n方案 D 拆解 (systolic):")
pe_area = T_4x4_ARR + 8*T_FA + 8*T_FF + 2*T_MUX2
print(f"  1 个 PE: 1 个 4x4 mult ({T_4x4_ARR}) + 1 个 8-bit 加法器 ({8*T_FA})")
print(f"          + 8 个寄存器 ({8*T_FF}) + 2 个 mux ({2*T_MUX2}) = {pe_area} T")
print(f"  16 个 PE: {16*pe_area} T")

11.8 llm27b_inference.py --- 27B LLM 推理

python 复制代码
"""
27B LLM 推理: 芯片规模  vs  推理速度
"""
import math

N_PARAMS = 27e9                # 27 billion params
PE_FREQ_GHZ = 1.0
PE_AREA_MM2 = 0.01

# 内存带宽 vs Decode 速度
print("27B Decode 速度 (INT8, 1 batch):")
print(f"{'带宽 GB/s':<14}{'读 27GB (ms)':<14}{'tok/s':<10}{'体感'}")
for bw in [50, 100, 200, 400, 800, 1600, 3200, 5000]:
    model_bytes = N_PARAMS * 1.0
    load_time_ms = model_bytes / (bw * 1e9) * 1000
    tok_per_s = bw * 1e9 / model_bytes
    feel = "勉强" if tok_per_s < 5 else ("打字机" if tok_per_s < 15 else ("流畅" if tok_per_s < 50 else "很快"))
    print(f"{bw:<14}{load_time_ms:<14.1f}{tok_per_s:<10.1f}{feel}")

# 算力 vs Prefill 速度
print("\n27B Prefill 速度 (INT8, 1 batch):")
print(f"{'TOPS':<10}{'tok/s':<10}{'场景'}")
ops_per_token = N_PARAMS
for tops in [1, 10, 50, 100, 300, 600, 1000, 2000]:
    tok_per_s = tops * 1e12 / ops_per_token
    if tops < 50: scenario = "边缘"
    elif tops < 500: scenario = "中等"
    else: scenario = "数据中心"
    print(f"{tops:<10}{tok_per_s:<10.0f}{scenario}")

# 主流芯片对照
chips = [
    ("Apple M2 Max",   0, 38,   430, 60,   400),
    ("TPU v2",         128, 45,  300, 200,  700),
    ("TPU v4",         128, 275, 650, 170, 1200),
    ("A100 (INT8)",    0, 624,  826, 300, 2000),
    ("H100 (INT8)",    0, 1979, 814, 700, 3350),
    ("理论 64x64",     64, 4,    40, 20,   200),
    ("理论 128x128",   128, 16,  160, 80,   500),
    ("理论 256x256",   256, 66,  640, 320, 1000),
]
print("\n芯片对照 (27B INT8 Decode):")
print(f"{'芯片':<16}{'阵列':<12}{'TOPS':<8}{'面积':<10}{'功耗':<8}{'带宽':<10}{'tok/s'}")
for name, arr, tops, area, pwr, bw in chips:
    tok_per_s = bw * 1e9 / (N_PARAMS * 1)
    pe_str = f"{arr}x{arr}" if arr > 0 else "(非 systolic)"
    print(f"{name:<16}{pe_str:<12}{tops:<8}{area:<10}{pwr:<8}{bw:<10}{tok_per_s:.1f}")

# 想达到 X tok/s 需要什么芯片
print("\n想达到 X tok/s, 需要什么芯片?")
print(f"{'目标':<8}{'带宽':<10}{'算力':<10}{'阵列':<15}{'面积':<8}{'功耗':<8}{'对标'}")
for target_tps in [10, 30, 50, 100, 200]:
    need_bw = target_tps * N_PARAMS * 1 / 1e9
    need_tops = 2 * 27e9 * target_tps / 1e12
    need_pe = int(need_tops * 1000)
    side = math.sqrt(need_pe) if need_pe > 0 else 0
    side_int = int(side) + 1 if side > 0 else 0
    area_mm2 = need_pe * PE_AREA_MM2
    power_w = area_mm2 * 0.5
    if target_tps <= 10:   similar = "edge NPU"
    elif target_tps <= 30: similar = "midrange"
    elif target_tps <= 100: similar = "A100/Ascend"
    else: similar = "H100"
    arr_str = f"{side_int}x{side_int}"
    print(f"{target_tps:<8}{need_bw:<10.0f}{need_tops:<10.0f}{arr_str:<15}{area_mm2:<8.0f}{power_w:<8.0f}{similar}")

print("\n关键公式:")
print("  Decode 速度 (1 batch):  tok/s = BW(GB/s) / 27    (INT8, 内存瓶颈)")
print("  Prefill 速度 (大 batch): tok/s = min(TOPS/27G, BW/27G)")
print("  芯片规模:  TOPS ~ N^2 x 1 GHz x 1 op/cycle/PE")

报告完成日期 : 实验 session 内

核心工具 : sympy 1.14.0

关键发现: LLM 推理芯片的瓶颈是 DRAM 带宽, 不是算力; systolic 阵列是工业标准

相关推荐
书生的梦3 小时前
《神经网络与深度学习》学习笔记(三):Transformer 模型
深度学习·神经网络·学习
星辰AI4 小时前
拒绝重复造轮子:用 LLM 重构开源 Issue 摘要自动化流水线
人工智能·ai·语言模型
weixin_468466854 小时前
神经网络模型评价指标新手实战指南
人工智能·神经网络·机器学习·scikit-learn·sklearn·评价指标·网络模型
俊基科技5 小时前
破界而生,声入人心 ——A-59 工业级 AI 神经网络降噪消回音语音处理模组
神经网络·硬件开发·ai降噪·音频技术·回音消除·语音模组
海兰14 小时前
【文字三国志:第一篇】天命重构,大语言模型(LLM)动态生成文言风格的叙事文本的文字游戏
人工智能·游戏·语言模型
yuanyuan2o216 小时前
模型预训练:Hugging Face Transformers 基础
算法·ai·语言模型·自然语言处理·nlp·深度优先
星辰AI18 小时前
多模态记忆:让 AI Agent 记忆各种类型的信息
人工智能·ai·语言模型
瑶总迷弟18 小时前
使用 mis-tei 在昇腾310P上部署 bge-m3模型
pytorch·python·华为·语言模型·自然语言处理·cnn·unix
海兰19 小时前
【文字三国志:第六篇】天命重构,UI组件设计细节
人工智能·ui·语言模型·小程序