fmt_pkg——MCDF验证环境(2)

按照文档描述可知,数据包长度有4个,并且根据数据包通过的时间定义4种带宽,所以先定义两个枚举变量,分别表示fifo的深度和fmt的带宽

package fmt_pkg(1);
import uvm::*;
`include "uvm_macros.svh"

typedef enum {SHORT, MED, LONG, ULTRA} fmt_fifo;
typedef enum {LOW_WIDTH, MED_WIDTH, HIGH_WIDTH, ULTRA_WIDTH} fmt_bandwidth_t;

从结构上看和chnl_pkg是一样的

 

 

fmt_trans

第一个类依然是从item开始写起,需要经过fmt的东西包括两个枚举变量显示fifo的状态,通道的id,存放payload的动态数组data[],length,32位奇偶校验,当然还有单比特位rsp。然后就是约束,注册,域的自动化,new函数。

class fmt_trans extends uvm_sequence_item;
rand fmt_fifo fifo;
rand fmt_bandwidth_t bandwidth;
rand bit [31:0] data[];
rand int [ 7:0] length;
rand int [31:0] parity;
rand int [ 1:0] ch_id;
bit rsp;

constraint cstr{
soft fifo=MED;
soft bandwidth=MED_WIDTH;
foreach(data[i]) soft data[i]=(length<<24)+(ch_id<<8)+i;
soft length=;
soft parity=;
soft ch_id=-1;
};

`uvm_object_utils_begin(fmt_trans)
	`uvm_field_enum(fifo)
	`uvm_field_enum(bandwidth)
	`uvm_field_array_int(data)
	`uvm_field_int(length)
	`uvm_field_int(parity)
	`uvm_field_int(ch_id)
`uvm_object_utils_end

function new (string name="fmt_trans");
	super.new();
endfunction

endclass: fmt_trans

ch_id是8位,约束只有两个枚举变量且用==符号,rsp也要域的自动化,new函数有参数name,域的自动化要UVM_ALL_ON。枚举变量由于新定义了名称,域的自动化要写两个名。

class fmt_trans extends uvm_sequence_item;
rand fmt_fifo fifo;
rand fmt_bandwidth_t bandwidth;
rand bit [31:0] data[];
rand int [ 7:0] length;
rand int [31:0] parity;
rand int [ 7:0] ch_id;
bit rsp;

constraint cstr{
soft fifo==MED;
soft bandwidth==MED_WIDTH;
};

`uvm_object_utils_begin(fmt_trans)
	`uvm_field_enum(fmt_fifo, fifo, UVM_ALL_ON)
	`uvm_field_enum(fmt_bandwidth_t, bandwidth, UVM_ALL_ON)
	`uvm_field_array_int(data, UVM_ALL_ON)
	`uvm_field_int(length, UVM_ALL_ON)
	`uvm_field_int(parity, UVM_ALL_ON)
	`uvm_field_int(ch_id, UVM_ALL_ON)
	`uvm_field_int(rsp, UVM_ALL_ON)
`uvm_object_utils_end

function new (string name="fmt_trans");
	super.new(name);
endfunction

endclass: fmt_trans

fmt_driver

driver要做拿item和发送,然后返回rsp

第一个版本,回忆不起来drive中间的函数做了什么

class fmt_driver extends uvm_driver #(fmt_trans);
local virtual fmt_intf intf;

function new(string name="fmt_driver", uvm_component parent);
	super.name(name, parent);
endfunction

function void set_interface(virtual fmt_intf intf);
	if (intf=null) $error("intf handle is emtpy!");
	else this.intf=intf;
endfunction

task run_phase(uvm_phase phase);
	fork
	do_reset();
	do_drive();
	join
endtask

task do_reset();
	forever begin
	@(negedge intf.drv_ck.rstn)
	intf.drv_ck.fmt_ready=0;
	intf.drv_ck.fmt_valid=0;
	intf.drv_ck.fmt_data=0;
	intf.drv_ck.fmt_first=0;
	intf.drv_ck.fmt_last=0;
	end
endtask

task do_drive();
	fmt_trans req, rsp;
	seq_item_port.get_next_item(req);
	fmt_write(req);
	void'($cast(rsp, req.clone()));
	rsp.rsp=1;
	rsp.set_sequence_id(req.get_sequence_id);
	seq_item_port.item_done(rsp);
