路科验证V2实验svlab1

题目要求

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块,如下:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9vqzt0Un-1677632513166)(RackMultipart20230301-1-atmbur_html_e8bb9994d765f0eb.png)]

重新编译->仿真->添加信号->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

在这里插入图片描述

  1. 验证结构

请同学们下载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信号可以拉低吧。

附:如何修改字体大小:

  1. (118条消息) 如何修改Questa Sim-64 10.6c软件字体_毛up0827的博客-CSDN博客_questasim字体
  2. 单个文件串口里将字体放大,可以使用ctrl+,同理,ctrl-可以缩小字体

猜你喜欢

转载自blog.csdn.net/lvyunkai0518/article/details/129274125