Verilog PLI已死( 可能), SystemVerilog DPI当立

🔗【英文原文】 https://sutherland-hdl.com/papers/2004-SNUG-paper_Verilog_PLI_versus_SystemVerilog_DPI.pdf

Verilog PLI已死( 可能), SystemVerilog DPI当立

SNUG San Jose 2004
作者: Stuart Sutherland 译者: 韦和民

摘要

在古老的英格兰, 当一个君主死掉以后, 一个继承者会立即得到王位. 因此就有了这样的歌谣, “ The king is dead—long live the king!” . 随着新的SystemVerilog 直接编程接口(DPI)的出现, Verilog编 程 语 言 接 口 (PLI)看来也有了继承者。 那么是不是老的Verilog PLI已死, SystemVerilog DPI成为新的君主了呢? 本文讨论了工程师应该继续使用Verilog PLI, 还是切换到新的 SystemVerilog DPI的问题。 阐明了DPI能够简化与C语言的接口, 具有很多PLI所不具有的功能。 尽管如此, Verilog PLI还是具有一些DPI所不具有的独特功能。

1.0 介绍

Verilog编 程 语 言 接 口 (PLI)为Verilog代码调用C语言编写的函数提供了一个机制。 历史悠久的Verilog PLI 是Verilog在硬件设计领域获得如此成功的原因之一。 使用PLI, 第三方公司和最终用户能够扩展商用Verilog仿真器的功能。 几乎每个正规的采用Verilog的设计项目运用了Verilog PLI。
在它的差不多20年的历史上, Verilog PLI已经历经两代 “ PLI 1.0” and “ PLI 2.0” (官方称为PLI-VPI)。 Accellera最近为IEEE Verilog标准推出SystemVerilog扩展, 它包括一种新的Verilog代码调用C和 C++函数的办法。 这个新的接口称为直接变成接口DPI。 一些工程师认为SystemVerilog DPI的出现标志着老的Verilog PLI的消亡。 DPI用一种简单的、 简明的C语言接口替换了复杂的Verilog PLI。 是不是SystemVerilog让Verilog PLI变得过时了? 我们是不是可以高唱“ Verilog PLI已死, SystemVerilog DP将立!” 了呢 ?
尽管如此, 在开始庆祝之前, 还是有几个重要的问题需要回答。 SystemVerilog DPI真的比Verilog PLI更好吗? 或者是否有应该继续使用PLI的理由? 是否有些应用还不能用SystemVerilog DPI实现, 从而必须让Verilog PLI存在下去?

2.0 DPI 概述

SystemVerilog直接编程接口(DPI)允许Verilog代码直接调用C函数的名称, 就好像该函数是Verilog语言自身定义的Verilog任务或者函数。 它通过使用简单的 import 声明把C函数名导入到Verilog语言中实现。 导入声明定义该函数使用
DPI接口, 并包括函数名和变元的原型。 举例来说:

import "DPI" function real sin(real in); // sine function in C math library

以上导入声明为Verilog代码定义了一个函数名为 sin。 函数的返回值数据类型为 real (双精度), 函数有一个输入, 也是 real数据类型。 一旦该C函数被导入到Verilog, 我们就能像调用Verilog自带函数一样调用。 举例:

always @(posedge clock) begin
slope <= sin(angle); // call the "sin" function from C
end

导入的C函数必须编译并链接到Verilog仿真器。 这个流程随不同的仿真器而有所不同。 对于 Synopsys VCS仿真器而言, 该流程想当直白: C源文件名, 或者预编译的目标代码文件名, 同Verilog源代码文件一起作为 vcs 编译命令的一部分。使用SystemVerilog DPI, Verilog代码不知道它在调用C函数, 同样C函数也不知道自己被Verilog在调用。
DPI与PLI的比较. 导入一个C函数然后使用DPI直接调用比Verilog PLI确实简单多了。 使用PLI, 用户必须定义一个 用户定义的系统任务或者用户定义的系统函数。 用户定义的系统任务/函数必须以美元符号( $ )开头。举例来说,上述sine函数在Verilog中用 $sine表示。 然后该函数与一个称为calltf 子程序的用户提供的C函数相关联。 Calltf子程序就可以访问C的数学运算库中的sin函数。 Calltf子程序以及它调用的任何函数必须被编译并链接到verilog仿真器中去。 完成这些步骤后, Verilog就能够像调用其他正规的函数一样调用$sine 系统函数。 当仿真执行到调用$sine的时候, 首先调用calltf函数, 然后调用C库中的sin函数。

always @(posedge clock) begin
slope <= $sine(angle); // call the sine PLI application
end

初看起来, 好像在Verilog代码中, 使用PLI函数和导入DPI函数的差别很小。 实际上, 在Verilog代码中确实没有显著的差别; 在两种情况下, Verilog代码都在调用一个任务或者函数。 显著的差异出现在使用什么方法定义PLI用户定义系统
任务或者函数上面。 使用DPI, Verilog代码能够直接从C数学运算库调用 sin函数, 直接传递输入到函数, 直接获取C函数的返回值。 使用PLI 却需要若干步骤, 先生成用户定义的系统任务, 然后间接调用和传递输入给 sin 函数。 这种间
接的流程在以下章节将详细阐述。