endtask

function fmt_write();
endfunction

这里的driver有个mailbox,命名为fifo;声明了fifo的最大值fifo_bound以及对应带宽的变量data_consum_peroid;

要注册;

new函数对mailbox进行例化,对两个变量做初始化;

run_phase做了四个线程,,接收,消化,配置,复位;

配置:拿到req后根据枚举变量配置bound(64,256,512,2048)并以这个bound作为参数例化mailbox,同理还有带宽和配置变量data_consum_peroid(8,4,2,1);做完两个配置后句柄转换、点亮、设置id、结束.

复位:只需要非阻塞赋值将ready置0,只有ready,因为接口的output只有ready;clk和rstn不需要加时钟块

接收:在clk上升沿的10ps后,valid为高就检查bound会不会比mailbox存放的数据量还大(例化的时候是按bound去例化mailbox-fifo的),如果会的话说明出错了,直接break,如果是正常的,就在下一个clk的10ps后将fmt_data放进mailbox,并在1ps后拉高ready;若valid为低,则ready为低。

消耗:声明一个临时的32位bit data,mailbox调用try_get函数,变量为data且不返回,然后在1到data_consum_peroid中系统函数随机范围,执行触发事件clk 的上升沿。

第二个版本

class fmt_driver extends uvm_driver #(fmt_trans);
local virtual fmt_intf intf;
local mailbox #(fmt_data) fifo;
local int [10:0] bound;
local int [ 2:0] data_consume_period;

function new(string name="fmt_driver", uvm_component parent);
	super.name(name, parent);
	this.fifo=new();
	this.bound=256;
	this.data_consume_period=4;
endfunction

function void set_interface(virtual fmt_intf intf);
	if (intf=null) $error("intf handle is emtpy!");
	else this.intf=intf;
endfunction

task run_phase(uvm_phase phase);
	fork
	do_config();
	do_receive();
	do_reset();
	do_drive();
	join
endtask

task do_config();
	fmt_trans req, rsp;
	seq_item_port.get_next_item(req);
	case(req.fifo)
		SHORT:this.bound=64;
		MED  :this.bound=256;
		LONG :this.bound=512;
		ULTRA:this.bound=2048;
	endcase
	this.fifo=new(bound);
	case(req.bandwidth)
		LOW_WIDTH  :this.data_consume_period=8;
		MED_WIDTH  :this.data_consume_period=4;
		HIGH_WIDTH :this.data_consume_period=2;
		ULTRA_WIDTH:this.data_consume_period=1;
	endcase
	void'($cast(rsp, req.clone()));
	rsp.rsp=1;
	rsp.set_sequence_id(req.get_sequence_id);
	seq_item_port.item_done(rsp);
endtask

task do_reset();
	forever begin
	@(negedge intf.rstn)
	intf.drv_ck.fmt_ready<=0;
	end
endtask

