【IC验证】systemverilog_线程的控制、同步与通信
systemverilog_线程的控制、同步与通信
一.线程
1.定义
独立运行的程序。
2.特性
(1)每个always过程块和initial过程块都是相互独立的线程;
(2)每个线程呈现树状结构,父线程可以开辟多个子线程;
例子:
fork
begin
$display("Block one\n");
# 20;
end
begin
$display("Block two\n");
@eventA;
end
join
解释:fork join内部包含两个并行运行的begin end块,即fork join内部包含两个线程;
(3)线程可以持续运行(如always过程块),也可以提前结束(如initial过程块);
3.分类
(1)串行线程
语法:begin end
特点:串行执行
(2)并行线程
语法:fork join, fork join_any, fork join_none
特点:并行执行
二.并行线程
(1)详述
链接: link
(2)例子:线程树
代码:
module tb;
task automatic exec(int id,int t);
$display("%0t exec[%0d] entered",$time,id);
#(t*1ns);
$display("%0t exec[%0d] exited",$time,id);
endtask
initial begin:proc1
exec(1,25);
end
initial begin:proc2
fork
exec(2,10);
exec(3,20);
join_any
fork
exec(4,30);
exec(5,40);
join_none
end
endmodule
线程示意图:

结果:

三.线程的结束
(1)wait fork
(2)disable fork
待补充
四.线程的控制(时序控制)
(1)简介
sysytemverilog可以通过使用事件触发、延时和等待实现时序控制;
(2)具体方法
(a) 事件触发
语法:
@(触发事件) 执行代码;
例子:
@(data1) out = in;
@(posedge clk) out = in;
(b) 延时
语法:
#(延迟时间) 执行代码;
例子:
#10 out = in;
(c) 等待
语法:
wait(条件) 执行代码;
例子:
wait(data.size>0) out = in;
注意:
等待也可以通过事件触发的形式实现;
五.线程的同步与通信
1.概述
目的:线程之间需要进行同步和交换数据;
2.事件
(1)概述
定义:
事件就是一个信号标志,多个线程可以等待这个信号,当信号被触发,等待这个时间的线程就可以继续执行。
作用:
协调并行线程的操作顺序,保证某些线程在其他操作完成后才开始执行。
(2)语法
//定义
event 事件名;
//触发事件
-> 事件名;
//等待事件触发
@事件名;
wait(事件名.triggered);
(3)注意
(a)event声明的是句柄(地址);
(b)事件触发是一个瞬时脉冲;
(c)@和wait
因为事件触发是一个脉冲信号,所以等待边沿触发**@和等待电平触发wait**是等效的;
(4)例子
(a)event实现的例子
代码:
module tb1;
task automatic wait_event(event e,input string name);
$display("@%0t Start waiting event %s",$time,name);
@e;
//wait(e.triggered);
$display("@%0t Finish waiting event %s",$time,name);
endtask
event e1,e2,e3;
initial begin
fork
wait_event(e1,"e1");
wait_event(e2,"e2");
wait_event(e3,"e3");
join
end
initial begin
fork
begin #10 ->e1; end
begin #20 ->e2; end
begin #30 ->e3; end
join
end
endmodule
结果:

(b)@(事件名)和wait(事件名)的相同
i 情况一:在不同时刻,先等待事件,后触发事件,事件均能被捕捉到;
代码:
module tb3;
event e1,e2;
initial begin
#5;
$display("@%t:Start waititng event %s",$time,"e1");
@(e1);
$display("@%t:Finish waiting event %s",$time,"e1");
end
initial begin
#5;
$display("@%t:Start waititng event %s",$time,"e2");
wait(e2);
$display("@%t:Finish waiting event %s",$time,"e2");
end
initial begin
#10;
$display("@%t:Start trig event %s",$time,"e1");
-> e1;
$display("@%t:Finish trig event %s",$time,"e1");
end
initial begin
#10;
$display("@%t:Start trig event %s",$time,"e2");
-> e2;
$display("@%t:Finish trig event %s",$time,"e2");
end
endmodule
结果:

