2线程间的通信
2.1概述
• 测试平台中的所有线程都需要同步并交换数据。
• 一个线程需要等待另一个。
• 多个线程可能同时访问同一个资源。
• 线程之间可能需要交换数据。
• 所有这些数据交换和同步称之为线程间的通信(IPC, Interprocess Communication)。
2.2 event
Verilog中,一个线程总要等待一个带@操作符的事件,这个操作符是边沿触发的,所以它总是阻塞的、等待事件的发生。
其它线程可以通过->操作符来触发事件,结束对第一个线程的阻塞。
event el, e2;
initial begin
$display("@%0t: 1: before trigger", $time);
-> el; //触发事件e1
@ e2; //等待事件e2
$display("@%0t: 1: after trigger", $time);
end
initial begin
$display ("@%0t: 2: before trigger", $time);
-> e2; //触发事件e2
@ el; //等待事件e1
$display ("@%0t: 2: after trigger", $time);
end
代码结果:
@0: 1: before trigger //第一个初始化块启动, 触发e1事件, 然后阻塞在e2上。
@0: 2: before trigger //第二个初始化块启动, 触发e2事件, 然后阻塞在e1上。
@0: 1: after trigger
//el和e2在同—个时刻被触发, 但由于delta cycle的时间差使得两个初始化块可能同时无法等到el或者e2。
所以, 更安全的方式可以使用event的方法triggered() 。
triggered()应用:
event el, e2;
initial begin
$display("@%0t: 1: before trigger", $time);
-> el; //触发事件e1
wait(e2. triggered()); //等待事件e2
$display("@%0t: 1: after trigger", $time);
end
initial begin
$display ("@%0t: 2: before trigger", $time);
-> e2; //触发事件e2
wait(e1. triggered()); //等待事件e1
$display ("@%0t: 2: after trigger", $time);
end
@0: 1: before trigger
@0: 2: before trigger
@0: 1: after trigger
@0: 2: after trigger
可以使用电平敏感的wait(e1, triggered ())来替代边沿敏感的阻塞语句@e1。
如果事件在当前时刻已经被触发, 则不会引起阻塞。 否则, 会一直等到事件被触发为止。
这个方法比起@而言, 更有能力保证, 只要event被触发过, 就可以防止引起阻塞。
实例
不同的线程之间,有时会相互告知需求,比如,我们要开一辆车,在踩油门行驶之前,首先要看看汽车有没有发动,设计代码如下:
class car;
bit start=0;
task launch();
start = 1;
$display("car is launched");
endtask
task move();
wait(start=1);
$display("car is moving");
endtask
task drive();
fork
this.launch();
this.move();
join
endtask
endclass
module road;
initial begin
automatic car byd =new();
byd.drive();
end
endmodule
代码结果:
# car is launched
# car is moving我们发现,car::drive()同时启动线程car::move和car::launch,然而car::move是通过wait语句完成线程launch通知线程move
见上述代码改成event触发也可以实现一样的功能。
class car;
event start_e;
task launch();
->start_e;
$display("car is launched");
endtask
task move();
wait(start_e.triggered);
$display("car is moved");
endtask
task drive();
fork
this.launch();
this.move();
join
endtask
endclass
module road;
initial begin
automatic car byd =new();
byd.drive();
end
endmodule
汽车要加速的时候,速度仪表信息又是如何显示的呢?
class car;
event e_start;
event e_speedup;
int speed= O;
...
task speedup() ;
# 10ns;
-> e_speedup;
endtask
task display() ;
forever begin
@ e_speedup;
speed++;
$display("speed is %0d", speed);
end
endtask
task drive() ;
fork
this.launch();
this.move();
this.display() ;
join_none
endtask
endclass
module road;
initial begin
automatic car byd = new();
byd.drive();
byd.speedup();
byd.speedup();
byd.speedup();
从这个汽车加速的例子来看,如果你要一直踩着油门不放的话,这个加速的event必定会被不断触发,而当线程A要给线程B传递超过一次的事件时,利用公共变量就不再是个好的选择。
上边的例子依然可以通过event触发,来多次通知另外一个线程。注意,对于线程多次通知的需求,应该使用“@”,而无法使用wait(event,triggered()),这是由于,当一个event被触发时,它的状态会使得event.triggered一直保持true,不过,event没有方法可以清除event.triggered的状态。