问题:在进程等待event触发的同一时间步长内,若该event触发,则不一定能等到该event
E文原文:http://www.deepchip.com/items/0466-07.html
(转载请注明出处,谢谢! seabeam)verilog通过event数据类型提供一种基本的进程同步机制,使用这种机制会遇到两个问题。第一个问题也许一开始不会被当做问题,但是过了几年你会意识到这真是个问题:许多工程师甚至不知道sv中还有event,根本没想到去用它。有个工程师使用了N年的verilog后最近才打算和他的team参加verilog培训班。当上到event这节,他问这是不是sv里的新玩意,答案是它早就在Verilog里。他惊呆了:“以前咋没人告诉我呢?”
第二点,说点有现实意义的,event事件触发在仿真时很容易产生竞争条件,看看下面这个例子:
module event_example1;event get_data, send_data; // handshaking flagsinitial -> get_data; // trigger get_data event at time zeroalways @(get_data) begin // wait for a get_data event... // do code to get data... // when done, trigger send_data-> send_data; // sync with send_data processendalways @(send_data) begin // wait for a send_data event... // do code to send data... // when done, trigger get_data-> get_data; // sync with get_data processendendmodule
这个简单的例子里有两个always块,使用event同步做了一个简单的握手模型,当一个块结束时另一块开始工作。问题在于仿真开始的0时刻,两个always块都处于活动状态,如果initial比get_data的always块先激活,那这个module就别想开工了。怎么办呢?verilog里只有一条路:延迟initial里的event触发时间,至少到get_data的always块被激活之后:
initial #0 -> get_data; // start handshaking at time 0, but after all// procedural blocks have been activated
使用#0延迟触发get_data,保证在0时刻get_data的always块先跑起来。但是#0本身就是个问题,在verilog里是个容易被滥用且不能保证在一个给定的时间步长里,加了#0就真的会在所有语句执行完毕
后执行(译者注:例如工程是个庞然大物,在不同的结构里都存在#0语句,那么他们之间也会互相竞争)。所以许多老师教导我们不要用#0,要用非阻塞赋值,或者预先确定顺序的event.除了event数据
类型,不使用#0是个好的guideline.但是在verilog里,没法将event触发做成非阻塞event队列。
这时候sv带了两种解决方案来救世了,现在可以和#0道别了。
方案1:操作符->>,它可以让event触发排成非阻塞队列。在本节的例子里,避免0时刻竞争条件的同时彻底抛弃了#0.将get_data event放入非阻塞队列使其触发前保证过程块处于激活状态(译者注:->>操作符不能出现在class里,只能存在于module中)。
initial ->> get_data; // start handshaking at time 0 nonblocking// queue, after all procedural blocks have// been activated
方案2:sv提供另一种方法以满足更多情况的需求。这种方法利用event内建的trigger属性使触发在整个时间步长内可见,而不是触发的那一瞬间。
module event_example2 ( ... );event get_data, send_data; // handshaking flagsinitial -> get_data; // trigger get_data event at time zeroalways beginwait(get_data.triggered) // wait for a get_data event... // do code to get data... // when done, trigger send_data-> send_data; // sync with send_data processendalways @(send_data) begin // wait for a send_data event// could have used wait(send_data.triggered) here also, but it is// not needed since there is no race condition between the two// always blocks... // do code to send data... // when done, trigger get_data-> get_data; // sync with get_data processendendmodule
当get_data触发时,wait(get_data.triggered)返回为真。event触发行为发生在wait语句激活前后都无关紧要。在上面的例子中,如果initial块先于第一个always块激活,那么激活行为在第一个always block激活时仍然可见,而且wait(get_data.triggered)语句也会被执行。