SystemVerilog 第7章 线程以及线程间的通信
7.1 线程的使用
-
进程和线程的区别
每个进程包含一个在独有存储空间里运行的程序;线程的量级要比进程小,存储空间可共享
SV绿皮书中介绍的很简略,这里推荐前辈的一篇文章:线程与进程的一个简单解释
7.1.1 fork…join和begin…end
- begin…end 顺序执行;fork…join 并发执行,且等所有语句都执行完后才继续块后处理
7.1.2 fork…join_none
- fork…join_none 块在调度其子线程时,父线程继续执行
7.1.3 fork…join_any
- fork…join_any 块对其子线程调度,完成一个语句之后,父线程继续执行
7.1.4 在类中创建线程
- 即在class中定义带有fork…join的方法
7.1.5 动态线程
- 即使用fork…join_none
7.1.6 线程中的自动变量
- 在并发线程中务必使用自动变量来保存数据
7.1.7 等待衍生线程
- wait fork; //等待所有子线程结束
7.1.8 线程间共享变量
7.2 停止线程
7.2.1 停止单个线程
- disable fork块名;
7.2.2 停止多个线程
- 会把方法线程中调用的子threads一并disable掉
- disable fork; //作用域为其所在的fork语句块内且disable执行前就已经产生的线程
7.2.3 disable多次调用的task
-
disable task名 //若在task内部disable该task,则:
①停止该任务启动的线程
②停止调用改任务产生的线程
7.3 线程间的通信
7.4 event
- event在SV中是同步对象的句柄,可以直接作为参数传递给子程序
- 声明和使用
//声明
event event_name;
//使用1
@event_name;
->event_name;
//使用2
wait(event_name.triggered);
->event_name;
7.4.1 边沿阻塞
- 先@(event);后-> event
- @是等边沿信号
7.4.2 等待事件触发
- wait(event.triggered)和-> event不分先后
- wait是等电平信号为真
7.4.3 循环中使用事件
7.4.4 传递事件
7.4.5 等待多个事件
- for循环中等待每个事件+wait fork; (仅适用于按顺序完成)
- 记录事件触发的个数+wait个数达标
- 记录线程运行的个数+等待线程数为零
- wait_order(a,b,c); //等待a,b,c三个event顺序发生
7.5 旗语(semaphore)
- 声明和使用格式
//声明&例化
semaphore sem;
sem=new(1);
//使用
sem.get(1):
sem.put(1);
sem.try_get(1);
7.6 mailbox
- mailbox可以看作一个有源端和收端的FIFO
- new()可以设置容量上限:0或缺省为无限容量,大于零的数值即为上限
- 用mail发送transaction时务必在循环内部实例化transaction对象
- 声明和使用格式
//声明&例化
mailbox mbx;
mbx = new();
//使用
sem.put(tr):
sem.get(tr);
sem.peek(tr); //只复制不取出
7.6.1 测试平台里的信箱
-
class中
①声明mailbox句柄
②在new()函数中获取对应“mailbox对象”
③在方法中使用mailbox进行数据传递 -
TB中
①声明mailbox, class句柄
②实例化mailbox,并在实例化class的时候传入mailbox
③调用class的方法
7.6.2 定容信箱
- new()的时候指定信箱上限的信箱,可以当作缓冲器用作线程同步
7.6.3 异步线程间的信箱通信
- 同步异步线程的方法有:
event、变量、旗语、mailbox+peek、两个mailbox
7.6.4 定容信箱+peek同步线程
- 先用peek()获取数据进行处理,处理结束后再使用get()取出数
- 实例mailbox时候要指定容量为1(new(1))
7.6.5 event同步线程
- mbx.put(tr);之后使用@event;
- mbx.get(tr);之后使用->event;
- 使用wait的话要确保循环一个cycle时间要向前推进,否则零延时下wait不会触发新的判断
7.6.6 两个信箱同步线程
- 一个信箱用于发送信号;一个信箱用于将发送的信号传回
7.7 搭建通信线程TB
7.7.1 agent(事务处理器)
class Agent;
mailbox gen2agt, agt2drv;
Transaction tr;
function new();
this.gen2agt=gen2agt;
this.agt2gen=agt2gen;
endfunction
task run();
forever begin
gen2agt.get(tr);
...
agt2drv.put(tr);
end
endtask
endclass
7.7.2 配置类
- 配置类允许你在每次仿真时对系统的配置进行随机化
class Config;
bit [31:0] run_for_n_trans;
constraint reasonable {
run_for_n_trans inside {[1:1000]};
}
endclass
7.7.3 环境类
- 环境类时TB的测试环境对应的class
class Environment;
Config cfg;
Generator gen;
Agent agt;
Driver drv;
Monitor mon;
Checker chk;
Scoreboard scb;
mailbox gen2agt, agt2drv, mon2chk;
extern function new();
extern function void gen_cfg();
extern function void build();
extern task run();
extern task wrap_up();
endclass
function Environment::new();
cfg = new();
endfunction
function Environment::cfg();
assert(cfg.randomize);
endfunction
function Environment::build();
gen2agt = new();
agt2drv = new();
mon2chk = new();
gen = new(gen2agt);
agt = new(gen2agt,agt2drv);
drv = new(agt2drv);
mon = new(mon2chk);
chk = new(mon2chk);
scb = new();
endfunction
task Environment::run();
fork
gen.run(cfg.run_for_n_trans);
agt.run();
drv.run();
mon.run();
chk.run();
scb.run(cfg.run_for_n_trans);
join
endtask
task Environment::wrap_up();
fork
gen.wrap_up();
agt.wrap_up();
drv.wrap_up();
mon.wrap_up();
chk.wrap_up();
scb.wrap_up();
join
endtask
7.7.4 测试程序
- 测试代码放到一个program块中
program automatic test;
Environment env;
initial begin
env = new();
env.gen_cfg();
env.build();
env.run();
env.wrap_up();
end
endprogram