fpga系列 HDL:Verilog(以及 SystemVerilog)中的编译指令(Compiler Directives / Preprocessor Directives)

1.说明

  • 编译指令以反引号 ` 开头,在编译预处理阶段生效,类似于 C 语言中的 #define#include 等。

RTL开发真正需要熟练掌握的编译指令只有:
defineifdefifndefincludetimescaledefault_nettype
其余大多出现在 ASIC 标准单元库、门级仿真或 EDA 工具链中。

1,1 Verilog/SystemVerilog 编译指令总表

指令功能示例注释/使用频率
define定义宏`define WIDTH 32define 全局文本替换;parameter 是模块参数
undef取消宏定义`undef WIDTH⭐⭐⭐
ifdef宏已定义时编译`ifdef FPGAifdef 编译期决定;generate if 综合期决定
ifndef宏未定义时编译`ifndef WIDTH⭐⭐⭐⭐⭐
elsif条件编译分支`elsif ASIC⭐⭐⭐⭐
else条件编译否则分支`else⭐⭐⭐⭐⭐
endif结束条件编译`endif⭐⭐⭐⭐⭐
include包含文件`include "defs.v"⭐⭐⭐⭐⭐
timescale设置时间单位/精度`timescale 1ns/1ps⭐⭐⭐⭐⭐
default_nettype设置默认网络类型`default_nettype none⭐⭐⭐⭐
resetall恢复编译器默认设置`resetall⭐⭐
celldefine开始标准单元定义`celldefine
endcelldefine结束标准单元定义`endcelldefine
delay_mode_distributed分布式延时模式`delay_mode_distributed
delay_mode_path路径延时模式`delay_mode_path
delay_mode_unit单位延时模式`delay_mode_unit
delay_mode_zero零延时模式`delay_mode_zero
begin_keywords指定关键字版本`begin_keywords "1800-2017"
end_keywords结束关键字版本作用域`end_keywords
line指定源文件和行号`line 100 "gen.v" 0
__FILE__当前文件名(SV)`__FILE__调试定位错误来源
__LINE__当前行号(SV)`__LINE__调试定位错误来源

2.常用命令

2.1. 宏定义相关

`define

基础示例

`define DATA_WIDTH 32

reg [`DATA_WIDTH-1:0] data;
  • 展开后:
reg [31:0] data;

带参数宏

`define ADD(a,b) ((a)+(b))
  • 使用:
wire [7:0] c;

assign c = `ADD(x,y);
  • 展开:
assign c = ((x)+(y));

`undef

`define WIDTH 32

`undef WIDTH  // 之后再使用被undef的定义会报错

2.2. 条件编译

类似 C 的 #ifdef


`ifdef

  • 如果宏已定义则编译。
`define FPGA

`ifdef FPGA

    assign clk = fpga_clk;

`endif

`ifndef

  • 如果宏未定义则编译。
`ifndef WIDTH

    `define WIDTH 32

`endif
  • 常用于头文件保护。

`elsif

  • 多分支条件。
`ifdef FPGA

    assign clk = fpga_clk;

`elsif ASIC

    assign clk = asic_clk;

`else

    assign clk = sim_clk;

`endif

`else

  • 条件编译否则分支。
`ifdef FPGA

    ...

`else

    ...

`endif

`endif

  • 结束条件编译块。

2.3. 文件包含

`include

  • 将其它文件内容插入当前位置。
`include "defines.v"

module top;
...
endmodule
  • 常见目录结构:
project/
├── rtl/
│   └── top.v
├── include/
│   └── defines.v

2.4. 时间尺度定义

`timescale

定义:

  • 时间单位(time unit)
  • 时间精度(time precision)

语法

`timescale unit/precision

示例

`timescale 1ns/1ps

表示:

  • 时间单位 = 1ns
  • 时间精度 = 1ps

示例

#10

表示:

10ns

2.5. 默认网络类型

`default_nettype

  • 用于控制未显式声明类型的信号的默认网络类型。

示例

`default_nettype wire
  • 在此设置下,即使未显式声明网络类型,以下代码也能通过编译:
assign a = b;
  • 因为编译器会将 ab 默认为 wire 类型,相当于:
wire a;
wire b;

推荐用法(大型项目):

`default_nettype none
  • 例如:
`default_nettype none

assign aa = bb;
  • 此时,如果未显式声明网络类型:
wire aa;
wire bb;
  • 编译器会报错,强制要求显式声明所有网络类型,从而提高代码的严谨性和可维护性。

2.其他命令

2.1. 单元控制

