[转]《SystemVerilog验证测试平台编写指南》学习笔记——线程以及线程间的通信(二)

一、线程间的通信

  • 测试平台中的所有线程都需要同步并交换数据。
  • 多个线程可能会同时访问同一资源。
  • 在最高的层面上,线程需要彼此交换数据,例如从发生器传递给代理的事务对象。
  • 所有这些数据交换和控制的同步被称为线程间通信(IPC),在SV中可以使用事件、旗语和信箱来完成。

二、事件

Verilog事件可以实现线程的同步,一个线程总是要等待一个带@操作符的事件。这个操作符是边沿敏感的,其他的线程可以通过->操作符来触发事件,解除对第一个线程的阻塞。
在SV中,事件成为了同步对象的句柄,可以传递给子程序。允许在对象间共享事件,而不用把事件定义为全局的。

1、在事件的边沿阻塞

event e1,e2;
initial begin
	$display("@%0t: 1: before trigger", $time);
	-> e1;
	@e2;
	$display("@%0t: 1: after trigger", $time);
end

initial begin
	$display("@%0t: 2: before trigger", $time);
	-> e2;
	@e1;
	$display("@%0t: 2: after trigger", $time);
end

输出结果:

@0: 1: before trigger
@0: 2: before trigger
@0: 1: after trigger

/*
第一个初始化块启动,触发e1事件,然后阻塞在另一个事件上,
第二个初始化块启动,触发e2事件(唤醒第一个块),然后阻塞在第一个事件上。
但是因为第一个事件是一个零宽度的脉冲,所以第二个线程会因为错过第一个事件而被锁住。
*/

2、等待事件的触发

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

event e1,e2;
initial begin
	$display("@%0t: 1: before trigger", $time);
	-> e1;
	wait(e2.triggered());
	$display("@%0t: 1: after trigger", $time);
end

initial begin
	$display("@%0t: 2: before trigger", $time);
	-> e2;
	wait(e1.triggered());
	$display("@%0t: 2: after trigger", $time);
end

输出结果:

@0: 1: before trigger
@0: 2: before trigger
@0: 1: after trigger
@0: 2: after trigger

3、在循环中使用事件

可以使用事件来实现两个线程的同步,如果在循环中使用wait(handshake.triggered()),一定确保在下次等待之前时间可以向前推进。否则你的代码将进入一个零时延循环,原因是wait会在单个事件触发器上反复执行。

等待事件导致零时延循环

forever begin
	//这是一个零时延循环
	wait(handshake.triggered());
	$display("Received next event");
	process_in_zero_time();
end

等待事件的边沿

forever begin
	//这里避免了零时延循环
	@handshake;
	$display("Received next event");
	process_in_zero_time();
end

如果需要在同一时刻发送多个通告,那就不应该使用事件,而应该使用其他内嵌排队机制的线程通信(IPC)方法,如旗语或者信箱。

4、传递事件

SV中的事件可以像参数一样传递给子程序。

把事件传递给构造器

class Generator;
	event done;
	function new(event done);		//从测试平台中传来事件
		this.done = done;
	endfunction
	
	task run();
		fork
			begin
				...		//创建事务
				->done;		//告知测试程序任务已完成
			end
		join_none
	endtask
endclass

program automatic test;
	event gen_done;
	Generator gen;
	initial begin
		gen = new(gen_event);		//测试程序实例化
		gen.run();		//运行事务处理器
		wait(gen_done.triggered());		//等待任务结束
	end
endprogram

5、等待多个事件

使用wait fork等待多个线程

event done[N_GENERATORS];
initial begin
	foreach(gen[i]) begin
		gen[i] = new();		//创建N个发生器
		gen[i].run(done[i])		//使它们开始运行
	end
	//通过等待每个事件来等待所有发生器完成
	foreach(gen[i])
		fork
			automatic int k = i;
			wait(done[k].triggered());
		join_none
	wait fork;		//等待所有触发事件完成
end

通过对触发事件进行计数来等待多个线程

event done[N_GENERATORS];
int done_count;
initial begin
	foreach(gen[i]) begin
		gen[i] = new();		//创建N个发生器
		gen[i].run(done[i])		//使它们开始运行
	end
	//通过等待每个事件来等待所有发生器完成
	foreach(gen[i])
		fork
			automatic int k = i;
			begin
				wait(done[k].triggered());
				done_count++;
			end
		join_none
	wait (done_count == N_GENERATORS);		//等待所有触发事件完成
end

使用线程计数来等待多个线程

class Generator;
	static int thread_count = 0;
	task run();
		thread_count++;		//启动另一个线程
		fork
			begin
				...
				//当工作完成时,对线程数目减少计数
				thread_count--;
			end
		join_none
	endtask
endclass

Generator gen[N_GENERATORS];
initial begin
	foreach(gen[i]) begin
		gen[i] = new();
		gen[i].run();
	end
	wait(Generator::thread_count == 0);
end	

---------------------
作者:煎丶包
来源:CSDN
原文:https://blog.csdn.net/qq_39794062/article/details/113096156
版权声明:本文为作者原创文章,转载请附上博文链接!
内容解析By:CSDN,CNBLOG博客文章一键转载插件

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值