Verilog HDL语言中always敏感信号对比
来源:UC论文网2015-11-15 16:30
0 引 言 硬件描述语言(Hardware Description Language, HDL)是一种用形式化方法来描述数字电路和系统的语言。Verilog HDL和VHDL是目前世界上最流行的两种硬件描述语言,都是在20世纪80年代中
硬件描述语言(Hardware Description Language, HDL)是一种用形式化方法来描述数字电路和系统的语言。Verilog HDL和VHDL是目前世界上最流行的两种硬件描述语言,都是在20世纪80年代中期开发出来的,两种HDL均为IEEE标准。但是Veriolg 语言的很多规定与C语言相似,代码简单,有大量支持仿真的语句与可综合语句,对于初学者设计简单的数字系统,更容易学习和掌握[1]。所以,Verilog HDL语言在大规模集成电路和现场可编程门阵列设计中得到了广泛的应用[2?4]。
在集成电路设计中,Verilog语言中的always语句经常用来描述时序逻辑电路和组合电路。always语句是一种结构化的过程语句,是行为级建模的基本语句,它的语句格式为:always@(敏感事件列表),敏感事件可以是时钟边沿信号也可以是电平信号,分别对应时序逻辑电路和组合逻辑电路[5]。敏感事件列表中可以包含多个敏感事件,只要所列举的任意一种情况发生,都将激活事件控制,各个敏感事件之间是“或”的关系;但不可以同时包括电平敏感事件和边沿敏感事件,也不可以同时包括同一个信号的上升沿和下降沿,这两个事件可以合并为一个电平敏感事件。而且,按照语法要求,在always块中只能给寄存器变量赋值。
在实际应用中,敏感信号为时钟边沿信号,仿真综合结果一般正确。但是当敏感信号为电平信号时,情况就会变得复杂,仿真综合结果会变得不确定。文献[6]对always敏感信号与仿真结果的这种不确定性问题也进行了肯定,但是并没有进一步的分析。本文对always语句中的事件控制敏感信号出现的各种情况进行对比探讨,发掘always语句中敏感信号分别为时钟边沿信号和电平信号的差异,并通过仿真图形去验证。
1 敏感信号为时钟边沿信号
Always语句中的敏感信号如果为时钟边沿敏感事件,一般用来表示时序逻辑电路,时序逻辑电路在逻辑功能上的特点是任意时刻的输出不仅取决于当时的输入信号,而且还取决于电路原来的状态,或者说,还与之前的输入有关。从电路行为上讲,不管输入如何变化,仅当时钟的沿(上升沿或下降沿)到达时,才有可能使输出发生变化[7]。这里以经常用到的D触发器为例,其仿真图如图1所示。
module dff(q,a,b,clk );
output q;
input a,b,clk;
reg q;
always @(posedge clk) //时钟的上升沿
begin
q<=a | b; //D触发器的驱动方程是a|b
end
endmodule 图1 D触发器的仿真图
上面所述的D触发器,赋值语句为q<=a|b,等式右端为wire型变量。再举个多敏感信号的时序逻辑电路的例子,比如带有清零端的16分频,其仿真图如图2所示。
module div16(clk,rst,clk_16);
output clk_16;
input clk,rst;
reg[3:0] cnt16;
always @(posedge clk or negedge rst)
//时钟的上升沿和清零信号的下降沿
begin
if(!rst)
cnt16<=0;
else
cnt16<=cnt16+1; //计数器加1
end
assign clk_16=cnt16[3];
endmod
图2 带清零端16分频仿真图
只要在always块的敏感信号表中定义有效的时钟沿,敏感词的作用立竿见影,然后使用过程赋值语句对信号赋值,就可以实现时序逻辑电路。
2 敏感信号为电平信号
always语句中的敏感信号如果为电平敏感事件,一般用来表示组合逻辑电路,组合逻辑电路的特点是输出信号只是当前时刻输入信号的函数,与其他时刻的输入状态无关,无存储电路。从电路行为上看,其特征就是输出信号的变化仅仅与输入信号的电平有关,不涉及对信号跳变沿的处理[8]。always电平敏感信号列表,必须将所有的输入信号和条件判断信号都列在信号列表中。有时不完整的信号列表会造成不同的仿真和综合结果,因此需要保证敏感信号的完备性。在实际的PLD 器件开发中,EDA 工具都会默认将所有的输入信号和条件判断语句作为触发信号,增减敏感信号列表中的信号不会对最终的执行结果产生影响,因此如果期望在设计中通过修改敏感信号来得到不同的逻辑,是不能实现的,这也是经常犯错的地方,这是因为仿真器在工作时不会自动补充敏感信号表。如果缺少信号,则无法触发和该信号相关的仿真进程,也就得不到正确的仿真结果。这里以一个2?4译码器为例,其仿真图如图3所示。
module trans24(data_out,data_in,enable);
input [1:0] data_in;
input enable;
output [3:0] data_out;
reg [3:0] data_out;
always @(data_in or enable)
begin
if (enable==1)
if(data_in==3′b00) data_out=~8′b1110;
else if(data_in==3′b01)data_out=~8′b1101;
else if(data_in==3′b10)data_out=~8′b1011;
else if(data_in==3′b11)data_out=~8′b0111;
else data_out=8′bxxxx;
else
data_out = ~8′b1111;
end
endmodule
图3 2?4译码器仿真图
如果想用一个敏感信号来控制逻辑变化,比如当enable信号的电平发生变化时,再去译码,程序如下,仿真图如图4所示。
图4 敏感信号只有enable的2?4译码器的仿真图
module tans24(data_out,data_in,enable);
input [1:0] data_in;
input enable;
output [3:0] data_out;
reg [3:0] data_out;
always @(enable)
begin
if(data_in==3′b00) data_out=~8′b1110;
else if(data_in==3′b01)
data_out=~8′b1101;
else if(data_in==3′b10)data_out=~8′b1011;
else if(data_in==3′b11)data_out=~8′b0111;
else data_out=8′bxxxxxxxx;
end
endmodule
由图4可以看出,这并不是所需的结果,这就是前面所说的,系统自动将所有的输入作为了敏感信号。
因此,在应用always块语句表述组合逻辑电路时,一定要注意敏感信号的完整性,要求触发为所有内部用到的信号,可以用always@(*),此时,综合工具和仿真工具会自动将所有的敏感信号自动加入敏感信号列表。
前面已经提到过always敏感信号不可以同时包括同一个信号的上升沿和下降沿,这两个事件可以合并为一个电平敏感事件。在设计中,一些初学的设计者经常在时钟的上升沿和下降沿都进行计数器加1,以为这样能实现倍频,仿真结果如图5所示。module beipin(clk,clknot,cnt8,clk_2,clk_4,clk_8);
input clk;
output clknot,clk_2,clk_4,clk_8;
output[3:0] cnt8;
reg [2:0]cnt8;
reg[2:0]state;
reg clknot;
always @ (clk)
clknot=~clk;
always @ (clk)
cnt8=cnt8+1;
assign clk_2=cnt8[0];
assign clk_4=cnt8[1];
assign clk_8=cnt8[2];
endmodule
图5 直接计数倍频仿真波形
从图5中可以发现并没有出现想要的结果,而是呈现出了高阻态。将直接加1运算改为直接的赋值语句,程序如下,仿真结果如图6所示。
从图6中可以看出,cnt8这个变量存储的是最后一次赋值,这时当always敏感信号为电平信号,系统默认为组合逻辑电路,虽然将信号定义为reg 型,但只是为了满足always 模块中的信号必须定义为reg 型的语法要求,最终的实现结果中并没有寄存器,在图5中出现高阻态,因为cnt8=cnt8+1是计数器,是时序逻辑电路。
图6 直接赋值的倍频仿真波形
module beipin1(clk,clknot,cnt8,clk_2,clk_4,clk_8);
input clk;
output clknot,clk_2,clk_4,clk_8;
output[3:0] cnt8;
reg [2:0]cnt8;
reg[2:0]state;
reg clknot;
always @ (clk)
clknot=~clk;
always @ (clk)
case(state)
0:begin cnt8<=3′b000;state=1;end
1:begin cnt8<=3′b001;state=2;end
2:begin cnt8<=3′b010;state=3;end
3:begin cnt8<=3′b011;state=4;end
4:begin cnt8<=3′b100;state=5;end
5:begin cnt8<=3′b101;state=6;end
6:begin cnt8<=3′b110;state=7;end
7:begin cnt8<=3′b111;state=0;end
endcase
assign clk_2=cnt8[0];
assign clk_4=cnt8[1];
assign clk_8=cnt8[2];
endmodule
3 结 论
本文对Verilog语言中always块语句中的敏感信号进行了对比探讨,得到如下结论:
(1) 如果敏感信号是时钟边沿触发信号,表示的是时序逻辑电路,而且在描述时序电路的always块中的reg型信号都会被综合成寄存器,而且时序逻辑的敏感信号列表只需要加入所用的时钟触发沿即可。
(2) 如果敏感信号是电平触发信号,表示的是组合逻辑电路,这里一定要注意敏感信号的完整性,即所有的输入和判断语句的信号都要加为敏感信号,否则,得不到想要的设计结果。
(3) 在组合逻辑电路描述中,将信号定义为reg型,只是为了满足always模块中的信号必须定义为reg 型的语法要求,最终实现结果中并没有寄存器。
参考文献
[2] 孙继荣,李志蜀,王莉,等.程序切片技术在软件测试中的应用[J].计算机应用研究,2007,24(5):210?213.
[3] 宁佐林,邱智亮.PCI桥接IP Core的Verilog HDL实现[J].电子科技,2006,19(4):43?46.
[4] 赵东,耿卫东,吴春亚,等.用FPGA实现OLED灰度级显示[J].光电子[?]激光,2002,13(6):554?558.
[5] 罗杰.Verilog HDL与数字ASIC设计基础[M].武汉:华中科技大学出版社,2008.
[6] PADMANABHAN T R, SUNDARI B B T. Design through Verilog HDL [M]. New York: John Wiley & Sons, 2013.
[7] CILETTI M D. Advanced digital design with the Verilog HDL [M]. 2nd ed. Beijing: Electronic Industry Press, 2010.