第二章 Verilog程序设计语句和描述方式
文章目录
1,数据流建模
1.1 连续赋值语句
- 连续赋值的目标类型主要是标量线网和向量线网两种。
(1)标量线网,如:wire a,b;
(2)向量线网,如:wire [3:0]a,b; - 显式连续赋值语句:
<net_declaratlon><range><name>;
assign #<delay><name> = Assignment expression;
module exa1(a,b,m,n,c,y,w);
input[3:0]a,b,m,n;
output[3:0]c,y;
wire[3:0]a,b,m,n,c,y;
assign y = m|n;
assign #(3,2,4) c = a&b;
endmodule
- 隐式连续赋值语句:
<net_declaration><drive_strength><range>#<delay><name> = Assignment expression;
module exa2(a,b,m,n,c,y,w);
input[3:0]a,b,m,n;
output[3:0]c,y,w;
wire[3:0]a,b,m,n;
wire[3:0] y = m|n;
wire[3:0]#(3,2,4) c=a&b;
wire(strong0,weakl) [3:0] #(2,l,3) w = (a^b)&(m^n);
endmodule
<net_declaratlon>:连线型变量类型
<range>: 变量位宽,指明了变量数据类型的宽度,格式为[msb:lab],缺省为1。
<drive_strength>:赋值驱动强度,可选的,只能在“隐式连续赋值语句”格式中得到指定。它用来对连线型变量受到的驱动强度进行指定。例如:wire (weak0, strong1) out = in1&in2;
<delay>:延时量,可选。#(delay1, delay2, delay3,…)。
- 连续赋值语句需要注意的以下几点:
1,赋值目标只能是线网类型(wire)。
2,在连续赋值中,只要赋值语句右边表达式任何一个变量有变化,表达式立即被计算,计算的结果立即赋给左边信号(若没有定义延时量)。
3,连续赋值语句不能出现在过程块中。
4,多个连续赋值语句之间是并行语句,因此与位置顺序无关。
5,连续赋值语句中的延时具有硬件电路中惯性延时的特性,任何小于其延时的信号变化脉冲都将被滤除掉,不会体现在输出端口上。
2,行为级建模
类别 | 语句 | 可综合性 |
过程语句 | initial | |
always | √ | |
语句块 | 串行语句块begin-end | |
并行语句块fork-join | ||
赋值语句 | 连续赋值assign | √ |
过程赋值=,<= | √ | |
条件语句 | if-else | √ |
case,casez,casex | √ | |
循环语句 | forever | |
repeat | ||
while | ||
for | √ | |
循环语句 | `define | √ |
`include | √ | |
`ifdef,`else,`endif | √ |
2.1 过程语句
- 1.initia过程语句
- 2.always语句块
从语法描述角度,相对于initial过程块,always语句块的触发状态是一直存在的,只要满足always后面的敏感事件列表,就执行过程块。
其语法格式是:
always@(<敏感事件列表>) 语句块;
例如:
@(a) //当信号a的值发生改变时
@(a or b) //当信号a或信号b的值发生改变时
@(posedge clock) //当clock的上升沿到来时
@(negedge clock) //当clock的下降沿到来时
@(posedge clk or negedge reset) //当clk的上升沿到来或reset信号的下降沿到来时
-
- 过程语句使用中需要注意的问题
在信号定义形式方面,无论是对时序逻辑还是组合逻辑描述,Verilog HDL 要求在过程语句(initial和always)中,被赋值信号必须定义为”reg “类型。
在敏感事件表方面,这是Verilog HDL语言中一个关键陸设计,如何选取敏感事件作为过程的触发条件,在Verilog HDL程序中有一定的设计要求。
(1)采用过程对组合电路进行描述时,作为全部的输入信号需要列入敏感信号列表。
(2)采用过程对时序电路进行描述时,需要把时间信号和部分输入信号列入敏感信号列表。应当注意的是,不同的敏感事件列表会产生不同的电路形式。
- 过程语句使用中需要注意的问题
例如:用initial语句产生测试信号
例如:用always语句描述4选1数据线选择器。
例如:用always语句描述同步置数,同步清零计数器
例如:用always过程语句描述异步清零计数器
2.2 语句块
- 语句块包括串行语句块(begin-end)和并行语句块(fork-join )两种。
-
串行语句块采用关键字’ begin”和”end”,其中的语句按串行方式顺序执行,可以用于可综合电路程序和仿真测试程序。其语法格式是:
- 串行语句块有以下特点:
(1)串行语句块中的每条语句依据块中的排列次序,先后逐条顺序执行。块中每条语句给出的延时都是相对于前一条语句执行结束的相对时间。
(2)串行语句块的起始执行时间就是串行语句块中第一条语句开始执行的时间。串行语句块的结束时间就是块中最后一条语句执行结束的时间。
- 串行语句块有以下特点:
-
并行语句块采用关键字”fork”和”join ”,其中的语句按并行方式并发执行,只能用于仿真测试程序,不能用于可综合电路程序。其语法格式是:
- 并行语句块有以下几个特点:
(1)块内语句是同时执行的,即程序流程控制一进入到该并行语句块,块内语句则开始同时并行地执行。
(2)块内每条语句的延迟时间是相对于程序流程控制进入到块内的仿真时间的。
- 并行语句块有以下几个特点:
-
如下例:
波形仿真图如下:
两种方式的对比:
2.3 过程赋值语句
- 过程赋值语句有阻塞性过程赋值语句和非阻塞性过程赋值语句两种形式。
- 阻塞赋值语句的操作符号为”=”,语法格式是:
变量 = 表达式;
例如:b= a;- 阻塞赋值语句有如下特点:
(1)在串行语句块中,各条阻塞赋值语句将按照先后排列顺序依次执行;在并行语句块中的各条阻塞赋值语句则同时执行,没有先后顺序;
(2)执行阻塞赋值语句的顺序是,先计算等号右端表达式的值,然后立刻将计算的值赋给左边的变量,与仿真时间无关。
- 阻塞赋值语句有如下特点:
- 非阻塞赋值语句的操作符号为”<=”语法格式是:
变量 <= 表达式;
例如:b<= a,- 非阻塞赋值语句有如下特点:
(1)在串行语句块中,各条非阻塞赋值语句的执行没有先后顺序之分,排在前面的语句不会影响到后面的语句的执行,各条语句并行执行;
(2)执行非阻塞赋值语句的顺序是,先计算右端表达式的值,然后等到延时时间结束时,将计算的值赋给左边的变量。
- 非阻塞赋值语句有如下特点:
- 阻塞赋值语句的操作符号为”=”,语法格式是:
下例详解:
在上面两个程序中,基本描述相同,不同的是程序(1)采用了阻塞赋值语句,而程序(2)采用了非阻塞赋值语句。
如果采用阻塞赋值语句描述程序(2)的电路,其Verilog程序代码是:
又如下例:
2.4 过程性连续赋值语句
- 在Verilog中,过程性连续赋值语句有两种类型:赋值、重新赋值语句(assign、deassign)和强制、释放语句(force、release)。
- 赋值语句和重新赋值语句采用的关键字是”assign”和”deasslgn”语法格式分别是:
assign <寄存器型变量> = <赋值表达式>;
deassign <寄存器型变量>;
下例:
- 强制语句和释放语句采用的关键字”force”和”release”,可以对连线型和寄存器变量类型进行赋值操作,”force”语句的优先级高于”assign”语句。语法格式分别是:
force <寄存器或连线型变量> = <赋值表达式>;
release<寄存器或连线型变量>;
2.5 条件分支语句
- Verilog HDL的条件分支语句有两种:if条件语句和case条件分支语句。
-
1.if条件语句
if条件语句就是判断所给的条件是否满足,然后根据判断的结果来确定下一步的操作。
例如:
- 在if语句中允许一个或多个if语句的嵌套使用,语法格式是:
- 在if语句中允许一个或多个if语句的嵌套使用,语法格式是:
-
- case条件分支语句
相对于if语句只有两个分支而言,case语句是一种可实现多路分支选择控制的语句,比if-else条件语句显得更为方便和直观。一般,case语句多用于多条件译码电路设计,case语句的语法格式是:
下面是case,casez,casex的真值表,用来判断表达式与值是否满足:
- case条件分支语句
-
case | 0 | 1 | x | z |
0 | 1 | 0 | 0 | 0 |
1 | 0 | 1 | 0 | 0 |
x | 0 | 0 | 1 | 0 |
z | 0 | 0 | 0 | 1 |
casez | 0 | 1 | x | z |
0 | 1 | 0 | 0 | 1 |
1 | 0 | 1 | 0 | 1 |
x | 0 | 0 | 1 | 1 |
z | 1 | 1 | 1 | 1 |
casex | 0 | 1 | x | z |
0 | 1 | 0 | 1 | 1 |
1 | 0 | 1 | 1 | 1 |
x | 1 | 1 | 1 | 1 |
z | 1 | 1 | 1 | 1 |
- 使用case语句需注意:
(1)值1到值n之间必须各不相同,一旦判断到与某值相同并执行相应语句块后,case语句的执行便结束。
(2) 如果某几个连续排列的值项执行的是同一条语句,则这几个值项间可用逗号相隔,而将语句放在这几个值项的最后一个中。
(3) default选项相当于if-else语句中的else部分,可依据需要用或者不用,当前面己经列出了敏感表达式的所有可能值,则default可以省略。
(4) case语句的所有表达式的值的位宽必须相等,只有这样控制表达式和分支表达式才能进行对应位的比较。
例如:用case语句描述BCD数码译码管
在使用case语句时,应包含所有状态,如果没包含,那么缺省项必须写,否则将产生锁存器,这在同步时序电路设计中是不允许的。
2.6 循环语句
- 1.forever循环语句
关键字”forever”所引导的循环语句表示永久循环。在永久循环中不包含任何条件表达式,只执行无限的循环,直到遇到系统任务$finish
为止。如果需要从forever循环中退出,则可以使用disable语句。forever语句的语法格式是:
forever 语句或语句块;
例:用forever语句产生时钟信号
- 2.repeat循环语句
repeat循环语句关键字”repeat”所引导的循环语句表示执行固定次数的循环。其语法格式是:
repeat (循环次数表达式) 语句或语句块;
例,使用repeat循环产生固定周期数时钟信号
- 3.while循环语句
关键字”while”所引导的循环语句表示的是一种“条件循环” ,while语句根据条件表达式的真假来确定循环体的执行,当指定的条件表达式取值为真时才会重复执行循环体,否则就不执行循环体,其语法格式是:
while(条件表达式) 语句或语句块;
例,使用while语句产生时钟信号
- 4.for循环语句
循环语句关键字”for”所要引导的循环语句也表示一种“条件循环”,只有在指定的条件表达式成立时才进行循环,其语法格式是:for(循环变量赋初值; 循环结束条件; 循环变量增值) 语句块;
3.结构化建模
结构描述方式就是将硬件电路描述成一个分级子模块系统,通过逐层调用这些模块构成功能复杂的数字逻辑电路和系统的一种描述方式。在这种描述方式下,组成硬件电路的各个子模块之间的相互层次关系以及相互连接关系都需要得到说明。
根据所调用子模块的不同抽象级别,可以将模块的结构描述方式分成如下三类:
〈1)模块级建模:通过调用由用户设计生成的低级子模块来对硬件电路结构进行说明,这种情况下模块由低级模块的实例组成。
(2)门级建模:通过调用Verilog内部的基本门级元件来对硬件电路的结构进行说明,这种情况下模块将由基本门级元件的实例组成。
(3)开关级建模:通过调用Verilog内部的基本开关元件来对硬件电路的结构进行说明,这种情况下模块将由基本开关级元件的实例组成。
3.1 模块级建模
模块级建模就是通过调用由用户自己描述产生的module模块对硬件电路结构进行说明,并设计出电路。
模块建模方式可以把一个模块看作是由其它模块像积木一样搭建而成。模块中被调用模块属于低一层次的模块,如果当前模块不再被其它模块所调用,那么这个模块一定是所谓的顶层模块。在对一个硬件系统的描述中,必定有而且只能有一个顶层模块。
- 1,模块调用方式
在Verilog HDL中,模块可以被任何其它模块调用,这种调用实际上是将模块所描述的电路复制并连接的。其中模块调用的基本语法格式是:模块名 <参数值列表>实例名(端口名列表);
例如:
如果同一个模块在当前模块中被调用几次,则需要用不同的实例名加以标识,但可在同一条模块调用语句中被定义,只要各自的实例名和端口名列表相互间用逗号隔开即可。基本语法格式是:
<参数值列表>实例名1 (端口名列表1),
<参数值列表>实例名2 (端口名列表2),
... ...
<参数值列表>实例名n (端口名列表n);
在上面的格式当中,模块名就是被调用的模块,参数值列表是可选项,实例名代表生成的模块实例,实例名必须各不相同,端口列表指明了模块实例与外部信号的连接。
比如在上面的例中,如果想调用更多的2输入与门实现更复杂的功能,就可以用使用如下的多条模块实例语句:
当需要对同一个模块进行多次调用时,还可以采用阵列调用的方式对模块进行调用,其中阵列调用的语法格式如下:
<被调用模块名><实例阵列名>[阵列左边界:阵列右边界](<端口连接表>);
其中“阵列左边界”和“阵列右边界“是两个常量表达式,它们用来指定调用后生成的模块实例阵列的大小。
例如,使用阵列调用方式的模块实例语句来进行结构描述:
- 2.模块端口对应方式
(1)端口位置对应方式
端口位置对应方式是被调用的模块按照一定的顺序出现在端口连接表中的一种模块调用方式。其语法格式是:
模块名<参数值列表>实例名(<信号名1>,<信号名2>,...,<信号名n>);
例如,采用模块结构建模方式用1bit半加器构成1bit全加器
//半加器模块
module halfadder(a,b,s,c);
input a,b;
output c,s;
assign s = a^b;
assign c = a&b;
endmodule
//全加器模块
module fulladder(p,q,ci,co,sum);
input q,p,ci;
output co,sum;
wire w1,w2,w3;
halfadder U1(p,q,w1,w2);
halfadder U2(ci,w1,sum,w3);
or U3(co,w2,w3);
endmodule
(2)端口名对应方式
端口名对应方式是Verilog允许的另一种模块调用方式,其语法格式如下:
模块名<参数值列表> 实例名(.端口名1<信号名1> , .端口名2<信号名2>, ... , .端口名n<信号名n>);
例如:端口名对应的模块调用的例子
module dff(d,clk,clr,q);//D触发器模块,时被调用的模块,属于底层模块
input d,clk,clr;
output q;
reg q;
always@(posedge clk or negrdge clr)
begin
if(!clr)
q = 0;
else
q = d;
end
endmodule
module shifter_D(din,clock,clear,out);//顶层模块,用来调用底层模块
inprt din,clock,clear;
output [3:0] out;
dff U1(.q(out[0]),.d(din),.clk(clock),.clr(clear));
dff U1(.q(out[1]),.d((out[0]),.clk(clock),.clr(clear));
dff U1(.q(out[2]),.d((out[1]),.clk(clock),.clr(clear));
dff U1(.q(out[3]),.d((out[2]),.clk(clock),.clr(clear));
endmodule
(3)不同端口位宽的匹配
在端口和端口表达式之间存在着一种隐含的连续赋值的语句。因此当端口和端口表达式的位宽不一致时,会进行端口的匹配,采用的位宽匹配规则与连续赋值时使用的规则相同。
例如:模块调用时不同位宽的匹配问题
- 3.模块参数值
用下面两种途径可以改变模块实例的参数值,分别是使用带有参数的模块实例语句修改参数值和使用定义参数语句(defparam语句)修改参数值。
(1)使用带有参数的模块实例语句修改参数值,在这种方法中,模块实例的本身就能指定新的参数值,其语法格式是:
模块名<参数值列表> 调用名(端口名列表);
其中参数值列表又分为位置对应和名称对应两种方式。
例如:模块调用改变参数值
(2)使用定义参数语句(defparam语句)修改参数值
在进行模块调用时更改被调用模块内参数值的第二种方法就是利用“参数重定义语句” (defparam语句)。参数重定义语句的语法格式是:
需要注意的是,参数名必须采用分级路径的形式,才能锁定需要修改的参数是哪个模块当中的。
例:使用defparam语句修改参数值
module halfadder(a,b,s,c);//半加器模块
input a,b;
output c,s;
parameter xor_delay = 2, and_delay = 3;
assign #xor_delay s=a^b;
assign #and_delay c=a&b;
endmodule
module fulladder(p,q,ci,co,sum);//全加器模块
input p,q,ci;
output co,sum;
parameter or_delay = 1;
wire w1,w2,w3;
halfadder U1(p,q,w1,w2);
halfadder U2(ci,w1,sum.w3);
or #or_delay U3(co,w2,w3);
endmodule
module top1(top1a,top1b,top1s,top1c);//修改半加器模块参数的模块top1
input top1a,top1b;
output top1s,top1c;
defparam U1.xor_delay = 4, U1.and_delay = 5;//名为U1的半加器实例中对参数xor_delay和参数and_delay值的修改
halfadder U1(top1a,top1b,top1s,top1c);
endmodule
module top2(top2p,top2q,top2ci,top2co,top2sum);//修改全加器模块参数的模块top2
input top2p,top2q,top2ci;
output top2co,top2sum;
defparam U2.U1.xor_delay = 6,//名为U2的全加器实例中引用的名为U2的半加器实例中对参数xor_delay和and_delay的修改
U2.U2.and_delay = 7;
U2.or_delay = 5;//名为U2的全加器实例中对参数or_delay值修改
fulladder U2(top2p,top2q,top2ci,top2co,top2sum);
endmodule
3.2 门级建模
(1)Verilog HDL基本门级元件的类型
Verilog HDL内置26个基本元件,其中14个是门级元件,12个为开关级元件,这26个基本元件及其分类见下表。
类型 | 元件 | |
基本门 | 多输入门 | and, nand, or, nor, xor, xnor |
多输入门 | buf, not | |
三态门 | 允许定义驱动强度 | buif0, bfif1, notif0, notif1 |
mos开关 | 无驱动强度 | nmos, pmos, cmos, rnmos, rpmos, rcmos |
双向开关 | 无驱动强度 | tran,tranif0,tranif1 |
无驱动强度 | rtran, rtranif0, rtranif1 | |
上拉,下拉电阻 | 允许定义驱动强度 | pullup, pulldown |
Verilog中丰富的门级元件为电路的门级结构提供了方便。Verilog语言中的门级元件见下表:
(2)门级模块调用
多输入门元件调用的语法格式是:
门类型 <实例名>(<输出端口1>, <输出端口2>, ..., <输出端口n>, <输入端口>);
例如:
多输出门元件调用的语法格式是:
元件名 <实例名>(<输出端口1>, <输出端口2>, ..., <输出端口n>, <输入端口>);
例如:
三态门元件调用的语法格式是:
元件名 <实例名>(<数据输出端口>, <数据输入端口>, <控制输入端口>);
例如:
例如:调用门级元件实现下图2-4译码管
3.3 开关级建模
Verilog语言提供了十几种开关级基元,它们是实际的MOS管的抽象表示。这些开关级基元分为两大类:一类是MOS开关,一类是双向开关。每一大类又可分为电阻型(前缀用r表示)和非电阻型。
(1)MOS开关
MOS开关模拟了实际的MOS器件的功能,包括nmos、pmos、cmos 三种。
nmos和pmos的实例化语格式是:
nmos或pmos 实例名(out, data, control);
coms开关的实例化语言格式是:
cmos 实例名(out, data, ncontrol, pcontrol);
(2)双向开关
MOS开关只提供了信号的单向驱动能力,为了模拟实际的具有双向驱动能力的门级开关,Verilog语言提供了双向开关。双向开关的每个脚都被声明为inout类型,都可以作为输入驱动另一脚,也可以作为输出被另一脚驱动。
双向开关包括无条件双向开关(tran)和有条件双向开关(tranif0、 tranifl)。
无条件双向开关的例化语言格式是:
tran 实例名(inout1,inout2);
有条件双向开关示例化语言格式是:
tranif0或tranif1 实例名(inoutl,inout2, control);
下表列出Verilog语言中提供的开关级元件和功能说明。
例如,基本的nmos开关电路
来源:蔡觉平老师的Verilog课程
今天的文章Verilog学习笔记(2):Verilog程序设计语句和描述方式分享到此就结束了,感谢您的阅读。
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
如需转载请保留出处:https://bianchenghao.cn/84927.html