书※目:Verilog数字系统设计教程(第四版)夏宇闻等编著
虚拟机:VMware -14.0.0.24051
环 境:ubuntu 18.04.1
脚 本:makefile(点击直达)
应用工具:vcs 和 verdi
文章目录
一、条件语句
条件语句有if-else和case两类,都属于顺序执行语句,都写在always块内。
(1)if-else语句
- 判定所给条件是否满足,根据判定的结果(真或假)决定执行给出的两种操作之一。
- if-else语句有3种形式,其中“表达式”为逻辑表达式或关系表达式,或一位的变量;若表达式的值为0、x或z,则判定的结果为“假”;若为1,则结果为“真”。语句可为单句,也可为多句;多句时一定要用“begin_end”语句括起来,形成一个复合块语句。
//方式1
if(表达式) 语句1;
//方式2
if(表达式) 语句1;
else 语句2;
//方式3
if(表达式1) 语句1;
else if(表达式2) 语句2;
...
else if(表达式n) 语句n;
if语句可以嵌套;若if与else的数目不一样,注意用“begin_end”语句来确定if与else的配对关系!
if(表达式)
if(表达式2) 语句1;
else 语句2;
else
if(表达式3) 语句3;
else 语句4;
if (表达式1)begin
if (表达式2)语句1;
end
else begin
语句2;
语句3;
end
【例 1】
在always块内,语句是顺序执行的,always和assign是并发执行的。
(2)case语句
☛ 当敏感表达式取不同的值时, 执行不同的语句。
☛功能:当某个(控制)信号取不同的值时,给另一个(输出)信号赋不同的值。常用于多条件译码电路(如译码器、数据选择器、状态机、微处理器的指令译码)
☛ case语句有3种形式:case,casez,casex
case(敏感表达式)
值1:语句1;
值2:语句2;
…
值n:语句n;
default: 语句n+1;
endcase
说 明 :
- 其中“敏感表达式”又称为“控制表达式”,通常表示为控制信号的某些位。
- 值1~值n称为分支表达式,用控制信号的具体状态值表示,因此又称为常量表达式。
- default项可有可无,一个case语句里只能有一个default项!
- 值1~值n必须互不相同,否则矛盾。
- 值1~值n的位宽必须相等,且与控制表达式的位宽相同。
- 在case语句中,分支表达式每一位的值都是确定的(或者为0,或者为1);
- 在casez语句中,若分支表达式某些位的值为高阻值z,则不考虑对这些位的比较;
- 在casex语句中,若分支表达式某些位的值为z或不定值x,则不考虑对这些位的比较。
- 在分支表达式中,可用“?”来标识x或z
【例 2】用casez描述的数据选择器
module mux_z(out,a,b,c,d,select);
output out;
input a,b,c,d;
input[3:0] select;
reg out; //必须声明
always@ (select[3:0] or a or b or c or d) begin
casez (select)
4’b???1: out = a;//?表示高阻态
4’b??1? : out = b;
4’b? 1?? : out = c;
4’b 1??? : out = d;
endcase
end
endmodule
在组合电路中,左图缺少else,会生成锁存器,锁存器不利于时序收敛。避免生成锁存器的原则:如果用到if语句,最好写上else项;如果用到case语句,最好写上default项。
二、循环语句
循环语句分为4种:
- for语句——通过3个步骤来决定语句的循环执行:
(1)给控制循环次数的变量赋初值。
(2)判定循环执行条件,若为假则跳出循环;若为真,则执行指定的语句后,转到第(3)步。
(3)修改循环变量的值,返回第(2)步。 - repeat语句——连续执行一条语句n次
- while语句——执行一条语句,直到循环执行条件不满足;若一开始条件即不满足,则该语句一次也不能被执行!
- forever语句——无限连续地执行语句,可用disable语句中断!
(1)for语句
**【例 1】**用for语句描述的7人投票表决器:若超过4人(含4人)投赞成票,则表决通过。
module vote7 (
output pass,
input [6:0] vote
);
reg[2:0] sum; //sum为reg型变量,用于统计赞成的人数
integer i;
reg pass;
always @(vote) begin
sum = 0; //sum初值为0
for(i = 0;i<=6;i = i+1) begin//for语句
if(vote[i]) sum = sum+1; //只要有人投赞成票,则 sum加1
end
if(sum[2]) pass = 1; //若超过4人赞成,则表决通过
else pass = 0;
end
endmodule
仿真结果:超过四人时,pass输出1
(2)repeat语句
**【例 2】**用repeat语句和移位操作实现两个8位二进制数乘法
仿真结果:(用for实现会更简单)
(3)while语句
- 有条件地执行一条或多条语句。
- 首先判断循环执行条件表达式是否为真。若为真,则执行后面的语句或语句块;然后再回头判断循环执行条件表达式是否为真,若为真,再执行一次后面的语句;如此不断,直到条件表达式不为真为止。
注1 :首先判断循环执行条件表达式是否为真,若不为真,则其后的语句一 次也不被执行!
注2 :在执行语句中,必须有一条改变循环执行条件表达式的值的语句。
注3 :whille语句只有当循环块有事件控制(即@(posedge clk))时才可综合。
**【例 3】**用while语句对一个8位二进制数中值为1的位进行计数
module count1s_while ( count,rega,clk );
output[3:0] count;
input [7:0] rega;
input clk;
reg[3:0] count;
always @(posedge clk)
begin:count1
reg[7:0] tempreg; //用作循环执行条件表达式
count = 0; // count初值为0
tempreg = rega; // tempreg 初值为rega
while(tempreg) begin// 若tempreg非0,则执行以下语句
if(tempreg[0]) count = count+1;
//只要tempreg最低位为1,则 count加1
tempreg = tempreg >>1; //右移1位
end
end
endmodule
【例 4】用for语句对一个8位二进制数中值为1的位进行计数
结果:都一样
(4)forever语句
forever一般是不可综合的,所以用在搭建测试平台testbeech中比较多。
//常用在测试模块中产生周期性的波形,作为仿真激励信号。
//常用disable语句跳出循环
initial
begin : Clocking
clk = 0;
#10 forever #10 clk = !clk;
end
initial
begin : Stimulus
……
disable Clocking; // 停止时钟
end
三、顺序块与并行块
(1)顺序块(也称过程块)
关键字
begin - end
用于将多条语句组成顺序块,顺序块的特点:
a. 顺序块中的语句按照顺序依次执行,只有前面的语句执行完之后后面的语句才会被执行(除了带内嵌延迟控制的非阻塞赋值语句)。
b.如果语句包含延迟或者事件控制,那么延迟总是相对于前面那条语句执行完的仿真时间
【例 1】顺序块
//说明1:不带延时
reg x , y;
reg [1:0] z , w;
initial begin
x = 1'b0;
y = 1'b1;
z = {
x,y};
w = {
y,x};
end
//说明2:带延时的顺序块
reg x,y;
reg [1:0] z , w;
initial begin
x = 1'b0; //在仿真0时刻完成
#5 y = 1'b1;//在仿真5时刻完成
#10 z = {x,y}//在仿真时刻15完成
#20 w = {y,x}//在仿真时刻35完成
end
(2)并行块
并行块由关键字
fork-join
声明,并行块有以下特点:
a.并行块内的语句并发执行:
b.语句执行的顺序是由各自语句内延迟或事件控制决定的;
c.语句中的延时或事件控制是相对于块语句开始执行的时刻而言的。
//说明:代延时的并行块
reg x, y;
reg [1:0] z,w;
initial begin
fork
x = 1'b0; //在仿真0时刻完成
#5 y = 1'b1;//在仿真5时刻完成
#10 z = {x,y}//在仿真时刻10完成
#20 w = {y,x}//在仿真时刻20完成
join
end
在 “always”模块内,逻辑按书写的顺序执行。 顺序语句——“always”模块内的语句。在 “always”模块内,若随意颠倒赋值语句的书写顺序,可能导致不同的结果 。 注意阻塞赋值语句当本语句结束时即完成赋值操作!下面有两个例子:
//[ 例] 顺序执行模块1 。
module serial1(q,a,clk);
output q,a;
input clk;
reg q,a;
always @(posedge clk) begin
q=~q; //阻塞赋值
a=~q;
end
endmodule
结果:a和q的波形反相。
[例]顺序执行模块2 。
module serial2(q,a,clk);
output q,a;
input clk;
reg q,a;
always @(posedge clk) begin
a=~q;
q=~q;
end
endmodule
结果:a和q的波形一样。
块语句有三个特点:嵌套块、命名块和命名块的禁用。其中Verilog通过关键字disable
提供一种中止命名块执行的方法。disable可以通过用来从循环中退出、处理错误条件及根据控制信号来控制某些代码段是否被执行。格式:disable 块名;
四、思考题&答案
-
为什么建议在编写Verilog模块程序时,如果用到if语句建议大家把配套的else情况也考虑在内?
因为如果没有配套的else语句,在不满足if条件语句时,将会保持原来的状态不变,从而在综合时会产生一个锁存器,而这是设计不想要的结果。 -
用if语句,elseif 语句, elseif 语句,…else 语句和用case,endcase表示不同条件下的多个分支是完全相同的,还是有什么不同?
不是完全相同。与case语句中的控制表达式和多分支表达式这种比较相比,if else_ if结构中条件表达式更为直观些;对于那些分支表达式中存在不定值x和高阻值z的位时case语句提供了处理这种情况的段。 -
如果case语句的分支条件没有覆盖所有可能的组合条件,定义了default项和没有定义default项有什么不同?
定义了default项则会使电路描述的更加的清楚,综合的时候不会产生不想要的结果,没用定义default则会使在综合是产生一个锁存器。 -
仔细阐释case、casex 和casez之间的不同。
case、casex、 casez 对应的真值表如上图所示,可以看出case 无论是0,1,x,z,只有两者都一样才为1。而casez不将高阻进行比较,有z都为1,在其它情况相同时为1。而casex不将高阻z和x进行比较,有z或x都为1,其它情况进行比较,相同为1。
-
forever语句如果运行了,在它下面的语句能否运行?它位于begin end和位于fork join块有什么不同?
不能运行。位于begin end,由于begin and是顺序块,所以只要执行到forever则将不能运行下面的程序;而位于fork join,它是并行块,执行了forever 还是能够执行forever下面的语句。 -
forever语句repeat语句能否独立于过程块而存在,即能否不在initial或always 块中使用?
forever不能独立于过程块中,而repeat能够独立于过程块中。 -
用for循环为存储器许多单元赋值时是否需要时间?为什么如果不定义时间延迟,它可以不需要时间就把不管多大的储存器赋值完毕?
如果定义了时间延迟则需要时间,否则不需要时间。因为循环的边界是确定的,那么在综合时该循环语句被认为是重复的硬件结构。 -
for循环是否可以表示可以综合的组合逻辑?请举例说明。
可以表示综合的组合逻辑。例如用for循环实现的乘法器。 -
在编写测试模块时用什么方法可以使for循环按照时钟的节拍运行?请比较下面的程序段。
可以在for循环的最后嵌套时钟节拍运行的信号。第一种程序不能按照时钟节拍来对mem[i]赋值,而第二种程序可以。 -
声明一个为oscillate的寄存器变量并将它初始化为0,使其每30个时间单位进行一次取反操作,不要使用always语句(提示:使用forever 循环)。
reg oscillate;
initial begin
oscillate=0;
forever
#30 oscillate = !oscillate;
end
- 设计一个周期为40个时钟单位的时钟循环,其占空比为25%,使用always和initial块进行设计,将其在仿真0时刻的值初始化为0。
initial begin
clock = 0;
always begin
#30 clock = 0;
#10 clock = 1;
end
end
- 给定下面含有阻塞过程赋值语句的initial块,每个语句在什么仿真时刻开始执行?a,b,c和d在仿真过程中的中间值和仿真结束时的值是什么?
initial begin
a = 1'b0;
b = #10 1'b0;
c = #5 1'b0;
d = #20{a,b,c};
end
第一条语句在仿真开始时就执行,第二句在仿真10个时钟单元后执行,第三句在仿真15个时钟信号单元后执行,第四句在仿真35个时钟单元后执行。在中间仿真过程中a=0,b,c,d为不确定值,结束时abed的值是a=1’b0,b= 1’0,c=1’0,d=3’b000。
-
在第12题中,如果initial块中包含的是非阻塞过程赋值语句,那么各个问题的答案是什么?
如果是非阻塞过程赋值则答案是;第一条语句在仿真开始时就执行,第二句在仿真10个时钟单元后执行,第三句在仿真5个时钟信号单元后执行,第四句在仿真20个时钟单元后执行。在中间仿真过程a=0,b,c,d 为不确定值结束时abcd的值是a=1b0,b=1’0,c=1’0,d=3’b000。 -
下面例子中d的最终值是什么?
initial begin
b=1"b1;
c=1’b0;
#10 b=1’b0;
end
initial begin
d=#25(b|c);
end
答:d的最终值为0
- 使用带同步清零端的D触发器(清零高电平有效,在时钟下降沿执行清零操作)设计下一个下降沿触发的D触发器,只能使用行为语句(提示: D触发器的输出q应当声明为寄存器变量)。使用设计出的D触发器输出一个周期为10个时间单位的时钟信号。
module D_ FF(CLR,CLK,D,Q);
input CLR,CLK,D;
output Q;
reg Q;
always @(posedge CLR or necedge CLK) begin
if(CLR) Q=0;
else #10Q<=D;
end
endmodule
- 使用带有异步清零端的D触发器设计第64题要求的D触发器(在清零端变为高电平后立即执行清零操作,无须等待下一个时钟下降沿),并对这个D触发器进行测试。
module D_ FF(CLR,CLK,D,Q);
input CLR,CLK,D;
output Q;
reg Q;
always @(posedge CLR ) begin
Q <= 0;
end
always @(necedge CLK) begin
#10 Q <= D;
end
endmodule
- 使用wait语句设计一个电平敏感的锁存器,该锁存器的输入信号为d和clock输出为q,其功能是当clock=1时q=d。
module L_ FF(d,clock,q);
input d,clk;
output q;
reg q;
always begin
wait(clock==1)
q=d;
end
endmodule
- 使用条件语句设计【例5.18】中的四选一多路选择器,外部端口必须保持不变。
module mux4to1(out, i0, i1, i2, i3, s1, s0);
output out;
input i0, i1, i2, i3;
input s1, s0;
reg out;
always @ (*) begin
if ({
s1,s0} == 2'b00) out = i0;
else if ({
s1,s0} == 2'b01) out = i1;
else if ({
s1,s0} == 2'b10) out = i2;
else if ({
s1,s0} == 2'b11) out = i3;
else out = 1'bx;
end
endmodule
- 使用while循环设计一个时钟信号发生器。其时钟信号的初值为0,周期为10个时间单元。
initial begin
clk=0;
while(1)
#10 clk = !clk;
end
- 使用for 循环对一个长度为1024(地址从0~ 1023)、位宽为4的寄存器类型数组cache_ var 进行初始化,把所有单元都设置为0。
begin
reg [3:0] cache_ var[1023:0];
intiger i;
for(i=0;i<1024;i++)
cache_ var[i]=0;
end
- 使用forever 循环设计一个时钟信号,周期为10,占空比为40%,初值为0。
initial begin
clk=0;
forever begin
#6 clk=0;
#4 clk=1;
end
end
- 使用repeat 将语句a=a+1延迟20个时钟上升沿之后再执行。
parameter delay= 20;
intiger i;
reg a;
begin
repeat(delay)
always @(posedge clk) begin
i++;
if(i== 20) a=a+1;
end
end
- 下面是一个内嵌顺序块和并行块的块语句。该块的执行结束时间是多少?事件的顺序是怎样的?每条语句的仿真结束时间是多少?
initial begin
x=1’b0;
#5 y=1’b1; //5
fork
#20 a=x; //25
#15 b=y; //20
join
#40 x=1"b1; //65
fork
#10p=x; //75
begin
#10a=y; //75
#30b=x; //105
end
#5 m=y; //70
join
end
该块的执行结束时间是5+20+40+40= 105个时钟单位
作者:xlinxdu
版权:本文是作者读书整理的笔记,部分材料来源于参考教材 or 其他,侵权联系删。
转载:未经作者允许,禁止转载,转载必须保留此段声明,必须在文章中给出原文连接。