写入和读取寄存器是控制和查询大多数IP行为的主要方式。由于基本寄存器对设计的正确操作有直接影响,因此,寄存器测试是设计验证和启动时看似简单但很重要的一环。在IP级别,必须验证寄存器的正确实现——可以从IP块上的接口访问它们。在子系统级,验证对寄存器的访问有助于确认已根据规范实现了互连网络和地址解码。在SoC级别,验证对寄存器的访问可以确认处理器字节顺序与互连实现是否匹配,并且引导代码正确配置存储器管理单元(MMU),使得IP寄存器对处理器可见。
在本文中,我们将探讨通过Portable Stimulus Standard(PSS)的portable stimulus,如何利用寄存器模型中捕获的信息来自动创建块、子系统和SoC寄存器访问测试。
UVM(Universal Verification Methodology,通用验证方法学)提供了一个寄存器模型,用于对寄存器空间进行建模 - 可用的寄存器、字段及其属性,如地址、可访问性和复位值。UVM库还提供内置的定向序列,它们遍历UVM寄存器模型并检查寄存器的复位值,并通过修改和检查所有读/写寄存器字段来确认寄存器是否可访问。这些内置测试在IP级别非常有用,可以确认单个IP块内的寄存器是否已正确实现。
然而,尝试在子系统级重用这些测试序列变得很困难,因为子系统中存在寄存器的数量以及内置UVM序列是期望测试设计中的每个寄存器的定向序列的情况。尝试在SoC级别重用这些内置测试序列也很困难,因为内置序列是在SystemVerilog中实现的自检序列。为了在SoC的嵌入式处理器上运行,我们需要使用裸机C或汇编语言。
通过使用portable stimulus对测试意图进行建模,我们可以灵活地将测试空间划分为多个较小的测试单元。我们还可以通过在C或汇编语言中创建嵌入式软件测试来获得针对SoC级别测试的灵活性。
portable stimulus可将测试中的两个元素分开,这两个元素通常在定向测试中合并。测试意图是测试内容的高级设计,测试实现是执行该测试的机制。
在测试寄存器模型的情况下,我们的测试意图类似于以下内容:
从寄存器块中选择一个寄存器
从寄存器中选择读/写字段
在该字段中选择一个要测试的位
上述测试意图与我们是否验证块、子系统或SoC级设计无关。它也与SystemVerilog环境还是嵌入式软件环境无关。
暂时忽略适当的约束,我们测试单个寄存器的测试意图由上面的PSS代码捕获:
有一个reg_id字段来捕获我们正在测试的寄存器
有一个flip_bit字段来捕获我们希望测试的寄存器中的哪个位
有一个reg_addr字段来捕获内存映射中字段的地址
现在,如果没有将此测试意图连接到特定测试环境的测试实现,我们的测试意图就毫无价值。理想情况下,可以考虑用portable stimulus来设计我们的测试实现。这样做可以让我们设计一个可在所有环境中使用的通用API。例如,可以指定名为testbit的方法在所有环境中都可用,并且此方法将测试修改指定位的能力。这种功能的功能原型如下所示。
在SystemVerilog IP或子系统级环境中,我们可以将此方法实现为UVM序列中的任务。下面显示的SystemVerilog任务使用内存访问API来读写内存,并通过以下方式实现寄存器位测试操作:
读取寄存器的当前值
取消寄存器的目标位,并写入新的寄存器值
读回寄存器地址,并检查该位是否保留其值
在上面显示的示例中,如果遇到错误,可以继续运行,这是UVM测试的典型特征。但是,嵌入式软件环境具有不同的约束。
上面的示例显示了嵌入式处理器的testbit函数的可能实现。如您所见,该方法与SystemVerilog版本非常相似,但具体情况不同。最大的区别是:在这种情况下,假设如果寄存器位测试失败,我们将结束整个测试。
分离测试意图和测试实现是portable stimulus的核心要素,并且是使测试意图在各种环境中轻松重新定位的关键。
我们已经研究了使用PSS捕获测试意图和测试实现,以进行寄存器测试的方法,下面简单地看看我们的UVM寄存器模型是如何形成的。出于本示例的目的,看一下图1中非常简单的子系统,该子系统有一个处理器、两个以太网控制器和两个DMA引擎。
在块级别验证以太网和DMA控制器时,为其创建了UVM寄存器模型。这些UVM寄存器模型可以手工创建,但更有可能是使用寄存器创建工具创建的,如图2所示。
寄存器创建工具接受各种标准(IP-XACT,SystemRDL)和非标准(例如,CSV)格式的寄存器规范,并可生成该寄存器规范的各种视图。生成寄存器模型的RTL实现可节省设计实现的时间,生成UVM寄存器模型可缩短启动测试平台的时间。在所有情况下,确保一切都与高级寄存器规范保持一致可以节省大量时间。
当达到子系统级别时,寄存器创建工具可以帮助我们为整个子系统组装寄存器模型。或者,我们可以简单地采用单独的块级寄存器模型并组装子系统级寄存器模型。下面的代码汇集了以太网和DMA控制器寄存器模型中的子系统级寄存器模型。如您所见,不需要太多代码。
设计中可用的寄存器及其地址
这些寄存器中的字段和任何访问限制
令人惊讶的是寄存器的数量累积的速度很快,当我们为两个以太网控制器和两个DMA控制器创建寄存器模型时,有989个带可测试字段的寄存器。试想一下完整的SoC包含多少寄存器!
现在我们已经捕获了子系统或SoC级寄存器模型,如何继续创建寄存器访问测试呢?首先,需要创建寄存器访问测试意图的PSS模型。然后,我们需要将测试意图连接到具有测试实现的特定验证环境。
以前,我们查看了寄存器访问测试的核心测试意图:选择一个寄存器,并在该寄存器中进行测试。当然,这种高级测试意图必须基于正在测试的设计中的寄存器来约束。好消息是我们已经捕获了在寄存器模型中生成这些约束所需的所有信息。
在某些情况下,我们的寄存器创建工具可能能够直接创建PSS寄存器测试作为其输出之一。如果没有,那么自动创建测试意图的一种方法是:运行SystemVerilog代码,该代码遍历UVM寄存器模型并写出我们的PSS寄存器测试意图。下面的代码显示了一个UVM测试,它调用名为regmodel2pss的类来创建PSS测试意图。
其结果是形成了一个portable stimulus描述,即捕获我们的子系统寄存器映射的寄存器测试意图。下面显示的代码是PSS组件的第一部分和为测试寄存器模型而创建的操作。
我们的操作(其名称来自寄存器块名称)声明了三个rand字段:reg_id字段包含目标寄存器的ID,范围介于0和寄存器数减1之间;flip_bit字段指定要测试的寄存器位,该字段以及寄存器地址将根据寄存器ID进行约束。
上面自动创建的约束将确保我们的操作根据被测试的寄存器产生有效的寄存器地址和flip_bit。
除了自动创建约束外,还可以自动创建PSS覆盖模型,以确保已覆盖寄存器中的所有寄存器和所有位。
一旦拥有核心寄存器测试意图,我们需要将其集成到顶级PSS场景中。虽然核心寄存器测试意图是从寄存器模型自动派生的,但我们的顶级寄存器测试场景却是手工创建的。
上图显示了测试场景,它构建在编码测试意图的核心操作之上。根据PSS的要求,测试场景封装在顶层组件中。我们在顶级组件中创建了一个register-test组件(subsys_reg_block_c)的实例,因为我们将使用该组件中的操作。在顶级操作(my_subsys_regtest_a)中,创建了一个名为testbit的subsys_reg_block_regs_a操作实例,此操作内部是register-test字段和covergroup的实例。在顶级操作中,运行100次testbit,这意味着每次测试运行时,将测试100个寄存器位。
顶级测试场景实际上并没有做任何事情,因为缺少测试实现。幸运的是,PSS允许我们轻松地在测试实现中进行分层,而无需更改核心描述 - 在本例中为subsys_reg_block_regs_a操作。
上图显示了利用PSS程序接口的SystemVerilog测试实现描述。声明外部函数的签名,并从操作的exec块调用该函数。这种测试实现方式适用于所有支持可调用过程的环境,如C,C ++,SystemVerilog等。
在多数情况下,对SoC的裸机嵌入式软件测试将用C语言编写。但是,如果需要创建汇编语言测试呢?幸运的是,PSS也提供了相应的方法!
在上面讨论的测试实现中,我们使用PSS 目标模板 exec块来指定必须生成的汇编代码片段(在这种情况下为RISC-V)以测试寄存器位。大括号(例如,{{reg_addr}})用于引用PSS模型中字段的当前值,并将该值替换为生成的代码。用汇编语言进行测试实现肯定有它的局限性,但是当需要特定技术时,PSS就有可能实现。
既然有测试意图和测试实现来测试对寄存器的访问,我们就可以开始运行测试了。在UVM环境中,PSS为我们提供了预生成定向测试或运行PSS求解器引擎,以及模拟的灵活性。这两种方法都有其优点和缺点。一方面,定向测试很容易理解。
例如,上面的UVM序列非常容易理解,并且总是完全相同。但是,我们希望测试能够在运行不同的“种子”时略微不同。而且,我们希望能够在回归中运行的不同模拟中轻松划分成千上万的寄存器测试。这就是使用与模拟一起运行的PSS求解器引擎真正有用的地方。使用不同种子运行相同的序列会导致不同的行为,而像Questa?inFact这样的PSS工具提供了专用功能,可以在回归中运行的模拟中动态分区测试。
如果能够在裸机软件环境中使用C,我们就可以使用testbit函数实现和PSS程序接口来生成C测试代码,如上所示。
但是,如果需要使用程序集,我们可以使用target-template exec块测试实现来生成完全独立的测试。该测试的片段测试了两个寄存器/位组合。
寄存器测试是从IP到子系统,再到SoC级别的所有环境中非常有用的“烟雾”测试。虽然UVM库中的内置寄存器测试序列主要用于IP级别,但在PSS模型中捕获寄存器测试意图使得寄存器测试功能可从IP移植到SoC级别,并且在控制哪些寄存器方面提供更大的灵活性。
寄存器生成工具的输入文件和生成的UVM寄存器模型都包含足够的信息来自动创建portable stimulus测试意图。这使得寄存器生成工具很容易添加对portable stimulus测试的支持。它还可以很容易地从现有UVM寄存器模型中获取portable stimulus测试,无论它们是如何创建的。
因此,portable stimulus不仅适用于最难的测试,它还可以像看似简单的测试任务一样轻松高效地应用,例如注册访问测试,它可以提供可移植性,从而减少重复工作。
学习园地