什么是线程
在SV中,可以认为 线程即独立运行的程序。 线程需要被触发,可以结束或者不结束。
举例:
在硬件module中的initial和always,都可以看做独立的线程,它们会在仿真0时刻开始,而选择结束或者不结束。
硬件模型的线程的特点
硬件模型中由于都是always语句块,所以可以看成是多个独立运行的线程,而这些线程会一直占用仿真资源,因为它们并不会结束。
验证环境中线程的特点
initial语句中例化的各个验证环境、验证对象,以及整个验证平台,可以在仿真过程中动态的创建或者销毁,因此验证环境的资源是动态的利用过程。
验证环境中的initial块有两种执行方式:
begin. ..end语句(以顺序方式执行)
fork. . .join语句(以并发方式执行)
并行方式语句有:
fork. . . join(可以开辟多个子线程,即触发多个程序的执行,这些被触发的程序可以被看做fork join语句的子线程)
fork.. . join_any
ork. . . join_none
线程的概念
线程的执行轨迹是呈树状结构的,即任何的线程都应该有父线程。
子线程和父线程的关系:
父线程可以开辟若干个子线程,父线程可以暂停或者终止子线程。
当子线程终止时,父线程可以继续执行。
当父线程终止时,其所开辟的所有子线程都应当会终止。
并行线程
Verilog中的创建线程
顺序线程begin. . .end
并行线程fork.. . join(所有并行的线程都结束以后才会继续执行接下来的程序)
SV引入了两种新的创建线程的方法:(并行线程)
fork...join_any(等到任何一个线程结束以后就继续执行接下来的程序)
fork. .. join_none(不会等待其子线程而继续执行接下来的程序)
fork join语句
在进入fork 以后可以触发多个子线程运行, fork join需要等待所有并行的子线程都结束以后才会继续执行。
fork...join_any
在进入fork 以后可以触发多个子线程运行,fork...join_any等到其中一个最短的子线程结束以后就继续执行接下来的程序
fork. .. join_none
在进入fork 以后可以触发多个子线程运行,不会等待其子线程而继续执行接下来的程序。
注意:
fork.. .join_any和fork.. .join_none语句不需要等待全部子线程执行结束,但是在执行 fork.. .join_any和fork.. .join_none语句的同时,之前已经被触发的子线程,依然会在后台执行,直到它们执行结束。
fork join语句的示例:
fork
begin
$display( "First Block\n”);
# 20ns;
end
begin
$display ("Second Block\n”);
@eventA;
end
join
#fork join开辟了两个begin end子线程,这两个线程分别需要等待20ns和事件A被触发以后,才能分别结束
#从而,fork join语句块才能退出,执行下面的程序
initial begin
$display ("@%0t: start fork.. .join example",$time);
#10 $display("@%0t: sequential after #10",$time);
//fork join触发了四个子线程,需要不同的时间点完成打印操作
//最慢的时间为50ns。在fork join语句退出时,已经经历了10ns+50ns=60ns的仿真时间
//程序结束时。经历了60ns+80ns=140ns的仿真时间
fork
$display("@%0t: parallel start", $time) ;
#50 $display("@%0t: parallel after #50",$time);
#10 $display("@%Ot: parallel after #10", $time) ;
begin
#30 $display("@%0t: sequential after #30",$time);
#10 $display("@%0t: sequential after #10",$time) ;
end
join
$display("@%0t: after join", $time) ;
#80 $display("@%0t: finish after #80",$time);
end
wait fork或者disable fork
fork.. .join_any和fork.. .join_none语句结束后继续执行下面的程序,其一些未完成的子线程怎么处理。可以使用
wait fork:等待这些子程序全部完成
disable fork:停止这些子线程
task do_test;
fork
exec1) ;
exec2() ;
join_any
fork
exec3() ;
exec4();
join_none
wait fork; //等待所有子线程结束
// 在任务退出前,保证四个子线程都完成后,才退出任务。
endtask
task get_first( output int adr ) ;
//fork join_any分别触发了三个同名的子线程。
//每个子线程完成时间不一样,都会给adr参数赋值。
//任务是只要其中一个线程完成,就可以退出任务
fork
wait_device( 1. adr ) ;
wait_device( 7. adr ) ;
wait_device( 13, adr ) ;
join_any
//如果没有 disable fork,任务也可以退出,并且可以获得 wait_device( 1. adr )返回的变量
//后台的两个子线程有可能还在执行,甚至处于阻塞状态。
//这种还在等待的却已经不需要的子线程属于资源浪费
//在退出任务之前,通过 disable fork停止还在后台运行的不需要的子线程,
//保证退出任务时,资源被清理干净。
disable fork;
endtask
时序控制
SV可以用来实现阻塞的时序控制的方式:
延迟控制(#)
事件(event)等待或者完成边沿触发(@)
电平触发式赋值(wait)
1、延迟控制即通过#来完成。
#10 rega = regb;
2、事件(event)控制,完成边沿触发方式即通过@来完成。
@r rega = regb;
@(posedge clock) rega = regb;
3、wait语句也可以与事件或者表达式结合来完成。
real AOR[];
initial wait(AOR.size() > 0) ....;