3.0 Verilog PLI 功能

为了全面讨论SystemVerilog DPI的优缺点。 有必要了解Verilog PLI的功能, 以及作为PLI标准一部分的C语言库。
Verilog PLI是一个仿真接口。 它提供一个使得用户自带的C函数能够在运行时间访问仿真数据结构的接口。 这些C用户自带函数还能读取和修改某些数据结构。 Verilog PLI并不与Verilog源代码一起运行。 它仅仅操作仿真数据结构。 PLI只是设计用于仿真工具, 而非综合编译器等其他工具。

3.1 PLI如何调用C函数

Verilog PLI 提供一套C语言函数, 允许程序员访问Verilog仿真的数据结构。 这些C函数被打包成三个PLI库, TF库, ACC库和VPI库。 PLI对仿真数据结构的访问是动态的, 能够在仿真运行的过程中进行访问。 访问还是双向的。 通过PLI, 程序员不仅可以从仿真器的数据结构中读取信息, 还能修改数据结构的信息。
通过编写系统任务和系统函数, 程序员能够用Verilog PLI扩展Verilog语言。 这些用户定义的系统任务和函数的名称必须以美元符号( $) 开头。 Verilog里面的任务相当于一个子程序。 当调用任务时, 仿真器的执行流程跳转到子程序, 完成任务后执行流程返回。 Verilog任务并不返回数值, 但是可以有输入, 输出和双向的形参。 Verilog里面的函数跟大多数语言里面的函数一样。 当调用函数时, 它运行一套指令, 然后返回一个数值给调用它的指令。 Verilog任务和函数可以被定义成Verilog语言的一部分。
使用Verilog PLI的时候, 程序员必须首先定义一个系统任务函数名称, 比如$sine。 程序员然后编写C函数, 被引作calltf程序, 并关联到$sine t任务/函数名称。 当仿真执行到调用$sine s系统函数的语句时, 仿真器调用与 s i n e 关 联 的 c a l l t f 程 序 。 这 个 c a l l t f 程 序 是 sine关联的calltf程序。 这个calltf程序是 sinecalltfcalltfsine 跟 C数学函数库中的sin函数之间的一个中间层。 $sine的参数并不是直接传递给calltf程序。 Calltf程序必须从PLI库中调用一个特殊的PLI函数来读取$sine的输入参数。 然后Calltf程序调用sin函数,传递输入给函数。 Calltf程序从sin函数接收返回值, 然后调用另外一个特殊的PLI函数, 把返回值写回到$sine函数。 使用Verilog PLI的最后步骤是把用户定义的系统任务/函数名称与用户名义的calltf程序绑定。 这个步骤对于每个仿真器来说都是不一样的, 有的需要编辑一个特殊的表格文件, 有的需要编辑和编译复杂的C语言文件。
PLI库使得calltf子程序不是只能处理系统任务或者函数的参数。 该库还使得calltf应用程序能够在仿真数据结构中搜索对象, 修改数据结构内的延时和逻辑数值, 同步仿真活动和仿真时序。

3.2 Verilog PLI的演化

Verilog PLI最早开发于上个世纪 80年代中后期, 作为Gateway设计自动化公司(已经被Cadence设计系统公司收购)的Verilog-XL仿真器产品的专用接口。 Gateway开发PLI满足两个基本需要: 第一个是允许Verilog-XL用户使用C语言编写文件输入输出等任务。 第二个是允许ASIC生产商分析标准单元库的使用, 用于计算每个单元内的精确的时间延迟。 从这两个基本的目标开始, Verilog PLI的应用范围日益扩大。 Verilog PLI在今天的工程环境中的普遍应用包括: 商用和
专用波形观察器, 商用和专用设计调试工具, RAM/ROM程序下载工具, 扫描链矢量下载工具, 客户定制文件读写工具,功耗分析、 电路跟踪, cmodel接口, 协同仿真环境, 并行流程分配等等, 还有很多很多。 PLI能够用来扩展Verilog仿真器的功能仅仅受程序员的想象能力制约。
下列章节介绍了Verilog PLI的发展历史。
1985 — TF接口. Verilog PLI的第一代被称为 任务/函数接口, 简称TF接口。 TF接口包括一整套C语言函数库, 函数定义在 verisuer.h 文件中。 这些C函数一般就叫TF子程序。 TF子程序的主要目的是把任务/函数参数传递给C函数。 举例来说, 可以建立一个用户定义的系统任务, 把数据下载到RAM模型中。 在上下文中, 用户定义的系统任务可以如下所示:

initial begin
$loadram(chip.ram.core, "ram_data.pgm"); // load the ram model
...
e