`celldefine

`celldefine 是一个 Verilog/SystemVerilog 编译指令,用于告知 EDA 工具(如仿真器、综合器、时序分析工具)后续定义的模块(module)是标准单元(Standard Cell)物理单元(Physical Cell),而非普通的 RTL 模块。

语法与用法

`celldefine

module 单元模块名 (...);
    // 模块内部描述
endmodule

`endcelldefine
  • `celldefine:标记标准单元定义的开始。
  • `endcelldefine:标记标准单元定义的结束。两者必须成对使用。

主要作用

  1. 标识标准单元:在 ASIC 设计流程中,标准单元库(如与门、或门、触发器、缓冲器等)的 Verilog 模型通常会使用 celldefine/endcelldefine` 包裹。这有助于工具:

    • 在门级仿真(Gate-level Simulation)中区分标准单元和用户自定义模块。
    • 在时序分析(Timing Analysis)和功耗分析(Power Analysis)中应用针对标准单元的特定处理规则或模型。
    • 在综合(Synthesis)后网表中标识出由工艺库提供的标准单元实例。
  2. 影响仿真行为:某些仿真器在遇到被 celldefine 标记的模块时,可能会启用特殊的仿真优化或检查,例如:

    • 忽略或简化单元内部的详细时序(如果使用了零延时或单位延时模式)。
    • 应用特定的 SDF(Standard Delay Format)反标规则。

典型使用场景

  • ASIC 标准单元库的 Verilog 模型:工艺厂商提供的 .v 库文件中,每个标准单元(如 AND2X1, DFFRS)的定义通常都被 celldefine/endcelldefine` 包裹。
  • 门级网表(Gate-level Netlist):综合工具输出的门级网表中,实例化的标准单元模块可能仍保留此标记。
  • 单元建模与特征化:在创建自定义标准单元或宏单元(Macro Cell)的仿真模型时使用。

注意事项

  • RTL 设计通常不需要:在 RTL(寄存器传输级)设计阶段,开发者几乎不会主动使用 celldefine。它主要出现在由工具生成或由工艺厂商提供的库文件中。
  • default_nettype 等指令的交互resetall 指令会将 celldefine 的状态重置为默认(即未激活状态)。
  • 仿真性能:标记为标准单元可能使仿真器采用更高效的算法,从而提升门级仿真的速度。

简单示例

一个简单的 2 输入与门标准单元模型可能如下所示:

// 标准单元库文件 cell_lib.v
`celldefine

module AND2 (output Y, input A, B);
    // 此处可能是行为级描述或原语实例化
    assign Y = A & B;
    // 通常还会包含 specify 块用于时序建模
endmodule

`endcelldefine

当工具读取此文件时,会知道 AND2 是一个标准单元。

`endcelldefine

结束 Cell 定义。

`endcelldefine
  • endcelldefine 必须与 celldefine 成对出现,以界定标准单元定义的范围。一个文件中可以有多对 celldefine/endcelldefine`,也可以将多个单元定义包裹在一对指令中。

2.2. 延时(仿真)相关

  • delay_mode_distributed 的主要使用场景是门级网表(Gate-Level Netlist)仿真和 SDF 回标验证。在 ASIC 流程中,综合或布局布线后生成的网表可能同时包含门延时(如 and #(2))、连续赋值延时(如 assign #3)以及 specify 路径延时,此时需要告诉仿真器优先采用哪种延时模型。使用 delay_mode_distributed 时,仿真器采用分布在各个门、开关和连续赋值语句上的延时,因此适合验证标准单元库自带的门延时模型是否正确工作。相比之下,如果进行 SDF 回标后的精确时序验证,更常见的是使用 delay_mode_path 或由仿真器自动采用 SDF 中的路径延时数据。对于日常 RTL 功能仿真、FPGA 开发以及常规模块设计,几乎不会主动使用 delay_mode_distributed,因为 RTL 仿真通常关注逻辑功能而非门级延时,很多项目甚至直接采用零延时仿真。简而言之,delay_mode_distributed 是一个偏 ASIC 后端验证阶段的仿真控制选项,典型场景是综合后/布局布线后门级时序仿真,而不是 RTL 开发阶段
延时模式延时值仿真速度时序精度主要用途
delay_mode_zero0最快无时序纯功能验证
delay_mode_unit1 个时间单位快速功能验证
delay_mode_path路径延时值中等中等模块级时序验证
delay_mode_distributed分布式门延时较慢门级时序验证

`delay_mode_distributed 分布式延时模式

  • `delay_mode_distributed 是一个 Verilog/SystemVerilog 编译指令,用于设置仿真器的分布式延时(Distributed Delay)模式。在此模式下,延时值被分配到逻辑门或模块内部的各个路径上,而不是集中在输出端口。

语法

`delay_mode_distributed

