Support user-provided DPI-C function declarations
Signed-off-by: Jakub Michalski <jmichalski@antmicro.com>
This commit is contained in:
parent
a5f4d40901
commit
babb4a071f
|
|
@ -294,6 +294,25 @@ or "`ifdef`"'s may break other tools.
|
|||
(if appropriate :vlopt:`--coverage` flags are passed) after being
|
||||
disabled earlier with :option:`/*verilator&32;coverage_off*/`.
|
||||
|
||||
.. option:: /*verilator&32;dpi_c_decl "<C function declaration>"*/
|
||||
|
||||
Specifies C function declaration that will be emitted for given DPI-C
|
||||
function into the Verilator-generated __Dpi.h header, replacing the declaration
|
||||
that Verilator would build by default using the Verilog function signature.
|
||||
|
||||
This enables use of C functions with types not specified in the
|
||||
standard. For example, it enables use of functions that return ``char*``:
|
||||
|
||||
.. code-block:: sv
|
||||
|
||||
module t;
|
||||
import "DPI-C" function string getenv(input string arg) /*verilator dpi_c_decl "char* getenv(const char*)"*/;
|
||||
|
||||
initial begin
|
||||
$display("%s", getenv("HOME"));
|
||||
end
|
||||
endmodule
|
||||
|
||||
.. option:: /*verilator&32;fargs <arguments>*/
|
||||
|
||||
For Verilator developers only. When a source file containing these `fargs`
|
||||
|
|
|
|||
|
|
@ -94,6 +94,7 @@ class AstNodeFTask VL_NOT_FINAL : public AstNode {
|
|||
// @astgen op4 := scopeNamep : Optional[AstScopeName]
|
||||
string m_name; // Name of task
|
||||
string m_cname; // Name of task if DPI import
|
||||
string m_dpiCDecl; // Custom DPI-C function declaration
|
||||
string m_ifacePortName; // Interface port name for out-of-block definition (IEEE 25.8)
|
||||
uint64_t m_dpiOpenParent = 0; // DPI import open array, if !=0, how many callees
|
||||
bool m_taskPublic : 1; // Public task
|
||||
|
|
@ -199,6 +200,9 @@ public:
|
|||
void dpiOpenChild(bool flag) { m_dpiOpenChild = flag; }
|
||||
bool dpiTask() const { return m_dpiTask; }
|
||||
void dpiTask(bool flag) { m_dpiTask = flag; }
|
||||
bool dpiCDeclOverride() const { return !m_dpiCDecl.empty(); }
|
||||
const string& dpiCDecl() const { return m_dpiCDecl; }
|
||||
void dpiCDecl(const string& cDecl) { m_dpiCDecl = cDecl; }
|
||||
bool isConstructor() const { return m_isConstructor; }
|
||||
void isConstructor(bool flag) { m_isConstructor = flag; }
|
||||
bool isHideLocal() const { return m_isHideLocal; }
|
||||
|
|
@ -511,6 +515,7 @@ class AstCFunc final : public AstNode {
|
|||
string m_rtnType; // void, bool, or other return type
|
||||
string m_argTypes; // Argument types
|
||||
string m_ifdef; // #ifdef symbol around this function
|
||||
string m_cDecl; // Custom DPI-C function declaration
|
||||
VBoolOrUnknown m_isConst; // Function is declared const (*this not changed)
|
||||
bool m_isStatic : 1; // Function is static (no need for a 'this' pointer)
|
||||
bool m_isTrace : 1; // Function is related to tracing
|
||||
|
|
@ -640,6 +645,9 @@ public:
|
|||
void dpiImportPrototype(bool flag) { m_dpiImportPrototype = flag; }
|
||||
bool dpiImportWrapper() const { return m_dpiImportWrapper; }
|
||||
void dpiImportWrapper(bool flag) { m_dpiImportWrapper = flag; }
|
||||
bool dpiCDeclOverride() const { return !m_cDecl.empty(); }
|
||||
const string& dpiCDecl() const { return m_cDecl; }
|
||||
void dpiCDecl(const string& cDecl) { m_cDecl = cDecl; }
|
||||
bool isCoroutine() const { return m_rtnType == "VlCoroutine"; }
|
||||
void recursive(bool flag) { m_recursive = flag; }
|
||||
bool recursive() const { return m_recursive; }
|
||||
|
|
|
|||
|
|
@ -137,6 +137,10 @@ void EmitCBaseVisitorConst::emitCDefaultConstructor(const AstNodeModule* const m
|
|||
void EmitCBaseVisitorConst::emitCFuncHeader(const AstCFunc* funcp, const AstNodeModule* modp,
|
||||
bool withScope) {
|
||||
if (funcp->slow()) putns(funcp, "VL_ATTR_COLD ");
|
||||
if (funcp->dpiCDeclOverride()) {
|
||||
putns(funcp, funcp->dpiCDecl() + ";\n");
|
||||
return;
|
||||
}
|
||||
if (!funcp->isDestructor()) {
|
||||
putns(funcp, funcp->rtnTypeVoid());
|
||||
puts(" ");
|
||||
|
|
|
|||
|
|
@ -1434,8 +1434,12 @@ void EmitCSyms::emitDpiHdr() {
|
|||
if (!firstImp++) puts("\n// DPI IMPORTS\n");
|
||||
putsDecoration(nodep, "// DPI import" + sourceLoc + "\n");
|
||||
}
|
||||
putns(nodep, "extern " + nodep->rtnTypeVoid() + " " + nodep->nameProtect() + "("
|
||||
+ cFuncArgs(nodep) + ");\n");
|
||||
if (nodep->dpiCDeclOverride()) {
|
||||
putns(nodep, "extern " + nodep->dpiCDecl() + ";\n");
|
||||
} else {
|
||||
putns(nodep, "extern " + nodep->rtnTypeVoid() + " " + nodep->nameProtect() + "("
|
||||
+ cFuncArgs(nodep) + ");\n");
|
||||
}
|
||||
}
|
||||
|
||||
puts("\n");
|
||||
|
|
|
|||
|
|
@ -545,6 +545,19 @@ void V3PreProcImp::comment(const string& text) {
|
|||
if (arg.size() && baseCmd == "public_flat_rw_on")
|
||||
baseCmd += "_sns"; // different cmd for applying sensitivity
|
||||
if (!printed) insertUnreadback("/*verilator " + baseCmd + "*/ " + arg + " /**/");
|
||||
} else if (VString::startsWith(cmd, "dpi_c_decl")) {
|
||||
// "/*verilator dpi_c_decl foo(bar) */" -> "/*verilator dpi_c_decl*/ \"foo(bar)\""
|
||||
string baseCmd = cmd.substr(0, std::strlen("dpi_c_decl"));
|
||||
string::size_type startOfArg = baseCmd.size();
|
||||
while (std::isspace(cmd[startOfArg])) startOfArg++;
|
||||
string arg = cmd.substr(startOfArg);
|
||||
if (arg.empty()) {
|
||||
fileline()->v3warn(
|
||||
BADVLTPRAGMA,
|
||||
"No function declaration provided in /*verilator dpi_c_decl*/ meta-comment");
|
||||
return;
|
||||
}
|
||||
if (!printed) insertUnreadback("/*verilator " + baseCmd + "*/ \"" + arg + "\" /**/");
|
||||
} else {
|
||||
if (!printed) insertUnreadback("/*verilator " + cmd + "*/");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1060,6 +1060,7 @@ class TaskVisitor final : public VNVisitor {
|
|||
|
||||
// Add DPI Import to top, since it's a global function
|
||||
m_topScopep->scopep()->addBlocksp(funcp);
|
||||
funcp->dpiCDecl(nodep->dpiCDecl());
|
||||
if (!makePortList(nodep, funcp)) return nullptr;
|
||||
return funcp;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -825,6 +825,7 @@ vnum {vnum1}|{vnum2}|{vnum3}|{vnum4}|{vnum5}
|
|||
"/*verilator coverage_block_off*/" { FL; return yVL_COVERAGE_BLOCK_OFF; }
|
||||
"/*verilator coverage_off*/" { FL_FWD; PARSEP->lexFileline()->coverageOn(false); FL_BRK; }
|
||||
"/*verilator coverage_on*/" { FL_FWD; PARSEP->lexFileline()->coverageOn(true); FL_BRK; }
|
||||
"/*verilator dpi_c_decl*/" { FL; return yVL_DPI_C_DECL; } // The "foo(bar)" is converted by the preproc
|
||||
"/*verilator forceable*/" { FL; return yVL_FORCEABLE; }
|
||||
"/*verilator full_case*/" { FL; return yVL_FULL_CASE; }
|
||||
"/*verilator hier_block*/" { FL; return yVL_HIER_BLOCK; }
|
||||
|
|
|
|||
|
|
@ -780,6 +780,7 @@ BISONPRE_VERSION(3.7,%define api.header.include {"V3ParseBison.h"})
|
|||
%token<fl> yVL_CLOCKER "/*verilator clocker*/"
|
||||
%token<fl> yVL_CLOCK_ENABLE "/*verilator clock_enable*/"
|
||||
%token<fl> yVL_COVERAGE_BLOCK_OFF "/*verilator coverage_block_off*/"
|
||||
%token<fl> yVL_DPI_C_DECL "/*verilator dpi_c_decl*/"
|
||||
%token<fl> yVL_FORCEABLE "/*verilator forceable*/"
|
||||
%token<fl> yVL_FULL_CASE "/*verilator full_case*/"
|
||||
%token<fl> yVL_HIER_BLOCK "/*verilator hier_block*/"
|
||||
|
|
@ -4928,6 +4929,14 @@ dpi_import_export<nodep>: // ==IEEE: dpi_import_export
|
|||
$5->dpiPure($3 == iprop_PURE);
|
||||
$5->dpiImport(true);
|
||||
GRAMMARP->checkDpiVer($1, *$2); v3Global.dpi(true); }
|
||||
| yIMPORT yaSTRING dpi_tf_import_propertyE dpi_importLabelE function_prototype yVL_DPI_C_DECL yaSTRING ';'
|
||||
{ $$ = $5;
|
||||
if (*$4 != "") $5->cname(*$4);
|
||||
$5->dpiContext($3 == iprop_CONTEXT);
|
||||
$5->dpiPure($3 == iprop_PURE);
|
||||
$5->dpiImport(true);
|
||||
if (*$7 != "") $5->dpiCDecl(*$7);
|
||||
GRAMMARP->checkDpiVer($1, *$2); v3Global.dpi(true); }
|
||||
| yIMPORT yaSTRING dpi_tf_import_propertyE dpi_importLabelE task_prototype ';'
|
||||
{ $$ = $5;
|
||||
if (*$4 != "") $5->cname(*$4);
|
||||
|
|
|
|||
|
|
@ -0,0 +1,22 @@
|
|||
// -*- mode: C++; c-file-style: "cc-mode" -*-
|
||||
//*************************************************************************
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify it
|
||||
// under the terms of either the GNU Lesser General Public License Version 3
|
||||
// or the Perl Artistic License Version 2.0.
|
||||
// SPDX-FileCopyrightText: 2026 Antmicro
|
||||
// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||
//
|
||||
//*************************************************************************
|
||||
#include "Vt_dpi_decl__Dpi.h"
|
||||
|
||||
char* func(const char* arg) {
|
||||
static char str[] = "abc";
|
||||
return str;
|
||||
}
|
||||
// some functions (e.g. getenv() from glibc) may have additional specifier, lack of it in the
|
||||
// declaration will cause build errors
|
||||
char* func_with_specifier(const char* arg) throw() {
|
||||
static char str[] = "efd";
|
||||
return str;
|
||||
}
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
#!/usr/bin/env python3
|
||||
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify it
|
||||
# under the terms of either the GNU Lesser General Public License Version 3
|
||||
# or the Perl Artistic License Version 2.0.
|
||||
# SPDX-FileCopyrightText: 2026 Wilson Snyder
|
||||
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||
|
||||
import vltest_bootstrap
|
||||
|
||||
test.scenarios('simulator')
|
||||
test.pli_filename = "t/t_dpi_decl.cpp"
|
||||
|
||||
test.compile(v_flags2=[test.pli_filename])
|
||||
|
||||
test.execute()
|
||||
|
||||
test.passes()
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
// DESCRIPTION: Verilator: Verilog Test module
|
||||
//
|
||||
// This file ONLY is placed under the Creative Commons Public Domain.
|
||||
// SPDX-FileCopyrightText: 2026 Antmicro
|
||||
// SPDX-License-Identifier: CC0-1.0
|
||||
|
||||
`define stop $stop
|
||||
`define checks(gotv,expv) do if ((gotv) != (expv)) begin $write("%%Error: %s:%0d: got='%s' exp='%s'\n", `__FILE__,`__LINE__, (gotv), (expv)); `stop; end while(0);
|
||||
|
||||
module t;
|
||||
import "DPI-C" function string func(input string arg) /*verilator dpi_c_decl "char* func(const char*)"*/;
|
||||
import "DPI-C" function string func_with_specifier(input string arg) /*verilator dpi_c_decl "char* func_with_specifier(const char*) throw()"*/;
|
||||
|
||||
initial begin
|
||||
`checks(func("arg"), "abc");
|
||||
`checks(func_with_specifier("arg"), "efd");
|
||||
$write("*-* All Finished *-*\n");
|
||||
$finish;
|
||||
end
|
||||
endmodule
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
%Error-BADVLTPRAGMA: t/t_dpi_decl_bad.v:8:57: No function declaration provided in /*verilator dpi_c_decl*/ meta-comment
|
||||
8 | import "DPI-C" function string func(input string arg) /*verilator dpi_c_decl*/;
|
||||
| ^~~~~~~~~~~~~~~~~~~~~~~~
|
||||
... For error description see https://verilator.org/warn/BADVLTPRAGMA?v=latest
|
||||
%Error: Exiting due to
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
#!/usr/bin/env python3
|
||||
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify it
|
||||
# under the terms of either the GNU Lesser General Public License Version 3
|
||||
# or the Perl Artistic License Version 2.0.
|
||||
# SPDX-FileCopyrightText: 2026 Wilson Snyder
|
||||
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||
|
||||
import vltest_bootstrap
|
||||
|
||||
test.scenarios('simulator')
|
||||
|
||||
test.lint(fails=True, expect_filename=test.golden_filename)
|
||||
|
||||
test.passes()
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
// DESCRIPTION: Verilator: Verilog Test module
|
||||
//
|
||||
// This file ONLY is placed under the Creative Commons Public Domain.
|
||||
// SPDX-FileCopyrightText: 2026 Antmicro
|
||||
// SPDX-License-Identifier: CC0-1.0
|
||||
|
||||
module t;
|
||||
import "DPI-C" function string func(input string arg) /*verilator dpi_c_decl*/;
|
||||
|
||||
initial begin
|
||||
$write("*-* All Finished *-*\n");
|
||||
$finish;
|
||||
end
|
||||
endmodule
|
||||
Loading…
Reference in New Issue