在这个例子中, 系统任务 $loadram具有两个参数: Verilog层次结构内的RAM模型存储阵列的实体名称, 下载到RAM中的数据文件名。 当Verilog仿真器执行到 l o a d r a m 系 统 任 务 的 时 候 , 会 跳 转 到 一 个 用 户 定 义 的 与 loadram系统任务的时候, 会跳转到一个用户定义的与 loadramloadram关联的C函数。 那个C函数从TF库中调用函数, 读取$loadram的参数并继续执行。
TF库函数提供的一个重要功能就是它的跟仿真器的事件调度引擎同步PLI行为的功能。 事件同步是许多类型的PLI应用程序的关键部分, 比如协同仿真应用程序, 并行处理应用程序以及总线功能接口cmodel。
TF接口有个关键的限制。 TF子程序的库只能访问用户定义系统任务或者函数的参数。 在上一个例子里, RAM模型储存单元的名称必须作为系统任务的参数, 传递给PLI应用程序。 $loadram应用程序不能在仿真数据结构中任意搜索RAM模型,也不能在哪些RAM模型中搜索储存单元。
TF接口的缺点是它定义系统任务/函数, 函数返回值以及需要关联任务/函数名称到C函数的机制。 该机制并不是标准化的, 这就意味着每个仿真器有不同的PLI接口机制。 大多数机制要求C编程知识, 还要求对PLI标准有相当多的理解。 许
多工程师并没有很好地在他们的设计项目中利用Verilog PLI, 就是因为PLI应用太复杂了。
1989 — ACC接口. Verilog PLI的第二代引入另一个称为访问程序库的C函数, 简称ACC函数库。 该库由单独的一个文件acc_user.h定义。 ACC库函数是TF函数的一个附加, 而非替换。 是应ASIC厂商的要求而开发的。 在基于Verilog设计的早先年代, 大多数ASIC厂商使用专有延时计算器来提高仿真的精确性。 这些演示计算器使用Verilog PLI搜索仿真数据结构, 找到每个ASIC单元实体。 针对每个单元, ACC函数能够搜索输出fanout。 其他的ACC库函数能够访问存储在单元模型中的信息, 比如电容、 电阻以及本身的延时。 使用这些信息, PLI应用程序能够为每个单元实体计算精确的延时, 然后用这些计算得到的延时信息修改仿真数据结构。
这种动态的基于PLI的延时计算方法最终被静态方法和SDF文件取代。 尽管如此, ACC库函数搜索仿真数据结构中的对象, 跟踪设计连接关系, 读取当前对象数值的能力还是被众多PLI应用程序广泛采用。 举例来说, 使用ACC库函数, PLI应用程序能够搜索仿真数据结构, 找到设计的所有扫描链, 然后并行载入测试向量到扫描寄存器。 波形显示和图形化调试工具常常使用ACC库函数寻找整个或者部分设计的所有线网与/或变量。 这些工具软件读取线网和变量的数值, 显示出来, 或者跟踪设计互连关系找到该数值的根源。
搜索仿真数据结构的能力是ACC库函数和TF库相比的主要优点。 但还是需要指出ACC函数库的一些限制。 ACC库函数只能搜索设计中的结构化对象的仿真数据结构。 结构化的对象包括modul和primitive,以及module和primitive之间的连接。 ACC库函数不能访问RTL和行为级的设计部分。
ACC接口应用TF接口同样的机制定义用户自定义的系统任务/函数名称, 并把该名称同用户定义的C函数相关联。 这种复杂的、 随仿真器各异的接口机制也是ACC接口的一个缺点。
1995 — VPI接口. 第三代的Verilog PLI称为Verilog过程接口, 简称VPI。 VPI库是老的TF和ACC库的超集, 用于完全替换老库。 VPI库使用第三个单独的C函数库文件vpi_user.h。 使用VPI库, 用户能够访问整个仿真数据结构。 包括系统任务和函数的参数(替换TF库) 以及设计的层次结构单元(替换ACC库)。 此外VPI库能够访问设计中所有RTL和行为级的描述部分。 VPI库还能访问仿真器的事件调度引擎, 添加和删除调度事件。 VPI库替换并改进了用于同步PLI应用和仿真事件的TF库函数。
跟TF和ACC接口一样, VPI接口要求编写一个用户定义的系统任务或者用户定义的系统函数。 系统任务/函数的定义方法同老的接口有所不同, 克服了一些老接口的缺点。 尽管如此, 定义方法依旧复杂, 需要程序员对PLI标准有相当好的理
解。
VPI库函数的详细情况不在本文讨论之列。 总之, 使用VPI库, 一个应用程序能够完全、 无限制地访问仿真数据接口。
VPI库函数提供了一个超强的仿真数据结构接口。 PLI应用程序通过它与仿真交互的途径几乎是无穷的。
2003 — SystemVerilog DPI接口. 最新一代的Verilog与C之间的接口就是SystemVerilog直接编程接口(DPI)。 DPI的功能将在后续章节更详细介绍。

