3. Verilog HDL工业控制编程基础
在工业控制 FPGA 设计中,Verilog 不只是“能仿真”的语言,更要求代码能够被综合工具映射为确定的硬件资源。工业现场对确定性、抗干扰和可复位性要求很高,因此从语法选择开始就要面向综合、面向时序收敛。本节围绕四个核心问题展开:哪些语法可以安全使用、组合逻辑与时序逻辑如何区分、状态机如何写成可综合且可维护的形式、复位策略如何与时钟域配合。
一、面向综合的语法子集
可综合代码的核心约束是:每一条语句都必须能映射为查找表、触发器、块 RAM、DSP 或布线资源。工业控制中建议优先使用以下子集:
- 连续赋值:
assign用于描述纯组合逻辑,如使能、比较、译码。 - always 块:组合逻辑用
always @(*),时序逻辑用always @(posedge clk)或带异步复位的边沿敏感列表。 - if-else 与 case:用于优先级判断和多路选择,但必须覆盖所有分支,避免锁存器。
- 参数与本地参数:
parameter、localparam用于状态编码、位宽和定时参数,便于跨模块复用。 - 位操作与拼接:
&、|、^、{}可直接映射为逻辑资源,适合工业信号打包与拆分。
应避免或谨慎使用的语法包括:initial 块(仅用于仿真初始化,不能作为 FPGA 上电复位手段)、#delay 延时、while 循环(除非循环次数固定且可展开)、实数运算、动态数组和文件 I/O。工业控制中常见的“上电初值”应通过复位逻辑或寄存器初始化属性实现,而不是依赖仿真延时。
logic 或 wire/reg 风格,但同一工程内保持一致;位宽必须显式声明,避免隐式扩展导致比较错误。二、组合逻辑与时序逻辑的描述边界
组合逻辑的输出只取决于当前输入,时序逻辑的输出取决于当前输入和寄存器状态。工业控制中,组合逻辑常用于急停译码、限位判断、PWM 比较;时序逻辑常用于计数器、状态寄存器、通信移位和去抖。
组合逻辑使用 always @(*) 时,敏感列表必须完整,且所有输出在所有分支中都要赋值。否则综合工具会推断出锁存器,在工业现场可能引入毛刺和不确定上电状态。例如一个三路选择器:
always @(*) begin
case (sel)
2'b00: y = a;
2'b01: y = b;
2'b10: y = c;
default: y = 1'b0;
endcase
end
时序逻辑则使用非阻塞赋值 <=,保证同一时钟沿内多个寄存器并行更新,避免仿真与综合不一致。组合逻辑使用阻塞赋值 =,时序逻辑使用非阻塞赋值,这是工业代码可维护性的基本纪律。
三、状态机设计:从状态图到可综合代码
工业控制中的状态机通常承担流程控制,如“待机—启动—运行—暂停—故障—复位”。推荐采用三段式写法:第一段用时序逻辑更新当前状态,第二段用组合逻辑计算次态,第三段用组合或时序逻辑产生输出。三段式的好处是状态跳转与输出分离,便于时序分析和故障注入测试。
状态编码建议使用 localparam 定义,例如:
localparam IDLE = 3'd0,
START = 3'd1,
RUN = 3'd2,
PAUSE = 3'd3,
FAULT = 3'd4,
RESET = 3'd5;
次态逻辑必须覆盖所有状态和所有输入组合,default 分支回到 IDLE 或 FAULT,防止非法状态锁死。输出逻辑若为组合输出,需注意毛刺;若输出直接驱动继电器或功率器件,建议打一拍寄存输出,牺牲一个时钟周期换取稳定。
四、同步与异步复位策略
工业控制中复位来源通常包括上电复位、看门狗复位、急停复位和通信复位。异步复位的好处是响应快,不依赖时钟;缺点是释放时刻若靠近时钟沿,可能引起亚稳态。因此推荐“异步复位、同步释放”:复位信号先进入两级触发器同步,再作为同步复位使用。
reg rst_meta, rst_sync;
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
rst_meta <= 1'b0;
rst_sync <= 1'b0;
end else begin
rst_meta <= 1'b1;
rst_sync <= rst_meta;
end
end
上述代码中,rst_n 为低有效异步复位,rst_sync 为同步后的高有效复位。状态机和输出寄存器使用 rst_sync 复位,可以避免释放时的亚稳态。若系统存在多个时钟域,每个时钟域都要独立做复位同步,不能跨时钟域直接使用同一复位信号。
五、工业控制中的编码与检查要点
面向综合的工业 Verilog 代码,建议在提交前做以下检查:所有 always 块是否明确组合或时序;组合逻辑是否所有分支都有赋值;状态机是否有 default 返回;复位是否同步释放;跨时钟域信号是否做了同步处理;输出是否根据负载特性决定是否寄存。对于安全相关信号,如急停、超限、互锁,建议采用“常闭逻辑”并在代码中显式注释有效电平,避免现场接线与逻辑极性不一致。
下面给出一个可直接参考的工业状态机输出寄存片段,用于将状态译码结果打一拍后驱动外部设备:
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
out_reg <= 4'b0000;
end else if (rst_sync) begin
out_reg <= 4'b0000;
end else begin
out_reg <= {run_en, pause_en, fault_en, ready_en};
end
end
该片段中,rst_sync 为同步复位,out_reg 每个时钟沿更新一次,外部继电器看到的是一拍延迟后的稳定电平。对于 50 MHz 时钟,这一拍为 20 ns,相对于机械继电器毫秒级动作时间可以忽略,但能显著降低毛刺风险。
综合来看,工业控制 Verilog 的编写重点不是语法花哨,而是可综合、可复位、可时序收敛、可现场维护。把组合逻辑和时序逻辑分开写,把状态机写成三段式,把复位做成异步复位同步释放,再配合输出寄存,就能覆盖大多数工业实时接口场景。