目录
1. 线程的使用
1.1 什么是线程
- 线程即独立运行的程序;
- 线程需要被触发,可以结束或者不结束;
- 在module中的initial和always,都可以看做独立的线程,它们会在仿真0时刻开始,而选择结束或者不结束;
- 硬件模型中由于都是always语句块,所以可以看成是多个独立运行的线程,而这些线程会一直占用仿真资源,因为他们并不会结束;
- 软件测试平台中的验证环境都需要有initial语句块去创建,而在仿真过程中,验证环境中的对象可以创建和销毁,因此在软件测试端的资源占用是动态的;
- 如果按照软件的思维理解仿真,仿真中的各个模块首先显示独立运行的线程;
- 模块(线程)在仿真一开始便并行执行,除了每个线程会依照自身内部产生的事件来触发过程语句之外,也同时依靠相邻模块间的信号变化来完成模块之间的线程同步;
- 软件环境中的initial块对语句有两种分组方式,使用begin-end或fork-join;
- begin-end中的语句顺序执行,而fork-join中的语句并发执行;
1.2 线程的概念澄清
- 线程的执行轨迹是呈树状结构的,即任何的线程都应该有父线程;
- 父线程可以开辟若干个子线程,父线程可以暂停或者终止子线程;
- 当父线程终止时,其所开辟的所有子线程都应当会终止;
2. 线程的控制

2.1 fork...join
所有任务必须完成才能继续。
- 所有并行任务必须全部完成,才会继续执行后续代码。
- 如果任何一个任务运行时间过长,整个进程都会被阻塞。
2.2 fork...join_any
任意一个任务完成即可继续。
- 只要有一个任务完成,就可以继续执行后续代码,不会等待其他任务结束。
- 这种结构适用于只需要其中一个任务结果的场景,可以提高效率。
2.3 fork...join_none
不等待任何任务完成,直接继续。
- 程序不会等待任何任务完成,直接继续执行后续代码。
- 适用于不需要等待任务结果的场景,例如后台任务或异步处理。
答案:A,对于B,没有"...finsh after...",完成一个parallel start 的任务,直接跳出,然后完成after join_none,总共20ns,程序直接结束,故不能打印完全。C同理。
2.4 等待所有衍生程序
- 在SV中,当程序中的initial块全部执行完毕,仿真器就退出了;
- 如果希望等待fork块中的所有线程执行完毕再退出结束initial块,可以使用
wait fork
语句来等待所有子线程结束
task run_threads;
fork
check_trans(tr1); // 线程1
check_trans(tr2); // 线程2
check_trans(tr3); // 线程3
join_none
...
// 等待所有fork中的线程结束再退出task
wait fork;
endtask
2.5 停止单个线程
- 上述task结束的情况下,SV中父线程结束了,并不会主动回收它开辟的子线程,僵尸线程
- 建议在fork-join_none、fork-join_any开辟的线程,一旦认为它没有必要了,一定要给他打上名字,标记记号,方便后期主动disable
- 在使用了fork-join_any或者fork-join_none以后,可以使用disable来指令需要停止的线程
parameter TIME_OUT = 1000;
task check_trans(Transaction tr);
fork
begin
// 等待回应,或者达到某个最大时延
fork: time_block
begin
wait (bus.cb.addr == tr.addr);
$display("@%0t: Addr match %d", $time, tr.addr);
end
#TIME_OUT $display("@%0t: Error: timeout", $time);
join_any
disable time_block
end
join_none
endtask
这样,当folk...join_any 任意一个县城执行完毕后,另外一个线程就清理掉,不会在执行。
2.6 停止多个线程
- disable fork可以停止从当前线程中衍生出来的所有子线程
initial begin
check_trans(tr0); // 线程0
// 创建一个线程来限制disable fork的作用范围
fork // 线程1
begin
check_trans(tr1); // 线程2
fork // 线程3
check_trans(tr2); // 线程4
join
// 停止线程1-4,单独保留线程0
#(TIME_OUT/2) disable fork;
end
join
end
2.7 停止被多次调用的任务
- 如果给一个任务或者线程指明标号,那么当这个线程被调用多次以后,如果通过disabe去禁止这个线程标号,所有衍生的同名线程都将被禁止
task wait_for_time_out(int id);
if(id == 0)
fork
begin
#2;
$display("@%0t: disable wait_for_time_out", $time);
disable wait_for_time_out;
end
join_none
fork: just_a_little
begin
$display("@%0t: %m: %0d entering thread", $time, id);
#TIME_OUT;
$display("@%0t: %m: %0d done", $time, id);
end
join_none
endtask
initial begin
wait_for_time_out(0); // Spawn thread 0
wait_for_time_out(1); // Spawn thread 1
wait_for_time_out(2); // Spawn thread 2
#(TIME_OUT*2) $display("@%0t: All done", $time);
end
- 任务wait_for_time_out被调用三次,从而衍生了三个线程
- 线程0在#2延时之后禁止了该任务,而由于三个线程均是“同名”线程,因此这些线程都被禁止了,最终也都没有完成