3.3 Verilog PLI 标准 — 它的过去和未来

上述PLI的历史历经多个Verilog标准。
OVI (现为Accellera) PLI 1.0. TF和ACC接口的最早定义是Gateway设计自动化公司的专有产品, 就像Verilog语言一样。
1989年, Gateway被Cadence设计系统公司收购, 1990年, Cadence公布了Verilog语言和PLI。 并成立一个叫OVI的非盈利组织推动Verilog的使用。 OVI把1990公开的Verilog称为Verilog 1.0和PLI 1.0.
OVI PLI 2.0. 1993年, OVI发布Verilog语言和PLI的2.0版本。 PLI 2.0是全新的过程接口, 与PLI 1.0(TF和ACC库)大不相同。 PLI 2.0最初并不与PLI1.0后向兼容, 因此没有得到Verilog仿真器EDA厂家的广泛使用。 发布了Verilog 2.0
标准以后, OVI把Verilog捐献给了IEEE, 并成为了IEEE标准。
IEEE 1364-1995. 1995年,发布了首个IEEE版本的Verilog标准。 这个版本包括OVI的PLI 1.0和OVI的PLI 2.0的完全重写库。 因此它是后向兼容的。 这个重写的库成了VPI库。
IEEE 1364-2001. 2001年, IEEE发布了Verilog语言的多个改进。 VPI库也得到增强, 能够访问仿真数据结构的新语言特性。 1364-2001Verilog标准表示:保留老的TF和ACC接口仅仅为了文档和后向兼容。 这两个老库没有进行增强以支持新
的语言特性。
Accellera SystemVerilog 3.1. 2003年5月, Accellera(过去的OVI)发布了SystemVerilog 3.1标准。 SystemVerilog并不是新的语言。 它是IEEE 1364-2001 Verilog语言的扩展。 SystemVerilog标准包括直接编程接口(DPI)。
Accellera SystemVerilog 3.1a (规划). 该版本将修订完成SystemVerilog并捐献给IEEE 1364 Verilog标准。 目标捐献日期是2004年6月。 SystemVerilog 3.1a将包括Verilog VPI库的扩展, 以支持SystemVerilog相对于Verilog的扩展。
IEEE 1364-2005/2006 (规划). IEEE 将在2005年或者2006年发布下一版本的Verilog标准。 预计这个版本的IEEE标准将会包括SystemVerilog扩展, 还有当前正在由1364工作小组定义中的若干改进。
IEEE 1364工作小组正在考虑从下一个1364 Verilog标准中删除TF和ACC库(PLI 1.0)。 这些库被认为已经过时了。 它们不支持Verilog-2001增强特性, 也不会支持SystemVerilog和下一个IEEE Verilog标准的其他增强特性。

4.0 审视SystemVerilog DPI

如本文开头所述, DPI的关键特性在于它使得Verilog代码可以直接调用C函数, 无需编写和定义系统任务或函数名称并关联到calltf子程序。 使用DPI, 数值能够直接传递给C函数, 并直接接收返回值。 这种直接的调用和直接的与C函数传递数值并不需要使用任何的过程接口库函数。 这一点使得DPI的使用比PLI的TF、 ACC以及VPI接口更直接了当。
DPI标准源自两个专有接口, 一个来自Synopsys公司的VCS DirectC接口, 另一个是来自Co-Design公司( 已被Synopsys公司收购) 的SystemSim Cblend接口。 这 两 个 专 有 接 口 起 初 是 为 他 们 各 自 的 仿 真 器 专 门 开 发 的 , 而 不是 一 个 能 够 工 作 在 任 何 仿 真 器 上 的 标 准 。 Accellera组织的SystemVerilog标准委员会把这两个捐献技术合并在一起, 并定义了DPI接口的语义, 使得DPI能够与 任 何 V e r i l o g 仿 真 器 一 起 工 作 。

4.1 DPI 导入声明

DPI的导入声明定义了C函数名称, 参数以及函数返回类型的原型。 C函数既可以作为Verilog任务, 也可以作为Verilog函数导入。 Verilog的任务可以有输入和输出参数, 但是不能返回数值。 在Verilog中, 任务被称为Verilog过程代码的编程描述。 函数与任务的不同点在于它可以有返回值, 还有输入和输出参数。 函数可以在Verilog代码中表达式需要出现的任何地方被调用。
两个导入声明样例如下:

import "DPI" function real sin(real in); // sine function in C math library import "DPI" task
file_write(string data); // user-supplied C function

DPI导入声明还能使用一个别称来表示C函数名。 这个别称在C函数名和Verilog代码中的其他名称冲突的时候很有用。 举例来说, 上述这个 sin C函数在Verilog中可以用sine_func这个名称来称呼:

import "DPI" sine_func = function real sin(real in);

