SystemVerilog 第7章 线程以及线程间的通信

SystemVerilog 第7章 线程以及线程间的通信


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

SystemVerilog 第7章 线程以及线程间的通信

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
上一篇:《Git与Github使用笔记》第13章 Pull Request的使用


下一篇:基于FPGA的异构计算在多媒体中的应用