阶段二:量化与压缩

11课:稀疏计算

📖 课程概述

剪枝后权重含大量零值,稀疏计算引擎跳过零值运算,实际计算量与稀疏度成正比。本课实现零值跳过和CSC稀疏格式解码。

🔍 稀疏数据格式

格式存储随机访问适合
COO(行,列,值)三元组转换中间格式
CSR行指针+列索引+值行优先好稀疏矩阵×向量
CSC列指针+行索引+值列优先好矩阵乘法

💻 Verilog实现

稀疏引擎:检测零值跳过MAC,统计活跃PE数。CSC解码器:从压缩格式恢复稀疏矩阵行索引和值。

📈 性能分析

90%稀疏度→仅10%MAC执行→10倍加速。CSC解码约2周期延迟。面积增加~15%用于零检测。

🧪 练习

1) 实现CSR格式解码器 2) 设计2:4结构化稀疏(NVIDIA方案) 3) 评估不同稀疏度的实际加速比

🔬 稀疏矩阵格式深入分析

选择合适的稀疏格式对硬件效率至关重要:

CSR格式详解

// CSR格式:3个数组
// val[]: 非零值
// col_idx[]: 列索引
// row_ptr[]: 行指针(row_ptr[i]到row_ptr[i+1]是该行的非零元素)

// 示例矩阵:
// [1 0 0 2]
// [0 3 4 0]
// [5 0 6 0]
// val = [1,2,3,4,5,6]
// col_idx = [0,3,1,2,0,2]
// row_ptr = [0,2,4,6]

稀疏矩阵向量乘(SpMV)

y[i] = Σ(A[i][j] × x[j]),仅遍历非零元素

SpMV是稀疏计算的核心操作,瓶颈在于不规则的内存访问模式。

📊 稀疏硬件的实际加速比

稀疏度理论加速实际加速(非结构化)实际加速(2:4结构化)
50%1.2-1.5×1.8-1.9×
75%1.5-2.0×N/A
90%10×2.0-3.0×N/A
95%20×3.0-5.0×N/A
关键洞察:非结构化稀疏的实际加速远低于理论值,因为不规则的内存访问和索引开销。2:4结构化稀疏虽然最大稀疏度只有50%,但实际加速比接近理论值。
🏆

成就解锁:稀疏计算

完成本课后,你已掌握 稀疏计算 的核心概念与硬件实现方法。

🔬 稀疏计算的硬件架构

不同稀疏度需要不同的硬件策略:

低稀疏度(<50%):门控MAC

// 门控MAC:检测零值后跳过
// always_ff @(posedge clk)
//   if (|operand_a && |operand_b)  // 非零检测
//     result <= result + operand_a * operand_b;
//   else
//     result <= result;  // 保持不变,省功耗
//
// 面积开销:<5%(加一个比较器)
// 功耗节省:与稀疏度成正比
// 性能提升:几乎为0(仍然占用周期)

中等稀疏度(50-80%):2:4结构化

// 2:4结构化稀疏引擎:
// 每4个权重2个非零,硬件直接处理
// 
// 硬件结构:
// [权重解码器] → [2个MAC] → [累加器]
//   2-bit索引 → 选择2个激活值
//   2个MAC同时执行非零乘法
//
// 带宽节省:50%(只传输非零权重+2-bit索引)
// 计算节省:50%(2个MAC代替4个)
// 面积开销:<10%(索引解码器)

高稀疏度(>90%):CSC/CSR解码器

// CSC稀疏矩阵解码器:
// 输入:col_ptr[], row_idx[], nz_vals[]
// 输出:按列输出的非零元素+行索引
//
// 处理流程:
// 1. 从col_ptr[c]到col_ptr[c+1]获取第c列的非零元素
// 2. row_idx[i]指示该非零值的行位置
// 3. 激活值按row_idx从输入向量中索引
// 4. 执行MAC: result[col] += nz_vals[i] * input[row_idx[i]]
//
// 关键挑战:不规则的内存访问模式
// 优化:预取+缓存行索引对应的激活值

📊 稀疏硬件的实际性能数据

方案稀疏度面积开销功耗节省延迟加速
门控MAC30%<5%20%~1×
2:4结构化50%10%45%~1.9×
CSC解码器90%20%80%~3×
专用稀疏引擎95%40%90%~5×

🔧 第11课:实现细节与调试指南

本节提供该模块的详细实现指南、常见bug和调试方法:

模块接口定义

每个模块应遵循统一的接口规范:

// 标准模块接口模板:
// module xxx #(
//   parameter DATA_W = 16,
//   parameter ...
// )(
//   input  wire                  clk,      // 时钟
//   input  wire                  rst_n,    // 异步复位,低有效
//   input  wire                  en,       // 模块使能
//   // 数据输入
//   input  wire [DATA_W-1:0]     data_in,
//   input  wire                  data_valid,
//   // 数据输出
//   output reg  [DATA_W-1:0]     data_out,
//   output reg                   data_out_valid,
//   // 状态
//   output wire                  busy,
//   output wire                  error
// );
//
// 设计规则:
// 1. 所有时序逻辑使用posedge clk
// 2. 复位使用异步复位(negedge rst_n)
// 3. 参数化设计,便于配置
// 4. valid-ready握手协议
// 5. 每个模块有独立的en信号

常见Bug与解决方案

症状可能原因调试方法解决方案
输出全零复位后未初始化检查复位逻辑确保复位释放后en有效
数据错位流水线级数不匹配波形对比输入输出对齐valid信号延迟
溢出累加器位宽不够监测累加器最高位增加位宽或加饱和逻辑
死锁valid-ready互等追踪握手信号确保下游始终能接收
时序违例组合逻辑路径太长查看综合报告插入流水线寄存器

Verilator验证步骤

// Step 1: 语法检查
// verilator --lint-only module.v
//
// Step 2: 编写Testbench
// module tb;
//   reg clk, rst_n;
//   // 实例化DUT
//   // 生成测试向量
//   // 自动比对结果
// endmodule
//
// Step 3: 编译仿真
// verilator --cc module.v tb.v
// make -C obj_dir -f Vmodule.mk
// ./obj_dir/Vmodule
//
// Step 4: 波形调试(如果需要)
// verilator --trace --cc module.v tb.v
// 在testbench中: $dumpfile("wave.vcd"); $dumpvars;

性能优化checklist

与其他模块的集成

// 模块集成清单:
// 1. 确认接口信号匹配(位宽、协议)
// 2. 时钟域是否一致(跨域需FIFO)
// 3. 复位策略是否统一
// 4. 信号命名是否规范
// 5. 是否有环路依赖(需打破)
// 6. 参数是否正确传递

🔧 稀疏引擎的完整架构

稀疏计算引擎的系统架构

完整的稀疏计算引擎包含多个子模块:

// 稀疏引擎架构:
// [CSC解码器] → [索引匹配] → [MAC阵列] → [结果收集]
//
// CSC解码器:
//   输入: col_ptr, row_idx, nz_vals
//   输出: 非零值 + 对应行索引
//   面积: ~1000门
//
// 索引匹配:
//   根据row_idx从激活向量中选取对应元素
//   使用SRAM + 地址生成器
//   面积: ~2000门(含小SRAM)
//
// MAC阵列:
//   与密集MAC相同,但输入已经筛选
//   面积: 同等规模密集引擎
//
// 结果收集:
//   将MAC输出写回对应输出位置
//   面积: ~500门

📋 本课关键概念总结

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

🎯 学习目标检查

📚 拓展阅读

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

🔬 深入探讨:从理论到实践的映射

将算法原理映射到硬件实现时,需要解决多个层次的抽象差距:

算法→微架构映射原则

  1. 计算并行化:识别算法中的数据并行性和流水线并行性,映射到PE阵列
  2. 存储层次化:根据数据生命周期和复用模式,分配到不同存储层次
  3. 通信局部化:减少全局通信,使用局部传递和广播
  4. 控制简化:将复杂控制流简化为有限状态机

硬件设计的Pareto最优

硬件设计没有唯一最优解,只有Pareto前沿上的多个平衡点:

设计点面积(mm²)功耗(mW)算力(GOPS)能效比
最小面积0.5103.2320
平衡设计2.05025.6512
最高性能5.0200102.4512
最低功耗1.056.41280

验证驱动开发(VDD)

先写testbench,再写RTL,确保每个功能都有对应的测试:

// 验证驱动开发流程:
// 1. 根据规格编写testbench框架
// 2. 定义输入输出接口和预期行为
// 3. 编写RTL代码使测试通过
// 4. 添加边界条件测试
// 5. 添加随机化测试(约束随机)
// 6. 覆盖率分析确保测试充分
//
// 优势:
// - 减少bug引入率
// - 测试即文档
// - 重构有安全网
// - 提高设计质量

功耗-性能-面积(PPA)优化循环