Verilog的基础知识

Verilog的基本介绍:

硬件描述语言发展至今已有二十多年历史,当今业界的标准中( IEEE标准)主要有VHDL和Verilog HDL 这两种硬件描述语言。
一个设计往往从系统级设计开始,把系统划分成几个大的基本的功能模块,每个功能模块再按一定的规则分成下一个层次的基本单元,如此一直划分下去。自顶向下的设计方法可用下面的树状结构表示:

主要功能


1, 基本逻辑门,例如and 、 or 和nand 等都内置在语言中。
2, 开关级基本结构模型,例如pmos 和nmos 等也被内置在语言中。
3, 可采用三种不同方式或混合方式对设计建模。这些方式包括:行为描述方式—使用过程化结构建模;数据流方式—使用连续赋值语句方式建模;结构化方式—使用门和模块实例语句描述建模。
4, Verilog HDL 中有两类数据类型:线网数据类型和寄存器数据类型。线网类型表示构件间的物理连线,而寄存器类型表示抽象的数据存储元件。
5, 能够描述层次设计,可使用模块实例结构描述任何层次。
6, 设计的规模可以是任意的;语言不对设计的规模(大小)施加任何限制。
7, Verilog HDL 不再是某些公司的专有语言而是I E E E 标准。
8, 人和机器都可阅读Verilog 语言,因此它可作为E D A 的工具和设计者之间的交互语言。
9, 设计能够在多个层次上加以描述,从开关级、门级、寄存器传送级(RT L )到算法级。
10, 能够使用内置开关级原语在开关级对设计完整建模。
11, 同一语言可用于生成模拟激励和指定测试的验证约束条件,例如输入值的指定。
12, Verilog HDL 能够监控模拟验证的执行,即模拟验证执行过程中设计的值能够被监控和显示。这些值也能够用于与期望值比较,在不匹配的情况下,打印报告消息。
13, 在行为级描述中, Verilog HDL 不仅能够在RT L 级上进行设计描述,而且能够在体系结构级描述及其算法级行为上进行设计描述。
14, 能够使用门和模块实例化语句在结构级进行结构描述。
15, 对高级编程语言结构,例如条件语句、情况语句和循环语句,语言中都可以使用。
下图显示了Verilog HDL 的混合方式建模能力,即在一个设计中每个模块均可以在不同设计层次上建模。

模块


模块(module)是Verilog 的基本描述单位,用于描述某个设计的功能或结构及与其他模块通信的外部端口。
模块在概念上可等同一个器件就如我们调用通用器件(与门、三态门等)或通用宏单元(计数器、 ALU、 CPU)等,因此,一个模块可在另一个模块中调用。
一个电路设计可由多个模块组合而成,因此一个模块的设计只是一个系统设计中的某个层次设计,模块设计可采用多种建模方式。
 

例[1] 加法器
module addr (a, b, cin, count, sum);
input [2:0] a;
input [2:0] b;
input cin;
output count;
output [2:0] sum;
assign {count,sum} = a +b + cin;
endmodule
例[2] 比较器
module compare (equal, a, b);
input [1:0] a,b; // declare the input signal ;
output equare ; // declare the output signal;
assign equare = (a == b) ? 1:0 ;
/ * if a = b , output 1, otherwise 0; */
endmodule

/* .... */ 和 // ... 表示注释部分


模块的端口定义部分:


如上例: module addr (a, b, cin, count, sum); 其中module 是模块的保留字, addr 是模块的名字,相当于器件名。()内是该模块的端口声明,定义了该模块的管脚名,是该模块与其他模块通讯的外部接口,相当于器件的pin 。
模块的内容,包括I/O说明,内部信号、调用模块等的声明语句和功能定义语句。
I/O说明语句如: input [2:0] a; input [2:0] b; input cin; output count; 其 中 的 input 、output、 inout 是保留字,定义了管脚信号的流向, [n:0]表示该信号的位宽(总线或单根信号线)。

