chnl_pkg 1
package chnl_pkg1;
class chnl_trans;
rand bit[31:0] data[];
rand int ch_id; //目前trans要发送到哪个chnl
rand int pkt_id; //当前trans是第几个pkg包,也就是当前trans的id
rand int data_nidles;
rand int pkt_nidles; //trans 和 trans 数据包之间的空闲周期数
bit rsp;
local static int obj_id = 0;
// USER TODO 1.1.
// Specify constraint to match the chnl_basic_test request
constraint cstr{ //动态数组约束
data.size inside {[4:8]}; //每个trans发送4~8个data
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]};
pkt_nidles inside {[3:5]};
};
function new();
this.obj_id++;
endfunction
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;
// USER TODO 1.2
// Could we put c.obj_id = this.obj_id here? and why?
return c;
endfunction
function string sprint(); // 当前所有值都打印出来 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
class chnl_initiator;
local string name;
local virtual chnl_intf intf;
mailbox #(chnl_trans) req_mb;
mailbox #(chnl_trans) rsp_mb;
function new(string name = "chnl_initiator");
this.name = name;
endfunction
function void set_name(string s);
this.name = s;
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.drive();
endtask
task drive();
chnl_trans req, rsp;
@(posedge intf.rstn); //只要信号复位之后
forever begin //一直等
this.req_mb.get(req); //等待 generator 放入req_mb信箱req之后,拿到这个句柄
this.chnl_write(req); //发送这个数据包
rsp = req.clone(); //克隆这个对象 句柄rsp,这里有一个默认的rsp成员默认值0
rsp.rsp = 1; //如果发送数据成功后,将对象里面的值rsp改为1,说明发送成功
this.rsp_mb.put(rsp); //再把rsp句柄放入rsp_mb信箱
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];
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
class chnl_generator;
int pkt_id;
int ch_id;
int ntrans;
int data_nidles;
mailbox #(chnl_trans) req_mb;
mailbox #(chnl_trans) rsp_mb;
function new(int ch_id, int ntrans);
this.ch_id = ch_id;
this.pkt_id = 0;
this.ntrans = ntrans;
this.req_mb = new(); //句柄指向了对象,两个信箱在generator里面例化
this.rsp_mb = new();
endfunction
task run();
repeat(ntrans) send_trans();
$finish();
endtask
// generate transaction and put into local mailbox
task send_trans();
chnl_trans req, rsp;
req = new();
assert(req.randomize with {ch_id == local::ch_id; pkt_id == local::pkt_id;}) //给req指向的对象随机化 generator产生对应要求的数据包
else $fatal("[RNDFAIL] channel packet randomization failure!");
this.pkt_id++;
$display(req.sprint());
this.req_mb.put(req); //generator产生一个req放入req_mb信箱
this.rsp_mb.get(rsp);
$display(rsp.sprint());
assert(rsp.rsp)
else $error("[RSPERR] %0t error response received!", $time);
endtask
endclass: chnl_generator
class chnl_agent;
chnl_generator gen;
chnl_initiator init;
local virtual chnl_intf vif;
function new(string name = "chnl_agent", int id = 0, int ntrans = 1);
this.gen = new(id, ntrans);
this.init = new(name);
endfunction
function void set_interface(virtual chnl_intf vif);
this.vif = vif;
init.set_interface(vif);
endfunction
task run();
this.init.req_mb = this.gen.req_mb; //将不悬空的两个句柄指向了悬空的两个句柄,原来悬空的句柄指向了对象
this.init.rsp_mb = this.gen.rsp_mb;
fork
gen.run();
init.run(); //永远都不会结束,所有只要Gen.run不停止,agent.run()就不停止
join_any
endtask
endclass: chnl_agent
class chnl_root_test;
chnl_agent agent[3];
protected string name;
function new(int ntrans = 100, string name = "chnl_root_test");
foreach(agent[i]) begin
this.agent[i] = new($sformatf("chnl_agent%0d",i), i, ntrans);
end
this.name = name;
$display("%s instantiate objects", this.name);
endfunction
task run();
$display($sformatf("*****************%s started********************", this.name));
fork
agent[0].run(); //可以结束
agent[1].run();
agent[2].run();
join
$display($sformatf("*****************%s finished********************", this.name));
// USER TODO 1.3
// Please move the $finish statement from the test run task to generator
// You woudl put it anywhere you like inside generator to stop test when
// all transactions have been transfered
// USER TODO 1.4
// Apply 'vsim -novopt -solvefaildebug -sv_seed 0 work.tb1' to run the
// simulation, and check if the generated data is the same as previously
// Then use 'vsim -novopt -solvefaildebug -sv_seed random work.tb1' to
// run the test 2 times, and check if the data generated of 2 times are
// is the same or not?
// USER TODO 1.5
// In the last chnl_trans object content display, why the object_id is
// 1200? How is it counted and finally as the value 1200?
endtask
function void set_interface(virtual chnl_intf ch0_vif, virtual chnl_intf ch1_vif, virtual chnl_intf ch2_vif);
agent[0].set_interface(ch0_vif);
agent[1].set_interface(ch1_vif);
agent[2].set_interface(ch2_vif);
endfunction
endclass: chnl_root_test
// USER TODO 1.1
// each channel send data with idle_cycles inside [0:2]
// and idle peroids between sequential packets should be inside [3:5]
// each channel send out 200 data then to finish the test
// The idle_cycle constraint should be specified inside chnl_trans
class chnl_basic_test extends chnl_root_test;
function new(int ntrans = 200, string name = "chnl_basic_test");
super.new(ntrans, name);
endfunction
endclass: chnl_basic_test
class chnl_burst_test extends chnl_root_test;
function new(int ntrans = 500, string name = "chnl_burst_test");
super.new(ntrans, name);
endfunction
endclass: chnl_burst_test
class chnl_fifo_full_test extends chnl_root_test;
function new(int ntrans = 1_000_000, string name = "chnl_fifo_full_test");
super.new(ntrans, name);
endfunction
endclass: chnl_fifo_full_test
endpackage
chnl_pkg 2
package chnl_pkg2;
semaphore run_stop_flags = new();
class chnl_trans;
rand bit[31:0] data[];
rand int ch_id;
rand int pkt_id;
rand int data_nidles;
rand int pkt_nidles;
bit rsp;
local static int obj_id = 0;
constraint cstr{
soft data.size inside {[4:8]}; //如果没有soft 会产生约束冲突
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]};
pkt_nidles inside {[1:10]};
};
function new();
this.obj_id++;
endfunction
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
class chnl_initiator;
local string name;
local virtual chnl_intf intf;
mailbox #(chnl_trans) req_mb;
mailbox #(chnl_trans) rsp_mb;
function new(string name = "chnl_initiator");
this.name = name;
endfunction
function void set_name(string s);
this.name = s;
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.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
class chnl_generator;
rand int pkt_id = -1;
rand int ch_id = -1;
rand int data_nidles = -1;
rand int pkt_nidles = -1;
rand int data_size = -1;
rand int ntrans = 10;
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();
endtask
// generate transaction and put into local mailbox
task send_trans();
chnl_trans req, rsp;
req = new();
assert(req.randomize with {local::ch_id >= 0 -> ch_id == local::ch_id;
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
//对上面对的理解是:外部的test对gen的成员变量随机化,并且需要满足外部的约束;
gen.run()这个函数对产生的chnl_trans随机化,需要满足chnl_trans内部的约束
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
class chnl_agent;
chnl_initiator init;
virtual chnl_intf vif;
function new(string name = "chnl_agent");
this.init = new(name);
endfunction
function void set_interface(virtual chnl_intf vif);
this.vif = vif;
init.set_interface(vif);
endfunction
task run();
fork
init.run();
join
endtask
endclass: chnl_agent
class chnl_root_test;
chnl_generator gen[3];
chnl_agent agent[3];
protected string name;
event gen_stop_e;
function new(string name = "chnl_root_test");
foreach(agent[i]) begin
this.agent[i] = new($sformatf("chnl_agent%0d",i));
this.gen[i] = new();
// USER TODO 2.1
// Connect the mailboxes handles of gen[i] and agent[i].init
this.agent[i].init.req_mb = this.gen[i].req_mb;
this.agent[i].init.rsp_mb = this.gen[i].rsp_mb;
end
this.name = name;
$display("%s instantiate objects", this.name);
endfunction
virtual task gen_stop_callback();
// 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(); //generator 随机化
fork
agent[0].run(); //让agent 先run起来,不会等待它的结束,因为永远不会结束
agent[1].run();
agent[2].run();
join_none
fork
this.gen_stop_callback();
@(this.gen_stop_e) disable gen_threads;
join_none
fork : gen_threads
gen[0].run(); //要等待三个generator 随机化trans, 产生完所有数据
gen[1].run();
gen[2].run();
join
run_stop_callback();
endtask
virtual function void set_interface(virtual chnl_intf ch0_vif, virtual chnl_intf ch1_vif, virtual chnl_intf ch2_vif);
agent[0].set_interface(ch0_vif);
agent[1].set_interface(ch1_vif);
agent[2].set_interface(ch2_vif);
endfunction
virtual function void 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!");
// USER TODO 2.2
// To randomize gen[1] with
// ntrans==50, data_nidles inside [1:2], pkt_nidles inside [3:5],
// data_size == 6
assert(gen[1].randomize() with {ntrans==50; data_nidles inside{[1:2]}; pkt_nidles inside{[3:5]}; data_size==6;})
else $fatal("[RNDFAIL] gen[0] randomization failure!");
// USER TODO 2.3
// ntrans==80, data_nidles inside [0:1], pkt_nidles inside [1:2],
// data_size == 32
assert(gen[2].randomize() with {ntrans==80; data_nidles inside{[0:1]}; pkt_nidles inside{[1:2]}; data_size==32;})
else $fatal("[RNDFAIL] gen[0] randomization failure!");
endfunction
endclass
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!");
// USER TODO 2.2
// To randomize gen[1] with
// ntrans==50, data_nidles inside [1:2], pkt_nidles inside [3:5],
// data_size == 6
assert(gen[1].randomize() with {ntrans==50; data_nidles inside{[1:2]}; pkt_nidles inside{[3:5]}; data_size==6;})
else $fatal("[RNDFAIL] gen[0] randomization failure!");
// USER TODO 2.3
// ntrans==80, data_nidles inside [0:1], pkt_nidles inside [1:2],
// data_size == 32
assert(gen[2].randomize() with {ntrans==80; data_nidles inside{[0:1]}; pkt_nidles inside{[1:2]}; data_size==32;})
else $fatal("[RNDFAIL] gen[0] randomization failure!");
endfunction
endclass: chnl_basic_test
// USER TODO 2.4
// each channel send data packet number inside [80:100]
// data_nidles == 0, pkt_nidles == 1, data_size inside {8, 16, 32}
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
// USER TODO 2.5
// keep channel sending out data packet with number, and please
// let all of slave channels raising fifo_full (ready=0) at the same time
// and then to stop the test
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();
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 {agent[2].vif.mon_ck.ch_ready
,agent[1].vif.mon_ck.ch_ready
,agent[0].vif.mon_ck.ch_ready
};
endfunction
virtual task gen_stop_callback();
bit[2:0] chnl_ready_flags;
$display("gen_stop_callback enterred");
@(posedge agent[0].vif.rstn);
forever begin
@(posedge agent[0].vif.clk);
chnl_ready_flags = this.get_chnl_ready_flags();
if($countones(chnl_ready_flags) <= 1) 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
wait(agent[0].vif.ch_margin == 'h20);
wait(agent[1].vif.ch_margin == 'h20);
wait(agent[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
endpackage
chnl_pkg 3
package chnl_pkg3;
// static variables shared by resources
semaphore run_stop_flags = new();
class chnl_trans;
rand bit[31:0] data[];
rand int ch_id;
rand int pkt_id;
rand int data_nidles;
rand int pkt_nidles;
bit rsp;
local static int obj_id = 0;
constraint cstr{
soft data.size inside {[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]};
pkt_nidles inside {[1:10]};
};
function new();
this.obj_id++;
endfunction
function chnl_trans clone();
chnl_trans c = new();
c.data = this.data;
c.ch_id = this.ch_id; //由于内部有约束,外部没约束打印出0
c.pkt_id = this.pkt_id; //打印出0
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
class chnl_initiator;
local string name;
local virtual chnl_intf intf;
mailbox #(chnl_trans) req_mb;
mailbox #(chnl_trans) rsp_mb;
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();
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
class chnl_generator;
rand int pkt_id = -1;
rand int ch_id = -1;
rand int data_nidles = -1;
rand int pkt_nidles = -1;
rand int data_size = -1;
rand int ntrans = 10;
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();
endtask
// generate transaction and put into local mailbox
task send_trans();
chnl_trans req, rsp;
req = new();
assert(req.randomize with {local::ch_id >= 0 -> ch_id == local::ch_id;
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)}; // 值为-1,外部的test没有对ch_id和pkg_id进行约束,值为默认-1
s = {s, $sformatf("pkt_id = %0d: \n", this.pkt_id)}; // 值为-1,外部的test没有对ch_id和pkg_id进行约束,值为默认-1
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
typedef struct packed {
bit[31:0] data;
bit[1:0] id;
} mon_data_t;
class chnl_monitor;
local string name;
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));
// USER TODO 3.1
// Put the data into the mon_mb and use $display() to print the stored
// data value with monitor name
m.data = intf.mon_ck.ch_data; //initiator 写入的数据传给m.data
mon_mb.put(m); // 监测到的 数据写入到与cheker共享的mailbox里面
$display("%0t %s monitored channle data %8x", $time, this.name, m.data);
end
endtask
endclass
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
task run();
this.mon_trans();
endtask
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 mon_trans();
mon_data_t m;
forever begin
@(posedge intf.clk iff intf.mon_ck.mcdt_val==='b1);
// USER TODO 3.1
// Put the data into the mon_mb and use $display() to print the stored
// data value with monitor name
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
class chnl_agent;
local string name;
chnl_initiator init;
chnl_monitor mon;
// USER TODO 3.2
// Refer to how we create, set virtual interface and run the initiator
// object, use do the similar action to the monitor object
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
class chnl_checker;
local string name;
local int error_count;
local int cmp_count;
mailbox #(mon_data_t) in_mbs[3];
mailbox #(mon_data_t) out_mb;
function new(string name="chnl_checker");
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
// USER TODO 3.3
// 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)
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
// USER TODO 3.4
// Create, set interface and run the object mcdt_mon and checker
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;
function new(string name = "chnl_root_test");
this.name = name;
this.chker = new();
foreach(agents[i]) begin
this.agents[i] = new($sformatf("chnl_agent%0d",i));
this.gen[i] = new();
// USER TODO 2.1
// 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 instantiated and connected objects", this.name);
endfunction
virtual task gen_stop_callback();
// 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();
fork
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;
join_none
fork : gen_threads
gen[0].run();
gen[1].run();
gen[2].run();
join
run_stop_callback(); // wait until run stop control task finished
// USER TODO 1.3
// Please move the $finish statement from the test run task to generator
// You would put it anywhere you like inside generator to stop test when
// all transactions have been transfered
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(); // 虚方法,(动态查找),test调用了root_test里面的run(),调用父类的do_config ,动态查找子类里的do_config,执行子类的do_config
endfunction
endclass
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!");
// USER TODO 2.2
// To randomize gen[1] with
// ntrans==50, data_nidles inside [1:2], pkt_nidles inside [3:5],
// data_size == 6
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!");
// USER TODO 2.3
// ntrans==80, data_nidles inside [0:1], pkt_nidles inside [1:2],
// data_size == 32
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
// USER TODO 2.4
// each channel send data packet number inside [80:100]
// data_nidles == 0, pkt_nidles == 1, data_size inside {8, 16, 32}
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
// USER TODO 2.5
// keep channel sending out data packet with number, and please
// let at least two slave channels raising fifo_full (ready=0) at the same time
// and then to stop the test
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();
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;
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
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
endpackage