概述
SYNOPSYS—SystemVerilog入门实验3
文章目录
- SYNOPSYS---SystemVerilog入门实验3
- 前言
- 一、Monitor是什么?
- 1. 实验思路
- 2. 实验步骤
- 1)声明与调用recv()
- 2)读懂时序图
- 二、Checker是什么?
- 1. 实验思路
- 2. 实验步骤
- 1)声明与调用check()
- 2)创建compare()
- 三、 总结
前言
在实验1与实验2中,我们已将Stimulator(激励发生器)对应的四部分(Configure、Generator、Transactor、Driver)初步搭建完成。在本实验中,我们则需要对剩下的环境组件:Monitor(监视器)、Checker(比较器)完成从无到有的构造。
一、Monitor是什么?
如实验1中所描述的:Monitor主要是用来观察DUT的边界或者内部信号,并且经过打包整理传送至其他验证平台的组件。一句话概括:它就是观察DUT输出信号的一个容器,以便在Checker中与DUT输入端口的信号进行比较。
从 检测信号 的角度来划分Monitor的功能,可以分为以下两部分:
观察DUT边界信号: 对于系统信号如时钟,可以监测其频率变化;对于总线信号,可以监测总线的传输类型和数据内容,以及检查总线时序是否符合协议。
观察DUT内部信号: 灰盒验证往往需要探视DUT内部信号,以指导激励发生器的激励发送,或者完成覆盖率收集,或者完成内部功能的检查。
在初步了解Monitor的大概功能后,针对它的问题也随之而来。
1. 实验思路
- Monitor如何接收DUT输出的数据?
- 从DUT输出的数据包格式与内容是什么?
2. 实验步骤
上述两条思路较实验2中如何产生发送激励的方法大体一致,依旧采用:按照设计师提供的输出数据包时序图,将输出的数据存储至队列中即可。
1)声明与调用recv()
在编写代码前,首先需想到应声明一个 接收 task,因为通过DUT的输出端口发送至外部(这里为Monitor)的数据相对于外部(Monitor)来说则是 接收 DUT发来的数据,one 定义一个存放接收数据的队列,名称为pkt2cmp_payload。因此在实验2的基础上增加如下test.sv的代码:
program automatic test(router_io.TB router_io);
//---one---
logic [7:0] pkt2cmp_payload[$];
initial begin
reset();
gen();
//---two---
fork
send();
recv();
join
end
//---three---
task recv();
endtask: recv
代码分析: two 声明 recv() 【 receive接收简写为recv】,为了使发送与接收能够并行工作,故采用fork…join语句将send()与recv()包裹起来。three 定义接收recv()的一个task。
fork…join、fork…join_any、fork…join_none用法
Verilog中与顺序线程begin…end相对的是并行线程fork…join
fork…join :需要所有并行的线程都结束以后才会继续执行。
fork…join_any :会等到任何一个线程结束以后就继续执行。
fork…join_none :不会等待其子线程而继续执行。
为了更好理解,故绘制fork…join、fork…join_none、fork…join_any图示如下
2)读懂时序图
Monitor在接收DUT发来的数据时,也应当按照一定的 时序关系 才能够将数据正确接收!
上图中,dout[da] 为DUT的数据输出信号,即Monitor的数据输入信号,该信号规定,数据须组装为一个字节(8bits)才能进行输出,输出至Monitor时则会进入已定义的pkt2cmp_payload[$]队列中。valido_n[da] 为数据有效信号,当该信号由高变低时,第一个数据开始输出(数据的最低位),直到一个字节输出完毕后,该信号由低变高,第二个字节同理。frameo_n[da] 为帧使能信号(一帧信号包含一字节或多字节的数据),在第一个数据发送前,应将该信号置为低电平,并且在最后一个字节的最后一个bit的数据到来时,将该信号由低变高。
task get_payload();
//---one---
pkt2cmp_payload.delete();
//---two---
fork
begin: wd_timer_fork
//***************************************************
fork: frameo_wd_timer
//---线程1---
@(negedge router_io.cb.frameo_n[da]);
//---线程2---
begin
repeat(1000) @(router_io.cb);
$display("n%mn[ERROR]%t Frame signal timed out!n", $realtime);
$finish;
end
join_any: frameo_wd_timer
//***************************************************
disable fork;
end: wd_timer_fork
join
//---three---
forever begin
logic[7:0] datum;
for(int i=0; i<8; i=i) begin
if(!rtr_io.cb.valido_n[da])
datum[i++] = rtr_io.cb.dout[da];
//---four---
if(rtr_io.cb.frameo_n[da])begin
if(i==8) begin //byte alligned
pkt2cmp_payload.push_back(datum);
return; //done with payload
end
else begin
$display("n%mn[ERROR]%t Packet payload not byte aligned!n", $realtime);
$finish;
end
end
@(rtr_io.cb);
end
//---five---
pkt2cmp_payload.push_back(datum);
end
endtask: get_payload
代码分析: one 在进行队列操作前,须将队列进行清空操作,以防原有队列中的数据影响实验结果。two 此处fork…join的目的是若程序出现bug,可使仿真退出,不会造成持续运行的情况(即超时退出)。 fork…join_any 表示只要出现一个子线程执行结束的情况,即可退出操作,在这里有两个子线程:1、@(negedge router_io.cb.frameo_n[da]);2、begin…end块。disable fork 表示若一个线程执行完毕后,应将没有执行完的线程关闭。
超时退出: 正常模式下,DUT输出信号frameo_n[da]的下降沿(数据输出的开始标志)是会随着数据同时出现的,从而进入下面的forever begin…end块中。但为了防止程序存在bug,须设置超时退出机制(在这里延时1000个cycle,若没有等到输出信号frameo_n[da]的下降沿,即报错([ERROR])并停止运行($finish))。
three 若程序正确,即frameo_n[da]的下降沿已出现。则跳转至forever begin…end块中。首先定义一个缓存数据的datum,若valido_n[da]低有效,就将DUT输出的数据dout[da]按照低位在前、高位在后的形式依次存放至datum中,前7bits由于rtr_io.cb.frameo_n[da]信号始终为低电平,将不会执行 four,而会直接跳转至 five (此时前7bit已由队列pkt2cmp_payload依次从datum中读出)。根据时序图可知,若frameo_n[da]在第8bit(最后1bit)到来时由低变高,则代表整帧数据已传输完毕,目前只需要将已缓存的第8bit(最后1bit)从datum读走即可,至此,发送部分的程序将会停止(return的作用)。
可以通过内建方法push_back()、push_front()、pop_back()、pop_front()来顺序添加或移除并且获得数据成员。
push_back(): 在尾部加入数据。
push_front(): 在首部加入数据。
pop_back(): 从尾部拿出数据。
pop_front(): 从首部拿出数据
return 语法:作用是从当前函数退出,并从这个函数返回一个值。
retrun[() [expression] [] ]
可选项 expression 参数是要从函数返回的值。如果省略,则该函数不返回值。
二、Checker是什么?
1. 实验思路
- 将发送的数据与接收的数据进行对比
2. 实验步骤
1)声明与调用check()
program automatic test(router_io.TB router_io);
logic [7:0] pkt2cmp_payload[$];
initial begin
reset();
gen();
fork
send();
recv();
join
//---one---
check();
end
代码分析: 当发送与接收的任务都执行完毕后,one声明check()。
2)创建compare()
在编写check()的任务时,需想到:在进行发送的数据与接收的数据比较时,无非相同或相异,相同时 打印 出包中的数据(第几包也可以考虑在内),相异时 打印 出第几包数据出错,因此在test.sv的代码中编写如下代码:
针对SV中有关 打印 的知识点可以参考该链接。
//----------------------error--------------------------
task check();
//---one---
if (payload != pkt2cmp_payload) begin
$display("n%mn[ERROR]%t Packet #%0d %sn", $realtime, pkts_checked, message);
$finish;
end
$display("[NOTE]%t Packet #%0d %s", $realtime, pkts_checked++, message);
endtask: check
代码分析: one 在此处本人忽略了在进行队列中的数据比较时,不仅要比较队列中的数据,而且还要比较队列的宽度,即size!!! ,为了增加代码可读性,用return返回值(0或1)的思路来代替if的判断条件(payload != pkt2cmp_payload),若数据与队列宽度一致时,返回1,否则返回0。
因此创建一个compare()函数如下:
function bit compare(ref string message);
if(payload.size() != pkt2cmp_payload.size()) begin
message = "Payload size Mismatch:n";
message = { message, $sformatf("payload.size() = %0d, pkt2cmp_payload.size() = %0dn", payload.size(),pkt2cmp_payload.size()) };
return (0);
end
if(payload == pkt2cmp_payload) ;
else begin
message = "Payload Content Mismatch:n";
message = { message, $sformatf("Packet Sent: %pnPkt Received: %p", payload, pkt2cmp_payload) };
return (0);
end
message = "Successfully Compared";
return(1);
endfunction: compare
代码分析: 在上述代码中,如果发送的队列与接收的队列宽度不同,则将各自的宽度打印并从函数function返回数值0;如果宽度相同,则进行队列中数据的比较,若相同,打印出Successfully Compared并从函数function返回数值1,反之打印出各自的数据并返回数值0。
于是将check()更改如下:
task check();
string message;
static int pkts_checked = 0;
if (!compare(message)) begin
$display("n%mn[ERROR]%t Packet #%0d %sn", $realtime, pkts_checked, message);
$finish;
end
$display("[NOTE]%t Packet #%0d %s", $realtime, pkts_checked++, message);
endtask: check
ref 用法
task和function的参数列表的类型有四种:input,output,inout,ref。其中,ref类似于软件的指针,调用时不会复制,而是直接引用或修改外部传入的数据对象。
inout和ref的区别是:inout只有当方法结束之后才可以传递到外部,而ref可以在方法执行过程中就修改数据对象,无须等到方法结束。如果保护数据对象只被读取不被写入,则通过const的方法来限定ref声明的参数。
针对 ref 的理解亦可以参考该链接。
三、 总结
该部分实验主要将红色框内的大致结构(DUT输出数据的接收与比对)搭建了起来,至此,一个基本完整的验证环境已搭建完毕。后续会继续优此验证环境,并引入功能覆盖率的测试,感谢大家支持。
最后
以上就是飘逸母鸡为你收集整理的SystemVerilog系列实验3SYNOPSYS—SystemVerilog入门实验3前言一、Monitor是什么?二、Checker是什么?三、 总结的全部内容,希望文章能够帮你解决SystemVerilog系列实验3SYNOPSYS—SystemVerilog入门实验3前言一、Monitor是什么?二、Checker是什么?三、 总结所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复