系统框图:
知识点:
1:void函数:不能等待,不能包括时延,不能包括事件触发
代码分析:
1:fmt_pkg.sv部分
package fmt_pkg; //实现一个被动的响应,模拟一个fifo出来
import rpt_pkg::*;
typedef enum {SHORT_FIFO, MED_FIFO, LONG_FIFO, ULTRA_FIFO} fmt_fifo_t;
typedef enum {LOW_WIDTH, MED_WIDTH, HIGH_WIDTH, ULTRA_WIDTH} fmt_bandwidth_t;
class fmt_trans;
class fmt_driver;
task run();
fork
this.do_receive(); //接收数据
this.do_consume(); //发送数据
this.do_config(); //配置
this.do_reset(); //复位
join
endtask
class fmt_generator;
class fmt_monitor;
class fmt_agent;
endpackage
fmt_driver作为responder存在的,是一个不断接受的数据的从端slave,类似一个fifo,并且位宽不定。不断地接收来自formatter的数据,按照fifo的位宽不断地发送数据。do_config配置参数,do_reset复位,do_receive和do_consume负责接收和发送数据。
do_receive不断地从从formatter里面拿数据。grant=1代表同意接收数据,grant=1也是有条件的(确保有余量存在),然后等待fmt_start被拉高,然后下个时钟周期再把fmt_grant拉低,开始接收数据,重复次数是fmt_length,不用等待前面的fmt_grant拉低。
do_consume的作用是按照我们给的fifo宽度不断地从fifo里面拿取数据,即使没有。并根据带宽决定发送数据的周期。fifo的宽度是在do_config里面配置的。
fmt_monitor把数据发送给mcdf_checker,用作比较。
2:mcdf_pkg.sv部分
硬件的输入主要是reg_agent和chnl_agent,在这两个共同作用下汇集到formatter里面。mcdf_checker模拟硬件的行为,数据进行打包在do_packet里面进行,更新寄存器模型的方法do_reg_config。打包以后放入out_mbs里面,根据in_mbs输入的不同长度进行打包,依据偿付分配到相应的out_mbs里面,所以refmod里面放置三个缓存,对应着三个chanl,无论是formatter还是refmod里面拿出来的都是数据包。
`include "param_def.v"
package mcdf_pkg;
import chnl_pkg::*;
import reg_pkg::*;
import arb_pkg::*;
import fmt_pkg::*;
import rpt_pkg::*;
typedef struct packed { } //数据结构体包含长度、优先级、使能信号、余量
typedef enum {RW_LEN, RW_PRIO, RW_EN, RD_AVAIL} mcdf_field_t; //表示当前域
class mcdf_refmod;
class mcdf_checker;
class mcdf_env;
class mcdf_base_test;
class mcdf_data_consistence_basic_test extends mcdf_base_test;
endpackage
mcdf_refmod:参考上图中,主要三个功能 do_reset、do_packet和do_reg_update.
mcdf_checker:最主要的就是do_compare,cmp两组数据比较结果为1 还是为0,来打印比较的成功还是失败,并对total_count和chnl_count计数。
mcdf_env:整合环境,包含 chnl_agent chnl_agts[3]、reg_agent reg_agt、fmt_agentfmt_agt、mcdf_checker chker。
mcdf_base_test;
virtual task run();
fork
env.run();
join_none
rpt_pkg::rpt_msg("[TEST]",
$sformatf("=====================%s AT TIME %0t STARTED=====================", this.name, $time),
rpt_pkg::INFO,
rpt_pkg::HIGH);
this.do_reg();
this.do_formatter();
this.do_data();
rpt_pkg::rpt_msg("[TEST]",
$sformatf("=====================%s AT TIME %0t FINISHED=====================", this.name, $time),
rpt_pkg::INFO,
rpt_pkg::HIGH);
this.do_report();
$finish();
endtask
首先运行do_reg,让driver配置寄存器,然后do_formatter,因为formatter需要模仿MCDF的下行,需要提前做好准备,配置好参数,再运行do_data。
cdf_data_consistence_basic_test extends mcdf_base_test:通过移动该有的位数配置数据,然后分别读取写入的数据和读回来的值进行比较,看是否出错。
3:reg_pkg部分
大体上和实验三类似
4:rpt_pkg部分
适应UVM中对于消息的管理。
测试功能点
测试代码参考:路科验证MCDF_svlab4笔记_Hardworking_IC_boy的博客-优快云博客_路科验证
1、寄存器读写测试:
测试所有控制寄存器的读写、所有状态寄存器的读写(状态寄存器只读)。
class mcdf_reg_stability_test extends mcdf_base_test;
function new(string name = "mcdf_data_consistence_basic_test");
super.new(name);
endfunction
task do_reg();
bit[7:0] chnl_rw_addrs[] = '{`SLV0_RW_ADDR, `SLV1_RW_ADDR, `SLV2_RW_ADDR};
bit[7:0] chnl_ro_addrs[] = '{`SLV0_R_ADDR, `SLV1_R_ADDR, `SLV2_R_ADDR};
int pwidth = `PAC_LEN_WIDTH + `PRIO_WIDTH + 1; //=3+2+1=6
bit[31:0] check_pattern[] = '{((1<<pwidth)-1), 0, ((1<<pwidth)-1)};
bit[31:0] wr_val, rd_val;
// RW register access and bits toggle
foreach(chnl_rw_addrs[i]) begin
foreach(check_pattern[i]) begin
wr_val = check_pattern[i];
this.write_reg(chnl_rw_addrs[i], wr_val);
this.read_reg(chnl_rw_addrs[i], rd_val);
void'(this.diff_value(wr_val, rd_val));
end
end
// RO register read access
foreach(chnl_ro_addrs[i]) begin
this.read_reg(chnl_ro_addrs[i], rd_val);
end
// send IDLE command
this.idle_reg();
endtask
endclass
check_pattern为要写入寄存器的数值,通过比较rd_val和wr_val是否一致,来验证控制寄存器的读写。
2、寄存器稳定性测试:
测试读写寄存器的bit(31:6)是否无法写入。
class mcdf_reg_illegal_access_test extends mcdf_base_test;
function new(string name = "mcdf_reg_illegal_access_test");
super.new(name);
endfunction
task do_reg();
bit[7:0] chnl_rw_addrs[] = '{`SLV0_RW_ADDR, `SLV1_RW_ADDR, `SLV2_RW_ADDR};
bit[7:0] chnl_ro_addrs[] = '{`SLV0_R_ADDR, `SLV1_R_ADDR, `SLV2_R_ADDR};
int pwidth = `PAC_LEN_WIDTH + `PRIO_WIDTH + 1; //=6
bit[31:0] check_pattern[] = '{32'h0000_FFC0, 32'hFFFF_0000};
bit[31:0] wr_val, rd_val;
// RW register write reserved field and check
foreach(chnl_rw_addrs[i]) begin
foreach(check_pattern[j]) begin
wr_val = check_pattern[j];
this.write_reg(chnl_rw_addrs[i], wr_val);
this.read_reg(chnl_rw_addrs[i], rd_val);
void'(this.diff_value(wr_val & ((1<<pwidth)-1), rd_val));//将期望值和读出来的值对比
end
end
// RO register write reserved field and check (no care readable field
// value)
foreach(chnl_ro_addrs[i]) begin
wr_val = 32'hFFFF_FF00;
this.write_reg(chnl_ro_addrs[i], wr_val);
this.read_reg(chnl_ro_addrs[i], rd_val);
void'(this.diff_value(0 , rd_val & 32'hFFFFFF00));
end
// send IDLE command
this.idle_reg();
endtask
endclass