系统任务和编译指令一、系统任务1. 输出类任务 display write strobe monitor2. 仿真控制类 stop finish3. 仿真时间 time stime realtime timeformat4. 命令行传参 plusargs5.文件读写 fopen fscanf readmemb readmemh二、编译指令1. 宏定义define2. 条件编译ifdef3. 文件包含include4. default_nettype5. resetall6. celldefineVerilog中的两个特殊概念系统任务和编译指令。一、系统任务Verilog 为某些常用操作提供了标准的系统任务也称系统函数 这些操作包括屏幕显示 、线网值动态监视 、 暂停和结束仿真等。所有的系统任务都具有$keyword的形式。系统任务主要用于仿真调试与控制而非综合生成硬件电路。1. 输出类任务 display write strobe monitor$display的使用方法和 C 语言中的 printf 函数非常类似可以直接打印字符串也可以在字符串中指定变量的格式对相关变量进行打印。$display(This is a test.);// 直接打印字符串$display(This is a test number: %b.,num);// 以二进制格式打印变量 num如果没有指定变量的显示格式变量值会根据在字符串的位置显示出来相当于参与了字符串连接。例如$display(This is a test number: ,num,!!!);常用的输出格式在输出浮点数的时候还可以指定精度moduletop;initial begin real num123.456789;$display(Default precision: %f,num);// 默认6位小数$display(2 decimal places: %.2f,num);// 2位小数$display(5 decimal places: %.5f,num);// 5位小数end endmodule此外还可以使用转义字符显示特殊字符$wirte使用方法与$display完全一样只是前者会在每次显示信息完毕后不会自动换行后者会自动换行。当输出后不需要换行时可以使用显示任务$write。$strobe的使用方法与$display一致但打印信息的时间和$display有所差异。当许多语句与$display任务在同一时间内执行时这些语句和 $display 的执行顺序是不确定的一般按照程序的顺序结构执行。$strobe则是在其他语句执行完毕之后才执行显示任务。示例1reg[3:0]a;initial begin a1;#1;aa1;//第一次显示$display($display excuting result: %d.,a);$strobe($strobe excuting result: %d.,a);#1;$display();//第二次显示$display($display excuting result: %d.,a);$strobe($strobe excuting result: %d.,a);end执行结果如下执行第一次显示任务时非阻塞赋值与$display同时执行$display显示赋值之前的变量值而$strobe显示赋值之后的变量值。示例2integer i;initial beginfor(i0;i4;ii1)begin $display(Run times of $display: %d.,i);$strobe(Run times of $strobe: %d.,i);end end显示结果如下$display按照程序结构执行显示操作 4 次。而此循环语句是在 0 时刻执行的所以$strobe显示的变量值是循环结束时变量的结果即 i4 退出循环后$strobe才会执行。$monitor为监测任务用于变量的持续监测。只要变量发生了变化$monitor就会打印显示出对应的信息。reg[3:0]cnt;initial begin cnt3;forever begin #5;if(cnt7)cntcnt1;end end initial begin $monitor(Counter change to value %d at the time %t.,cnt,$time);end显示的内容如下2. 仿真控制类 stop finish$finish是结束本次仿$stop是暂停当前的仿真。仿真暂停后通过 Verilog 仿真工具或命令行还可以使仿真继续进行而结束仿真后仿真无论如何也不能再进行示例initial begin forever begin #100;if($time10000)$finish(0);//if ($time 10000) $finish(1) ;//if ($time 10000) $finish(2) ;end end$finish(0)仿真退出时不打印任何信息。$finish(1)仿真退出时打印仿真时间单位是ps和$finish所在的行信息$finish(2)仿真退出时不仅打印仿真时间和行信息还打印一些其他信息。3. 仿真时间 time stime realtime timeformat仿真时间$realtime、$time和$stime的时间单位是timescale中指定的时间精度$realtime会按照当前的时间对仿真时间进行准确读取而$time和$stime会对当前时间进行四舍五入的读取。示例initial begin #10;$display($time output1: %t,$time);$display($stime output1: %t,$stime);$display($realtime output1: %t,$realtime);#3.2;$display($time output2: %t,$time);$display($stime output2: %t,$stime);$display($realtime output2: %t,$realtime);#5.6;$display($time output2: %t,$time);$display($stime output2: %t,$stime);$display($realtime output2: %t,$realtime);end结果如下时间显示格式$timeformat是一个系统任务用于配置仿真中时间值的打印格式影响$display、$strobe等系统任务中%t格式符的输出样式。定义$timeformat之后它会持续生效直到执行了另一个$timeformat。$timeformat的基本语法如下$timeformat(units_number,precision_number,suffix_string,minimum_field_width);units_number指定时间单位范围从 0 到 -15对应不同时间单位0 为 s-9 为 ns-12 为 ps。precision_number定义时间值小数点后的位数四舍五入保留。suffix_string在时间值后添加自定义后缀如单位符号。minimum_field_width设置输出字符串的最小宽度不足时左侧补空格示例timescale10ns/1psmoduletb_timeformat();initial $timeformat(-9,2, ns,10) #1.2345 $display(%t: simulation started.,$realtime);end endmodule///////////////////////////// 12.34 ns: simulation started.///////////////////////////4. 命令行传参 plusargs仿真时可通过命令行传参的方式进行参数的传递使用$test$plusargs( str )时只需在仿真命令行中加入str 即可。使用$value$plusargs( strvar )时需要在 str 内部指定传递参数时数值的类型。而在命令行传递参数时数值不需要添加任何有关进制的说明只保留相关进制的数值即可。命令行传递参数的格式需要参照$value$plusargs时 str 声明的格式。示例initial beginif($test$plusargs(DISPLAY_CTRL))begin $display(Display simulation information!!!);end end reg[1:0]display_sel;initial beginif($value$plusargs(INFO_SEL%b,display_sel))begin $display(Parameter transfer succeeds!!!);endelsebegin display_sel2b0;end end initial begin #1;if(display_sel2b01)$display(You have selected Runoob!!!);elseif(display_sel2b10)$display(You have selected Verilog!!!);elseif(display_sel2b11)$display(You have selected Me!!!);else$display(What do you really what???);end在仿真的命令行中添加DISPLAY_CTRL INFO_SEL01即可传递参数./simv \DISPLAY_CTRLINFO_SEL01\-l logs/run.log输出结果如下因为在定义INFO_SEL的时候已经指定其类型为二进制INFO_SEL%b所有船体给它的 01 会被仿真工具认为是二进制而不是其他类型。5.文件读写 fopen fscanf readmemb readmemh$fopenVerilog 中的$fopen用来打开一个文件其基本用法如下integer handle1;handle1$fopen(filename,type)使用fopen打开一个文件之后将其保存在变量 handle1 中可以将其称之为文件指针后续针对该文件的操作就可以直接使用 handle1 这个变量。type 指定文件的打开方式具体如下选项如下其中带有 b 的表示的是二进制文件其他表示的是普通文本文件。$fopen只负责打开文件如果要读取文件中的内容还需要使用其他系统函数如$fscanf、$readmemb等。$fscanf$fscanf是 SystemVerilog 中的一个文件输入函数用于从文件中读取数据并将其转换为特定格式其基本用法如下integer fstat;fstat$fscanf(fd, format, args)其中fd 是使用$fopen打开文件时指定的变量args 是一个 reg 类型的变量。该命令的意思是从 fd 中去读取数据将其转换为 format 指定的格式然后将转换结果保存在 args 中。$fscanf中常用的数据格式包括%b二进制、%o八进制、%d十进制、%h十六进制、%s字符串等$fscanf停止的条件是读取到了空格、制表符、换行符或换页符等具体含义如下实例所示。假设一个名称为 test.txt 的文件中的内容为0123456789adcde fffff55555bbbbb eeeee33333使用fscanf读取其中的内容integer fd1;//句柄integer code;//返回操作状态initial begin fd1$fopen(test.txt,r);//可读end integer i;reg[19:0]din;//存放$fscanf读取的数据位宽与单个数据位宽一致reg[19:0]file_data[0:7];//将文件中的所有数据进行存储initial beginfor(i0;i8;ii1)begin code$fscanf(fd1,%h,din);//将16进制数据写入din中file_data[i]din;//依次记录所有16进制数据endfor(i0;i8;ii1)begin $display(file_data[%2d]%h,i,file_data[i]);//输出数据进行查看end $fclose(fd1);end输入结果如下file_data[0]01234 file_data[1]56789file_data[2]adcde file_data[3]fffff file_data[4]55555file_data[5]bbbbb file_data[6]eeeee file_data[7]33333也就是说可以使用fscanf对同一个文件进行多次读取下一次读取开始的地方是上一次读取停止的地方每次读取停止的条件是读取到了空格、制表符、换行符或换页符等$readmemb $readmemh这两个系统函数的功能是将文件中的数据读取到 reg 数组中readmemb 按照二进制格式读取readmemh 按照16进制格式读取其他没有区别。使用这两个系统任务的时候被读取的数据文件的内容只能包含空白位置空格、换行、制表格、注释行、二进制或十六进制的数字。数字中不能包含位宽说明和格式说明并且必须用空白位置或注释行来分隔开。$readmemb的基本用法如下$readmemb(数据文件名,数组名)$readmemb(数据文件名,数组名,起始地址$readmemb(数据文件名,存贮器名,起始地址,结束地址)注意这里是直接使用文件名而不是文件指针。示例假设一个命令为 test.txt 的文件中的内容为00 01 02 03 04 05 06 07 08 09使用$readmemb来读取文件中的内容timescale1ns/1nsmoduletb_read_test();integer i;reg[7:0]mem_test[9:0];//mem_test是位宽8bit个数为10的数组initial $readmemh(test.txt,mem_test);//绝对路径//显示数组的10个值initial beginfor(i0;i10;ii1)$display(%d: %h,i,mem_test[i]);end endmodule输出结果如下0: 001: 012: 023: 034: 045: 056: 067: 078: 089: 09在不指定地址的情况下$readmemh将从数据文件中读到的第 1 个数据填入数组的第 0 个位置以此类推直到数组被填满。如果数据的个数大于数据文件中数据的个数则数组无法被填满未被填满的部分则依然未被赋值。如果指定开始地址就是将读到的第一个数据填充到指定的开始地址以此类推如果指定结束地址填充到结束地址之后不在填充数据。二、编译指令就像 C/C 程序需要编译成 .exe 或可执行文件才能运行一样VCS 编译就是将 HDL “源代码” 编译成计算机 CPU 可以直接高效执行的“仿真程序”simv在编译的过程中会对源代码进行分析、检查和优化。编译指令Compiler Directives 是一种特殊的预处理命令以特定符号Verilog中的是 开头。它们在代码编译前被处理用于指导编译器如何解析、转换或配置源代码从而影响编译结果和最终生成的仿真模型。编译指令并不是Verilog语言的一部分而是编译器的处理指令Verilog 中常用的一些编译指令如下1. 宏定义define在编译阶段define用于定义宏常量或者宏函数或者说是用于文本替换。一旦define指令被编译其在整个编译过程中都会有效相当于是定义了一个 “全局变量”。例如在一个文件中定义宏常量define DATA_DW32在另一个文件中可以直接使用 DATA_DWreg[DATA_WIDTH-1:0]data使用define定义函数或者说带参数的宏的示例如下defineMAX(a,b)((a)(b)?(a):(b))initial $display(Max: %d,MAX(5,9));如果使用define定义的内容很多可以使用换行符defineCOUNTER_MODULE(name,width)\modulename(\ input wire clk,\ input wire reset_n,\ input wire enable,\ output reg[width-1:0]count \);\ always (posedge clkornegedge reset_n)begin \if(!reset_n)\ count{width{1b0}};\elseif(enable)\ countcount1b1;\ end \ endmodule//COUNTER_MODULE(counter8,8)// 生成8位计数器COUNTER_MODULE(counter16,16)// 生成16位计数器undef用于取消之前定义的宏释放宏名称以便重新定义。使用define定义宏的时候是不能重复定义的需通过undef指令取消原有定义之后才能重新定义。undef DATA_DW2. 条件编译ifdef条件编译指令允许根据预定义条件选择性地包含或排除代码段实现一套代码支持多种配置的目标。条件编译相关的命令包括indef、ifndef、elsif、else、endif。ifdef FPGA_TARGET ifdef XILINX_FPGA// Xilinx FPGA特定代码parameter VENDORXILINX;(*KEEPTRUE*)wire keep_signal;elsif ALTERA_FPGA// Intel/Altera FPGA特定代码parameter VENDORINTEL;(*preserve*)wire keep_signal;else// 其他FPGAparameter VENDORGENERIC;endif elsif ASIC_TARGET// ASIC特定代码parameter VENDORASIC;// 功耗优化代码else// 默认仿真代码parameter VENDORSIMULATION;endif3. 文件包含includeinclude指令在编译时将指定文件的内容插入到当前位置该指令通常用于将全局或公用的头文件包含在设计文件里。文件路径既可以使用相对路径也可以使用绝对路径。include../../param.vincludecpu_defines.v为了避免重复定义include命令中指定的头文件一般包含保护机制// 每个.vh文件都应该有保护机制ifndef PROJECT_DEFINES_VH define PROJECT_DEFINES_VH// 使用严格的网络类型检查default_nettype none// 常量定义define WORD_SIZE32define CACHE_SIZE1024// 宏函数定义defineCLOG2(x)$clog2(x)endif// PROJECT_DEFINES_V4. default_nettype该指令用于为隐式的线网变量指定为线网类型即将没有被声明的连线定义为线网类型。// 设置默认为wire类型默认值default_nettype wire// 禁用隐式网络声明推荐default_nettype nonemodulestrict_module(input wire clk,input wire reset_n,output wire result);// 所有信号必须显式声明wire internal_signal;// 必须声明reg state_reg;// 必须声明assign internal_signalclkreset_n;assign resultinternal_signal;endmodule// 恢复默认行为default_nettype wire5. resetall该编译器指令将所有的编译指令重新设置为默认值。resetall可以使得缺省连线类型为线网类型。当resetall加到模块最后时可以将当前的timescale取消防止其进一步传递只保证当前的timescale在局部有效避免timescale的错误继承。6. celldefinecelldefine和endcelldefine用于标记标准单元库中的基本单元他们包含模块的定义。例如一些与、或、非门一些 PLL 单元PAD 模型以及一些 Analog IP 等。celldefinemodule(input clk,input rst,output clk_pll,output flag);…… endmodule endcelldefine
【Verilog】系统任务和编译指令
发布时间:2026/6/9 14:12:01
系统任务和编译指令一、系统任务1. 输出类任务 display write strobe monitor2. 仿真控制类 stop finish3. 仿真时间 time stime realtime timeformat4. 命令行传参 plusargs5.文件读写 fopen fscanf readmemb readmemh二、编译指令1. 宏定义define2. 条件编译ifdef3. 文件包含include4. default_nettype5. resetall6. celldefineVerilog中的两个特殊概念系统任务和编译指令。一、系统任务Verilog 为某些常用操作提供了标准的系统任务也称系统函数 这些操作包括屏幕显示 、线网值动态监视 、 暂停和结束仿真等。所有的系统任务都具有$keyword的形式。系统任务主要用于仿真调试与控制而非综合生成硬件电路。1. 输出类任务 display write strobe monitor$display的使用方法和 C 语言中的 printf 函数非常类似可以直接打印字符串也可以在字符串中指定变量的格式对相关变量进行打印。$display(This is a test.);// 直接打印字符串$display(This is a test number: %b.,num);// 以二进制格式打印变量 num如果没有指定变量的显示格式变量值会根据在字符串的位置显示出来相当于参与了字符串连接。例如$display(This is a test number: ,num,!!!);常用的输出格式在输出浮点数的时候还可以指定精度moduletop;initial begin real num123.456789;$display(Default precision: %f,num);// 默认6位小数$display(2 decimal places: %.2f,num);// 2位小数$display(5 decimal places: %.5f,num);// 5位小数end endmodule此外还可以使用转义字符显示特殊字符$wirte使用方法与$display完全一样只是前者会在每次显示信息完毕后不会自动换行后者会自动换行。当输出后不需要换行时可以使用显示任务$write。$strobe的使用方法与$display一致但打印信息的时间和$display有所差异。当许多语句与$display任务在同一时间内执行时这些语句和 $display 的执行顺序是不确定的一般按照程序的顺序结构执行。$strobe则是在其他语句执行完毕之后才执行显示任务。示例1reg[3:0]a;initial begin a1;#1;aa1;//第一次显示$display($display excuting result: %d.,a);$strobe($strobe excuting result: %d.,a);#1;$display();//第二次显示$display($display excuting result: %d.,a);$strobe($strobe excuting result: %d.,a);end执行结果如下执行第一次显示任务时非阻塞赋值与$display同时执行$display显示赋值之前的变量值而$strobe显示赋值之后的变量值。示例2integer i;initial beginfor(i0;i4;ii1)begin $display(Run times of $display: %d.,i);$strobe(Run times of $strobe: %d.,i);end end显示结果如下$display按照程序结构执行显示操作 4 次。而此循环语句是在 0 时刻执行的所以$strobe显示的变量值是循环结束时变量的结果即 i4 退出循环后$strobe才会执行。$monitor为监测任务用于变量的持续监测。只要变量发生了变化$monitor就会打印显示出对应的信息。reg[3:0]cnt;initial begin cnt3;forever begin #5;if(cnt7)cntcnt1;end end initial begin $monitor(Counter change to value %d at the time %t.,cnt,$time);end显示的内容如下2. 仿真控制类 stop finish$finish是结束本次仿$stop是暂停当前的仿真。仿真暂停后通过 Verilog 仿真工具或命令行还可以使仿真继续进行而结束仿真后仿真无论如何也不能再进行示例initial begin forever begin #100;if($time10000)$finish(0);//if ($time 10000) $finish(1) ;//if ($time 10000) $finish(2) ;end end$finish(0)仿真退出时不打印任何信息。$finish(1)仿真退出时打印仿真时间单位是ps和$finish所在的行信息$finish(2)仿真退出时不仅打印仿真时间和行信息还打印一些其他信息。3. 仿真时间 time stime realtime timeformat仿真时间$realtime、$time和$stime的时间单位是timescale中指定的时间精度$realtime会按照当前的时间对仿真时间进行准确读取而$time和$stime会对当前时间进行四舍五入的读取。示例initial begin #10;$display($time output1: %t,$time);$display($stime output1: %t,$stime);$display($realtime output1: %t,$realtime);#3.2;$display($time output2: %t,$time);$display($stime output2: %t,$stime);$display($realtime output2: %t,$realtime);#5.6;$display($time output2: %t,$time);$display($stime output2: %t,$stime);$display($realtime output2: %t,$realtime);end结果如下时间显示格式$timeformat是一个系统任务用于配置仿真中时间值的打印格式影响$display、$strobe等系统任务中%t格式符的输出样式。定义$timeformat之后它会持续生效直到执行了另一个$timeformat。$timeformat的基本语法如下$timeformat(units_number,precision_number,suffix_string,minimum_field_width);units_number指定时间单位范围从 0 到 -15对应不同时间单位0 为 s-9 为 ns-12 为 ps。precision_number定义时间值小数点后的位数四舍五入保留。suffix_string在时间值后添加自定义后缀如单位符号。minimum_field_width设置输出字符串的最小宽度不足时左侧补空格示例timescale10ns/1psmoduletb_timeformat();initial $timeformat(-9,2, ns,10) #1.2345 $display(%t: simulation started.,$realtime);end endmodule///////////////////////////// 12.34 ns: simulation started.///////////////////////////4. 命令行传参 plusargs仿真时可通过命令行传参的方式进行参数的传递使用$test$plusargs( str )时只需在仿真命令行中加入str 即可。使用$value$plusargs( strvar )时需要在 str 内部指定传递参数时数值的类型。而在命令行传递参数时数值不需要添加任何有关进制的说明只保留相关进制的数值即可。命令行传递参数的格式需要参照$value$plusargs时 str 声明的格式。示例initial beginif($test$plusargs(DISPLAY_CTRL))begin $display(Display simulation information!!!);end end reg[1:0]display_sel;initial beginif($value$plusargs(INFO_SEL%b,display_sel))begin $display(Parameter transfer succeeds!!!);endelsebegin display_sel2b0;end end initial begin #1;if(display_sel2b01)$display(You have selected Runoob!!!);elseif(display_sel2b10)$display(You have selected Verilog!!!);elseif(display_sel2b11)$display(You have selected Me!!!);else$display(What do you really what???);end在仿真的命令行中添加DISPLAY_CTRL INFO_SEL01即可传递参数./simv \DISPLAY_CTRLINFO_SEL01\-l logs/run.log输出结果如下因为在定义INFO_SEL的时候已经指定其类型为二进制INFO_SEL%b所有船体给它的 01 会被仿真工具认为是二进制而不是其他类型。5.文件读写 fopen fscanf readmemb readmemh$fopenVerilog 中的$fopen用来打开一个文件其基本用法如下integer handle1;handle1$fopen(filename,type)使用fopen打开一个文件之后将其保存在变量 handle1 中可以将其称之为文件指针后续针对该文件的操作就可以直接使用 handle1 这个变量。type 指定文件的打开方式具体如下选项如下其中带有 b 的表示的是二进制文件其他表示的是普通文本文件。$fopen只负责打开文件如果要读取文件中的内容还需要使用其他系统函数如$fscanf、$readmemb等。$fscanf$fscanf是 SystemVerilog 中的一个文件输入函数用于从文件中读取数据并将其转换为特定格式其基本用法如下integer fstat;fstat$fscanf(fd, format, args)其中fd 是使用$fopen打开文件时指定的变量args 是一个 reg 类型的变量。该命令的意思是从 fd 中去读取数据将其转换为 format 指定的格式然后将转换结果保存在 args 中。$fscanf中常用的数据格式包括%b二进制、%o八进制、%d十进制、%h十六进制、%s字符串等$fscanf停止的条件是读取到了空格、制表符、换行符或换页符等具体含义如下实例所示。假设一个名称为 test.txt 的文件中的内容为0123456789adcde fffff55555bbbbb eeeee33333使用fscanf读取其中的内容integer fd1;//句柄integer code;//返回操作状态initial begin fd1$fopen(test.txt,r);//可读end integer i;reg[19:0]din;//存放$fscanf读取的数据位宽与单个数据位宽一致reg[19:0]file_data[0:7];//将文件中的所有数据进行存储initial beginfor(i0;i8;ii1)begin code$fscanf(fd1,%h,din);//将16进制数据写入din中file_data[i]din;//依次记录所有16进制数据endfor(i0;i8;ii1)begin $display(file_data[%2d]%h,i,file_data[i]);//输出数据进行查看end $fclose(fd1);end输入结果如下file_data[0]01234 file_data[1]56789file_data[2]adcde file_data[3]fffff file_data[4]55555file_data[5]bbbbb file_data[6]eeeee file_data[7]33333也就是说可以使用fscanf对同一个文件进行多次读取下一次读取开始的地方是上一次读取停止的地方每次读取停止的条件是读取到了空格、制表符、换行符或换页符等$readmemb $readmemh这两个系统函数的功能是将文件中的数据读取到 reg 数组中readmemb 按照二进制格式读取readmemh 按照16进制格式读取其他没有区别。使用这两个系统任务的时候被读取的数据文件的内容只能包含空白位置空格、换行、制表格、注释行、二进制或十六进制的数字。数字中不能包含位宽说明和格式说明并且必须用空白位置或注释行来分隔开。$readmemb的基本用法如下$readmemb(数据文件名,数组名)$readmemb(数据文件名,数组名,起始地址$readmemb(数据文件名,存贮器名,起始地址,结束地址)注意这里是直接使用文件名而不是文件指针。示例假设一个命令为 test.txt 的文件中的内容为00 01 02 03 04 05 06 07 08 09使用$readmemb来读取文件中的内容timescale1ns/1nsmoduletb_read_test();integer i;reg[7:0]mem_test[9:0];//mem_test是位宽8bit个数为10的数组initial $readmemh(test.txt,mem_test);//绝对路径//显示数组的10个值initial beginfor(i0;i10;ii1)$display(%d: %h,i,mem_test[i]);end endmodule输出结果如下0: 001: 012: 023: 034: 045: 056: 067: 078: 089: 09在不指定地址的情况下$readmemh将从数据文件中读到的第 1 个数据填入数组的第 0 个位置以此类推直到数组被填满。如果数据的个数大于数据文件中数据的个数则数组无法被填满未被填满的部分则依然未被赋值。如果指定开始地址就是将读到的第一个数据填充到指定的开始地址以此类推如果指定结束地址填充到结束地址之后不在填充数据。二、编译指令就像 C/C 程序需要编译成 .exe 或可执行文件才能运行一样VCS 编译就是将 HDL “源代码” 编译成计算机 CPU 可以直接高效执行的“仿真程序”simv在编译的过程中会对源代码进行分析、检查和优化。编译指令Compiler Directives 是一种特殊的预处理命令以特定符号Verilog中的是 开头。它们在代码编译前被处理用于指导编译器如何解析、转换或配置源代码从而影响编译结果和最终生成的仿真模型。编译指令并不是Verilog语言的一部分而是编译器的处理指令Verilog 中常用的一些编译指令如下1. 宏定义define在编译阶段define用于定义宏常量或者宏函数或者说是用于文本替换。一旦define指令被编译其在整个编译过程中都会有效相当于是定义了一个 “全局变量”。例如在一个文件中定义宏常量define DATA_DW32在另一个文件中可以直接使用 DATA_DWreg[DATA_WIDTH-1:0]data使用define定义函数或者说带参数的宏的示例如下defineMAX(a,b)((a)(b)?(a):(b))initial $display(Max: %d,MAX(5,9));如果使用define定义的内容很多可以使用换行符defineCOUNTER_MODULE(name,width)\modulename(\ input wire clk,\ input wire reset_n,\ input wire enable,\ output reg[width-1:0]count \);\ always (posedge clkornegedge reset_n)begin \if(!reset_n)\ count{width{1b0}};\elseif(enable)\ countcount1b1;\ end \ endmodule//COUNTER_MODULE(counter8,8)// 生成8位计数器COUNTER_MODULE(counter16,16)// 生成16位计数器undef用于取消之前定义的宏释放宏名称以便重新定义。使用define定义宏的时候是不能重复定义的需通过undef指令取消原有定义之后才能重新定义。undef DATA_DW2. 条件编译ifdef条件编译指令允许根据预定义条件选择性地包含或排除代码段实现一套代码支持多种配置的目标。条件编译相关的命令包括indef、ifndef、elsif、else、endif。ifdef FPGA_TARGET ifdef XILINX_FPGA// Xilinx FPGA特定代码parameter VENDORXILINX;(*KEEPTRUE*)wire keep_signal;elsif ALTERA_FPGA// Intel/Altera FPGA特定代码parameter VENDORINTEL;(*preserve*)wire keep_signal;else// 其他FPGAparameter VENDORGENERIC;endif elsif ASIC_TARGET// ASIC特定代码parameter VENDORASIC;// 功耗优化代码else// 默认仿真代码parameter VENDORSIMULATION;endif3. 文件包含includeinclude指令在编译时将指定文件的内容插入到当前位置该指令通常用于将全局或公用的头文件包含在设计文件里。文件路径既可以使用相对路径也可以使用绝对路径。include../../param.vincludecpu_defines.v为了避免重复定义include命令中指定的头文件一般包含保护机制// 每个.vh文件都应该有保护机制ifndef PROJECT_DEFINES_VH define PROJECT_DEFINES_VH// 使用严格的网络类型检查default_nettype none// 常量定义define WORD_SIZE32define CACHE_SIZE1024// 宏函数定义defineCLOG2(x)$clog2(x)endif// PROJECT_DEFINES_V4. default_nettype该指令用于为隐式的线网变量指定为线网类型即将没有被声明的连线定义为线网类型。// 设置默认为wire类型默认值default_nettype wire// 禁用隐式网络声明推荐default_nettype nonemodulestrict_module(input wire clk,input wire reset_n,output wire result);// 所有信号必须显式声明wire internal_signal;// 必须声明reg state_reg;// 必须声明assign internal_signalclkreset_n;assign resultinternal_signal;endmodule// 恢复默认行为default_nettype wire5. resetall该编译器指令将所有的编译指令重新设置为默认值。resetall可以使得缺省连线类型为线网类型。当resetall加到模块最后时可以将当前的timescale取消防止其进一步传递只保证当前的timescale在局部有效避免timescale的错误继承。6. celldefinecelldefine和endcelldefine用于标记标准单元库中的基本单元他们包含模块的定义。例如一些与、或、非门一些 PLL 单元PAD 模型以及一些 Analog IP 等。celldefinemodule(input clk,input rst,output clk_pll,output flag);…… endmodule endcelldefine