4. Verilog HDL基础与Nexus设计入门

Verilog HDL 是 Lattice Nexus 平台最常用的 RTL 描述语言。对初学者而言,真正需要掌握的不是语法大全,而是“哪些写法能被综合成硬件、哪些写法在 Nexus 上更省电、时钟和复位如何组织、如何把第一个 LED 闪烁工程跑通”。本节围绕这四件事展开,语法只讲与 Nexus 设计直接相关的部分。

一、Verilog 语法要点:从“描述电路”出发

Verilog 有两种建模风格:结构化描述(实例化门或模块)和行为级描述(用 always 块描述逻辑)。Nexus 工程中绝大多数代码是行为级描述,但必须牢记:always 块描述的是硬件并发行为,不是软件顺序执行。

模块是基本单元,端口方向用 input/output/inout 声明。组合逻辑用 always @(*),时序逻辑用 always @(posedge clk)。赋值语句分两类:assign 和 always 中的阻塞赋值 = 用于组合逻辑;非阻塞赋值 <= 用于时序逻辑。混用阻塞与非阻塞是新手最常见的错误来源,会导致仿真与综合结果不一致。

数据类型方面,Nexus 设计中常用 wire(连线,由 assign 或模块输出驱动)和 reg(在 always 块中被赋值的变量,综合后可能是触发器或组合逻辑)。位宽必须显式声明,例如 reg [7:0] cnt;。位宽不匹配会隐式截断或补零,容易埋下隐患。

module blink (
    input  wire clk,      // 系统时钟
    input  wire rst_n,    // 低电平复位
    output reg  led       // LED 输出
);
    reg [23:0] cnt;

    always @(posedge clk or negedge rst_n) begin
        if (!rst_n)
            cnt <= 24'd0;
        else if (cnt == 24'd9_999_999)
            cnt <= 24'd0;
        else
            cnt <= cnt + 1'b1;
    end

    always @(posedge clk or negedge rst_n) begin
        if (!rst_n)
            led <= 1'b0;
        else if (cnt == 24'd9_999_999)
            led <= ~led;
        else
            led <= led;
    end
endmodule

这段代码描述了一个分频计数器:每计满 10,000,000 个时钟周期翻转一次 LED。若时钟为 50 MHz,则翻转周期为 0.2 s,LED 闪烁周期约 0.4 s,肉眼可见。

二、面向 Nexus 的编码风格

Nexus 属于低功耗 FPGA 系列,其架构对时钟资源和功耗敏感。编码时应遵循几条原则:

  • 时钟统一:尽量使用全局时钟网络,避免用逻辑信号作为时钟(即“门控时钟”),否则会引入毛刺并增加功耗。
  • 复位同步化:异步复位、同步释放是推荐做法,可避免复位撤除时的亚稳态。
  • 避免不必要的翻转:计数器位宽够用即可,多余位会持续翻转增加动态功耗。
  • 组合逻辑保持简洁:Nexus 的 LUT 资源有限,复杂组合逻辑应拆分或流水化。
  • 显式声明位宽和常量:如 24'd9_999_999,避免综合器推断出意外位宽。
Nexus 器件支持多电压域和时钟门控单元,但在初学阶段,先把“单时钟、单复位、纯同步设计”写规范,比过早使用低功耗特性更重要。

三、时钟与复位设计

时钟和复位是数字设计的骨架。Nexus 开发板通常提供 50 MHz 或 12 MHz 晶振,经 PLL 倍频后作为系统时钟。在 Verilog 中,时钟信号只应出现在 posedge/negedge 事件中,不应参与组合逻辑运算。

复位分为异步复位和同步复位。异步复位响应快,但撤除时若靠近时钟边沿可能产生亚稳态;同步复位无此问题,但需要时钟存在才能生效。工程上常用“异步复位、同步释放”结构:

reg rst_sync1, rst_sync2;
always @(posedge clk or negedge rst_n) begin
    if (!rst_n) begin
        rst_sync1 <= 1'b0;
        rst_sync2 <= 1'b0;
    end else begin
        rst_sync1 <= 1'b1;
        rst_sync2 <= rst_sync1;
    end
end
// rst_sync2 作为内部同步复位使用

这样既保留了异步复位的即时性,又让复位撤除与时钟同步,降低亚稳态风险。

四、第一个 LED 闪烁工程

在 Lattice Diamond 或 Radiant 中新建工程,选择目标 Nexus 器件(如 LIFCL-40),添加上述 blink.v,再编写约束文件 blink.lpf,把 clk、rst_n、led 映射到实际引脚。约束示例:

LOCATE COMP "clk" SITE "P3" ;
IOBUF PORT "clk" IO_TYPE=LVCMOS33 ;
LOCATE COMP "rst_n" SITE "P4" ;
IOBUF PORT "rst_n" IO_TYPE=LVCMOS33 ;
LOCATE COMP "led" SITE "P5" ;
IOBUF PORT "led" IO_TYPE=LVCMOS33 ;

综合、布局布线后生成比特流,下载到开发板。若 LED 以约 0.4 s 周期闪烁,说明时钟、复位、计数器和引脚约束均正确。若 LED 常亮或常灭,优先检查复位极性、计数器比较值和引脚分配。

Nexus LED 闪烁工程:时钟—复位—计数—输出信号链 50 MHz 晶振 板载时钟源 PLL / 时钟网络 全局时钟 clk 复位同步器 异步复位 / 同步释放 24 位计数器 cnt 0 → 9,999,999 循环 比较器 cnt == 9,999,999 ? LED 触发器 led <= ~led LED 引脚输出 clk 同步 clk 同步 rst_n 复位 时序关系(示例) 时钟周期 T = 20 ns(50 MHz) 计数满值 = 10,000,000 个周期 翻转间隔 ≈ 0.2 s,闪烁周期 ≈ 0.4 s 关键检查点 ① 复位极性:rst_n 低有效 ② 计数器位宽 ≥ 24 位 ③ 比较值常量位宽匹配 ④ 引脚约束与 IO 电平正确

上图把 LED 闪烁工程拆成“时钟源—PLL—复位同步—计数器—比较器—LED 触发器—引脚输出”的信号链,并标出时钟与复位如何同步作用于各时序单元。理解这条链,就理解了 Nexus 上最基础的数字设计骨架。

下载后若 LED 不闪,先确认开发板供电和下载线连接正常,再用示波器或逻辑分析仪测量 LED 引脚。若引脚始终为高或低,检查 cnt 是否被优化掉、比较值是否写错、复位是否一直有效。

掌握以上内容后,你已经具备在 Nexus 上编写、约束、综合并下载一个基本时序逻辑工程的能力。后续更复杂的设计,都是在这条“时钟—复位—逻辑—输出”链路上扩展。