$sformatf()/$sformat()
packed压缩数组
$display
中位向量系统函数
命令行输入
画一半的结构图,跑完仿真还是得再完善。画图太慢,还不如上一篇那样文字描述,多次总结。
ch_pkg代码
package ch_pkg;
semaphore run_stop_flags=new();//创建这个运行停止的标志存放桶
//**************************************************//
class ch_trans;
//首先,对变量做随机化,加入间隔
//Packed arrays can be made of only the single bit data types (bit, logic, reg), enumerated types, and other packed arrays and packed structures. This also means you cannot have packed arrays of integer types with predefined widths (e.g. a packed array of byte).
rand bit [31:0] data[];//数据变成动态数组(合并数组两边都有[]的只能用单比特的数据类型比如bit logic reg、枚举类型)
rand int channel_id;
rand int pkt_id;
rand int data_idle;
rand int pkt_idle;
bit rsp;
local static int object_id=0;//静态变量,一开始就分配了空间且一直存在
constraint cstr{
data.size inside {[4:8]};
foreach(data[i])//按一定规律给出数据(伪随机数)
data[i]=='h00C0_0000+(this.channel_id<<24)+i;
soft channel_id==0;
soft pkt_id==0;
data_idle inside{[0:2]};
pkt_idle inside{[1:10]};//注意没有加soft,必须实现
};
//定义变量和约束后,每个类都需要做的是new函数,初始化/例化?
function new();
object_id++;
endfunction
//克隆函数,所有变量复制一次
function ch_trans clone();
ch_trans c;
c=new();//这两可以写成一句 ch_trans c = new();
c.data =this.data;
c.channel_id =this.channel_id;
c.pkt_id =this.pkt_id;
c.data_idle =this.data_idle;
c.pkt_idle =this.pkt_idle;
c.rsp =this.rsp;
return c;
endfunction
function string sprint();
string s;
s={s, $sformatf("==============\n")};
s={s, $sformatf("ch_trans object content\n")};
s={s, $sformatf("object_id=%0d", this.object_id)};
foreach(data[i])
s={s, $sformatf("data[%0d]=%8x \n", i, this.data[i])};
s={s, $sformatf("channel_id=%0d \n", this.channel_id)};
s={s, $sformatf("pkt_id=%0d \n", this.pkt_id)};
s={s, $sformatf("data_idle=%0d \n", this.data_idle)};
s={s, $sformatf("pkt_idle=%0d \n", this.pkt_idle)};
s={s, $sformatf("rsp=%0d \n", this.rsp)};
s={s, $sformatf("==============\n")};
return s;//这里要返回s
endfunction
endclass
//**************************************************//
class initiator_chen;
virtual interface_chen intf;//virtual表示在子类中会被调用到,声明类的成员变量接口指针必须用virtual
mailbox #(ch_trans) req_mailbox;
mailbox #(ch_trans) rsp_mailbox;//不用例化,只取gen中的用,但要声明
local string name;
function new(string name="initiator_chen");
this.name=name;
endfunction
function void set_interface(virtual interface_chen intf);
if(intf==null)
$error("null, need to be intantiated.");
else this.intf=intf;
endfunction
task run();
this.drive();//要加this表示这个class中的drive
endtask
task drive();//取出gen的mb,取完放在哪里?句柄,而且是ch_trans
ch_trans req, rsp;
@(posedge intf.rstn)//复位后开始一直执行
forever begin
this.req_mailbox.get(req);//先取
this.chnl_write(req);//后用
rsp=req.clone();//再复制和点亮
rsp.rsp=1;
this.rsp_mailbox.put(rsp);//最后放
end
endtask
task chnl_write (input ch_trans t);//data变成动态数组,要做相应的改变
foreach(t.data[i]) begin
@(posedge intf.clk);
intf.drv_clk.channel_valid<=1'b1;
intf.drv_clk.channel_data<=t.data[i];//data要加t.引用
@(negedge intf.clk);
wait(intf.channel_ready==1'b1);
$display("%t channel initial [%s] sent data %x", $time, name, t.data[i]);
repeat (t.data_idle) channel_idle();//原来的idle由ch_trans中的data_idle替代
end
repeat (t.pkt_idle) channel_idle();
endtask
task channel_idle();
@(posedge intf.clk);
intf.drv_clk.channel_valid<=1'b0;
intf.drv_clk.channel_data<=1'b0;
endtask
endclass
//**************************************************//
class generator_chen;
rand int pkt_id = -1;
rand int channel_id = -1;
rand int data_idle = -1;
rand int pkt_idle = -1;
rand int data_size = -1;
rand int ntrans = 10;
constraint cstr{
soft channel_id == -1;
soft pkt_id == -1;
soft data_size == -1;
soft data_idle == -1;
soft pkt_idle == -1;
soft ntrans == 10;
}
mailbox #(ch_trans) req_mailbox;
mailbox #(ch_trans) rsp_mailbox;
function new();//例化两个信箱
this.req_mailbox=new();
this.rsp_mailbox=new();
endfunction
task run();
repeat(ntrans) send_ntrans();
run_stop_flags.put();
endtask
task send_ntrans();
ch_trans req, rsp;
req=new();//想随机化必须先例化?
assert(req.randomize with { local::channel_id>=0 -> channel_id==local::channel_id;
local::pkt_id >= 0 -> pkt_id == local::pkt_id;
local::data_idle >= 0 -> data_idle == local::data_idle;
local::pkt_idle >= 0 -> pkt_idle == local::pkt_idle;
local::data_size >0 -> data.size() == local::data_size;}
)
else $fatal("randomization failure");
this.pkt_id++;
$display(req.sprint());
this.req_mailbox.put(req);
this.rsp_mailbox.get(rsp);
$display(rsp.sprint());
assert(rsp.rsp)
else $error("%0t error response received", $time);
endtask
function string sprint();//同名函数,内容不一样
string s;
s = {s, $sformatf("=======================================\n")};
s = {s, $sformatf("generator_chen object content is as below: \n")};
s = {s, $sformatf("ntrans = %0d: \n", this.ntrans)};
s = {s, $sformatf("ch_id = %0d: \n", this.channel_id)};
s = {s, $sformatf("pkt_id = %0d: \n", this.pkt_id)};
s = {s, $sformatf("data_nidles = %0d: \n", this.data_idle)};
s = {s, $sformatf("pkt_nidles = %0d: \n", this.pkt_idle)};
s = {s, $sformatf("data_size = %0d: \n", this.data_size)};
s = {s, $sformatf("=======================================\n")};
return s;
endfunction
function void post_randomize();//在上面randomize之后马上打印出来
string s;
s={"after randomization", this.sprint()};
$display(s);
endfunction
endclass
//**************************************************//
typedef struct packed{
bit[31:0] data;
bit[1:0] id;} monitor_data;
//**************************************************//
class channel_monitor;
local string name;
local virtual interface_chen intf;
mailbox #(monitor_data) mon_mailbox;
function new(string name="channel_monitor");
this.name=name;
endfunction
function void set_interface(virtual interface_chen intf);
if(intf==null)
$error("null");
else this.intf=intf;
endfunction
task run();
this.mon_trans();
endtask
task mon_trans();
monitor_data m;
forever begin
@(posedge intf.mon_clk iff(intf.mon_clk.channel_valid===1'b1 && intf.mon_clk.channel_ready===1'b1));
m.data=intf.mon_clk.channel_data;
mon_mailbox.put(m);
$display("%0t %s monitored channel data %8x", $time, this.name, m.data);
end
endtask
endclass
//**************************************************//
class mcdt_monitor;
local string name;
local virtual mcdt_chen_interface intf;
mailbox #(monitor_data) mon_mailbox;
function new(string name="mcdt_monitor");
this.name=name;
endfunction
function void set_interface(virtual mcdt_chen_interface intf);
if(intf==null)
$error("null");
else this.intf=intf;
endfunction
task run();
this.mon_trans();
endtask
task mon_trans();
monitor_data m;
forever begin
@(posedge intf.mon_clk iff(intf.mon_clk.mcdt_valid===1'b1));
m.data=intf.mon_clk.mcdt_data_output;
m.id=intf.mon_clk.mcdt_id;
mon_mailbox.put(m);
$display("%0t %s monitored channel data %8x and id %0d", $time, this.name, m.data, m.id);
end
endtask
endclass
//**************************************************//
class checker_chen;
mailbox #(monitor_data) mon_input[3];
mailbox #(monitor_data) mon_output;
local string name;
local int error_count;
local int comp_count;
function new(string name="checker_chen");
this.name=name;
foreach(mon_input[i])
this.mon_input[i]=new();
this.mon_output=new();
endfunction
task run();
this.compare();
endtask
task compare();//typedef的monitor_data也有句柄?
monitor_data in, out;
forever begin
mon_output.get(out);
case(out.id)
1: mon_input[0].get(in);
2: mon_input[1].get(in);
3: mon_input[2].get(in);
default: $fatal("id %0d is not avalidable", out.id);
endcase
if(out.data != in.data) begin
this.error_count++;
$error("mcdt_data_output %8x, channel id %0d is not equal with channel in data %8x", out.data, out.id, in.data);
end
else begin
$display("%8x, %0d,= %8x", out.data, out.id, in.data);
end
this.comp_count++;
end
endtask
endclass
//**************************************************//
class ch_agent;
channel_monitor ch_mon;
initiator_chen init;
local string name;
virtual interface_chen aintf;
function new(string name="ch_agent");
this.name=name;
this.ch_mon=new(name);
this.init=new(name);
endfunction
function void set_interface(virtual interface_chen aintf);
this.aintf=aintf;
init.set_interface(aintf);
ch_mon.set_interface(aintf);
endfunction
task run();
fork
init.run();
ch_mon.run();
join
endtask
endclass
//**************************************************//
class root_test;
//声明所有句柄
ch_agent agent[3];
generator_chen gen[3];
checker_chen chk;
mcdt_monitor mcdt_mon;
protected string name;
event gen_stop_event;
function new(string name="root_test");
this.chk=new();
this.name=name;
foreach(agent[i]) begin
this.agent[i]=new($sformatf("ch_agent%0d", i));//不仅要new,还要给id
this.gen[i]=new();//可以直接和agent共用这个foreach
this.agent[i].init.req_mailbox=this.gen[i].req_mailbox;
this.agent[i].init.rsp_mailbox=this.gen[i].rsp_mailbox;
this.agent[i].ch_mon.mon_mailbox=this.chk.mon_input[i];
end
this.mcdt_mon=new();
this.mcdt_mon.mon_mailbox=this.chk.mon_output;
$display("%s instantiated and connected objects", this.name );
endfunction
virtual task run();
$display($sformatf("*****************%s started********************", this.name));
this.do_config();
fork
agent[0].run();
agent[1].run();
agent[2].run();
mcdt_mon.run();
chk.run();
join_none
fork
this.gen_stop_callback();
@(this.gen_stop_event) disable gen_run;
join_none
fork: gen_run
gen[0].run();
gen[1].run();
gen[2].run();
join
run_stop_callback();
endtask
virtual function void do_config();
endfunction
virtual task gen_stop_callback();
endtask
virtual task run_stop_callback();
$display("run_stop_callback enterred");
$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 function void set_interface(virtual interface_chen intf1
,virtual interface_chen intf2
,virtual interface_chen intf3
,virtual mcdt_chen_interface mcdt_intf);
agent[0].set_interface(intf1);
agent[1].set_interface(intf2);
agent[2].set_interface(intf3);
endfunction
endclass
//**************************************************//
class basic_test extends root_test;
function new(string name="basic_test");
super.new(name);
endfunction
virtual function void do_config();//root里面给空,virtual多态在这里写具体的
super.do_config();
assert(gen[0].randomize() with {ntrans==100;
data_idle==0;
pkt_idle==1;
data_size==8;})
else $fatal("gen[0] randomization failure");
assert(gen[1].randomize() with {ntrans==50;
data_idle inside {[1:2]};
pkt_idle inside {[3:5]};
data_size==6;})
else $fatal("gen[1] randomization failure");
assert(gen[2].randomize() with {ntrans==80;
data_idle inside {[0:1]};
pkt_idle inside {[1:2]};
data_size==32;})
else $fatal("gen[2] randomization failure");
endfunction
endclass: basic_test
//**************************************************//
class burst_test extends root_test;
function new(string name="burst_test");
super.new(name);
endfunction
virtual function void do_config();//root里面给空,virtual多态在这里写具体的
super.do_config();
assert(gen[0].randomize() with {ntrans inside {[80:100]};
data_idle==0;
pkt_idle==1;
data_size inside {8, 16, 32};})
else $fatal("[RNDFAIL] gen[0] randomization failure!");
assert(gen[1].randomize() with {ntrans inside {[80:100]};
data_idle==0;
pkt_idle==1;
data_size inside {8, 16, 32};})
else $fatal("[RNDFAIL] gen[1] randomization failure!");
assert(gen[2].randomize() with {ntrans inside {[80:100]};
data_idle==0;
pkt_idle==1;
data_size inside {8, 16, 32};})
else $fatal("[RNDFAIL] gen[2] randomization failure!");
endfunction
endclass: burst_test
//**************************************************//
class full_test extends root_test;
function new(string name="full_test");
super.new(name);
endfunction
virtual function void do_config();
super.do_config();
assert(gen[0].randomize() with {ntrans inside {[1000:2000]};
data_idle==0;
pkt_idle==1;
data_size inside {8, 16, 32};})
else $fatal("[RNDFAIL] gen[0] randomization failure!");
assert(gen[1].randomize() with {ntrans inside {[1000:2000]};
data_idle==0;
pkt_idle==1;
data_size inside {8, 16, 32};})
else $fatal("[RNDFAIL] gen[1] randomization failure!");
assert(gen[2].randomize() with {ntrans inside {[1000:2000]};
data_idle==0;
pkt_idle==1;
data_size inside {8, 16, 32};})
else $fatal("[RNDFAIL] gen[2] randomization failure!");
endfunction
//这里报错(vlog-13169) Packed dimension must specify a range.
local function bit[0:2] get_channel_ready_flag();
return{agent[2].aintf.mon_clk.channel_ready
,agent[1].aintf.mon_clk.channel_ready
,agent[0].aintf.mon_clk.channel_ready};//是agent中的接口,命名成了aintf
endfunction
virtual task gen_stop_callback();
bit[0:2] channel_ready_flag;//表示赋值?
$display("gen_stop_callback enterred");
@(posedge agent[0].aintf.rstn);
forever begin
@(posedge agent[0].aintf.clk);
channel_ready_flag=this.get_channel_ready_flag();
if($countones(channel_ready_flag)<=1) break;//只剩一个以下的通道不给写入数据了,就break
//这个还得再商榷一下
end
$display("%s: stop 3 generators running", this.name);
-> this.gen_stop_event;//这个就是gen_stop的event
endtask
virtual task run_stop_callback();
$display("run_stop_callback enterred");
$display("%s: waiting DUT transfering all of data", this.name);
fork
wait(agent[0].aintf.slave_margin=='h0);
wait(agent[1].aintf.slave_margin=='h0);
wait(agent[2].aintf.slave_margin=='h0);
join
$display("%s: 3 channel fifos have transferred all data", this.name);
$display($sformatf("*****************%s finished********************", this.name));
$finish();
endtask
endclass: full_test
//**************************************************//
endpackage :ch_pkg
//**************************************************//
tb代码
`timescale 1ns/1ps
//*********************接口多了个mcdt的,monitor也需要时钟块**************************//
interface interface_chen (input clk, input rstn);
logic [31:0] channel_data;
logic channel_valid;
logic channel_ready;
logic [ 7:0] slave_margin;
clocking drv_clk @(posedge clk);
default input #1ns output #1ns;
input channel_ready, slave_margin;//采样
output channel_data, channel_valid;//驱动
endclocking
//定义了monitor的时钟块
clocking mon_clk @(posedge clk);
default input #1ns output #1ns;
input channel_ready, slave_margin, channel_data, channel_valid;
endclocking
endinterface
interface mcdt_chen_interface (input clk, input rstn);
//存放mcdt三个输出,monitor的时钟块
logic [31:0] mcdt_data_output;
logic mcdt_valid;
logic [ 1:0] mcdt_id;
clocking mon_clk @(posedge clk);
default input #1ns output #1ns;
input mcdt_data_output, mcdt_valid, mcdt_id;//都是input
endclocking
endinterface
//**************************************************//
module tb1_chen();//
logic clk;
logic rstn;
//原来的3个mcdt输出变成了接口
mcdt_chen dut(//括号里面的信号变成接口里面的
.clk(clk)
,.rstn(rstn)
,.channel1_data(ch1_intf.channel_data)//信号改为接口中的名称
,.channel1_valid(ch1_intf.channel_valid)
,.slave1_margin(ch1_intf.slave_margin)
,.channell_ready(ch1_intf.channel_ready)
,.channel2_data(ch2_intf.channel_data)
,.channel2_valid(ch2_intf.channel_valid)
,.channel2_ready(ch2_intf.channel_ready)
,.slave2_margin(ch2_intf.slave_margin)
,.channel3_data(ch3_intf.channel_data)
,.channel3_valid(ch3_intf.channel_valid)
,.channel3_ready(ch3_intf.channel_ready)
,.slave3_margin(ch3_intf.slave_margin)
,.mcdt_data_output(mcdt_data_output)
,.mcdt_valid(mcdt_valid)
,.mcdt_id(mcdt_id)
);
//*****************clk和rstn******************************//
task clk_gen(input int T);
clk <= 0;
forever begin
#(T/2) clk<= !clk;
end
endtask
initial begin
clk_gen(10);
end
task rstn_gen();
#10 rstn <= 0;
repeat(20) @(posedge clk);
rstn <= 1;
endtask
initial begin
rstn_gen();
end
import ch_pkg::*;//引入包中所有的类
//**************************************************//
basic_test b1;
burst_test b2;
full_test b3;
root_test test[string];
string name;
//**************接口************************//
interface_chen ch1_intf(.*);
interface_chen ch2_intf(.*);
interface_chen ch3_intf(.*);
mcdt_chen_interface mcdt_intf(.*);
initial begin
b1=new();
b2=new();
b3=new();
test["basic_test"]=b1;
test["burst_test"]=b2;
test["full_test"]=b3;
if($value$plusargs("TESTNAME=%s", name)) begin
if(test.exists(name)) begin//函数exists()来检查元素是否存在。
test[name].set_interface(ch1_intf, ch2_intf, ch3_intf, mcdt_intf);
test[name].run();
end
else begin
$fatal($sformatf("[ERRTEST], test name %s is invalid, please specify a valid name!", name));
end
end
end
endmodule
以上编译已经通过,可以打开仿真,结果如何等今晚再校验。
2022年7月2日18:21:55