题目要求
1.从verilog到sv (TB1)
(1)将tb1修改为tb1.sv,按照之前的步骤进行仿真,查看仿真的行为是否同tb1.v相似,结果说明什么?
(2)将信号变量类型由reg或者wire修改为logic类型,再仿真编译看是否与之前的行为相似,这是为什么?
(3)在步骤的基础之上,将rstn的类型由logic改成bit类型,再仿真编译,行为是否和步骤2一致呢,为什么?
2.方法task和函数function (TB2)
在tb2.sv文件中,可以看出不同于tb1.sv文件的是,之前产生时钟和发起复位的两个Initial过程快语句都被两个task即clk_gen()和rstn_gen()取代了,完成实验的部分:
(1)不做修改的情况下,对tb2.sv进行编译仿真,时钟信号和复位信号还正常吗?为什么?
(2)同学们在路桑标记的两个initial块中分别调用产生时钟和复位的task,再编译仿真查看时钟信号和复位信号,是否恢复正常呢?
(3)为什么要将两个task和两个initial块中调用,这是为什么呢?是否可以在一个initial块中调用呢?如果可以,调用的顺序是什么?
(4)同学们是否可以读出目前时钟的周期和频率呢?如何测量呢?如果我们想进化clk_gen()方法,使其变为可以设置时钟周期的任务clk_gen(20),看看波形中的时钟周期是否变为20ns呢?
(5)如果将’timescale 1ns/1ps修改为’timescale 1ps/1ps,那么仿真中的时钟周期是否发生变化了呢,这是为什么呢?
3.数组的使用 (TB3)
(1)如果现在要求同学们对每一个slave的数据发出100个数,那么如何实现呢?
(2)如果现在生成100个数,并对它们按照目前的数值规则进行赋值,请同学们创建3个动态数组,分别放置要发送3个slave的数据。
(3)接下来利用生成的数组数据,将他们读取并发送给三个channel。
4.验证结构 (TB3)
为了实现清晰的验证结构,我们希望将DUT和激励发生器(stimulator)之间划分。因此,我们可以将激励方法chnl_write()封装在新的模块lhnl_initiator中,请同学们下载tb4.sv,在接下来开展实验步骤前,同学们可以将"数组的使用"环节中添加的代码部分移柏到tb4.sv对应的位置上。
从tb4.sv中同学们可以发现之前的initil语句块"channel_write_task"已经不见了,在其位置上的变为了三个例化的chnl_initiator实例chnl0_init、chnl1_init和chnl2_init。它们的作用扮演每个channel_slave通道对应的stimulator,发送激励,因此我们在其模块chnl_initiator中定义了它的三个方法,即set_name()、chpl_write()和chnl_idle()。
(1)chnl_idle()要实现的一个时钟周期的空闲,在该周期中,ch.valid应为低,ch.data应为0。
(2)chnl_write()要实现一次有效的写数据,并随后调用chnl_idle(),实现一个空闲周期,在实现有效写数据时,请同学们考虑如何使用ch_ready信号,结合功能描述的channel_slave接口时序来看,只有当valid为高且ready为高时,数据写入才算成功,如果此时ready为低,那么则应该保持数据和valid信号,直到ready拉高时,数据写入才算成功。
(3)set_name()即设置实例的名称,在initial过程块"data_test"中,在发送各个channel数据前,请设置各个channel_initiator的实例名称,这样方便在仿真时各个实例的打印信总可以显示它们各自的名称、数据发送时间和数据内容,便于阅读和调试。
(4)最后,同学们进入了本次实验的最后一个步骤了,路桑之所以提出发送更多的数据,并发送更紧读高速的数据,为了同学们可以观察到,是否你的二个channel.slave各自的chX_ready信号可以拉低呢?如果拉低了,这代表着什么?那么请你试试看,考虑如何发送更多更快的数据,让MCDT的三个chx_ready信号可以拉低吧。
一.从verilog 到SV
1.问题(1)
1)
- 打开QuestaSim,新建工程:
- 导入文件
- 编译
先编译abiter和slave文件,再编译mcdt文件,最后编译tb.v文件。
- 在work库中对tb1.v文件进行仿真
- 给dut添加波形
鼠标点击dut,在相应的Objects列表中选择信号(利用鼠标左键+shift键批量选择),并右键选择Add Wave。
- 运行仿真
在命令窗口中输入run 1us,在Wave窗口中,英文键盘下按F键,可以查看到波形wave1.do。
2)将t1.v的后缀改为.sv,编译t1.sv文件,并重复以上的步骤,得到仿真波形wave2.do:
可以观察到两者波形没有区别,说明SV完全兼容Verilog。
2.问题(2)
- 将所有信号变量类型由reg或者wire修改为logic类型,文件为t12.sv:
仿真得到wave3.0:
可以观察到两者波形没有区别,在SV中用logic类型对reg和wire进行了简化。
3.问题(3)
在问题2的基础之上,将rstn的类型由logic改成bit类,文件为t13.sv:
再仿真编译,得到wave4.0:
如上图在wave4.do中,rstn初始状态值为0;而在wave3.do中,如下图,可以看到rstn在初始状态下值为"X"。这是因为理论上,rstn复位信号由logic变为bit类型,是由四值逻辑变为二值逻辑,只有0和1,而没有"X"和"Z"这两种高阻态。
二.方法** task 和函数 function**
在tb2.sv文件中,可以看出不同于tb1.sv文件的是,之前产生时钟和发起复位的两个Initial过程块语句都被两个task即clk_gen()和rstn_gen()取代了,完成实验的部分:
(1)不做修改的情况下,对tb2.sv进行编译仿真,时钟信号和复位信号还正常吗?为什么?
(2)同学们在路桑标记的两个initial块中分别调用产生时钟和复位的task,再编译仿真查看时钟信号和复位信号,是否恢复正常呢?
(3)为什么要将两个task和两个initial块中调用,这是为什么呢?是否可以在一个initial块中调用呢?如果可以,调用的顺序是什么?
(4)同学们是否可以读出目前时钟的周期和频率呢?如何测量呢?如果我们想进化clk_gen()方法,使其变为可以设置时钟周期的任务clk_gen(20),看看波形中的时钟周期是否变为20ns呢?
(5)如果将’timescale 1ns/1ps修改为’timescale 1ps/1ps,那么仿真中的时钟周期是否发生变化了呢,这是为什么呢?
1.问题(1)
-对tb2.sv进行编译仿真:
观察发现,clk信号和rstn信号没有正常产生。原因可能是没有在开始前调用两个task()。
2.问题(2)
解决问题(1),在t2.sv文件task()后补充两个initial begin块,如下:
重新编译->仿真->添加信号->wave5.do,
可以看到时钟信号和复位信号已经恢复正常。
3.问题(3)
- 为什么要将两个task和两个initial块中调用,这是为什么呢?
Answer:多个initial块在仿真运行一开始就会同步执行,而不是顺序执行;而时钟信号和复位信号需要在其他信号发生前就产生激励,并且两者同步发生,所以要分别放在两个initial块中执行。
- 是否可以在一个initial块中调用呢?如果可以,调用的顺序是什么?
尝试1:将clk、rstn信号先后放在同一个initial块中,文件名为t21.sv
波形如下wave6.do:
Answer: clk、rstn信号先后放在同一个initial块中行不通,rstn信号时钟始终保持高阻态状态。
尝试2:将rstn 、clk信号先后放在同一个initial块中,文件名为t22.sv
波形如下wave7.do:
Answer:rstn、clk信号先后放在同一个initial块中也行不通,clk信号时钟始终保持高阻态状态,数据无法正常发生。
4.问题(4)
同学们是否可以读出目前时钟的周期和频率呢?如何测量呢?如果我们想进化clk_gen()方法,使其变为可以设置时钟周期的任务clk_gen(20),看看波形中的时钟周期是否变为20ns呢?
将clk_gen()改为如下:文件为t3.sv,编译仿真:
在左下角绿色的加号,添加Cursor,屏幕上就会出现有两条黄线
再点击中间的grid
勾选show frequency,两根黄线之间就会出现时间差和频率,如下wave8.do:
观察到周期为40ns,并没有变为20ns,这是因为clk_gen(20)是20ns翻转一次,故对应到波形上就是40ns。
如果想让clk_gen(20)输出周期为20ns的时钟信号,可以这样改进,文件为t31.sv:
将T右移一位,便可以实现半个T翻转一次,则clk_gen(20)输出周期为20ns的时钟信号。如图Wave9.do所示:
5.问题(5)
如果将’timescale 1ns/1ps修改为’timescale 1ps/1ps(文件为tb32.sv),那么运行10us后,仿真图变为下图wave10.do:
仔细看时钟周期:T = 20ps
修改运行时间,改为run 1ns,仿真图变为如下:
综上,时钟周期了发生变化,由20ns变为20ps,缩小了一个数量级。这是因为timescale由1ns变为1ps,缩小导致的。
三.数组的使用
1.问题(1):如果现在要求同学们对每一个slave的数据发出100个数,那么如何实现呢?
- 声明动态数组
logic[31:0] chnl0\_arr[];
logic[31:0] chnl1\_arr[];
logic[31:0] chnl2_arr[];
- 初始化动态数组
chnl0_arr[] = new[100];
chnl1_arr[] = new[100];
chnl2_arr[] = new[100];
2.问题(2):如果现在生成100个数,并对它们按照目前的数值规则进行赋值,请同学们创建3个动态数组,分别放置要发送3个slave的数据。
logic[31:0] chnl0_arr[]; //或者int chnl0_arr[];
logic[31:0] chnl1_arr[]; //或者int chnl1_arr[];
logic[31:0] chnl2_arr[]; //或者int chnl2_arr[];
initital begin
chnl0_arr[] = new[100];
chnl1_arr[] = new[100];
chnl2_arr[] = new[100];
foreach(chnl0_arr[i]) begin
chnl0_arr[i] = 'h00C0_00000 + i;
chnl1_arr[i] = 'h00C1_00000 + i;
chnl2_arr[i] = 'h00C2_00000 + i;
end
end
3.问题(3):接下来利用生成的数组数据,将他们读取并发送给三个channel。
// USER TODO
// use the dynamic array, user would send all of data
// data test
initial begin
@(posedge rstn);
repeat(5) @(posedge clk);
// channel 0 test
// TODO use chnl0_arr to send all data
foreach(chnl0_arr[i]) chnl_write(0, chnl0_arr[i]);
// channel 1 test
// TODO use chnl1_arr to send all data
foreach(chnl1_arr[i]) chnl_write(1, chnl1_arr[i]);
// channel 2 test
// TODO use chnl2_arr to send all data
foreach(chnl2_arr[i]) chnl_write(2, chnl2_arr[i]);
end
- 验证结构
请同学们下载tb4.sv,在接下来开展实验步骤前,同学们可以将“数组的使用”环节中添加的代码部分移柏到tb4.sv对应的位置上。
从tb4.sv中同学们可以发现之前的initil语句块“channel_write_task”已经不见了,在其位置上的变为了三个例化的chnl_initiator实例chnl0_init、chnl1_init和chnl2_init。它们的作用扮演每个channel_slave通道对应的stimulator,发送激励,因此我们在其模块chnl_initiator中定义了它的三个方法,即set_name()、chpl_write()和chnl_idle()。
1.问题(1):chnl_idle()要实现的一个时钟周期的空闲,在该周期中,ch.valid应为低,ch.data应为0。
代码:
task chnl_idle();
@(posedge clk); //一个时钟周期
ch_valid <= 0;
ch_data <= 0;
endtask
2.问题(2):chnl_write()要实现一次有效的写数据,并随后调用chnl_idle(),实现一个空闲周期,在实现有效写数据时,请同学们考虑如何使用ch_ready信号,结合功能描述的channel_slave接口时序来看,只有当valid为高且ready为高时,数据写入才算成功,如果此时ready为低,那么则应该保持数据和valid信号,直到ready拉高时,数据写入才算成功。
代码:
下图是tb2.sv中的chnl_write()任务,对照可以模仿写出以下代码:
task chnl_write(input logic[31:0] data);
//在上升沿阶段将valid置为高电平
@(posedge clk);
ch_valid <= 1;
//接着写入数据
ch_data <= data;
//等待ready信号也为高,数据才算写入成功,否则要保持valid信号和数据
@(negedge clk);
wait (ch_ready === 'b1);
//数据写入成功后,等待一个空闲周期
chnl_idle();
endtask
最后代码如下图:
3.问题(3):set_name()即设置实例的名称,在initial过程块"data_test"中,在发送各个channel数据前,请设置各个channel_initiator的实例名称,这样方便在仿真时各个实例的打印信总可以显示它们各自的名称、数据发送时间和数据内容,便于阅读和调试。
- 先调用set_name()函数为示例命名:
再把动态数组初始化,代码如下图:(这时文件标记为tb41.sv)
此时,tb41.sv已经可以仿真得到数据,波形如下wave11.do:
- 再进一步,改为以下形式:
文件标记为tb42.sv
波形如下wave12.do.
4.问题(4):最后,同学们进入了本次实验的最后一个步骤了,路桑之所以提出发送更多的数据,并发送更紧读高速的数据,为了同学们可以观察到,是否你的二个channel.slave各自的chX_ready信号可以拉低呢?如果拉低了,这代表着什么?那么请你试试看,考虑如何发送更多更快的数据,让MCDT的三个chx_ready信号可以拉低吧。文章来源:https://www.toymoban.com/news/detail-564847.html
附:如何修改字体大小:文章来源地址https://www.toymoban.com/news/detail-564847.html
- (118条消息) 如何修改Questa Sim-64 10.6c软件字体_毛up0827的博客-CSDN博客_questasim字体
- 单个文件串口里将字体放大,可以使用ctrl+,同理,ctrl-可以缩小字体
到了这里,关于路科验证V2实验svlab1的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!