前言:在实验3添加了随机约束来随机化产生的数据,使用了信箱来完成类之间的通信,添加了monitor、checker让验证的组件越来越完整。
种一棵树最好的时间是十年前,其次是现在。不是吗?
实验3需要用到随机化、信箱、类的多态这几个主要知识,大家可以先去学习哦!
- 在验证中习惯把各个验证文件独立放置,在这次实验把package的内容和tb内容分成两个文件,仿真编译先编译package,再编译tb文件。
- 对实验2generator和initiator中数据生成和数据的传输做了升级。
- 添加了验证的新组件monitor和checker。
源代码
将package和tb文件分开成两个文件。
1. package
在package包括了 chnl_trans、chnl_initiator、chnl_generator、chnl_monitor、chnl_checker和chnl_root_test。
package chnl_pkg3;
// static variables shared by resources
semaphore run_stop_flags = new();
1.1 chnl_trans
chnl_trans在实验2用来存放数据,内部结构很简单。在实验3给他添加了随机约束,让产生的数据更复杂。
添加了控制数据传输间隔的变量,声明为了随机变量,trans中的数据从一个数据变成了多个数据。
function new:
1.11 trans中数据的随机化
变量声明前加rand,在constraint中添加约束。
新添加了pkt_nidles,代表数据包之间的间隔。假设一共有5批货,每批货100件,1件与1件的间隔是data_nidles,当发完第一批货的100件,与第二批货之间的间隔是pkt_nidles。
通过foreach遍历的方法,随机产生数据。简单的分析下产生的数据:ch_id左移了24位,pkt_id左移了8位,剩下的八位留给了变量i。假设有三个发送数据的端口,把3个ch_id赋值为00、01、02,这样传出的数据来自于哪个端口就一目了然。
class chnl_trans;
rand bit[31:0] data[];//rand随机
rand int ch_id;
rand int pkt_id;//数据包的id
rand int data_nidles;//单个数据之间的间隔
rand int pkt_nidles;//数据包之间的间隔
bit rsp;
local static int obj_id = 0; //local只能在本类中使用的变量obj_id
constraint cstr{
//约束内容
soft data.size inside {
[4:8]};//数据是4到8位
foreach(data[i]) data[i] == 'hC000_0000 + (this.ch_id<<24) + (this.pkt_id<<8) + i;
soft ch_id == 0; //soft软约束
soft pkt_id == 0;
data_nidles inside {
[0:2]};
pkt_nidles inside {
[1:10]};
};
1.12 function new
每例化一次trans,让obj_id加1,用来统计总共例化了多少次。
function new(); //new 函数,每例化一次trans,obj_id++
this.obj_id++;
endfunction
1.13 function clone
让trans具有克隆的功能,创建了句柄c。并例化,把上边的所有变量全部复制给句柄c指向的对象。最后返回值为句柄c。
function chnl_trans clone();
chnl_trans c = new();
c.data = this.data;
c.ch_id = this.ch_id;
c.pkt_id = this.pkt_id;
c.data_nidles = this.data_nidles;
c.pkt_nidles = this.pkt_nidles;
c.rsp = this.rsp;
return c;
endfunction
1.14 function sprint
一个用来打印的function。
function string sprint();
string s;
s = {
s, $sformatf("=======================================\n")};
s = {
s, $sformatf("chnl_trans object content is as below: \n")};
s = {
s, $sformatf("obj_id = %0d: \n", this.obj_id)};
foreach(data[i]) s = {
s, $sformatf("data[%0d] = %8x \n", i, this.data[i])};
s = {
s, $sformatf("ch_id = %0d: \n", this.ch_id)};
s = {
s, $sformatf("pkt_id = %0d: \n", this.pkt_id)};
s = {
s, $sformatf("data_nidles = %0d: \n", this.data_nidles)};
s = {
s, $sformatf("pkt_nidles = %0d: \n", this.pkt_nidles)};
s = {
s, $sformatf("rsp = %0d: \n", this.rsp)};
s = {
s, $sformatf("=======================================\n")};
return s;
endfunction
endclass: chnl_trans
1.2 chnl_initiator
与之前的实验相比,init的功能主要还是chnl_write,不过是从信箱中获取数据,并且会告诉信箱,它有没有把数据消化完毕。
- 声明了两个mail_box,但是并没有例化mail_box。但在test文件中,把gen中的信箱赋值给了initiator的信箱。
- init会get信箱中的数据,并把数据使用chnl_write写入总线,并给信箱放入rsq,反馈信息给gen。
- 新添加了task tun,其中的function new、set_interface和task chnl_write、chnl_idle与实验2相同。
class chnl_initiator;
local string name;
local virtual chnl_intf intf;//接口
mailbox #(chnl_trans) req_mb; //信箱req 和 rsp
mailbox #(chnl_trans) rsp_mb;
function new(string name = "chnl_initiator");
this.name = name;
endfunction
function void set_interface(virtual chnl_intf intf);
if(intf == null)
$error("interface handle is NULL, please check if target interface has been intantiated");
else
this.intf = intf;
endfunction
1.21 task run
当调用run时,会执行dirve这个任务。
第一行调用类chnl_trans,生成了两个句柄req、rsp。当上升沿来临。开始死循环,不断从信箱中取数据req_mb.get(req),假如信箱中放入了req,执行chnl_write(req)。并把req的值克隆给句柄rsp,并把其中的rsp改为1。
task run();
this.drive();
endtask
task drive();
chnl_trans req, rsp;
@(posedge intf.rstn);
forever begin//死循环,不断要求从mb中取数据。
this.req_mb.get(req);//initiator从信箱中取出gen放的数据req,此时mb里边就没有东西了
this.chnl_write(req);//通过chnl_write把req中的data,写入interface中
rsp = req.clone();//使用克隆
rsp.rsp = 1;//trans类中定义了bit型的rsp,默认初始值为0,此时改变变量rsp的值
this.rsp_mb.put(rsp);//把此时的rsp再传入gen,类似于给gen一个反馈,告诉gen自己已经消化完毕。
end
endtask
1.22 task chnl_write 、chnl_idle
task chnl_write(input chnl_trans t);
foreach(t.data[i]) begin
@(posedge intf.clk);
intf.drv_ck.ch_valid <= 1;
intf.drv_ck.ch_data <= t.data[i];
@(negedge intf.clk);
wait(intf.ch_ready === 'b1);
$display("%0t channel initiator [%s] sent data %x", $time, name, t.data[i]);
repeat(t.data_nidles) chnl_idle();
end
repeat(t.pkt_nidles) chnl_idle();
endtask
task chnl_idle();
@(posedge intf.clk);
intf.drv_ck.ch_valid <= 0;
intf.drv_ck.ch_data <= 0;
endtask
endclass: chnl_initiator
1.3 chnl_generator
- 类gen用来生成数据,并把产生数据放入信箱传递给init,再从init的信箱中获得init的反馈。
- 在generator中也在变量前加了rand,在约束条件中生成的全是负数。如果没有外界使这些数变为正数,那么断言失败。
class chnl_generator;
rand int pkt_id = -1;
rand int ch_id = -1;
rand int data_nidles = -1;
rand int pkt_nidles = -1;
rand int data_size = -1;
rand int ntrans = 10;
mailbox #(chnl_trans) req_mb;//两个信箱
mailbox #(chnl_trans) rsp_mb;
constraint cstr{
soft ch_id == -1;
soft pkt_id ==

该篇博客介绍了在验证环境中如何使用随机约束生成复杂数据,通过信箱进行类间通信,以及如何运用monitor和checker组件完善验证流程。实验3中,chnl_trans类添加了随机约束,chnl_initiator从信箱获取并处理数据,chnl_generator生成数据并放入信箱。此外,还详细阐述了chnl_agent、chnl_root_test、chnl_monitor和chnl_checker的实现,以及不同测试类如何配置和运行。整个验证框架通过随机化数据和反馈机制确保了数据的准确传输和验证的有效性。
最低0.47元/天 解锁文章
1974

被折叠的 条评论
为什么被折叠?