作用与原理

  1. 延时分布:在分布式延时模式下,仿真器会将模块或单元的延时值分配到其内部各个信号路径上。例如,一个多输入的逻辑门,每个输入到输出的路径可能有不同的延时值。
  2. 建模精度:这种模式提供了更高的时序建模精度,能够更真实地反映实际电路中的时序行为,因为不同路径的传播延时可能因负载、布线等因素而不同。
  3. 与 specify 块配合:分布式延时通常与模块内的 specify 块(时序规范块)一起使用。specify 块中可以定义从每个输入到每个输出的路径延时(使用 specparam 声明延时参数,并通过 (A => Y) = delay_value; 这样的路径延时语句指定)。

典型使用场景

  • ASIC 标准单元库的时序建模:工艺厂商提供的 .v 库文件中,标准单元(如与门、或门、触发器)的仿真模型通常会使用分布式延时模式,并配合 specify 块定义精确的输入到输出路径延时。
  • 门级仿真(Gate-level Simulation):在综合后的门级网表仿真中,为了获得准确的时序信息,仿真器需要工作在分布式延时模式下,以应用 SDF(Standard Delay Format)文件中反标的具体路径延时值。
  • 自定义模块的精确时序仿真:当设计者需要为自己的模块(如模拟的存储器、定制逻辑块)建立精确的时序模型时,可能会使用此模式。

示例

一个使用分布式延时模式的简单与门模型:

`timescale 1ns/1ps

module AND2_distributed (output Y, input A, B);
    // 功能描述
    assign #1 Y = A & B; // 这里的功能延时仅为示意,实际分布式延时在 specify 块中定义

    // specify 块:定义路径延时
    specify
        // 定义路径延时参数
        specparam t_rise_AtoY = 1.2; // A 到 Y 的上升延时
        specparam t_fall_AtoY = 1.0; // A 到 Y 的下降延时
        specparam t_rise_BtoY = 1.3;
        specparam t_fall_BtoY = 1.1;

        // 指定路径延时
        (A => Y) = (t_rise_AtoY, t_fall_AtoY);
        (B => Y) = (t_rise_BtoY, t_fall_BtoY);
    endspecify
endmodule
  • 当在仿真顶层使用 `delay_mode_distributed 指令后,仿真器会按照 specify 块中定义的路径延时值进行仿真。

注意事项

  1. RTL 设计无需使用:在 RTL(寄存器传输级)设计阶段,代码是功能性的,不包含具体的时序信息,因此不需要也不应该使用 `delay_mode_distributed。它主要用于门级仿真和单元库建模。
  2. 仿真速度:分布式延时模式比单位延时或零延时模式仿真速度慢,因为需要计算更多路径的延时。
  3. 与其他延时模式互斥`delay_mode_distributed`delay_mode_path`delay_mode_unit`delay_mode_zero 是互斥的,最后生效的指令会覆盖之前的设置。
  4. 默认模式:许多仿真器的默认延时模式可能是 `delay_mode_path`delay_mode_distributed,具体取决于工具和库文件。工艺厂商的库文件通常会在开头设置所需的延时模式。
  5. SDF 反标:在门级仿真中,SDF 文件包含的实际延时值(由布局布线工具产生)会反标到设计上。分布式延时模式能更好地利用这些精细的路径延时数据。

相关指令

  • `delay_mode_path:路径延时模式,延时集中在模块的输出端口。
  • `delay_mode_unit:单位延时模式,所有非零延时都用一个时间单位代替。
  • `delay_mode_zero:零延时模式,忽略所有延时。

`delay_mode_path 路径延时模式

  • 设置仿真器的路径延时(Path Delay)模式。在此模式下,延时值被集中定义在模块的输入到输出路径上,通常通过 specify 块进行描述。

语法

`delay_mode_path

作用与原理

  1. 路径集中延时:在路径延时模式下,仿真器将模块的延时信息集中在输入端口到输出端口的路径上,而不是像分布式延时那样分配到内部各个门级路径。每个输入到输出的路径可以有不同的上升、下降和关断延时。
  2. specify 块建模:路径延时主要通过模块内的 specify 块来定义。specify 块使用 specparam 声明延时参数,并通过路径延时语句(如 (A => Y) = delay_value;)指定具体的延时值。
  3. 仿真精度:路径延时模式提供了比单位延时更精确、比分布式延时更抽象的时序建模方式。它关注的是模块端口之间的延时,而不是内部门级细节。