DPI导入声明可以放在Verilog自带函数可以定义的任何地方。 包括模块, 接口, 程序块, 时钟块, 包以及编译单元空间。 导入的任务或者函数名称只在导入的作用域内起作用。 导入同一个C函数到不同的作用域是合法的, 比如多个模块, 只要同一个C函数名称的各个DPI导入声明使用完全一样的原型。 如 果 一 个 导 入 的C 函 数 被 导 入 到 多 个 作 用域 , 在 SystemVerilog包里面定 义 导入 声 明 是个 好 的 编程 风 格 。 这 个 包可 以 用 于任 意 数量 的 模 块, 接口和时钟块 , 从而无需复制DPI导入声明 。

4.2 函数形参

被导入的C函数可以有一系列的形参, 也可以没有。 缺省情况下, 每个形参被假定是C函数的输入。 DPI导入声明可以重载这些缺省值, 显式声明每个形参是作为 inputoutput 还是双向 inout参数。 在以下例子中, 平方根函数定义了两个参数: 一个双精度输入, 一个表示错误标志的一比特输出。

import "DPI" function real sqrt(input real base, output bit error);

从Verilog代码的角度看, 输入和输出形参看起来和定义在Verilog语言里面的任务或函数一模一样。 就是说, 被调用的时候, 好像输入参数值是被拷贝到任务或者函数似的, 输出的参数值直接被拷贝出来一样, 输入出的参数同时被拷入和
拷出。 并不是说Verilog仿真器真正实现了这种DPI拷入和拷出的行为。 只不过说在Verilog代码里面看起来的效果如此。 真正的实现可能使用指针或者其他手段, 用以优化仿真性能。
声明作为输入的形参只能接受传递给C函数的输入数值。 C函数不能修改参数值。 这一点可以通过把C函数的输入参数声明成 const 变量来加以保证。

4.3 函数返回值

导入到Verilog作为函数的C函数可以有任意的返回值类型, 只要对C语言来说是合法的。 比如 char, int, short,float, double, void 或 者 指 针 。 SystemVerilog 扩展了Verilog。 增加了一个 void 数据类型和一个特殊的chandle 数据类型用于导入返回一个指针数据类型的C函数。 C指针可以保存在chandle 变量内, 作为一个函数的参数传递回其他导入的C函数。

4.4 数据类型限制

在Verilog一侧, DPI限制传递给C函数的形参的数据类型, 以及导入函数的返回类型的使用。 合法的数据类型为: void,logic, bit, byte, shortint, int, longint, real, shortreal, chandle, string. 这些数据类型也可以作为函数的返回类型。
reg, logicbit数据类型的Verilog向量也可以传递进,传递出导入的C函数。 这些向量在C中的表示方法比较复杂, 不在本文讨论之列。 总而言之, 在C中表示简单Verilog向量的一种方法是作为整数的阵列, 其中每个整数表示Verilog向量的32比特。 Verilog reg和 logic 数据类型的四态逻辑编码成C语言中的整数对。 把Verilog向量映射到整数的阵列使得更难以在C中处理Verilog向量。
Verilog结构和阵列也能作为导入的C函数的输入输出。 当使用结构和阵列的时候, DPI有若干限制。 这些限制在SystemVerilog语言参考手册中有详细描述。 使用兼容C的数据类型的未压缩Verilog结构和阵列很容易作为C的输入输出。 他
们由C语言中对等的结构和阵列来表示。 更复杂的Verilog结构和阵列更难于使用DPI接口来处理, 因为在C语言中没有对等的该数据的表示方法。
注意! DPI把匹配C和Verilog中的相对应的数据类型的负担压在用户的肩头(在导入语句中)。 举例来说C函数中的into应该在DPI的导入原型中声明为int。 C函数中的double应该在DPI的导入语句中声明为real等等。 DPI没有提供机制, 让
C函数测试Verilog一侧是什么类型的数值。 C函数只是简单地读和写它的参数, 不清楚它是被Verilog语言调用的。 如果Verilog原型不匹配真正的C函数, C函数可能读写错误的数值。
这种严苛的、 甚至有点不可原谅的声明要求显然不同与Verilog PLI接口。 PLI提供一种机制, 让C函数测试系统任务/函数的参数的数据类型。 PLI应用能够根据Verilog一侧的数据类型调整如何读写数据。 此外, 读写数值的PLI函数还执行
自动的数据类型转换。

4.5 pure, context与generic C 函数

DPI把 C函数分成 pure函数, context函数或者 generic函数。
Pure C函数. 作为pure函数, 函数的结果必须仅仅依赖于通过形参传递进来的数值。 Pure函数的优点在于仿真器可以执行优化以改进仿真性能。 Pure函数不能使用全局或者静态变量, 不能执行文件I/O操作, 不能访问操作系统环境变量,不能调用来自Verilog PLI库的函数。 只有没有输出或者inout的非void函数可以被指定成pure。 Pure函数不能作为Verilog任务导入。 下面的例子声明了一个导入的C函数为pure函数:

import "DPI" pure function real sin(real in); // function in C math library

