与UVM序列同步

文章作者:Rich Edelman

为SystemVerilog uvm testbench写入序列是一个必不可少的,但通常被误解,部分设计验证过程。

SystemVerilog通用验证方法(UVM)是生成测试和检查功能验证结果的有效方法,最适合用于块级IC或FPGA或其他“较小”系统。在UVM测试台中,大多数活动是通过编写序列产生的,这些序列是验证程序的主力元素,导致诸如刺激生成和结果检查之类的事情发生。所以序列是你应该关注的。UVM有许多移动部件;考虑序列可以让你专注于一个完成任务的程序。

幸运的是,在阅读这篇文章之后,你会意识到他们对他们没有任何神秘的东西,相反,他们只是可以写入多种不同的东西的代码,从随机事务生成来同步到中断服务例程。

在本文中,你将学习以下序列:

  • 生成序列条目
  • 导致其他序列发生
  • 管理其他序列器上的序列
  • 生成无序事务

我们还将向您展示如何编写自检序列,并介绍构建和编写基本序列的基本知识,然后再尝试更高级的用法。最后,这些技巧将帮助您更加熟练地编写和调试UVM序列。

创建并运行UVM序列

正如我们前面提到的,UVM序列是SystemVerilog代码的集合,它导致在测试台中发生事件。它们经常被用来创建一个交易,随机化它,发送它到一个测序器,然后到一个驱动程序。在驱动程序中,生成的事务通常会在接口引脚上引起一些活动。

例如,如图1, write_read_sequence可以生成一个随机的写事务,并将其发送给序列和驱动程序。驱动程序将解释写事务有效负载,并使用指定的地址和数据进行写操作。

UVM序列 图1下面是一个行动的序列。

UVM序列是一个SystemVerilog对象,可以从许多不同的地方构造它,但通常测试可以构造序列然后运行它们——它们体现了测试。

例如,一个测试可能被伪编码为:

加载所有内存位置
读取所有内存位置,
检查期望的值是否匹配。

可能有一个序列把所有的内存位置从a写到b,另一个序列把所有的内存位置从a写到b,或者更简单一些:一个write_read_sequence首先写所有的内存位置,然后读所有的内存位置。

下面的测试在fork/join_none中创建了一个序列。将有四个序列并行运行。每个序列都有一个LIMIT变量集,并在fork/join_none的末尾开始运行。一旦所有的分叉都完成了,测试就完成了。

UVM测试代码

在下面的代码中,my_sequence是一个简单的序列,它创建事务并将它们发送给序列器和驱动程序。实现了body()任务。它是一个简单的for循环,遍历循环LIMIT次数。LIMIT是序列中的一个变量,可以从外部设置。

Uvm我的序列代码

在for循环中,通过调用new()或使用工厂来构造事务对象。然后调用start_item开始与排序程序的交互。此时,序列程序会停止序列的执行,直到驱动程序准备好为止。一旦驱动程序准备好,排序程序就会导致start_item返回。一旦start_item返回,这个序列就被授予使用驱动程序的权限。Start_item应该被称为request_to_send。现在序列有了使用驱动程序的权限,它将随机处理事务,或者根据需要设置数据值。这就是所谓的后期随机化,这是一个理想的特性。事务应该在尽可能接近执行时进行随机化,这样它们就可以在任何约束下捕获最新的状态信息。

随机事务后,将数据值设置为,它将发送到驱动程序以进行处理使用Finess_Item。Finish_Item应该真的被称为execute_item。此时,驱动程序获取交易句柄并将执行它。一旦驱动程序调用item_done(),那么finish_item将返回并且事务已执行。

执行驱动程序

驱动程序代码相对简单。它派生于uvm_driver并包含一个run_phase。run_phase是由UVM核心自动启动的线程。run_phase被实现为一个从开始到结束的永远循环。在开始-结束块中,驱动程序调用seq_item_port。get_next_item (t).这是一个将导致序列执行的任务——本质上是向序列请求应该执行的下一个事务。可能是没有可用的事务,在这种情况下,这个调用将阻塞,也就是说不会返回,因为它正在“等待某事”。(注意:还可以使用其他非阻塞调用,但这超出了本文的范围,不推荐使用)。当排序程序有一个事务要执行时,get_next_item调用将解除阻塞并返回任务参数列表中的事务句柄(在下例中是变量“t”)。现在驱动程序可以执行事务了。

对于此示例,执行很简单 - 它使用事务转换()调用打印邮件,并等待由事务持续时间类成员变量控制的时间量。

一旦执行完成,seq_item_port。进行Item_done()调用,向序列返回事务已被执行的信号,进而向序列发出信号。

uvm驱动代码

虚序列和相关序列

序列可以有其他序列的句柄;毕竟,序列只是一个带有数据成员和任务体()的类对象,任务体将作为线程运行。

虚拟序列是一种可能不生成序列项,而是在其他序列器上启动序列的序列。这是一种从一个控制点创建并行操作的方便方法。一个虚拟序列只是拥有其他序列和序列器的句柄。它启动它们或以其他方式管理它们。虚拟序列可以通过从上面分配、使用配置数据库查找或其他方式获得序列句柄。它可能已经构造了序列对象,或者已经通过类似的其他方法获得了它们。

虚拟序列就像傀儡主人,控制其他序列。虚拟序列可能如下所示:

虚拟的序列码