逻辑功能描述部分如: assign  d_out = d_en ? din :'bz;  

对每个模块都要进行端口定义,并说明输入、输出口,然后对模块的功能进行逻辑描述,当然,对测试模块,可以没有输入输出口。
除endmodule 语句外,每个语句后面需有分号表示该语句结束。
 

时延


信号在电路中传输会有传播延时等,如线延时、器件延时。时延就是对延时特性的HDL描述。举例如下:
assign # 2 B = A;
表示 B信号在2个时间单位后得到A信号的值。如下图:
 

在Verilog HDL中,所有时延都必须根据时间单位进行定义,定义方式为在文件头添加如下语句:
`timescale 1ns /100ps
其中’ timescale 是Verilog HDL 提供的预编译处理命令, 1ns 表示时间单位是1ns , 100ps表示时间精度是100ps。根据该命令,编译工具才可以认知 #2 为2ns。

三种建模方式
 

主要有结构化描述方式、数据流描述方式和行为描述方式
结构化描述方式
结构化的建模方式就是通过对电路结构的描述来建模,即通过对器件的调用( HDL概念称为例化),并使用线网来连接各器件的描述方式。这里的器件包括Verilog HDL的内置门如与门and,异或门xor等,也可以是用户的一个设计。结构化的描述方式反映了一个设计的层次结构。
一位全加器
 

module FA_struct (A, B, Cin, Sum, Count);
input A;
input B;
input Cin;
output Sum;
output Count;
wire S1, T1, T2, T3;
// -- statements -- //
xor x1 (S1, A, B);
xor x2 (Sum, S1, Cin);
and A1 (T3, A, B );
and A2 (T2, B, Cin);
and A3 (T1, A, Cin);
or O1 (Cout, T1, T2, T3 );
endmodule

该实例显示了一个全加器由两个异或门、三个与门、一个或门构成。 S1、 T1、 T2、 T3则是门与门之间的连线。代码显示了用纯结构的建模方式,其中xor 、 and、 or 是Verilog HDL 内置的门器件。以 xor x1 (S1, A, B) 该例化语句为例:xor 表明调用一个内置的异或门,器件名称xor ,代码实例化名x1(类似原理图输入方式)。括号内的S1, A, B 表明该器件管脚的实际连接线(信号)的名称 ,其中 A、 B是输入, S1是输出

数据流描述方式
数据流的建模方式就是通过对数据流在设计中的具体行为的描述的来建模。最基本的机制就是用连续赋值语句。在连续赋值语句中,某个值被赋给某个线网变量(信号),语法如下:
assign [delay] net_name = expression;
如: assign #2 A = B;
在数据流描述方式中,还必须借助于HDL提供的一些运算符,如按位逻辑运算符 :逻辑与(&),逻辑或(|)等。
一位全加器:

`timescale 1ns/100ps
module FA_flow(A,B,Cin,Sum,Count)
input A,B,Cin;
output Sum, Count;
wire S1,T1,T2,T3;
assign # 2 S1 = A ^ B;
assign # 2 Sum = S1 ^ Cin;
assign #2 T3 = A & B;
assign #2 T1 = A & Cin;
assign #2 T2 = B & Cin ;
endmodule

注意在各assign 语句之间,是并行执行的,即各语句的执行与语句之间的顺序无关。如上,当A有个变化时, S1、 T3、 T1 将同时变化, S1的变化又会造成Sum的变化。

行为描述方式
行为方式的建模是指采用对信号行为级的描述(不是结构级的描述)的方法来建模。在表示方面,类似数据流的建模方式,但一般是把用initial 块语句或always 块语句描述的归为行为建模方式。行为建模方式通常需要借助一些行为级的运算符如加法运算符(+),减法运算符(-)等。
以下举个例子,对initial 和always 语句的具体应用可看相关章节的介绍,这里,先对行为建模方式有个概念。
 