Context C函数. context C函数明白函数声明所在工作域的Verilog的层次。 这使得被导入的C函数能够调用来自PLI TF,ACC或者VPI库的函数, 从而DPI函数可以充分利用PLI的优势特性, 比如写仿真器的log文件以及Verilog源代码打开的文
件。 context任务声明的样例如下:

import "DPI" context task print(input int file_id, input bit [127:0] data);

Generic C函数. 本文把那些既没有明确声明为pure, 也没有声明为context的函数称为generic函数(SystemVerilog标准没有给除了pure或context之外的函数特定的称呼)。 generic C函数可以作为Verilog函数或者Verilog任务导入。 任务或者
函数可以由输入、 输出以及inout的参数。 函数可以有一个返回值, 或者声明为void。 generic C函数不允许调用VerilogPLI函数, 不能访问除了参数以外的任何数据, 只能修改这些参数。
注意! 正确的声明导入的函数为pure还是context是用户的责任。 缺省情况下, DPI函数假定是generic函数。 调用一个不正确声明成pure的C函数可能返回不正确或者不一致的结果, 导致不可预测的运行时间错误, 甚至于让仿真崩溃。同样, 如果一个C函数访问Verilog PLI库或者其他API库, 却没有声明为context函数, 会导致不可预见的仿真结果甚至仿真崩溃。

4.6 从DPI函数引用PLI库

作为context函数或者context任务导入的C函数允许调用Verilog TF、 ACC和VPI PLI库函数。 这至少具有两个重要的好处: 首先通过使用PLI库, DPI函数能够访问仿真数据结构的信息, 尽管没有传递给C函数的参数。 举例, DPI函数可以写
仿真器的log文件。 第二点, DPI导入方法比起复杂的PLI定义系统任务/函数名称的方法简单多了。 使用DPI, DPI应用程序就好像Verilog函数一样被导入, 不用复杂地定义系统任务/函数名称, 最后还要把系统任务/函数名称跟Verilogh仿真器绑定。
限制. 调用PLI库的子程序有一个重要的限制。 每个PLI用户定义系统任务或者函数的实体拥有一个独一无二的ID(称为实体句柄)。 假如两个不同的模块使用同一个用户定义的系统任务, 在仿真数据结构内两个应用采用独一无二的ID标
识。 PLI库中的大量子程序依赖这种专门特性。 调用DPI函数没有独特的实体句柄。 如果同一个DPI函数被Verilog代码中的两个不同的地方调用, 函数不能区分这两个调用。DPI与PLI的另一个不同点在于两个接口指定任务或者函数的作用域的不同。 context DPI应用程序使用的是任务或者函数DPI导入语句声明所在的作用域; 而不是任务或者函数调用所在的作用域。 这是符合Verilog语言规范的, Verilog任务或者函数的作用域就是任务或者函数被定义的所在。 但是PLI的上下文作用域, 是系统任务或系统函数被调用的地方。 PLI库要求上下文的作用域就是调用的作用域, 而非DPI导入声明的作用域。 这 一 点 限 制 了 DPI应用程序只能调用TF、 ACC和VPIPLI库中的某些函数。

5.0 导出Verilog任务和函数

除了可以从C导入函数, DPI还允许把Verilog任务和函数导出到C( 也可以是其他编程语言)。 这样C语言还可以执行Verilog编写的代码。 通过组合导入C函数和导出Verilog任务和函数, Verilog和C之间很容易传递数据, 让哪一边来修
改完全取决于方便性和设计需要。导出声明与DPI导入声明类似, 只要指明Verilog任务或者函数的名称即可。 Verilog任务或者函数的形参无需列出。 举
例来说:

export "DPI" adder_function;

也可以给C语言中的任务或者函数另外取名, 比如:

export "DPI" adder = adder_function; // called "adder" within C

Verilog任务或函数只能从该任务或者函数被定义的作用域中被导出, 并且一个任务或者 函数只能有一个DPI导出声明。 导出的任务或者函数的形式参数必须符合DPI导入声明中同样的数据类型规则。在Verilog中, 任务可以调用其他函数或者任务, 但是函数只能调用其他函数。 这种限制对于导出的任务或函数同样存在。
导出的Verilog函数只能被 那些已经作为context函数或者context任务导入的C函数所调用。 导出的Verilog任务只能被作为context任务导入的C函数调用。
导出的函数和导出的任务拥有一个对于DPI来说是独一无二的返回值。 导出任务或者函数的返回值是一个int值, 表示当前的任务执行中disable语句是不是活动的。
导入C函数和导出Verilog任务与函数的功能使得一个设计的一部分可以用Verilog描述, 另一部分用C语言(可能作为SystemC的模型) 来描述。 举例来说, 设计流程可以从一个很高抽象层级的C模型开始。 随着设计流程的推进, 这些C模
型的一部分使用RTL层级的Verilog替换。 通过导入描述那些模型的C函数, 这些RTL模型能够继续同抽象的C模型交互。
反过来, 描述更具体的RTL层级, 如果导出Verilog函数, 也可以被抽象的C模型调用。 整个设计能够保持完整无缺, 即便设计的一部分由一种语言描述转换成另外一种语言描述。
导出Verilog任务的一个重要好处是任务可以通过使用非阻塞赋值、 事件控制、 延时与等待语句来耗费仿真时间。 这提供了一条途径, 基于DPI的C函数可以把仿真行动跟仿真时序同步。 当C函数调用一个耗时的导出Verilog任务的时候, C函数的
执行将会中止, 直到Verilog任务执行完成并返回调用它的C函数为止。C函数调用Verilog任务和函数的功能是DPI所独有的一个强大功能。 Verilog PLI标准没有类似的导出任务和函数的功
能。

