为SystemVerilog uvm testbench写入序列是一个必不可少的,但通常被误解,部分设计验证过程。
SystemVerilog通用验证方法(UVM)是生成测试和检查功能验证结果的有效方法,最适合用于块级IC或FPGA或其他“较小”系统。在UVM测试台中,大多数活动是通过编写序列产生的,这些序列是验证程序的主力元素,导致诸如刺激生成和结果检查之类的事情发生。所以序列是你应该关注的。UVM有许多移动部件;考虑序列可以让你专注于一个完成任务的程序。
幸运的是,在阅读这篇文章之后,你会意识到他们对他们没有任何神秘的东西,相反,他们只是可以写入多种不同的东西的代码,从随机事务生成来同步到中断服务例程。
在本文中,你将学习以下序列:
我们还将向您展示如何编写自检序列,并介绍构建和编写基本序列的基本知识,然后再尝试更高级的用法。最后,这些技巧将帮助您更加熟练地编写和调试UVM序列。
创建并运行UVM序列
正如我们前面提到的,UVM序列是SystemVerilog代码的集合,它导致在测试台中发生事件。它们经常被用来创建一个交易,随机化它,发送它到一个测序器,然后到一个驱动程序。在驱动程序中,生成的事务通常会在接口引脚上引起一些活动。
例如,如图1, write_read_sequence可以生成一个随机的写事务,并将其发送给序列和驱动程序。驱动程序将解释写事务有效负载,并使用指定的地址和数据进行写操作。
UVM序列是一个SystemVerilog对象,可以从许多不同的地方构造它,但通常测试可以构造序列然后运行它们——它们体现了测试。
例如,一个测试可能被伪编码为:
加载所有内存位置
读取所有内存位置,
检查期望的值是否匹配。
可能有一个序列把所有的内存位置从a写到b,另一个序列把所有的内存位置从a写到b,或者更简单一些:一个write_read_sequence首先写所有的内存位置,然后读所有的内存位置。
下面的测试在fork/join_none中创建了一个序列。将有四个序列并行运行。每个序列都有一个LIMIT变量集,并在fork/join_none的末尾开始运行。一旦所有的分叉都完成了,测试就完成了。
在下面的代码中,my_sequence是一个简单的序列,它创建事务并将它们发送给序列器和驱动程序。实现了body()任务。它是一个简单的for循环,遍历循环LIMIT次数。LIMIT是序列中的一个变量,可以从外部设置。
在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()调用,向序列返回事务已被执行的信号,进而向序列发出信号。
虚序列和相关序列
序列可以有其他序列的句柄;毕竟,序列只是一个带有数据成员和任务体()的类对象,任务体将作为线程运行。
虚拟序列是一种可能不生成序列项,而是在其他序列器上启动序列的序列。这是一种从一个控制点创建并行操作的方便方法。一个虚拟序列只是拥有其他序列和序列器的句柄。它启动它们或以其他方式管理它们。虚拟序列可以通过从上面分配、使用配置数据库查找或其他方式获得序列句柄。它可能已经构造了序列对象,或者已经通过类似的其他方法获得了它们。
虚拟序列就像傀儡主人,控制其他序列。虚拟序列可能如下所示:
在下面的代码片段中,有两个序列,ping和pong。它们彼此都有一个把手。它们是按顺序设计的。第一个发送5个事务,然后是另一个,以此类推。请参阅附录以获得完整的代码。
首先构造句柄并设置运行限制。
然后把手得到他们的合作伙伴句柄。
最后,两个序列并行启动。
自检和流量生成器序列
自检序列是导致某些活动的序列,然后检查结果以获得适当的行为。最简单的自检序列对一个地址发出写操作,然后对同一地址发出读操作。现在将读取的数据与写入的数据进行比较。在某些方面,序列成为了黄金模型。
可以编写视频流量生成器来生成背景流量流,以模拟或模拟视频显示可能需要的数据量。视频需要一个最小带宽。计算结果将随着接口或总线上的负载而变化,但是对于这个示例,一个简单的计算就足够了。视频流量将产生一个屏幕的数据每秒60次。每个屏幕将有1920×1024点。每个点用一个32位的字表示。使用这些数字,流量生成器必须每秒创建471MB。
更完整的流量发生器将根据当前条件调整到达速率 - 随着其他流量上升或向下,应调整视频流量生成速率。
将创建和使用序列实用程序库。实用程序库是对序列编写者、辅助函数或验证过程的其他抽象有用的简单代码。
下面的open_door序列就像它的名字所暗示的那样。它打开了门,对音序器和司机。外部调用现在可以使用序列对象句柄(seq。读取()和seq。例如Write())。
从序列调用C代码(使用DPI-C)很容易,但有一些限制。DPI导入和导出语句不能放置在类中,因此它们必须位于文件、全局或包作用域的类之外。因此,它们没有设计或类对象作用域。
在本文讨论的示例代码中,每个序列都是并行运行的—同时在单个序列器上运行。下面的两个截图很容易看到(图3和4)每个序列如何轮流在驱动上发送和执行事务。