下面这段源码是因为习惯不好,出现不正确波形的例子。
module pwm_division(reset,clkin,clkout);
input reset,clkin;
output clkout;
reg clkout;
reg[:] count;
always @(posedge clkin)
begin
if(!reset)
begin
clkout<=;
count<='d0;
end
else
begin
count<=count+'d1;
$display("count1=%d",count);
if(count<'d21)
clkout<=;
else
begin
clkout<=;
if(count>'d30)
begin
$display("count2=%d",count);
count='d0;//
$display("count3=%d",count);
end
end
$display("count4=%d",count); end
$display("count5=%d",count);
$strobe("count6=%d",count);
end
endmodule
$display是后来加进去的。
首先来分析下这段代码。本意是输出一个占空比为1/3的波形,但是结果却是一个20/236的波形。意思也就是count=8'd0这个操作没有起作用。
后来检查到count=8'd0是阻塞赋值,猜测出错的原因可能是同一段always语句中即有非阻塞赋值又有阻塞赋值。改为count<=8'd0;后,波形正确。
进一步分析,在代码中加入系统显示函数,发现了有趣的事情。运行仿真后得到结果:
# count1= 31
# count2= 31
# count3= 0
# count4= 0
# count5= 0
# count6= 32
可以看出,count=8'd0;这个操作是有效的。但是在下一个clk到来时,count的值还是等于原值加1。这是为什么呢?
我暂时理解如下:
阻塞操作是在赋值时右值立即等于左值,而非阻塞操作是先计算右值,而在模块结束是才更新左值。
所以count<=count+8'd1;执行后,count+1的值(右值)已计算,但是真正改变count的值(左值)是在模块的最后。所以会有上面的结果。为了证明上面的理解
我改为count=count+8'd1;执行后,count的值正确。在书上找到一段层次化事件队列的描述支持我这种理解:
参考 《Verilog数字系统设计教程 第三版》197页。
上面是错误原因的分析,但是程序肯定不能这样写。导致会出现这种错误的原因是因为程序不严谨,进一步说是因为我刚学verilog,C语言顺序执行的思想已经根深蒂固,而verilog有并行执行的语句,所以很容易出现隐藏的逻辑错误。