fmt_pkg
fmt_agent与其他chnl_agent区别
注:
(1)与chnl_agent的initator不同,fmt_agent是属于responder类型,属于响应类型,接收到formatter的req信号后,fmt_agent模仿formatter的下行来消化数据,下行fifo的大小决定了何时可将grant信号(receive)拉高,fifo的带宽(消化数据周期)决定了下行消化(consume)速度
(2)
①模仿的下行fifo空间小,消化data慢,之后grant信号拉高的时间就晚;上行formatter传输data较快,而下行传输慢,所以data消耗仍需慢慢进行。
②模仿的下行fifo空间大,消化data快,grant信号拉高的时间就早一些,如时序图便可以在req拉高的的第二个周期拉高grant;
代码分析
1.定义两个枚举类型变量 fmt_fifo_t,fmt_bandwidth_t
typedef enum {SHORT_FIFO,MED_FIFO,LONG_FIFO,ULTRA_FIFO} fmt_fifo_t;
typedef enum {LOW_WIDTH,MED_WIDTH,HIGH_WIDTH,ULTRA_WIDTH} fmt_bandwith_t;
2.fmt_trans
(1)定义length(整形包长度),data,ch_id(便于从接口采集),以及两个枚举变量fifo和bandwidth,加constraint,便于向drv发送所需配置的fifo参数
(2)new函数
3.⭐fmt_driver
注:属于responder类型,向formatter返回发送回grant准许信号,并根据seq发送来的trans进行本地fifo配置,对formatter发送的数据进行接收消化作用。
(1)定义模拟下行fifo为mailbox类型,定义fifo_bound容量,data_consum_period数据消化周期;在new函数中对fifo进行创建,并对fifo_bound和data_consum_period进行赋初值;利用set_interface传递接口;
(2)在run_phase中并行调用四个函数,do_reset,do_config,do_receive,do_consume;
①do_reset:复位函数控制grant信号,当接口复位信号低电平时,将接口grant信号拉低;
②do_config(配置作用):通过接收seq发送来的req(fmt_trans类型)进行本地fifo_bound和data_consum_period(与bandwidth相关)进行配置,同时返回rsp给seq;
task do_config();//gen ju trans's fifo and bandwidth do_config
fmt_trans req,rsp;
forever begin
seq_item_port.get_next_item(req);
case(req.fifo)
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);//chuang xin chaung jian
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
void'($cast(rsp,req.clone()));
rsp.rsp = 1;
rsp.set_sequence_id(req.get_sequence_id);
seq_item_port.item_done(rsp);
end
endtask
③do_receive:接收formatter上行发过来的数据,在本地fifo容量大于整形包长度时,将grant拉高,代表下行fifo可以接收数据;之后等待fmt_start出现上升沿进行发送数据,在下一个周期将grant拉低,并将数据put进fifo中;注:grant拉高在此函数中。
task do_receive();
forever begin
@(posedge intf.fmt_req);
forever begin
if((this.fifo_bound-this.fifo.num()) >= intf.fmt_length)
break;
end
intf.fmt_grant <= 1;
@(posedge intf.fmt_start);
fork
begin
@(posedge intf.clk);
intf.fmt_grant <= 0;
end
join_none
repeat(intf.fmt_length) begin
@(posedge intf.clk);
this.fifo.put(intf.fmt_data);
end
end
endtask
④do_consume:消化数据,根据data_consum_period进行数据消化,周期越短数据消化越快;
task do_consume();
bit [31:0] data;
forever begin
void'(this.fifo.try_get(data));
repeat($urandom_range(1,this.data_consum_peroid)) @(posedge intf.clk);
end
endtask
4.⭐fmt_config_sequence
配置fifo的大小和带宽
(1)定义fifo和bandwidth,进行constraint,同时利用宏声明p_sequencer;(注:在seq中重新定义fifo和bandwidth是方便配置fmt_trans(item)里的fifo和bandwidth;同时在后续接入virtual sequence不同的case(mcdf_data_consistence_basic_virtual_sequence)后,在这些seq_case中对内部的fmt_seq进行配置时,就会配置这些重新定义的fifo和bandwidth,更像是层次化的配置,seq_case配置fmt_seq,在fmt_seq配置fmt_trans,之后driver接收trans配置自身的fifo。seq_case配置如下
`uvm_do_on_with(fmt_config_seq, p_sequencer.fmt_sqr, {fifo == LONG_FIFO; bandwidth == HIGH_WIDTH;})
)
rand fmt_fifo_t fifo = MED_FIFO;
rand fmt_bandwidth_t bandwidth = MED_WIDTH;
`uvm_declare_p_sequencer(fmt_sequencer)
(2)在body中send_trans;
(3)send_trans:定义两个trans,req利用宏进行创建,随机化,发送,rsp用来接收driver返回回来的句柄;
task send_trans();
fmt_trans req,rsp;
`uvm_do_with(req,{
local::fifo !=MED_FIFO -> fifo = local::fifo;
local::bandwidth !=MED_WIDTH ->bandwidth = local::fifo;
})
`uvm_info(get_type_name(), req.sprint(), UVM_HIGH)
get_response(rsp);
`uvm_info(get_type_name(), rsp.sprint(), UVM_HIGH)
assert(rsp.rsp)
else $error("[RSPERR] %0t error response received!", $time);
endtask
注:seq不需要与sqr在此pkg中挂载,在uvm中是通过virtual seq和virtual sqr在test中进行挂载,之后virtual seq内部的各个seq(如fmt_config_seq) 通过宏挂载到p_sequencer (p_sequencer.fmt_sqr) 上,进行发送。
5.fmt_monitor
进行数据采集监测,同时向scb发送;
(1)定义接口,传递接口,定义向scb传输数据的端口mon_bp_port;
(2)run_phase中调用mon_trans;
mon_trans:创建fmt_trans的对象m(需要new),在intf开始start数据时,也开始采集数据,采集完后通过port向scb发送trans;
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];
foreach(m.data[i]) begin
@(posedge intf.clk);
m.data[i]= intf.mon_ck.fmt_data;
end
mon_bp_port.put(m);
s = $sformatf("=======================================\n");
s = {s, $sformatf("%0t %s monitored a packet: \n", $time, this.m_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")};
`uvm_info(get_type_name(), s, UVM_HIGH)
end
endtask
6.fmt_agent
(1)build_phase中创建driver,monitor,sequencer对象
(2)传递接口set_interface
local virtual fmt_intf vif;
function void set_interface(virtual fmt_intf vif);
this.vif = vif;
this.driver.set_interface(vif);
this.monitor.set_interface(vif);
endfunction
(3)connect_phase中对driver和sequencer进行端口连接
this.driver.seq_item_port.connect(this.monitor.seq_item_export);