MyHDL中文手册(九)—— MyHDL与Verilog协同仿真

35 篇文章 9 订阅
21 篇文章 1 订阅

介绍

MyHDL最令人兴奋的一个方面是将其用作硬件验证语言(HVL)。HVL是一种用于编写测试台和验证环境以及控制仿真的语言。

目前,人们普遍认为HVL应该配备现代化的软件技术,如面向对象技术。究其原因,验证是设计过程中最复杂、最耗时的工作。因此,任何有用的技术都是受欢迎的。此外,并不要求测试台是可实现的。因此,与可综合代码不同的是,对创造性没有任何限制。

从技术上讲,验证用另一种语言实现的设计需要协同仿真。MyHDL能够与任何具有过程性语言接口(PLI)的HDL模拟器协同仿真。MyHDL side被设计为独立于一个特定的仿真器,另一方面,对于每个HDL仿真器,一个特定的PLI模块必须用C语言编写。目前,MyHDL发行版包含一个用于两个Verilog模拟器的PLI模块:Icarus和Cver。

hdl inside

为了介绍联合仿真,我们将继续使用前面章节中的格雷编码器示例。假设我们要综合它,并为此目的用Verilog编写它。显然,我们希望重用我们的单元测试验证环境。

首先,让我们回顾一下MyHDL中的Gray编码器的外观:

from myhdl import block, always_comb

@block
def bin2gray(B, G):
    """ Gray encoder.

    B -- binary input 
    G -- Gray encoded output
    """

    @always_comb
    def logic():
        G.next = (B>>1) ^ B

    return logic

为了显示协同仿真流程,我们还不需要Verilog实现,只需要接口。Verilog中的Gray编码器将具有以下接口:

module bin2gray(B, G);

   parameter width = 8;
   input [width-1:0]  B;
   output [width-1:0] G;
   ....

要编写一个测试平台,需要创建一个新的模块来实例化被测试的设计(DUT)。测试平台声明连接到DUT、刺激生成器和响应检查器的网和规则(或VHDL中的信号)。在全HDL流程中,生成器和检查器是用HDL本身编写的,但我们希望用MyHDL编写它们。为了建立连接,我们需要声明哪些正则和网是由MyHDL模拟器驱动和读取的。对于我们的示例,此操作如下所示:

