提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
文章目录
学习目标
1.将数据信息封装进入Packet类中
2.利用随机化(randomization)在packet类中随机产生源地址,目标地址和payload
3.创建两个packet对象(object),一个包用来在DUT输入端输入,另一个包用来和DUT输出的数据相参照
4.将compare()方法嵌入packet类,用来验证DUT工作的正确性
提示:以下是本篇文章正文内容,下面内容仅供参考学习
一、任务步骤
任务一:创建一个数据包类(class)文件
1.创建Packet.sv
2.声明class Packet
3.在类class语句的前面和后面使用宏作为标记,防止多次编译
4.声明随机变量sa,da和随机队列payload[$];定义字符串string name用来区分不同的数据包对象
任务二:定义数据包的属性约束
在属性约束声明之后添加约束块限制sa和da 在0至15以内,**payload.size()**在2至4之内。
任务三:定义数据包类方法的雏形
声明:
外部方法new,传递参数string name,初始值为Packet;
外部方法compare,返回值为bit类型,传递参数class类Packet pkt2cmp和ref类型的字符串string msg
外部方法display用来放置打印语句,无返回值,传递参数为字符串string prefix,初始值为Accomplish
任务四:定义数据包类new()构建函数
对于Packet对象来说,大部分性质将会调用randomize()来进行设置,但是name这一性质需要在new()中初始化。
1.在Class()体的外部,实现new方法。确定通过**:😗*域这一接口,使function映射到Packet类中
2.函数内使用参数传入的字符串name赋值给指向这个类域里面的name
任务五:定义Packet的方法compare()
为了比较两个数据对象的内容,通常会在数据对象中去建立compare()方法。
1.将test.sv的compare()剪切到Packet.sv中去
2.参照上边把方法指明::域Packet中的compare()
3.修改参数列表需要添加一个参数即类Packet和它的句柄pkt2cmp
4.在compare()方法内部,参照类属性的pkt2cmp.payload来修改pkt2cmp_payload
任务六:定义数据包的方法display()
为了打印出Packet对象的内容以便调试,display的方法应该被定义在这个Packet类中
1.同上创建display()方法
2.打印内容:当前时间点当前对象的输入输出通道sa和da,传递队列payload内的数据内容
任务七:修改test.sv来使用Packet类
Packet类现在封装了数据包的信息,在test.sv中我们要做的是随机产生Packet的对象,然后在这些随机对象的基础上通过路由器发送和接受数据包。
1.在程序块内部导入Packet类使用includ语句即可
2.创建和构造两个数据包对象,pkt2send和pkt2cmp。
任务八:修改gen()任务以产生数据包对象
1.删除gen()内之前的代码
2.声明静态变量static int pkts_generated用来记录产生了多少个数据包对象
3.将pkt2send的name设置为一个唯一的字符串变量(使用一个可变变量pkts_generated作为这个字符串的一部分)
4.随机化数据包对象pkt2send。如果随机化失败,打印错误信息并终止仿真
5.使用随机化pkt2send使对象中的变量sa,da和payload值更新了,因此你需要将它们的值赋值给program域里的sa,da和payload。这样就将类中数据包信息与program联系起来了。这使得pkt2send对象的内容对程序的所有组件都可见。
- 由原先在gen任务中直接产生数据包,改为在类中产生,然后通过类索引赋值传递给程序块。
任务九:修改recv()任务
这个任务是用来采样输出端的payload即task get_payload(),然后放入队列pkt2cmp_payload中的。
现在我们将这些步骤放入类Packet pkt2cmp中。这个类的对象会在后面检查和Packet pkt2send的对象的比较结果。
在recv()任务中,调用get_payload()之前需要进行以下操作:
1.创建一个静态int变量pkt_cnt来追踪数据包的数量。应该初始化为0
在调用get_payload()之后应该做一下操作:
1.连接pkt2cmp.da和全局变量da
2.连接pkt2cmp.payload和pkt2cmp_payload
3.和任务八-步骤3一样设置pkt2cmp.name,并将pkt_cnt作为字符串的一部分
任务十:修改check()任务
在check()任务中,我们将使用类Packet对象内的compare()方法来比较发送和接收的Packet对象内容是否一致。
并且为了方便调试错误,我们应该在数据包对象中使用display()方法。就是我们之前在类Packet定义的外部方法display().
1.将compare()放入类的对象中取代之前的compare。这一步我们之前做过了。
2.比较对象pkt2send和pkt2cmp。
3.当比较出现错误时,调用两个对象中的display()方法并传递相应的字符串,打印错误信息。
4.同样设置静态变量int pkts_checked用来记录检查次数。也可以使用之前定义的计数变量count
编译仿真:
编译报错
1.未声明约束块名称
2.pkt2cmp_payload未在类Packet中声明,即使在程序块中声明静态变量,类中也索引不到它。需要手动索引pkt2cmp对象中的payload
3.定义的外部方法忘记声明返回值,导致类找不到定义的外部方法
4.条件编译报错
ifndef 和 endif
要一起使用,如果丢失endif,会报错。
宏定义#define #ifndef #endif
仿真报错
打错字了。。仿真器没法解析变量
pkt2cmp和pkt_cnt
二、调试
1.随机化失败
条件给错了,少了一个!
2.数据包对象长度比较失败
第一个数据就比较失败了。输出端接收到的数据包有问题,查看波形发现输出端是有波形的。所以问题大概出在了recv()里面。
检查recv的代码发现,应该是之前get_payload的值传递给pkt2mp对象的payload,我写反了。pkt2mp对象是空的,pkt2cmp.payload自然是空的,da也反了,应该是全局变量da传递。
打印结果
波形结果
总结
`timescale 1ns/100ps
program automatic test(router_io.TB rtr_io);
`include "Packet.sv"
bit [3:0] sa; //source address(input port)
bit [3:0] da; //destination address (output port)
logic [7:0] payload[$];
int run_n_packets;
logic [7:0] pkt2cmp_payload[$];
Packet pkt2send = new("send-Packet");
Packet pkt2cmp = new("recv-Packet");
initial begin
reset();
run_n_packets = 5;
repeat(run_n_packets) begin
gen();
fork
send();
recv();
join
check();
end
repeat(10) @(rtr_io.cb);
end
//signal reset_n draw low,frame_n and valid_n draw high.After 2ns reset_n draw high.
//AND ues repeat statement to keep 15 cycles.
task reset();
rtr_io.reset_n = 1'b0;
rtr_io.cb.frame_n <= '1;
rtr_io.cb.valid_n <= '1;
#2 rtr_io.cb.reset_n <= 1'b1;
repeat(15) @(rtr_io.cb);
endtask: reset
task gen();
static int pkts_generated = 0;
pkt2send.name = $sformatf("send-Packet[%0d]",pkts_generated++);
if(!pkt2send.randomize()) begin
$display("object of handle pkt2send randomize fail");
$finish;
end
sa = pkt2send.sa;
da = pkt2send.da;
payload = pkt2send.payload;
endtask: gen
task send();
send_addrs();
send_pad();
send_payload();
endtask
task send_addrs();
rtr_io.cb.frame_n[sa] <= 1'b0; //start to send packets
for(int i = 0; i<4; i++) begin
rtr_io.cb.din[sa] <= da[i]; //stage1:send address to din
@(rtr_io.cb);
end
endtask: send_addrs
task send_pad();
rtr_io.cb.din[sa] <= 1'b1;
rtr_io.cb.valid_n[sa] <= 1'b1;
rtr_io.cb.frame_n[sa] <= 1'b0; //it is not necessary? Yes
repeat(5) @(rtr_io.cb);
endtask: send_pad
task send_payload();
static int id;
foreach(payload[id])
begin
for(int i = 0 ;i < 8 ;i++)begin
rtr_io.cb.din[sa] <= payload[id][i];
rtr_io.cb.valid_n[sa] <= 1'b0;
rtr_io.cb.frame_n[sa] <= (i == 7 && id == payload.size()-1) ? 1'b1:1'b0;
@(rtr_io.cb);
end
$display("sa = [%0d] da = [%0d] ,send payload[%0d] is %0d ",sa,da,id,payload[id]);
end
rtr_io.cb.valid_n[sa] <= 1'b1;
endtask: send_payload
task recv();
static int pkt_cnt = 0;
get_payload();
pkt2cmp.da = da;
pkt2cmp.payload = pkt2cmp_payload;
pkt2cmp.name = $sformatf("recv-Packet[%0d]",pkt_cnt++);
endtask: recv
task get_payload();
logic [7:0] datum;
pkt2cmp_payload.delete();
fork
begin
fork
@(negedge rtr_io.cb.frameo_n[da]);
begin
repeat(1000) @(rtr_io.cb);
$display("\n%m\n[ERROR]%t Frame signal timed out!\n", $realtime);
$finish;
end
join_any
disable fork;
end
join
@(negedge rtr_io.cb.valido_n[da]);
forever begin
for(int i = 0; i < 8; i++)begin
if(!rtr_io.cb.valido_n[da])
datum[i] = rtr_io.cb.dout[da];
if(rtr_io.cb.frameo_n[da])begin
if(i==7)begin
pkt2cmp_payload.push_back(datum);
return;
end
else begin
$display("%t \n[Error]:payload not a byte ",$realtime);
$finish;
end
end
@(rtr_io.cb);
end
pkt2cmp_payload.push_back(datum);
end
endtask: get_payload
task check();
string msg;
static int count;
if(!pkt2send.compare(pkt2cmp,msg))begin
$display("%m\n%t [Error] Packet: #%0d \n %s",$realtime,count,msg);
pkt2send.display("ERROR");
pkt2cmp.display("ERROR");
$finish;
end
else
$display("%m\n%t [Accomplish] Packet: #%0d \n %s",$realtime,count++,msg);
endtask
endprogram: test
`ifndef INC_PACKET_SV
`define INC_PACKET_SV //Use macros as a guard against multiple compilation before and after the class statements
class Packet;
rand bit [3:0] sa, da;
rand logic [7:0] payload[$];
string name; //identify the individual object
constraint constr {
sa inside {[0:15]};
da inside {[0:15]};
payload.size() inside {[2:4]};
}
extern function new(string name = "Packet");
extern function bit compare(Packet pkt2cmp, ref string msg);
extern function void display(string prefix = "Note");
endclass: Packet
`endif
function Packet::new(string name);
this.name = name;
endfunction: new
function bit Packet::compare(Packet pkt2cmp, ref string msg );
if(payload.size()!=pkt2cmp.payload.size()) begin
compare = 0;
msg = "Payload size mismatch:";
msg = {msg , $sformatf("Cause of failure : payload.size()=%0d, pkt2cmp_payload.size()=%0d queues size is inconsistent",payload.size(),pkt2cmp.payload.size())};
end
else
if(payload!=pkt2cmp.payload) begin
compare = 0;
msg = "Payload content mismatch:";
msg = {msg ,$sformatf("Cause of failure : payload = %p pkt2cmp_payload = %p queues data is inconsistent", payload,pkt2cmp.payload)};
end
else
begin
compare = 1;
msg = "Two queues data compare succeslly";
end
endfunction
function void Packet::display(string prefix);
$display("[%s]%t %s sa = %0d, da = %0d", prefix, $realtime, name, sa, da);
foreach(payload[i])
$display("[%s]%t %s payload[%0d] = %0d", prefix, $realtime, name, i, payload[i]);
endfunction