学习目标:
SV绿皮书第七章:线程以及线程间的通信
学习内容:
1.模块(线程)在仿真一开始便并行执行,除了每个线程会按照自身内部产生的事件来触发过程语句块之外,也同时依靠相邻模块间的信号变化来完成模块之间的线程同步。
2.线程即独立运行的程序,线程需要被触发,可以结束或者不结束
3.硬件模型中由于都是always语句块,所以可以看成是多个独立运行的线程,而这些线程会一直占用仿真资源;
软件测试平台中的验证环境都需要由initial语句由去创建,而在仿真过程中,验证环境中的对象是可以创建或者销毁的,因此资源占用是动态的。
4.线程的执行轨迹是呈树状结构的,即任何的线程都应该有父线程
父类线程可以开辟若干个子线程,父线程可以暂停或者终止子线程
当子线程终止时,父线程可以继续执行
当父线程终止时,其所开辟的所有子线程都应当会终止
5.fork…join
initial begin
$display("@%0t:start fork ... join example",$time);
#10 $display("@%0t:sequential after #10",$time);
fork
$display("@%0t:parallel start",$time);
#50 $display("@%0t:parallel after #50",$time);
#10 $display("@%0t:parallel after #10",$time);
begin
#30 $display("@%0t:sequential after #30",$time);
#10 $display("@%0t:sequential after #10",$time);
end
join
$display("@%0t:after join",$time);
#80 $display("@%0t:finish after #80",$time);
end
//结果
@0:start fork ... join example
@10:sequential after #10
@10:parallel start
@20:sequential after #30
@40:sequential after #10
@50:parallel after #50
@60:after join
@140:finish after #80
6.fork join_any
initial begin
$display("@%0t:start fork ... join_any example",$time);
#10 $display("@%0t:sequential after #10",$time);
fork
$display("@%0t:parallel start",$time); //注意:这也是一条子线程
#50 $display("@%0t:parallel after #50",$time);
#10 $display("@%0t:parallel after #10",$time);
begin
#30 $display("@%0t:sequential after #30",$time);
#10 $display("@%0t:sequential after #10",$time);
end
join_any
$display("@%0t:after join_any",$time);
#80 $display("@%0t:finish after #80",$time);
end
//结果
@0:start fork ... join_any example
@10:sequential after #10
@10:parallel start
@10:after join_any
@20:parallel after #10
@40:sequential after #30
@50:sequential after #10
@60:parallel after #50
@90:finish after #80
7.fork join_none
initial begin
$display("@%0t:start fork ... join_none example",$time);
#10 $display("@%0t:sequential after #10",$time);
fork
$display("@%0t:parallel start",$time); //注意:这也是一条子线程
#50 $display("@%0t:parallel after #50",$time);
#10 $display("@%0t:parallel after #10",$time);
begin
#30 $display("@%0t:sequential after #30",$time);
#10 $display("@%0t:sequential after #10",$time);
end
join_none
$display("@%0t:after join_none",$time);
#80 $display("@%0t:finish after #80",$time);
end
//结果
@0:start fork ... join_none example
@10:sequential after #10
@10:after join_none //直接退出join_none,先执行这一条
@10:parallel start
@20:parallel after #10
@40:sequential after #30
@50:sequential after #10
@60:parallel after #50
@90:finish after #80
8.如果希望等待fork块中的所有线程执行完毕再退出结束initial块,我们可以使用wait join语句来等待所有子线程结束
9.可以使用disable来指定需要停止的线程。
如果你给某一个任务或者线程指明标号,那么当这个线程被调用多次以后,如果通过disbale去禁止这个线程标号,所有衍生的同名线程都将被禁止。
//disable fork可以停止从当前线程中衍生出来的所有子线程
initial begin
check_trans(tr0); //线程0
//创建一个线程来限制disable fork的作用范围
fork //线程1(父)
begin
check_tran(tr1); //线程2
fork //线程3
check_tran(tr2); //线程4
join
//停止线程1-4,单独保留线程0
#(TIME_OUT/2) disable fork;
end
join
end
10.所有这些数据交换和同步称之为线程间的通信(event、semaphore旗语、mailbox信箱)
11.event不需要new
线程可以通过->操作符来触发事件
12.电平敏感wait(a.triggered())来代替边沿敏感的阻塞语句@a,只要event被触发过,就可以防止引起阻塞
class car;
event e_start;
task launch();
->e_start;
$dispaly("car is launched");
endtask
task move();
wait(e_start.triggered);
$display("car is moving");
endtask
task drive();
fork
this.launch();
this.move();
join
endtask
endclass
13.event没有方法可以清除event.triggered的状态
如果要多次触发event A,可以使用@A来多次等待A
14.semaphore可以实现对同一资源的访问控制
15.semaphore的三个基本操作:
new()方法可以创建一个带单个或者多个钥匙的semaphore----new里面指定多少钥匙,默认一把,可以初始化为0
使用get()可以获取一个或者多个钥匙
put()可以返回一个或者多个钥匙
16.如果获取一个semaphore而不希望被阻塞,可以使用try_get()函数,返回1表示钥匙足够,返回0表示钥匙不够
17.semaphore的操作
program automatic test(bus_ifc.TB bus);
semaphore sem; //创建一个semaphore
initial begin
sem=new(1); //分配一把钥匙
fork
sequencer(); //产生两个总线事务线程
sequencer();
join
end
task sequencer;
repeat(2)
@bus.cb;
sendTrans(); //执行总线事务
endtask
task sendTrans;
sem.get(1); //获取总线钥匙
@bus.cb; //把信号驱动在总线上
bus.cb.addr<=t.addr;
...
sem.put(1); //处理完成时把钥匙返回,不返回会发生死锁
endtask
endprogram
18.对于线程间共享资源的使用方式,应该遵循互斥访问原则,否则将“线程不安全”
19.mailbox是一种对象,因此也需要使用new()来例化,例化时有一个可选的参数size来限定其存储的最大数量
信箱为满时,put()会阻塞---------使用非阻塞加try_put()、try_get()、try_peek()
peek()可以获取对信箱里数据的拷贝而不移除它
20.信箱的容量可以固定也可以不固定
信箱可以通过参数化指定其存储的数据类型
多个对象可以同时使用信箱存储或者获取数据
21.限定mailbox中元素的类型,mailbox #(type=T)
event | 最小信息量的触发 |
---|---|
semaphore | 共享资源的安全卫士 |
mailbox | 精小的SV原生FIFO |
问题:
学习时间:
4/14
早:9-11
中:15-17
晚:20-22