说明:
事件均在第5个时间单位开始等待触发,而事件均在第10个时间单位进行触发,事件均被捕捉到;
ii 情况二:在不同时刻,先触发事件,后等待事件,事件均不能被捕捉到;
module tb3;
event e1,e2;
initial begin
#5;
$display("@%t:Start trig event %s",$time,"e1");
-> e1;
$display("@%t:Finish trig event %s",$time,"e1");
end
initial begin
#5;
$display("@%t:Start trig event %s",$time,"e2");
-> e2;
$display("@%t:Finish trig event %s",$time,"e2");
end
initial begin
#10;
$display("@%t:Start waititng event %s",$time,"e1");
@(e1);
$display("@%t:Finish waiting event %s",$time,"e1");
end
initial begin
#10;
$display("@%t:Start waititng event %s",$time,"e2");
wait(e2);
$display("@%t:Finish waiting event %s",$time,"e2");
end
endmodule
结果:

说明:
事件均在第5个时间单位进行触发,而事件均在第10个时间单位才开始等待,事件均未被捕捉到;
iii 情况三:在同一时刻,先等待事件,后触发事件,事件均能被捕捉到;
代码:
module tb3;
event e1,e2;
initial begin
//#10;
$display("@%t:Start waititng event %s",$time,"e1");
@(e1);
$display("@%t:Finish waiting event %s",$time,"e1");
end
initial begin
//#10;
$display("@%t:Start waititng event %s",$time,"e2");
wait(e2);
$display("@%t:Finish waiting event %s",$time,"e2");
end
initial begin
//#5;
$display("@%t:Start trig event %s",$time,"e1");
-> e1;
$display("@%t:Finish trig event %s",$time,"e1");
end
initial begin
//#5;
$display("@%t:Start trig event %s",$time,"e2");
-> e2;
$display("@%t:Finish trig event %s",$time,"e2");
end
endmodule
结果:

说明:
事件均在第0个时间单位等待触发和进行触发,且等待触发过程块在进行触发过程块之前,事件均被捕捉到;
iv 情况四:在同一时刻,先触发事件,后等待事件,事件均未被捕捉到;
代码:
module tb3;
event e1,e2;
initial begin
//#5;
$display("@%t:Start trig event %s",$time,"e1");
-> e1;
$display("@%t:Finish trig event %s",$time,"e1");
end
initial begin
//#5;
$display("@%t:Start trig event %s",$time,"e2");
-> e2;
$display("@%t:Finish trig event %s",$time,"e2");
end
initial begin
//#10;
$display("@%t:Start waititng event %s",$time,"e1");
@(e1);
$display("@%t:Finish waiting event %s",$time,"e1");
end
initial begin
//#10;
$display("@%t:Start waititng event %s",$time,"e2");
wait(e2);
$display("@%t:Finish waiting event %s",$time,"e2");
end
endmodule
结果:

说明:
事件均在第0个时间单位等待触发和进行触发,且进行触发过程块在等待触发过程块之前,事件均未被捕捉到;
(c)@(事件名)和wait(事件名.triggered)的不同
上述情况四(在同一时刻,先触发事件,后等待事件),wait(事件名.triggered)能够捕捉到事件;
代码:
module tb3;
event e1,e2;
initial begin
//#5;
$display("@%t:Start trig event %s",$time,"e1");
-> e1;
$display("@%t:Finish trig event %s",$time,"e1");
end
initial begin
//#5;
$display("@%t:Start trig event %s",$time,"e2");
-> e2;
$display("@%t:Finish trig event %s",$time,"e2");
end
initial begin
//#10;
$display("@%t:Start waititng event %s",$time,"e1");
@(e1);
$display("@%t:Finish waiting event %s",$time,"e1");
end
initial begin
//#10;
$display("@%t:Start waititng event %s",$time,"e2");
wait(e2.triggered);
$display("@%t:Finish waiting event %s",$time,"e2");
end
endmodule
结果:

说明:
事件均在第0个时间单位等待触发和进行触发,且进行触发过程块在等待触发过程块之前,wait(事件名.triggered)能捕捉到事件;
3.旗语(semaphore)
(1)作用
实现对共享资源的访问控制;
(2)说明
通俗举例:
一个资源必须通过钥匙才能访问,这个资源配了多把钥匙(比如3把,旗语名.new(3)),只要有其中的任意2把(也可以设置为其他值)就可以访问;
第一个人先拿走了两把钥匙(旗语名.get(2))对该资源进行访问,而第二个人也想访问该资源就请求获取两把钥匙(旗语名.get(2)),此时只剩下了一把闲余钥匙,所以他就需要一直等待第一个人至少归还1把钥匙(旗语名.put(1))以后才能访问该资资源;
但是当第二个人请求获取两把钥匙,可是闲余钥匙数小于需求数,此时不希望第二个人一直等着(这个人可以去干别的事),所以他可以通过使用旗语名.try_get(钥匙数),该方法会有一个返回值告诉这个人是否成功取到钥匙(取到返回1,没有取到返回0);
(3)语法
//声明
semaphore 旗语名;
//实例化
旗语名 = new(钥匙数(默认值为0));
//阻塞式获取钥匙
旗语名.get(获取钥匙数(默认值为1));
//非阻塞获取钥匙
旗语名.try_get(获取钥匙数(默认值为1));
//放入钥匙
旗语名.put(放入钥匙数(默认值为1));
(4)例子
(a)旗语的基本操作
代码:
module tb4;
semaphore sem;
initial begin
sem = new(3);
end
initial begin
#10;
$display("@time:%0t:A start get key",$time);
sem.get(2);
$display("@time:%0t:A finish get key",$time);
#20;
$display("@time:%0t:A start put key",$time);
sem.put(2);
$display("@time:%0t:A finish put key",$time);
end
initial begin
#20;
$display("@time:%0t:B start get key",$time);
sem.get(2);
$display("@time:%0t:B finidh get key",$time);
end
endmodule
结果:

(b)try_get
代码:
module tb5;
semaphore sem;
initial begin
sem = new(3);
end
initial begin
#10;
$display("@time:%0t: start get key",$time);
if(sem.try_get(4))
$display("@time:%0t:Success get key",$time);
else
$display("@time:%0t:Fialed get key",$time);
end
endmodule
结果:

(5)注意
(a)取钥匙可以请求取任意把数(get/try_get),只是取得过多很容易取失败;
(b)放入钥匙可以放入任意把钥匙,甚至可以超过取的把数,也可以超过最开始该资源配备的把数;
(c)旗语声明的也是句柄;
4.信箱
(1)作用
实现线程间的通信,相当于一个fifo队列;
(2)语法
//声明
mailbox 信箱名; //不限定成员变量类型
mailbox #(成员变量类型) 信箱名; //限定成员变量类型
//实例化
信箱名 = new(信箱大小(默认值为0,默认值表示不限定信箱大小));
//阻塞式写入、取出、读出数据
信箱名.put(写入数据);
信箱名.get(取出数据保存变量名);
信箱名.peek(读出数据保存变量名);
//非阻塞式写入、取出、读出数据
信箱名.try_put(写入数据); //有返回值,操作成功为1,操作失败为0
信箱名.try_get(取出数据保存变量名); //有返回值,操作成功为1,操作失败为0
信箱名.try_peek(读出数据保存变量名); //有返回值,操作成功为1,操作失败为0
//获取信箱内数据个数
信箱名.num(写入数据);
(3)例子
代码:
module tb6;
mailbox #(int) mai;
int put_data;
int data_peek;
int data_get;
initial begin
mai = new(10);
repeat(10)begin
put_data = $random();
mai.put(put_data);
$display("@time:%0t put data is %0d",$time,put_data);
end
end
initial begin
#5;
repeat(3)begin
mai.peek(data_peek);
$display("@time:%0t peek data is %0d",$time,data_peek);
end
end
initial begin
#10;
repeat(5)begin
mai.get(data_get);
$display("@time:%0t get data is %0d",$time,data_get);
end
end
initial begin
#10;
$display("@time:%0t data num is %0d",$time,mai.num());
end
endmodule
结果:

(4)注意
信箱声明的也是句柄。
更多推荐
所有评论(0)