6.0 DPI与C++,SystemC以及其他语言的接口

SystemVerilog标准只能为C语言定义DPI接口。 但是DPI设计了一个可扩展的接口, 还能支持其他的语言。 DPI标准定义了两个层: SystemVerilog层和其他语言层。 SystemVerilog层包含DPI导入和导出声明, 还有在Verilog中调用导入的外
部函数的规则。 不管其他语言是什么, 这一层看起来是一样的。 其他语言层在SystemVerilog 3.1a标准中是为C定义的。 为其他语言层的定义可能在SystemVerilog或者IEEE 1364 Verilog标准的将来版本中实现。 用户也可以定义专有的其他语言层。 既然SystemVerilog层和其他语言层是相互独立的, 导 入 函 数 采 用 什 么 语 言 定 义 对于Verilog来说是透明的。
使用DPI与C++、 SystemC接口. DPI是设计用来作为Verilog代码和C编程语言之间的接口的。 导入的任务和函数可以是用C++编写的, 只要遵循C链接规范得到满足。 SystemC是基于C++的。 只要遵循安全编码规范和安全的调用方法, 在Verilog中可以导入SystemC函数。

7.0 SystemVerilog DPI会替代Verilog PLI吗?

搞明白DPI是不是Verilog PLI的超集, 是不是能够完全替代PLI相当重要。 与TF、 ACC和VPI这三代Verilog PLI相比, DPI有三个重要的特性。
• DPI消除了PLI使用的复杂性, PLI必须定义系统任务或者函数名称, 并把calltf C函数和该系统任务/函数的名称关联在一起。 使用DPI, C函数的名称可以直接在Verilog源代码中调用。
• DPI提供一种机制, C函数可以调用Verilog写的任务和函数。 这在Verilog PLI种没有对等的功能。
• DPI导 入 声明包 含 导入函数参数的原型。 仿真器的编译器能够使用这个原型, 对每个调用导入函数的参数执行强劲的类型检查。 PLI并不定义系统任务和函数的原型, 但是提供一个机制 , 程序员可以添加自定义语法检查程序, 为每个系
统任务或者函数的实体所调用执行。从某种程度上说, DPI接口具有与PLI TF库相同的功能。 TF库和DPI接口都允许Verilog代码调用所有C函数, 传递数值给哪些C函数, 并接受这些C函数的返回值。 使用这两个接口, 回收的值可以作为函数的返回值写入形参; 两个接口都允许C函数作为Verilog代码中的一个函数或者任务被调用。 但是DPI不是TF接口的超集。 TF接口的某些功能是DPI不能实现的, 比如同步仿真时间和事件功能。 ACC和VPI库也有一些功能是DPI不能实现的。SystemVerilog DPI和Verilog PLI功能的主要不同点如下所示:
• DPI任务或函数能够设置的形参的类型限制为那些能够 拥有逻辑值的基本的Verilog和SystemVerilog变量数据类型。 相反, Verilog PLI系统任务/函数的参数不仅包括Verilog和SystemVerilog变量, 还有线网数据类型、 模块实体名称、 全层次域名称、 命名事件、 常数、 合并串、 以及null(空)参数。
• 通过DPI导入的任务和函数必须拥有固定数目的参数, 并在DPI导入原型中指明。 调用DPI任务或者函数时候必须传递原型指定的固定数目的参数。 PLI系统任务和函数可以拥有可变数目的参数。 举例来说, 我们可以编写一个系统任务把参数中列出的变量的当前值写入一个文件。 同样的 系统任务定义可以用一个参数、 两个参数或者10个参数来调用。 使用DPI,可能需要为不同参数数目写不同的C函数和导入声明。
• DPI应用程序不能安排在未来的仿真时间把值写入仿真。 Verilog PLI应用程序能够安排在未来任意时间发生的值改变。
• DPI应用程序不能与仿真事件队列中的其他事件同步, 在指定的仿真时间, 在任何其他的时间发生之前, PLI应用程序能够同步执行, 在非阻塞赋值执行前后的特定仿真时刻, 或者在所有的事件结束时的特定仿真时间。
• DPI应用程序不能与比如一些变量或者线网的值改变之类的事件同步。 当进入交互调试模式的时候, PLI应用程序能够与任何数据类型的值改变, 仿真启动, 仿真结束等事件同步。
• DPI应用程序不能重载仿真事件。 PLI应用程序可以把任何变量或者线网强制成任何数值, 而不管所有其他的针对那个对象的仿真行为。 PLI应用程序还能取消尚未发生的已调度事件。
• DPI应用程序不能直接分析仿真数据结构。 通过调用PLI库函数来访问仿真数据结构, DPI应用程序可以间接执行部分分析。 但是这种间接的访问跟PLI的访问相比较还是有很多限制的。
下列表格是DPI, TF, ACC和VPI接口的主要特性比较:
在这里插入图片描述

