3. 逻辑设计基础(一):Verilog/VHDL基本语法回顾、模块化设计思想、组合逻辑与时序逻辑、同步复位与异步复位

各位同学,欢迎来到我们课程的第一节基础课。说实话,很多刚入行的工程师觉得语法这东西太简单,翻翻书就会了。但我在航天项目里见过太多因为语法细节没吃透导致的bug,一查就是几天。所以今天咱们把基础打扎实,后面才能飞得稳。

3.1 Verilog/VHDL基本语法回顾

先说说我的个人习惯。我平时用Verilog多一些,但VHDL在军工和航天领域也有很深的根基。你想想看,欧洲那边的宇航项目,VHDL的占比就很高。所以两种语言咱们都得会看,但写的时候选一种就行。

3.1.1 模块结构

不管是Verilog还是VHDL,核心都是模块化。一个模块就是一个黑盒子,有输入有输出。我见过新手把整个设计写在一个模块里,那调试起来真是噩梦。

Verilog模块示例:

module counter (
    input  wire       clk,
    input  wire       rst_n,
    input  wire       en,
    output reg  [7:0] count
);

always @(posedge clk or negedge rst_n) begin
    if (!rst_n)
        count <= 8'd0;
    else if (en)
        count <= count + 1'b1;
end

endmodule

VHDL模块示例:

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 entity;

architecture rtl of counter is
    signal count_reg : unsigned(7 downto 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 architecture;

小提示:我个人建议新手先从Verilog入手,它的语法更接近C语言,上手快。但如果你要跟欧洲的供应商合作,VHDL还是得会读。

3.1.2 数据类型与运算符

这里有个坑,我当年就踩过。Verilog里wire和reg的区别,很多人搞混。说白了:

  • wire:连线型,用于组合逻辑赋值
  • reg:寄存器型,用于时序逻辑赋值

但注意,在always块里赋值的变量必须声明为reg,哪怕它综合出来只是一根线。嗯,这里要记住。

类型 Verilog VHDL
逻辑值 0, 1, x, z '0', '1', 'X', 'Z'
向量 [7:0] data std_logic_vector(7 downto 0)
整数 integer integer

注意:在航天级设计中,尽量避免使用'x'和'z'状态。这些状态在仿真中可能掩盖真实问题。我曾经在一个项目中因为用了'z'状态,导致后仿真死活过不了,最后发现是仿真器对高阻态的处理跟实际芯片不一样。

3.2 模块化设计思想

模块化设计,说白了就是分而治之。你想想看,一个复杂的航天电子系统,有成百上千个模块。如果都写在一个文件里,别说调试了,光看代码就能看吐。

我建议遵循以下几个原则:

  • 单一职责:一个模块只做一件事,比如计数器就只计数,不要在里面加状态机
  • 接口清晰:模块的输入输出要明确,不要搞什么全局变量
  • 层次化:顶层模块只做例化,具体功能交给子模块

举个例子,一个简单的通信接口可以拆成这样:

top_module
├── clock_manager    // 时钟管理
├── data_encoder     // 数据编码
├── data_decoder     // 数据解码
└── interface_ctrl   // 接口控制

我在项目中遇到过最头疼的情况,就是有人把时钟管理和数据通路写在一起。结果时钟域切换出了问题,查了整整一周才发现是模块划分不合理导致的。

3.3 组合逻辑与时序逻辑

这两个概念是FPGA设计的基石。我经常问面试的人一个问题:组合逻辑和时序逻辑的区别是什么?很多人答不上来。

3.3.1 组合逻辑

组合逻辑的输出只取决于当前输入。没有记忆功能,也没有时钟。说白了就是一堆门电路连在一起。

// 组合逻辑示例:加法器
assign sum = a + b;

组合逻辑的缺点很明显——容易产生毛刺。因为信号传输有延迟,多个输入变化时,输出可能会短暂地出现错误值。

重点:在航天设计中,组合逻辑的输出一定要经过寄存器打一拍再使用。这是抗辐射设计的基本要求。

3.3.2 时序逻辑

时序逻辑就不一样了,它有记忆功能,靠时钟驱动。输出不仅取决于当前输入,还取决于之前的状态。

// 时序逻辑示例:D触发器
always @(posedge clk) begin
    q <= d;
end

为什么时序逻辑更可靠?因为它在时钟边沿采样,只要满足建立时间和保持时间,输出就是稳定的。我做过一个项目,在强辐射环境下,组合逻辑的输出被单粒子效应打翻了好几次,但时序逻辑因为有三模冗余,愣是扛住了。

3.4 同步复位与异步复位

这个知识点,说实话,争议很大。不同的公司、不同的项目,习惯都不一样。我个人的经验是:

3.4.1 同步复位

同步复位只在时钟边沿生效。优点是抗干扰能力强,不容易被毛刺误触发。

// 同步复位
always @(posedge clk) begin
    if (rst_n == 1'b0)
        q <= 1'b0;
    else
        q <= d;
end

3.4.2 异步复位

异步复位不受时钟控制,复位信号一来就生效。优点是响应快,但容易受毛刺影响。

// 异步复位
always @(posedge clk or negedge rst_n) begin
    if (rst_n == 1'b0)
        q <= 1'b0;
    else
        q <= d;
end
特性 同步复位 异步复位
响应速度 慢(等时钟) 快(立即)
抗毛刺
资源占用 多(需要专用复位引脚)
航天适用性 推荐 慎用

避坑指南:我曾经在一个项目中用了异步复位,结果复位信号上有个毛刺,导致整个系统在运行过程中莫名其妙复位了。从那以后,我在航天项目里一律用同步复位,除非有特殊要求。

好了,这一节的内容就到这里。下一节我们会深入讨论状态机的设计,那是逻辑设计的核心技能之一。记住,基础不牢,地动山摇。把这些概念吃透了,后面的内容学起来就轻松多了。

公众号:蓝海资料掘金营,微信deep3321