目录
一、问题
1.随机实验中,多次重新启动仿真,对比连续两次生成的随机数据是否相同?为什么?如何解决?
两次生成的随机数据相同;因为使用的是同一个种子;通过"vsim -novopt -solvefaildebug -sv seed random work.tb1"使用不同的种子来加载仿真,比较数据。
-solvefaildebug.:调试随机变量,如果仿真中随机化失败,这个会告诉为什么失败?约束冲突了
-sv_seed NUM :传递随机种子
2.仿真时传入不同的参数传递来完成对测试的选择
在命令窗口中添加额外的命令"+TESTNAME=testname" ,这里的
+TESTNAME=:仿真命令项,在由内部解析之后,testname 会被捕捉并且识别,例如你可以传递命令"+TESTNAME=chnl_burst_test" 来在仿真时运行测试 chnl_burst_test。
- clocking一定是上一拍的结果,不能用于组合逻辑,组合逻辑不能用clocking,在采样时也要延时1ns或迟后1ns
二、实验要求
- 随机约束测试:了解随机约束在类中定义方式、如何随机化对象、随机种子的使用方法、对象的产生
a. 按照重新定义的数据类chnl_trans 的约束来定义chnl_basic_test 类;
b. 将原本在 chnl_root_test 类中用来结束仿真的$finish()变迁到 generator 中;
- 之前通过agent向generator发送要发送的数据包个数,而generator控制发送的随机数据,现在将generator独立出来,自己控制其发送的数据个数与随机变量,依靠 generator被随机化的成员变量,进一步随机化 generator中的 chnl_trans对象
a. 将 gen 和 agent中组件的 mailbox 连接起来,方便 gen 与 agent 中 init 的数据通信;此时内存中最多有9个chnl_trans对象
b. 在 test 中的 do_config 对 gen[0]、 gen[1]与 gen[2]进行随机控制;
在 chnl burst test::do_config()任务中对三个 generator进行随机控制;
在 chnl fifo_ full test:do_ config()任务中对三个generator 进行随机控制
- 验证环境中添加monitor和 checker组件;initiator和chnl_monitor置于agent
中;agent,mcdt_monitor,checker与generator置于 test层中。
a. chnl_monitor 类和 mcdt_monitor 类各自的 mon_trans()方法中需要采集正确的数据,将它们写入 mailbox 缓存,同时将捕捉的数据也打印出来,便于我们的调试。
b. chnl_agent 中,参考如何例化的 initiator 对象,也对 chnl_monitor 对象开始例化、传递虚接口和使其运行。
c. chnl_checker 的任务 do_compare()中, 从 checker 自己的数据缓存mailbox 中分别取得一个输出端的采集数据和一个输入端的采集数据,继而将它们的内容进行比较,需要注意的是,输出端的缓存只有一个,而输入端的缓存有三个,需要考虑好从哪个输入端获取数据与输出端缓存的数据进行比对。
d. 在顶层环境 chnl_root_test 中。先要对 mcdt_monitor 和 chnl_checker 进行例化。传递虚接口,并且将 chnl_monitor和mcdt_monitor 的邮箱句柄分别指向chnl_checker 中的邮箱实例。
三、测试平台结构图1
四、代码分析
1. testbench
时间单位
`timescale 1ns/1ps// 单位/精度
信号
输入时钟和复位信号,其他信号由接口读入或读出
logic clk;
logic rstn;
产生时钟
initial begin
clk <= 0;
forever begin
#5 clk <= !clk;
end
end
产生复位
initial begin
#10 rstn <= 0;
repeat(10) @(posedge clk);
rstn <= 1;
end
接口chnl_intf与mcdt_intf
slave与dut之间的接口,四个传输的数据;
定义驱动drive的clocking与检测monitor的clocking
interface chnl_intf(input clk, input rstn);
logic [31:0] ch_data;
logic ch_valid;
logic ch_ready;
logic [ 5:0] ch_margin;
clocking drv_ck @(posedge clk);
default input #1ns output #1ns;
output ch_data, ch_valid;//对于initiator来说,发送数据和有效信号
input ch_ready, ch_margin;//接收ready和fifo满信号
endclocking
clocking mon_ck @(posedge clk);
default input #1ns output #1ns;
input ch_data, ch_valid, ch_ready, ch_margin;//对于监测器来说,监测所有信号
endclocking
endinterface
interface mcdt_intf(input clk, input rstn);
logic [31:0] mcdt_data;
logic mcdt_val;
logic [ 1:0] mcdt_id;
clocking mon_ck @(posedge clk);
default input #1ns output #1ns;
input mcdt_data, mcdt_val, mcdt_id;
endclocking
endinterface
导包并实例化接口与测试类
导包,实例化四个接口
import chnl_pkg3::*;
//实例化四个接口
chnl_intf chnl0_if(.*);
chnl_intf chnl1_if(.*);
chnl_intf chnl2_if(.*);
mcdt_intf mcdt_if(.*);
//实例化要运行的测试类
chnl_basic_test basic_test;
chnl_burst_test burst_test;
chnl_fifo_full_test fifo_full_test;
chnl_root_test tests[string];//测试类名的稀疏矩阵
string name;
开始测试
1.实例化所有测试类,并将所有测试对象按名字放入稀疏矩阵中
2.$value$plusargs
为SystemVerilog仿真运行时调用的系统函数,可以在仿真命令直接进行赋值,并且不局限于不同仿真器对于参数在仿真命令中定义格式不同的限制,也避免了调换参数带来的频繁编译等问题。2
initial begin
basic_test = new();
burst_test = new();
fifo_full_test = new();
tests["chnl_basic_test"] = basic_test;
tests["chnl_burst_test"] = burst_test;
tests["chnl_fifo_full_test"] = fifo_full_test;
if($value$plusargs("TESTNAME=%s", name)) begin
if(tests.exists(name)) begin
tests[name].set_interface(chnl0_if, chnl1_if, chnl2_if, mcdt_if);
tests[name].run();
end
else begin
$fatal($sformatf("[ERRTEST], test name %s is invalid, please specify a valid name!", name));
end
end
else begin
$display("NO runtime optiont +TESTNAME=xxx is configured, and run default test chnl_basic_test");
tests["chnl_basic_test"].set_interface(chnl0_if, chnl1_if, chnl2_if, mcdt_if);
tests["chnl_basic_test"].run();
end
end
例化
将tb的信号与dut的端口相连
mcdt dut(
.clk_i (clk )
,.rstn_i (rstn )
,.ch0_data_i (chnl0_if.ch_data )
,.ch0_valid_i (chnl0_if.ch_valid )
,.ch0_ready_o (chnl0_if.ch_ready )
,.ch0_margin_o(chnl0_if.ch_margin )
,.ch1_data_i (chnl1_if.ch_data )
,.ch1_valid_i (chnl1_if.ch_valid )
,.ch1_ready_o (chnl1_if.ch_ready )
,.ch1_margin_o(chnl1_if.ch_margin )
,.ch2_data_i (chnl2_if.ch_data )
,.ch2_valid_i (chnl2_if.ch_valid )
,.ch2_ready_o (chnl2_if.ch_ready )
,.ch2_margin_o(chnl2_if.ch_margin )
,.mcdt_data_o (mcdt_if.mcdt_data )
,.mcdt_val_o (mcdt_if.mcdt_val )
,.mcdt_id_o (mcdt_if.mcdt_id )
);
2. package中的传输的数据类
semaphore3
Semaphore不是数据类,使用Semaphore可以实现对共享资源的互斥访问。Semaphore类似于一把钥匙,只有请求方获得钥匙才能访问资源,在访问完成后返回钥匙,其他的请求方才可以使用。
semaphore run_stop_flags = new();//设置0个钥匙
channel中传输的数据类chnl_trans
类名:chnl_trans
功能:channel传输的数据包
函数:clone复制一个相同的对象
sprint打印所有属性
class chnl_trans;
rand bit[31:0] data[];//数据包
rand int ch_id;//发送数据的通道号
rand int pkt_id;//当前为第几个数据包
rand int data_nidles;//每个数据之间间隔几个idle
rand int pkt_nidles;//每个包之间间隔几个idle
bit rsp;//握手成功为1,默认为0
local static int obj_id = 0;//创建了多少个该类的对象
// Specify constraint to match the chnl_basic_test request
constraint cstr{
soft data.size inside {[4:8]};//每个数据包由4-8个数据组成
foreach(data[i]) data[i] == 'hC000_0000 + (this.ch_id << 24) + (this.pkt_id << 8) + i;
soft ch_id == 0;//外部限制优先
soft pkt_id == 0;
data_nidles inside {[0:2]};//每个数据间间隔0-2个idle;
pkt_nidles inside {[1:10]};
};
function new();
this.obj_id++;//
endfunction
/*
函数名:clone
功能:创建一个属性与当前对象相同的新的对象
输入:输出:
返回值:返回当前类的一个对象
*/
function chnl_trans clone();
chnl_trans c = new();
c.data = this.data;
c.ch_id = this.ch_id;
c.pkt_id = this.pkt_id;
c.data_nidles = this.data_nidles;
c.pkt_nidles = this.pkt_nidles;
c.rsp = this.rsp;
return c;
endfunction
/*
功能:打印数据类的属性
*/
function string sprint();
string s;//""空字符串
s = {s, $sformatf("=======================================\n")};//拼接
s = {s, $sformatf("chnl_trans object content is as below: \n")};
s = {s, $sformatf("obj_id = %0d: \n", this.obj_id)};
foreach(data[i]) s = {s, $sformatf("data[%0d] = %8x \n", i, this.data[i])};
s = {s, $sformatf("ch_id = %0d: \n", this.ch_id)};
s = {s, $sformatf("pkt_id = %0d: \n", this.pkt_id)};
s = {s, $sformatf("data_nidles = %0d: \n", this.data_nidles)};
s = {s, $sformatf("pkt_nidles = %0d: \n", this.pkt_nidles)};
s = {s, $sformatf("rsp = %0d: \n", this.rsp)};
s = {s, $sformatf("=======================================\n")};
return s;
endfunction
endclass: chnl_trans
monitor中传输的数据结构mon_data_t
每次监测到的有效数据以及来自哪个通道
typedef struct packed{
bit[31:0] data;
bit[1:0] id;
}mon_data_t;
3. package中的环境组件
激励产生器:chnl_generator
将generator从agent中提取出来,test可以直接控制发送多少数据包与发送什么数据,对generator的随机变量进行随机化,用当前类的成员变量影响生成的transaction的随机变量
类名:chnl_generator激励产生器
功能:产生激励,自己控制发送的数据个数与随机数,把数据发送给请求mailbox
函数: new();//构造函数,创建请求mailbox和响应mailbox实例
task run();//数据发送完毕后会把钥匙返还,三个generator会返回三个钥匙,用于结束test
task send_trans();
//a. 利用randomize with产生(当前类的成员变量影响生成的)transaction的随机变量
//b. 将该数据包发送到请求mailbox(产生一个对象,把这个句柄传给req_mailbox,initiator从req_mailbox中获取该句柄,initiator将克隆生成一个新的对象,把rsp响应标志位置1,发送给rsp_mailbox信箱,克隆这种方式在存储其中至多有9个trans对象,但是随着没有指向的句柄,会被自动回收,不会在mianbox中有多个对象,这种机制可以减少内存占用)
//c. 从req_mailbox中获取响应的trans,通过判断其rsp来确定是否可以发送成功
string sprint();//将generator的产生的随机数据打印
void post_randomize();//打印随机化后的值
class chnl_generator;
//全部随机化,用来控制randomize的transaction
rand int pkt_id = -1;//数据包的id
rand int ch_id = -1;//发送数据的通道号
rand int ntrans = 10;//发送数据包个数
rand int data_nidles = -1;//每个数据之间间隔几个idle
rand int pkt_nidles = -1;//每个数据包间隔idles
rand int data_size = -1;//数据的尺寸
mailbox #(chnl_trans) req_mb;
mailbox #(chnl_trans) rsp_mb;
constraint cstr{//软约束
soft ch_id == -1;//约束里面使用等效表达式与赋值含义相同
soft pkt_id == -1;
soft data_size == -1;
soft data_nidles == -1;
soft pkt_nidles == -1;
soft ntrans == 10;
}
function new();
this.req_mb = new();
this.rsp_mb = new();
endfunction
task run();
repeat(ntrans) send_trans();//执行完毕后会结束
run_stop_flags.put();//每个generator运行完都要将钥匙返还
endtask
//产生事务并放入mailbox
task send_trans();
chnl_trans req, rsp;
req = new();
//嵌套,用当前类的成员变量影响生成的transaction的随机变量
assert(req.randomize with{
local::ch_id >= 0 -> ch_id == local::ch_id; //条件约束,如果成立,则执行后面的值;generator这一层的约束符合要求才把这个约束赋值给transaction的随机变量
local::pkt_id >= 0 -> pkt_id == local::pkt_id;
local::data_nidles >= 0 -> data_nidles == local::data_nidles;
local::pkt_nidles >= 0 -> pkt_nidles == local::pkt_nidles;
local::data_size >0 -> data.size() == local::data_size;
})
else $fatal("[RNDFAIL] channel packet randomization failure!");
this.pkt_id++;
$display(req.sprint());
this.req_mb.put(req);
this.rsp_mb.get(rsp);
$display(rsp.sprint());
assert(rsp.rsp)
else $error("[RSPERR] %0t error response received!", $time);
endtask
function string sprint();
string s;
s = {s, $sformatf("=======================================\n")};
s = {s, $sformatf("chnl_generator object content is as below: \n")};
s = {s, $sformatf("ntrans = %0d: \n", this.ntrans)};
s = {s, $sformatf("ch_id = %0d: \n", this.ch_id)};
s = {s, $sformatf("pkt_id = %0d: \n", this.pkt_id)};
s = {s, $sformatf("data_nidles = %0d: \n", this.data_nidles)};
s = {s, $sformatf("pkt_nidles = %0d: \n", this.pkt_nidles)};
s = {s, $sformatf("data_size = %0d: \n", this.data_size)};
s = {s, $sformatf("=======================================\n")};
return s;
endfunction
function void post_randomize();
string s;
s = {"AFTER RANDOMIZATION \n", this.sprint()};
$display(s);
endfunction
endclass: chnl_generator
激励发送器chnl_initiator
类名:chnl_initiator激励发送器
功能:将数据包发送给dut
函数: new(string name = "chnl_initiator");//构造函数,初始化该激励发送器的name
void set_interface(virtual chnl_intf intf);//设置接口,接口与dut之间传输数据
task run();//运行,通过调用drive函数
task drive();//从req_mailbox获取对应的数据包,将该数据包发送给dut,将该数据包的响应rsp设置为1,输入req_mailbox
task chnl_write(input chnl_trans t);//接口时钟上升沿将数据包和数据有效位发送给dut
task chnl_idle();//发送状态空
其他:调用run后不会自动停下来
**接口一定要用virtual修饰**
class chnl_initiator;
local string name;//激励发生器的名字
local virtual chnl_intf intf;//传入接口
mailbox #(chnl_trans) req_mb;//不同线程通信的缓存器
mailbox #(chnl_trans) rsp_mb;//接收从generator返回的数据
function new(string name = "chnl_initiator");
this.name = name;
endfunction
function void set_interface(virtual chnl_intf intf);//传入接口
if(intf == null)
$error("interface handle is NULL, please check if target interface has been intantiated");
else
this.intf = intf;
endfunction
task run();//运行,通过调用drive函数
this.drive();
endtask
task drive();//等待复位后发送数据,没办法自己结束
chnl_trans req,rsp;
@(posedge intf.rstn);
forever begin
this.req_mb.get(req);
this.chnl_write(req);
rsp = req.clone();
rsp.rsp = 1;
this.rsp_mb.put(rsp);
end
endtask
task chnl_write(input chnl_trans t);
foreach(t.data[i]) begin
@(posedge intf.clk);
intf.drv_ck.ch_valid <= 1;
intf.drv_ck.ch_data <= t.data[i];
@(negedge intf.clk)
wait(intf.ch_ready === 'b1);
$display("%0t channel initiator [%s] sent data %x", $time, name, t.data[i]);
repeat(t.data_nidles) chnl_idle();
end
repeat(t.pkt_nidles) chnl_idle();
endtask
task chnl_idle();
@(posedge intf.clk);
intf.drv_ck.ch_valid <= 0;
intf.drv_ck.ch_data <= 0;
endtask
endclass: chnl_initiator
通道监测器 chnl_monitor
类名:chnl_monitor
功能:从接口中获取输出的数据,并发送到monitor和checker的mailbox中
函数: new(string name = "chnl_monitor");//构造函数,当前monitor的名字
void set_interface(virtual chnl_intf intf);//将接口传入,以便获取接口中的数据
task run();//调用mon_trans()运行
task mon_trans();//iff后的valid与ready信号拉高时,@事件才会触发;从接口中获取输出的数据,并发送到monitor的mailbox中
其他:调用run后不会自动停下来
class chnl_monitor;
local string name;//monitor的名字;
local virtual chnl_intf intf;//监测的数据来自接口
mailbox #(mon_data_t) mon_mb;
function new(string name = "chnl_monitor");
this.name = name;
endfunction
function void set_interface(virtual chnl_intf intf);
if(intf == null)
$error("interface handle is NULL, please check if target interface has been intantiated");
else
this.intf = intf;
endfunction
task run();
this.mon_trans();
endtask
task mon_trans();
mon_data_t m;
forever begin
@(posedge intf.clk iff (intf.mon_ck.ch_valid === 'b1 && intf.mon_ck.ch_ready === 'b1));//iff后的valid与ready信号拉高时,@事件才会触发
m.data = intf.mon_ck.ch_data;
mon_mb.put(m);
$display("%0t %s monitored channle data %8x", $time, this.name, m.data);
end
endtask
endclass: chnl_monitor
MCDT监测器mcdt_monitor
类名:mcdt_monitor
功能:从接口中获取输出的数据,并发送到monitor的mailbox中
函数: new(string name = "mcdt_monitor");//构造函数,当前monitor的名字
void set_interface(virtual chnl_intf intf);//将接口传入,以便于从接口中获取数据
task run();//调用mon_trans()运行
task mon_trans();//从接口中获取mcdt数据,并发送到monitor的mailbox中
其他:调用run后不会自动停下来
class mcdt_monitor;
local string name;
local virtual mcdt_intf intf;
mailbox #(mon_data_t) mon_mb;
function new(string name="mcdt_monitor");
this.name = name;
endfunction
function void set_interface(virtual mcdt_intf intf);
if(intf == null)
$error("interface handle is NULL, please check if target interface has been intantiated");
else
this.intf = intf;
endfunction
task run();
this.mon_trans();
endtask
task mon_trans();
mon_data_t m;
forever begin
@(posedge intf.clk iff intf.mon_ck.mcdt_val === 'b1);
m.data = intf.mon_ck.mcdt_data;
m.id = intf.mon_ck.mcdt_id;
mon_mb.put(m);
$display("%0t %s monitored mcdt data %8x and id %0d", $time, this.name, m.data, m.id);
end
endtask
endclass: mcdt_monitor
chnl_agent
类名:chnl_agent
功能:将激励产生器和激励发送器封装起来
函数: new (string name = "chnl_agent", int id = 0, int ntrans = 1);//初始化generator和initiator
void set_interface(virtual chnl_intf vif);//传入接口,并将接口传入generator和monitor
task run();//并行执行gen.run和init.run
其他:run为运行函数,需要实例化后逐层调用,无法自己结束
class chnl_agent;
local string name;
chnl_initiator init;
chnl_monitor mon;
virtual chnl_intf vif;
function new (string name = "chnl_agent");
this.name = name;
this.init = new({name, ".init"});
this.mon = new({name, ".mon"});
endfunction
function void set_interface(virtual chnl_intf vif);
this.vif = vif;
init.set_interface(vif);
mon.set_interface(vif);
endfunction
task run();//无法结束
fork
init.run();
mon.run();
join
endtask
endclass: chnl_agent
检测器 chnl_checker
类名:chnl_checker
功能:比较输入与输出值是都一致
函数: new(string name="chnl_checker");//构造函数,实例化四个mailbox
task run();//执行do_compare()
task do_compare();
//a. 先从mcdt的mailbox获取输出监测器的值
//b. 通过该值的id获取输入通道的值
//c. 比对数据,得到错误个数和全部个数
class chnl_checker;
local string name;
local int error_count;
local int cmp_count;
mailbox #(mon_data_t) in_mbs[3];//3个input fifo
mailbox #(mon_data_t) out_mb;//1个output fifo
function new(string name="chnl_checker");//构造函数,实例化四个mailbox
this.name = name;
foreach(this.in_mbs[i]) this.in_mbs[i] = new();
this.out_mb = new();
this.error_count = 0;
this.cmp_count = 0;
endfunction
task run();
this.do_compare();
endtask
task do_compare();
mon_data_t im, om;
forever begin
// compare data once there is data in in_mb0/in_mb1/in_mb2 and out_mb
// first, get om from out_mb, and im from one of in_mbs
out_mb.get(om);
case (om.id)//通过输出的id号与输入通道的值比较
0: in_mbs[0].get(im);
1: in_mbs[1].get(im);
2: in_mbs[2].get(im);
default: $fatal("id %0d is not available", om.id);
endcase
if(om.data != im.data) begin
this.error_count++;
$error("[CMPFAIL] Compared failed! mcdt out data %8x ch_id %0d is not equal with channel in data %8x", om.data, om.id, im.data);
end
else begin
$display("[CMPSUCD] Compared succeeded! mcdt out data %8x ch_id %0d is equal with channel in data %8x", om.data, om.id, im.data);
end
this.cmp_count++;
end
endtask
endclass
4. package的测试类函数
测试基函数chnl_root_test
类名:chnl_root_test测试函数的父函数
功能:创建三个agent对象,并运行
函数: function new(string name = "chnl_root_test");
//a. 传入测试函数的名字,实例化checker,三个agent,三个generator与mcdt_monitor
//b. 将agents的initiator的req_mailbox和rsp_mailbox与generator连接起来。6个fifo
//c. 将agents的chnl_initiator的mon_mailbox与checker连接起来。3个fifo
//d. 将madt_monitor的mon_mailbox与checker连接起来。1个fifo
virtual task gen_stop_callback();//停止gennerator线程
virtual task run_stop_callback();//停止所有运行,获取到generator给的3把钥匙就停止仿真
virtual task run();
//a. config先确定发多少个数,发什么样的数
//b. 开辟子线程触发所有组件的run,这些run都不能自己停下来,
//c. 开辟子线程等待结束generator发送数据,一旦结束generator的触发条件满足就失能generator线程
//d.主线程运行generator线程,gen不能和agent调换,不然无法运行agent组件
//e. 结束仿真
virtual function void set_interface();//传入接口
virtual function void do_config();//完成generator的随机配置
class chnl_root_test;
chnl_generator gen[3];
chnl_agent agents[3];
mcdt_monitor mcdt_mon;
chnl_checker chker;
protected string name;
event gen_stop_e;//提前generator结束的触发条件
function new(string name = "chnl_root_test");//do_config先确定发多少个数,发什么样的数
this.name = name;
this.chker = new();
foreach(agents[i]) begin
this.agents[i] = new($sformatf("chnl_agent%0d",i));//先实例化才可以赋值
this.gen[i] = new();
//Connect the mailboxes handles of gen[i] and agents[i].init
this.agents[i].init.req_mb = this.gen[i].req_mb;
this.agents[i].init.rsp_mb = this.gen[i].rsp_mb;
this.agents[i].mon.mon_mb = this.chker.in_mbs[i];
end
this.mcdt_mon = new();
this.mcdt_mon.mon_mb = this.chker.out_mb;
$display("%s instantiate objects", this.name);
endfunction
virtual task gen_stop_callback();//停止gennerator线程
// empty
endtask
virtual task run_stop_callback();//停止所有运行
$display("run_stop_callback enterred");
// by default, run would be finished once generators raised 'finish'flags
$display("%s: wait for all generators have generated and tranferred transcations", this.name);
run_stop_flags.get(3);//获取三个钥匙,正常结束
$display($sformatf("*****************%s finished********************", this.name));
$finish();
endtask
virtual task run();
$display($sformatf("*****************%s started********************", this.name));
this.do_config();//do_config先确定发多少个数,发什么样的数
fork: trigger_all_components//触发所有组件
agents[0].run();
agents[1].run();
agents[2].run();
mcdt_mon.run();
chker.run();
join_none//子线程
// run first the callback thread to conditionally disable gen_threads
fork
this.gen_stop_callback();
@(this.gen_stop_e) disable gen_threads;//一直在等待有两个ready拉高后触发该事件,结束gen
join_none
fork: gen_threads
gen[0].run();
gen[1].run();
gen[2].run();
join//gen不能和agent调换,不然无法运行agent组件
run_stop_callback(); // wait until run stop control task
endtask
virtual function void set_interface(virtual chnl_intf ch0_vif, virtual chnl_intf ch1_vif, virtual chnl_intf ch2_vif, virtual mcdt_intf mcdt_vif);
agents[0].set_interface(ch0_vif);
agents[1].set_interface(ch1_vif);
agents[2].set_interface(ch2_vif);
mcdt_mon.set_interface(mcdt_vif);
endfunction
virtual function void do_config();
endfunction
endclass: chnl_root_test
chnl_basic_test
配置generator的随机化
class chnl_basic_test extends chnl_root_test;
function new(string name = "chnl_basic_test");
super.new(name);
endfunction
virtual function void do_config();
super.do_config();
assert(gen[0].randomize() with {ntrans==100; data_nidles==0; pkt_nidles==1; data_size==8;})
else $fatal("[RNDFAIL] gen[0] randomization failure!");
assert(gen[1].randomize() with {ntrans==50; data_nidles inside {[1:2]}; pkt_nidles inside {[3:5]}; data_size==16;})
else $fatal("[RNDFAIL] gen[1] randomization failure!");
assert(gen[2].randomize() with {ntrans==80; data_nidles inside {[0:1]}; pkt_nidles inside {[1:2]}; data_size==32;})
else $fatal("[RNDFAIL] gen[2] randomization failure!");
endfunction
endclass: chnl_basic_test
chnl_burst_test
和basic相同
class chnl_burst_test extends chnl_root_test;
function new(string name = "chnl_burst_test");
super.new(name);
endfunction
virtual function void do_config();
super.do_config();
assert(gen[0].randomize() with {ntrans inside {[80:100]}; data_nidles==0; pkt_nidles==1; data_size inside {8, 16, 32};})
else $fatal("[RNDFAIL] gen[0] randomization failure!");
assert(gen[1].randomize() with {ntrans inside {[80:100]}; data_nidles==0; pkt_nidles==1; data_size inside {8, 16, 32};})
else $fatal("[RNDFAIL] gen[1] randomization failure!");
assert(gen[2].randomize() with {ntrans inside {[80:100]}; data_nidles==0; pkt_nidles==1; data_size inside {8, 16, 32};})
else $fatal("[RNDFAIL] gen[2] randomization failure!");
endfunction
endclass: chnl_burst_test
chnl_fifo_full_test
类名:chnl_fifo_full_test
功能:一旦有两个通道的fifo满,generator立即停止发送数据(虽然generator不发了,但是dut的通道中还有数据,直到这些数据全部发送完,slave与arbiter的fifo满时停止运行)
函数: function new(string name = "chnl_fifo_full_test");
virtual function void do_config();//该config发送数据写很大但不会全部发送,data_nidles设置为0; pkt_nidles设置为1;
local function bit[2:0] get_chnl_ready_flags();//获取每个通道的ready信号,一旦ready为低,说明fifo满
virtual task run_stop_callback();//停止所有运行,三个通道的fifo为空,结束运行
virtual task gen_stop_callback();//一直将获取三个通道的ready信号,一旦两个或两个以上为0,触发gen_stop_e事件,使generator停止
$countones(chnl_ready_flags)向量中1的数量4
class chnl_fifo_full_test extends chnl_root_test;
function new(string name = "chnl_fifo_full_test");
super.new(name);
endfunction
virtual function void do_config();//do_config先确定发多少个数,发什么样的数
super.do_config();
assert(gen[0].randomize() with {ntrans inside {[1000:2000]}; data_nidles==0; pkt_nidles==1; data_size inside {8, 16, 32};})
else $fatal("[RNDFAIL] gen[0] randomization failure!");
assert(gen[1].randomize() with {ntrans inside {[1000:2000]}; data_nidles==0; pkt_nidles==1; data_size inside {8, 16, 32};})
else $fatal("[RNDFAIL] gen[1] randomization failure!");
assert(gen[2].randomize() with {ntrans inside {[1000:2000]}; data_nidles==0; pkt_nidles==1; data_size inside {8, 16, 32};})
else $fatal("[RNDFAIL] gen[2] randomization failure!");
endfunction
// get all of 3 channles slave ready signals as a 3-bits vector
local function bit[2:0] get_chnl_ready_flags();
return {agents[2].vif.mon_ck.ch_ready
,agents[1].vif.mon_ck.ch_ready
,agents[0].vif.mon_ck.ch_ready
};
endfunction
virtual task gen_stop_callback();
bit[2:0] chnl_ready_flags;
$display("gen_stop_callback enterred");
@(posedge agents[0].vif.rstn);
forever begin
@(posedge agents[0].vif.clk);
chnl_ready_flags = this.get_chnl_ready_flags();
if($countones(chnl_ready_flags) <= 1) break;//至少两个ready信号为0,两个通道满就break
end
$display("%s: stop 3 generators running", this.name);
-> this.gen_stop_e;//跳出后,发送一个事件
endtask
virtual task run_stop_callback();
$display("run_stop_callback enterred");
// since generators have been forced to stop, and run_stop_flag would not be raised by each generator, so no need to wait for the run_stop_flags any more
$display("%s: waiting DUT transfering all of data", this.name);
fork//三个通道的fifo为空,结束运行
wait(agents[0].vif.ch_margin == 'h20);
wait(agents[1].vif.ch_margin == 'h20);
wait(agents[2].vif.ch_margin == 'h20);
join
$display("%s: 3 channel fifos have transferred all data", this.name);
$display($sformatf("*****************%s finished********************", this.name));
$finish();
endtask
endclass: chnl_fifo_full_test
五、Questasim的Makefile
#############################
# User variables
#############################
TB = tb3
DFILES = {arbiter.v,slave_fifo.v,mcdt.v}
VFILES = $(TB).sv
#############################
# Environment variables
#############################
VLAB = vlib work
VCOMP = vlog -l com.log
VSTART = vsim -l sim.log
VSIMULATE = -voptargs=+acc +TESTNAME=chnl_fifo_full_test
#################### = -voptargs=+acc -solvefaildebug -sv seed random
VADDWAVE = add wave -position insertpoint sim:/$(TB)/*
VSAVE = log -r /*
RUN = run -all
QUIT = quit -f
all: create_lib compile simulate
create_lib:
$(VLAB)
compile:
$(VCOMP) $(DFILES) $(VFILES)
simulate:
#$(VSTART) -c $(VSIMULATE) work.$(TB) -do "$(SAVE) $(RUN);$(VQUIT)"
$(VSTART) $(VSIMULATE) work.$(TB) -do "$(VADDWAVE);$(VSAVE);$(RUN)"
clean:
rm -rf work mti_lib transcript modelsim.ini *.log vsim.wlf