在下面的代码片段中,有两个序列,ping和pong。它们彼此都有一个把手。它们是按顺序设计的。第一个发送5个事务,然后是另一个,以此类推。请参阅附录以获得完整的代码。

首先构造句柄并设置运行限制。

处理代码

然后把手得到他们的合作伙伴句柄。

合作伙伴处理代码

最后,两个序列并行启动。

并行处理的代码

自检和流量生成器序列

自检序列是导致某些活动的序列,然后检查结果以获得适当的行为。最简单的自检序列对一个地址发出写操作,然后对同一地址发出读操作。现在将读取的数据与写入的数据进行比较。在某些方面,序列成为了黄金模型。

自检序列码

可以编写视频流量生成器来生成背景流量流,以模拟或模拟视频显示可能需要的数据量。视频需要一个最小带宽。计算结果将随着接口或总线上的负载而变化,但是对于这个示例,一个简单的计算就足够了。视频流量将产生一个屏幕的数据每秒60次。每个屏幕将有1920×1024点。每个点用一个32位的字表示。使用这些数字,流量生成器必须每秒创建471MB。

视频通信量产生代码

更完整的流量发生器将根据当前条件调整到达速率 - 随着其他流量上升或向下,应调整视频流量生成速率。

同步序列
序列将用于同步其他序列(所谓的虚序列)。通常两个序列之间需要有形式化的关系。例如,它们不能一起进入它们的关键区域——它们必须是单文件的。或者,它们只能在某个公共临界区域通过后运行。
下面的代码声明了两个要同步的序列(synchro_A_h和synchro_B_h)。它还声明了一个同步器。这些类没有什么特别之处——它们只是同意使用一些技术来实现同步。
同步器代码
同步序列得到一个同步器句柄和一个起始地址。
同步器起始地址代码
同步器控制相当简单。它只是说去20个节拍,停止100个节拍。
同步器永远运行的代码
同步序列被启动。它们运行到完成,然后简单地重新启动。他们一直运行下去。
同步器控制码
简单的同步器有两个状态-去和停止。
同步器停止启动代码
使用同步器直到被告知才执行的类。
同步器执行代码
在模拟中,序列等待直到同步器处于GO状态。一旦进入GO状态,同步代码使用new生成一个事务,然后调用start_item/finish_item来执行它。在等待访问驱动程序并执行之后,同步序列返回到循环的顶部并检查同步器状态。它要么再次GO,要么停止/等待(图2).
图2模拟中的一个同步器等待GO状态。
实现中断服务程序
序列将用于提供中断服务例程.中断服务例程在需要时休眠。这是一种独特的序列。在这个示例实现中,它创建了一个中断服务事务,并执行start_item和finish_item。通过这种方式,它可以将ISR事务句柄发送给驱动程序。然后,驱动程序将一直持有该句柄,直到出现中断。
驱动程序将中断作为其处理SystemVerilog接口的整体工作的一部分来处理。在这种情况下,处理中断意味着将一些数据放入举行句柄,然后这个句柄被标记为完成。同时,中断服务序列一直在等待事务被标记为完成。在某些说法中,这被称为项目真正完成。在UVM中,还有其他机制可以处理这类事情,但它们不可靠,而且比这个解决方案更复杂。

中断的代码
实用工具库

将创建和使用序列实用程序库。实用程序库是对序列编写者、辅助函数或验证过程的其他抽象有用的简单代码。

下面的open_door序列就像它的名字所暗示的那样。它打开了门,对音序器和司机。外部调用现在可以使用序列对象句柄(seq。读取()和seq。例如Write())。

开放代码
open_door被构造,然后使用正常的方法开始。然后测试程序就可以像下面红线所示的那样简单地发出读写操作。
开门测试代码
从序列调用C代码

从序列调用C代码(使用DPI-C)很容易,但有一些限制。DPI导入和导出语句不能放置在类中,因此它们必须位于文件、全局或包作用域的类之外。因此,它们没有设计或类对象作用域。

C代码添加
DPI-C导出函数或任务只是使用export命令导出的SystemVerilog函数或任务。
函数无效代码
DPI-C导入函数或任务是带有返回值的C函数。对于一个任务,返回值是一个“int”(详细信息请参见SystemVerilog LRM)。对于一个函数,返回值是它应该是的任何值。
下面定义了简单的空函数c_code_add()。它有两个输入并在指针*z中返回一个值。这个C函数调用导出的SystemVerilog函数sv_code()。
虚函数c代码添加
h是检查API是否为DPI-C的方便方法。在这个例子中,dpiheader.h(下面)非常简单。
dpiheader.h代码
这个序列没有做什么特别的事情。它生成事务,但它调用一个C函数(c_code_add下面的红线)。就编写调用C代码的序列而言,对于序列实际上没有什么特别的事情要做。必须正确地编写DPI-C代码,并且必须在适当的范围内声明。
DPI-C代码
序列和事务记录

在本文讨论的示例代码中,每个序列都是并行运行的—同时在单个序列器上运行。下面的两个截图很容易看到(图3和4)每个序列如何轮流在驱动上发送和执行事务。

uvm序列事务 图3这个展开的视图显示了每个序列如何轮流发送和执行事务。

uvm序列事务 图4这是交易的放大图。
丰富的爱德曼是一家高级验证方法医学家,是西门子业务。

留下你的评论