----------------------------------------------------------------------------
Strength
强度
除了逻辑值外,Net类型的变量还可以定义强度,因而可以更精确地建模。Net的强度来自于动态 Net 驱动器的强度。在开关级仿真时,当 Net由多个驱动器驱动且其值互相矛盾时,常用强度(Strength)的概念来描述这种逻辑行为。
语法:
{either}
( Strength0, Strength1)
( Strength1, Strength0)
( Strength0)
{pulldown primitives only}
( Strength1)
{pullup primitives only}
( ChargeStrength)
{trireg nets only}
Strength0 = {either}
supply0
strong0
pull0
weak0
highz0
Strength1 = {either}
supply1
strong1
pull1
weak1
highz1
ChargeStrength = {either}
large
medium
small
在程序中位于何处:
请参照: Net、Instantiation、Continuous Assignment 的说明。
规则:
· 关键词Strength0 和 Strength1用于定义Net的驱动器强度。其中Strength
表
关于同志近三年现实表现材料材料类招标技术评分表图表与交易pdf视力表打印pdf用图表说话 pdf
示强度,与紧跟着的0和1连起来分别表示输出逻辑值为0和1时的强度。
· 在强度声明中可选择不同的强度关键字来代替strength,但(highz0,highz1) 和 (highz1,highz0)这两种强度定义是不允许的,在pullup (上拉)和 pulldown(下拉) 门的强度声明中 highz0 和 highz1是不允许的。
· 默认的强度定义为 (strong0,strong1),但下述情况除外:
1) 对于pullup and pulldown门,默认强度分别为(pull1) 和 (pull0)。
2) 对于trireg 的 Net,默认强度为 medium
3) 强度定义为supply0 和 supply1的Net,总是能提供强度。
· 在仿真期间,Net的强度来自于Net上的主驱动强度(即具有最大强度值的实例或连续赋值语句)。如果Net未被驱动,它会呈现高阻值,但以下情况除外:
1) tri0 和 tri1 类型的 net 分别具有逻辑值0和1,并为pull强度。
2) trireg 类型的 net 保持它们最后的驱动值。
3) 强度为supply0 和 supply1 的 nets分别具有逻辑值0和1,并能提供驱动能力。
· 强度值有强弱顺序,可从 supply(最强的)依次减弱排列到 highz(最弱的),当需要确定Net的确实逻辑值和强度时,或者当 Net由多个驱动器驱动而且驱动相互间出现冲突时,出现冲突的两个强度值在强弱顺序表中的相对位置就会对该Net的真实逻辑值起作用。
Supply
Strong
Pull
Large
Weak
Medium
Small
Highz
可综合性问题:
不可综合
提示:
可以在$display和$monitor等中用特定的
格式
pdf格式笔记格式下载页码格式下载公文格式下载简报格式下载
控制符 %V 显示其强度值。
举例说明:
assign (weak1,weak0) f= a + b;
trireg (large ) c1,c2;
and (strong1,weak0) u1(x,y,z);
请参阅:
Continous Assignment、Instantiation、Net、$display 的说明
---------------------------------------------------------------------------
String
字符串
字符串能够用在系统任务(诸如$display和$monitor等)中作为变量,字符串的值可以像数字一样储存在寄存器中,也可以像对数字一样对字符串进行赋值、比较和拼接。
语法
见“string” 说明。
在程序中位于何处:
请参见 Expression 说明。
规则
· 一条字符串不能占原代码的多行。
· 字符串可以包含下列扩展字符。
· \n
· 换行
· \t
· Tab符
· \\
· 反斜杠字符\
· \”
· 双引号字符”
· \nnn
· 八进制的ASCII字符
· %%
· 百分号%
· 诸如$display和$monitor等的系统任务中的打印字符串可以包含特殊的格式控制符(如%b)(参见$display的说明)。
· 当字符串存储于寄存器中,每个字符要占8位,字符以ASCII代码形式存储。VerilogHDL语言的字符串的定义和 C 语言的不一样。在C 语言中需要用,而在VerilogHDL语言中不需要用ASCII代码的 0字符来表示字符串的结束。
注意!
在表达式中使用字符串时,请注意填加物。对字符串的处理跟对数字的处理方式一样,当字符所占的位数少于寄存器的数目时,则在字符串的左边的寄存器中填加0。
举例说明:
reg [23:0] MonthName[1:12];
initial
begin
MonthName[1] = "Jan";
MonthName[2] = "Feb";
MonthName[3] = "Mar";
MonthName[4] = "Apr";
MonthName[5] = "May";
MonthName[6] = "Jun";
MonthName[7] = "Jul";
MonthName[8] = "Aug";
MonthName[9] = "Sep";
MonthName[10] = "Oct";
MonthName[11] = "Nov";
MonthName[12] = "Dec";
end
请参阅:
NUMBER, $display 的说明。
----------------------------------------------------------------------------
Task
任务
任务常用于把模块代码分割成由若干声明语句构成的较大的块,便于模块代码的理解和维护,也可以从模块代码的不同位置执行这样一个常见的顺序声明语句块。
语法:
task TaskName;
[ Declarations...]
Statement
endtask
Declaration = {either}
input [ Range] Name,...;
output [ Range] Name,...;
inout [ Range] Name,...;
Register
Parameter
Event
Range = [ ConstantExpression: ConstantExpression]
规则:
· 若用于任务中的命名变量或参数没有在任务块中声明,则指的是在模块中声明的命名变量或参数。
· 任务中的 input、output 和 inout 的个数不受限制(也可以为零个)。
· 任务中的变量(包括输入和双向端口(inout))可以声明为寄存器型。如果没有明确地声明,则默认为寄存器型,且其位宽与相应的变量匹配。
· 当启动任务时,相应于任务的输入和双向端口(inout)的变量表达式的值被存入相应的变量寄存器中。当任务结束时,输入和双向端口(inout)的变量寄存器中的值又被代入启动任务的语句中相应的表达式。
注意!
· 和模块的端口定义不一样,任务的变量不能在任务名后的括号中定义。
· 任务中若包括一句以上的语句,必须要用begin –end 或fork-join 将其包含成块。
· 任务的输入、双向端口 (inout) 、输出和局部寄存器的值都是静态储存的,也就是说即使多次启动任务,也只有一份这些寄存器的拷贝。若第一次启动的任务还未完成,便第二次启动该任务,其输入、双向端口 (inout)、输出和局部寄存器的值便会被覆盖。
· 当被启动的任务运行结束时,输出和双向端口 (inout)的值被代入任务中相应的寄存器表达式。如果任务中的输出和双向端口 (inout)在赋值后有时间的控制,则相应的寄存器只能在定时控制延迟后才被更新。
· 同样,对输出和双向端口 (inout)寄存器变量的非阻塞赋值语句也不会起作用,因为当任务返回时,赋值语句可能还未生效。
可综合性问题:
包含定时控制语句的任务是不可综合的。启动的任务往往被综合成组合逻辑。
提示:
· 复杂 RTL 模块通常需要用多个 always 块来构造。建议最好不要采用一个always块运行多个任务的方案。
· 在测试块中可用任务来产生重复的激励序列。例如,对存储器的数据读写(见例)序列。
· 某任务如果被多个模块引用,可以把它定义为一个独立的模块(只包括该任务),并可用层次命名来引用它。
举例说明:
这个例子表示一个简单的可以综合的RTL任务
task Counter;
inout [3:0] Count;
input Reset;
if (Reset)
// 同步复位
Count = 0;
// 对 RTL 必须用非阻塞方式赋值
else
Count = Count + 1;
Endtask
下面这个例子说明如何在测试模块中运用任务。
module TestRAM;
parameter AddrWidth = 5;
parameter DataWidth = 8;
parameter MaxAddr = 1 << AddrBits;
reg [DataWidth-1:0] Addr;
reg [AddrWidth-1:0] Data;
wire [DataWidth-1:0] DataBus = Data;
reg Ce, Read, Write;
Ram32x8 Uut (.Ce(Ce), .Rd(Read), .Wr(Write),
.Data(DataBus), .Addr(Addr));
initial
begin : stimulus
integer NErrors;
integer i;
// 错误开始记数
NErrors = 0;
// 为每个地址写上地址值
for ( i=0; i<=MaxAddr; i=i+1 )
WriteRam(i, i);
// 读且比较
for ( i=0; i<=MaxAddr; i=i+1 )
begin
ReadRam(i, Data);
if ( Data !== i )
RamError(i,i,Data);
end
//小结错误个数
$display(Completed with %0d errors, NErrors);
end
task WriteRam;
input [AddrWidth-1:0] Address;
input [DataWidth-1:0] RamData;
begin
Ce = 0;
Addr = Address;
Data = RamData;
#10 Write = 1;
#10 Write = 0;
Ce = 1;
end
endtask
task ReadRam;
input [AddrWidth-1:0] Address;
output [DataWidth-1:0] RamData;
begin
Ce = 0;
Addr = Address;
Data = RamData;
Read = 1;
#10 RamData = DataBus;
Read = 0;
Ce = 1;
end
endtask
task RamError;
input [AddrWidth-1:0] Address;
input [DataWidth-1:0] Expected;
input [DataWidth-1:0] Actual;
if ( Expected !== Actual )
begin
$display("Error reading address %h", Address);
$display(" Actual %b, Expected %b", Actual,
Expected);
NErrors = NErrors + 1;
end
endtask
endmodule
请参阅:
Task Enable,Function 的说明。
------------------------------------------------------------------------------
Task Enable
任务的启动
在模块代码中只需用任务名便可启动任务。当任务启动时,输入值通过任务的端口变量(输入和 inout变量)传递到任务中。当任务结束时,返回值通过任务的端口寄存器变量(输出和 inout变量)传出。
语法
TaskName[( Expression,...)];
规则
· 任务可以从 initial 或 always 块或其它任务中启动。任务可以多次调用。但任务不能被函数调用。
· 调用任务的语句中,端口表达式的顺序和任务端口变量声明的顺序必须一致。端口的个数必须与任务声明的端口变量的个数一致。
· 若任务的端口变量是输入时,则对应的端口变量可以是任何一种表达式;若端口变量为输出和 inout 时,对应的端口变量必须位于进程赋值语句的左边而且必须是有效的。
· 当任务启动时,输入和 inout 表达式复制到相应的变量寄存器中。当任务结束时,输出和 inout 寄存器的值会复制到启动任务相应的端口寄存器中。
· 可以在任务内部或任务外部把任务禁止(disable)。
注意!
任务中变量寄存器默认为静态的,所以当一个任务正在执行时又启动该任务时,输入和 inout 寄存器的值会被覆盖。
可综合性问题:
若任务不包含定时控制,是有可能被综合的。调用的任务往往被综合成组合逻辑。
举例说明:
task Counter;
inout [3:0] Count;
input Reset;
...
endtask
always @(posedge Clock)
Counter(Count, Reset);
请参阅:
Disable、Task、Function Call 的说明。
-------------------------------------------------------------------------
Timing control
定时控制
用于延迟语句的执行或按排语句的执行顺序。定时控制可以放在语句的前面,或者在程序的进程赋值语句表达式中的赋值操作符(即 = 或 <= )之间。前一种延迟语句的执行,后一种延迟声明的语句生效。
语法:
{Timing controls before statements}
{either}
DelayControl
EventControl
WaitControl
{Intra-assignment Timing controls}
{either}
DelayControl
EventControl
repeat ( Expression) EventControl
DelayControl = {either}
# UnsignedNumber
# ParameterName
# ConstantMinTypMaxExpression
# ( MinTypMaxExpression)
EventControl = {either}
@Name {of Register, Net or Event}
@( EventExpression)
EventExpression = {either}
Expression
Name {of Register, Net or Event}
posedge Expression {01, 0X, 0Z, X1 or Z1}
negedge Expression {10, 1X, 1Z, Z0 or X0}
EventExpression or EventExpression
WaitControl = wait ( Expression)
在程序中位于何处:
请参阅:statement、procedural assignment (for intra-assignment timing control) 的说明。
规则:
· 在某声明语句前面插入的事件或延迟控制使原本立刻要执行的该条语句延迟执行。
· 当执行到wait时,如果其表达式为假(0或X),wait 控制只延迟 wait 语句后的下一条语句;当表达式为真(非0)时,下一条语句才执行。当执行到 wait 时,如果表达式为真,下一句不延迟马上执行。
· 执行进程赋值语句时,要检查赋值语句右边的表达式,如果没有内部赋值延迟,若用的是阻塞赋值,则左边的寄存器类型变量立即更新,若用的是非阻塞赋值,则在下一个仿真周期更新。如果有内部赋值延迟,左边的寄存器类型变量只有在发生内部赋值延迟后才更新。
· 内部赋值延迟必须是常数的,但语句前的延迟可以是常数或变量(即Net或reg 型变量)。
· or 列表中的任何一个信号(事件)变化(发生)时,即触发事件控制。
· 对于 posedge(上升沿)和 negedge(下降沿)事件触发控制,只测试表达式的最低位。要不然的话,表达式的任何变化都会触发事件。
注意!
对于阻塞赋值语句而言,指定内部赋值延迟为零(# 0)与不指定是不一样的,也与没有赋值延迟的非阻塞赋值语句不同。 对于阻塞赋值语句而言,指定#0意味着该语句在所有待定事件完成以后,而在非阻塞赋值完成以前进行。(不指定内部赋值延迟和指定内部赋值延迟为零(#0)的赋值语句是一样的。)
可综合性问题:
· 综合时延迟被忽略。
· 综合工具不支持 wait语句和内部赋值延迟以及repeat(重复)语句。
· 事件控制用于控制always块的执行,从而能确定综合出的逻辑是组合的还是时序的。一般情况下,always后紧跟着的就是事件控制,这有时也称为敏感列表。
提示:
在用RTL(寄存器传输级HDL语言)描述电路时,可用内部赋值延迟可来描述在给表示触发器的寄存器变量赋值时的时钟偏移现象。
举例说明:
#10
#(Period/2)
#(1.2:3.5:7.1)
@Trigger
@(a or b or c)
@(posedge clock or negedge reset)
wait (!Reset)
非阻塞赋值时使用延迟来克服时钟的偏移:
always @(posedge Clock)
Count <= #1 Count + 1;
在周期时钟的第五个下降沿复位:
initial
begin
Reset = repeat(5) @(negedge Clock) 1;
Reset = @(negedge clock) 0;
End
请参阅:
Procedural Assignment、Always、Repeat语句的说明
---------------------------------------------------------------------------
User Defined Primitive
用户自定义原语
用户自定义原语(UDPS)可以为小型元件建立模型,这也是模块的另一种表示方法。可以用引用由门构建的实例同样的方式来实例引用用户自定义原语(UDPS)。
语法:
primitive UDPName (OutputName, InputName,...);
UDPPortDeclarations ...
UDPBody
endprimitive
UDPPortDeclaration = {either}
output OutputName;
input InputName,...;
reg OutputName; {Sequential UDP}
UDPBody = {either} CombinationalBody SequentialBody
CombinationalBody =
table
CombinationalEntry...
endtable
SequentialBody =
[initial OutputName = InitialValue;]
table
SequentialEntry...
endtable
InitialValue =
{either} 0 1 1'b0 1'b1 1'bx {not case sensitive}
CombinationalEntry = LevelInputList : OutputSymbol ;
SequentialEntry =
SequentialInputList : CurrentOutput : NextOutput;
SequentialInputList = {either}
LevelInputList
EdgeInputList
LevelInputList = LevelSymbol...
EdgeInputList =
[ LevelSymbol...] EdgeIndicator [ LevelSymbol...]
CurrentOutput = LevelSymbol
NextOutput = {either} OutputSymbol
EdgeIndicator = {either}
( LevelSymbol LevelSymbol)
EdgeSymbol
OutputSymbol = {either} 0 1 x {not case sensitive}
LevelSymbol = {either} 0 1 x ? b {not case sensitive}
EdgeSymbol = {either} r f p n * {not case sensitive}
规则:
· UDP只允许有一个输出端,至少允许有一个输入端。具体实施时,对输入端的个数是有限制的,但必须至少允许10个输入端口。
· 如果某UDP的输出端定义为reg 型(寄存器类型)变量,则该UDP是时序逻辑的UDP,否则为组合逻辑的UDP。
· 如果已对时序逻辑的UDP的输出进行了初始化,则只有待到在仿真开始时,初始值才开始从引用的原语实例的输出传出。
· 描述时序逻辑的UDPS可以是电平敏感的或边沿敏感的。若在真值表中有边沿敏感的指示(至少一个),则该描述时序逻辑的UDP为边沿敏感的。
· UDP的行为在表中定义。表的行定义为不同输入条件下的输出。对于描述组合逻辑的UDP,每一行定义为一个或多个输入的组合逻辑的输出。对于描述时序逻辑的UDP,每一行都要考虑reg类型变量的当前输出值。一行最多只能有一个边沿变化入口。行定义了在指定的边沿发生变化时,由输入值和当前输出值所产生的输出值。
· UDP表中所用的特殊的电平和边沿符号含义如下:
?
0 1 或 x
b or B
0 或 1
-
输出不变
(vw)
由 v 变为 w
r or R
(01)
f of F
(10)
p or P
(01)(0x)或(x1)
n or N
(10)(1x)或(x0)
*
(??)
· 若组合逻辑的输入值和触发边沿没有明确指定将会导致输出的不确定。
· 不支持Z值。输入时Z看成是X;输出值不允许设为X。注意 ?符号的特殊含义,它和在数字中的 ?符号意思不一样,在数字的表示中符号?和 z 含义相同。
注意!
在描述时序逻辑的UDP中,若在表中任何地方出现边沿触发的条件,则输入信号所有可能的边沿都要认真考虑并列出,因为默认的只是一种边沿的触发的条件,这将导致输出的不确定性。
可综合性问题:
任何一种工具都不能综合UDP,它只被用来建立基本的门级逻辑器件的逻辑仿真模型。
提示:
· 和行为模块相比较,用UDP来做仿真非常有效。为ASIC单元库的元件建立模型时应该使用UDP。
· 输出端口在一个以上的元件,应该对每个输出建立独立的UDP。
· 在表的第一行加上注释,指明每一列的含义。
举例说明:
primitive Mux2to1 (f, a, b, sel); // 组合UDP
output f;
input a, b, sel;
table
// a b sel : f
0 ? 0 : 0;
1 ? 0 : 1;
? 0 1 : 0;
? 1 1 : 1;
0 0 ? : 0;
1 1 ? : 1;
endtable
endprimitive
primitive Latch (Q, D, Ena);
output Q;
input D, Ena;
reg Q; // Level sensitive UDP
table
// D Ena : old Q : Q
0 0 : ? : 0;
1 0 : ? : 1;
? 1 : ? : -; // 保持原先值
0 ? : 0 : 0;
1 ? : 1 : 1;
endtable
endprimitive
primitive DFF (Q, Clk, D);
output Q;
input Clk, D;
reg Q; // Edge sensitive UDP
initial
Q = 1;
table
/ / Clk D : old Q : Q
r 0 : ? : 0; // Clock '0'
r 1 : ? : 1; // Clock '1'
(0?) 0 : 0 : -; // Possible Clock
(0?) 1 : 1 : -; // " "
(?1) 0 : 0 : -; // " "
(?1) 1 : 1 : -; // " "
(?0) ? : ? : -; // Ignore falling clock
(1?) ? : ? : -; // " " "
? * : - : -; // Ignore changes on D
endtable
endprimitive
请参阅:
Module、Gate、Instantiation 的语法说明。
---------------------------------------------------------------------------
While
条件循环语句
只要控制表达式为真(即不为零),循环语句就重复进行。
语法
wile {Expression}
Statement
可综合性问题:
只有当循环块有事件控制(即@(posedge Clock))才可综合。
举例说明:
reg [15:0] Word
while (Word)
begin
if (Word[0])
CountOnes = CountOnes + 1;
Word = Word >>1;
End
请参阅:
For、Forever、Repeat 语句的说明。
----------------------------------------------------------------------------
Compiler Directives
编译器指示
编译器指示是在源代码中对Verilog 编译器所发出的指令。在编译指示需要用反引号(`)做前导。编译器指示从它在源代码出现的地方开始生效,并一直继续生效到随后运行的所有的文件,直到编译器指示结束的地方或一直运行的最后的文件。
下面有Verilog 编译指示的摘要。摘要后面详细介绍了一些比较重要的编译指示。
注意!
编译器指示的生效依赖于编译时源代码中所包含文件的执行顺序。
-------------------------------------------------------------------------
Standard Compiler Directives
标准
excel标准偏差excel标准偏差函数exl标准差函数国标检验抽样标准表免费下载红头文件格式标准下载
的编译器指示
在Verilog LRM中定义了以下编译器指示:
1) `celldefine 和 `endcelldefine
可用来作为分别加在模块的前面和后面的标记,以表示该模块是一个库单元(cell)。单元可被 PLI 子程序调用来做某种应用,比如延迟的计算。
例子:
`celldefine
module Nand2 {…};
. . .
endmodule
2) `endcelldefine
3) `default_nettype
改变Net类型的默认类型。如果没有该声明,默认的Net类型是wire 型。
4) 例子: `default_nettype tril
5) `define 和 `undef
`define 定义一个文本宏,`undef 取消已定义的文本宏定义。
在编译的第一阶段期间, 宏(macro)被它所定义的文本字符串取代。宏也可以用来控制条件编译(请参阅 `ifdef)。想要知道关于 `define 应用的更多细节见下面说明。
6) `ifdef, `else 和 `endif
7) 根据是否定义了特殊的宏,来指示编译器是否要编译这一段 Verilog 源代码。详细细节见下面。
8) `include
指示编译器读入包含文件的内容,并在`include所在的地方编译该文件。
9) 例子: `include “definitions.v”
10) `resetall
把现行的已启动的所有编译器指示复位到原默认值。该编译指示可以写在每个Verilog源文件的第一行,以防止前面别的源文件的编译指示在该源文件编译时产生不需要的结果。
例子: `resetall
11) `timescale
定义仿真的时间单位和精度。细节请见下面说明。
12) `unconnected_drive 和 `nounconnected_drive
`unconnected_drive 编译指示把模块没连接的输入端口设置为上拉 pull up(pull1,即逻辑1)或为下拉 pull down(pull0, 即逻辑0)。`nounconnected_drive 编译指示把模块没连接输入端口的设置恢复到默认值,即把没连接的输入端口值设置为高阻浮动(Z)。
例子: `unconnected_drive pull0 //或 pull1 ( 即逻辑值为1)
----------------------------------------------------------------------------
Non-Standard Compiler Directives 非标准编译器指示
下面的编译指示并不属于Verilog HDL 语言的IEEE标准。但在CADENCE公司的Verilog LRM中提及。并不是所有的Verilog工具都支持以下这些编译指示。
1) `default_decay_time
若未明确给定衰减时间,则由该编译指示将其设置为默认的三态寄存器(trireg) 类型的线路连接(Net)的衰减时间。
例子:
`default_decay_time 50
`default_decay_time infinite //表示无衰减时间
2) `default_trireg_strength
把三态寄存器(trireg) 类型的线路连接(Net)的默认强度设置为整数。用整数来表示强度并不符合IEEE规定的Verilog语言标准,但仍属于Verilog语言非标准扩展部分。
例子:
`default_trireg_strength 30
3) `delay_mode_distribute、`delay_mode_path、`delay_mode_unit 和`delay_mode_zero
这些编译指示都会影响延迟的仿真方式。分布式延迟是在原语实例中的延迟、赋值延迟和线路连接延迟。路径延迟是在Specify(指定)块中定义的延迟。若用单位和零延迟代替分布式延迟和路径延迟将加快仿真的过程,但会丢失真实的延迟信息。在默认情况下,仿真器会自动选择最长的延迟仿真方式,即分布式延迟和路径延迟仿真方式。
4) `define
`define 定义一个文本宏。宏在编译的第一阶段被由它定义的文本所代替。在用参数和函数表达不适合或不允许的情况下,用宏可以提高 Verilog 源代码的可读性和可维护性。
语法:
{declaration}
`define Name[(Argument,…)] Text
{usage}
`Name [(Expression,…)]
在程序中位于何处:
宏可以在模块内或模块外定义。
规则:
· 像所有的编译指示一样,宏定义在整个文件中生效,除非被后面的`define、`undef 和`resetall编译指示改写或清除。宏定义没有范围的限制。
· 若定义的宏内有参数,即在宏文本中用到参数,则当宏调用时,宏的参数被实际的参数表达式所代替。
`define add(a , b) a + b
f = `add(1, 2); //f= 1 + 2;
· 宏定义可以用反斜杠(\)跨越几行。新的一行是宏文本中的一部分。
· 宏文本不允许分下列语言记号:注释,数字,字符串,名称,保留名称,操作符。
· 不能把编译器指示名用作宏名。
注意!
· 所有的具体电路实现工具都不支持带参数的宏。
· 若定义了宏,则必须把撇号(`)写在宏名的紧前面才能调用该宏。没有撇号(`)打头的名,即使名称与宏名一致,则为独立的标识符与宏定义无关。
· 要区别撇号(`)和表示数制的前引号(‘)的不同。
· 不要用分号来结束宏定义,除非真要在用宏代替分号。否则会引起语法错误。
提示:
· 通常更喜欢用参数而不是用宏给无含义的字符起一个有含义的名字。
· 仿真时,用带参数的宏要比用同样功能的函数效率高。
举例说明:
本例子说明在分层设计中如何用文本宏来选择不同的模块实现。这在综合时很有用,特别是当必须用RTL源代码模块和已综合成门级电路的模块做混合仿真时。
`define SUBBLOCK1 subblock1_rt1
`define SUBBLOCK2 subblock2_rt1
`define SUBBLOCK3 subblock3_gates
module TopLevel …
`SUBBLOCK1 sub1_inst (…);
`SUBBLOCK2 sub2_inst(…);
`SUBBLOCK3 sub3_inst(…);
…
endmodule
下面的例子说明带参数的文本宏的定义和调用:
`define nand (delay) nand #(delay)
nand(3) (f,a,b);
nand(4) (g,f,c);
请参阅:
‘ifdef 的说明。
5) `ifdef
根据是否定义了特定的宏,来决定是否编译这部分Verilog源代码。
语法:
`ifdef MacroName
VerilogCode…
[ `else
VerilogCode…]
`endif
规则:
· 如果宏名已经用`define定义,只编译Verilog编码的第一块。
· 如果宏名没有定义和`else 指示出现,只编译第二块。
· 这些编译指示是可以嵌套的。
· 没被编译的代码仍然必须是有效的 Verilog 代码。
提示:
这些编译指示可以用来调试模块。例如,可以在同一个模块的两种形式之间切换(如布线前仿真模块和带布线延迟的门级仿真模块之间)或有选择地开启诊断信息的打印输出。
例子
`define primitiveModel
module Test
…
`ifdef primitiveModel
Mydesign_primitives UUT (…);
`else
Mydesign_RTL UUT(…);
`endif
endmodule
请参阅:
`define的说明。
6) `timescale
定义时间单位和仿真精度
语法:
`timescale TimeUnit / PrecisionUnit
TimeUnit = Time Unit
PrecisionUnit = Time Unit
Time = {either} 1 10 100
Unit = {either} s ms us ns ps fs
规则:
· 像所有的编译指示一样,`timescale影响在该指示后的所有模块,无论位于同一个文件的还是位于独立编译的多个文件中的模块,直到碰到下一个`timescale或`resetall指示将其改写或复位到默认为止。
· 精度单位必须小于或等于时间单位。
· 仿真器运行的精度就是在`timescale指示中所定义的最小精度单位。所有的延迟时间都以精度单位为准取整。
提示:
在每个模块文件的第一句应写上`timescale指示,即使在模块中没有延迟,也是如此,因为有的仿真器必需要有`timescale指示才能正常工作。
举例说明:
`timescale 10ns / 1ps
请参阅:
$timeformat 的说明。
62
74