典型使用场景

  • ASIC 标准单元库的时序模型:许多工艺厂商的标准单元库使用路径延时模式来建模,因为这种方式可以更简洁地描述单元的整体时序特性。
  • 模块级时序建模:对于较大的功能模块(如ALU、存储器控制器),设计者可能使用路径延时模式来建立模块级的时序模型,而不需要关心内部门级结构。
  • SDF(Standard Delay Format)反标:在门级仿真中,布局布线工具生成的SDF文件通常包含路径延时信息。当仿真器工作在路径延时模式下时,可以正确应用这些SDF延时值。
  • 混合仿真:在同一个设计中,部分模块可能使用路径延时模式,而其他模块使用分布式延时模式,具体取决于各模块的建模需求。

示例

  • 一个使用路径延时模式的简单与门模型:
`timescale 1ns/1ps

module AND2_path (output Y, input A, B);
    // 功能描述
    assign Y = A & B; // 功能描述通常不带延时,延时在 specify 块中定义

    // specify 块:定义路径延时
    specify
        // 定义路径延时参数
        specparam t_rise = 1.5; // 路径的上升延时
        specparam t_fall = 1.2; // 路径的下降延时
        specparam t_turnoff = 1.0; // 关断延时(可选)

        // 指定路径延时(所有输入到输出的路径使用相同的延时)
        (A, B *> Y) = (t_rise, t_fall, t_turnoff);
        
        // 或者分别为每个输入到输出指定延时
        // (A => Y) = (1.2, 1.0);
        // (B => Y) = (1.3, 1.1);
    endspecify
endmodule
  • 当在仿真顶层使用 `delay_mode_path 指令后,仿真器会按照 specify 块中定义的路径延时值进行仿真。

注意事项

  1. 与分布式延时的区别

    • 路径延时:延时集中在模块的输入到输出路径上,通过 specify 块描述。
    • 分布式延时:延时分配到内部各个门级路径上,每个门或连续赋值语句都有自己的延时。
    • 路径延时模式通常比分布式延时模式仿真速度更快,因为需要计算的延时路径更少。
  2. RTL 设计无需使用:在 RTL(寄存器传输级)设计阶段,代码是功能性的,不包含具体的时序信息,因此不需要也不应该使用 `delay_mode_path。它主要用于门级仿真和单元库建模。

  3. 默认模式:许多仿真器的默认延时模式是 `delay_mode_path,特别是当设计包含 specify 块时。

  4. 与其他延时模式互斥`delay_mode_path`delay_mode_distributed`delay_mode_unit`delay_mode_zero 是互斥的,最后生效的指令会覆盖之前的设置。

  5. SDF 反标兼容性:路径延时模式与SDF文件格式有很好的兼容性,因为SDF主要描述的是路径延时信息。

`delay_mode_unit 单位延时模式

  • 在此模式下,所有非零延时值都被简化为一个时间单位(通常是 timescale 中定义的时间单位),而不是使用实际的门级或路径延时值。

语法

`delay_mode_unit

作用与原理

  1. 延时简化:在单位延时模式下,仿真器将所有非零延时(包括门延时、连续赋值延时和 specify 块中的路径延时)都替换为一个时间单位。例如,如果 timescale 设置为 1ns/1ps,那么所有延时都会变成 1ns

  2. 仿真加速:由于所有延时都被简化为相同的值,仿真器不需要计算复杂的多路径延时,从而显著提高仿真速度。这对于早期功能验证和快速迭代非常有用。

  3. 时序抽象:单位延时模式提供了一种高度抽象的时序模型,它保留了延时的概念(信号不会瞬间变化),但忽略了不同路径之间的延时差异。

典型使用场景

  • 早期功能验证:在 RTL 设计初期,当设计者主要关注逻辑功能的正确性而非精确时序时,可以使用单位延时模式进行快速仿真。
  • 大型系统级仿真:对于包含大量模块的系统级仿真,使用单位延时模式可以大幅减少仿真时间,使设计者能够在合理时间内完成功能验证。
  • 教学和示例代码:在 Verilog 教学材料和示例代码中,经常使用单位延时模式来简化时序模型,使学生更容易理解基本概念。
  • 与零延时模式的对比测试:设计者可以在单位延时模式和零延时模式之间切换,检查设计对延时的敏感性。

示例

  • 假设有一个简单的与门模块,在分布式延时模式下可能有不同的输入到输出延时:
`timescale 1ns/1ps

// 原始模块(分布式延时模式)
module AND2_original (output Y, input A, B);
    assign #1.2 Y = A & B; // 功能延时
endmodule
  • 当使用 delay_mode_unit 指令后:
`delay_mode_unit

module AND2_unit (output Y, input A, B);
    assign #1 Y = A & B; // 所有延时都变为 1 个时间单位(1ns)
