第三章 硬件设计基础回顾:数字电路基础与FPGA/ASIC设计流程

各位同学,欢迎来到第三章。在正式进入卷积神经网络的硬件加速之前,我觉得有必要先带大家回顾一下数字电路的基础知识。说白了,不管你是用FPGA还是做ASIC,底层跑的都是这些最基本的逻辑单元。我自己带项目这么多年,发现很多同学算法推得飞起,一到RTL编码就卡壳,问题往往就出在基础不牢。

3.1 组合逻辑:一切计算的起点

组合逻辑,就是输出只取决于当前输入的逻辑电路。没有记忆,没有状态。你想想看,一个加法器、一个多路选择器,都是典型的组合逻辑。

我在项目中遇到过最典型的组合逻辑问题,就是竞争冒险。比如下面这个简单的例子:

// 一个简单的组合逻辑:Y = A & B
assign Y = A & B;

看起来没问题对吧?但如果A和B来自不同的路径,延迟不一样,Y就可能出现毛刺。嗯,这里要注意,组合逻辑的输出不能直接作为时钟信号或者异步复位信号,否则你的芯片会变得非常“神经质”。

避坑指南: 我曾经在一个项目中,把组合逻辑的输出直接接到了触发器的时钟端。结果仿真没问题,上板子就随机出错。查了三天,最后发现是组合逻辑的毛刺导致了触发器误触发。从那以后,我所有跨时钟域的信号都老老实实加两级同步器。

3.2 时序逻辑:让电路有了“记忆”

时序逻辑就不一样了。它引入了时钟,输出不仅取决于当前输入,还取决于之前的状态。说白了,就是电路有了“记忆”。

最基本的时序单元是D触发器。我个人习惯用下面的模板来写:

always @(posedge clk or negedge rst_n) begin
    if (!rst_n)
        q <= 1'b0;
    else
        q <= d;
end

这里有个关键点:非阻塞赋值(<=)。很多新手容易写成阻塞赋值(=),结果仿真和综合出来的电路完全不一样。我建议你记住一句话:时序逻辑用非阻塞,组合逻辑用阻塞。这是铁律。

核心概念: 建立时间(Setup Time)和保持时间(Hold Time)。这是时序逻辑的命门。建立时间要求在时钟沿到来之前,数据必须稳定;保持时间要求在时钟沿之后,数据还要稳定一段时间。如果违反了,触发器就会进入亚稳态,输出既不是0也不是1。

3.3 流水线:用面积换速度的艺术

流水线是硬件加速的灵魂。你想想看,一个复杂的组合逻辑,如果路径太长,一个时钟周期内根本算不完。怎么办?把它切成几段,中间插上寄存器。

举个例子,一个32位加法器:

// 非流水线:一个周期算完
assign sum = a + b;

