From a40fae04ce2fbcbb44353e25040dfb99531ed08c Mon Sep 17 00:00:00 2001 From: Wilson Snyder Date: Thu, 3 Dec 2009 06:55:29 -0500 Subject: [PATCH] Support direct programming interface (DPI) "import". --- Changes | 2 + Makefile.in | 2 +- bin/verilator | 39 +++- include/svdpi.h | 367 ++++++++++++++++++++++++++++++ include/verilateddpi.cpp | 289 +++++++++++++++++++++++ src/V3Ast.h | 42 +++- src/V3AstNodes.cpp | 83 ++++++- src/V3AstNodes.h | 50 +++- src/V3EmitC.cpp | 104 +++++++-- src/V3EmitC.h | 1 + src/V3EmitMk.cpp | 3 + src/V3EmitV.cpp | 3 +- src/V3Global.h | 6 +- src/V3Life.cpp | 11 + src/V3Link.cpp | 31 ++- src/V3ParseImp.h | 3 + src/V3Signed.cpp | 12 +- src/V3Simulate.h | 8 +- src/V3Task.cpp | 250 ++++++++++++++++---- src/V3Width.cpp | 10 +- src/Verilator.cpp | 1 + src/verilog.l | 6 +- src/verilog.y | 40 +++- test_regress/Makefile_obj | 3 + test_regress/t/t_dpi_dup_bad.pl | 21 ++ test_regress/t/t_dpi_dup_bad.v | 18 ++ test_regress/t/t_dpi_import.pl | 20 ++ test_regress/t/t_dpi_import.v | 144 ++++++++++++ test_regress/t/t_dpi_import_c.cpp | 121 ++++++++++ test_regress/t/t_dpi_logic_bad.pl | 20 ++ test_regress/t/t_dpi_logic_bad.v | 17 ++ test_regress/t/t_dpi_name_bad.pl | 19 ++ test_regress/t/t_dpi_name_bad.v | 17 ++ 33 files changed, 1639 insertions(+), 124 deletions(-) create mode 100644 include/svdpi.h create mode 100644 include/verilateddpi.cpp create mode 100755 test_regress/t/t_dpi_dup_bad.pl create mode 100644 test_regress/t/t_dpi_dup_bad.v create mode 100755 test_regress/t/t_dpi_import.pl create mode 100644 test_regress/t/t_dpi_import.v create mode 100644 test_regress/t/t_dpi_import_c.cpp create mode 100755 test_regress/t/t_dpi_logic_bad.pl create mode 100644 test_regress/t/t_dpi_logic_bad.v create mode 100755 test_regress/t/t_dpi_name_bad.pl create mode 100644 test_regress/t/t_dpi_name_bad.v diff --git a/Changes b/Changes index 1189552ec..03b74454b 100644 --- a/Changes +++ b/Changes @@ -12,6 +12,8 @@ indicates the contributor was also the author of the fix; Thanks! ** Support typedef. [Donal Casey] +** Support direct programming interface (DPI) "import". + *** Support "reg [1:0][1:0][1:0]" and "reg x [3][2]", bug176. [Byron Bradley] *** Support declarations in loop initializers, bug172. [by Byron Bradley] diff --git a/Makefile.in b/Makefile.in index 13479de9d..24aec54e2 100644 --- a/Makefile.in +++ b/Makefile.in @@ -110,7 +110,7 @@ DISTFILES_INC = $(INFOS) .gitignore Artistic COPYING COPYING.LESSER \ MANIFEST.SKIP \ bin/* \ install-sh configure mkinstalldirs *.pod \ - include/verilated*.[chv]* \ + include/*.[chv]* \ include/*.in \ include/.*ignore \ .*attributes */.*attributes */*/.*attributes \ diff --git a/bin/verilator b/bin/verilator index ff180139a..cf638770a 100755 --- a/bin/verilator +++ b/bin/verilator @@ -969,6 +969,7 @@ For -sp mode, instead of .cpp and .h it creates: In certain optimization modes, it also creates: + {prefix}__Dpi.h // DPI import and export declarations {prefix}__Inlines.h // Inline support functions {prefix}__Slow.cpp // Constructors and infrequent routines {prefix}__Syms.cpp // Global symbol table C++ @@ -1121,6 +1122,32 @@ Lower modules are not pure SystemC code. This is a feature, as using the SystemC pin interconnect scheme everywhere would reduce performance by an order of magnitude. +=head1 DPI (DIRECT PROGRAMMING INTERFACE) + +Verilator supports SystemVerilog Direct Programming Interface import +statements. Only the SystemVerilog form ("DPI-C") is supported, not the +original Synopsys-only DPI. + +In the SYSTEMC example above, if you had in our.v: + + import "DPI-C" function integer add (input integer a, input integer b); + + initial begin + $display("%x + %x = %x", 1, 2, add(1,2)); + endtask + +Then after Verilating, Verilator will create a file Vour__Dpi.h with the +prototype to call this function: + + extern int add (int a, int b); + +From the sc_main.cpp file (or another .cpp file passed to the Verilator +command line, or the link), you'd then: + + #include "svdpi.h" + #include "Vour__Dpi.h" + int add (int a, int b) { return a+b; } + =head1 CROSS COMPILATION Verilator supports cross-compiling Verilated code. This is generally used @@ -2459,13 +2486,15 @@ file found in the include directory of the Verilator kit. =item Is the PLI supported? -No. +No, but the DPI is. More specifically, the common PLI-ish calls $display, $finish, $stop, -$time, $write are converted to C++ equivalents. If you want something more -complex, since Verilator emits standard C++ code, you can simply write your -own C++ routines that can access and modify signal values without needing -any PLI interface code, and call it with $c("{any_c++_statement}"). +$time, $write are converted to C++ equivalents. You can also use the +"import DPI" SystemVerilog feature to call C code (see the chapter above). +If you want something more complex, since Verilator emits standard C++ +code, you can simply write your own C++ routines that can access and modify +signal values without needing any PLI interface code, and call it with +$c("{any_c++_statement}"). =item How do I make a Verilog module that contain a C++ object? diff --git a/include/svdpi.h b/include/svdpi.h new file mode 100644 index 000000000..460611a13 --- /dev/null +++ b/include/svdpi.h @@ -0,0 +1,367 @@ +/* -*- C -*- + * + * svdpi.h + * + * SystemVerilog Direct Programming Interface (DPI). + * + * This file contains the constant definitions, structure definitions, + * and routine declarations used by SystemVerilog DPI. + * + * This file is from the SystemVerilog IEEE 1800-2005 Annex G, + * with modifications to support Verilator. + */ + + +#ifndef INCLUDED_SVDPI +#define INCLUDED_SVDPI + +#ifdef __cplusplus +extern "C" { +#endif + +/* Ensure that size-critical types are defined on all OS platforms. */ +#if defined (_MSC_VER) +typedef unsigned __int64 uint64_t; +typedef unsigned __int32 uint32_t; +typedef unsigned __int8 uint8_t; +typedef signed __int64 int64_t; +typedef signed __int32 int32_t; +typedef signed __int8 int8_t; +#elif defined(__MINGW32__) +#include +#elif defined(__linux) +#include +#else +#include +#endif + +/* Use to export a symbol from application */ +#if defined (_MSC_VER) +#define DPI_DLLISPEC __declspec(dllimport) +#else +#define DPI_DLLISPEC +#endif + +/* Use to import a symbol into application */ +#if defined (_MSC_VER) +#define DPI_DLLESPEC __declspec(dllexport) +#else +#define DPI_DLLESPEC + +#endif + +/* Use to mark a function as external */ +#ifndef DPI_EXTERN +#define DPI_EXTERN +#endif + +#ifndef DPI_PROTOTYPES +#define DPI_PROTOTYPES +/* object is defined imported by the application */ +#define XXTERN DPI_EXTERN DPI_DLLISPEC +/* object is exported by the application */ +#define EETERN DPI_EXTERN DPI_DLLESPEC +#endif + +/* canonical representation */ +#define sv_0 0 +#define sv_1 1 +#define sv_z 2 +#define sv_x 3 + +/* common type for 'bit' and 'logic' scalars. */ +typedef uint8_t svScalar; +typedef svScalar svBit; /* scalar */ +typedef svScalar svLogic; /* scalar */ + +/* + * DPI representation of packed arrays. + * 2-state and 4-state vectors, exactly the same as PLI's avalue/bvalue. + */ +#ifndef VPI_VECVAL +#define VPI_VECVAL +typedef struct vpi_vecval { + uint32_t a; + uint32_t b; +} s_vpi_vecval, *p_vpi_vecval; +#endif + +/* (a chunk of) packed logic array */ +typedef s_vpi_vecval svLogicVecVal; + +/* (a chunk of) packed bit array */ +typedef uint32_t svBitVecVal; + +/* Number of chunks required to represent the given width packed array */ +#define SV_PACKED_DATA_NELEMS(WIDTH) (((WIDTH) + 31) >> 5) + +/* + * Because the contents of the unused bits is undetermined, + * the following macros can be handy. + */ +#define SV_MASK(N) (~(-1 << (N))) + +#define SV_GET_UNSIGNED_BITS(VALUE, N) \ + ((N) == 32 ? (VALUE) : ((VALUE) & SV_MASK(N))) + +#define SV_GET_SIGNED_BITS(VALUE, N) \ + ((N) == 32 ? (VALUE) : \ + (((VALUE) & (1 << (N))) ? ((VALUE) | ~SV_MASK(N)) : ((VALUE) & SV_MASK(N)))) + +/* + * Implementation-dependent representation. + */ +/* + * Return implementation version information string ("P1800-2005" or "SV3.1a"). + */ +XXTERN const char* svDpiVersion(); + +/* a handle to a scope (an instance of a module or interface) */ +XXTERN typedef void* svScope; + +/* a handle to a generic object (actually, unsized array) */ +XXTERN typedef void* svOpenArrayHandle; + +/* + * Bit-select utility functions. + * + * Packed arrays are assumed to be indexed n-1:0, + * where 0 is the index of LSB + */ + +/* s=source, i=bit-index */ +XXTERN svBit svGetBitselBit(const svBitVecVal* s, int i); +XXTERN svLogic svGetBitselLogic(const svLogicVecVal* s, int i); + +/* d=destination, i=bit-index, s=scalar */ +XXTERN void svPutBitselBit(svBitVecVal* d, int i, svBit s); +XXTERN void svPutBitselLogic(svLogicVecVal* d, int i, svLogic s); + +/* + * Part-select utility functions. + * + * A narrow (<=32 bits) part-select is extracted from the + * source representation and written into the destination word. + * + * Normalized ranges and indexing [n-1:0] are used for both arrays. + * + * s=source, d=destination, i=starting bit index, w=width + * like for variable part-selects; limitations: w <= 32 + */ +XXTERN void svGetPartselBit(svBitVecVal* d, const svBitVecVal* s, int i, int w); +XXTERN void svGetPartselLogic(svLogicVecVal* d, const svLogicVecVal* s, int i, int w); + +XXTERN void svPutPartselBit(svBitVecVal* d, const svBitVecVal s, int i, int w); +XXTERN void svPutPartselLogic(svLogicVecVal* d, const svLogicVecVal s, int i, int w); + +/* + * Open array querying functions + * These functions are modeled upon the SystemVerilog array + * querying functions and use the same semantics. + * + * If the dimension is 0, then the query refers to the + * packed part of an array (which is one-dimensional). + * Dimensions > 0 refer to the unpacked part of an array. + */ + +/* h= handle to open array, d=dimension */ +XXTERN int svLeft(const svOpenArrayHandle h, int d); +XXTERN int svRight(const svOpenArrayHandle h, int d); +XXTERN int svLow(const svOpenArrayHandle h, int d); +XXTERN int svHigh(const svOpenArrayHandle h, int d); +XXTERN int svIncrement(const svOpenArrayHandle h, int d); +XXTERN int svLength(const svOpenArrayHandle h, int d); +XXTERN int svDimensions(const svOpenArrayHandle h); + +/* + * Pointer to the actual representation of the whole array of any type + * NULL if not in C layout + */ +XXTERN void *svGetArrayPtr(const svOpenArrayHandle); + +/* total size in bytes or 0 if not in C layout */ +XXTERN int svSizeOfArray(const svOpenArrayHandle); + +/* + * Return a pointer to an element of the array + * or NULL if index outside the range or null pointer + */ +XXTERN void *svGetArrElemPtr(const svOpenArrayHandle, int indx1, ...); + +/* specialized versions for 1-, 2- and 3-dimensional arrays: */ +XXTERN void *svGetArrElemPtr1(const svOpenArrayHandle, int indx1); +XXTERN void *svGetArrElemPtr2(const svOpenArrayHandle, int indx1, int indx2); +XXTERN void *svGetArrElemPtr3(const svOpenArrayHandle, int indx1, int indx2, + int indx3); + +/* + * Functions for copying between simulator storage and user space. + * These functions copy the whole packed array in either direction. + * The user is responsible for allocating an array to hold the + * canonical representation. + */ + +/* s=source, d=destination */ +/* From user space into simulator storage */ +XXTERN void svPutBitArrElemVecVal(const svOpenArrayHandle d, const svBitVecVal* s, + int indx1, ...); +XXTERN void svPutBitArrElem1VecVal(const svOpenArrayHandle d, const svBitVecVal* s, + int indx1); +XXTERN void svPutBitArrElem2VecVal(const svOpenArrayHandle d, const svBitVecVal* s, + int indx1, int indx2); +XXTERN void svPutBitArrElem3VecVal(const svOpenArrayHandle d, const svBitVecVal* s, + int indx1, int indx2, int indx3); +XXTERN void svPutLogicArrElemVecVal(const svOpenArrayHandle d, const svLogicVecVal* s, + int indx1, ...); +XXTERN void svPutLogicArrElem1VecVal(const svOpenArrayHandle d, const svLogicVecVal* s, + int indx1); +XXTERN void svPutLogicArrElem2VecVal(const svOpenArrayHandle d, const svLogicVecVal* s, + int indx1, int indx2); +XXTERN void svPutLogicArrElem3VecVal(const svOpenArrayHandle d, const svLogicVecVal* s, + int indx1, int indx2, int indx3); + +/* From simulator storage into user space */ +XXTERN void svGetBitArrElemVecVal(svBitVecVal* d, const svOpenArrayHandle s, + int indx1, ...); +XXTERN void svGetBitArrElem1VecVal(svBitVecVal* d, const svOpenArrayHandle s, + + int indx1); +XXTERN void svGetBitArrElem2VecVal(svBitVecVal* d, const svOpenArrayHandle s, + int indx1, int indx2); +XXTERN void svGetBitArrElem3VecVal(svBitVecVal* d, const svOpenArrayHandle s, + int indx1, int indx2, int indx3); +XXTERN void svGetLogicArrElemVecVal(svLogicVecVal* d, const svOpenArrayHandle s, + int indx1, ...); +XXTERN void svGetLogicArrElem1VecVal(svLogicVecVal* d, const svOpenArrayHandle s, + int indx1); +XXTERN void svGetLogicArrElem2VecVal(svLogicVecVal* d, const svOpenArrayHandle s, + int indx1, int indx2); +XXTERN void svGetLogicArrElem3VecVal(svLogicVecVal* d, const svOpenArrayHandle s, + int indx1, int indx2, int indx3); + +XXTERN svBit svGetBitArrElem(const svOpenArrayHandle s, int indx1, ...); +XXTERN svBit svGetBitArrElem1(const svOpenArrayHandle s, int indx1); +XXTERN svBit svGetBitArrElem2(const svOpenArrayHandle s, int indx1, int indx2); +XXTERN svBit svGetBitArrElem3(const svOpenArrayHandle s, int indx1, int indx2, + int indx3); +XXTERN svLogic svGetLogicArrElem(const svOpenArrayHandle s, int indx1, ...); +XXTERN svLogic svGetLogicArrElem1(const svOpenArrayHandle s, int indx1); +XXTERN svLogic svGetLogicArrElem2(const svOpenArrayHandle s, int indx1, int indx2); +XXTERN svLogic svGetLogicArrElem3(const svOpenArrayHandle s, int indx1, int indx2, + int indx3); +XXTERN void svPutLogicArrElem(const svOpenArrayHandle d, svLogic value, int indx1, + ...); +XXTERN void svPutLogicArrElem1(const svOpenArrayHandle d, svLogic value, int indx1); +XXTERN void svPutLogicArrElem2(const svOpenArrayHandle d, svLogic value, int indx1, + int indx2); +XXTERN void svPutLogicArrElem3(const svOpenArrayHandle d, svLogic value, int indx1, + int indx2, int indx3); +XXTERN void svPutBitArrElem(const svOpenArrayHandle d, svBit value, int indx1, ...); +XXTERN void svPutBitArrElem1(const svOpenArrayHandle d, svBit value, int indx1); +XXTERN void svPutBitArrElem2(const svOpenArrayHandle d, svBit value, int indx1, + int indx2); +XXTERN void svPutBitArrElem3(const svOpenArrayHandle d, svBit value, int indx1, + int indx2, int indx3); + +/* Functions for working with DPI context */ + +/* + * Retrieve the active instance scope currently associated with the executing + * imported function. Unless a prior call to svSetScope has occurred, this + * is the scope of the function's declaration site, not call site. + * Returns NULL if called from C code that is *not* an imported function. + */ +XXTERN svScope svGetScope(); + +/* + * Set context for subsequent export function execution. + * This function must be called before calling an export function, unless + * the export function is called while executing an extern function. In that + * case the export function shall inherit the scope of the surrounding extern + * function. This is known as the "default scope". + * The return is the previous active scope (per svGetScope) + */ +XXTERN svScope svSetScope(const svScope scope); + +/* Gets the fully qualified name of a scope handle */ +XXTERN const char* svGetNameFromScope(const svScope); + +/* + * Retrieve svScope to instance scope of an arbitrary function declaration. + * (can be either module, program, interface, or generate scope) + * The return value shall be NULL for unrecognized scope names. + */ +XXTERN svScope svGetScopeFromName(const char* scopeName); + +/* + * Store an arbitrary user data pointer for later retrieval by svGetUserData() + * The userKey is generated by the user. It must be guaranteed by the user to + * be unique from all other userKey's for all unique data storage requirements + * It is recommended that the address of static functions or variables in the + * user's C code be used as the userKey. + * It is illegal to pass in NULL values for either the scope or userData + * arguments. It is also an error to call svPutUserData() with an invalid + * svScope. This function returns -1 for all error cases, 0 upon success. It is + * suggested that userData values of 0 (NULL) not be used as otherwise it can + * be impossible to discern error status returns when calling svGetUserData() + */ +XXTERN int svPutUserData(const svScope scope, void *userKey, void* userData); + +/* + * Retrieve an arbitrary user data pointer that was previously + * stored by a call to svPutUserData(). See the comment above + * svPutUserData() for an explanation of userKey, as well as + * restrictions on NULL and illegal svScope and userKey values. + * This function returns NULL for all error cases, 0 upon success. + * This function also returns NULL in the event that a prior call + * to svPutUserData() was never made. + */ +XXTERN void* svGetUserData(const svScope scope, void* userKey); + +/* + * Returns the file and line number in the SV code from which the extern call + * was made. If this information available, returns TRUE and updates fileName + * and lineNumber to the appropriate values. Behavior is unpredictable if + * fileName or lineNumber are not appropriate pointers. If this information is + * not available return FALSE and contents of fileName and lineNumber not + * modified. Whether this information is available or not is implementation- + * specific. Note that the string provided (if any) is owned by the SV + * implementation and is valid only until the next call to any SV function. + * Applications must not modify this string or free it + */ +XXTERN int svGetCallerInfo(const char** fileName, int *lineNumber); + +/* + * Returns 1 if the current execution thread is in the disabled state. + * Disable protocol must be adhered to if in the disabled state. + */ +XXTERN int svIsDisabledState(); + +/* + * Imported functions call this API function during disable processing to + * acknowledge that they are correctly participating in the DPI disable protocol. + * This function must be called before returning from an imported function that is + * in the disabled state. + */ +XXTERN void svAckDisabledState(); + +/* + ********************************************************** + * DEPRECATED PORTION OF FILE REMOVED. + * DEPRECATED PORTION OF FILE ENDS HERE. + ********************************************************** + */ + +#undef DPI_EXTERN + +#ifdef DPI_PROTOTYPES +#undef DPI_PROTOTYPES +#undef XXTERN +#undef EETERN +#endif + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/include/verilateddpi.cpp b/include/verilateddpi.cpp new file mode 100644 index 000000000..8cecfe176 --- /dev/null +++ b/include/verilateddpi.cpp @@ -0,0 +1,289 @@ +// -*- C++ -*- +//************************************************************************* +// +// Copyright 2009-2009 by Wilson Snyder. 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. +// +// Verilator is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +//========================================================================= +/// +/// \file +/// \brief Verilator: DPI implementation code +/// +/// This file must be compiled and linked against all objects +/// created from Verilator or called by Verilator that use the DPI. +/// +/// Code available from: http://www.veripool.org/verilator +/// +//========================================================================= + +#include "verilatedos.h" +#include "verilated.h" +#include "svdpi.h" + +//====================================================================== +// Internal macros + +#define _VL_SVDPI_UNIMP() + +// Function requires a "context" in the import declaration +#define _VL_SVDPI_CONTEXT_WARN() + +//====================================================================== +// Version + +const char* svDpiVersion() { + return "P1800-2005"; +} + +//====================================================================== +// Bit-select utility functions. + +svBit svGetBitselBit(const svBitVecVal* s, int i) { + _VL_SVDPI_UNIMP(); return 0; +} +svLogic svGetBitselLogic(const svLogicVecVal* s, int i) { + _VL_SVDPI_UNIMP(); return 0; +} + +void svPutBitselBit(svBitVecVal* d, int i, svBit s) { + _VL_SVDPI_UNIMP(); +} +void svPutBitselLogic(svLogicVecVal* d, int i, svLogic s) { + _VL_SVDPI_UNIMP(); +} + +void svGetPartselBit(svBitVecVal* d, const svBitVecVal* s, int i, int w) { + _VL_SVDPI_UNIMP(); +} +void svGetPartselLogic(svLogicVecVal* d, const svLogicVecVal* s, int i, int w) { + _VL_SVDPI_UNIMP(); +} + +void svPutPartselBit(svBitVecVal* d, const svBitVecVal s, int i, int w) { + _VL_SVDPI_UNIMP(); +} +void svPutPartselLogic(svLogicVecVal* d, const svLogicVecVal s, int i, int w) { + _VL_SVDPI_UNIMP(); +} + +//====================================================================== +// Open array querying functions + +int svLeft(const svOpenArrayHandle h, int d) { + _VL_SVDPI_UNIMP(); return 0; +} +int svRight(const svOpenArrayHandle h, int d) { + _VL_SVDPI_UNIMP(); return 0; +} +int svLow(const svOpenArrayHandle h, int d) { + _VL_SVDPI_UNIMP(); return 0; +} +int svHigh(const svOpenArrayHandle h, int d) { + _VL_SVDPI_UNIMP(); return 0; +} +int svIncrement(const svOpenArrayHandle h, int d) { + _VL_SVDPI_UNIMP(); return 0; +} +int svLength(const svOpenArrayHandle h, int d) { + _VL_SVDPI_UNIMP(); return 0; +} +int svDimensions(const svOpenArrayHandle h) { + _VL_SVDPI_UNIMP(); return 0; +} + +void *svGetArrayPtr(const svOpenArrayHandle) { + _VL_SVDPI_UNIMP(); return NULL; +} + +int svSizeOfArray(const svOpenArrayHandle) { + _VL_SVDPI_UNIMP(); return 0; +} + +void *svGetArrElemPtr(const svOpenArrayHandle, int indx1, ...) { + _VL_SVDPI_UNIMP(); return NULL; +} +void *svGetArrElemPtr1(const svOpenArrayHandle, int indx1) { + _VL_SVDPI_UNIMP(); return NULL; +} +void *svGetArrElemPtr2(const svOpenArrayHandle, int indx1, int indx2) { + _VL_SVDPI_UNIMP(); return NULL; +} +void *svGetArrElemPtr3(const svOpenArrayHandle, int indx1, int indx2, int indx3) { + _VL_SVDPI_UNIMP(); return NULL; +} + +//====================================================================== +// s=source, d=destination +// From user space into simulator storage + +void svPutBitArrElemVecVal(const svOpenArrayHandle d, const svBitVecVal* s, + int indx1, ...) { + _VL_SVDPI_UNIMP(); +} +void svPutBitArrElem1VecVal(const svOpenArrayHandle d, const svBitVecVal* s, + int indx1) { + _VL_SVDPI_UNIMP(); +} +void svPutBitArrElem2VecVal(const svOpenArrayHandle d, const svBitVecVal* s, + int indx1, int indx2) { + _VL_SVDPI_UNIMP(); +} +void svPutBitArrElem3VecVal(const svOpenArrayHandle d, const svBitVecVal* s, + int indx1, int indx2, int indx3) { + _VL_SVDPI_UNIMP(); +} +void svPutLogicArrElemVecVal(const svOpenArrayHandle d, const svLogicVecVal* s, + int indx1, ...) { + _VL_SVDPI_UNIMP(); +} +void svPutLogicArrElem1VecVal(const svOpenArrayHandle d, const svLogicVecVal* s, + int indx1) { + _VL_SVDPI_UNIMP(); +} +void svPutLogicArrElem2VecVal(const svOpenArrayHandle d, const svLogicVecVal* s, + int indx1, int indx2) { + _VL_SVDPI_UNIMP(); +} +void svPutLogicArrElem3VecVal(const svOpenArrayHandle d, const svLogicVecVal* s, + int indx1, int indx2, int indx3) { + _VL_SVDPI_UNIMP(); +} + +//====================================================================== +// From simulator storage into user space + +void svGetBitArrElemVecVal(svBitVecVal* d, const svOpenArrayHandle s, + int indx1, ...) { + _VL_SVDPI_UNIMP(); +} +void svGetBitArrElem1VecVal(svBitVecVal* d, const svOpenArrayHandle s, + int indx1) { + _VL_SVDPI_UNIMP(); +} +void svGetBitArrElem2VecVal(svBitVecVal* d, const svOpenArrayHandle s, + int indx1, int indx2) { + _VL_SVDPI_UNIMP(); +} +void svGetBitArrElem3VecVal(svBitVecVal* d, const svOpenArrayHandle s, + int indx1, int indx2, int indx3) { + _VL_SVDPI_UNIMP(); +} +void svGetLogicArrElemVecVal(svLogicVecVal* d, const svOpenArrayHandle s, + int indx1, ...) { + _VL_SVDPI_UNIMP(); +} +void svGetLogicArrElem1VecVal(svLogicVecVal* d, const svOpenArrayHandle s, + int indx1) { + _VL_SVDPI_UNIMP(); +} +void svGetLogicArrElem2VecVal(svLogicVecVal* d, const svOpenArrayHandle s, + int indx1, int indx2) { + _VL_SVDPI_UNIMP(); +} +void svGetLogicArrElem3VecVal(svLogicVecVal* d, const svOpenArrayHandle s, + int indx1, int indx2, int indx3) { + _VL_SVDPI_UNIMP(); +} + +svBit svGetBitArrElem(const svOpenArrayHandle s, int indx1, ...) { + _VL_SVDPI_UNIMP(); return 0; +} +svBit svGetBitArrElem1(const svOpenArrayHandle s, int indx1) { + _VL_SVDPI_UNIMP(); return 0; +} +svBit svGetBitArrElem2(const svOpenArrayHandle s, int indx1, int indx2) { + _VL_SVDPI_UNIMP(); return 0; +} +svBit svGetBitArrElem3(const svOpenArrayHandle s, int indx1, int indx2, int indx3) { + _VL_SVDPI_UNIMP(); return 0; +} +svLogic svGetLogicArrElem(const svOpenArrayHandle s, int indx1, ...) { + _VL_SVDPI_UNIMP(); return sv_x; +} +svLogic svGetLogicArrElem1(const svOpenArrayHandle s, int indx1) { + _VL_SVDPI_UNIMP(); return sv_x; +} +svLogic svGetLogicArrElem2(const svOpenArrayHandle s, int indx1, int indx2) { + _VL_SVDPI_UNIMP(); return sv_x; +} +svLogic svGetLogicArrElem3(const svOpenArrayHandle s, int indx1, int indx2, int indx3) { + _VL_SVDPI_UNIMP(); return sv_x; +} +void svPutLogicArrElem(const svOpenArrayHandle d, svLogic value, int indx1, ...) { + _VL_SVDPI_UNIMP(); +} +void svPutLogicArrElem1(const svOpenArrayHandle d, svLogic value, int indx1) { + _VL_SVDPI_UNIMP(); +} +void svPutLogicArrElem2(const svOpenArrayHandle d, svLogic value, int indx1, int indx2) { + _VL_SVDPI_UNIMP(); +} +void svPutLogicArrElem3(const svOpenArrayHandle d, svLogic value, int indx1, int indx2, int indx3) { + _VL_SVDPI_UNIMP(); +} +void svPutBitArrElem(const svOpenArrayHandle d, svBit value, int indx1, ...) { + _VL_SVDPI_UNIMP(); +} +void svPutBitArrElem1(const svOpenArrayHandle d, svBit value, int indx1) { + _VL_SVDPI_UNIMP(); +} +void svPutBitArrElem2(const svOpenArrayHandle d, svBit value, int indx1, int indx2) { + _VL_SVDPI_UNIMP(); +} +void svPutBitArrElem3(const svOpenArrayHandle d, svBit value, int indx1, int indx2, int indx3) { + _VL_SVDPI_UNIMP(); +} + +//====================================================================== +// Functions for working with DPI context + +svScope svGetScope() { + _VL_SVDPI_UNIMP(); return NULL; +} + +svScope svSetScope(const svScope scope) { + _VL_SVDPI_UNIMP(); return NULL; +} + +const char* svGetNameFromScope(const svScope) { + _VL_SVDPI_UNIMP(); return ""; +} + +svScope svGetScopeFromName(const char* scopeName) { + _VL_SVDPI_UNIMP(); return NULL; +} + +int svPutUserData(const svScope scope, void *userKey, void* userData) { + _VL_SVDPI_UNIMP(); + return -1; // -1 == error +} + +void* svGetUserData(const svScope scope, void* userKey) { + return NULL; // NULL == error +} + +int svGetCallerInfo(const char** fileNamepp, int *lineNumberp) { + _VL_SVDPI_UNIMP(); return false; + //UNSUP if (!Verilated::dpiInContext) { _VL_SVDPI_CONTEXT_WARN(); return false; } + //UNSUP if (fileNamep) *fileNamepp = Verilated::dpiFilenamep; // thread local + //UNSUP if (lineNumberp) *lineNumberp = Verilated::dpiLineno; // thread local + //UNSUP return true; +} + +//====================================================================== +// Disables + +int svIsDisabledState() { + return 0; // Disables not implemented +} + +void svAckDisabledState() { + // Disables not implemented +} diff --git a/src/V3Ast.h b/src/V3Ast.h index d0bbf0dad..99d927355 100644 --- a/src/V3Ast.h +++ b/src/V3Ast.h @@ -218,6 +218,14 @@ public: }; return names[m_e]; }; + const char* dpiType() const { + static const char* names[] = { + "unsigned char", "char", "void*", "int", "int", "svLogic", "long long", + "double", "double", "short int", "float", "long long", + "" + }; + return names[m_e]; + }; inline AstBasicDTypeKwd () {} inline AstBasicDTypeKwd (en _e) : m_e(_e) {} explicit inline AstBasicDTypeKwd (int _e) : m_e(static_cast(_e)) {} @@ -251,6 +259,9 @@ public: int isBitLogic() const { // Don't be as anal about width warnings return (m_e==LOGIC || m_e==BIT); } + int isDpiUnsupported() const { + return (m_e==LOGIC || m_e==TIME || m_e==REALTIME); + } }; inline bool operator== (AstBasicDTypeKwd lhs, AstBasicDTypeKwd rhs) { return (lhs.m_e == rhs.m_e); } inline bool operator== (AstBasicDTypeKwd lhs, AstBasicDTypeKwd::en rhs) { return (lhs.m_e == rhs); } @@ -735,6 +746,7 @@ public: // CONSTANT ACCESSORS static int instrCountBranch() { return 4; } ///< Instruction cycles to branch static int instrCountDiv() { return 10; } ///< Instruction cycles to divide + static int instrCountDpi() { return 1000; } ///< Instruction cycles to call user function static int instrCountLd() { return 2; } ///< Instruction cycles to load memory static int instrCountMul() { return 3; } ///< Instruction cycles to multiply integers static int instrCountPli() { return 20; } ///< Instruction cycles to call pli routines @@ -1228,25 +1240,39 @@ struct AstNodeSel : public AstNodeBiop { struct AstNodeFTask : public AstNode { private: string m_name; // Name of task + string m_cname; // Name of task if DPI import bool m_taskPublic:1; // Public task bool m_didSigning:1; // V3Signed completed; can skip iteration bool m_attrIsolateAssign:1;// User isolate_assignments attribute + bool m_prototype:1; // Just a prototype + bool m_dpiImport:1; // DPI imported + bool m_dpiContext:1; // DPI import context + bool m_dpiTask:1; // DPI import task (vs. void function) + bool m_pure:1; // DPI import pure public: // Node that simply puts name into the output stream AstNodeFTask(FileLine* fileline, const string& name, AstNode* stmtsp) : AstNode(fileline) , m_name(name), m_taskPublic(false), m_didSigning(false) - , m_attrIsolateAssign(false) { + , m_attrIsolateAssign(false), m_prototype(false) + , m_dpiImport(false), m_dpiContext(false), m_dpiTask(false), m_pure(false) { addNOp3p(stmtsp); + cname(name); // Might be overridden by dpi import/export } ASTNODE_BASE_FUNCS(NodeFTask) virtual void dump(ostream& str=cout); virtual string name() const { return m_name; } // * = Var name virtual bool maybePointedTo() const { return true; } // {AstFunc only} op1 = Range output variable - // op3 = Statements/Ports/Vars virtual void name(const string& name) { m_name = name; } - AstNode* stmtsp() const { return op3p()->castNode(); } // op1 = List of statements + string cname() const { return m_cname; } + void cname(const string& cname) { m_cname = cname; } + // op1 = Output variable (functions only, NULL for tasks) + AstNode* fvarp() const { return op1p()->castNode(); } + void addFvarp(AstNode* nodep) { addNOp1p(nodep); } + bool isFunction() const { return fvarp(); } + // op3 = Statements/Ports/Vars + AstNode* stmtsp() const { return op3p()->castNode(); } // op3 = List of statements void addStmtsp(AstNode* nodep) { addNOp3p(nodep); } void taskPublic(bool flag) { m_taskPublic=flag; } bool taskPublic() const { return m_taskPublic; } @@ -1254,6 +1280,16 @@ public: bool didSigning() const { return m_didSigning; } void attrIsolateAssign(bool flag) { m_attrIsolateAssign = flag; } bool attrIsolateAssign() const { return m_attrIsolateAssign; } + void prototype(bool flag) { m_prototype = flag; } + bool prototype() const { return m_prototype; } + void dpiImport(bool flag) { m_dpiImport = flag; } + bool dpiImport() const { return m_dpiImport; } + void dpiContext(bool flag) { m_dpiContext = flag; } + bool dpiContext() const { return m_dpiContext; } + void dpiTask(bool flag) { m_dpiTask = flag; } + bool dpiTask() const { return m_dpiTask; } + void pure(bool flag) { m_pure = flag; } + bool pure() const { return m_pure; } }; struct AstNodeFTaskRef : public AstNode { diff --git a/src/V3AstNodes.cpp b/src/V3AstNodes.cpp index bdf4af68d..db7e5531c 100644 --- a/src/V3AstNodes.cpp +++ b/src/V3AstNodes.cpp @@ -102,16 +102,80 @@ string AstVar::verilogKwd() const { } } -string AstVar::cType() const { - if (widthMin() == 1) { - return "bool"; +string AstVar::vlArgType(bool named, bool forReturn) const { + if (forReturn) named=false; + if (forReturn) v3fatalSrc("verilator internal data is never passed as return, but as first argument"); + string arg; + if (isWide() && isInOnly()) arg += "const "; + if (widthMin() <= 8) { + arg += "CData"; + } else if (widthMin() <= 16) { + arg += "SData"; } else if (widthMin() <= VL_WORDSIZE) { - return "uint32_t"; + arg += "IData"; + } else if (isQuad()) { + arg += "QData"; } else if (isWide()) { - return "uint32_t"; // []'s added later - } else { - return "uint64_t"; + arg += "WData"; // []'s added later } + if (isWide()) { + arg += " (& "+name(); + arg += ")["+cvtToStr(widthWords())+"]"; + } else { + if (isOutput()) arg += "&"; + if (named) arg += " "+name(); + } + return arg; +} + +string AstVar::cpubArgType(bool named, bool forReturn) const { + if (forReturn) named=false; + string arg; + if (isWide() && isInOnly()) arg += "const "; + if (widthMin() == 1) { + arg += "bool"; + } else if (widthMin() <= VL_WORDSIZE) { + arg += "uint32_t"; + } else if (isWide()) { + arg += "uint32_t"; // []'s added later + } else { + arg += "uint64_t"; + } + if (isWide()) { + if (forReturn) v3error("Unsupported: Public functions with >64 bit outputs; make an output of a public task instead"); + arg += " (& "+name(); + arg += ")["+cvtToStr(widthWords())+"]"; + } else { + if (isOutput() && !forReturn) arg += "&"; + if (named) arg += " "+name(); + } + return arg; +} + +string AstVar::dpiArgType(bool named, bool forReturn) const { + if (forReturn) named=false; + string arg; + if (!basicp()) arg = "UNKNOWN"; + if (isWide()) v3error("Unsupported: DPI functions with vectored outputs > 32-bits"); + if (basicp()->isBitLogic()) { + if (widthMin() == 1) { + arg = "unsigned char"; + if (!forReturn && isOutput()) arg += "*"; + } else { + if (forReturn) { + arg = "svBitVecVal"; + } else if (isInOnly()) { + arg = "const svBitVecVal*"; + } else { + arg = "svBitVecVal*"; + } + } + } else { + arg = basicp()->keyword().dpiType(); + if (!forReturn && isOutput()) arg += "*"; + } + if (named) arg += " "+name(); + return arg; } string AstVar::scType() const { @@ -467,6 +531,9 @@ void AstNodeFTaskRef::dump(ostream& str) { void AstNodeFTask::dump(ostream& str) { this->AstNode::dump(str); if (taskPublic()) str<<" [PUBLIC]"; + if (prototype()) str<<" [PROTOTYPE]"; + if (dpiImport()) str<<" [DPII]"; + if (dpiImport() && cname()!=name()) str<<" [c="<AstNode::dump(str); @@ -508,4 +575,6 @@ void AstCCall::dump(ostream& str) { void AstCFunc::dump(ostream& str) { this->AstNode::dump(str); if (slow()) str<<" [SLOW]"; + if (pure()) str<<" [PURE]"; + if (dpiImport()) str<<" [DPII]"; } diff --git a/src/V3AstNodes.h b/src/V3AstNodes.h index 078982b48..e8f346998 100644 --- a/src/V3AstNodes.h +++ b/src/V3AstNodes.h @@ -208,7 +208,6 @@ private: } setNOp1p(rangep); } - AstBasicDTypeKwd keyword() const { return m_keyword; } // private - use isSomething accessors instead public: ASTNODE_NODE_FUNCS(BasicDType, BASICDTYPE) virtual void dump(ostream& str); @@ -229,6 +228,7 @@ public: virtual AstNodeDType* skipRefp() const { return (AstNodeDType*)this; } virtual int widthAlignBytes() const; // (Slow) recurses - Structure alignment 1,2,4 or 8 bytes (arrays affect this) virtual int widthTotalBytes() const; // (Slow) recurses - Width in bytes rounding up 1,2,4,8,12,... + AstBasicDTypeKwd keyword() const { return m_keyword; } // Avoid using - use isSomething accessors instead bool isBitLogic() const { return keyword().isBitLogic(); } bool isSloppy() const { return keyword().isSloppy(); } bool isZeroInit() const { return keyword().isZeroInit(); } @@ -381,9 +381,9 @@ struct AstSel : public AstNodeTriop { :AstNodeTriop(fl, fromp, lsbp, widthp) { if (widthp->castConst()) width(widthp->castConst()->toUInt(), widthp->castConst()->toUInt()); } - AstSel(FileLine* fl, AstNode* fromp, int lsbp, int bitwidth) + AstSel(FileLine* fl, AstNode* fromp, int lsb, int bitwidth) :AstNodeTriop(fl, fromp, - new AstConst(fl,lsbp), new AstConst(fl,bitwidth)) { + new AstConst(fl,lsb), new AstConst(fl,bitwidth)) { width(bitwidth,bitwidth); } ASTNODE_NODE_FUNCS(Sel, SEL) @@ -482,8 +482,10 @@ public: AstVarType varType() const { return m_varType; } // * = Type of variable void varType2Out() { m_tristate=0; m_input=0; m_output=1; } void varType2In() { m_tristate=0; m_input=1; m_output=0; } - string cType() const; // Return C type for declaration: bool, uint32_t, uint64_t, etc. - string scType() const; // Return SysC type: bool, uint32_t, uint64_t, sc_bv + string scType() const; // Return SysC type: bool, uint32_t, uint64_t, sc_bv + string cpubArgType(bool named, bool forReturn) const; // Return C /*public*/ type for argument: bool, uint32_t, uint64_t, etc. + string dpiArgType(bool named, bool forReturn) const; // Return DPI-C type for argument + string vlArgType(bool named, bool forReturn) const; // Return Verilator internal type for argument: CData, SData, IData, WData void combineType(AstVarType type); AstNodeDType* dtypep() const { return op1p()->castNodeDType(); } // op1 = Range of variable AstNodeDType* dtypeSkipRefp() const { return dtypep()->skipRefp(); } // op1 = Range of variable (Note don't need virtual - AstVar isn't a NodeDType) @@ -965,9 +967,6 @@ struct AstFunc : public AstNodeFTask { addNOp1p(fvarsp); } ASTNODE_NODE_FUNCS(Func, FUNC) - // op1 = Range output variable (functions only) - AstNode* fvarp() const { return op1p()->castNode(); } - void addFvarp(AstNode* nodep) { addNOp1p(nodep); } }; struct AstTaskRef : public AstNodeFTaskRef { @@ -984,6 +983,23 @@ struct AstFuncRef : public AstNodeFTaskRef { ASTNODE_NODE_FUNCS(FuncRef, FUNCREF) }; +struct AstDpiExport : public AstNode { + // We could put a AstNodeFTaskRef instead of the verilog function name, + // however we're not *calling* it, so that seems somehow wrong. + // (Probably AstNodeFTaskRef should be renamed AstNodeFTaskCall and have-a AstNodeFTaskRef) +private: + string m_name; // Name of function + string m_cname; // Name of function on c side +public: + AstDpiExport(FileLine* fl, const string& vname, const string& cname) + :AstNode(fl), m_name(vname), m_cname(cname) { } + ASTNODE_NODE_FUNCS(DpiExport, DPIEXPORT) + virtual string name() const { return m_name; } + virtual void name(const string& name) { m_name = name; } + string cname() const { return m_cname; } + void cname(const string& cname) { m_cname = cname; } +}; + //###################################################################### struct AstSenItem : public AstNodeSenItem { @@ -3065,6 +3081,8 @@ private: bool m_isStatic:1; // Function is declared static (no this) bool m_symProlog:1; // Setup symbol table for later instructions bool m_entryPoint:1; // User may call into this top level function + bool m_pure:1; // Pure function + bool m_dpiImport:1; // From dpi import public: AstCFunc(FileLine* fl, const string& name, AstScope* scopep, const string& rtnType="") : AstNode(fl) { @@ -3081,6 +3099,8 @@ public: m_isStatic = true; // Note defaults to static, later we see where thisp is needed m_symProlog = false; m_entryPoint = false; + m_pure = false; + m_dpiImport = false; } ASTNODE_NODE_FUNCS(CFunc, CFUNC) virtual string name() const { return m_name; } @@ -3090,9 +3110,11 @@ public: virtual V3Hash sameHash() const { return V3Hash(); } virtual bool same(AstNode* samep) const { return ((funcType()==samep->castCFunc()->funcType()) && (rtnTypeVoid()==samep->castCFunc()->rtnTypeVoid()) - && (argTypes()==samep->castCFunc()->argTypes())); } + && (argTypes()==samep->castCFunc()->argTypes()) + && (!dpiImport() || name()==samep->castCFunc()->name())); } // virtual void name(const string& name) { m_name = name; } + virtual int instrCount() const { return dpiImport() ? instrCountDpi() : 0; } AstScope* scopep() const { return m_scopep; } void scopep(AstScope* nodep) { m_scopep = nodep; } string rtnTypeVoid() const { return ((m_rtnType=="") ? "void" : m_rtnType); } @@ -3118,8 +3140,12 @@ public: void symProlog(bool flag) { m_symProlog = flag; } bool entryPoint() const { return m_entryPoint; } void entryPoint(bool flag) { m_entryPoint = flag; } + bool pure() const { return m_pure; } + void pure(bool flag) { m_pure = flag; } + bool dpiImport() const { return m_dpiImport; } + void dpiImport(bool flag) { m_dpiImport = flag; } // - // If adding node accessors, see below + // If adding node accessors, see below emptyBody AstNode* argsp() const { return op1p()->castNode(); } void addArgsp(AstNode* nodep) { addOp1p(nodep); } AstNode* initsp() const { return op2p()->castNode(); } @@ -3165,8 +3191,8 @@ public: AstNode* exprsp() const { return op1p()->castNode(); } // op1= expressions to print virtual bool isGateOptimizable() const { return false; } virtual bool isPredictOptimizable() const { return false; } - virtual bool isSplittable() const { return false; } // SPECIAL: $display has 'visual' ordering - virtual bool isOutputter() const { return true; } + virtual bool isSplittable() const { return funcp()->pure(); } + virtual bool isOutputter() const { return !(funcp()->pure()); } AstCFunc* funcp() const { return m_funcp; } string hiername() const { return m_hiername; } void hiername(const string& hn) { m_hiername = hn; } diff --git a/src/V3EmitC.cpp b/src/V3EmitC.cpp index 960dcb493..c77fbb591 100644 --- a/src/V3EmitC.cpp +++ b/src/V3EmitC.cpp @@ -90,16 +90,9 @@ public: if (AstVar* portp = stmtp->castVar()) { if (portp->isIO() && !portp->isFuncReturn()) { if (args != "") args+= ", "; - if (portp->isWide()) { - if (portp->isInOnly()) args += "const "; - args += portp->cType(); - args += " (& "+portp->name(); - args += ")["+cvtToStr(portp->widthWords())+"]"; - } else { - args += portp->cType(); - if (portp->isOutput()) args += "&"; - args += " "+portp->name(); - } + if (nodep->dpiImport()) args += portp->dpiArgType(true,false); + else if (nodep->funcPublic()) args += portp->cpubArgType(true,false); + else args += portp->vlArgType(true,false); } } } @@ -709,9 +702,9 @@ class EmitCImp : EmitCStmts { //--------------------------------------- // VISITORS virtual void visit(AstCFunc* nodep, AstNUser*) { - if (nodep->funcType().isTrace()) { - return; // TRACE_* handled specially - } + // TRACE_* and DPI handled elsewhere + if (nodep->funcType().isTrace()) return; + if (nodep->dpiImport()) return; if (!(nodep->slow() ? m_slow : m_fast)) return; m_blkChangeDetVec.clear(); @@ -1557,10 +1550,12 @@ void EmitCImp::emitIntFuncDecls(AstNodeModule* modp) { for (vector::iterator it = funcsp.begin(); it != funcsp.end(); ++it) { AstCFunc* funcp = *it; - ofp()->putsPrivate(funcp->declPrivate()); - if (funcp->isStatic()) puts("static "); - puts(funcp->rtnTypeVoid()); puts("\t"); - puts(funcp->name()); puts("("+cFuncArgs(funcp)+");\n"); + if (!funcp->dpiImport()) { // DPI is prototyped in __Dpi.h + ofp()->putsPrivate(funcp->declPrivate()); + if (funcp->isStatic()) puts("static "); + puts(funcp->rtnTypeVoid()); puts("\t"); + puts(funcp->name()); puts("("+cFuncArgs(funcp)+");\n"); + } } } @@ -1580,6 +1575,7 @@ void EmitCImp::emitInt(AstNodeModule* modp) { if (v3Global.needHInlines()) { // Set by V3EmitCInlines; should have been called before us puts("#include \""+topClassName()+"__Inlines.h\"\n"); } + // No __Dpi.h needed, we've shimmed it all into the interface // Declare foreign instances up front to make C++ happy puts("class "+symClassName()+";\n"); @@ -1757,6 +1753,9 @@ void EmitCImp::emitImp(AstNodeModule* modp) { // Us puts("#include \""+ symClassName() +".h\"\n"); + if (v3Global.dpi()) { + puts("#include \""+ topClassName() +"__Dpi.h\"\n"); + } if (optSystemPerl() && (splitFilenum() || !m_fast)) { puts("\n"); @@ -2130,6 +2129,66 @@ public: } }; +//###################################################################### +// DPI definitions + +class EmitCDpi : EmitCStmts { + // METHODS + void newOutCFile() { + string filename = (v3Global.opt.makeDir()+"/"+ topClassName() + + "__Dpi.h"); + + AstCFile* cfilep = newCFile(filename, false/*slow*/, false/*source*/); + cfilep->support(true); + + if (m_ofp) v3fatalSrc("Previous file not closed"); + m_ofp = new V3OutCFile (filename); + m_ofp->putsHeader(); + } + + void emitTop() { + puts("// DESCR" "IPTION: Verilator output: Prototypes for DPI import and export functions.\n"); + puts("//\n"); + puts("// Verilator includes this file in all generated .cpp files that use DPI functions.\n"); + puts("// Manually include this file where DPI .c import functions are declared to insure\n"); + puts("// the C functions match the expectations of the DPI imports.\n"); + puts("\n"); + puts("#ifdef __cplusplus\n"); + puts("extern \"C\" {\n"); + puts("#endif\n"); + puts("\n"); + } + + void emitBottom() { + puts("\n"); + puts("#ifdef __cplusplus\n"); + puts("}\n"); + puts("#endif\n"); + } + + // VISITORS + virtual void visit(AstNodeModule* nodep, AstNUser*) { + nodep->iterateChildren(*this); + } + virtual void visit(AstCFunc* nodep, AstNUser*) { + if (nodep->dpiImport()) { + puts("// dpi import at "+nodep->fileline()->ascii()+"\n"); + puts("extern "+nodep->rtnTypeVoid()+" "+nodep->name()+" ("+cFuncArgs(nodep)+");\n"); + } + } +public: + EmitCDpi() {} + virtual ~EmitCDpi() {} + void main() { + // Put out the file + newOutCFile(); + emitTop(); + v3Global.rootp()->accept(*this); + emitBottom(); + delete m_ofp; m_ofp=NULL; + } +}; + //###################################################################### // EmitC class functions @@ -2149,7 +2208,14 @@ void V3EmitC::emitc() { void V3EmitC::emitcTrace() { UINFO(2,__FUNCTION__<<": "<castTask() ? "task ":"function "); + putbs(nodep->isFunction() ? "function":"task"); + puts(" "); puts(nodep->name()); puts(";\n"); putbs("begin\n"); diff --git a/src/V3Global.h b/src/V3Global.h index 44e06ceb8..cff60c814 100644 --- a/src/V3Global.h +++ b/src/V3Global.h @@ -43,7 +43,8 @@ class V3Global { AstNetlist* m_rootp; // Root of entire netlist int m_debugFileNumber; // Number to append to debug files created bool m_assertWidthsSame; // Tree should have width()==widthMin() - bool m_needHInlines; // Need a __Inlines file + bool m_needHInlines; // Need __Inlines file + bool m_dpi; // Need __Dpi include files public: // Options @@ -56,6 +57,7 @@ public: m_debugFileNumber = 0; m_assertWidthsSame = false; m_needHInlines = false; + m_dpi = false; } void clear() { if (m_rootp) m_rootp->deleteTree(); m_rootp=NULL; @@ -76,6 +78,8 @@ public: } bool needHInlines() const { return m_needHInlines; } void needHInlines(bool flag) { m_needHInlines=flag; } + bool dpi() const { return m_dpi; } + void dpi(bool flag) { m_dpi = flag; } }; extern V3Global v3Global; diff --git a/src/V3Life.cpp b/src/V3Life.cpp index e27f5c759..f5e0dc630 100644 --- a/src/V3Life.cpp +++ b/src/V3Life.cpp @@ -405,10 +405,21 @@ private: nodep->funcp()->accept(*this); } } + virtual void visit(AstCFunc* nodep, AstNUser*) { + //UINFO(4," CCALL "<dpiImport() && !nodep->pure()) { + m_sideEffect = true; // If appears on assign RHS, don't ever delete the assignment + } + nodep->iterateChildren(*this); + } virtual void visit(AstUCFunc* nodep, AstNUser*) { m_sideEffect = true; // If appears on assign RHS, don't ever delete the assignment nodep->iterateChildren(*this); } + virtual void visit(AstCMath* nodep, AstNUser*) { + m_sideEffect = true; // If appears on assign RHS, don't ever delete the assignment + nodep->iterateChildren(*this); + } virtual void visit(AstVar*, AstNUser*) {} // Don't want varrefs under it virtual void visit(AstNode* nodep, AstNUser*) { diff --git a/src/V3Link.cpp b/src/V3Link.cpp index 503edd285..a1d885b34 100644 --- a/src/V3Link.cpp +++ b/src/V3Link.cpp @@ -326,22 +326,21 @@ private: // Convert the func's range to the output variable // This should probably be done in the Parser instead, as then we could // just attact normal signal attributes to it. - if (AstFunc* funcp = nodep->castFunc()) { - if (!funcp->fvarp()->castVar()) { - AstNodeDType* dtypep = funcp->fvarp()->castNodeDType(); - // If unspecified, function returns one bit; however when we support NEW() it could - // also return the class reference. - if (dtypep) dtypep->unlinkFrBack(); - else dtypep = new AstBasicDType(nodep->fileline(), AstBasicDTypeKwd::LOGIC); - AstVar* newvarp = new AstVar(nodep->fileline(), AstVarType::OUTPUT, nodep->name(), dtypep); - newvarp->isSigned(funcp->isSigned()); - newvarp->funcReturn(true); - newvarp->trace(false); // Not user visible - newvarp->attrIsolateAssign(funcp->attrIsolateAssign()); - funcp->addFvarp(newvarp); - // Explicit insert required, as the var name shadows the upper level's task name - symsInsert(newvarp->name(), newvarp); - } + if (nodep->fvarp() + && !nodep->fvarp()->castVar()) { + AstNodeDType* dtypep = nodep->fvarp()->castNodeDType(); + // If unspecified, function returns one bit; however when we support NEW() it could + // also return the class reference. + if (dtypep) dtypep->unlinkFrBack(); + else dtypep = new AstBasicDType(nodep->fileline(), AstBasicDTypeKwd::LOGIC); + AstVar* newvarp = new AstVar(nodep->fileline(), AstVarType::OUTPUT, nodep->name(), dtypep); + newvarp->isSigned(nodep->isSigned()); + newvarp->funcReturn(true); + newvarp->trace(false); // Not user visible + newvarp->attrIsolateAssign(nodep->attrIsolateAssign()); + nodep->addFvarp(newvarp); + // Explicit insert required, as the var name shadows the upper level's task name + symsInsert(newvarp->name(), newvarp); } m_ftaskp = nodep; nodep->iterateChildren(*this); diff --git a/src/V3ParseImp.h b/src/V3ParseImp.h index 93f084474..e2bd855f1 100644 --- a/src/V3ParseImp.h +++ b/src/V3ParseImp.h @@ -40,6 +40,8 @@ class V3Lexer; typedef enum { uniq_NONE, uniq_UNIQUE, uniq_PRIORITY } V3UniqState; +typedef enum { iprop_NONE, iprop_CONTEXT, iprop_PURE } V3ImportProperty; + //============================================================================ // We can't use bison's %union as we want to pass the fileline with all tokens @@ -53,6 +55,7 @@ struct V3ParseBisonYYSType { double cdouble; V3UniqState uniqstate; AstSignedState signstate; + V3ImportProperty iprop; AstNode* nodep; diff --git a/src/V3Signed.cpp b/src/V3Signed.cpp index af841c3d5..2b0fa4815 100644 --- a/src/V3Signed.cpp +++ b/src/V3Signed.cpp @@ -156,18 +156,14 @@ private: // However a later operation may have changed the node->signed w/o changing // the number's sign. So we don't: nodep->isSigned(nodep->num().isSigned()); } - virtual void visit(AstFunc* nodep, AstNUser*) { - // Avoid recursion; can't use user() as they're all full, and anyhow this is often called - if (nodep->didSigning()) return; - nodep->didSigning(true); - nodep->iterateChildren(*this); - nodep->signedFrom(nodep->fvarp()); // Which will get it from fvarp()->dtypep() - } - virtual void visit(AstTask* nodep, AstNUser*) { + virtual void visit(AstNodeFTask* nodep, AstNUser*) { // Avoid recursion; can't use user() as they're all full, and anyhow this is often called if (nodep->didSigning()) return; nodep->didSigning(true); nodep->iterateChildren(*this); + if (nodep->fvarp()) { + nodep->signedFrom(nodep->fvarp()); // Which will get it from fvarp()->dtypep() + } } virtual void visit(AstNodeFTaskRef* nodep, AstNUser*) { nodep->iterateChildren(*this); diff --git a/src/V3Simulate.h b/src/V3Simulate.h index ee4ffb4f8..bcbf7156a 100644 --- a/src/V3Simulate.h +++ b/src/V3Simulate.h @@ -290,6 +290,7 @@ private: } virtual void visit(AstNodeFTask* nodep, AstNUser*) { if (!m_params) { badNodeType(nodep); return; } + if (nodep->dpiImport()) { clearOptimizable(nodep,"DPI import functions aren't simulatable"); } checkNodeInfo(nodep); nodep->iterateChildren(*this); } @@ -505,9 +506,9 @@ private: virtual void visit(AstFuncRef* nodep, AstNUser*) { UINFO(5," FUNCREF "<taskp()->castFunc(); if (!funcp) nodep->v3fatalSrc("Not linked"); + AstNodeFTask* funcp = nodep->taskp()->castNodeFTask(); if (!funcp) nodep->v3fatalSrc("Not linked"); if (m_params) { V3Width::widthParamsEdit(funcp); } funcp=NULL; // Make sure we've sized the function - funcp = nodep->taskp()->castFunc(); if (!funcp) nodep->v3fatalSrc("Not linked"); + funcp = nodep->taskp()->castNodeFTask(); if (!funcp) nodep->v3fatalSrc("Not linked"); // Apply function call values to function // Note we'd need a stack if we allowed recursive functions! V3TaskConnects tconnects = V3Task::taskConnects(nodep, nodep->taskp()->stmtsp()); @@ -532,7 +533,8 @@ private: // Evaluate the function funcp->accept(*this); if (!m_checkOnly && optimizable()) { - // Grab return value from output variable + // Grab return value from output variable (if it's a function) + if (!funcp->fvarp()) nodep->v3fatalSrc("Function reference points at non-function"); newNumber(nodep)->opAssign(*fetchNumber(funcp->fvarp())); } } diff --git a/src/V3Task.cpp b/src/V3Task.cpp index 9d260acc8..4f1a3c5cc 100644 --- a/src/V3Task.cpp +++ b/src/V3Task.cpp @@ -201,6 +201,7 @@ private: UINFO(9," TASK "<dpiImport()) m_curVxp->noInline(true); nodep->iterateChildren(*this); m_curVxp = lastVxp; } @@ -304,14 +305,17 @@ private: IM_AFTER, // Pointing at last inserted stmt, insert after IM_WHILE_PRECOND // Pointing to for loop, add to body end }; + typedef map > DpiNames; // STATE - TaskStateVisitor* m_statep; // Common state between visitors + TaskStateVisitor* m_statep; // Common state between visitors AstNodeModule* m_modp; // Current module + AstTopScope* m_topScopep; // Current top scope AstScope* m_scopep; // Current scope InsertMode m_insMode; // How to insert AstNode* m_insStmtp; // Where to insert statement int m_modNCalls; // Incrementing func # for making symbols + DpiNames m_dpiNames; // Map of all created DPI functions // METHODS static int debug() { @@ -423,7 +427,7 @@ private: // Create function output variables if (outvscp) { //UINFO(0, "setflag on "<fvarp()<<" to "<taskp()->castFunc()->fvarp()->user2p(outvscp); + refp->taskp()->fvarp()->user2p(outvscp); } // Replace variable refs // Iteration requires a back, so put under temporary node @@ -487,7 +491,8 @@ private: } } // First argument is symbol table, then output if a function - ccallp->argTypes("vlSymsp"); + bool needContext = !refp->taskp()->dpiImport() || refp->taskp()->dpiContext(); + if (needContext) ccallp->argTypes("vlSymsp"); if (outvscp) { ccallp->addArgsp(new AstVarRef(refp->fileline(), outvscp, true)); } @@ -503,23 +508,128 @@ private: return beginp; } + AstCFunc* makeDpiCFunc(AstNodeFTask* nodep, AstVar* rtnvarp) { + if (nodep->cname() != AstNode::prettyName(nodep->cname())) { + nodep->v3error("DPI function has illegal characters in C identifier name: "<cname())); + } + AstCFunc* dpip = new AstCFunc(nodep->fileline(), + nodep->cname(), + m_scopep, + (rtnvarp ? rtnvarp->dpiArgType(true,true) + // Tasks (but not void functions) return bool indicating disabled + : nodep->dpiTask() ? "int" + : "")); + dpip->dontCombine(true); + dpip->entryPoint (false); + dpip->funcPublic (true); + dpip->isStatic (false); + dpip->pure (nodep->pure()); + dpip->dpiImport (true); + // Add DPI reference to top, since it's a global function + m_topScopep->scopep()->addActivep(dpip); + return dpip; + } - AstCFunc* makeUserFunc(AstNodeFTask* nodep, bool forUser) { + void bodyDpiCFunc(AstNodeFTask* nodep, AstVarScope* rtnvscp, AstCFunc* cfuncp) { + // Convert input/inout arguments to DPI types + string args; + for (AstNode* stmtp = cfuncp->argsp(); stmtp; stmtp=stmtp->nextp()) { + if (AstVar* portp = stmtp->castVar()) { + AstVarScope* portvscp = portp->user2p()->castNode()->castVarScope(); // Remembered when we created it earlier + if (portp->isIO() && !portp->isFuncReturn() && portvscp != rtnvscp) { + bool bitvec = (portp->basicp()->isBitLogic() && portp->width() > 32); + + if (args != "") { args+= ", "; } + if (bitvec) {} + else if (portp->isOutput()) args += "&"; + else if (portp->basicp() && portp->basicp()->isBitLogic() && portp->widthMin() != 1) args += "&"; // it's a svBitVecVal + + args += "__Vcvt_"+portp->name(); + + string stmt; + if (bitvec) { + stmt += "svBitVecVal __Vcvt_"+portp->name(); + stmt += " ["+cvtToStr(portp->widthWords())+"]"; + } else { + stmt += portp->dpiArgType(true,true); + stmt += " __Vcvt_"+portp->name(); + } + if (portp->isInput()) { + // Someday we'll have better type support, and this can make variables and casts. + // But for now, we'll just text-bash it. + if (bitvec) { + // We only support quads, so don't need to sweat longer stuff + stmt += "; VL_SET_WQ(__Vcvt_"+portp->name()+", "+portp->name()+")"; + } else { + stmt += " = "; + if (portp->basicp() && portp->basicp()->keyword()==AstBasicDTypeKwd::CHANDLE) { + stmt += "(void*)"; + } + stmt += portp->name(); + } + } + stmt += ";\n"; + cfuncp->addStmtsp(new AstCStmt(portp->fileline(), stmt)); + } + } + } + + // Store context, if needed + if (nodep->dpiContext()) { + // TBD + } + + {// Call the user function + string stmt; + if (rtnvscp) { // isFunction will no longer work as we unlinked the return var + stmt += rtnvscp->varp()->dpiArgType(true,true) + " __Vcvt_"+rtnvscp->varp()->name() + " = "; + } + stmt += nodep->cname()+"("+args+");\n"; + cfuncp->addStmtsp(new AstCStmt(nodep->fileline(), stmt)); + } + + // Convert output/inout arguments back to internal type + for (AstNode* stmtp = cfuncp->argsp(); stmtp; stmtp=stmtp->nextp()) { + if (AstVar* portp = stmtp->castVar()) { + if (portp->isIO()) { + AstVarScope* portvscp = portp->user2p()->castNode()->castVarScope(); // Remembered when we created it earlier + if (portp->isOutput() || portp->isFuncReturn()) { + string stmt; + if (portp->basicp() && portp->basicp()->keyword()==AstBasicDTypeKwd::CHANDLE) { + stmt += "(QData)"; + } + stmt += "__Vcvt_"+portp->name(); + // Use a AstCMath, as we want V3Clean to mask off bits that don't make sense. + int cwidth = VL_WORDSIZE; if (portp->basicp()) cwidth = portp->basicp()->keyword().width(); + if (portp->basicp() && portp->basicp()->isBitLogic()) cwidth = VL_WORDSIZE*portp->widthWords(); + cfuncp->addStmtsp(new AstAssign(portp->fileline(), + new AstVarRef(portp->fileline(), portvscp, true), + new AstSel(portp->fileline(), + new AstCMath(portp->fileline(), stmt, cwidth, false), + 0, portp->width()))); + } + } + } + } + } + + AstCFunc* makeUserFunc(AstNodeFTask* nodep, bool ftaskNoInline) { // Given a already cloned node, make a public C function, or a non-inline C function // Probably some of this work should be done later, but... // should the type of the function be bool/uint32/64 etc (based on lookup) or IData? AstNode::user2ClearTree(); AstVar* rtnvarp = NULL; AstVarScope* rtnvscp = NULL; - if (nodep->castFunc()) { + if (nodep->isFunction()) { AstVar* portp = NULL; - if (NULL!=(portp = nodep->castFunc()->fvarp()->castVar())) { + if (NULL!=(portp = nodep->fvarp()->castVar())) { if (!portp->isFuncReturn()) nodep->v3error("Not marked as function return var"); if (portp->isWide()) nodep->v3error("Unsupported: Public functions with return > 64 bits wide. (Make it a output instead.)"); - if (!forUser) portp->funcReturn(false); // Converting return to 'outputs' + if (ftaskNoInline) portp->funcReturn(false); // Converting return to 'outputs' portp->unlinkFrBack(); rtnvarp = portp; rtnvarp->funcLocal(true); + rtnvarp->name(rtnvarp->name()+"__Vfuncrtn"); // Avoid conflict with DPI function name rtnvscp = new AstVarScope (rtnvarp->fileline(), m_scopep, rtnvarp); m_scopep->addVarp(rtnvscp); rtnvarp->user2p(rtnvscp); @@ -527,30 +637,58 @@ private: nodep->v3fatalSrc("function without function output variable"); } } + string prefix = ""; + if (nodep->dpiImport()) prefix = "__Vdpiimwrap_"; + else if (ftaskNoInline) prefix = "__VnoInFunc_"; AstCFunc* cfuncp = new AstCFunc(nodep->fileline(), - string(forUser?"":"__VnoInFunc_") + nodep->name(), + prefix + nodep->name(), m_scopep, - ((forUser && rtnvarp)?rtnvarp->cType():"")); - cfuncp->dontCombine(true); - cfuncp->entryPoint(true); - cfuncp->funcPublic(forUser); - cfuncp->isStatic(!forUser); + ((nodep->taskPublic() && rtnvarp)?rtnvarp->cpubArgType(true,true):"")); + // It's ok to combine imports because this is just a wrapper; duplicate wrappers can get merged. + cfuncp->dontCombine(!nodep->dpiImport()); + cfuncp->entryPoint (!nodep->dpiImport()); + cfuncp->funcPublic (nodep->taskPublic()); + cfuncp->isStatic (!(nodep->dpiImport()||nodep->taskPublic())); + cfuncp->pure (nodep->pure()); + //cfuncp->dpiImport // Not set in the wrapper - the called function has it set - if (forUser) { - // We need to get a pointer to all of our variables (may have eval'ed something else earlier) - cfuncp->addInitsp( - new AstCStmt(nodep->fileline(), - EmitCBaseVisitor::symClassVar()+" = this->__VlSymsp;\n")); - } else { - // Need symbol table - cfuncp->argTypes(EmitCBaseVisitor::symClassVar()); + bool needContext = !nodep->dpiImport() || nodep->dpiContext(); + if (needContext) { + if (nodep->taskPublic()) { + // We need to get a pointer to all of our variables (may have eval'ed something else earlier) + cfuncp->addInitsp( + new AstCStmt(nodep->fileline(), + EmitCBaseVisitor::symClassVar()+" = this->__VlSymsp;\n")); + } else { + // Need symbol table + cfuncp->argTypes(EmitCBaseVisitor::symClassVar()); + } } // Fake output variable if was a function if (rtnvarp) cfuncp->addArgsp(rtnvarp); - cfuncp->addInitsp(new AstCStmt(nodep->fileline()," "+EmitCBaseVisitor::symTopAssign()+"\n")); + if (!nodep->dpiImport()) { + cfuncp->addInitsp(new AstCStmt(nodep->fileline(), EmitCBaseVisitor::symTopAssign()+"\n")); + } + + AstCFunc* dpip = NULL; + string dpiproto; + if (nodep->dpiImport()) { + if (nodep->pure()) dpiproto += "pure "; + if (nodep->dpiContext()) dpiproto += "context "; + dpiproto += rtnvarp ? rtnvarp->dpiArgType(true,true):"void"; + dpiproto += " "+nodep->cname()+" ("; + + // Only create one DPI extern for each specified cname, + // as it's legal for the user to attach multiple tasks to one dpi cname + if (m_dpiNames.find(nodep->cname()) == m_dpiNames.end()) { + // m_dpiNames insert below + dpip = makeDpiCFunc(nodep, rtnvarp); + } + } // Create list of arguments and move to function + string args; for (AstNode* nextp, *stmtp = nodep->stmtsp(); stmtp; stmtp=nextp) { nextp = stmtp->nextp(); if (AstVar* portp = stmtp->castVar()) { @@ -559,6 +697,18 @@ private: portp->unlinkFrBack(); portp->funcLocal(true); cfuncp->addArgsp(portp); + if (dpip) { + dpip->addArgsp(portp->cloneTree(false)); + if (!portp->basicp() || portp->basicp()->keyword().isDpiUnsupported()) { + portp->v3error("Unsupported: DPI argument of type "<basicp()->prettyTypeName()); + portp->v3error("... For best portability, use bit, byte, int, or longint"); + } + } + if (!portp->isFuncReturn()) { + if (args != "") { args+= ", "; dpiproto+= ", "; } + args += portp->name(); // Leftover so ,'s look nice + if (nodep->dpiImport()) dpiproto += portp->dpiArgType(false,false); + } } else { // "Normal" variable, mark inside function portp->funcLocal(true); @@ -568,11 +718,30 @@ private: portp->user2p(newvscp); } } + dpiproto += ")"; + + if (nodep->dpiImport()) { + // Only create one DPI extern for each specified cname, + // as it's legal for the user to attach multiple tasks to one dpi cname + DpiNames::iterator iter = m_dpiNames.find(nodep->cname()); + if (iter == m_dpiNames.end()) { + m_dpiNames.insert(make_pair(nodep->cname(), make_pair(dpip, dpiproto))); + } else if (iter->second.second != dpiproto) { + nodep->v3error("Duplicate declaration of DPI function with different formal arguments: "<prettyName()); + nodep->v3error("... New prototype: "<second.first->v3error("... Original prototype: "<second.second); + } + } + // Move body AstNode* bodysp = nodep->stmtsp(); if (bodysp) { bodysp->unlinkFrBackWithNext(); cfuncp->addStmtsp(bodysp); } + if (nodep->dpiImport()) { + bodyDpiCFunc(nodep, rtnvscp, cfuncp); + } + // Return statement - if (rtnvscp && forUser) { + if (rtnvscp && nodep->taskPublic()) { cfuncp->addFinalsp(new AstCReturn(rtnvscp->fileline(), new AstVarRef(rtnvscp->fileline(), rtnvscp, false))); } @@ -585,8 +754,7 @@ private: } // Delete rest of cloned task and return new func pushDeletep(nodep); nodep=NULL; - if (debug()>=9 && forUser) { cfuncp->dumpTree(cout,"-userFunc: "); } - if (debug()>=9 && !forUser) { cfuncp->dumpTree(cout,"-noInFunc: "); } + if (debug()>=9) { cfuncp->dumpTree(cout,"-userFunc: "); } return cfuncp; } @@ -633,6 +801,10 @@ private: nodep->iterateChildren(*this); m_modp = NULL; } + virtual void visit(AstTopScope* nodep, AstNUser*) { + m_topScopep = nodep; + nodep->iterateChildren(*this); + } virtual void visit(AstScope* nodep, AstNUser*) { m_scopep = nodep; m_insStmtp = NULL; @@ -659,12 +831,13 @@ private: UINFO(4," Func REF "<=9) { nodep->dumpTree(cout,"-preref:"); } // First, do hierarchical funcs - AstFunc* funcp = nodep->taskp()->castFunc(); + AstNodeFTask* funcp = nodep->taskp(); if (!funcp) nodep->v3fatalSrc("unlinked"); + if (!funcp->isFunction()) nodep->v3fatalSrc("func reference to non-function"); // Inline func refs in the function iterateIntoFTask(funcp); // Create output variable - string namePrefix = "__Vfunc_"+funcp->shortName()+"__"+cvtToStr(m_modNCalls++); + string namePrefix = "__Vfunc_"+nodep->taskp()->shortName()+"__"+cvtToStr(m_modNCalls++); AstVarScope* outvscp = createVarScope (funcp->fvarp()->castVar(), namePrefix+"__out"); // Create cloned statements @@ -693,30 +866,26 @@ private: AstNode* prevInsStmtp = m_insStmtp; m_insMode = IM_BEFORE; m_insStmtp = nodep->stmtsp(); // Might be null if no statements, but we won't use it - if (!nodep->user1()) { + if (!nodep->user1()) { // Just one creation needed per function // Expand functions in it nodep->user1(true); - if (nodep->taskPublic()) { + if (nodep->dpiImport() || nodep->taskPublic() || m_statep->ftaskNoInline(nodep)) { // Clone it first, because we may have later FTaskRef's that still need // the original version. + if (m_statep->ftaskNoInline(nodep)) m_statep->checkPurity(nodep); AstNodeFTask* clonedFuncp = nodep->cloneTree(false); - AstCFunc* cfuncp = makeUserFunc(clonedFuncp, true); - nodep->addNextHere(cfuncp); - iterateIntoFTask(clonedFuncp); // Do the clone too - } - if (m_statep->ftaskNoInline(nodep)) { - m_statep->checkPurity(nodep); - AstNodeFTask* clonedFuncp = nodep->cloneTree(false); - AstCFunc* cfuncp = makeUserFunc(clonedFuncp, false); - m_statep->ftaskCFuncp(nodep, cfuncp); + AstCFunc* cfuncp = makeUserFunc(clonedFuncp, m_statep->ftaskNoInline(nodep)); nodep->addNextHere(cfuncp); + if (nodep->dpiImport() || m_statep->ftaskNoInline(nodep)) { + m_statep->ftaskCFuncp(nodep, cfuncp); + } iterateIntoFTask(clonedFuncp); // Do the clone too } // Any variables inside the function still have varscopes pointing to them. // We're going to delete the vars, so delete the varscopes. - if (nodep->castFunc()) { - if (AstVar* portp = nodep->castFunc()->fvarp()->castVar()) { + if (nodep->isFunction()) { + if (AstVar* portp = nodep->fvarp()->castVar()) { AstVarScope* vscp = m_statep->findVarScope(m_scopep, portp); UINFO(9," funcremovevsc "<unlinkFrBack()); vscp=NULL; @@ -773,6 +942,7 @@ public: TaskVisitor(AstNetlist* nodep, TaskStateVisitor* statep) : m_statep(statep) { m_modp = NULL; + m_topScopep = NULL; m_scopep = NULL; m_insStmtp = NULL; AstNode::user1ClearTree(); diff --git a/src/V3Width.cpp b/src/V3Width.cpp index 2038f6222..b0ff0ef40 100644 --- a/src/V3Width.cpp +++ b/src/V3Width.cpp @@ -855,13 +855,15 @@ private: nodep->paramsp()->iterateAndNext(*this); m_cellRangep = NULL; } - virtual void visit(AstFunc* nodep, AstNUser* vup) { - // Grab width from the output variable - UINFO(5," FUNC "<width()==0) { // Function hasn't been widthed, so make it so. nodep->iterateChildren(*this); - nodep->width(nodep->fvarp()->width(), nodep->fvarp()->width()); + if (nodep->fvarp()) { + nodep->width(nodep->fvarp()->width(), nodep->fvarp()->width()); + } } } virtual void visit(AstFuncRef* nodep, AstNUser* vup) { diff --git a/src/Verilator.cpp b/src/Verilator.cpp index 7b2bca4cc..2dcd36a1e 100644 --- a/src/Verilator.cpp +++ b/src/Verilator.cpp @@ -507,6 +507,7 @@ void process () { V3EmitC::emitcInlines(); V3EmitC::emitcSyms(); V3EmitC::emitcTrace(); + V3EmitC::emitcDpi(); } // Unfortunately we have some lint checks in emitc. V3EmitC::emitc(); diff --git a/src/verilog.l b/src/verilog.l index bba6672a2..bc5b70579 100644 --- a/src/verilog.l +++ b/src/verilog.l @@ -369,11 +369,13 @@ escid \\[^ \t\f\r\n]+ "byte" { FL; return yBYTE; } "chandle" { FL; return yCHANDLE; } "clocking" { FL; return yCLOCKING; } + "context" { FL; return yCONTEXT; } "do" { FL; return yDO; } "endclocking" { FL; return yENDCLOCKING; } "endpackage" { FL; return yENDPACKAGE; } "endprogram" { FL; return yENDPROGRAM; } "endproperty" { FL; return yENDPROPERTY; } + "export" { FL; return yEXPORT; } "final" { FL; return yFINAL; } "iff" { FL; return yIFF; } "import" { FL; return yIMPORT; } @@ -383,6 +385,7 @@ escid \\[^ \t\f\r\n]+ "package" { FL; return yPACKAGE; } "priority" { FL; return yPRIORITY; } "program" { FL; return yPROGRAM; } + "pure" { FL; return yPURE; } "shortint" { FL; return ySHORTINT; } "static" { FL; return ySTATIC; } "timeprecision" { FL; return yTIMEPRECISION; } @@ -401,7 +404,6 @@ escid \\[^ \t\f\r\n]+ "break" { yyerrorf("Unsupported: SystemVerilog 2005 reserved word not implemented: %s",yytext); } "class" { yyerrorf("Unsupported: SystemVerilog 2005 reserved word not implemented: %s",yytext); } "constraint" { yyerrorf("Unsupported: SystemVerilog 2005 reserved word not implemented: %s",yytext); } - "context" { yyerrorf("Unsupported: SystemVerilog 2005 reserved word not implemented: %s",yytext); } "continue" { yyerrorf("Unsupported: SystemVerilog 2005 reserved word not implemented: %s",yytext); } "covergroup" { yyerrorf("Unsupported: SystemVerilog 2005 reserved word not implemented: %s",yytext); } "coverpoint" { yyerrorf("Unsupported: SystemVerilog 2005 reserved word not implemented: %s",yytext); } @@ -413,7 +415,6 @@ escid \\[^ \t\f\r\n]+ "endsequence" { yyerrorf("Unsupported: SystemVerilog 2005 reserved word not implemented: %s",yytext); } "enum" { yyerrorf("Unsupported: SystemVerilog 2005 reserved word not implemented: %s",yytext); } "expect" { yyerrorf("Unsupported: SystemVerilog 2005 reserved word not implemented: %s",yytext); } - "export" { yyerrorf("Unsupported: SystemVerilog 2005 reserved word not implemented: %s",yytext); } "extends" { yyerrorf("Unsupported: SystemVerilog 2005 reserved word not implemented: %s",yytext); } "extern" { yyerrorf("Unsupported: SystemVerilog 2005 reserved word not implemented: %s",yytext); } "first_match" { yyerrorf("Unsupported: SystemVerilog 2005 reserved word not implemented: %s",yytext); } @@ -433,7 +434,6 @@ escid \\[^ \t\f\r\n]+ "null" { yyerrorf("Unsupported: SystemVerilog 2005 reserved word not implemented: %s",yytext); } "packed" { yyerrorf("Unsupported: SystemVerilog 2005 reserved word not implemented: %s",yytext); } "protected" { yyerrorf("Unsupported: SystemVerilog 2005 reserved word not implemented: %s",yytext); } - "pure" { yyerrorf("Unsupported: SystemVerilog 2005 reserved word not implemented: %s",yytext); } "rand" { yyerrorf("Unsupported: SystemVerilog 2005 reserved word not implemented: %s",yytext); } "randc" { yyerrorf("Unsupported: SystemVerilog 2005 reserved word not implemented: %s",yytext); } "randcase" { yyerrorf("Unsupported: SystemVerilog 2005 reserved word not implemented: %s",yytext); } diff --git a/src/verilog.y b/src/verilog.y index 416bfaf46..7b80f42c6 100644 --- a/src/verilog.y +++ b/src/verilog.y @@ -131,6 +131,9 @@ public: } } string deQuote(FileLine* fileline, string text); + void checkDpiVer(FileLine* fileline, const string& str) { + if (str != "DPI-C") { fileline->v3error("Unsupported DPI type '"< yCASEZ "casez" %token yCHANDLE "chandle" %token yCLOCKING "clocking" +%token yCONTEXT "context" %token yCOVER "cover" %token yDEFAULT "default" %token yDEFPARAM "defparam" @@ -269,6 +273,7 @@ class AstSenTree; %token yENDSPECIFY "endspecify" %token yENDTABLE "endtable" %token yENDTASK "endtask" +%token yEXPORT "export" %token yFINAL "final" %token yFOR "for" %token yFOREVER "forever" @@ -304,6 +309,7 @@ class AstSenTree; %token yPROPERTY "property" %token yPULLDOWN "pulldown" %token yPULLUP "pullup" +%token yPURE "pure" %token yREG "reg" %token yREPEAT "repeat" %token ySCALARED "scalared" @@ -594,7 +600,7 @@ package_or_generate_item_declaration: // ==IEEE: package_or_generate_item | data_declaration { $$ = $1; } | task_declaration { $$ = $1; } | function_declaration { $$ = $1; } - //UNSUP dpi_import_export { $$ = $1; } + | dpi_import_export { $$ = $1; } //UNSUP extern_constraint_declaration { $$ = $1; } //UNSUP class_declaration { $$ = $1; } // // class_constructor_declaration is part of function_declaration @@ -1993,12 +1999,20 @@ task_declaration: // ==IEEE: task_declaration { $$ = $3; $$->addStmtsp($4); SYMP->popScope($$); } ; +task_prototype: // ==IEEE: task_prototype + yTASK taskId '(' tf_port_listE ')' { $$=$2; $$->addStmtsp($4); $$->prototype(true); SYMP->popScope($$); } + ; + function_declaration: // IEEE: function_declaration + function_body_declaration yFUNCTION lifetimeE funcId funcIsolateE tfGuts yENDFUNCTION endLabelE { $$ = $3; $3->attrIsolateAssign($4); $$->addStmtsp($5); SYMP->popScope($$); } ; +function_prototype: // IEEE: function_prototype + yFUNCTION funcId '(' tf_port_listE ')' { $$=$2; $$->addStmtsp($4); $$->prototype(true); SYMP->popScope($$); } + ; + funcIsolateE: /* empty */ { $$ = 0; } | yVL_ISOLATE_ASSIGNMENTS { $$ = 1; } @@ -2137,6 +2151,30 @@ parenE: // // method_call_root not needed, part of expr resolution // // What's left is below array_methodNoRoot +dpi_import_export: // ==IEEE: dpi_import_export + yIMPORT yaSTRING dpi_tf_import_propertyE dpi_importLabelE function_prototype ';' + { $$ = $5; if (*$4!="") $5->cname(*$4); $5->dpiContext($3==iprop_CONTEXT); $5->pure($3==iprop_PURE); + $5->dpiImport(true); GRAMMARP->checkDpiVer($1,*$2); v3Global.dpi(true); } + | yIMPORT yaSTRING dpi_tf_import_propertyE dpi_importLabelE task_prototype ';' + { $$ = $5; if (*$4!="") $5->cname(*$4); $5->dpiContext($3==iprop_CONTEXT); $5->pure($3==iprop_PURE); + $5->dpiImport(true); $5->dpiTask(true); GRAMMARP->checkDpiVer($1,*$2); v3Global.dpi(true); } + | yEXPORT yaSTRING dpi_importLabelE yFUNCTION idAny ';' { $$ = new AstDpiExport($1,*$5,*$3); + GRAMMARP->checkDpiVer($1,*$2); v3Global.dpi(true); } + | yEXPORT yaSTRING dpi_importLabelE yTASK idAny ';' { $$ = new AstDpiExport($1,*$5,*$3); + GRAMMARP->checkDpiVer($1,*$2); v3Global.dpi(true); } + ; + +dpi_importLabelE: // IEEE: part of dpi_import_export + /* empty */ { static string s = ""; $$ = &s; } + | idAny/*c_identifier*/ '=' { $$ = $1; $$=$1; } + ; + +dpi_tf_import_propertyE: // IEEE: [ dpi_function_import_property + dpi_task_import_property ] + /* empty */ { $$ = iprop_NONE; } + | yCONTEXT { $$ = iprop_CONTEXT; } + | yPURE { $$ = iprop_PURE; } + ; + //************************************************ // Expressions // diff --git a/test_regress/Makefile_obj b/test_regress/Makefile_obj index 18de0a740..cbdb78b7d 100644 --- a/test_regress/Makefile_obj +++ b/test_regress/Makefile_obj @@ -32,6 +32,9 @@ endif ####################################################################### +# Needed by DPI tests +CPPFLAGS += -DVERILATOR=1 + # Needed by tracing routines CPPFLAGS += -DVL_DEBUG=1 CPPFLAGS += -DVM_PREFIX=$(VM_PREFIX) diff --git a/test_regress/t/t_dpi_dup_bad.pl b/test_regress/t/t_dpi_dup_bad.pl new file mode 100755 index 000000000..97d32f718 --- /dev/null +++ b/test_regress/t/t_dpi_dup_bad.pl @@ -0,0 +1,21 @@ +#!/usr/bin/perl +if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; } +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2003 by Wilson Snyder. 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. + +compile ( + v_flags2 => ["--lint-only"], + fails=>$Self->{v3}, + expect=> +'%Error: t/t_dpi_dup_bad.v:\d+: Duplicate declaration of DPI function with different formal arguments: v.oth_f_int2 +%Error: t/t_dpi_dup_bad.v:\d+: ... New prototype: pure int dpii_fa_bit \(int, int\) +%Error: t/t_dpi_dup_bad.v:\d+: ... Original prototype: int dpii_fa_bit \(int\) +%Error: Exiting due to .*' + ); + +ok(1); +1; diff --git a/test_regress/t/t_dpi_dup_bad.v b/test_regress/t/t_dpi_dup_bad.v new file mode 100644 index 000000000..8b59d29dd --- /dev/null +++ b/test_regress/t/t_dpi_dup_bad.v @@ -0,0 +1,18 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// Copyright 2009 by Wilson Snyder. 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. + +module t (); + + // Same name w/ different args + import "DPI-C" dpii_fa_bit = function int oth_f_int1(input int i); + import "DPI-C" pure dpii_fa_bit = function int oth_f_int2(input int i, input int bad); + + initial begin + $stop; + end + +endmodule diff --git a/test_regress/t/t_dpi_import.pl b/test_regress/t/t_dpi_import.pl new file mode 100755 index 000000000..708ef7b4a --- /dev/null +++ b/test_regress/t/t_dpi_import.pl @@ -0,0 +1,20 @@ +#!/usr/bin/perl +if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; } +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2003 by Wilson Snyder. 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. + +compile ( + # Amazingly VCS, NC and Verilator all just accept the C file here! + v_flags2 => ["t/t_dpi_import_c.cpp"], + ); + +execute ( + check_finished=>1, + ); + +ok(1); +1; diff --git a/test_regress/t/t_dpi_import.v b/test_regress/t/t_dpi_import.v new file mode 100644 index 000000000..fe77e8866 --- /dev/null +++ b/test_regress/t/t_dpi_import.v @@ -0,0 +1,144 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// Copyright 2009 by Wilson Snyder. 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. + +module t (); + + // Allowed import return types: + // void, byte, shortint, int, longint, real, shortreal, chandle, and string + // Scalar bit and logic + // + // Allowed argument types: + // Same as above plus packed arrays + + import "DPI-C" pure function bit dpii_f_bit (input bit i); + import "DPI-C" pure function bit [8-1:0] dpii_f_bit8 (input bit [8-1:0] i); + import "DPI-C" pure function bit [9-1:0] dpii_f_bit9 (input bit [9-1:0] i); + import "DPI-C" pure function bit [16-1:0] dpii_f_bit16 (input bit [16-1:0] i); + import "DPI-C" pure function bit [17-1:0] dpii_f_bit17 (input bit [17-1:0] i); + import "DPI-C" pure function bit [32-1:0] dpii_f_bit32 (input bit [32-1:0] i); + // Illegal to return > 32 bits, so we use longint + import "DPI-C" pure function longint dpii_f_bit33 (input bit [33-1:0] i); + import "DPI-C" pure function longint dpii_f_bit64 (input bit [64-1:0] i); + import "DPI-C" pure function int dpii_f_int (input int i); + import "DPI-C" pure function byte dpii_f_byte (input byte i); + import "DPI-C" pure function shortint dpii_f_shortint (input shortint i); + import "DPI-C" pure function longint dpii_f_longint (input longint i); + import "DPI-C" pure function chandle dpii_f_chandle (input chandle i); + //import "DPI-C" pure function real dpii_f_real (input real i); + //import "DPI-C" pure function shortreal dpii_f_shortreal (input shortreal i); + //import "DPI-C" pure function string dpii_f_string (input string i); + + import "DPI-C" pure function void dpii_v_bit (input bit i, output bit o); + import "DPI-C" pure function void dpii_v_int (input int i, output int o); + import "DPI-C" pure function void dpii_v_byte (input byte i, output byte o); + import "DPI-C" pure function void dpii_v_shortint (input shortint i, output shortint o); + import "DPI-C" pure function void dpii_v_longint (input longint i, output longint o); + import "DPI-C" pure function void dpii_v_chandle (input chandle i, output chandle o); + //import "DPI-C" pure function void dpii_v_real (input real i, output real o); + //import "DPI-C" pure function void dpii_v_shortreal(input shortreal i, output shortreal o); + //import "DPI-C" pure function void dpii_v_string (input string i, output string o); + + import "DPI-C" function void dpii_f_void (); + + // Try a task + import "DPI-C" task dpii_t_void (); + import "DPI-C" context task dpii_t_void_context (); + + import "DPI-C" task dpii_t_int (input int i, output int o); + + // Try non-pure, aliasing with name + import "DPI-C" dpii_fa_bit = function int oth_f_int1(input int i); + import "DPI-C" dpii_fa_bit = function int oth_f_int2(input int i); + + // Try context + import "DPI-C" context function int dpii_context(); + + bit i_b, o_b; + bit [7:0] i_b8, o_b8; + bit [8:0] i_b9, o_b9; + bit [15:0] i_b16, o_b16; + bit [16:0] i_b17, o_b17; + bit [31:0] i_b32, o_b32; + bit [32:0] i_b33, o_b33; + bit [63:0] i_b64, o_b64; + + int i_i, o_i; + byte i_y, o_y; + shortint i_s, o_s; + longint i_l, o_l; + chandle i_c, o_c; + + bit [127:0] wide; + + initial begin + wide = 128'h36f3e51d15caff7a73c48afee4ffcb57; + + i_b = 1'b1; + i_b8 = {1'b1,wide[8-2:0]}; + i_b9 = {1'b1,wide[9-2:0]}; + i_b16 = {1'b1,wide[16-2:0]}; + i_b17 = {1'b1,wide[17-2:0]}; + i_b32 = {1'b1,wide[32-2:0]}; + i_b33 = {1'b1,wide[33-2:0]}; + i_b64 = {1'b1,wide[64-2:0]}; + + i_i = {1'b1,wide[32-2:0]}; + i_y = {1'b1,wide[8-2:0]}; + i_s = {1'b1,wide[16-2:0]}; + i_l = {1'b1,wide[64-2:0]}; + + if (dpii_f_bit (i_b) !== ~i_b) $stop; + if (dpii_f_bit8 (i_b8) !== ~i_b8) $stop; + if (dpii_f_bit9 (i_b9) !== ~i_b9) $stop; + if (dpii_f_bit16 (i_b16) !== ~i_b16) $stop; + if (dpii_f_bit17 (i_b17) !== ~i_b17) $stop; + if (dpii_f_bit32 (i_b32) !== ~i_b32) $stop; + + // These return different sizes, so we need to truncate + // verilator lint_off WIDTH + o_b33 = dpii_f_bit33 (i_b33); + o_b64 = dpii_f_bit64 (i_b64); + // verilator lint_on WIDTH + if (o_b33 !== ~i_b33) $stop; + if (o_b64 !== ~i_b64) $stop; + + if (dpii_f_bit (i_b) !== ~i_b) $stop; + if (dpii_f_int (i_i) !== ~i_i) $stop; + if (dpii_f_byte (i_y) !== ~i_y) $stop; + if (dpii_f_shortint(i_s) !== ~i_s) $stop; + if (dpii_f_longint (i_l) !== ~i_l) $stop; + if (dpii_f_chandle (i_c) !== i_c) $stop; + + dpii_v_bit (i_b,o_b); if (o_b !== ~i_b) $stop; + dpii_v_int (i_i,o_i); if (o_i !== ~i_i) $stop; + dpii_v_byte (i_y,o_y); if (o_y !== ~i_y) $stop; + dpii_v_shortint(i_s,o_s); if (o_s !== ~i_s) $stop; + dpii_v_longint (i_l,o_l); if (o_l !== ~i_l) $stop; + dpii_v_chandle (i_c,o_c); if (o_c !== i_c) $stop; + + dpii_f_void(); + dpii_t_void(); + dpii_t_void_context(); + + i_i = 32'h456789ab; + dpii_t_int (i_i,o_i); if (o_b !== ~i_b) $stop; + + // Check alias + if (oth_f_int1(32'd123) !== ~32'd123) $stop; + if (oth_f_int2(32'd124) !== ~32'd124) $stop; + +`ifndef verilator // Not all sims support SV2009 `__LINE__, and some that do fail the specific-line test + if (!dpii_context()) $stop; +`else + //UNSUP if (dpii_context() !== `__LINE__) $stop; +`endif + + $write("*-* All Finished *-*\n"); + $finish; + end + +endmodule diff --git a/test_regress/t/t_dpi_import_c.cpp b/test_regress/t/t_dpi_import_c.cpp new file mode 100644 index 000000000..81830068b --- /dev/null +++ b/test_regress/t/t_dpi_import_c.cpp @@ -0,0 +1,121 @@ +// -*- C++ -*- +//************************************************************************* +// +// Copyright 2009-2009 by Wilson Snyder. 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. +// +// Verilator is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +//************************************************************************* + +#include +#include + +//====================================================================== + +#if defined(VERILATOR) +# include "Vt_dpi_import__Dpi.h" +#elif defined(VCS) +# include "../vc_hdrs.h" +#elif defined(CADENCE) +# define NEED_EXTERNS +#else +# error "Unknown simulator for DPI test" +#endif + +#ifdef NEED_EXTERNS +extern "C" { + + extern unsigned char dpii_f_bit (unsigned char i); + extern svBitVecVal dpii_f_bit8 (const svBitVecVal *i); + extern svBitVecVal dpii_f_bit9 (const svBitVecVal *i); + extern svBitVecVal dpii_f_bit16 (const svBitVecVal *i); + extern svBitVecVal dpii_f_bit17 (const svBitVecVal *i); + extern svBitVecVal dpii_f_bit32 (const svBitVecVal *i); + extern long long dpii_f_bit33 (const svBitVecVal *i); + extern long long dpii_f_bit64 (const svBitVecVal *i); + extern int dpii_f_int (int i); + extern char dpii_f_byte (char i); + extern short int dpii_f_shortint(short int i); + extern long long dpii_f_longint (long long i); + extern void* dpii_f_chandle (void* i); + + extern void dpii_v_bit (unsigned char i, unsigned char *o); + extern void dpii_v_int (int i, int *o); + extern void dpii_v_byte (char i, char *o); + extern void dpii_v_shortint (short int i, short int *o); + extern void dpii_v_longint (long long i, long long *o); + extern void dpii_v_chandle (void* i, void* *o); + + extern void dpii_f_void (); + extern int dpii_t_void (); + extern int dpii_t_void_context (); + extern int dpii_t_int (int i, int *o); + + extern int dpii_fa_bit(int i); + + extern int dpii_context(); +} +#endif + +//====================================================================== + +unsigned char dpii_f_bit (unsigned char i) { return SV_MASK(1) & ~i; } +svBitVecVal dpii_f_bit8 (const svBitVecVal *i) { return SV_MASK(8) & ~*i; } +svBitVecVal dpii_f_bit9 (const svBitVecVal *i) { return SV_MASK(9) & ~*i; } +svBitVecVal dpii_f_bit16(const svBitVecVal *i) { return SV_MASK(16) & ~*i; } +svBitVecVal dpii_f_bit17(const svBitVecVal *i) { return SV_MASK(17) & ~*i; } +svBitVecVal dpii_f_bit32(const svBitVecVal *i) { return ~*i; } +long long dpii_f_bit33(const svBitVecVal *i) { return ((1ULL<<33)-1) & ~((long long)(i[1])<<32ULL | i[0]); } +long long dpii_f_bit64(const svBitVecVal *i) { return ~((long long)(i[1])<<32ULL | i[0]); } + +int dpii_f_int (int i) { return ~i; } +char dpii_f_byte (char i) { return ~i; } +short int dpii_f_shortint(short int i) { return ~i; } +long long dpii_f_longint (long long i) { return ~i; } +void* dpii_f_chandle (void* i) { return i; } + +void dpii_v_bit (unsigned char i, unsigned char *o) { *o = SV_MASK(1) & ~i; } +void dpii_v_int (int i, int *o) { *o = ~i; } +void dpii_v_byte (char i, char *o) { *o = ~i; } +void dpii_v_shortint (short int i, short int *o) { *o = ~i; } +void dpii_v_longint (long long i, long long *o) { *o = ~i; } +void dpii_v_chandle (void* i, void* *o) { *o = i; } + +//====================================================================== + +void dpii_f_void () {} + +#ifdef VCS +void dpii_t_void () {} +void dpii_t_void_context () {} +void dpii_t_int (int i, int *o) { + *o = i; +} +#else +int dpii_t_void () { return svIsDisabledState(); } +int dpii_t_void_context () { return svIsDisabledState(); } +int dpii_t_int (int i, int *o) { + *o = i; + return svIsDisabledState(); // Tasks generally need this +} +#endif + +int dpii_fa_bit (int i) { + return ~i; +} + +int dpii_context() { + const char* filename = ""; + int lineno = 0; + if (svGetCallerInfo(&filename, &lineno)) { + return lineno; + } else { + return 0; + } +} diff --git a/test_regress/t/t_dpi_logic_bad.pl b/test_regress/t/t_dpi_logic_bad.pl new file mode 100755 index 000000000..e23755ee4 --- /dev/null +++ b/test_regress/t/t_dpi_logic_bad.pl @@ -0,0 +1,20 @@ +#!/usr/bin/perl +if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; } +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2003 by Wilson Snyder. 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. + +compile ( + v_flags2 => ["--lint-only"], + fails=>$Self->{v3}, + expect=> +'%Error: t/t_dpi_logic_bad.v:\d+: Unsupported: DPI argument of type .* +%Error: t/t_dpi_logic_bad.v:\d+: ... For best portability, use bit, byte, int, or longint +%Error: Exiting due to .*' + ); + +ok(1); +1; diff --git a/test_regress/t/t_dpi_logic_bad.v b/test_regress/t/t_dpi_logic_bad.v new file mode 100644 index 000000000..9ff259396 --- /dev/null +++ b/test_regress/t/t_dpi_logic_bad.v @@ -0,0 +1,17 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// Copyright 2009 by Wilson Snyder. 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. + +module t (); + + // Can't handle logic (yet?) + import "DPI-C" dpii_fa_bit = function int oth_f_int1(input logic [2:0] i); + + initial begin + $stop; + end + +endmodule diff --git a/test_regress/t/t_dpi_name_bad.pl b/test_regress/t/t_dpi_name_bad.pl new file mode 100755 index 000000000..c39aad5bd --- /dev/null +++ b/test_regress/t/t_dpi_name_bad.pl @@ -0,0 +1,19 @@ +#!/usr/bin/perl +if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; } +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2003 by Wilson Snyder. 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. + +compile ( + v_flags2 => ["--lint-only"], + fails=>$Self->{v3}, + expect=> +'%Error: t/t_dpi_name_bad.v:\d+: DPI function has illegal characters in C identifier name: badly.named +%Error: Exiting due to .*' + ); + +ok(1); +1; diff --git a/test_regress/t/t_dpi_name_bad.v b/test_regress/t/t_dpi_name_bad.v new file mode 100644 index 000000000..33de52601 --- /dev/null +++ b/test_regress/t/t_dpi_name_bad.v @@ -0,0 +1,17 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// Copyright 2009 by Wilson Snyder. 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. + +module t (); + + // Can't handle logic (yet?) + import "DPI-C" function int \badly.named (int i); + + initial begin + $stop; + end + +endmodule