学习目标:
1.构造一个从路由器输出端进行取样的监视器
2.构造一个可以验证路由器输出的比较器
3.运行Driver和Monitor程序,检验Checker能否正确比数
check()用来比较输入端发送的数据和输出的得到的数据
recv()接收从输出端拿到的数据,后续还会填充很多功能。
get_payload()实现从输出端拿到数据的功能
任务一:创建顶层测试环境
- 在test.sv中声明一个8bit的队列pkt2cmp_payload。
- 在发送数据的同时核查数据,使用并行线程用来接收输出端的数据的任务recv()和发送数据的任务send()并行进行。并在后边调用比较二者的任务check()。
任务二:实现一个监视器
- 声明recv()任务,并在其中调用get_payload()任务以获取输出端数据。后续还需要往里面添加内容。
- 声明get_payload()任务,在该任务中删除队列pkt2cmp_payload[$]的内容(删除之前的数据包)
- 等待输出端frame的下降沿。
4. 保持循环直到frameo_n[da]被检测到,采样输出端数据包,使用循环体,每8个时钟周期打包一个字节byte并将于他们存入队列pkt2cmp_payload[$]
5.如果payload没有连成一个byte,则打印错误信息。
Tips :
对于步骤3:
意思是需要将等待下降沿的语句放入并行线程fork-join中。防止在类中提到的中断线程fork的问题。
步骤4:
a.将数据存放进队列,一般采用queue.push_back(data)从队列尾部排队放入。要求8bit一个字节的数据存入队列,因此需要声明一个8bit的变量,使用循环为这个变量的每一位赋值,即每一时钟周期的输出端的数据dout[da]。
b.下降沿触发保持frameo_n[da] = 0,当valido=0时接收数据,当frameo_n[da]=1且数据接收到最后一位时,8bit数据接收完毕,就可以将其存入到用于监测dout的队列中了。
任务三:发展比较器
- 创建compare函数,返回1个bit的值并进行数据比较
- 在compare()函数中比较队列payload[$]和队列pkt2cmp_payload[$]中存储的数据。
【注:可以直接用==来比较两个队列】
如果两个队列的长度不匹配,设置描述该错误的语句,并返回0结束该子程序。
如果两个队列的数据不匹配,设置描述该错误的语句,并且返回0结束该子程序。
如果两个队列的数据一致,设置compare success的语句,并且返回1结束该子程序。
3.创建check()任务
4.在check任务中,声明一个字符串string和一个计数器变量count
5.在check()中调用compare函数,检查接收的的数据
如果有步骤2中的错误产生,则打印错误message并终止仿真
如果success,则打印语句包含message with compare success and packet numbers
对比参考代码
函数返回:参考代码使用return(bit)返回,不知道compare=bit是否可以作为返回值?
函数调用:在task check()中调用函数时,我使用的是if条件判断,与参考代码一致。
队列比较:参考代码的队列比较payload == pkt2cmp_payload,这里需要修改。
字符串信息:太简陋了,参考代码给出了比较失败时的两个队列长度,以及内容。(使用$sformat整合字符串)
- 可以使用%p来打印队列内容(用于打印聚合表达式,例如解压缩结构,数组和联合(unpacked structure,array,unions),结构体等等)。
- %m和%l分别可以显示打印语句所在当前模块的模块名,和该模块所在的仿真所构建的运行库路径信息,对于调试跟踪来说非常有用,其效果参见以下代码例。
修改后的代码
monitor
task recv();
get_payload();
endtask: recv
task get_payload();
logic [7:0] datum;
pkt2cmp_payload.delete();
fork
@(negedge rtr_io.cb.frameo_n[da]);
join
foreach(pkt2cmp_payload[id]) 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]==1'b1 && i==7)
pkt2cmp_payload.push_back(datum);
else
begin
$display("%m \n[Error]:payload not a byte ",$realtime);
$finish;
end
@(rtr_io.cb);
end
end
endtask: get_payload
checker
task check();
string msg;
int count;
if(!compare(msg))begin
$display("%m [Error] Packet: \n %s",$realtime,msg);
$finish;
end
else
$display("%m [Accomplish] Packet: \n %s",$realtime,msg);
endtask
function bit compare(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
编译仿真没有问题,但是打印结果与波形错误。
Question Summary:学习参考代码
-
monitor步骤3的区别
1.参考代码开辟了一个fork...join_any,用于防止设计出现问题即frame_n的下降沿等不到仿真一直运行的情况,那么它给出了1000个时钟周期的等待时间,如果等不到就停止仿真。
等到下降沿或者等不到,这两个线程任意一个完成就可以执行下一个语句即终止另一个线程。实际上这里的终止线程也终止了最外面的fork,可以明确disable frameo_wd_timer。
-
monitor步骤4的区别:
- 使用了forever,没看懂,有必要吗而且它怎么退出。
- 还有这个for循环(这个循环有点深奥。。。),i=i?,datum[i++]?,我理解的是手动递增i,不把递增的步骤卸载for循环的括号里,正常代码应该是我的方式,可以试着修改参考代码跑一下仿真。for-loop实现的功能就是每8bit为一个包装进datum。
我的代码push_back的条件有问题,这个条件和上边payload阶段的不一样,payload是当i=7且queue的长度为size()-1,是用来拉高frame_n的。这里的条件应该是要在frameo_n拉高的并且dout最后一位已经装入datum内,才能push_back。那么这个时候i应该是7还是8?
- 两个push.back[datum]没看懂
正常字节的写入时下边的push.back[datum],for-loop里面的push.back[datum]是用来写入最后一个字节。也就是说在完成8个for-loop后由于还没有等待frameo_n拉高所以不满足第二个if条件,这样会结束循环,执行下边的push.back[datum]写入操作。这是第一个byte,但整个pay_load过程不止发送一个byte,所以还需要继续执行for-loop。
怎么执行?参考代码使用了forever,这样for-loop就可以一直执行下去。那么如何结束呢?当frameo_n为高并且恰好是第8位时就能通过return结束task但这个时候没有将存有最后一个byte的datum写入队列因此在return之前有个push.back[datum],如果不是第8位呢?即datum没有填满一个byte,不满足要求则打印相关信息并通过系统函数$finish结束仿真。
- 输入是在payload期间以8bit即一个字节byte发送数据,在这个试验中get_payload也是以8bit接收数据,如果在激励改为12bit的数据发送两次,那么在get_payload中还能否在一个frameo_n期间先接收8bit的数据?还是说在一个frameo_n期间接收整数倍的byte(即24bit)的数据。
设计要求会有说明能否实现前者,但首先应该验证设计而非看设计说明
-
checker步骤2区别
1.函数的参数列表我没有声明端口方向。参考代码使用了ref类型;如果使用input类型会如何?
修改input类型后,不影响仿真结果,但是打印结果会有变化,在compare修改的msg不会打印出来。
2.定长数组,队列,动态数组的比较;函数返回值return();
-
checker步骤3区别
1.参考代码计数变量声明了static,全局变量,这样pkts_checked的值可以被下一次比较所共享,pkts_checked++后的值可以累积。
二次修改后代码
monitor
task recv();
get_payload();
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
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]==1'b1 && i==7)begin
pkt2cmp_payload.push_back(datum);
return;
end
else begin
$display("%m \n[Error]:payload not a byte ",$realtime);
$finish;
end
@(rtr_io.cb);
end
pkt2cmp_payload.push_back(datum);
end
endtask: get_payload
checker
task check();
string msg;
static int count;
if(!compare(msg))begin
$display("%m [Error] Packet: #%0d \n %s",$realtime,count,msg);
$finish;
end
else
$display("%m [Accomplish] Packet: #%0d \n %s",$realtime,count++,msg);
endtask
function bit compare(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
仿真在[Error]:payload not a byte处停止。没有等到frameo_n拉高且datum最后一位。
调试
- if(rtr_io.cb.frameo_n[da]==1'b1 && i==7)两个条件放一起,会出现以下情况:
即当满足第一个条件第二个条件不满足时会执行else语句这是我们期望的情况,
当第一个条件不满足,二个条件满足,也会执行else语句,这是正常写入数据的情况。
当两个条件都不满足时,也会执行else语句,这也是正常写入数据的情况。
因此应该按照参考代码的方式将两个条件分开
再次仿真,发送了数据后就结束了仿真。
调试:108和109,110,120设置断点,发现在发送两次数据后,get_padload有工作,两次数据都写入了pkt2cmp_payload队列。但是第二次存入的数据并没有等到frameo_n拉高且i==7。使得foever没有return,重复了4次for-loop后等到了frameo_n拉高但是i不等于7,因此执行了报错。实际上在查看波形时发现在第二次数据存入pkt2cmp_payload前frameo_n已经翻转了,但不知道为什么没有执行条件判断。
调试:使用参考代码i=i 和datum[i++]和i==8,打印结果正常。说明compare=[bit]是可以作为返回值的。但是为什么使用常用的for循环不行?
【思考】为什么for(int i=0,;i<8;i++)不可以?
通过设置断点,整个for循环的执行过程如下: