2. 数字逻辑设计基础回顾:组合逻辑与时序逻辑、D触发器与时钟域、同步与异步复位、Verilog/VHDL快速入门

做FPGA加速,说白了就是在跟数字逻辑打交道。你想想看,不管是做市信号的生成,还是订单的快速处理,底层都是0和1的舞蹈。这一章,咱们把基础打牢。我见过不少新手,上来就写代码,结果时序跑不通,找半天bug,最后发现是最基本的逻辑概念没搞清。

2.1 组合逻辑 vs 时序逻辑

这两个概念,是数字设计的基石。我习惯这么区分:组合逻辑的输出,只取决于当前输入;时序逻辑的输出,还跟之前的状态有关。

举个例子,一个加法器就是组合逻辑。你输入A和B,它立马算出A+B。没有记忆,没有延迟(理想情况下)。而一个计数器,就是时序逻辑。它得记住当前计数值,下一个时钟沿来了,才加1。

核心区别一句话:

  • 组合逻辑:无记忆,输出 = f(当前输入)
  • 时序逻辑:有记忆,输出 = f(当前输入 + 历史状态)

在FPGA里,组合逻辑用查找表(LUT)实现,时序逻辑用触发器(FF)实现。我刚开始做项目时,总想把所有逻辑都塞进一个always块里,结果综合出来一堆LUT和FF混在一起,时序一塌糊涂。后来才明白,写代码前,先想清楚哪些是组合,哪些是时序,这是基本功。

2.2 D触发器与时钟域

D触发器,是时序逻辑的核心元件。它长什么样?说白了,就是一个存储单元。时钟上升沿(或下降沿)到来时,它把输入D的值,锁存到输出Q上。

它的真值表很简单:

时钟CLK 输入D 输出Q (下一时刻)
上升沿 0 0
上升沿 1 1
非上升沿 X 保持不变

嗯,这里要注意:时钟域的概念。一个FPGA里可能有多个时钟,比如系统时钟200MHz,以太网时钟125MHz。不同时钟域的信号直接交互,就会出问题——这叫跨时钟域(CDC)问题。

避坑指南:我曾经在一个做市项目里,把行情时钟域的信号直接送到了订单处理时钟域。结果呢?偶尔出现亚稳态,导致订单价格错乱。那次回测亏了不少钱,才让我彻底重视CDC问题。跨时钟域一定要用同步器(两级或三级D触发器打拍),别偷懒。

2.3 同步复位 vs 异步复位

复位,就是把电路初始化到一个已知状态。FPGA里,复位有两种方式:

  • 同步复位:只在时钟有效沿才检查复位信号。复位信号必须满足建立/保持时间。
  • 异步复位:复位信号一有效,立刻复位,不受时钟控制。

我个人的习惯是:能用同步复位,就用同步复位。为什么?异步复位容易受毛刺干扰,而且复位释放时如果跟时钟沿太近,容易产生亚稳态。

但有些场景,异步复位确实方便。比如系统上电瞬间,时钟还没稳定,异步复位就能保证所有寄存器进入已知状态。

我的经验:如果你非要用异步复位,记得做异步复位、同步释放处理。也就是复位信号先经过两级D触发器同步,再送给各个模块。这样既保证了复位的即时性,又避免了亚稳态。

2.4 Verilog/VHDL快速入门

做FPGA,你得会一门硬件描述语言。我个人更偏爱Verilog,语法简洁,跟C语言有点像。VHDL更严谨,适合大型工程。这里我以Verilog为例,带你快速上手。

一个典型的Verilog模块长这样:

module counter (
    input  wire       clk,      // 时钟
    input  wire       rst_n,    // 异步复位,低有效
    input  wire       en,       // 使能
    output reg  [7:0] count     // 8位计数器输出
);

// 时序逻辑:计数器
always @(posedge clk or negedge rst_n) begin
    if (!rst_n)
        count <= 8'd0;          // 复位清零
    else if (en)
        count <= count + 1'b1;  // 使能时加1
    else
        count <= count;         // 保持
end

endmodule

你看,这个模块包含了:

  • 端口声明:input/output,wire/reg类型
  • always块:描述时序逻辑,敏感列表里有时钟和复位
  • 非阻塞赋值 <= :时序逻辑里一定要用这个,别用=

VHDL的写法类似,但语法更啰嗦:

library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.NUMERIC_STD.ALL;

entity counter is
    Port ( clk   : in  STD_LOGIC;
           rst_n : in  STD_LOGIC;
           en    : in  STD_LOGIC;
           count : out STD_LOGIC_VECTOR (7 downto 0));
end counter;

architecture Behavioral of counter is
    signal count_reg : unsigned(7 downto 0) := (others => '0');
begin
    process(clk, rst_n)
    begin
        if rst_n = '0' then
            count_reg <= (others => '0');
        elsif rising_edge(clk) then
            if en = '1' then
                count_reg <= count_reg + 1;
            end if;
        end if;
    end process;
    
    count <= std_logic_vector(count_reg);
end Behavioral;

快速入门要点:

  • Verilog用always @(posedge clk)描述时序,VHDL用process(clk)
  • 组合逻辑用阻塞赋值=,时序逻辑用非阻塞赋值<=
  • 千万别把wirereg搞混。简单记:wire是连线,reg是变量

好了,基础就这些。你想想看,这些概念是不是都很简单?但就是这些简单的东西,组合起来,就能做出复杂的做市系统。下一节,咱们开始真正进入FPGA加速的世界。

数字逻辑设计基础 - 知识体系 FPGA数字逻辑设计 组合逻辑 vs 时序逻辑 D触发器与时钟域 同步复位 vs 异步复位 Verilog/VHDL快速入门 无记忆,LUT实现 有记忆,FF实现 时钟沿采样,存储状态 跨时钟域需同步器 同步:时钟沿检查复位 异步:立即复位 核心:理解组合与时序的区别,掌握D触发器与时钟域处理,学会Verilog/VHDL基本语法

我的建议:学数字逻辑,别光看书。打开Vivado或Quartus,写几个小模块,看看RTL视图,跑跑仿真。实践出真知。我在带新人时,总让他们先写一个计数器,再写一个状态机,最后做一个简单的SPI接口。走完这三步,基础就扎实了。

好了,这一章就到这里。记住,基础不牢,地动山摇。做FPGA加速,这些概念会反复用到。下一章,咱们聊聊FPGA的硬件架构,看看里面到底有什么宝贝。

专注资料整理