// 2级流水线:分成两个周期
reg [15:0] sum_low, sum_high;
always @(posedge clk) begin
    sum_low  <= a[15:0] + b[15:0];
    sum_high <= a[31:16] + b[31:16] + (a[15:0] + b[15:0] > 16'hFFFF);
end
assign sum = {sum_high, sum_low};

非流水线版本,时钟频率可能只能跑到100MHz。但加了流水线,每级逻辑变短了,频率可以跑到200MHz甚至更高。代价是什么?延迟增加了1个周期,但吞吐量翻倍了。在CNN加速中,我们经常用几十级甚至上百级的流水线,就是为了把乘法器和加法器跑得飞快。

个人经验: 流水线不是越多越好。我曾经在一个项目中,为了追求极致频率,把流水线切得太细,结果控制逻辑变得极其复杂,面积暴增,功耗也上去了。后来发现,适度的流水线(比如每级逻辑延迟控制在时钟周期的60%-70%)是最优的。

3.4 FPGA架构简介:LUT、DSP、BRAM

FPGA说白了就是一块“万能积木”。你通过编程,把里面的基本单元连接起来,实现任意数字电路。FPGA的核心资源有三种:LUT、DSP、BRAM。

资源类型 全称 功能 典型数量(以Xilinx XC7K325T为例)
LUT Look-Up Table 实现任意组合逻辑(本质是一个小RAM) 203,800
DSP Digital Signal Processing 实现乘法、乘加运算 840
BRAM Block RAM 实现数据存储(36Kb每块) 445

LUT:一个6输入LUT,可以看成是一个64x1的RAM。你输入6位地址,它输出1位数据。任何6输入的组合逻辑,都可以通过查表实现。我刚开始学FPGA时,觉得这玩意儿很神奇——原来逻辑门是“算”出来的,不是“搭”出来的。

DSP:这是CNN加速的核心。一个DSP48E2(Xilinx 7系列)可以做一个25x18的乘法,然后累加。在卷积运算中,我们大量使用DSP来做乘加运算。我个人习惯把DSP配置成流水线模式,这样每个时钟周期都能出一个结果。

BRAM:用来存权重和中间结果。一个BRAM是36Kb,可以配置成各种位宽和深度。比如配置成512x72(512个地址,每个地址72位),非常适合存卷积核的权重。

关键点: 在FPGA上做CNN加速,本质上就是合理分配这三种资源。LUT做控制逻辑和地址生成,DSP做乘加运算,BRAM做数据缓存。资源不够怎么办?要么换更大的芯片,要么优化算法。

3.5 ASIC设计流程简介

ASIC和FPGA不一样。FPGA是现成的芯片,你只需要编程。ASIC是从零开始设计一颗芯片。流程复杂得多,但性能、功耗、成本都有优势。

典型的ASIC设计流程分为以下几个阶段:

  1. 架构设计:确定芯片的功能、性能指标、接口协议。这个阶段我一般会用SystemC或者Python做快速原型验证。
  2. RTL编码:用Verilog/VHDL写出寄存器传输级描述。和FPGA开发一样,但要求更严格。
  3. 功能仿真:验证RTL逻辑是否正确。我建议用UVM验证方法学,虽然学习曲线陡,但覆盖率确实高。
  4. 逻辑综合:把RTL代码转换成门级网表。工具会告诉你面积、时序、功耗。这一步很关键,我曾经因为综合策略没选对,导致芯片面积超标20%。
  5. 布局布线:把门级网表放到芯片的物理位置上,并连好线。这一步决定了芯片能不能跑在目标频率上。
  6. 静态时序分析:检查所有路径是否满足建立时间和保持时间。嗯,这一步不能省。
  7. 流片:把设计交给晶圆厂生产。从提交到拿到芯片,一般要3-6个月。
避坑指南: 我曾经在流片前一周,发现一个跨时钟域的信号没有做同步处理。当时整个团队都慌了,最后不得不改RTL、重新综合、重新布局布线,差点没赶上流片截止日期。从那以后,我要求所有跨时钟域信号必须加同步器,并且在代码审查时重点检查。

3.6 本章知识体系

下面这张图,是我自己画的本章知识体系。你可以把它当作一个思维导图,快速回顾本章的核心内容。

硬件设计基础回顾 数字电路基础 组合逻辑:无记忆,输出仅由输入决定 时序逻辑:有记忆,引入时钟和状态 流水线:切分逻辑,提高吞吐量 FPGA架构 LUT:查找表,实现组合逻辑 DSP:数字信号处理,乘加运算 BRAM:块RAM,数据存储 ASIC设计流程 架构设计 → RTL编码 功能仿真 → 逻辑综合 布局布线 → STA → 流片 核心思想:数字电路是CNN加速的物理基础 FPGA灵活快速,ASIC高性能低功耗,各有所长

好了,这一章的内容就到这里。数字电路基础、FPGA架构、ASIC流程,这三块是后续所有硬件加速设计的基石。你如果觉得哪里不太清楚,建议回头再看看。下一章我们开始真正进入CNN硬件加速的核心——卷积运算的硬件实现。