例[1] 一位全加器的行为建模
module FA_behav1(A, B, Cin, Sum, Cout );
input A,B,Cin;
output Sum,Cout;
reg Sum, Cout;
reg T1,T2,T3;
always@ ( A or B or Cin )
begin
Sum = (A ^ B) ^ Cin ;
T1 = A & Cin;
T2 = B & Cin ;
T3 = A & B;
Cout = (T1| T2) | T3;
end
endmodule

需要先建立以下概念:
1、只有寄存器类型的信号才可以在always和initial 语句中进行赋值,类型定义通过reg语句实现。
2、 always 语句是一直重复执行,由敏感表(always 语句括号内的变量)中的变量触发。
3、 always 语句从0 时刻开始。
4、在begin 和end 之间的语句是顺序执行,属于串行语句。

混合设计描述
在实际的设计中,往往是多种设计模型的混合。一般地,对顶层设计,采用结构描述方式,对低层模块,可采用数据流、行为级或两者的结合。两bit 全加器,对顶层模块采用结构描述方式对低层进行例化,对低层模块(FA)可采用结构描述、数据流描述或行为级描述

标识符

标识符( identifier)用于定义模块名、端口名、信号名等。 Verilog HDL 中的标识符( identifier)可以是任意一组字母、数字、 $符号和_(下划线)符号的组合,但标识符的第一个字符必须是字母或者下划线。另外,标识符是区分大小写的。以下是标识符的几个例子:
Count
COUNT //与Count 不同。
R56_68
FIVE$
关键词
Verilog HDL 定义了一系列保留字,叫做关键词,附录A 列出了语言中的所有保留字。注意只有小写的关键词才是保留字。例如,标识符always (这是个关键词)与标识符ALWAYS(非关键词)是不同的。
 

数字值集合


值集合
Verilog HDL中规定了四种基本的值类型:
0:逻辑0或“假”;
1:逻辑1或“真”;
X:未知值;
Z:高阻。
注意这四种值的解释都内置于语言中。如一个为z 的值总是意味着高阻抗,一个为0 的值通常是指逻辑0 。在门的输入或一个表达式中的为“z ”的值通常解释成“x ”。
此外, x 值和z 值都是不分大小写的,也就是说,值0x1z 与值0X1Z 相同。
Verilog HDL 中的常量是由以上这四类基本值组成的。
 

常量
Verilog HDL 中有三种常量:
整型、实型、字符串型。
下划线符号(_)可以随意用在整数或实数中,它们就数量本身没有意义。它们能用来提高易
读性;唯一的限制是下划线符号不能用作为首字符。
下面主要介绍整型和字符串型。


整型
整型数可以按如下两种方式书写:
1) 简单的十进制数格式
2) 基数格式
A. 简单的十进制格式
这种形式的整数定义为带有一个可选的“+”(一元)或“-”(一元)操作符的数字序列。
下面是这种简易十进制形式整数的例子。
32 十进制数32
-15 十进制数-15
B. 基数表示法
这种形式的整数格式为:
[size ] 'base value
size 定义以位计的常量的位长; base 为o 或O(表示八进制), b 或B(表示二进制), d 或D(表示十进制), h 或H (表示十六进制)之一; value 是基于base 的值的数字序列。值x 和z 以及十六进制中的a 到f 不区分大小写。
下面是一些具体实例:
5 'O37 5 位八进制数(二进制 11111 )
4 'D2 4 位十进制数 (二进制0011)
4 'B1x_01 4 位二进制数
7 'Hx 7位x(扩展的x), 即xxxxxxx
4 'hZ 4 位z(扩展的z) , 即zzzz
4 'd-4 非法:数值不能为负
8 'h 2A 在位长和字符之间,以及基数和数值之间允许出现空格
3' b 001 非法: ` 和基数b 之间不允许出现空格
(2+3)'b10 非法:位长不能够为表达式
注意, x (或z )在十六进制值中代表4 位x(或z ),在八进制中代表3 位x(或z ),在二进
制中代表1 位x (或z )。
基数格式计数形式的数通常为无符号数。这种形式的整型数的长度定义是可选的。如果没有
定义一个整数型的长度,数的长度为相应值中定义的位数。下面是两个例子:

'o 721
'h AF
9 位八进制数
8 位十六进制数

如果定义的长度比为常量指定的长度长,通常在左边填0 补位。但是如果数最左边一位为x 或z ,就相应地用x 或z 在左边补位。例如:
10'b10 左边添0 占位, 0000000010
10'bx0x1 左边添x 占位, x x x x x x x 0 x 1
如果长度定义得更小,那么最左边的位相应地被截断。例如:
3 ' b1001 _ 0011 与3'b011 相等
5'H0FFF 与5'H1F 相等

字符串型
字符串是双引号内的字符序列。字符串不能分成多行书写。例如:
"INTERNAL ERROR"
" REACHED->HERE "
用8 位ASCII 值表示的字符可看作是无符号整数。因此字符串是8 位ASCII 值的序列。为存储
字符串“INTERNAL ERROR ”,变量需要8 * 1 4 位。
r e g [1: 8*14] Message;
Message = "INTERNAL ERROR"

数据类型


Verilog HDL 主要包括两种数据类型
线网类型(net type) 和 寄存器类型(reg type)。
线网类型
1. wire 和 tri 定义
线网类型主要有wire 和tri 两种。线网类型用于对结构化器件之间的物理连线的建模。如器件的管脚,内部器件如与门的输出等。以上面的加法器为例,输入信号A, B是由外部器件所驱动,异或门X1的输出S1是与异或门X2输入脚相连的物理连接线,它由异或门X1所驱动。
由于线网类型代表的是物理连接线,因此它不存贮逻辑值。必须由器件所驱动。通常由assign进行赋值。如 assign A = B ^ C;
当一个wire 类型的信号没有被驱动时,缺省值为Z(高阻)。
信号没有定义数据类型时,缺省为 wire 类型。
如上面一位全加器的端口信号 A, B, SUM等,没有定义类型,故缺省为wire 线网类型。
2. 两者区别
tri 主要用于定义三态的线网。

寄存器类型
1. 定义
reg 是最常用的寄存器类型,寄存器类型通常用于对存储单元的描述,如D型触发器、 ROM等。存储器类型的信号当在某种触发机制下分配了一个值,在分配下一个值之时保留原值。但必须注意的是, reg 类型的变量,不一定是存储单元,如在always 语句中进行描述的必须用reg 类型的变量。
reg 类型定义语法如下:
reg [msb: lsb] reg1, reg2, . . . r e g N;
msb 和lsb 定义了范围,并且均为常数值表达式。范围定义是可选的;如果没有定义范围,缺省值为1 位寄存器。例如:

reg [3:0] Sat;
reg Cnt;
// S a t 为4 位寄存器。
//1 位寄存器。

reg [1:32] Kisp, Pisp, Lisp ;
寄存器类型的值可取负数,但若该变量用于表达式的运算中,则按无符号类型处理,如:
reg A ;
A = -1;
则A的二进制为 1111,在运算中, A总按 无符号数15 来看待。
2. 寄存器类型的存储单元建模举例
用寄存器类型来构建两位的D触发器如下:
reg [1: 0] Dout ;
always@(posedge Clk)
Dout <= Din;
用寄存器数组类型来建立存储器的模型,如对2个8位的RAM建模如下:
reg [7: 0] Mem[0: 1] ;
对存储单元的赋值必须一个个第赋值,如上2个8位的RAM的赋值必须用两条赋值语句:
Mem[0] = ’ h 55;
Mem[1] = ’ haa;
对数组类型,请按降序方式,如[7: 0] ;

运算符和表达式

