路科验证V2实验svlab1

文章详细介绍了从Verilog到SystemVerilog的转换过程,包括信号类型的修改、使用logic类型、task和function的运用,以及数组的使用和验证结构的设计。在转换过程中,重点讨论了时钟和复位信号的生成,时钟周期和频率的测量,以及如何通过调整代码来影响这些参数。此外,文章还阐述了如何通过动态数组向多个通道发送数据,并设计了验证结构以实现清晰的激励生成。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目要求

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的博客-优快云博客_questasim字体
  2. 单个文件串口里将字体放大,可以使用ctrl+,同理,ctrl-可以缩小字体
SystemVerilog的听课学习笔记,包括讲义截取、知识点记录、注意事项等细节的标注。 目录如下: 第一章 SV环境构建常识 1 1.1 数据类型 1 四、二值逻辑 4 定宽数组 9 foreach 13 动态数组 16 队列 19 关联数组 21 枚举类型 23 字符串 25 1.2 过程块和方法 27 initial和always 30 function逻辑电路 33 task时序电路 35 动态 静态变量 39 1.3 设计例化和连接 45 第二章 验证的方法 393 动态仿真 395 静态检查 397 虚拟模型 403 硬件加速 405 效能验证 408 性能验证 410 第三章 SV组件实现 99 3.1 接口 100 什么是interface 101 接口的优势 108 3.2 采样和数据驱动 112 竞争问题 113 接口中的时序块clocking 123 利于clocking的驱动 133 3.3 测试的开始和结束 136 仿真开始 139 program隐式结束 143 program显式结束 145 软件域program 147 3.4 调试方法 150 第四章 验证的计划 166 4.1 计划概述 166 4.2 计划的内容 173 4.3 计划的实现 185 4.4 计划的进程评估 194 第五章 验证的管理 277 6.1 验证的周期检查 277 6.2 管理三要素 291 6.3 验证的收敛 303 6.4 问题追踪 314 6.5 团队建设 321 6.6 验证的专业化 330 第六章 验证平台的结构 48 2.1 测试平台 49 2.2 硬件设计描述 55 MCDF接口描述 58 MCDF接口时序 62 MCDF寄存器描述 65 2.3 激励发生器 67 channel initiator 72 register initiator 73 2.4 监测器 74 2.5 比较器 81 2.6 验证结构 95 第七章 激励发生封装:类 209 5.1 概述 209 5.2 类的成员 233 5.3 类的继承 245 三种类型权限 protected/local/public 247 this super 253 成员覆盖 257 5.4 句柄的使用 263 5.5 包的使用 269 第八章 激励发生的随机化 340 7.1 随机约束和分布 340 权重分布 353 条件约束 355 7.2 约束块控制 358 7.3 随机函数 366 7.4 数组约束 373 7.5 随机控制 388 第九章 线程与通信 432 9.1 线程的使用 432 9.2 线程的控制 441 三个fork...join 443 等待衍生线程 451 停止线程disable 451 9.3 线程的通信 458 第十章 进程评估:覆盖率 495 10.1 覆盖率类型 495 10.2 功能覆盖策略 510 10.3 覆盖组 516 10.4 数据采样 524 10.5 覆盖选项 544 10.6 数据分析 550 第十一章 SV语言核心进阶 552 11.1 类型转换 552 11.2 虚方法 564 11.3 对象拷贝 575 11.4 回调函数 584 11.5 参数化的类 590 第十二章 UVM简介 392 8.2 UVM简介 414 8.3 UVM组件 420 8.4 UVM环境 425
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值