SV多线程通信

1.线程的使用

  Verilog中对语句分组使用: begin...end(顺序执行) fork...join(并发执行)

  System Verilog中引入两种新的创建线程的方法: fork...join_any fork...join_none

  begin...end与fork...join可以相互嵌套。在begin...end内部语句串行执行,在fork...join内部语句并行执行,fork...join_any在其中内部任一语句执行完成后继续执行块外的内容,块内没执行完的继续执行。fork...join_none在其中内部语句执行时同步执行块外的内容。

 

2.event 

  事件是静态的同步对象句柄(可以像参数一样在子程序中传递),它用 来同步多个并发的进程,比如某个进程等待着事件,而另一个进程则触发这个事件。 事件的触发和阻塞等待

  触发: -> event_handle

  等待:@event_handle      or       wait(event_handle.triggered)                   @操作符是阻塞的,也就是说过了操作的时间点,如果没有捕捉到,那么就会被阻塞在那里, 直到下一次到来。如果没有下一次,就一直阻塞。 在SV中,引入了triggered属性,该属性可用于查询事件是否已经被触发,而不是只检测当 前时刻,线程只等待该结果,而不是在@处阻塞。

  特征:可以被赋值给其它事件;这样两个事件变量(句柄)会指向同一个对象,触发任意一个变量 就触发这个事件 

             可以传给队列,函数和任务

 1 event eve;  
 2 event tmp;
 3 
 4 initial begin  
 5     tmp = eve;
 6     #10 -> tmp;  
 7 end
 8 
 9 initial begin  
10     wait(eve.triggered);
11     $display("@%0t : event eve receive trigger",$time);  
12 end

@10 : event eve receive trigger  //执行结果

 

 1 program automatic test();
 2 
 3     event e1,e2;  
 4     initial begin
 5         $display("@%0d:1:before trigger",$time);
 6         -> e1;
 7         @e2;
 8         $display("@%0d:1:after trigger",$time); 
 9     end
10 
11     initial begin
12         $display("@%0d:2:before trigger",$time);
13         -> e2;
14         @e1;
15         $display("@%0d:2:after trigger",$time); 
16     end
17 
18 endprogram

因为触发是瞬态的,因此按先后顺序,只有 @e2是可以触发的,另一种就没了,仿真结果 如下: @0:1:before trigger @0:2:before trigger @0:1:after trigger 如果调换两个initial块的顺序,则输出: @0:2:before trigger @0:1:before trigger @0:2:after trigger 由此我们也可以知道timeslot中initial块 是有执行顺序的,顺序为自上而下。

 

 1 program automatic test();
 2     event e1,e2;
 3     initial begin
 4         $display("@%0d:1:before trigger",$time);
 5         -> e1;
 6         wait(e2.triggered);
 7         $display("@%0d:1:after trigger",$time);  
 8     end
 9 
10     initial begin
11         $display("@%0d:2:before trigger",$time);
12         -> e2;
13         wait(e1.triggered);
14         $display("@%0d:2:after trigger",$time); 
15 end
16 endprogram

因为e1先触发,因此wait (e1.triggered)先实现,先打印2再 打印1,因此仿真结果如下: @0:1:before trigger @0:2:before trigger @0:2:after trigger @0:1:after trigger

 

 1 module tb;  
 2     event a, b, c;  
 3     initial begin
 4         #10 -> a;
 5         #10 -> b;
 6         #10 -> c;
 7     end
 8     initial begin  
 9         wait_order (a,b,c)
10             $display ("Events were executed in the correct order");  
11         else
12             $display ("Events were NOT executed in the correct order !");
13     end  
14 endmodule

wait_order阻塞等待多个事件的触发,并且 要求这多个事件按照用户决定顺序触发。 wait_order可以和else一同使用,当多个事件 按顺序触发时,执行wait_order后的语句,否 则执行else后的语句。

Events were executed in the correct order //执行结果

 

3.mailbox

  Mailbox 是一种在进程之间交换消息的机制。数据可以通过一个进程发送到Mailbox, 然后由另一个进程获取。数据可以是任何有效的SystemVerilog数据类型,包括类 class数据类型。

 

 1 class transaction;  
 2     rand bit valid;
 3     rand bit [7:0] data;  
 4 endclass
 5 
 6 class generator;
 7     mailbox #(transaction) gen2drv;  
 8     transaction tr;
 9     function new (input mailbox #(transaction) gen2drv);
10     this.gen2drv=gen2drv;  
11 endfunction
12 
13 task gen(input int num);  
14     for(int i=0;i<num;i++) begin
15         tr=new();
16         assert(tr.randomize)  gen2drv.put(tr);
17         $display(“trans valid =%d”,tr.valid);
18         $display(“trans data =%d”,tr.data);
19         #1ns;  
20     end
21 endtask  
22 endclass
23 
24 class driver;
25     mailbox #(transaction) gen2drv;  
26     transaction tr;
27     function new (input mailbox #(transaction) gen2drv);
28         this.gen2drv=gen2drv;
29     endfunction  
30 
31     task run;
32         tr=new();  
33         while(1) begin
34             gen2drv.get(tr);
35             $display(“trans valid =%d”,tr.valid);
36             $display(“trans data =%d”,tr.data);
37         end
38     endtask 
39 endclass
40 
41 program test;
42     mailbox #(transaction) gen2drv;  
43     driver drv;
44     generator gen;  
45     initial begin
46         gen2drv=new();  
47         drv=new(gen2drv);  
48         gen=new(gen2drv);  
49         fork
50             gen.gen(5); 
51             drv.run;
52         join_any  
53     end
54 endprogram              

 可以理解为FIFO,空时读和满时写都会阻塞。try_*表示非阻塞,此外,get为取走,peek为复制走。

       如果为空时,去读,会挂起,直到有数据写入。

       new时,传参缺省或为0时,表示此mailbox是无边界的。传参表mailbox深度。new返回句柄,传参必须为正,负时会导致不确定后果。

       try_put,如果放入了,函数返回1,否则返回0。try_get,拿走了,返回1,数据类型不匹配,返回负1,空的返回0。try_peek类似。

       num得到此mailbox内的数据的个数。

       申明时,默认的mailbox无类型,可以接收各种数据,也可以申明是指定需要传输的数据类型,方便编译器发现类型不匹配。

 

4.semaphore

使用旗语可以实现对同一资源的访问控制。当测试平台中存在一个 资源,如一条总线,对应着多个请求方,而实际物理设计中只允许单一驱动时, 便可使用旗语。 在system verilog中一个线程如果请求“钥匙”而得不到,则会一直阻塞,多个阻塞 的线程会以先进先出的方式进行排队

  key的概念

       new时,传参缺省值为0,传参表示key的个数。

       put传参表示放回key的个数,缺省值1.

       get传参表示获得key的个数,缺省值1,不存在指定数目key时,进程会阻塞到对应key数量的出现。

       try_get无阻塞的获得指定数目的key,存在则返回1,否则返回0。

 

posted @ 2022-05-22 22:59  NBI  阅读(403)  评论(0)    收藏  举报