目录
接着上篇的 MCDF实验4(1)MCDF实验4(1)
给出 class fmt_driver 的完整代码
class fmt_driver; // fmt 的 driver 类似于一个 fifo 故 接收 do_receive() 并发送 do_consume()
local string name;
local virtual fmt_intf intf;
mailbox #(fmt_trans) req_mb;
mailbox #(fmt_trans) rsp_mb;
local mailbox #(bit[31:0]) fifo; // 让 driver 模拟 fifo
local int fifo_bound; // fifo 的 最大深度
local int data_consum_peroid; // 消耗 周期 也就是侧面反映了 带宽
function new(string name = "fmt_driver");
this.name = name;
this.fifo = new(); // 后面的 do_config 已经 new() 过 fifo 在这还有没有必要 new() 为什么 给它的 容量是无穷大4096 不是小的数?
this.fifo_bound = 4096;
this.data_consum_peroid = 1;
endfunction
function void set_interface(virtual fmt_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();
fork
this.do_receive(); // do_reveice() 和 do_consume() 并行 是因为 这些行为就类似硬件的行为 它们随时都可以进行
this.do_consume();
this.do_config(); // 对 driver 进行配置, 为了配置它的行为,让它的行为表现的更像一个 buffer
this.do_reset();
join
endtask
task do_config(); // 相当于 别的模块的 do_drive()
fmt_trans req, rsp;
forever begin
this.req_mb.get(req);
case(req.fifo) // 下面的两个case 相当于别的模块的 reg_write()
SHORT_FIFO: this.fifo_bound = 64;
MED_FIFO: this.fifo_bound = 256;
LONG_FIFO: this.fifo_bound = 512;
ULTRA_FIFO: this.fifo_bound = 2048;
endcase
this.fifo = new(this.fifo_bound);
case(req.bandwidth)
LOW_WIDTH: this.data_consum_peroid = 8;
MED_WIDTH: this.data_consum_peroid = 4;
HIGH_WIDTH: this.data_consum_peroid = 2;
ULTRA_WIDTH: this.data_consum_peroid = 1;
endcase
rsp = req.clone();
rsp.rsp = 1;
this.rsp_mb.put(rsp);
end
endtask
task do_reset();
forever begin
@(negedge intf.rstn)
intf.fmt_grant <= 0;
end
endtask
task do_receive(); // 模仿 formatter 的 时序 接收数据
forever begin
@(posedge intf.fmt_req);
forever begin
@(posedge intf.clk);
if((this.fifo_bound-this.fifo.num()) >= intf.fmt_length) // 括号里的表示agent(fifo)的余量
break;
end
intf.drv_ck.fmt_grant <= 1;
@(posedge intf.fmt_start); // start 开始数据发送 对于 fifo 也就是接收数据
fork
begin
@(posedge intf.clk);
intf.drv_ck.fmt_grant <= 0;
end
join_none
repeat(intf.fmt_length) begin // repeat 是 接收数据 不用等待上面的 grant 信号
@(negedge intf.clk);
this.fifo.put(intf.fmt_data);
end
end
endtask
task do_consume();
bit[31:0] data;
forever begin
void'(this.fifo.try_get(data)); // 从 fifo 中 不管有没有数据 都尝试拿一个数据,并经过 data_consum_peroid 次周期 一直拿
repeat($urandom_range(1, this.data_consum_peroid)) @(posedge intf.clk);
end
endtask
endclass
fmt_generator
它和其他的 chnl_generator , reg_generator 相差不大
class fmt_generator;
rand fmt_fifo_t fifo = MED_FIFO;
rand fmt_bandwidth_t bandwidth = MED_WIDTH; // 数据的初始化
mailbox #(fmt_trans) req_mb;
mailbox #(fmt_trans) rsp_mb; // 创建与 driver 通信的 mailbox
constraint cstr{
soft fifo == MED_FIFO;
soft bandwidth == MED_WIDTH; // 数据的约束
}
function new();
this.req_mb = new();
this.rsp_mb = new(); // mailbox的 实例化
endfunction
task start();
send_trans();
endtask
// generate transaction and put into local mailbox
task send_trans();
fmt_trans req, rsp;
req = new();
assert(req.randomize with {local::fifo != MED_FIFO -> fifo == local::fifo;
local::bandwidth != MED_WIDTH -> bandwidth == local::bandwidth;
})
else $fatal("[RNDFAIL] formatter packet randomization failure!");
$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("fmt_generator object content is as below: \n")};
s = {s, $sformatf("fifo = %s: \n", this.fifo)};
s = {s, $sformatf("bandwidth = %s: \n", this.bandwidth)};
s = {s, $sformatf("=======================================\n")};
return s;
endfunction
function void post_randomize();
string s;
s = {"AFTER RANDOMIZATION \n", this.sprint()};
$display(s);
endfunction
endclass
主要的步骤都一样:
主要的就是数据不同,fmt_generator 有两个数据: fmt_fifo_t fmt_bandwidth_t
这两个数据都是对 fmt_driver 模拟 fifo 而配置的,一个深度,一个带宽,由我们在顶层调用时,传进来它的配置。
fmt_monitor
在 monitor 中,创建一个 mailbox, 但mailbox的例化, 在chnl_chenker 中,监测 来自 fmt 的数据,假定协议是正确的,然后接收来自总线的 数据,数据id, chnl_id。
class fmt_monitor;
local string name;
local virtual fmt_intf intf;
mailbox #(fmt_trans) mon_mb;
function new(string name="fmt_monitor");
this.name = name;
endfunction
function void set_interface(virtual fmt_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();
fmt_trans m;
string s;
forever begin
@(posedge intf.mon_ck.fmt_start); // 在这里 假设协议是正确的
m = new();
m.length = intf.mon_ck.fmt_length;
m.ch_id = intf.mon_ck.fmt_chid;
m.data = new[m.length]; // 为 data 开辟 m.length 这么大的空间 动态数组的例化
foreach(m.data[i]) begin
@(posedge intf.clk);
m.data[i] = intf.mon_ck.fmt_data;
end
mon_mb.put(m);
s = $sformatf("=======================================\n");
s = {s, $sformatf("%0t %s monitored a packet: \n", $time, this.name)};
s = {s, $sformatf("length = %0d: \n", m.length)};
s = {s, $sformatf("chid = %0d: \n", m.ch_id)};
foreach(m.data[i]) s = {s, $sformatf("data[%0d] = %8x \n", i, m.data[i])};
s = {s, $sformatf("=======================================\n")};
$display(s);
end
endtask
endclass
fmt_agent
fmt_agent 里面包含了两个组件 fmt_driver 和 fmt_monitor , 故内容也简单,就是对这两个组件的例化,以及接口的传递,最后让 fmt_driver 和 fmt_monitor 运行,同时运行run()。
class fmt_agent;
local string name;
fmt_driver driver;
fmt_monitor monitor;
local virtual fmt_intf vif; // 这个接口 为啥是 local virtual
function new(string name = "fmt_agent");
this.name = name;
this.driver = new({name, ".driver"});
this.monitor = new({name, ".monitor"});
endfunction
function void set_interface(virtual fmt_intf vif);
this.vif = vif;
driver.set_interface(vif);
monitor.set_interface(vif);
endfunction
task run();
fork
driver.run();
monitor.run();
join
endtask
endclass