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