|
一般而言,Verilog的always块不能触发自己,见下面的例子:
[例3] 使用阻塞赋值的非自触发振荡器
module osc1 (clk);
output clk;
reg clk;
initial #10 clk = 0;
always @(clk) #10 clk = ~clk;
endmodule
上例描述的时钟振荡器使用了阻塞赋值。阻塞赋值时,计算RHS表达式并更新LHS的值,此时不允许其他语句的干扰。阻塞赋值必须在@(clk)边沿触发到来时刻之前完成。当触发事件到来时,阻塞赋值已经完成了,因此没有来自always块内部的触发事件来触发@(clk),是一个非自触发振荡器。
而例4中的振荡器使用的是非阻塞赋值,它是一个自触发振荡器。
[例4] 采用非阻塞赋值的自触发振荡器
module osc2 (clk);
output clk;
reg clk;
initial #10 clk = 0;
always @(clk) #10 clk <= ~clk;
endmodule
@(clk)的第一次触发之后,非阻塞赋值的RHS表达式便计算出来,把值赋给LHS的事件被安排在更新事件队列中。在非阻塞赋值更新事件队列被激活之前,又遇到了@(clk)触发语句,并且always块再次对clk的值变化产生反应。当非阻塞LHS的值在同一时刻被更新时, @(clk)再一次触发。该例是自触发式,在编写仿真测试模块时不推荐使用这种写法的时钟信号源。
上边这段文字对于初学者来说很不容易明白,很容易进入死胡同,我也一样,想破脑袋最终才略微明白123。下边咱们就分析一下这123,也不知道对不对,希望高手指点一下。
如果直接拿上边代码仿真会分别得到波形(modelsim)
阻塞赋值
非阻塞赋值
仿真虽然不一样,但用Quartus2综合的结果确是一样的,都是振荡器。
综合结果如图:
为什么会有这样的结果呢?网上的答案基本就两个,一个是上边那个,另个一个是一篇外文翻译,但是基本一样,看了好久也没看懂。突然有一个时刻,有点明白了,就跟大家探讨一下。
为弄懂这段代码,本人感觉有必要搞清楚层积事件队列,层积事件列是层次模型:层积事件列的执行顺序是按优先级排列的。本人感觉这篇文章讲的不错:http://www.21eic.com/eda/method5322.html,理解它有助于我们理解阻塞和非阻塞赋值的功能。
最初我不明白的是虽然阻塞和非阻塞赋值在这里调度事件和执行的不一样,但跟always触发块的时间关系又是什么呢?我记得在网上哪个博客上看到这样一句话:阻塞赋值执行完,always块才结束;非阻塞赋值在always块执行完后才执行(估右值在其前,刷新左值在其后)。个人感觉这句话对于此代码仿真的理解非常有帮助。
下边是我用我的理解来画的事件执行图
阻塞赋值:
在阻塞赋值流程中,个人感觉主要是在always块结束,才能让always块再次对敏感信号敏感。
非阻塞赋值:
同理,非阻塞赋值的刷新RHS是在上次always块结束后才被执行的,所以可以再次引起always块敏感事件。
下边为说明这点我们改变一下非阻塞赋值例子:
代码
module oscl(clk);
output clk;
reg clk;
reg a, b, c;
initial # 10 clk = 0;
initial # 5 b = 0;
initial # 13 b = 1;
initial # 23 b = 0;
always@(clk) begin
#10 clk <= ~clk;
#
end
endmodule
它在ModelSim中的仿真图形是
它的事件分析跟上边非阻塞赋值基本是一样。
在理解中,要注意这些分析,基于电路设计本身的少,大部分是从仿真器的内部处理角度看的。由于电路是并行的,但仿真器大部分是顺序执行来处理并行事件,所以采用层积事件的特性,这一特性跟仿真器本身的设计有关,由于仿真器的存在是介于我们设计电路本意和综合出实际电路的之间,这样有时候设计本意、仿真、综合这三者之间就会存在差异,如上边,仿真不同,但得到的综合结果是一样。
仿真事件的分析最好是看一下Verilog协议第五讲,没有多少文字,但讲的很好!如下摘录一段:
5.4 The Verilog simulation reference model
In all the examples that follow, T refers to the current simulation time, and all events are held in the event queue, ordered by simulation time.
while (there are events) {
if (no active events) {
if (there are inactive events) {
activate all inactive events;
} else if (there are non blocking assign update events) {
activate all non blocking assign update events;
} else if (there are monitor events) {
activate all monitor events;
} else {
advance T to the next event time;
activate all inactive events for time T;
}
}
E = any active event;
if (E is an update event) {
update the modified object;
add evaluation events for sensitive processes to event queue;
} else { /* shall be an evaluation event */
evaluate the process;
add update events to the event queue;
}
}