SystemVerilog------线程的管理


前言

本文主要介绍System Verilog中的线程控制以及线程间通信。


一、线程的使用

verilog对语句有两种分组方式,使用begin end或者fork join,begin end中的语句会以顺序方式执行,而fork join中的语句则是以并发的方式执行。而system verilog中引入了两种新的创造线程的方法:fork join_any 和fork join_none。

1.1、线程的创建

1.1.1、fork…join

必须等块内所有语句执行完才继续进行块内后续的处理。

1.1.2、fork…join_none

在调度其块内语句的时候,父线程继续执行。

1.1.3、fork…join_any

当其块内第一个语句完成之后,父线程才会继续执行,其他挺纯的线程也得以继续。

1.1、线程中的自动变量:

如果使用循环来创建线程时,再进入下一轮循环前没有保存变量值,将会导致后面的调用会覆盖前面调用的值,所以在并发线程中务必使用自动变量来保存数值。
在verilog-1995中,如果在测试程序的多个地方调用同一个任务,由于任务里的局部变量会使用共享的静态存储区,所以不同线程之间会窜用这些局部变量。在verilog-2001中可以指定这些任务、函数、模块使用自动存储,从而迫使仿真器使用堆栈区存储局部变量。
在sysetm verilog中,模块和program块中的子程序缺省情况下仍然使用静态存储,如果要使用静态存储,需要在程序语句中声明automatic关键词或者使用automatic关键词声明变量。

1.2、等待子线程:

fork join_none语句会生成多个子线程,有的线程运行时间比较长,可以使用wait_fork来等待所有的子线程结束。

1.3、线程间共享变量

在一个类内部的子程序里,可以使用局部变量、类变量或者程序里定义的变量。如果某个变量未被声明,SV会到更高层的作用范围中进行寻找,直到找到匹配的声明。有时最内层变量忘记声明,使得两部分代码无意间共享了同一变量,可能会导致难以发现的漏洞。

1.4、停止线程

1.4.1、多个线程的停止

通过使用disable语句完成对特定线程的停止。在时延#100之后如果还未等待到a==b,则会打印错误信息并关闭剩余线程。

fork 
	begin
		fork : a1
			begin 
				#100;
				$display("error info\n");
			end 
			begin 
				wait(a==b);
				$display("info\n");
		join_any
	disable a1;
join_none
1.4.2、单个线程的停止

disable语句可以停止一个署名块中的所有线程,SV通过使用disable fork语句能够停止从当前线程中衍生出来的所有子线程。

initial begin
	$display("info0\n");                        //线程0
	fork                                        //线程1
		begin             
			$diplay("info1\n");                 //线程2
			fork 							    //线程3
				$display("info2\n");            //线程4
			join 
			#10 disable_fork;                   //停止线程1-4
		end 
	join
end 

线程0在带有disable的fork join块之外,所以不受影响。
也可使用带标号的disable停止线程,这样可以明确指定希望停止的线程名称:

fork 
	begin:a1
		#10;
		$display("info0\n");
	end
	#2 disable a1; 
join
1.4.3 禁止被多次调用的任务

如果在某个任务内部禁止该任务,也会停止所有由该任务启动的线程。如果该任务已经被多个线程调用,禁止其中一个将导致他们全部被禁止。

二、线程间通信

2.1 事件

verilog通过->触发事件,@等待事件。

event e1,e2;
->e1; //触发e1
@e2;  //等待e2

可以使用电平敏感的wait(e1.triggered())来代替边沿敏感的阻塞语句@e1。
如果事件在当前时间步已经被触发,则不会引起阻塞,否则会一直等到事件被触发为止。

2.2 旗语

使用旗语可以实现对同一资源的访问控制;使用new方法可以创建旗语,并为其分配固定的钥匙数量;
使用旗语的进程必须先获得钥匙,才可访问资源;旗语的钥匙数量可以有多个,等待旗语的进程也可以有多个;旗语的等待队列是先进先出的,即先排队等待旗语的将优先得到钥匙;

旗语操作
get操作可以获取一个或者多个钥匙(阻塞);
put可以返回一个或者多个钥匙;
try_get()方法是非阻塞的,会立即返回值,返回为1表示有足够多的钥匙,返回为0则表示钥匙不够。

semaphore sem; //创建旗语
sem=new(1);  //分配一个钥匙
sem.get(1);  //获取一个钥匙
sem.put(1);  //归还一个钥匙

ps1:
返回的钥匙可以比取出来的要多;
ps2:
当程序需要获取或者返回多个钥匙的时候,如下情形:
钥匙只剩一把,如果先有一个线程请求两把而因为钥匙不够而阻塞,这是第二个线程出现并且只请求一把钥匙,那么这时一把钥匙的请求会被优先接受,这里就会忽略先进先出的规则。

信箱

信箱可以简单地看成一个具有源端和收端的FIFO,源端把数据放进信箱,收端则从信箱中获取数据。

mailbox mbx;  //声明句柄
mbx=new(size);  //创建信箱,参数缺省值为0,表示容量无限大
mbx.put(tr);  //写入事务
mbx.get(tr); //获取事务
mbx.peek(tr); //获取信箱中事务的拷贝而不移除

缺省情况下,信箱没有数据类型,允许在其中放入任何混合类型的数据,但推荐一个信箱只放入一种数据类型。
信箱中可以放入句柄,但不能是对象。
需要注意的一点是,再循环内构建一个对象,并在循环内进行对象随机化并写入信箱,由于信箱中保存的都是指向同一个对象的句柄,所以最终信箱中获取句柄之后只能见到最后一组随机值。解决方法是把对象的创建移入循环内。

线程同步
a、使用定容信箱和peek

消费方使用一个内建的peek()方法来探视信箱里的数据而不将其移除,此时由于信箱满所以生产方会一直阻塞,直到消费方处理完数据之后将数据从信箱移除,生产方才能继续写入数据。

b、使用信箱和事件:

在生产方把数据放入信箱后使用事件去阻塞,消费方在处理完数据后再触发事件。

c、使用两个信箱:

可以使用另一个信箱将消费方的完成信息发回给生产方完成同步。这个返回信息可以是任意值,也可以是与发送数据有关的特殊值,方便用于调试。

总结

本文主要摘要简述了SystemVerilog对于线程管理的一些语法和技巧。

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值