阶段一:计算基础

2课:定点数运算

📖 课程概述

定点数运算是AI加速器的基石。大多数推理使用INT8/INT16而非浮点,因为定点面积更小、功耗更低。本课深入Q格式、饱和算术和硬件实现。

🔍 Q格式详解

Q值 = 整数值 / 2^n
格式范围精度用途
Q0.15[-1, 1-2⁻¹⁵]3.05e-5高精度分类
Q7.8[-128, 128-2⁻⁸]3.9e-3通用CNN
Q0.7(INT8)[-1, 1-2⁻⁷]7.8e-3量化推理
面积对比(16位):定点加法器~200门,浮点加法器~4000门(20倍)。相同面积下定点PE数量是浮点的4-8倍!

🔧 运算规则

加减法

同Q格式直接加减,结果Q不变,注意溢出保护。

乘法

Qm1.n1 × Qm2.n2 = Q(m1+m2).(n1+n2)

Q7.8 × Q7.8 = Q15.16,需截断回Q7.8。

舍入策略

💻 Verilog实现

实现了5种运算(ADD/SUB/MUL/MAC/SHIFT)的定点单元和浮点-定点转换器。乘法后自动截断高位和小数位,MAC支持三操作数累加。

📈 性能分析

Q7.8乘法器~2500门,1周期延迟;Q0.15精度SNR≈90dB,Q0.7≈42dB。INT8面积仅为FP16的1/4。

🧪 练习

1) 用Q7.8计算3.14×2.72并对比误差 2) 实现stochastic rounding 3) 设计INT8矩阵乘的溢出保护

🔬 定点数精度深入分析

理解定点数的精度限制对设计高质量AI加速器至关重要。我们来量化分析不同Q格式的误差特性:

量化噪声模型

量化噪声功率 σ² = (2^(-2n)) / 12,其中n为小数位宽
信噪比 SNR(dB) ≈ 6.02n + 1.76
格式n(小数位)SNR(dB)均方误差最大误差
Q0.151591.067.6e-103.05e-5
Q7.8849.921.3e-53.9e-3
Q0.7(INT8)743.905.1e-57.8e-3
Q0.3(INT4)319.823.3e-31.25e-1

误差传播与累积

在矩阵乘法中,量化误差会随着累加而增大:

累积误差 ≈ √K × 单次误差(K为累加长度)

对于K=1024的累加,误差放大约32倍。这就是为什么累加器位宽通常比数据位宽大——INT8乘法需要INT32累加器。

🔧 饱和算术实现详解

饱和算术是AI加速器中防止溢出导致精度崩溃的关键机制:

饱和加法算法

// 饱和加法伪代码
if (a > 0 && b > 0 && result < 0) → 正溢出,结果 = MAX
if (a < 0 && b < 0 && result > 0) → 负溢出,结果 = MIN
否则 → 结果 = a + b

不同舍入策略的硬件实现

舍入方式硬件开销误差偏向应用
截断(Truncate)0(直接丢弃)偏向负无穷面积敏感场景
四舍五入(Round)1个加法器无偏通用推荐
收敛舍入(Conv.)LFSR+1个加法器无偏+等概率高精度训练
随机舍入(Stoch.)LFSR+比较器期望无偏理论研究

📐 累加器位宽设计

累加器位宽的选择是精度和面积的权衡:

累加器位宽 = 数据位宽 × 2 + ⌈log₂(K)⌉,K为累加次数
数据格式乘法结果位宽累加K=256累加K=1024典型选择
INT8 × INT816242632(留余量)
INT16 × INT1632404240
FP16 × FP16FP16FP32FP32FP32
实践建议:INT8推理使用32位累加器,INT16使用40位累加器。多余的位宽用于防止溢出,计算完成后截断到目标精度。

🔬 浮点与定点的精度对比实验

以ResNet-18在ImageNet上的精度为例,不同数值表示的效果:

数值格式Top-1精度精度损失面积效率
FP3269.76%基线
FP1669.74%0.02%1.8×
INT16(Q7.8)69.72%0.04%2.5×
INT8(per-ch)69.50%0.26%4.0×
INT8(per-tensor)68.90%0.86%4.0×
INT4(per-ch)65.20%4.56%8.0×
🏆

成就解锁:定点数运算

完成本课后,你已掌握 定点数运算 的核心概念与硬件实现方法。

🔬 定点数硬件实现细节

本节深入探讨定点数运算的Verilog实现细节,包括边界条件和时序优化:

饱和加法器的完整实现

// 饱和加法器:检测溢出并钳位
// 关键信号:
//   sign_a, sign_b: 操作数符号位
//   sign_sum: 和的符号位
//   overflow_pos: 两个正数相加得到负数
//   overflow_neg: 两个负数相加得到正数
//
// always_ff @(posedge clk)
//   {carry, sum} <= a + b;  // 宽一位的加法
//   if (sign_a == sign_b && sign_sum != sign_a)
//     result <= sign_a ? MIN_VAL : MAX_VAL;  // 饱和
//   else
//     result <= sum;
//
// 延迟:1 cycle(流水线)
// 面积:比普通加法器多约10%(溢出检测逻辑)

乘法器的截断与舍入

16×16→32位乘法后需要截断到16位。截断方式对精度影响显著:

