sv_labs学习笔记——sv_lab5_上(System Verilog)
本节将介绍lab5的第一部分,主要总结一般设计学习与思考的方式与需要着重学习的点,同时以lab5作为参考,分析数据流流向,验证组件的通信与抽象化,实现的整体思路。
sv_labs学习笔记——sv_lab5_上(System Verilog)
- lab5 Braod Spectrum Verfication(上)
- 实验目标
- 思路梳理
- sim.do文件
- 代码分析
- router_test_top.sv
- router_test.h
- router_io.sv
- test.sv
- Generator.sv
- %m
- new 操作符复制
- DriverBase.sv
- Driver .sv
lab5 Braod Spectrum Verfication(上)
实验目标
首先明确实验目标。我们在lab4中根据实验指导,已经实现了指定端口的数据发送与检测。在该节lab中,我们将实现16路通道同时发送数据,同时实现对16组输出chanel的监测。将验证组件以class的形式进行进一步的封装。进一步完善我们的验证结构。
思路梳理
我们明确了设计的目标,可以明显感觉lab5相比较于前面的更为抽象。我们根据最基本的验证组件框架,结合rest.sv代码,来梳理数据流向,程序运行大致的顺序。更细节的部分会放在设计的模块分析中进行解释梳理。
需要理解基本的两个概念,mailbox、semaphore。简单来说,mailbox用于线程间的数据交互。semaphore实现同一个资源的访问控制。详细参考笔者的System Verilog 线程间的通信
首先是数据的产生,由Generator产生数据,数据产生的形式是Package的形式,放入out_box送入Driver,Driver将根据sa,由对应的drvr发送数据,同时打包数据送到scoreboard的driver_mbox,将由scoreboard进行数据比对,然后rcvr接收送到scoreboard的receiver_mbox,进行数据的比对。具体比对的方式将在后续代码中进行说明。
这里需要注意思考的点有
- gen数据产生的形式以及数据格式是什么样的。
- 怎么解决多个sd发送到一个da的情况
- Driver怎么使用gen产生的数据完成数据发送的
- scoreboard是怎么进行数据比较的
- 测试环境是怎么停止仿真的
该博文将分析数据的产生,发送,以及整体的验证结构。下一篇分析数据的监测,数据的比对。
sim.do文件
set rtl ../../rtl/router.v
set svtb {./router_test_top.sv ./router_io.sv ./test.sv}
vlib work
vmap work work
vlog $rtl
vlog -sv ./router_test_top.sv ./router_io.sv ./test.sv
vsim -t ns -novopt +notimingchecks -l router_test_top.log work.router_test_top
run -all
代码分析
首先看测试顶层,顶层将接口,测试程序进行实例化。这些都是与前面一样的,这里不进行解释。这里重要的是对测试程序进行分析。分析会为了确保代码的完整度,将代码全部展示。
router_test_top.sv
`timescale 1ns/100ps
module router_test_top;parameter simulation_cycle = 100;bit SystemClock;//bit 是二值逻辑。只有0、1,初始值默认是0router_io top_io(SystemClock);test t(top_io);router dut(.reset_n (top_io.reset_n),.clock (top_io.clock),.din (top_io.din),.frame_n (top_io.frame_n),.valid_n (top_io.valid_n),.dout (top_io.dout),.valido_n (top_io.valido_n),.busy_n (top_io.busy_n),.frameo_n (top_io.frameo_n));initial begin$timeformat(-9, 1, "ns", 10);SystemClock = 0;forever begin#(simulation_cycle/2)SystemClock = ~SystemClock;endendendmodule
router_test.h
mailbox是一种通信机制,它使得数据可以在进程间传递和通信,数据被一个进程发送到另一个mailbox中,而另外一个进程可以从中可以获得。
mailbox #(type = dynamic_type)
其中dynamic_type代表一个特殊的类型,它能够执行运行时的类型检查(缺省情况)。详细参考笔者的System Verilog 线程间的通信
typedef class Packet;
typedef mailbox #(Packet) pkt_mbox;
router_io.sv
`timescale 1ns/100ps
interface router_io(input bit clock);logic reset_n;logic [15:0] din;logic [15:0] frame_n;logic [15:0] valid_n;logic [15:0] dout;logic [15:0] valido_n;logic [15:0] busy_n;logic [15:0] frameo_n;clocking cb @(posedge clock);//default input #1 output #1;output reset_n;output din;output frame_n;output valid_n;input dout;input valido_n;input busy_n;input frameo_n;endclockingmodport TB(clocking cb, output reset_n);
endinterface
test.sv
测试程序semaphore,Driver,Receiver实例化16份,将Generator,Scoreboard实例化一份
。这里是将gen生成的所有对应的数据(out_box)送到每一个实例化的drvr[]中mailbox中,每一个drvr[]复制一份后放到drvr自身的mailbox中。drvr完成发送,将发送的数据放入driver_mbox,就完成了发送
`timescale 1ns/100ps
program automatic test(router_io.TB rtr_io);`include "Packet.sv"int run_for_n_packets; // number of packets to testint TRACE_ON = 1;`include "router_test.h"`include "Driver.sv"`include "Receiver.sv"`include "Generator.sv"`include "Scoreboard.sv"semaphore sem[]; //声明动态数组Driver drvr[]; // driverReceiver rcvr[]; // receiverGenerator gen; // generatorScoreboard sb; // scoreboardinitial begin run_for_n_packets = 200;sem = new[16]; //分配16个元素(16个句柄)drvr = new[16];rcvr = new[16];gen = new("gen");sb = new("sb");foreach (sem[i])sem[i] = new(1);//为每一个semaphore分配一个钥匙foreach (drvr[i])drvr[i] = new($sformatf("drvr[%0d]",i), i, sem, gen.out_box[i], sb.driver_mbox, rtr_io);foreach (rcvr[i])rcvr[i] = new($sformatf("rcvr[%0d]",i), i, sb.receiver_mbox, rtr_io);
//以上过程均没有消耗时间,因此是在仿真0时刻就已经完成了。完成组件之间的通信与reset();gen.start();sb.start();foreach (drvr[i]) drvr[i].start();foreach (rcvr[i]) rcvr[i].start();//start函数都是不耗时间的,因此在执行上也是在复位结束后在同一个时刻执行的。可以自行查看打印信息wait(sb.DONE.triggered);//等待scoreboard结束事件被触发endtask reset();if (TRACE_ON) $display("[TRACE]%t :%m", $realtime);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: resetendprogram: test
Generator.sv
out_box[]将传入对应drvr,由其进行发送,这也是数据产生的逻辑。一共有16组out_box。每一组分别送到对应的drvr发送出去。每一组sa相同的放入同一个out_box。
%m
%m
将打印目录结构
new 操作符复制
Packet pkt = new this.pkt2send;//将pkt2send复制一份到pkt
该句涉及到了 通过new来实现浅拷贝。参看绿皮书第五章5.15
使用new操作符复制一个对象使用new复制一个对象简单而且可靠,它创建了一个新的对象,并且复制了现有对象的所有变量。这是一种简易复制(shallow copy),类似于原对象的一个影印本,原对象的值被盲目地抄写到目的对象中。如果类中包含一个指向另一个类的句柄,那么,只有最高一级的对象被new操作符复制,下层的对象都不会被复制。
`ifndef INC_GENERATOR_SV
`define INC_GENERATOR_SV
class Generator;string name; // unique identifierPacket pkt2send; // stimulus Packet objectpkt_mbox out_box[]; // mailbox to Driversextern function new(string name = "Generator");extern virtual task gen();extern virtual task start();
endclass: Generatorfunction Generator::new(string name);if (TRACE_ON) $display("[TRACE]%t %s:%m", $realtime, name);this.name = name;this.pkt2send = new();this.out_box = new[16];//分配16个元素(16个句柄)foreach(this.out_box[i])this.out_box[i] = new();//将每一个mailbox new
endfunction: newtask Generator::gen();static int pkts_generated = 0;if (TRACE_ON) $display("[TRACE]%t %s:%m", $realtime, this.name);this.pkt2send.name = $psprintf("Packet[%0d]", pkts_generated++);if (!this.pkt2send.randomize()) begin$display("\n%m\n[ERROR]%t Randomization Failed!\n", $realtime);$finish;end
endtask: gentask Generator::start();if (TRACE_ON) $display("[TRACE]%t %s:%m", $realtime, this.name);forkfor (int i=0; i<run_for_n_packets || run_for_n_packets <= 0; i++) beginthis.gen();beginPacket pkt = new this.pkt2send;//将pkt2send复制一份到pkt//this.out_box[pkt.sa].put(this.pkt2send);this.out_box[pkt.sa].put(pkt);//根据数据本身的sa送到对应的out_box中endendjoin_none//为了不在rest.sv中阻塞下面的进程,使用join_none。但是这里修改为join也是可以的,因为这里没有延时语句
endtask: start
`endif
DriverBase.sv
基本的基类,扩展类参考这里。
DriverBase的作用就是发送pkt2send中的数据到对应的da端口,同前面的实现是一样的。将数据送到接口。
`ifndef INC_DRIVERBASE_SV
`define INC_DRIVERBASE_SV
class DriverBase;virtual router_io.TB rtr_io; // interface signalstring name; // unique identifierbit[3:0] sa, da; // source and destination addresseslogic[7:0] payload[$]; // Packet payloadPacket pkt2send; // stimulus Packet objectextern function new(string name = "DriverBase", virtual router_io.TB rtr_io);extern virtual task send();extern virtual task send_addrs();extern virtual task send_pad();extern virtual task send_payload();
endclassfunction DriverBase::new(string name, virtual router_io.TB rtr_io);if (TRACE_ON) $display("[TRACE]%t %s:%m", $realtime, name);this.name = name;this.rtr_io = rtr_io;
endfunctiontask DriverBase::send();if (TRACE_ON) $display("[TRACE]%t %s:%m", $realtime, this.name);this.send_addrs();this.send_pad();this.send_payload();
endtasktask DriverBase::send_addrs();if (TRACE_ON) $display("[TRACE]%t %s:%m", $realtime, this.name);this.rtr_io.cb.frame_n[this.sa] <= 1'b0;for(int i=0; i<4; i++) beginthis.rtr_io.cb.din[this.sa] <= this.da[i];@(this.rtr_io.cb);end
endtasktask DriverBase::send_pad();if (TRACE_ON) $display("[TRACE]%t %s:%m", $realtime, this.name);this.rtr_io.cb.din[this.sa] <= 1'b1;this.rtr_io.cb.valid_n[this.sa] <= 1'b1;repeat(5) @(this.rtr_io.cb);
endtasktask DriverBase::send_payload();if (TRACE_ON) $display("[TRACE]%t %s:%m", $realtime, this.name);foreach(this.payload[index]) beginfor(int i=0; i<8; i++) beginthis.rtr_io.cb.din[this.sa] <= this.payload[index][i];this.rtr_io.cb.valid_n[this.sa] <= 1'b0;this.rtr_io.cb.frame_n[this.sa] <= ((index == (this.payload.size() - 1)) && (i == 7));@(this.rtr_io.cb);endendthis.rtr_io.cb.valid_n[this.sa] <= 1'b1;
endtask
`endif
Driver .sv
将顶层产生的数据复制到实例化每一个drvr内部。然后sem都传进来。每一个实例化的Driver都可以发送数据并且有一个自己的ID。假设ID为3,则只能发送sa=3的数据。
this.in_box.get(this.pkt2send);
这里的将传入的诗句取出来一份放到pkt2send中。准备接下来判断sa发送口是不是当前的端口。
if (this.pkt2send.sa != this.sa) continue;
需要注意这里的continue。当比较数据的sa与当前端口不一致的时候,continue,就是退出当前循环,开始下一次循环。相当于这个数据
`ifndef INC_DRIVER_DV
`define INC_DRIVER_DV
`include "DriverBase.sv"
class Driver extends DriverBase;pkt_mbox in_box; // Generator mailboxpkt_mbox out_box; // Scoreboard mailboxsemaphore sem[]; // output port arbitrationextern function new(string name = "Driver", int port_id, semaphore sem[], pkt_mbox in_box, out_box, virtual router_io.TB rtr_io);extern virtual task start();
endclassfunction Driver::new(string name, int port_id, semaphore sem[], pkt_mbox in_box, out_box, virtual router_io.TB rtr_io);super.new(name, rtr_io);if (TRACE_ON) $display("[TRACE]%t %s:%m", $realtime, this.name);this.sa = port_id;//一共有16个ID,这些个ID对应着16个SA,也就是说,一个SA,有一个发送激励。SA对应的DRIVER标号是相同的。sa是1,则使用对应的实例化的发送数据this.sem = sem;//将测试顶层的sem传入内部this.in_box = in_box;//将顶层产生的数据复制到实例化每一个drvr内部this.out_box = out_box;
endfunction: newtask Driver::start();if (TRACE_ON) $display("[TRACE]%t %s:%m", $realtime, this.name);forkforever beginthis.in_box.get(this.pkt2send);//从已经复制过的数据中获取packet内容if (this.pkt2send.sa != this.sa) continue;//从in_box中获取相关的发送信息。包括从哪里来,到那里去,包含多少个相关的该信息。要是Driver的ID与之不同,则放过去进行下一次判断,反之呢,就退出接收下一个信息。//其实这里也并不需要判断,因为放进来的信箱的里数据sa本身与当前发送的ID就是对应的。见Generatorthis.da = this.pkt2send.da;this.payload = this.pkt2send.payload;this.sem[this.da].get(1);//这里防止有多个SA发送同一个DA。进行保护。当多个sa向一个da发数据的时候会进行阻塞,就形成了保护。this.send();this.out_box.put(this.pkt2send);//将发送的数据放入out_box中this.sem[this.da].put(1);endjoin_none
endtask: start
`endif
sv_labs学习笔记——sv_lab5_上(System Verilog)相关推荐
- sv_labs学习笔记——sv_lab5_下(System Verilog)
本文延续前一篇sv_labs学习笔记--sv_lab5_上(System Verilog),进一步学习完善lab5的内容 sv_labs学习笔记--sv_lab5_下(System Verilog) ...
- sv_labs学习笔记 专栏说明博文目录
栏目说明 专栏主要提供sv_labs学习过程中笔者认为比较重要的点以及相关延申.本文假设读者已经具有相关语言的背景.不会过分纠结语言本身,但会重点对于一些语言进行理解.语言的理解见笔者的另外一个专栏S ...
- sv_labs学习笔记——sv_lab4(System Verilog)
sv_labs学习笔记--sv_lab4(System Verilog) lab4 OOP encapsulation 实验概述 任务代码解析 Packat实现与理解 语法点解析 randomize( ...
- sv_labs学习笔记——sv_lab3(System Verilog)
这里我们在前两个lab的基础上继续完善,搭建一个数据接受的功能.在lab2 中我们实现了相关发送信息的打印,选择发送接受的端口,完善发送时序,在这一小节中将实现对端口发送数据的回收,然后进行比较发送的 ...
- sv_labs学习笔记——sv_lab2(System Verilog)
该博文在lab1的基础上,根据说明以及solution里的文件,实现了接口发送时序.简单对代码进行分析.分析的内容在文章的注释区中.对于不是很清楚的语法进行解释.最后在原文件的基础上给出增加了相关打印 ...
- sv_labs学习笔记——sv_lab1(System Verilog)
sv_labs学习笔记--sv_lab1(System Verilog) 概述 lab1 功能简述 代码分析 仿真精度 理解repeat(15) @(rtr_io.cb); 理解$timeformat ...
- 小狐狸横版游戏开发学习笔记(上)
小狐狸横版游戏开发学习笔记(上) 目录 小狐狸横版游戏开发学习笔记(上) 1.关于如何创建Tilemap 2.关于地图格子之间出现间隙的问题 3.如何设置自己想要的控制按键 4.如何解决玩家移动过程中 ...
- 2020年Yann Lecun深度学习笔记(上)
2020年Yann Lecun深度学习笔记(上)
- SiamRPN论文学习笔记(上)
SiamRPN论文学习笔记(上) 引言 SiamRPN的网络结构 孪生子网络部分 区域候选子网络部分 RPN的诞生 区域候选子网络 训练阶段 两阶段训练 anchors尺寸设置 分类分支中anchor ...
最新文章
- 学界 | 和清华大学自然语言处理与社会人文计算实验室一起读机器翻译论文
- python爬取天天基金_python多线程+代理池爬取天天基金网、股票数据过程解析
- ckeditor的使用实例
- nth_element
- git拉取远程分支到本地分支或者创建本地新分支
- 论文阅读笔记(五)——FD-MOBILENET
- 什么叫预判_挖机事故发生之前,挖机司机做了什么?
- ArcGIS10.8安装(附最新<2022年10月29日>下载地址)
- 用纯前端表格控件SpreadJS,搭建上海泛微协同OA管理平台
- 计算机基础知识试题和答案
- js日期减去日期算出剩余的天数
- 猫狗大战-caffe模型训练实例amp;NSDK识别运行
- java.io.IOException: Unable to establish loopback connection
- php短视频转码,YYC松鼠短视频系统V2.0版本发布,亮点新增转码加水印功能
- 图片存档和通信系统(PACS)的全球与中国市场2022-2028年:技术、参与者、趋势、市场规模及占有率研究报告
- linux 动画软件,免费开源的 2D 动画软件
- 2021年危险化学品生产单位安全生产管理人员考试及危险化学品生产单位安全生产管理人员考试资料
- 售价2999元起 华为5G自拍视频旗舰nova7系列强势来袭
- Java反射Method和Field简单实例
- 列王的纷争服务器维护中,《cok列王的纷争》不忘初心,持续做玩家喜欢的列王!...
热门文章
- 解决Linux下pcieport 0000:00:1c.5问题导致的系统根目录/磁盘空间不足
- 具有左,中或右对齐项的Bootstrap NavBar
- jQuery获取select onChange的值
- 什么是clearfix?
- 如何计算列表项的出现?
- mysql show status 过滤_给MySQL的show table status结果做过滤
- Win11内存占用高怎么办,Win11内存占用高解决方法
- SpringBoot配置RunDashboard
- 麦克纳姆轮速度分解再分析
- mysql主从同步破坏测试_mysql主从同步 错误测试(1)