Bootstrap

路科MCDF项目UVM环境搭建之fmt_pkg

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);
;