endmodule
  • 或者对于带有 specify 块的模块:
`timescale 1ns/1ps
`delay_mode_unit

module MUX2_unit (output Y, input S, A, B);
    // 功能描述
    assign Y = S ? A : B;
    
    // specify 块中的路径延时也会被简化为单位延时
    specify
        (S => Y) = (1.5, 1.3); // 原始值
        (A => Y) = (1.2, 1.0);
        (B => Y) = (1.3, 1.1);
    endspecify
endmodule
  • 在单位延时模式下,上述所有路径延时都会变为 1ns

注意事项

  1. 时序精度损失:单位延时模式完全忽略了不同路径之间的延时差异,因此不能用于时序验证。它只适用于功能验证。
  2. 与其他延时模式互斥delay_mode_unitdelay_mode_distributeddelay_mode_pathdelay_mode_zero 是互斥的,最后生效的指令会覆盖之前的设置。
  3. 默认行为:如果没有显式指定任何延时模式,仿真器通常会使用其默认模式(可能是 delay_mode_pathdelay_mode_distributed)。
  4. SDF 反标影响:在单位延时模式下,SDF(Standard Delay Format)文件中的精确延时值不会被使用,所有延时都会被简化为单位延时。
  5. 综合无关:与所有 delay_mode_* 指令一样,delay_mode_unit 只影响仿真行为,不参与综合、布局布线或时序优化。
  6. 使用时机
    • 使用:在早期 RTL 功能验证阶段,需要快速仿真时。
    • 避免使用:在进行门级时序仿真、SDF 反标验证或需要精确时序分析时。

`delay_mode_zero

**delay_mode_zero** 是一个 Verilog/SystemVerilog 编译指令,用于设置仿真器的**零延时(Zero Delay)模式**。在此模式下,所有延时值(包括门延时、连续赋值延时和 specify` 块中的路径延时)都被忽略,信号变化立即生效。

语法

`delay_mode_zero

作用与原理

  1. 忽略所有延时:在零延时模式下,仿真器完全忽略代码中的所有延时信息。这意味着:

    • 所有 # 延时操作符(如 #5#1.2)被忽略。
    • 所有门级延时(如 and #(2) u1 (y, a, b);)被忽略。
    • 所有 specify 块中定义的路径延时被忽略。
    • 所有 SDF(Standard Delay Format)反标的延时值被忽略。
  2. 纯功能仿真:零延时模式提供了一种纯粹的逻辑功能验证环境,信号变化在零时间内传播,仿真器只检查逻辑正确性,不模拟任何时序行为。

  3. 仿真速度最快:由于不需要计算和调度延时事件,零延时模式是所有延时模式中仿真速度最快的。

典型使用场景

  • RTL 功能验证:在 RTL(寄存器传输级)设计初期,当设计者只关心逻辑功能的正确性时,可以使用零延时模式进行快速仿真验证。
  • 大型系统级仿真:对于包含大量模块的复杂系统,使用零延时模式可以极大提高仿真速度,快速验证系统级功能。
  • 与有时序的仿真对比:设计者可以在零延时模式和有时序模式(如单位延时、路径延时)之间切换,检查设计对延时的敏感性,发现潜在的竞争条件和时序问题。
  • 教学和示例代码:在 Verilog 教学材料和简单示例中,经常使用零延时模式来简化仿真,使学生专注于逻辑设计本身。

示例

  • 假设有一个简单的与门模块,在分布式延时模式下有延时:
`timescale 1ns/1ps

// 原始模块(带延时)
module AND2_with_delay (output Y, input A, B);
    assign #1.2 Y = A & B; // 1.2ns 的功能延时
endmodule
  • 当使用 delay_mode_zero 指令后:
`delay_mode_zero

module AND2_zero (output Y, input A, B);
    assign #1.2 Y = A & B; // 延时被忽略,Y 立即随 A&B 变化
endmodule
  • 或者对于带有 specify 块的模块:
`timescale 1ns/1ps
`delay_mode_zero

module MUX2_zero (output Y, input S, A, B);
    // 功能描述
    assign Y = S ? A : B;
    
    // specify 块中的路径延时被忽略
    specify
        (S => Y) = (1.5, 1.3);
        (A => Y) = (1.2, 1.0);
        (B => Y) = (1.3, 1.1);
    endspecify
endmodule
  • 在零延时模式下,上述所有延时都会被忽略,信号变化立即生效。

注意事项

  1. 无时序验证能力:零延时模式完全忽略了时序信息,因此绝对不能用于时序验证。它只适用于纯功能验证。
  2. 可能掩盖时序问题:在零延时模式下,潜在的竞争条件、时序违例和毛刺可能被掩盖,因为所有信号变化都是瞬时的。
  3. 与其他延时模式互斥delay_mode_zerodelay_mode_unitdelay_mode_pathdelay_mode_distributed 是互斥的,最后生效的指令会覆盖之前的设置。
  4. 默认行为:如果没有显式指定任何延时模式,仿真器通常会使用其默认模式(可能是 delay_mode_pathdelay_mode_distributed)。
  5. SDF 反标无效:在零延时模式下,SDF 文件中的精确延时值完全被忽略。
  6. 综合无关:与所有 delay_mode_* 指令一样,delay_mode_zero 只影响仿真行为,不参与综合、布局布线或时序优化。
  7. 使用时机
    • 使用:在早期 RTL 功能验证阶段,需要最快仿真速度时;在验证纯组合逻辑功能时。
    • 避免使用:在进行任何形式的时序仿真、门级仿真、SDF 反标验证或需要检查时序行为时。
  8. 现代 RTL 开发中的使用:虽然零延时模式仿真速度最快,但在现代 RTL 开发中,设计者通常更倾向于使用单位延时模式(delay_mode_unit)进行功能验证,因为它保留了基本的延时概念(信号不会瞬间变化),能更好地模拟实际电路行为,同时仿真速度仍然很快。

与 SDC 约束的区别

  • delay_mode_*是 Verilog 仿真编译指令,用来控制仿真器在门级仿真或 SDF 回标时如何解释和应用延时信息,例如采用门延时、路径延时、单位延时或零延时,它只影响仿真行为和仿真结果,不参与综合、布局布线和时序优化;而 SDC(Synopsys Design Constraints)约束则用于告诉综合、STA 和 P&R 工具设计的时序目标和时序例外(如时钟周期、输入输出延时、False Path、Multicycle Path 等),从而指导工具进行逻辑优化、布局布线和时序分析,直接影响最终硬件实现。因此,delay_mode_* 解决的是“仿真时如何模拟延时”的问题,而 SDC 解决的是“芯片实际需要满足什么时序要求”的问题,前者属于仿真控制机制,后者属于设计约束机制。

  • FPGA 内部当然存在大量真实路径延时,这些延时真实存在,而且是 FPGA STA(静态时序分析)检查的核心对象。

  • RTL综合后,Vivado/Quartus/Radiant会生成内部数据库,然后 STA 直接分析,而不是依赖 Verilog 的 delay_mode_distributeddelay_mode_path 等仿真延时模型。delay_mode_* 更多是传统 ASIC 门级仿真时代的产物,在现代 FPGA 开发中几乎不会直接接触。

2.3. 仿真库相关

`resetall

  • `resetall 是一个 Verilog/SystemVerilog 编译指令,用于重置(恢复)所有编译器指令到其默认状态。它通常用在文件的开头或包含多个独立模块/库的文件中,以确保后续代码不会受到之前文件中设置的编译指令的影响。
  • resetall 的影响从它出现的位置开始,持续到文件结束,或直到被另一个 resetall 或显式的指令设置覆盖。

语法

`resetall

