按照文档描述可知,数据包长度有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忘记注册导致无法例化;
拿下。