算术运算符
在常用的算术运算符主要是 :
加法(二元运算符):“+”;
减法 (二元运算符): “-”;
乘法(二元运算符):“*”;
在算术运算符的使用中,注意如下两个问题:
1. 算术操作结果的位数长度
算术表达式结果的长度由最长的操作数决定。在赋值语句下,算术操作结果的长度由操作符
左端目标长度决定。考虑如下实例:
reg [3:0] Arc, Bar, Crt;
reg [5:0] Frx;
. . .
Arc = Bar + Crt;
Frx = Bar + Crt;
第一个加的结果长度由Bar , Crt 和A rc 长度决定,长度为4 位。
第二个加法操作的长度同样由Frx 的长度决定( Frx 、 Bat 和Crt 中的最长长度),长度为6位。
在第一个赋值中,加法操作的溢出部分被丢弃;而在第二个赋值中,任何溢出的位存储在结果位Frx [ 4 ]中。
在较大的表达式中,中间结果的长度如何确定?在Verilog HDL 中定义了如下规则:表达式中的所有中间结果应取最大操作数的长度(赋值时,此规则也包括左端目标)。考虑另一个实例:
wire [4:1] Box, Drt;
wire [5:1] Cfg;
wire [6:1] Peg;
wire [8:1] Adt;
. . .
assign Adt = (Box + Cfg) + (Drt + Peg) ;
表达式右端的操作数最长为6 ,但是将左端包含在内时,最大长度为8 。所以所有的加操作使用8 位进行。例如: Box 和Cfg 相加的结果长度为8 位。
2. 有符号数和无符号数
在设计中,请先按无符号数进行。
关系运算符
关系运算符有:
?>(大于)
?<(小于)
?>=(不小于)
?<=(不大于)
= = (逻辑相等)
! = (逻辑不等)
关系操作符的结果为真( 1 )或假( 0 )。如果操作数中有一位为X 或Z ,那么结果为X 。
例:
23 > 45
结果为假( 0 ),而:
52 < 8'hxFF
结果为x 。
如果操作数长度不同,长度较短的操作数在最重要的位方向(左方)添0 补齐。例如:
'b1000 > = 'b01110
等价于:
'b01000 > = 'b01110
结果为假( 0 )。
在逻辑相等与不等的比较中,只要一个操作数含有x 或z,比较结果为未知 ( x),如:
假定:
Data = 'b11x0;
Addr = 'b11x0;
那么:
Data = = Addr 比较结果不定,也就是说值为x 。
逻辑运算符
逻辑运算符有:
&& (逻辑与)
|| (逻辑或)
! (逻辑非)
用法为:(表达式1) 逻辑运算符 (表达式2) ....
这些运算符在逻辑值0(假) 或1(真) 上操作。逻辑运算的结果为0 或1 。例如, 假定:
Crd = 'b0; //0 为假
Dgs = 'b1; //1 为真
那么:
Crd && Dgs 结果为0 (假)
Crd || Dgs 结果为1 (真)
! D g s 结果为0 (假)
按位逻辑运算符
按位运算符有:
?~(一元非):(相当于非门运算)
?&(二元与):(相当于与门运算)
?|(二元或): (相当于或门运算)
?^(二元异或):(相当于异或门运算)
?~ ^, ^ ~(二元异或非即同或) :(相当于同或门运算)
wire [7:0] Dbus;
assign Dbus [7:4] = {Dbus [0], Dbus [1], Dbus[2], Dbus[ 3 ] } ;
/ /以反转的顺序将低端4 位赋给高端4 位。
assign Dbus = {Dbus [3:0], Dbus [ 7 : 4 ] } ;
 

条件语句:

if(Sum < 60)
begin
Grade = C;
Total_C = Total _c + 1;
end
else if(Sum < 75)
begin
Grade = B;
Total_B = Total_B + 1;
end
else
begin
Grade = A;
Total_A = Total_A + 1;
end