// 乘法结果截断策略:
// 假设 Q7.8 × Q7.8 = Q15.16 (32位)
// 需要截断回 Q7.8 (16位)
//
// 策略1: 直接截断 (Truncate)
//   result = product[23:8];  // 取中间16位
//   误差范围: [0, -1 LSB]  偏向性误差
//
// 策略2: 四舍五入 (Round-Half-Up)
//   result = (product + (1 << 7))[23:8];  // 加0.5后截断
//   误差范围: [-0.5, +0.5 LSB]  无偏
//
// 策略3: 收敛舍入 (Convergent/Round-To-Even)
//   当低位恰好=0.5时,舍入到偶数
//   需要: if(低8位==128) result[0]==0 ? 截断 : 加1
//   误差范围: [-0.5, +0.5 LSB]  无偏且等概率

MAC单元的累加器设计

MAC(乘累加)是神经网络的核心操作,累加器位宽选择至关重要:

// MAC: result <= result + a * b
// 位宽分析:
//   a: Q7.8 (16位有符号)
//   b: Q7.8 (16位有符号)
//   a*b: Q15.16 (32位有符号)
//   累加K次需要额外 ⌈log2(K)⌉ 位
//
//   K=256: 需要 32+8 = 40位累加器
//   K=1024: 需要 32+10 = 42位累加器
//   K=4096: 需要 32+12 = 44位累加器
//
// 实际设计通常选32位(8×8乘法)或40位(16×16乘法)
// 溢出时通过饱和或右移处理

📐 浮点-定点转换电路

训练用FP32,推理用INT8/INT16,需要高效的转换电路:

FP32→INT8转换流程

  1. 提取FP32的指数和小数部分
  2. 根据量化参数(scale, zero_point)调整
  3. 四舍五入到整数
  4. 饱和到[0, 255]或[-128, 127]
步骤操作延迟面积
提取字段位选择0 cycle~0
缩放乘法(24×16)1-2 cycle~2000门
舍入加法+截断1 cycle~200门
饱和比较+MUX1 cycle~300门
总计-3-4 cycle~2500门

💡 不同精度格式的面积对比实验

以4×4矩阵乘法阵列为例,对比不同精度格式的面积和功耗:

格式PE面积(gate)16个PE面积功耗(mW@200MHz)精度损失
INT4×INT4~1502,4000.55-10%
INT8×INT8~6009,6001.6<1%
INT16×INT16~2,50040,0006.4<0.1%
FP16×FP16~8,000128,00020.5基线

INT8相比FP16面积减少12.5倍,功耗减少12.8倍,精度损失仅1%!这是INT8成为推理标准的根本原因。

🔧 定点数运算的Verilog实现细节

本节展示关键运算的完整Verilog代码片段和设计考量:

饱和乘法器实现

// 饱和乘法:Q7.8 × Q7.8 → Q7.8
// 步骤1: 16×16→32位乘法
// 步骤2: 取中间16位[23:8]作为Q7.8结果
// 步骤3: 检查高8位是否全部为符号位扩展
//   如果高位不一致 → 溢出 → 饱和
//
// wire signed [31:0] product = a * b;  // 32位乘法结果
// wire signed [15:0] truncated = product[23:8]; // 截断到Q7.8
// wire overflow_pos = product[31:24] != 8'h00 && !(product[23] && product[31:24] == 8'hFF);
// wire overflow_neg = product[31:24] != 8'hFF && product[23];
// assign result = overflow_pos ? 16'sd32767 :    // 正饱和
//                 overflow_neg ? 16'sd32768 :    // 负饱和
//                 truncated;

MAC流水线设计

// 3级流水MAC单元:
// Stage 1: 乘法 (组合逻辑,需要关键路径优化)
// Stage 2: 累加 (寄存器,1 cycle延迟)
// Stage 3: 截断/饱和 (组合逻辑)
//
// 优点:时序好,可以跑更高频率
// 缺点:3 cycle延迟,但吞吐量1 MAC/cycle
//
// always_ff @(posedge clk)
//   stage1_mul <= a * b;
//   stage2_acc <= stage2_acc + stage1_mul;
//   stage3_result <= saturate(stage2_acc);
//
// 吞吐量 = f_max × 1 MAC/cycle
// 200MHz下 = 200 MAC/s/PE

不同位宽的面积对比(综合结果)

运算8-bit16-bit32-bit
加法器0.0001mm²0.0002mm²0.0005mm²
乘法器0.0005mm²0.002mm²0.01mm²
MAC单元0.0006mm²0.0025mm²0.012mm²
延迟(cycle)112
功耗(mW@200MHz)0.050.150.6

定点数运算的时序优化

📋 本课关键概念总结

概念定义硬件影响
计算密度FLOPs/访存字节数决定计算/带宽受限
数据复用同一数据被多个PE使用减少DRAM访问次数
流水线多级操作重叠执行提高吞吐量,增加延迟
双缓冲两组缓冲区交替使用隐藏数据加载延迟
分块计算大矩阵分成小块处理适配有限片上存储
饱和算术溢出时钳位到极值防止精度崩溃
时钟门控空闲模块关闭时钟降低动态功耗

🎯 学习目标检查

📚 拓展阅读

下一课预告:本课掌握的知识将在下一课中进一步深化。建议先完成练习,确保理解核心概念后再继续。