Verilog HDL 编译器指令
复杂一点的系统在进行设计或者验证时,都会用到一些编译器指令,那么什么是编译器指令?
Verilog HDL编译器指令由重音符(')开始。在Verilog 语言编译时,特定的编译器指令在整个编译过程中有效(编译过程可跨越多个文件),直到遇到其它的不同编译程序指令。不完整的标准编译器指令如下:
下面分解一下,每个指令单独说明一下:
’define和’undef
1.’define指令
’define指令用于文本替换,它很像C语言中#define指令。它生成一个文本宏。该指令既可以在模块内部定义,也可以在模块之外定义。一旦编译了’define指令,它在整个编译过程中都有效。
如果已经定义了一个文本宏,那么在它的宏名之前加上重音符号(’)就可以在源程序中'引用该文本宏。
在编译器编译时,将会自动用相应的文本块代替字符串'macro\_name。将Verilog HDL中的所有编译指令都看作预定义的宏名,将一个编译指令重新定义为一个宏名是非法的。
一个文本宏定义可以带有一个参数。这样,就允许为每一个单独的应用定制文本宏。
文本宏定义的语法格式如下:
'define <text_macro_name> <macro_text>
其中: (1)<text_macro_name>为文本的宏名字,其语法格式为
text_macro_identifier[<list_of_formal_arguments>]
①text_macro_identifier为宏标识符,要求简单标识符。
②<list_of_formal_arguments>为形参列表。一旦定义一个宏名,就可以在源程序的任何地方使用它,而没有范围限制。
(2)<macro_text>为宏文本,可以是与宏名同行的任意指定文本。
①如果指定的文本超过一行,那么新的一行需要用反斜杠()作为起始。这样,反斜杠后面的文本也将作为宏文本的一部分,参与宏替换。反斜杠本身并不参与宏替换,编译时将忽略它。
②如果宏文本包含了一个单行注释语句(以“//”开始的注释语句),则该语句不属于替换文本,编译时不参与替换。
③宏文本可以空白。
[例] ’define指令Verilog HDL化述的例子1。
'define wordsize 8
reg[1:'wordsize] data;
//define a nand with variable delay
'define var_nand(dly) nand #dly
'var_nand(2) gl21 (q21, nl0, nil);
'var_nand(5) gl22(q22, nl0, nil);
[例] ’define指令Verilog HDL非法描述的例子2
'define first_half "start of string
$ display( ’first_half end of string");
[例] 'define 指令 Verilog HDL 非法描述的例子 3。
'define max(a,b)((a)>(b)?(a):(b))
n = 'max(p + q, r + s) ;
将要扩展为
n = ((p + q ) >(r + s))?(p + q ) :(r + s);
2.'undef指令
'undef指令用于取消前面定义的宏。如果先前并没有使用指令’define进行宏定义,那么使用’undef指令将会导致一个警告。
’undef指令的语法格式如下:
'undef text_macro_identifier
一个取消的宏没有值, 就如同没有被定义一样。
'define SIZE 8
'define xor_b(x,y)(x &!y)|(!x & y)
//These text macros can be used as follow:
reg ['SIZE - 1 : 0] data out;
c = xor_b(a, b);
'undef SIZE
’celldefine和’endcelldefine
这两个指令用于将模块标记为单元模块,它们表示包含模块定义。某些PLI使用单元模块用于这些应用,如计算延迟。
该命令可以出现在源代码描述中的任何地方。但是,推荐将其放在模块定义的外部。
[例] ’celldefine指令Verilog HDL描述的例子。
'celldefine
module my_and(y, a, b);
output y;
input a, b;
assign y = a & b;
endmodule
'endcelldefine
’default_nettype
该指令用于为隐含网络指定网络类型,也就是为那些没有被说明的连线定义网络类型。它只可以出现在模块声明的外部,允许多个’default\_netype指令。
如果没有出现’default_netype指令,或者如果指定了’resetall指令,则隐含的网络类型是wire。当default_netype设置为none时,需要明确地声明所有网络;如果没有明确地声明网络,则产生错误。
’default_netype指令格式为:
'default_nettype default_nettype_value
其中default_nettype_value的值可以是wire、tri、tri0、tri1、wand、triand、wor、trior、trireg、uwire和none。
'ifdef、 'else、 ’elsif、 ’endif 和’ifndef
'ifdef编译器命令
条件编译:
显而易见,即只有在条件满足的时候才对这部分代码进行编译,也就是对一部分内容指定了编译的条件:
当满足条件时对一组语句进行编译,
当条件不满足时则对另外一组语句进行编译。
用途:
1、选择一个模板的不同代表部分。
2、选择不同的时序或结构信息。
3、对不同的EDA工具,选择不同的激励。(如:Verilog代码中的一部分可能因编译环境不同而不同,为避免在不同环境需要替换不同版本的Verilog 设计,条件编译就是一个很好的解决方案)
用法
'ifdef 宏名(标识符)
程序段1...
'else
程序段2...
'endif
当宏名被定义过了,就编译程序段1;反之,当宏名未被定义过,就编译程序段2;
其中,else部分可以省略。即:当宏名被定义过了,就编译程序段1;反之,不编译程序段1;
[例] ’ifdef 指令 Verilog HDL 描述的例子。
module and_op (a, b, c);
output a;
input b, c;
'ifdef behavioral
wire a = b & c;
'else
and a1 (a,b,c);
'endif
endmodule
‘ifndef编译器命令
额外的,还有‘ifndef语句,与’ifdef功能相反:
即当宏名没被定义过,就编译程序段1;反之,当宏名未被定义过了,就编译程序段2;
'ifndef 宏名(标识符)
程序段1...
'else
程序段2...
'endif
[例] ’ifndef 指令 Verilog HDL 描述的例子。
module test;
'ifdef first_block
'ifndef second_nest
initial $ display("first_block_is_defined");
'else
initial $ display("first_block and second_nest defined");
'endif
'elsif second_block
initial $ display( "second_block defined, first_block is not”);
'else
'ifndef last_result
initial $ display("first_block, second_block,"
”last_result not defined.");
'elsif real_last
initial $ display("first_block, second_block not defined,"
" last_result and real last defined.");
'else
initial $ display("Only last result defined!");
'endif
'endif
endmodule
这里还有一个‘elsif指令,简单说明一下。
'ifndef test_macro_identifier
ifndef_group_of_1ines
{ 'elsif text_macro_identifier elsif_group_of_lines }
[ 'else else_group_of_lines ]
'endif
①当遇到’ifndef时,测试’ifdef文本宏标识符,查看在Verilog HDL源文件描述中是否使用'define作为一个文本宏名字;②如果’ifndef没有定义文本宏标识符,则对’ifndef所包含的行作为描述的一部分进行编译,如果还有’else或者’dsif编译器指令,则忽略这些编译器指令和相关的行组;③如果定义’ifiidef文本宏标识符,则忽略’ifndef所包含的行;④如果有’elsif编译器指令,测试'elsif文本宏标识符,查看在Verilog HDL源文件描述中,是否使用'define作为一个文本宏名字;⑤如果’elsdef定义文本宏标识符,则对’elsdef所包含的行作为描述的一部分进行编译,如果还有’else或者’elsif编译器指令,则忽略这些编译器指令和相关的行组;⑥如果没有定义第一个'elsif文本宏标识符,则忽略第一个’elsif所包含的行;⑦如果有多个’elsif编译器命令,将按照它们在Verilog HDL源文件中的描述顺序和评估第一个’elsif编译器指令的方法,对这些指令进行评估;⑧如果有一个’else编译器命令,则将’else所包含的行作为描述的一部分进行编译。
’include
在编译期间,’include编译器指令用于嵌入另一个文件的内容。既可以用相对路径名定义文件,也可以用全路径名定义文件。其语法格式为:
'include "filename"
使用’inchide编译器指令的优势主要体现在以下几方面:
(1)提供了一个配置管理不可分割的一部分;
(2)改善了VerilogHDL源文件描述的组织结构;
(3)便于维护Verilog HDL源文件描述。
[例 ]’include指令Verilog HDL描述的例子。
'include "parts/count. v"
'include "fileB"
'include "fileB" //包含 fileB
'resetall
该编译器遇到’resetall指令时,会将所有的编译指令重新设置为默认值。推荐在源文件的开始放置’resetall.将'resetall命令放置在模块内或者UDP声明中是非法的。其语法格式为
'resetall
’line
对于Verilog工具来说,跟踪Verilog HDL源文件的名字和文件的行的行号是非常重要的,这些信息可以用于调试错误消息或者源代码,Verilog PL1访问可以它。
然而,在很多情况下,Verilog源文件由其他工具进行了预处理。由于预处理工具可能在Verilog HDL源文件中添加了额外的行,或者将多个源代码行合并为一个行,或者并置多个源文件,等等,可能会丢失原始的源文件和行信息。
'line编译器命令可以用于指定的原始源代码的行号和文件名。如果其他过程修改了源文件,这允许定位原始的文件。当指定了新行的行号和文件名时,编译器就可以正确地定位原始的源文件位置。然而,这要求相应的工具不产生’line命令。
其语法格式为
'line number "filename" level
其中,number是一个正整数,用于指定跟随文本行的新行行号,filename是一个字符串常数,将其看作文件的新名字,文件名可以是全路径名字或者相对路径名字;level为该参数的值,可以是0、1或者2:①当为1的时候,输入一个include行后的下面一行是第一行;②当为2的时候,退出一个inlcude行后的下面一行是第一行;③当为0的时候,指示任何其他行。
[例] 'line 指令 Verilog HDL 描述的例子。
'line3 "orig.v" 2
//该行是 orig.v 存在 include 文件后的第 3 行。
’timescale
在Verilog HDL模型中,所有的时延都用单位时间表述。可使用'timescale编译器指令将时间单位与实际时间相关联,该指令用于定义时延的单位和时延精度。
作用:
timescale用于定义延时的单位和延时的精度,如
timescale 1ns/100ps那么时间单位就是1ns,精度就是100ps。时间单位,表示了仿真时测量的单位,比如延时1,1ns;精度则表示仿真器只识别的范围,比如精度是100ps,那么如果你1.3ns,编译器是识别,但是如果写1.32,那么由于精度达不到那么细,所以0.02被四舍五入掉。
\`timescale影响着全部模块,直到遇到另外的timescale。
’timescale编译器指令格式为:
’timescale time_unit/time_precision
其中,time_unit指定用于时间和延迟测量的单位,可选的值为1、10或100;time_precision用于仿真前,确定四舍五入延迟值。时间分辨率子,可选的单位为s \ms\us\ns\ps或fs。
[例] 'timescale 指令 Verilog HDL 描述的例子。
'timescale 10 ns / 1 ns
module test;
reg set;
parameter d = 1.55;
initial begin
# d set = 0;
# d set = 1;
end
endmodule
根据时间精度, 参数 d 的值从 1.55 四舍五入到 1.6。模块的时间单位是 10ns 精度是1 ns。因此, 参数 d 的延迟从 1.6 标定到 16。
’unconnected_drive和'nounconnected_drive
当一个模块所有未连接的端口出现在'unconnected_drive和’nounconnecteddrive指令之间时,将这些未连接的端口上拉或者下拉,而不是按通常的默认值处理。
指令’unconnected_drive使用pull1/pull0参数中的一个:当指定pull时,所有未连接的端口自动上拉;当指定pill0时,所有未连接的端口自动下拉。
建议成对使用’unconnected_drive和'nounconnected_drive指令,但不是强制要求。这些指令在模块外部成对指定。
'resetall指令包括'nounconnected_drive指令的效果。
[例]nounconnected_drive/ 'unconnected drive 指令 Verilog HDL 描述的例子。
'unconnected_drive pull1
module my_and(y, a, b);
output y;
input a, b;
assign y = a & b;
endmodule
module test;
reg b;
wire y;
my_and ul(y, ,b);
endmodule
'nounconnected_drive
'pragma
’pragma指令是一个结构化的说明,它用于改变对Verilog HDL源文件的理解。由这个指令所引入的说明称为编译指示。编译指示不同于Verilog HDL标准所指定的结果,它为指定实现的结果。其语法格式为
’pragma pragma_name [ pragma_expression { , pragma_expression } ]
其中 ,pragma_name 为编译指示的名字, 可以是 $ 开头的系统标识符或者一般标识符; pragma_expression 为编译指示表达式。
注:reset和resetall编译指示将恢复默认值和pragma_keywords所影响的状态。
'begin_keywords和’end_keyword
'begin_keywords和'end_keyword指令用于指定在一个源代码块中,基于不同版本的IEEE_Stdl364标准,确定用于关键字的保留字。该对指令只指定那些作为保留关键字的标识符。只能在设计元素(模块、原语和配置)外指定该关键字,并且需要成对使用。其语法格式为:
'begin_keywords "version_specifier"
...
'end_keyword
其中,version_specifier为可选的参数,包括1364-1995、1364-2001、1364-2001-noconfig和1364-2005。
[例] ’begin_keywords 和 'end_keyword 指令 Verilog HDL 描述的例子。
'begin_keywords "1364- 2001" //使 用 IEEE Std 1364- 2001 Verilog 关 键 字
'module m2(...);
reg[63:0] logic; //logic 不 是 1364 - 2001 的 关 键 字
...
endmodule
'end_keywords
(补充一)Verilog编译器指示语句
设计者在写设计代码时,有时可能针对仿真写一些语句,这些语句可能是不为DC所接受,也不希望DC接受;设计者如果不对这些语句进行特殊说明,DC读入设计代码时就会产生语法错误。另一种情况是,设计者在写设计代码,有些设计代码是为专有的对象写的(如公司内部),这些专有的设计代码可能不希望被综合。Synopsys提供了引导语句,设计者可以使用这些引导语句控制DC综合的对象
可以利用HDL描述中的一些特定的注释语句来控制综合工具的工作,从而弥补仿真环境和综合环境之间的差异,这些注释语句称为编译器指示语句。
translate_off/ translate_on
这组语句用来指示DC停止翻译 “//synopsys.。.translate_off”之后的Verilog描述,直至出现 “//synopsys translate_on”。当Verilog代码钟含有供仿真用的不可综合语句时,这项功能能使代码方便地在仿真工具与综合工具之间移植。
例1(translate_off/ translate_on指示语句的使用):
//synopsys translate_off
//synopsys translate_on
parallel_case/ full_case
DC可能使用带优先级的结构来综合Verilog的case语句,为避免这种情况,可以使用“//synopsys.。.parallel_case”指示DC将case语句综合为并行的多路选择器结构。
(parallel_case指示语句的使用):
always @ (state)
case (state) //synopsys parallel_case
2’b00:new_state = 2’b01;
2’b01:new_state = 2’b10;
2’b10:new_state = 2’b00;
default:new_state = 2’b00;
endcase
另外,Verilog允许case语句不覆盖所有可能情况,当这样的代码由DC综合时将产生锁存器。为避免这种情况,可以使用“//synopsys full_case”指示DC所有可能已完全覆盖。
例2 (full_case指示语句的使用):
always @ (sel or a1 or a2)
case (sel) //synopsys full_case
2’b00:z = a1;
2’b01:z = a2;
2’b10:z = a1 & a2;
endcase
(补充二)Verilog PL1是什么?
上面有提到过PLI接口,这里简单介绍下,因为用的比较少,所以就一笔带过。
编程语言接口(Program Language Interface,PLI)提供了通过C语言函数对Verilog数据结构进行存储和读取操作的方法。
PLI接口主要提供以下三种功能。
(1)PLI接口允许用户编写自定义的系统任务和系统函数。用户写出相应的PLI程序并连接到仿真器后,就可以在自己写的VerilogHDL程序中使用这些系统任务和系统函数。一旦在仿真过程中调用这些任务或者函数,仿真器就会找到对应的用户所编写的PLI程序并执行,从而实现仿真器的定制。
(2)这个接口还允许用户在自己的PLI程序中与仿真器中例化的VerilogHDL硬件进行交互,如读一个线网络的值、向一排寄存器写值以及设置一个单元的延迟,等等。
对于PLI程序而言,仿真器中的Verilog实例完全透明,用户可以对这些硬件做任何操作(当然,不能修改硬件结构)。有了这个功能,用户就可以在自定义的任务/函数中对硬件执行某些用VerilogHDL语言难以完成的操作。
(3)某些特定的操作需要对仿真过程中一些信号的变化做出响应,虽然可以用always过程语句来监控少量信号的变化,但如果需要监测大量信号,这种机制并不现实。
PLI接口提供了一种函数回调机制解决这个问题。用户可以将某个线网络/寄存器等信号挂上一个PLI程序中的C函数。每当该信号变化时,调用这个C函数,从而很方便地监测信号。
除了上面所说的这些机制外,PLI还能让用户控制仿真的过程,例如暂停、退出以及向日志文件里写信息等,还可以获取仿真过程的数据,如当前仿真时间等。在实际的PLI程序中,同样不可缺少这些功能。
参考资料
1、http://www.elecfans.com/d/651...
2、EDA原理及Verilog实现
3、https://www.cnblogs.com/IClea...
原文:FPGA 的逻辑
作者:碎碎思
相关文章推荐