case语句:

case (HEX)
4'b0001 : LED = 7'b1111001; // 1
4'b0010 : LED = 7'b0100100; // 2
4'b0011 : LED = 7'b0110000; // 3
4'b0100 : LED = 7'b0011001; // 4
4'b0101 : LED = 7'b0010010; // 5
4'b0110 : LED = 7'b0000010; // 6
4'b0111 : LED = 7'b1111000; // 7
4'b1000 : LED = 7'b0000000; // 8
4'b1001 : LED = 7'b0010000; // 9
4'b1010 : LED = 7'b0001000; // A
4'b1011 : LED = 7'b0000011; // B
4'b1100 : LED = 7'b1000110; // C
4'b1101 : LED = 7'b0100001; // D
4'b1110 : LED = 7'b0000110; // E
4'b1111 : LED = 7'b0001110; // F
default :LED = 7'b1000000; // 0
endcase

case 的缺省项必须写,防止产生锁存器
 

实例化语句
在我另一篇博客有例子

https://blog.csdn.net/qq_38531460/article/details/106862846

悬空端口的处理
在我们的实例化中,可能有些管脚没用到,可在映射中采用空白处理,如:
DFF d1 (
.Q(QS),
.Qbar ( ),
.Data (D ) ,
.Preset ( ), // 该管脚悬空
.Clock (CK)
); //名称对应方式。
对输入管脚悬空的,则该管脚输入为高阻 Z,输出管脚被悬空的,该输出管脚废弃不用。


不同端口长度的处理
当端口和局部端口表达式的长度不同时,端口通过无符号数的右对齐或截断方式进行匹配。
例如:
module Child (Pba, Ppy) ;
input [5:0] Pba;
output [2:0] Ppy;
. . .
endmodule
module Top;
wire [1:2] Bdl;
wire [2:6] M p r;
Child C1 (Bdl, Mpr) ;
endmodule
在对Child 模块的实例中, Bdl[2]连接到Pba[ 0 ], Bdl[1] 连接到Pba[ 1 ],余下的输入端口Pba[5]、 Pba[4]和Pba[3]悬空,因此为高阻态z 。与之相似, Mpr[6]连接到Ppy[0], Mpr[5]连接到Ppy[1], Mpr[4] 连接到Ppy[2 ]。参见下图:
 

过程赋值语句


Verilog HDL 中提供两种过程赋值语句 initial 和 always 语句,用这两种语句来实现行为的建
模。这两种语句之间的执行是并行的,即语句的执行与位置顺序无关。这两种语句通常与语句块
(begin ....end)相结合,则语句块中的执行是按顺序执行的。
1. initial 语句
initial 语句只执行一次,即在设计被开始模拟执行时开始( 0时刻)。通常只用在对设计进行
仿真的测试文件中,用于对一些信号进行初始化和产生特定的信号波形。
 

initial
begin
#2 Stream = 1;
#5 Stream = 0;
#3 Stream = 1;
#4 Stream = 0;
#2 Stream = 1;
#5 Stream = 0;
end

always 语句
always 语句与initial 语句相反,是被重复执行,执行机制是通过对一个称为敏感变量表的事件驱动来实现的,下面会具体讲到。 always 语句可实现组合逻辑或时序逻辑的建模。
例[1]:
initial
Clk = 0 ;
always
#5 Clk = ~Clk;
因为always 语句是重复执行的,因此, Clk 是初始值为0 的,周期为10 的方波。
 

例[2] D 触发器
always @ ( posedge Clk or posedge Rst )
begin
if Rst
Q <= ‘ b 0;
else
Q <= D;