作用与原理

  • 重置编译器状态resetall 会将所有已生效的编译指令恢复为仿真器/编译器的默认值。这包括但不限于:

    • **timescale**:恢复为工具默认的时间单位和精度(通常为 1ns/1ns` 或由命令行选项指定)。
    • **default_nettype**:恢复为默认的 wire` 类型。
    • celldefine** / **endcelldefine:如果之前有 celldefine 生效,resetall 会结束其作用域,恢复到非标准单元定义模式。
    • delay_mode_*:恢复为仿真器的默认延时模式(通常是 delay_mode_pathdelay_mode_distributed)。
    • begin_keywords** / **end_keywords:结束当前关键字版本作用域,恢复为默认或命令行指定的语言版本。
    • 其他编译器指令,如 unconnected_drivenounconnected_drivepragma 等。
  • 创建独立的编译上下文:当多个文件被 include 或一起编译时,前一个文件中设置的指令可能会意外影响后一个文件。在文件开头使用 resetall 可以创建一个“干净”的起点,避免跨文件污染。

  • 提高代码可移植性:确保模块或库文件不依赖于外部编译环境,使其在任何编译上下文中都能有一致的行为。

典型使用场景

  • 库文件或 IP 核文件开头:工艺厂商提供的标准单元库(.v 文件)或第三方 IP 核文件通常会在开头使用 resetall,确保其内部指令设置不会泄露到用户设计中。
  • 多文件编译的隔离:在编译由多个独立团队或来源提供的 Verilog 文件时,可以在每个文件(或每组文件)前使用 resetall,防止指令冲突。
  • 测试平台(Testbench):在复杂的测试环境中,可能会在不同阶段切换不同的编译指令(如切换延时模式),使用 resetall 可以明确重置状态。
  • 确保仿真可重复性:在仿真脚本中,在编译设计之前使用 resetall 可以确保仿真从一个已知的默认状态开始。

示例

  • 一个标准单元库文件可能这样使用:
// cell_lib.v
`resetall                    // 重置所有指令到默认状态
`timescale 1ns/100ps        // 为本文件设置时间尺度
`celldefine                  // 开始标准单元定义

module AND2X1 (Y, A, B);
    // 标准单元实现
endmodule

`endcelldefine
  • 在用户顶层文件中:
// top.v
`timescale 1ns/1ps          // 用户设置的时间尺度
`default_nettype none       // 用户设置的默认网络类型

`include "cell_lib.v"       // 包含库文件,库文件内的 `resetall` 不会影响本文件之后的设置

// 此后的编译仍遵循 1ns/1ps 和 `default_nettype none`
module top;
    // ...
endmodule

2.4. 关键字版本控制

  • begin_keywordsend_keywords 是 SystemVerilog(IEEE 1800)引入的一对编译指令,用于显式指定源代码所使用的 Verilog/SystemVerilog 语言版本。它们主要用于确保不同版本的代码在编译时能正确解析关键字和语法,避免因语言标准演进导致的关键字冲突或语法误解。

`begin_keywords

  • `begin_keywords 指令用于开始一个关键字版本作用域,在该作用域内,编译器将按照指定的语言标准版本解析关键字。

语法

`begin_keywords "version_string"
  • version_string:指定语言标准版本的字符串,必须用双引号包裹。

支持的版本字符串

版本字符串对应的语言标准说明
"1364-1995"IEEE Std 1364-1995原始 Verilog 标准
"1364-2001"IEEE Std 1364-2001Verilog-2001(添加了 generatelocalparam 等)
"1364-2005"IEEE Std 1364-2005Verilog-2005(小修订)
"1800-2005"IEEE Std 1800-2005SystemVerilog-2005(首次统一标准)
"1800-2009"IEEE Std 1800-2009SystemVerilog-2009(添加 interface class 等)
"1800-2012"IEEE Std 1800-2012SystemVerilog-2012(添加 letimplements 等)
"1800-2017"IEEE Std 1800-2017SystemVerilog-2017(当前广泛使用的版本)

作用与原理

  1. 关键字解析控制:不同版本的 Verilog/SystemVerilog 标准引入了新的关键字(如 logicbyteshortintlongintbitstringenumstructunionclassinterfacepackageprogramassertcoverconstraintrandomize 等)。使用 begin_keywords 可以告诉编译器按照指定版本的关键字列表来解析代码。

  2. 向后兼容:当需要编译旧版本(如 Verilog-1995)的代码时,使用 begin_keywords "1364-1995" 可以确保新版本的关键字(如 logic)不会被误认为是关键字,从而保持兼容性。

  3. 向前兼容:当在较老的环境中编译新版本代码时,可以指定新版本字符串,但实际支持程度取决于工具。

典型使用场景

  • 混合编译新旧代码:在同一个项目中包含不同语言版本的模块时,可以用 begin_keywords/end_keywords 对每个文件或代码块进行隔离。
  • 第三方 IP 集成:集成来自不同供应商、基于不同语言标准编写的 IP 核时,确保关键字被正确解析。
  • 测试平台(Testbench):SystemVerilog 的测试平台通常使用较新的语言特性(如类、随机化、断言),而 DUT(Design Under Test)可能是纯 Verilog。可以在测试平台文件中使用 begin_keywords "1800-2017"
  • 避免关键字冲突:如果用户标识符恰好与新版本的关键字同名,在旧版本模式下可以正常使用。

示例

// 文件开头指定使用 SystemVerilog-2017 关键字
`begin_keywords "1800-2017"

module testbench;
    logic [7:0] data;        // SystemVerilog 关键字
    bit         valid;        // SystemVerilog 关键字
    string      message = "Hello"; // SystemVerilog 关键字
    
    initial begin
        $display("Testbench using SystemVerilog-2017 keywords");
    end
endmodule

`end_keywords

`end_keywords

  • end_keywords** 指令用于**结束由 begin_keywords` 开始的关键字版本作用域,恢复到此前的关键字版本设置(或工具默认设置)。

示例

// 默认模式(可能是工具默认或命令行指定)
module legacy_module;
    // 这里使用默认关键字集
endmodule

// 切换到 SystemVerilog-2017 关键字
`begin_keywords "1800-2017"

module sv_module;
    logic clk;  // SystemVerilog 关键字
    // ...
endmodule

`end_keywords  // 恢复为默认关键字集

module another_legacy_module;
    // 这里又使用默认关键字集
endmodule

`line

  • `line 是一个 Verilog/SystemVerilog 编译指令,用于修改编译器内部记录的文件名和行号信息。它主要用于代码生成工具和调试工具,在普通 RTL 开发中几乎不会直接使用。

语法

`line line_number "filename" level
  • line_number:一个十进制整数,指定新的行号。
  • “filename”:用双引号包裹的字符串,指定新的文件名。
  • level:一个整数,通常为 0、1 或 2,控制指令的生效范围:
    • 0:重置行号和文件名信息,恢复为实际源文件位置。
    • 1:设置新的行号和文件名,后续行号从新值递增。
    • 2:设置新的行号和文件名,但后续行号从新值递减(较少使用)。

2.5. Verilog-AMS扩展(较少见)

  • Verilog-AMS(Verilog Analog and Mixed-Signal)是 Verilog 的模拟混合信号扩展,主要用于模拟电路、混合信号系统以及数模混合建模。除了支持模拟行为描述外,Verilog-AMS 也引入了一些特有的编译指令,主要用于IP保护AMS建模控制

protect / endprotect

  • protect** 和 **endprotect 是一对编译指令,用于标记需要加密保护的代码区域。它们通常用于保护知识产权(IP),防止源代码被未经授权的查看或修改。

语法

`protect
// 需要保护的代码区域
`endprotect

示例

// 普通可查看的接口描述
module adc_core (
    input  wire         clk,
    input  wire         rst_n,
    input  wire [11:0]  analog_in,
    output reg  [11:0]  digital_out
);

// 加密保护的内部核心算法
`protect
    // 专有的 sigma-delta 调制器算法
    // 此处代码会被加密,用户无法直接查看
    real sigma = 0.0;
    always @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin
            digital_out <= 12'b0;
            sigma <= 0.0;
        end else begin
            // 专有算法实现...
            sigma <= sigma + analog_in - digital_out;
            digital_out <= (sigma > 0) ? 12'hFFF : 12'h000;
        end
    end
`endprotect

endmodule
  • 除了上述保护指令外,Verilog-AMS 环境可能还会用到以下指令(具体支持情况需查阅工具手册):

    • `discipline:定义信号 discipline(域),如电压、电流、温度等。
    • `nature:定义 nature 类型,描述模拟量的物理特性。
    • `bound_step:控制模拟求解器的步长边界。
    • `initial_step:设置模拟仿真的初始步长。
    • `analog:标记模拟行为描述块的开始(实际是关键字而非编译指令)。

工具支持与注意事项

  1. 厂商差异protect/endprotectprotected/endprotected 的支持程度、加密算法和具体行为因 EDA 厂商(Cadence、Synopsys、Siemens EDA 等)而异。
  2. AMS 建模:在纯 Verilog-AMS 建模中,这些保护指令可以用于保护敏感的模拟行为描述、参数方程或专有的器件模型。
  3. 混合流程:在数模混合仿真中,加密的 AMS 模块仍需能与数字仿真器正确交互。
  4. 许可证绑定:加密代码通常需要与特定的许可证文件或硬件狗绑定,确保只有授权用户可使用。
  5. 交付物:供应商交付的通常是加密后的 .v.va 文件,用户无法查看内部实现,但可进行仿真、综合等操作。

总结

  • protect/endprotect** 和 **protected/endprotected 是 Verilog-AMS 环境中用于 IP 保护的编译指令。
  • 主要用于保护商业 IP 核和公司内部敏感模块,防止源代码泄露。
  • 支持程度和具体实现因 EDA 工具而异,使用时需仔细查阅厂商文档。
  • 在普通数字 RTL 设计中极少使用,主要出现在模拟/混合信号 IP 和 AMS 建模中。

2.6. SystemVerilog新增常见指令

  • 除了继承 Verilog 指令外,SystemVerilog 经常使用:

`FILE

当前文件名。

$display("%s", `__FILE__);

`LINE

当前行号。

$display("%0d", `__LINE__);

示例:

$display("%s:%0d", `__FILE__, `__LINE__);

输出:

tb.sv:128

3.示例

工程模板示例

defines.v

`ifndef __DEFINES_V__
`define __DEFINES_V__

`timescale 1ns/1ps
`default_nettype none

`define DATA_WIDTH 32
`define ADDR_WIDTH 16

`endif

top.v

`include "defines.v"

module top;

reg [`DATA_WIDTH-1:0] data;

endmodule
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值