module dut_bin2gray;

   reg [`width-1:0] B;
   wire [`width-1:0] G;

   initial begin
      $from_myhdl(B);
      $to_myhdl(G);
   end

   bin2gray dut (.B(B), .G(G));
   defparam dut.width = `width;

endmodule

f r o m m y h d l 任 务 调 用 声 明 哪 些 r e g s 由 M y H D L 驱 动 , from_myhdl任务调用声明哪些regs由MyHDL驱动, frommyhdlregsMyHDLto_myhdl任务调用由它读取regs&net。这些任务使用任意数量的参数。它们在用C编写的PLI模块中定义,并以依赖于仿真器的方式提供。在IcarusVerilog中,任务是在从C源代码编译的myhdl.vpi模块中定义的。

MyHDL侧

MyHDL支持通过协同仿真对象进行协同仿真。协同仿真对象必须知道如何运行HDL仿真。因此,其构造函数的第一个参数是用于执行仿真的命令字符串。

生成和运行模拟可执行文件的方法依赖于仿真器。例如,在Icarus Verilog中,可以通过运行iverilog编译器获得示例的仿真可执行文件,如下所示:

% iverilog -o bin2gray -Dwidth=4 bin2gray.v dut_bin2gray.v

这将通过编译贡献的Verilog文件,生成参数宽度为4的bin2Gray可执行文件。

仿真本身由VVP命令运行:

% vvp -m ./myhdl.vpi bin2gray

这将运行bin2Gray仿真,并指定使用当前目录中的myhdl.vpiPLI模块。(这只是一个命令行使用示例;实际上,使用myhdl.vpi模块进行仿真只在协同仿真对象中有意义)。

我们可以使用一个协同仿真对象为MyHDL仿真器提供一个设计的HDL版本。我们编写的函数不是生成器函数,而是返回协同仿真对象的函数。对于我们的示例和Icarus Verilog模拟器,执行以下操作:

import os

from myhdl import Cosimulation

cmd = "iverilog -o bin2gray.o -Dwidth=%s " + \
      "../../test/verilog/bin2gray.v " + \
      "../../test/verilog/dut_bin2gray.v "

def bin2gray(B, G):
    width = len(B)
    os.system(cmd % width)
    return Cosimulation("vvp -m ../myhdl.vpi bin2gray.o", B=B, G=G)

在可执行命令参数之后,协同仿真构造函数接受任意数量的关键字参数。这些参数通过命名关联将MyHDL信号与HDL网、规则或信号联系起来。关键字是 t o m y h d l 或 to_myhdl或 tomyhdlfrom_myhdl调用中参数的名称;参数是MyHDL信号。

完成所有这些之后,我们现在可以使用现有的单元测试来验证Verilog实现。注意,我们为bin2Gray函数保留了相同的名称和参数:我们所需要做的就是为现有的单元测试提供这个替代定义。

让我们在Verilog设计上尝试一下:

module bin2gray(B, G);

   parameter width = 8;
   input [width-1:0]  B;
   output [width-1:0] G;

   assign G = (B >> 1) ^ B;

endmodule // bin2gray

当我们运行单元测试时,我们得到:

% python test_gray.py
testSingleBitChange (test_gray_properties.TestGrayCodeProperties)
Check that only one bit changes in successive codewords. ... ok
testUniqueCodeWords (test_gray_properties.TestGrayCodeProperties)
Check that all codewords occur exactly once. ... ok
testOriginalGrayCode (test_gray_original.TestOriginalGrayCode)
Check that the code is an original Gray code. ... ok

----------------------------------------------------------------------
Ran 3 tests in 0.706s

OK

限制

在理想的情况下,应该可以用MyHDL无缝地仿真任何HDL描述。此外,每一方的通信信号应作为一个单一的透明行为,充分无竞争冒险地操作。

由于各种原因,或许不可能或不希望实现全面的普遍性。任何使用Verilog PLI开发应用程序的人都可以证明,特定仿真器中的限制以及不同仿真器之间的差异可能会非常令人沮丧。此外,与可能足以满足目标应用程序的稍微不太通用的解决方案相比,完全的通用性可能需要高得不成比例的开发努力。

因此,我试图实现一个足够简单的解决方案,以便人们可以合理地期望任何支持PLI的模拟器都可以支持它,并且这个解决方案比较容易验证和维护。同时,该解决方案具有足够的通用性,足以覆盖目标应用程序空间。

其结果是对HDL代码施加某些限制的折衷方案。在本节中,将介绍这些限制。

只有被动的hdl可以协同仿真

MyHDL协同仿真解决方案最重要的限制是只有“被动”HDL才能协同仿真。这意味着HDL代码不应该包含任何带有时间延迟的语句。换句话说,MyHDL仿真器应该是时间的主人;特别是,任何时钟信号都应该在MyHDL端生成。

起初,这看起来似乎是一个重要的限制,但如果考虑到协同仿真的目标应用程序,可能就不是了。

MyHDL支持协同仿真,因此HDL设计的测试台可以用Python编写。让我们考虑一下目标HDL设计的本质。对于不打算实现的高级行为模型,我建议直接用MyHDL编写它们也就不足为奇了;这是MyHDL工作的目标之一。同样,带有注释时序的门级设计不是目标应用程序:静态时序分析是此类设计的一种更好的验证方法。

相反,目标HDL设计自然是为了实现的模型,很可能是通过综合实现的。由于时间延迟在可综合代码中是无意义的,因此该限制与目标应用程序兼容。

竞争敏感性问题

在典型的RTL代码中,某些事件会导致其他事件在同一时间步长内发生。例如,当时钟信号触发时,某些信号可能在同一时间步长内发生变化。对于无竞争的操作,HDL必须在一个时间步长内区分这类事件。这是通过“增量”循环的概念来完成的。在完全通用的、无竞争的联合仿真中,联合仿真器将在增量循环级别进行通信。然而,在MyHDL共同仿真中,情况并不完全如此。

保留了从MyHD器到HDL 协同模拟器的Delta周期。然而,在相反的方向上,他们不是。只有在HDL 协同仿真器中执行了所有增量周期之后,信号变化才会返回给MyHDL仿真器。

这是什么意思?让我们从好消息开始。正如上一节所解释的,MyHDL协同仿真背后的概念意味着时钟是在MyHDL端生成的。当直接使用MyHDL时钟及其对应的HDL信号作为时钟时,协同仿真是无竞争的。换句话说,最能反映MyHDL协同仿真方法的情况是没有竞争冒险的。

当您希望使用HDL驱动的信号(以及相应的MyHDL信号)作为时钟时,情况就不同了。由这样的时钟触发的通信不是无竞争的。解决方案是将这种接口视为芯片接口,而不是RTL接口。例如,当数据在正时钟边沿触发时,可以安全地在负时钟边沿对其进行采样。或者,可以用延迟值来声明MyHDL数据信号,以便保证它们在时钟边缘之后发生变化。

实施说明

本节需要一些PLI术语的知识。

为协同仿真启用仿真器需要一个用C编写的PLI模块。在Verilog中,PLI是“标准”的一部分。但是,不同的仿真器实现了标准的不同版本和部分。更糟糕的是,某些PLI回调的行为没有在某些基本点上定义。因此,应该计划为任何仿真器编写或至少定制一个特定的PLI模块。该版本包含一个用于开源Icarus和Cver模拟器的PLI模块。

本节记录了PLI模块实现的当前方法和状态,以及对未来实现的一些思考。

Icarus Verilog

delta增量循环实现

为了使协同仿真工作,需要一种特定类型的PLI回调。回调应该在处理完所有挂起的事件后运行,同时允许在当前时间步长内创建新事件(例如,通过MyHDL模拟器)。在一些Verilog仿真器中,cbReadWriteSync回调就是这样做的。然而,在其他地方,包括Icarus,情况并非如此。回调的行为没有完全标准化;一些模拟器在处理非阻塞赋值事件之前运行回调。

因此,我不得不寻找一个变通办法。解决方案的一半是使用cbReadOnlySync回调。此回调在处理完所有挂起的事件后运行。但是,它不允许在当前时间步长中创建新事件。解决方案的后半部分是将MyHDL delat增量周期映射到真正的Verilog时间点。注意,幸运的是,我在这里有一些自由,因为这里的限制是只有被动的HDL代码可以共同模拟。

我选择使Verilog仿真器中的时间粒度比MyHDL模拟器中的时间粒度精细1000倍。对于每个MyHDL时间步,1000个Verilog时间步可用于MyHDL增量周期。在实践中,每一时间步只需要几个delta周期。超过此限制几乎肯定表示设计错误;该限制在运行时被检查。因子1000还使得在打印Verilog时间时很容易区分“实时”时间和增量周期时间。

被动Verilog检查

如前所述,共同仿真Verilog不应包含延迟语句。理想情况下,应该进行运行时检查,以标记不兼容的代码。然而,目前在Icarus模块中没有这样的检查。

可以使用Verilog中的cbNextSimTimeVPI回调来编写检查。然而,Icarus0.7不支持这种回调。同时,Icarus开发分支增加了支持。当Icarus 0.8发布时,将添加一个复选框。

同时别这么做。它可能看起来“工作”,但它实际上不会,因为事件将被错过的共同仿真界面。

cver

MyHDL协同仿真支持开放源代码的Verilog仿真器cver。PLI模块是基于Icarus的模块,基本上具有相同的功能。只需要做一些表面修饰。

其他Verilog仿真器

Icarus模块是使用最新一代的Verilog PLI提供的VPI调用编写的。有些仿真器可能只支持TF/ACC调用,需要对接口模块进行完全重新设计。

如果仿真器支持VPI,那么Icarus模块应该在很大程度上是可重用的。然而,有可能在此基础上加以改进。支持Section Delta Cycle Implementation中描述的增量周期的解决方法可能不是必需的。在某些模拟器中,cbReadWriteSync回调发生在处理完所有事件(包括非阻塞分配)之后。在这种情况下,在Verilog模拟器中不需要更细的时间粒度就可以支持该功能。

还有Verilog标准化工作正在进行中,以解决cbReadWriteSync回调的模糊性。解决方案是引入新的、定义良好的回调。通过阅读一些建议,我得出结论,cbEndOfSimTime回调将提供所需的功能。

MyHDL项目目前无法访问商业Verilog仿真器,因此协同仿真支持的进展取决于外部兴趣和参与。用户报告说,他们正在使用MyHDL与来自Aldec和Modelsim的仿真器进行联合仿真。

中断的系统调用

PLI模块使用读写系统调用在联合仿真器之间进行通信。该实现假设这些调用在中断时由操作系统自动重新启动。这显然就是在开发MyHDL的Linux机器上发生的情况。

我们知道应该如何处理未重新启动的中断的系统调用,但是目前还不能在MyHDL开发平台上测试这些代码。此外,也不清楚这是否仍然是一个与现代操作系统相关的问题。因此,这一问题目前尚未得到处理。但是,已经包含了在发生这种情况时应该触发的断言。

每当在PLI模块中触发断言时,请报告它。这同样适用于不容易解释的Python异常。

那VHDL呢?

如果能有一个与VHDL仿真器(如Modelsim VHDL仿真器)的接口,那就太好了。让我们总结一下实现这一目标的要求:

我们需要一个到仿真器内部的过程性接口。
过程性接口应该是一种广泛使用的行业标准,这样我们就可以在多个模拟器中重用这些工作。
MyHDL是一个开源项目,因此也应该有一个实现过程性接口的开源仿真器。

Verilog的VPI符合这些要求。它是一种广泛使用的标准,并得到开源Verilog仿真器Icarus和Cver的支持。

然而,对于VHDL来说,情况却是不同的。虽然存在一种称为vhpi的标准,但它远不如VPI流行。而且,据我们所知,只有一个可信的开放源码VHDL仿真器(GHDL),而且不清楚它是否具有足够强大的vhpi功能来满足MyHDL的目的。

因此,VHDL协同仿真的发展目前被搁置。对于某些应用程序,有一种替代方法:请参见测试工作台的转换。

  • 0
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值