上面括号内的内容称为敏感变量,即整个always 语句当敏感变量有变化时被执行,否则不执行。因此,当Rst 为1 时, Q被复位,在时钟上升沿时, D被采样到Q。 有@ 的用来描述一个时序器件。
注意以下几点:
1、对组合逻辑的always 语句,敏感变量必须写全,敏感变量是指等式右边出现的所有标识符
如上的a, b和条件表达式中出现的所以标识符 如上例3的sel。
2、对组合逻辑器件的赋值采用阻塞赋值 “=”
3、时序逻辑器件的赋值语句采用非阻塞赋值 “<=”,如上的 Q 〈= D;
 

"文件包含"处理`include

所谓"文件包含"处理是一个源文件可以将另外一个源文件的全部内容包含进来,即将另外的文件包含到本文件之中。Verilog HDL语言提供了`include命令用来实现"文件包含"的操作。其一般形式为:

`include "文件名"

在编译的时候,需要对`include命令进行"文件包含"预处理:将File2.v的全部内容复制插入到`include "File2.v"命令出现的地方,即将File2.v被包含到File1.v中,得到图C的结果。在接着往下进行编译中,将"包含"以后的File1.v作为一个源文件单位进行编译。

1)文件aaa.v

module aaa(a,b,out);
input a, b;
output out;
wire out;
assign out = a^b;
endmodule
(2)文件 bbb.v

`include "aaa.v"
module bbb(c,d,e,out);
input c,d,e;
output out;
wire out_a;
wire out;
aaa aaa(.a(c),.b(d),.out(out_a));
assign out=e&out_a;
endmodule

在上面的例子中,文件bbb.v用到了文件aaa.v中的模块aaa的实例器件,通过"文件包含"处理来调用。模块aaa实际上是作为模块bbb的子模块来被调用的。在经过编译预处理后,文件bbb.v实际相当于下面的程序文件bbb.v:

module aaa(a,b,out);
input a, b;
output out;
wire out;
assign out = a ^ b;
endmodule

module bbb( c, d, e, out);
input c, d, e;
output out;
wire out_a;
wire out;
aaa aaa(.a(c),.b(d),.out(out_a));
assign out= e & out_a;
endmodule

关于"文件包含"处理的四点说明:

1) 一个`include命令只能指定一个被包含的文件,如果要包含n个文件,要用n个`include命令。注意下面的写法是非法的`include"aaa.v""bbb.v"

2) `include命令可以出现在Verilog HDL源程序的任何地方,被包含文件名可以是相对路径名,也可以是绝对路径名。例如:'include"parts/count.v"

3) 可以将多个`include命令写在一行,在`include命令行,只可以出空格和注释行。例如下面的写法是合法的。

'include "fileB" 'include "fileC" //including fileB and fileC

4) 如果文件1包含文件2,而文件2要用到文件3的内容,则可以在文件1用两个`include命令分别包含文件2和文件3,而且文件3应出现在文件2之前。

这个`includel里面定义的主要是一些参数,然后给另外的文件进行一个调用

define宏定义+`inlude "file.v"文件包含来实现参数模块化设计

1.新建参数模块文件(我命名为para.v);

  2.在para.v文件中使用'define宏定义参数(部分、有错误): 

     //`define+name+参数  
    `define   STATE_INIT     3'd0
    `define   STATE_IDLE    3'd1
    `define   STATE_WRIT   3'd2
    `define   STATE_READ  3'd3
    `define   STATE_WORK      3'd4
    `define   STATE_RETU  3'd5

  3.在需要调用参数的文件init.v中使用`include "para.v":

    `include "para.v"

  4.在init.v文件需要参数的地方使用`name 调用(部分):

    state_init <= `STATE_INIT;

若直接在module中通过localparam或者parameter进行参数定义的话,会带来两个问题:

    1.代码长度增加,不够美观;

    2.不利于参数和代码修改;

`define 与localparam和parameter最大的区别就是`define 可以跨文件传递参数;parameter只能在模块间传递参数;localparam只能在其所在的module中起作用,不能参与参数传递。

猜你喜欢

转载自blog.csdn.net/qq_38531460/article/details/106872142