1. 旗语 semaphore
如果把进程执行看成“开车”这个行为,开车需要钥匙,旗语就类似于“钥匙”。一个线程执行要先申请钥匙(旗语),如果只有一个钥匙(旗语)并且被其他线程申请了,那么这个线程阻塞。当那个线程执行完了,返回钥匙(旗语),被阻塞的线程可以再申请钥匙(旗语)。
旗语可以用在多个进程共享资源的时候。
-
new方法创建一个或多个旗语
-
get获取一个或多个旗语,获取失败阻塞
-
put返回一个或多个旗语
-
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