创建线程的方法
fork …join_none以及fork…join_any。两者的区别在于,前者在调度块内语句时,父线程继续执行,且父线程的后续语句早于fork内线程;后者则在块内任一个语句完成后。线程内的语句是并行的。而传统的fork…join则需要等到块内所有语句执行完成后才进行后续语句执行。
停止线程的方法
使用disable forklabel来停止线程。若没有指明线程标记,则会停止当前线程衍生出的所有子线程。若停止某个的任务的调用,那么其他调用该任务的线程也会停止。
线程间的通讯
可以使用事件、旗语、信箱、@事件控制以及wait语句。
旗语
可以利用旗语实现对同一资源的访问管理。
program automatic test(bus_ifc.TB bus);
semaphore sem;/*创建旗语*/
initial begin
sem = new(1);/*分配钥匙数目*/
fork
sequencer();
sequencer();
join
end
task sequencer;
repeat($urandom%10)
@(bus.cb);
sendTrans();
endtask
task sendTrans;
sem.get(1);/*获取钥匙*/
@(bus.cb);
bus.cb.addr<=t.addr;
...
sem.put(1);/*返回钥匙*/
endtask
endprogram
信箱
先进先出的一个存储器。可以用put()和get()语句进行取放。不过要注意取放的先后,无法对满的信箱放,无法对空的信箱取。peek()可以拷贝数据而不移除。所以为了保证取放步调一致,可以采用peek()、事件、额外信箱。
program automatic mbx_evt;
mailbox mbx; /*信箱*/
event handshake; /*控制取放同步的事件*/
class Producer; /*放类*/
task run;
for(int i=0; i<4; i++) begin
$display("Producer:before put(%0d)",i);
mbx.put(i); /*放入数据*/
@handshake; /*在控制信号还没有触发前,不会继续放*/
$display("Producer:after put(%0d)",i);
end
endtask
endclass
class Consumer; /*取类*/
task run;
int i;
repeat (3) begin
mbx.get(i); /*取出数据*/
$display("Consumer:after get(%0d)",i);
-> handshake; /*触发控制信号*/
end
endtask
endclass:Consumer
Producer p;
Consumer c;
initial begin
mbx = new();
p = new();
c = new();
fork /*取放并行启动*/
p.run();
c.run();
join
end
endprogram