第12章块语句以及过程块说说
通过前面章节的学习,我相信大家对verilog有了一定的基础。大家在编程时会碰到这样的问题,用一个语句不能完全描述自己想写的电路,怎么办?这时候我们可以用块语句,一个语句不够,我们用两个,用三个,甚至更多。那么什么是块语句,通常指将两个或多个语句组合在一起,让它可以作为一个整体来使用,格式上像一条语句一样。块语句有两种,一种是begin_end语句,另一种是fork_join语句。下面我们就说说这两种块语句。
12.1 说说begin_end
begin_end是一个顺序执行的语句块,跟C语言里面的“{”和“}”一样,整个语句是从第一条语句依次执行,直到到最后一条语句执行完,才会跳出该块语句。另外
我们可以通过一个例子来看看begin_end语句块的顺序性,实例如下:
reg a;
initial begin
a = 1'b0;
#10 a = 1'b1;
#10 a = 1'b0;
#10;
$stop;
end
整个begin_end块**有5个语句组成,从第一句开始执行,在0时刻,a值为0;接下来执行第二句,延时10个时间单位后将1赋给a;再执行第三个语句,延时10个时间单位后将0赋给a;执行第4,5个语句,延时10个时间单位,停止。
仿真出来的图形如下:
从仿真结果来看,begin_end语句块是顺序执行的。
begin_end语句块是可以带有块名的。
其格式为:
Begin :块名
块内声明语句
语句1;
…
语句n;
end
要注意,begin和end成双成对的,缺少一个就会产生语法错误。声明语句可以是参数声明语句,reg型变量声明语句,integer型变量语句和real型变量声明语句。
语句块带有块名有什么好处?
1.可以在块内定义局部变量,即只能在块内使用的变量;
2.该语句块可以被其他语句调用,如disable语句。
例如:
module begin_end_test;
reg a;
initial begin: cc
reg a;
reg b;
a = 1'b0;
b = 1'b0;
#10 a = 1'b1;
#10 b = 1'b1;
#20 a = 1'b0;
#20 b = 1'b0;
end
initial begin
a = 1'b1;
#10 a = 1'b0;
#5 ;
disable cc;
#45;
$stop;
end
endmodule
下图为运行的结果:
该仿真程序用了带块名的begin_end块,begin_end块定义了两个reg型变量,仿真结果可以看出,变量a是局部变量,不会影响全局变量。另外该begin_end块可以被其他语句调用,例子中被disable语句调用,故在t=15以后的时刻,cc块的语句不在执行。
12.2 说说fork join
fork_join是一个并行的语句块,即程序一进入,整个语句块内的语句则开始同时并行执行。当按时间时序,排在最后的语句执行完后,或者一个disable语句执行时,程序流程控制则跳出该程序块。我们还是通过一个例子来验证一下fork_join的并行性。
reg a,b;
initial fork
a = 1'b0;
b = 1'b0;
#10 a = 1'b1;
#10 b = 1'b1;
#20 a = 1'b1;
#20 b = 1'b1;
#30 $stop;
join
从程序来看,在t=0时刻,a=0,b=0;在t=10的时刻,a变为1,b变为1;在t=20的时刻,a变为0,b变为0;在t=30的时刻跳出仿真程序,我来看一下仿真的结果。
从上图可以看到,放真结果和分析的一致,可以得出fork_join块语句是并行语句。
fork_join语句块也是可以带有块名的。
其格式为:
fork :块名
块内声明语句
语句1;
…
语句n;
join
和begin_end一样也要注意,fork和join是成双成对的,缺少一个就会产生语法错误。声明语句可以是参数声明语句,reg型变量声明语句,integer型变量语句和real型变量声明语句,time型变量声明语句和事件(event)说明语句。
带块名的fork_join语句块和begin_end类似,有同样的好处,具体的例子大家可以参考begin end。
另外,还要注意fork_join不可综合,而begin_end可以综合。
12.3 说说assign
模块内部赋值方式有两种,过程性赋值语句和连续赋值语句。我们要讲的assign语句就是连续赋值语句,与其他语句并行执行,在右端操作数的值发生变化的时候执行,在前面将变量的时候说过,assign语句用于驱动线网变量(wire)。Assign语句右边表达书的变量都是敏感信号,当右值发生变化时,计算表达式的值,并将结果传给左值,具有组合电路的特性,输入变化,输出立即变化。
举个例子:
wireaout;
reg b;
assignaout=b;
例子说的是将reg型变量b的赋给aout;当b变化时,aout也随之变化。在always外,assign语句是组合电路的主要实现方式。
12.4 说说 always
always块在verilog语言中占很重要的地位,它可以描述组合逻辑电路,也可以描述时序逻辑电路。always块语句主要由时序控制条件和执行语句,格式为
always 时序控制语句
always块语句是不断重复执行,只要满足时序控制条件,执行语句就会执行一次过程块。注意always块语句必须得有时序控制,否则always块就是进入一个0时延的无限的循环跳变中,导致仿真死锁。
例如:
module begin_end_test;
reg a,b,c;
reg clk;
initial fork
a = 1'b0;
b = 1'b0;
#10 a = 1'b1;
#10 b = 1'b1;
#20 a = 1'b0;
#20 b = 1'b0;
#30 $stop;
join
always clk=!clk;
endmodule
由于仿真程序里的always块没有时钟控制,它会生成一个0时延的无限循环的跳变过程,这时就会发生仿真死锁,导致整个仿真不能正常运行。
仿真运行以后如下图:
下面我通过几个例子来说明常用的用法。
例子1:
always #5 clk=~clk;
这个例子是生成了一个周期为10的无限延续的信号。常用来描述时钟信号,可作为激励信号来测试所设计的电路。
例子2:
regcout;
always @(ain or bin or sel)
if(sel)
cout = ain;
else cout = bin;
这是一个二选一多路器,输出cout与输入ain还是输入bin一致,由sel的电平决定。当sel为高时,cout输出为ain;sel为低时,cout输出为bin。这类always块的时间控制是电平触发,常常用来描述组合逻辑电路的行为。
例子3:
reg clk_div_2;
always @ (posedgeclk or posedgerst)
begin
if(rst)
clk_div_2<=1’b0;
else
clk_div_2<=~clk_div_2;
end
这个always块是二分频模块,是每当clk信号的上升沿出现时,则把clk_div_2信号反相,将原来的时钟进行二分频。这类always块的时间控制是沿触发,常常用来描述时序逻辑电路的行为。
文章评论(0条评论)
登录后参与讨论