目录
1.线程通信的意义
在测试平台中,需要通过控制和模拟事务之间的调度,来控制整个测试平台的运行。例如何时开始发送激励,监测到信号时,触发另一些事件,或者是通过mailBox充当FIFO,来存放句柄。通过这些线程之间的通信,可以控制和搭建更完整的验证平台。
2.线程的使用
线程的使用主要包括begin…… end 的顺序执行语句块以及fork …… join, fork……join_none,fork …… join_any等的并行执行语句块。
fork……join:全部子线程执行完再进入下一阶段。
fork …… join_any:有一个子线程完成即进入下一阶段。
fork……join_none:直接进入下一阶段。
示例:
//一直监听复位信号而不阻塞其他进程的运行
virtual protected task reset_listener();
`uvm_info(get_type_name(), "reset_listener ...", UVM_HIGH)
fork
forever begin
@(negedge vif.hresetn); // 等待复位信号
_do_drive_idle(); // 复位
end
join_none
endtask
3.线程的控制
线程可以通过wait fork来等待fork块中所有线程执行完毕再退出initial块,也可以通过disable来禁止某个线程的进行。
示例:
initial begin::wait_fork
fork
wait_fork_1();
wait_fork_2();
join_none
wait fork; //等待线程结束
end
initial begin::disable_fork
fork:d
fork:d_1
dis_fork_1();
join
fork:d_2
dis_fork_2(); //100ns后 禁止
join
fork:d_3
#100ns;
disalbe d_1; //100ns 后终止进程d_1
join
join
end
4.线程间的通信
线程之间需要通过同步并交换数据,另一个线程可能需要等待一个线程的触发后才会运行,以及多个线程之间可以需要共享一个资源。这些线程之间的数据交换和同步被成为是线程之间的通信。线程之间的通信方法主要包括event、semaphore、mailBox。
4.1 event
在Verilog中可以通过->来触发事件,同时通过@来等待事件,而在systemVerilog中新添加了wait(event.triggered())。
@是边沿敏感的阻塞语句,而wait(event.triggered())是电平敏感的。但需要对一个事件重复触发时,需要用@,因为event没有方法能够清除wait(event.triggered())的状态。
wait(event.triggered())无法捕捉已经被触发,但后续才等待的事件,若没有采样到事件就会一直阻塞。
示例:
event e1,e2;
initial begin
$display( "1: before trigger");
-> e1;
wait(e2.triggered()); //等待e2触发才会继续执行
$display("1: after trigger");
end
initial begin
$display( "2: before trigger");
-> e2;
wait(e1.triggered()); //等待e1触发才会继续执行
$display("2: after trigger");
end
4.2 semaphore
semaphore(旗语)用于对同一资源的访问,一般用于多资源访问请求时但只允许由单一驱动时(只有一把车钥匙,多个人都想开车,但同一时间只有一个人能开上车)。
示例:
program automatic test()
semaphore sem; //声明
initial begin
sem = new(1); //分配1个钥匙
fork
sem_1();
sem_2();
join
end
task sem_1();
sem.get(1); //获取钥匙
#100ns;
sem.put(1); //放回钥匙
endtask
task sem_2();
#10ns;
sem.get(1); //100ns等sem_1返回钥匙后才能拿到钥匙
#100ns;
sem.put(1);
endtask
endprogram
4.3 mailBox
mailBox可以用于传递线程之间的信息,用于充当SV中的FIFO。
mailBox和队列类似,但mailBox需要通过new()来例化,若不指定size,则其容量为无限大。
put() : 可以把数据放入mailBox里。(阻塞,如果mailBox为满,则方法会阻塞)
get() : 从mailBox中移除数据。(阻塞,如果mailBox为空,则方法会阻塞)
peek() : 获得mailBox的信箱的数据拷贝而不移除,多用于线程之间的同步。可以将数据处理完后,再把数据删除。(阻塞)
以及try_put() 、try_get() 和try_peek()等非阻塞方法。
示例:
program automatic bounded;
mailBox mbx; //声明
initial begin
mbx = new(l); //例化 指定大小
fork
// Producer线程
for (int i=1; i<4; i++) begin
$display("Producer: before put(%0d)", i);
mbx.put(i); //放入数据
$display("Producer: after put(%0d)", i);
end
// Consumer线程
repeat(4) begin
int j;
#lns mbx.get(j); //拿出数据
$display("Consumer:after get(%0d)", j);
end
join
end
endprogram
mailBox和队列的区别