SV系统验证—第七课:线程的控制和同步(一)

目录

1. 线程的使用

1.1 什么是线程

1.2 线程的概念澄清

2. 线程的控制

​编辑2.1 fork...join

2.2 fork...join_any

2.3 fork...join_none

2.4 等待所有衍生程序

2.5 停止单个线程

2.6 停止多个线程

2.7 停止被多次调用的任务

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延时之后禁止了该任务,而由于三个线程均是“同名”线程,因此这些线程都被禁止了,最终也都没有完成
    评论
    添加红包

    请填写红包祝福语或标题

    红包个数最小为10个

    红包金额最低5元

    当前余额3.43前往充值 >
    需支付:10.00
    成就一亿技术人!
    领取后你会自动成为博主和红包主的粉丝 规则
    hope_wisdom
    发出的红包
    实付
    使用余额支付
    点击重新获取
    扫码支付
    钱包余额 0

    抵扣说明:

    1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
    2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

    余额充值