Support direct programming interface (DPI) "import".

This commit is contained in:
Wilson Snyder 2009-12-03 06:55:29 -05:00
parent f8cb6979d7
commit a40fae04ce
33 changed files with 1639 additions and 124 deletions

View File

@ -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]

View File

@ -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 \

View File

@ -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?

367
include/svdpi.h Normal file
View File

@ -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 <stdint.h>
#elif defined(__linux)
#include <inttypes.h>
#else
#include <sys/types.h>
#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

289
include/verilateddpi.cpp Normal file
View File

@ -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
}

View File

@ -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<en>(_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 {

View File

@ -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="<<cname()<<"]";
}
void AstBegin::dump(ostream& str) {
this->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]";
}

View File

@ -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; }

View File

@ -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<AstCFunc*>::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__<<": "<<endl);
if (v3Global.opt.trace()) {
{ EmitCTrace trace (true); trace.main(); }
{ EmitCTrace trace (false); trace.main(); }
{ EmitCTrace imp (true); imp.main(); }
{ EmitCTrace imp (false); imp.main(); }
}
}
void V3EmitC::emitcDpi() {
UINFO(2,__FUNCTION__<<": "<<endl);
if (v3Global.dpi()) {
{ EmitCDpi imp; imp.main(); }
}
}

View File

@ -35,6 +35,7 @@ public:
static void emitcInlines();
static void emitcSyms();
static void emitcTrace();
static void emitcDpi();
};
#endif // Guard

View File

@ -78,6 +78,9 @@ public:
of.puts(" += \\\n");
if (support==2 && !slow) {
putMakeClassEntry(of, "verilated.cpp");
if (v3Global.dpi()) {
putMakeClassEntry(of, "verilateddpi.cpp");
}
if (v3Global.opt.systemPerl()) {
putMakeClassEntry(of, "Sp.cpp"); // Note Sp.cpp includes SpTraceVcdC
}

View File

@ -61,7 +61,8 @@ class EmitVBaseVisitor : public EmitCBaseVisitor {
puts("endmodule\n");
}
virtual void visit(AstNodeFTask* nodep, AstNUser*) {
putbs(nodep->castTask() ? "task ":"function ");
putbs(nodep->isFunction() ? "function":"task");
puts(" ");
puts(nodep->name());
puts(";\n");
putbs("begin\n");

View File

@ -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;

View File

@ -405,10 +405,21 @@ private:
nodep->funcp()->accept(*this);
}
}
virtual void visit(AstCFunc* nodep, AstNUser*) {
//UINFO(4," CCALL "<<nodep<<endl);
if (nodep->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*) {

View File

@ -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);

View File

@ -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;

View File

@ -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);

View File

@ -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 "<<nodep<<endl);
if (!m_params) { badNodeType(nodep); return; }
AstFunc* funcp = nodep->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()));
}
}

View File

@ -201,6 +201,7 @@ private:
UINFO(9," TASK "<<nodep<<endl);
TaskBaseVertex* lastVxp = m_curVxp;
m_curVxp = getFTaskVertex(nodep);
if (nodep->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<string,pair<AstCFunc*,string> > 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 "<<funcp->fvarp()<<" to "<<outvscp<<endl);
refp->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: "<<AstNode::prettyName(nodep->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 "<<portp->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: "<<nodep->prettyName());
nodep->v3error("... New prototype: "<<dpiproto);
iter->second.first->v3error("... Original prototype: "<<iter->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 "<<nodep<<endl);
if (debug()>=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 "<<vscp<<endl);
pushDeletep(vscp->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();

View File

@ -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 "<<nodep<<endl);
virtual void visit(AstNodeFTask* nodep, AstNUser* vup) {
// Grab width from the output variable (if it's a function)
UINFO(5," FTASK "<<nodep<<endl);
if (nodep->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) {

View File

@ -507,6 +507,7 @@ void process () {
V3EmitC::emitcInlines();
V3EmitC::emitcSyms();
V3EmitC::emitcTrace();
V3EmitC::emitcDpi();
}
// Unfortunately we have some lint checks in emitc.
V3EmitC::emitc();

View File

@ -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); }

View File

@ -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 '"<<str<<"': Use 'DPI-C'"); }
}
};
const AstBasicDTypeKwd LOGIC = AstBasicDTypeKwd::LOGIC; // Shorthand "LOGIC"
@ -250,6 +253,7 @@ class AstSenTree;
%token<fl> yCASEZ "casez"
%token<fl> yCHANDLE "chandle"
%token<fl> yCLOCKING "clocking"
%token<fl> yCONTEXT "context"
%token<fl> yCOVER "cover"
%token<fl> yDEFAULT "default"
%token<fl> yDEFPARAM "defparam"
@ -269,6 +273,7 @@ class AstSenTree;
%token<fl> yENDSPECIFY "endspecify"
%token<fl> yENDTABLE "endtable"
%token<fl> yENDTASK "endtask"
%token<fl> yEXPORT "export"
%token<fl> yFINAL "final"
%token<fl> yFOR "for"
%token<fl> yFOREVER "forever"
@ -304,6 +309,7 @@ class AstSenTree;
%token<fl> yPROPERTY "property"
%token<fl> yPULLDOWN "pulldown"
%token<fl> yPULLUP "pullup"
%token<fl> yPURE "pure"
%token<fl> yREG "reg"
%token<fl> yREPEAT "repeat"
%token<fl> ySCALARED "scalared"
@ -594,7 +600,7 @@ package_or_generate_item_declaration<nodep>: // ==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<ftaskp>: // ==IEEE: task_declaration
{ $$ = $3; $$->addStmtsp($4); SYMP->popScope($$); }
;
task_prototype<ftaskp>: // ==IEEE: task_prototype
yTASK taskId '(' tf_port_listE ')' { $$=$2; $$->addStmtsp($4); $$->prototype(true); SYMP->popScope($$); }
;
function_declaration<ftaskp>: // IEEE: function_declaration + function_body_declaration
yFUNCTION lifetimeE funcId funcIsolateE tfGuts yENDFUNCTION endLabelE
{ $$ = $3; $3->attrIsolateAssign($4); $$->addStmtsp($5);
SYMP->popScope($$); }
;
function_prototype<ftaskp>: // IEEE: function_prototype
yFUNCTION funcId '(' tf_port_listE ')' { $$=$2; $$->addStmtsp($4); $$->prototype(true); SYMP->popScope($$); }
;
funcIsolateE<cint>:
/* 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<nodep>: // ==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<strp>: // IEEE: part of dpi_import_export
/* empty */ { static string s = ""; $$ = &s; }
| idAny/*c_identifier*/ '=' { $$ = $1; $<fl>$=$<fl>1; }
;
dpi_tf_import_propertyE<iprop>: // IEEE: [ dpi_function_import_property + dpi_task_import_property ]
/* empty */ { $$ = iprop_NONE; }
| yCONTEXT { $$ = iprop_CONTEXT; }
| yPURE { $$ = iprop_PURE; }
;
//************************************************
// Expressions
//

View File

@ -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)

21
test_regress/t/t_dpi_dup_bad.pl Executable file
View File

@ -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;

View File

@ -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

20
test_regress/t/t_dpi_import.pl Executable file
View File

@ -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;

View File

@ -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

View File

@ -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 <stdio.h>
#include <svdpi.h>
//======================================================================
#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;
}
}

View File

@ -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;

View File

@ -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

View File

@ -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;

View File

@ -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