systemverilog线程及线程间的通信(二)

 
 

1. 旗语 semaphore

如果把进程执行看成“开车”这个行为,开车需要钥匙,旗语就类似于“钥匙”。一个线程执行要先申请钥匙(旗语),如果只有一个钥匙(旗语)并且被其他线程申请了,那么这个线程阻塞。当那个线程执行完了,返回钥匙(旗语),被阻塞的线程可以再申请钥匙(旗语)。

旗语可以用在多个进程共享资源的时候。

  1. new方法创建一个或多个旗语

  2. get获取一个或多个旗语,获取失败阻塞

  3. put返回一个或多个旗语

  4. try_get()试图获取旗语,但不阻塞。返回1表示有足够旗语,返回0表示旗语不够。

program automatic test;
    semaphore sem; //创建一个旗语
    initial begin
        sem = new(1); //分配一把钥匙
        run();
    end
    task run();
        sem.get(1); //申请一把钥匙;此时如果其他进程申请这个旗语的钥匙,则阻塞。
        ....       // 使用资源
        sem.put(1); //执行结束,返回钥匙
    endtask
endprogram

2. 信箱 mailbox

信箱用来再多个线程之间传递事务,比如生成器和驱动器之间。

信箱类似一个FIFO,先入先出。

如果向一个定容的满了的信箱里添加对象,会阻塞;向空的信箱取对象也会阻塞。

信箱是一种对象,需要实例化。

get、put、peak(peek取对象数据的拷贝,但不删除它)。

信箱中最好只放一种类型的数据。(虽然放不同的数据语法不会错)。

信箱其实是一个参数类,可以指定信箱中放数据的类型。

信箱中存的是句柄(指针),如果只new了一个对象,只是将它随机了许多次,那么存入mailbox的对象是一个。

initial begin
    mailbox mlb#(Transaction); //指定类型
    Transaction tr,tmp;
    repeat(10) begin
        tr = new(); // 在循环内创建对象
        assert(tr.randomize());
        mlb.put(tr); //存入mlb
    end
    repeat(10) begin
        mlb.get(tmp); //从mlb取
        tmp.show();
    end
end

2.1 使用定容信箱同步

class Transaction;
  rand bit[7:0] data;
  rand bit[7:0] addr;
endclass
class Generator;
  mailbox mlb;
  Transaction tr;
  function new(mailbox m);
    this.mlb= m;
  endfunction
  task run();
    repeat(5) begin   
      tr=new();
      assert(tr.randomize());
      mlb.put(tr);
      $display("@ %0t [gen] put a tr",$time);
    end 
  endtask
endclass
​
class Driver;
  mailbox mlb;
  Transaction tr;
  function new(mailbox m);
    this.mlb= m;
  endfunction
  task run();
  forever begin
      #10; //每延迟10ns,driver取一个数据,也可以同步到时钟沿@(posedge clk);
    mlb.get(tr);
    $display("@ %0t [drv] get a tr",$time);
    end
  endtask
endclass
​
program automatic tb;
Generator gen;
Driver drv;
mailbox mlb;
initial begin
    mlb=new(1);//定容信箱,容量1,如果参数为空,则mailbox不限容量
  gen=new(mlb);
  drv=new(mlb);
  fork // 并发执行
    gen.run();
    drv.run();
  join
end
endprogram 
// output 
@ 0 [gen] put a tr
@ 10 [drv] get a tr
@ 10 [gen] put a tr
@ 20 [drv] get a tr
@ 20 [gen] put a tr
@ 30 [drv] get a tr
@ 30 [gen] put a tr
@ 40 [drv] get a tr
@ 40 [gen] put a tr
@ 50 [drv] get a tr

从输出可以看到,put一个tr,阻塞put操作,然后get一个tr,再put如此循环。

2.2 使用信箱和事件来同步

program automatic tb;
event handshake; // 事件
mailbox mlb;
class Generator;
  ...
  task run();
    repeat(5) begin   
      ...
      mlb.put(tr);
      $display("@ %0t [gen] put a tr",$time);
      @handshake;  //生成完一个tr后阻塞。
    end 
  endtask
endclass
​
class Driver;
  ...
  task run();
  forever begin
    #10;
    mlb.get(tr);
    $display("@ %0t [drv] get a tr",$time);
  ->handshake; //获取完一个tr后触发事件
    end
  endtask
​
endclass
​
Generator gen;
Driver drv;
initial begin
    mlb=new(); //无限容量
  gen=new(mlb);
  drv=new(mlb);
  fork ... join_none
end
endprogram 
@ 0 [gen] put a tr
@ 10 [drv] get a tr
@ 10 [gen] put a tr
@ 20 [drv] get a tr
@ 20 [gen] put a tr
@ 30 [drv] get a tr
@ 30 [gen] put a tr
@ 40 [drv] get a tr
@ 40 [gen] put a tr
@ 50 [drv] get a tr

2.3 使用两个信箱同步

program automatic tb;
mailbox#(Transaction) mlb;
    mailbox#(int) m;  // 定义两个信箱
class Generator;
  Transaction tr;
  int i;
  task run();
    repeat(5) begin
      tr=new();
      assert(tr.randomize());
      mlb.put(tr);
      $display("@ %0t [gen] put a tr",$time);
        m.get(i); //m为空,阻塞
    end 
  endtask
endclass
​
class Driver;
  Transaction tr;
  int i;
  task run();
  forever begin
#10;
    mlb.get(tr);
    $display("@ %0t [drv] get a tr",$time);
      m.put(i); // m不为空,触发了Generator中的语句。
    end
  endtask
endclass
​
Generator gen;
Driver drv;
initial begin
  mlb=new();
    m=new(); //新建信箱
  gen=new();
  drv=new();
  fork
    gen.run();
    drv.run();
  join
end
endprogram 
@ 0 [gen] put a tr
@ 10 [drv] get a tr
@ 10 [gen] put a tr
@ 20 [drv] get a tr
@ 20 [gen] put a tr
@ 30 [drv] get a tr
@ 30 [gen] put a tr
@ 40 [drv] get a tr
@ 40 [gen] put a tr
@ 50 [drv] get a tr

猜你喜欢

转载自blog.csdn.net/m0_38037810/article/details/125022272