3. 硬件设计基础回顾:数字电路基础、流水线设计、并行计算概念、片上存储层次
各位同学,咱们今天聊点实在的。做卷积神经网络硬件加速,说白了就是在数字电路上跳舞。你算法再漂亮,落不了地都是白搭。这一章,我带你回顾几个最核心的硬件设计概念。别嫌基础,我当年在这些坑里摔过不少跟头。
3.1 数字电路基础:从门电路到状态机
数字电路的世界里,只有0和1。但就是这两个小家伙,能变出无穷的花样。我个人习惯把数字电路分成两大类:组合逻辑和时序逻辑。
组合逻辑,就是输入一变,输出立马跟着变。比如一个与门,两个输入都是1,输出才是1。简单直接,没有记忆。
时序逻辑就不一样了。它有个时钟信号,输出不仅看当前输入,还得看之前的状态。触发器(Flip-Flop)就是最典型的代表。时钟上升沿一来,它把输入锁住,直到下一个时钟沿才更新。
核心要点:组合逻辑负责计算,时序逻辑负责存储和同步。两者配合,才能构成完整的数字系统。
我在项目中遇到过一个问题:一个简单的加法器,组合逻辑路径太长,导致时序不满足。后来拆成两级流水,问题就解决了。嗯,这里要注意,组合逻辑的延迟是硬件设计的头号敌人。
再聊聊状态机。状态机是控制逻辑的灵魂。一个卷积加速器,什么时候读数据,什么时候算乘加,什么时候写结果,全靠状态机调度。
// 一个简单的状态机示例(Verilog风格)
always @(posedge clk or negedge rst_n) begin
if (!rst_n)
state <= IDLE;
else
case (state)
IDLE: if (start) state <= READ;
READ: if (data_ready) state <= COMPUTE;
COMPUTE: if (done) state <= WRITE;
WRITE: state <= IDLE;
default: state <= IDLE;
endcase
end
你想想看,没有状态机,整个芯片就像没头苍蝇一样乱转。所以,设计状态机时一定要考虑所有可能的状态转移,尤其是异常情况。我曾经因为漏了一个状态,导致芯片在特定条件下死锁,查了整整三天。
3.2 流水线设计:用面积换速度
流水线,这是硬件加速的看家本领。说白了,就是把一个大的组合逻辑路径,切成若干小段,每段中间插一级寄存器。
为什么要这么做?因为时钟频率受限于最长的组合逻辑路径。路径越长,延迟越大,频率就越低。切成小段后,每段延迟都变小了,时钟频率就能提上去。
我的经验:流水线级数不是越多越好。每加一级流水,就会多一个时钟周期的延迟(Latency)。对于卷积加速器,我一般控制在3-5级流水,既能提升频率,又不会让控制逻辑太复杂。
举个例子,一个4级流水线的乘加单元:
// 流水线乘加单元
// 第1级:输入寄存
reg [7:0] a_reg, b_reg;
always @(posedge clk) begin
a_reg <= a;
b_reg <= b;
end
// 第2级:乘法
reg [15:0] mul_reg;
always @(posedge clk) begin
mul_reg <= a_reg * b_reg;
end
// 第3级:加法
reg [15:0] add_reg;
always @(posedge clk) begin
add_reg <= mul_reg + c;
end
// 第4级:输出寄存
reg [15:0] out_reg;
always @(posedge clk) begin
out_reg <= add_reg;
end
你看,每个时钟周期只做一件小事。虽然单个数据需要4个周期才能出结果,但每个周期都能进一个新数据,吞吐率(Throughput)提高了4倍。
避坑指南:流水线最怕的就是数据冒险(Data Hazard)。比如后一级要用前一级刚算出来的结果,但前一级还没算完。我曾经在设计中没处理好这个问题,结果仿真全对,上板子就乱套。解决方案是加旁路(Bypass)或者插入气泡(Bubble)。
3.3 并行计算概念:空间换时间
并行计算,这是硬件加速的另一个杀手锏。卷积神经网络里全是矩阵运算,天生适合并行。
并行计算分几种:
- 数据并行:多个处理单元同时处理不同的数据。比如16个乘法器同时算16个像素的卷积。
- 任务并行:不同的处理单元做不同的事。比如一个单元读数据,另一个单元算乘法,第三个单元写结果。
- 流水线并行:就是上面说的流水线,每个阶段同时处理不同的数据。
我建议在设计卷积加速器时,优先考虑数据并行。因为卷积运算的每个输出像素都是独立的,可以随便并行。但要注意,并行度不是越高越好。并行度越高,面积越大,功耗也越高。
举个例子,一个3x3的卷积核,如果用一个乘法器,需要9个周期才能算完一个窗口。如果用9个乘法器并行,一个周期就能算完。但代价是面积增加了9倍。
| 并行度 | 吞吐率 | 面积 | 适用场景 |
|---|---|---|---|
| 1(串行) | 低 | 小 | 低功耗、小面积 |
| 9(全并行) | 高 | 大 | 高性能、大芯片 |
| 3(部分并行) | 中 | 中 | 平衡方案 |
你想想看,设计就是在做权衡。没有最好的方案,只有最适合的方案。
3.4 片上存储层次:数据搬家的艺术
做硬件加速,最头疼的不是计算,而是数据搬运。计算只占一小部分时间,大部分时间都花在等数据上。
片上存储层次,说白了就是给数据分等级。离计算单元越近,速度越快,但容量越小。典型的层次是:
- 寄存器(Register):最快,但最贵。一个触发器大概几十个晶体管。一般用来存中间结果。
- SRAM(静态随机存取存储器):比寄存器慢一点,但密度高很多。一个SRAM单元只要6个晶体管。适合做缓存。
- DRAM(动态随机存取存储器):容量大,但速度慢。一般放在片外。
对于卷积加速器,我一般这样安排:
- 输入特征图和权重存在片上的SRAM里,叫Buffer。
- 计算时,从Buffer里读数据到寄存器。
- 算完的结果,先写回Buffer,再批量写回片外DRAM。
关键原则:尽量减少片外访问。片外访问一次DRAM的时间,够你在片上算几百次乘加了。所以,数据复用是硬件加速的核心。
我记得有一次做项目,卷积加速器的计算单元利用率只有30%。一分析,发现大部分时间都在等数据从DRAM搬过来。后来把输入特征图做了分块(Tiling),让数据在片上多复用几次,利用率直接提到了85%。
嗯,这里要注意,片上SRAM的面积很宝贵。你要在容量和性能之间找平衡。我个人的习惯是,先估算出最坏情况下的数据需求,然后留20%的余量。
好了,这一章的内容就这些。数字电路基础是地基,流水线和并行计算是工具,存储层次是策略。把这几个概念吃透了,后面设计卷积加速器的时候,你就能游刃有余了。