8.0 结语

8.1 何时使用DPI

SystemVerilog DPI开辟了一条新的把Verilog代码和C代码集成在一起的途经。 使用DPI的导入声明, 一个C函数看起来就好像Verilog自带的函数一样。 一经导入, 就像Verilog自带函数一样在任何地方都能被调用。 Verilog逻辑数值可以作为输入直接输入到C函数, C函数返回或者输出参数可以直接传递回Verilog。 使用DPI, 用户无需再像Verilog PLI那样, 事先编写系统任务/函数名称, 然后通过复杂的PLI库间接传递数值给/回C函数。
DPI接口的主要好处在于, 在Verilog代码里面, 任务或者函数是在调用本征的Verilog任务或函数, 还是调用其他语言写的代码是完全透明的。 这一特点使得很容易与Verilog自带的代码或者C代码互换任务或者函数的定义。 举例来说, 设
计的各个部分可以分别使用Verilog和SystemC。 使用DPI, 切换仿真的不同语言设计显得非常简单。
SystemVerilog DPI简单和直接的特性使其成为最理想的调用来自C标准库的函数的方法, 比如C数学运算库或者用户自定义库。 DPI提供一种直截了当的机制, 可以定义一个大型设计, 部分采用抽象的C(SystemC) 模型, 部分采用更具体
的Verilog行为级、 RTL级或者门级模型。
当C函数操作的数据需要通过函数参数和返回值直接跟Verilog交互的时候, DPI是个理想的接口。 然而, DPI并不能直接访问仿真数据就构的内部。 这一点限制了DPI的应用。 需要指出的是, 尽管SystemVerilog DPI的这些限制的大部分能够
通过混合使用DPI和Verilog PLI加以解决。 基于DPI的应用程序, 假如作为context任务或者Context函数导入, 就能够调用来自Verilog PLI库的函数。 采用这种方法, 用户可以在获得DPI导入机制的简洁性的同时, 还能够访问部分仿真数
据结构。

8.2 何时使用PLI

DPI不是Verilog PLI的替代品。 PLI能够全面访问内部仿真数据结构, 而基于DPI的应用即使调用来自PLI库的函数, 也不能访问全部仿真数据结构。 PLI库还具有一种机制, Verilog PLI应用可以用多种途径同步仿真行为。 基于DPI的应用程序不能直接与仿真行为同步。 Verilog PLI的这种间接特质, 加上一套特殊的库, 为用户开发的C程序和仿真数据结构之间建立了一个保护层。
对于波形观察器, 图形化调试工具以及其他需要访问和分析仿真数据结构的工具软件而言, Verilog PLI依旧是最好的, 唯一的过程接口。 还有, 某些应用还必须需要PLI的, 比如那些需要与Verilog仿真器同步事件安排的协 同 仿 真环 境 。

8.3 PLI 1.0标准可以退休了吗?

历史悠久的Verilog PLI已经19岁了。 它已历经三代PLI库, 置于多个标准之下。 最古老的PLI库就是TF和ACC库, 也叫PLI 1.0标准。 IEEE 1364 Verilog标准认为这些库已经过时, 然而IEEE还是保留了这些老库以备文档和后向兼容之需。 这些库的所有函数功能已经被更强大的VPI库替代, 也就是常说的PLI 2.0标准。 下一代的IEEE 1364标准可能将删除这些太老的TF和ACC库。
TF接口比ACC和VPI接口多一个优点。 仅仅使用TF函数库的PLI应用只能访问那些作为系统任务/函数变元输入传递进来的Verilog数据结构信息。 这种对仿真数据结构有限制的访问意味着仿真器可以在编译的时候决定PLI应用将访问哪些数据结构。 这样仿真器能够更好地优化仿真数据结构, 提高仿真器的性能。DPI的pure函数同样只能有限访问。 它只能读取被作为函数输入传递进来的Verilog值。 因此, 使用TF接口的主要优点现在也能通过DPI接口实现。

8.4 正确的歌谣

我们不能说“Verilog PLI已死, SystemVerilog DPI将立。 ” , 尽管DPI将在Verilog设计的某些方面发挥重要作用,Verilog PLI还是在某些方面有独特的作用, 特别是PLI的VPI部分。
正确的歌谣应该是“ PLI 1.0已死, PLI VPI和SystemVerilog DPI将立!

  • 8
    点赞
  • 48
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值