热度 19
2012-12-19 07:59
3148 次阅读|
0 个评论
第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块的时间控制是沿触发,常常用来描述时序逻辑电路的行为。