本文分两个部分
一,Verilog基础简单复习
二,模块化设计
Verilog基础简单复习
加法器。
module adder (count,sum,a,b,cin ); //加法器模块端口声明
input [2:0]a,b; //端口说明
input cin;
output count;
output [2:0]sum;
assign {count,sum} = a + b + cin; //加法器算法实现
endmodule
这个例子通过连续赋值语句描述了一个名为adder的三位加法器可以根据两个三比特数a、b和进位(cin)计算出和(sum)和进位(count)。从例子中可以看出整个Verilog HDL程序是嵌套在module和endmodule声明语句里的
模块的端口声明了模块的输入输出端口,其格式如下:
Module 模块名(端口1,端口2,端口3,端口4, …);
I/O说明的格式如下:
输入口:input 端口名1,端口名2,…,端口名i; //(共有i个输入口)
输出口:output 端口名1,端口名2,…,端口名j; //(共有j个输出口)
I/O说明也可以写在端口声明语句里,其格式如下:
module module_name(input port1,input port2,…,output port1,output port2… )
Verilog HDL中总共有19种数据类型,数据类型是用来表示数字电路硬件中的数据储存和传送元素的。我们先只介绍4个最基本的数据类型,它们分别是:reg型,wire型,integer型和parameter型。其他数据类型在后面的章节里逐步介绍。其他的类型如下:
large型、medium型、scalared型、time型、small型、tri型、trio型、tri1型、triand型、trior型、trireg型、vectored型、wand型和wor型。
在数字电路中,x代表不定值,z代表高阻值。一个x可以用来定义十六/八/二进制数的四/三/一位二进制
数的状态。z的表示方式同x类似。z还有一种表达方式是可以写作?。在使用case表达式时建议使用这种写法,以提高程序的可读性,如下例所示:
4'b10x0 //位宽为4的二进制数从低位数起第二位为不定值
4'b101z //位宽为4的二进制数从低位数起第一位为高阻值
12'dz //位宽为12的十进制数其值为高阻值(第一种表达方式)
12'd? //位宽为12的十进制数其值为高阻值(第二种表达方式)
8'h4x //位宽为8的十六进制数其低四位值为不定值
(注意宽度的填充和截断原则)
当常量不声明位数时,默认值是32位,每个字母用8位的ASCII值表示,例如:
10=32'd10=32'b1010 //十进制和二进制
1=32'd1=32'b1 //十进制和二进制
-1=-32'd1=32'hFFFFFFFF //十进制和十六进制
'BX=32'BX=32'BXXXXXXX…X //默认声明为32位
"AB"=16'B01000001_01000010 //每个字母用8位表示
在Verilog HDL中用parameter来定义常量,即用parameter来定义一个标识符代表一个常量,称为符号常量,即标识符形式的常量。采用标识符代表一个常量可提高程序的可读性和可维护性。parameter型数据是一种常数型的数据,其说明格式如下:
Parameter 参数名1=表达式,参数名2=表达式, …, 参数名n=表达式;
parameter是参数型数据的确认符,确认符后跟着一个用逗号分隔开的赋值语句表。在每一个赋值语句的右边必须是一个常数表达式。也就是说,该表达式只能包含数字或先前已定义过的参数,例如:
parameter msb=7; //定义参数msb为常量7
parameter e=25, f=29; //定义两个常数参数
参数型常数经常用于定义延迟时间和变量宽度。在模块或实例引用时可通过参数传递改变在被引用模块或实例中已定义的参数。
Wire reg与存储器
wire [7:0]b; //定义了一个八位的wire型变量
reg [4:1]regc, regd; //定义了两个四位的名为regc和regd的reg型变量
reg [7:0]mema[255:0]; //定义一个名为mema的256×8的存储器
在Verilog HDL语言中,信号有两种赋值方式:非阻塞(Non_Blocking)赋值方式和阻塞(Blocking)赋值
方式。
(1)非阻塞赋值方式。
典型语句:b <= a;
① 块结束后才完成赋值操作。
②b的值并不是立刻就改变的。
③ 这是一种比较常用的赋值方法,特别在编写可综合模块时。
(2)阻塞赋值方式。
典型语句:b = a;
① 赋值语句执行完后,块才结束。
②b的值在赋值语句执行完后立刻就改变。
③ 可能会产生意想不到的结果。
非阻塞赋值方式和阻塞赋值方式的区别常给设计人员带来问题。问题主要是给“always”块内的reg 型信号的赋值方式不易把握。
二,模块化
采用模块化的设计使系统看起来更有条理也便于仿真和测试
设计思想就是模块套模块 自顶向下依次展开
//
//三态驱动器设计----------------------------------------
//
module trist1(out,in,enable); //三态启动器模块端口声明
output out; //端口说明
input in, enable;
mytri tri_inst(out,in,enable);//实例化由mytri模块定义的实例元件tri_inst
endmodule
module mytri(out,in,enable); //三态启动器模块端口声明
output out; //端口说明
input in, enable;
assign out = enable? in : 'bz; //三态启动器算法描述
endmodule
/*
这个例子通过另一种方法描述了一个三态门。在这个例子中存在着两个模块。模块trist1 调用由模块mytri
定义的实例元件tri_inst。模块trist1是顶层模块。模块mytri则被称为子模块。
通过上面的例子可以看到。
·Verilog HDL程序是由模块构成的。每个模块的内容都是嵌在module和endmodule两个语句之间。
每个模块实现特定的功能。模块是可以进行层次嵌套的。正因为如此,才可以将大型的数字电路设
计分割成不同的小模块来实现特定的功能,最后通过顶层模块调用子模块来实现整体功能。
· 每个模块要进行端口定义,并说明输入输出口,然后对模块的功能进行行为逻辑描述。
·Verilog HDL程序的书写格式自由,一行可以写几个语句,一个语句也可以分写多行。
· 除了endmodule语句外,每个语句和数据定义的最后必须有分号。
*/
通过上面的实例可看出,一个设计是由一个个模块(module)构成的。一个模块的设计如
下:
1、模块内容是嵌在module 和endmodule两个语句之间。每个模块实现特定的功能,模块可进
行层次的嵌套,因此可以将大型的数字电路设计分割成大小不一的小模块来实现特定的功能,最
后通过由顶层模块调用子模块来实现整体功能,这就是Top-Down的设计思想,如3.3.1的例[3]。
2、模块包括接口描述部分和逻辑功能描述部分。这可以把模块与器件相类比。
模块的端口定义部分:
如上例: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;
mytri u_mytri(din,d_en,d_out);
功能描述用来产生各种逻辑还可用来实例化一个器件,该器件可以是厂家的器件库也可以是我们自己用HDL设计的模块(相当于在原理图输入时调用一个库元件)。在逻辑功能描述中,主要用到assign 和always 两个语句。
用户377235 2016-4-3 21:38
用户377235 2015-2-10 09:28
用户446135 2013-8-30 00:49
用户440970 2013-8-29 15:21