task do_receive();
	forever begin
	@(posedge intf.clk) #10ps;
	if(intf.drv_ck.fmt_valid===1'b1) begin
		if(this.bound-this.fifo.num()>=1)  begin 
		break;
		else @(posedge intf.clk) #10ps; end
		this.fifo.put(intf.drv_ck.fmt_data);
		#1ps intf.drv_ck.fmt_ready<=1'b1;
	else intf.drv_ck.fmt_ready<=1'b0; end
endtask

task do_consume();
	bit [31:0] data;
	forever begin
	void'(fifo.try_get(data));
	repeat($ur_random(1,data_consume_period)) @(posedge intf.clk); end
endtask

endclass: fmt_driver

mailbox的类型是32位bit,可以用接口中的data吗?

bound和period没有限定位数;

要注册!

4个线程要加this强调在这个class中;

do_config要加forever,永不停歇

用时钟块表示clk上升沿:@(intf.drv_ck)?

if中的valid不用加时钟块;

valid为1后要forever;

随机范围的系统函数是$urandom_range();

第三个版本

class fmt_driver extends uvm_driver #(fmt_trans);
local virtual fmt_intf intf;
local mailbox #(fmt_data) fifo;
local int bound;
local int data_consume_period;

function new(string name="fmt_driver", uvm_component parent);
	super.name(name, parent);
	this.fifo=new();
	this.bound=256;
	this.data_consume_period=4;
endfunction

function void set_interface(virtual fmt_intf intf);
	if (intf=null) $error("intf handle is emtpy!");
	else this.intf=intf;
endfunction

task run_phase(uvm_phase phase);
	fork
	this.do_config();
	this.do_receive();
	this.do_reset();
	this.do_drive();
	join
endtask

task do_config();
	fmt_trans req, rsp;
	forever begin
	seq_item_port.get_next_item(req);
	case(req.fifo)
		SHORT:this.bound=64;
		MED  :this.bound=256;
		LONG :this.bound=512;
		ULTRA:this.bound=2048;
	endcase
	this.fifo=new(this.bound);
	case(req.bandwidth)
		LOW_WIDTH  :this.data_consume_period=8;
		MED_WIDTH  :this.data_consume_period=4;
		HIGH_WIDTH :this.data_consume_period=2;
		ULTRA_WIDTH:this.data_consume_period=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

task do_reset();
	forever begin
	@(negedge intf.rstn)
	intf.drv_ck.fmt_ready<=0;
	end
endtask

task do_receive();
	forever begin
	@(intf.drv_ck) #10ps;
	if(intf.fmt_valid===1'b1) begin
		forever begin
		if((this.bound-this.fifo.num())>=1) break;
		@(intf.drv_ck); #10ps; end
		//else @(posedge intf.clk) #10ps; end
		this.fifo.put(intf.fmt_data);
		#1ps; intf.fmt_ready<=1'b1; end
	else  begin #1ps; intf.fmt_ready<=1'b0; end
	end
endtask

task do_consume();
	bit [31:0] data;
	forever begin
	void'(fifo.try_get(data));
	repeat($urandom_range(1, this.data_consume_period)) @(posedge intf.clk); 
	end
endtask

endclass: fmt_driver

fmt_sequencer

注册和new函数即可

class fmt_sequencer extends uvm_sequencer #(fmt_trans);
`uvm_component_utils(fmt_sequencer)

function new(string name="fmt_sequencer", uvm_component parent);
	super.new(name, parent);
endfunction

endclass: fmt_sequencer

fmt_sequence

sequence需要去做什么呢?

对两个枚举变量进行配置,所以要进行声明和随机化、域的自动化;

发送时的约束先参考chnl_pkg的。

第一个版本

class fmt_sequence extends uvm_sequence #(fmt_trans);
rand fmt_fifo fifo;
rand fmt_bandwidth_t bandwidth;
constraint cstr {
	soft fifo==MED;
	soft bandwidth==MED_WIDTH;
};

`uvm_object_utils_begin(fmt_sequence)
	`uvm_field_enum(fmt_fifo, fifo, UVM_ALL_ON)
	`uvm_field_enum(fmt_bandwidth_t, bandwidth, UVM_ALL_ON)
`uvm_object_utils_end
`uvm_declare_p_sequencer(fmt_sequencer)

function new(string name="fmt_sequence");
	super.new(name);
endfunction

task body();
	this.send_trans();
endtask

task send_trans();
	fmt_trans req, rsp;
	`uvm_do_with(req, {
	local::fifo!=MED ->fifo==local::fifo;
	local::bandwidth!=MED_WIDTH ->bandwidth==local::bandwidth;
	})
	`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

function void post_randomize();
	string s;
	s={s, "after randomization\n"};
	s={s, super.sprint()};
	`uvm_info(get_type_name(), s, UVM_HIGH)
endfunction

endclass: fmt_sequencer

注意sequence也是个参数类,和driver、sequencer传递的都是item;

body中的任务没有加this;

fmt_monitor

monitor做的事:有接口,声明和例化ap,run_phase中对data进行采集

参考chnl的,写完觉得这里可能会需要一个fifo,因为发包要根据length拿到足够的payload

第一个版本

class fmt_monitor extends uvm_monitor;
local virtual fmt_intf intf;
uvm_analysis_port #(fmt_trans) mon_ana_port;
`uvm_component_utils(fmt_monitor)

function void set_interface(virtual fmt_intf intf);
	if(intf==null) $error("intf handle empty");
	else this.intf=intf;
endfunction

function new(string name="fmt_monitor", uvm_component parent);
	super.new(name, parent);
	mon_ana_port=new();
endfunction

task run_phase(uvm_phase phase);
	this.mon_trans();
endtask

task mon_trans();
	fmt_trans m;
	forever begin
	@(intf.mon_ck iff (intf.mon_ck.fmt_valid===1'b1 && intf.mon_ck.fmt_ready===1'b1));
	m.data= intf.mon_ck.fmt_data;
	mon_ana_port.write(m);
	`uvm_info(get_type_name(), $sformatf("monitor fmt data 'h%8x", m.data), UVM_HIGH) end
endtask

endclass: fmt_monitor

ap例化时要带上“名字”和this

根据fmt数据包格式这个图来看fmt的monitor需要做的是什么,首先接口中first和last都是单比特变量,点亮表示发送到对应的payload,所以在first、valid、ready都为高时表示开始传输包的第一个payload,payload的高8位是ch_id,次高8位是length(对应fmt的item),最后一个payload是32的奇偶校验,也对应item中的parity。所以在三个信号同时拉高时做id和length的写入,然后对data[]分配空间(根据length),采集每个data。根据i和size函数的关系判断是否到最后一个payload,是则采集到parity,否就用触发事件检查valid和ready是否还为高。最后是声明一个临时字符串s来打印必要的消息。

这里还需要例化item的句柄,那为什么chnl_monitor中的就没有例化呢?那边是包含了data和id的结构体,不需要例化,这里是sequence_item的句柄,需要例化。

第二个版本

class fmt_monitor extends uvm_monitor;
local string name;
local virtual fmt_intf intf;
uvm_analysis_port #(fmt_trans) mon_ana_port;
`uvm_component_utils(fmt_monitor)

function void set_interface(virtual fmt_intf intf);
	if(intf==null) $error("intf handle empty");
	else this.intf=intf;
endfunction

function new(string name="fmt_monitor", uvm_component parent);
	super.new(name, parent);
	mon_ana_port=new("mon_ana_port", this);
endfunction

task run_phase(uvm_phase phase);
	this.mon_trans();
endtask

task mon_trans();
	fmt_trans m;
	forever begin
	@(intf.mon_ck iff (intf.mon_ck.fmt_first===1'b1 && intf.mon_ck.fmt_valid===1'b1 && intf.mon_ck.fmt_ready===1'b1));
	m=new();
	m.ch_id =intf.mon_ck.fmt_data[31:24];
	m.length=intf.mon_ck.fmt_data[23:16];
	m.data[]=new(m.length+3);
	foreach(data[i]) begin 
	m.data[i]=intf.mon_ck.fmt_data;
	if(i==m.data.size()-1) m.parity=intf.mon_ck.fmt_data;
	if(i< m.data.size()-1) @(intf.mon_ck iff(intf.mon_ck.fmt_valid===1'b1 && intf.mon_ck.fmt_ready===1'b1));
	end
	mon_ana_port.write(m);
	string s;
	s={s, $sformatf("channel id %0d", m.ch_id)};
	s={s, $sformatf("payload length %0d", m.length)};	
	`uvm_info(get_type_name(), s, UVM_HIGH)
endtask

endclass: fmt_monitor

动态函数的分配空间是new[]不是括号;

foreach里面主要加m前缀;

判断是最后一个payload的话,将m的data[i]给到parity而不是还从fmt_data拿,因为已经拿过了

有关打印的格式:

System_Verilog打印格式_我不是悍跳狼丶的博客-CSDN博客

第三个版本

class fmt_monitor extends uvm_monitor;
local string name;
local virtual fmt_intf intf;
uvm_analysis_port #(fmt_trans) mon_ana_port;
`uvm_component_utils(fmt_monitor)

function void set_interface(virtual fmt_intf intf);
	if(intf==null) $error("intf handle empty");
	else this.intf=intf;
endfunction

function new(string name="fmt_monitor", uvm_component parent);
	super.new(name, parent);
	mon_ana_port=new("mon_ana_port", this);
endfunction

task run_phase(uvm_phase phase);
	this.mon_trans();
endtask

task mon_trans();
	fmt_trans m;
	forever begin
	@(intf.mon_ck iff (intf.mon_ck.fmt_first===1'b1 && intf.mon_ck.fmt_valid===1'b1 && intf.mon_ck.fmt_ready===1'b1));
	m=new();
	m.ch_id =intf.mon_ck.fmt_data[31:24];
	m.length=intf.mon_ck.fmt_data[23:16];
	m.data[]=new(m.length+3);
	foreach(data[i]) begin 
	m.data[i]=intf.mon_ck.fmt_data;
	if(i==m.data.size()-1) m.parity=intf.mon_ck.fmt_data;
	if(i< m.data.size()-1) @(intf.mon_ck iff(intf.mon_ck.fmt_valid===1'b1 && intf.mon_ck.fmt_ready===1'b1));
	end
	mon_ana_port.write(m);
	string s;
	s={s, $sformatf("%0t %s monitored a packet\n", $time, this.m.name )};
	s={s, $sformatf("channel id %0d\n", m.ch_id)};
	s={s, $sformatf("payload length %0d\n", m.length)};	
	foreach(m.data[i]) s={s, $sformatf("data[%0d]=%8x\n", i, m.data[i])};
	`uvm_info(get_type_name(), s, UVM_HIGH)
endtask

endclass: fmt_monitor

 打印时name那里是m_name,我先用m.试试看。

fmt_agent

例化组件,连接端口

class fmt_agent extends uvm_agent;
fmt_driver driver;
fmt_sequencer sequencer;
fmt_monitor monitor;
local virtual fmt_intf intf;
`uvm_component_utils(fmt_agent)

function build_phase(uvm_phase phase);
	super.build(phase);
	driver=fmt_driver::type_id::create("driver", this);
	sequencer=fmt_sequencer::type_id::create("sequencer", this);
	monitor=fmt_monitor::type_id::create("monitor", this);
	this.set_interface(intf);
endfunction

function void set_interface(virtual fmt_intf intf);
	fork
	this.monitor.set_interface(intf);
	this.driver.set_interface(intf);
	join
endfunction

function connect_phase(uvm_phase phase);
	super.connect(phase);
	this.driver.seq_item_port.connect(sequencer.seq_item_export);
endfunction

task run();
	super.run();
endtask

endclass: fmt_agent

接口怎么连接?用config机制if()在build phase,然后再connect_phase调用set_interface

      if(!uvm_config_db#(virtual fmt_intf)::get(this,"","vif", vif)) begin
        `uvm_fatal("GETVIF","cannot get vif handle from config DB")
      end

agent是comp,也要new函数;

没有run phase了;

set_interface不用fork join;

组件都不用加this前缀;

build和connect两个phase都是void类型

第二个版本

class fmt_agent extends uvm_agent;
fmt_driver driver;
fmt_sequencer sequencer;
fmt_monitor monitor;
local virtual fmt_intf intf;
`uvm_component_utils(fmt_agent)

function new(string name="fmt_agent", uvm_component parent);
	super.new(name, parent);
endfunction

function void build_phase(uvm_phase phase);
	super.build_phase(phase);
	if(!uvm_config_db #(virtual fmt_intf)::get("intf","","this", this)) begin
		`uvm_fatal("GET INTF", "no intf!") end
	driver=fmt_driver::type_id::create("driver", this);
	sequencer=fmt_sequencer::type_id::create("sequencer", this);
	monitor=fmt_monitor::type_id::create("monitor", this);
endfunction

function void set_interface(virtual fmt_intf intf);
	monitor.set_interface(intf);
	driver.set_interface(intf);
endfunction

function void connect_phase(uvm_phase phase);
	super.connect_phase(phase);
	driver.seq_item_port.connect(sequencer.seq_item_export);
	this.set_interface(intf);
endfunction

endclass: fmt_agent

 uvm两句话少了个_pkg

 item只有两个枚举变量是随机变量,且不能是int而是bit;

driver中mailbox的类型是bit [31:0],不能直接用接口的fmt_data;

set_interface的if括号中是等式;

foreach中忘记加m前缀;

声明字符串s必须在一开始;

forecer begin少了end;

动态数组m.data=new[]前面不用[];

driver忘记注册导致无法例化;

 拿下。

 

  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值