Add support for Verilator's --timing option, allowing use of delays
in Verilog source code. Also add two parameters to d_cosim: sim_args is used to pass string arguments to a Verilator simulation; and lib_args is for future use. In vlnggen, also check for two causes of failure: a verilator error may lead to creation of interfering header files; and misleading instances of verilated_shim.cpp can cause an obscure failure (reported by Diarmuid Collins). Use a generic name for the generated DLL in MSVC.CMD.
This commit is contained in:
parent
4481531baf
commit
e201f144d5
|
|
@ -1,4 +1,6 @@
|
|||
/* Header file for the shim code between d_cosim and a co-simulator. */
|
||||
/* Header file for the shim code between XSPICE and a co-simulator
|
||||
* attached by the d_cosim code model.
|
||||
*/
|
||||
|
||||
#if __cplusplus
|
||||
extern "C" {
|
||||
|
|
@ -12,7 +14,7 @@ extern "C" {
|
|||
* so step() must be called after input.
|
||||
*/
|
||||
|
||||
typedef enum {Normal, After_input} Cosim_method;
|
||||
typedef enum {Normal, After_input, Both} Cosim_method;
|
||||
|
||||
/* Structure used by Cosim_setup() to pass and return
|
||||
* co-simulation interface information.
|
||||
|
|
@ -26,7 +28,8 @@ struct co_info {
|
|||
unsigned int inout_count;
|
||||
|
||||
/* The co-simulator may specify a function to be called just before
|
||||
* it is unloaded at the end of a simulation run.
|
||||
* it is unloaded at the end of a simulation run. It should not free
|
||||
* this structure.
|
||||
*/
|
||||
|
||||
void (*cleanup)(struct co_info *);
|
||||
|
|
@ -59,8 +62,17 @@ struct co_info {
|
|||
|
||||
void (*out_fn)(struct co_info *, unsigned int, Digital_t *);
|
||||
void *handle; // Co-simulator's private handle
|
||||
double vtime; // Time in the co-simulation.
|
||||
volatile double vtime; // Time in the co-simulation.
|
||||
Cosim_method method; // May be set in Cosim_setup;
|
||||
|
||||
/* Arguments for the co-simulator shim and the simulation itself
|
||||
* are taken from parameters in the .model card.
|
||||
*/
|
||||
|
||||
int lib_argc;
|
||||
int sim_argc;
|
||||
const char * const * const lib_argv;
|
||||
const char * const * const sim_argv;
|
||||
};
|
||||
|
||||
extern void Cosim_setup(struct co_info *pinfo); // This must exist.
|
||||
|
|
|
|||
|
|
@ -39,7 +39,7 @@ char *dlerror(void) // Lifted from dev.c.
|
|||
if (rc == 0) { /* FormatMessage failed */
|
||||
(void) sprintf(errstr, errstr_fmt, (unsigned long) GetLastError());
|
||||
} else {
|
||||
snprintf(errstr, sizeof errstr, errstr_fmt, lpMsgBuf);
|
||||
snprintf(errstr, sizeof errstr, "%s", lpMsgBuf);
|
||||
LocalFree(lpMsgBuf);
|
||||
}
|
||||
return errstr;
|
||||
|
|
@ -94,6 +94,10 @@ static void callback(ARGS, Mif_Callback_Reason_t reason)
|
|||
return;
|
||||
if (ip->info.cleanup)
|
||||
(*ip->info.cleanup)(&ip->info);
|
||||
if (ip->info.lib_argv)
|
||||
free((void *)ip->info.lib_argv);
|
||||
if (ip->info.sim_argv)
|
||||
free((void *)ip->info.sim_argv);
|
||||
if (ip->so_handle)
|
||||
dlclose(ip->so_handle);
|
||||
if (ip->q)
|
||||
|
|
@ -232,8 +236,9 @@ static int advance(struct instance *ip, ARGS)
|
|||
* Try and recover: this may lead to a warning on output.
|
||||
*/
|
||||
|
||||
DBG("Attempting recovey from failed timestep lop %.16g->%.16g",
|
||||
TIME, when);
|
||||
DBG("Attempting recovery from failed timestep truncation: %s "
|
||||
"%.16g->%.16g",
|
||||
cm_message_get_errmsg(), TIME, when);
|
||||
delta = (TIME - ip->info.vtime) / 1000;
|
||||
when = ip->info.vtime;
|
||||
if (when < ip->last_step) {
|
||||
|
|
@ -298,7 +303,8 @@ static void run(struct instance *ip, ARGS)
|
|||
/* Step the simulation forward to the input event time. */
|
||||
|
||||
ip->info.vtime = rp->when;
|
||||
if (ip->info.method == Normal && advance(ip, XSPICE_ARG)) {
|
||||
if ((ip->info.method == Normal || ip->info.method == Both) &&
|
||||
advance(ip, XSPICE_ARG)) {
|
||||
ip->q_index = -1;
|
||||
return;
|
||||
}
|
||||
|
|
@ -316,7 +322,8 @@ static void run(struct instance *ip, ARGS)
|
|||
|
||||
/* Simulator requested to run after input change. */
|
||||
|
||||
if (ip->info.method == After_input && advance(ip, XSPICE_ARG)) {
|
||||
if ((ip->info.method == After_input || ip->info.method == Both) &&
|
||||
advance(ip, XSPICE_ARG)) {
|
||||
ip->q_index = -1;
|
||||
return;
|
||||
}
|
||||
|
|
@ -427,7 +434,39 @@ void ucm_d_cosim(ARGS)
|
|||
ip->info.out_fn = accept_output;
|
||||
CALLBACK = callback;
|
||||
|
||||
/* Store the simulation interface information. */
|
||||
if (PARAM_NULL(lib_args)) {
|
||||
ip->info.lib_argc = 0;
|
||||
*(void **)&ip->info.lib_argv = NULL;
|
||||
} else {
|
||||
char **args;
|
||||
|
||||
ip->info.lib_argc = PARAM_SIZE(lib_args);
|
||||
args = malloc((ip->info.lib_argc + 1) * sizeof (char *));
|
||||
if (args) {
|
||||
for (i = 0; i < ip->info.lib_argc; ++i)
|
||||
args[i] = PARAM(lib_args[i]);
|
||||
args[i] = NULL;
|
||||
}
|
||||
*(char ***)&ip->info.lib_argv = args;
|
||||
}
|
||||
|
||||
if (PARAM_NULL(sim_args)) {
|
||||
ip->info.sim_argc = 0;
|
||||
*(void **)&ip->info.sim_argv = NULL;
|
||||
} else {
|
||||
char **args;
|
||||
|
||||
ip->info.sim_argc = PARAM_SIZE(sim_args);
|
||||
args = malloc((ip->info.sim_argc + 1) * sizeof (char *));
|
||||
if (args) {
|
||||
for (i = 0; i < ip->info.sim_argc; ++i)
|
||||
args[i] = PARAM(sim_args[i]);
|
||||
args[i] = NULL;
|
||||
}
|
||||
*(char ***)&ip->info.sim_argv = args;
|
||||
}
|
||||
|
||||
/* Get the simulation interface information. */
|
||||
|
||||
(*ifp)(&ip->info);
|
||||
|
||||
|
|
@ -597,11 +636,21 @@ void ucm_d_cosim(ARGS)
|
|||
* forward, replaying any saved input events.
|
||||
*/
|
||||
|
||||
if (TIME <= ip->info.vtime)
|
||||
if (ip->last_step == 0.0) {
|
||||
/* First step. "Step" the co-simulation to time zero,
|
||||
* as that may trigger initialisations.
|
||||
*/
|
||||
|
||||
if (advance(ip, XSPICE_ARG))
|
||||
return;
|
||||
}
|
||||
|
||||
if (TIME <= ip->info.vtime) {
|
||||
cm_message_printf("XSPICE time is behind vtime:\n"
|
||||
"XSPICE %.16g\n"
|
||||
"Cosim %.16g",
|
||||
TIME, ip->info.vtime);
|
||||
}
|
||||
run(ip, XSPICE_ARG);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -67,6 +67,28 @@ Vector: no
|
|||
Vector_Bounds: -
|
||||
Null_Allowed: no
|
||||
|
||||
PARAMETER_TABLE:
|
||||
|
||||
Parameter_Name: lib_args
|
||||
Description: "Argument strings made available to the shared library"
|
||||
Data_Type: string
|
||||
Default_Value: -
|
||||
Limits: -
|
||||
Vector: yes
|
||||
Vector_Bounds: -
|
||||
Null_Allowed: yes
|
||||
|
||||
PARAMETER_TABLE:
|
||||
|
||||
Parameter_Name: sim_args
|
||||
Description: "Argument strings made available to the simulation"
|
||||
Data_Type: string
|
||||
Default_Value: -
|
||||
Limits: -
|
||||
Vector: yes
|
||||
Vector_Bounds: -
|
||||
Null_Allowed: yes
|
||||
|
||||
/* Instances maintain an internal input event queue that should be at least
|
||||
* as large as the number of inputs. Performance with clocked logic may
|
||||
* be improved by making it larger than (2 * F) / MTS, where F is
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
CL /O2 /LD /EHsc /Fe..\adc.DLL /I. /IC:\mingw64\share\verilator\include\vltstd /IC:\mingw64\share\verilator\include Vlng__ALL.cpp verilator_shim.cpp C:\mingw64\share\verilator\include\verilated.cpp C:\mingw64\share\verilator\include\verilated_threads.cpp /link /DLL /EXPORT:Cosim_setup
|
||||
CL /O2 /LD /EHsc /Fe..\Verilated.DLL /I. /IC:\mingw64\share\verilator\include\vltstd /IC:\mingw64\share\verilator\include Vlng__ALL.cpp verilator_shim.cpp C:\mingw64\share\verilator\include\verilated.cpp C:\mingw64\share\verilator\include\verilated_threads.cpp /link /DLL /EXPORT:Cosim_setup
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@
|
|||
#include "ngspice/cosim.h" // For struct co_info and prototypes
|
||||
|
||||
int main(int argc, char** argv, char**) {
|
||||
struct co_info info;
|
||||
struct co_info info = {};
|
||||
|
||||
Cosim_setup(&info);
|
||||
for (;;)
|
||||
|
|
|
|||
|
|
@ -93,6 +93,11 @@ static void accept_input(struct co_info *pinfo,
|
|||
|
||||
/* The step function that calls the Verilator code. */
|
||||
|
||||
#ifndef WITH_TIMING
|
||||
/* The simple case is when the Verilog source contained no time delays,
|
||||
* or was compiled with compiled with --no-timing.
|
||||
*/
|
||||
|
||||
#define VL_DATA(size, name, msb, lsb) \
|
||||
for (i = msb; i >= lsb; --i) { \
|
||||
if (topp->name & (1 << i)) \
|
||||
|
|
@ -106,7 +111,7 @@ static void accept_input(struct co_info *pinfo,
|
|||
} \
|
||||
++index; \
|
||||
}
|
||||
|
||||
|
||||
static void step(struct co_info *pinfo)
|
||||
{
|
||||
static Digital_t oval = {ZERO, STRONG};
|
||||
|
|
@ -123,6 +128,71 @@ static void step(struct co_info *pinfo)
|
|||
#include "outputs.h"
|
||||
#include "inouts.h"
|
||||
}
|
||||
|
||||
#else /* WITH_TIMING */
|
||||
|
||||
#define VL_DATA(size, name, msb, lsb) \
|
||||
for (i = msb; i >= lsb; --i) { \
|
||||
if (topp->name & (1 << i)) \
|
||||
bit = 1; \
|
||||
else \
|
||||
bit = 0; \
|
||||
if (bit ^ previous_output[index]) { \
|
||||
stop = 1; \
|
||||
previous_output[index] = bit; \
|
||||
oval.state = (Digital_State_t)bit; \
|
||||
(*pinfo->out_fn)(pinfo, index, &oval); \
|
||||
} \
|
||||
++index; \
|
||||
}
|
||||
|
||||
static void step(struct co_info *pinfo)
|
||||
{
|
||||
static Digital_t oval = {ZERO, STRONG};
|
||||
VerilatedContext *contextp;
|
||||
Vlng *topp;
|
||||
double tick;
|
||||
uint64_t target, next;
|
||||
int index, i, stop = 0;
|
||||
unsigned char bit;
|
||||
|
||||
/* When Verilog source was compiled with --timing, run queued evants
|
||||
* until the Verilog simulation catches up with SPICE.
|
||||
*/
|
||||
|
||||
topp = (Vlng *)pinfo->handle;
|
||||
contextp = topp->contextp();
|
||||
tick = pow(10, contextp->timeprecision());
|
||||
target = pinfo->vtime / tick;
|
||||
|
||||
/* Step the Verilog simulation towards the target time. */
|
||||
|
||||
do {
|
||||
if (topp->eventsPending())
|
||||
next = topp->nextTimeSlot();
|
||||
else
|
||||
next = target;
|
||||
if (next >= target) {
|
||||
stop = 1;
|
||||
next = target;
|
||||
}
|
||||
contextp->time(next);
|
||||
topp->eval();
|
||||
|
||||
/* Scan for output and stop early if found. */
|
||||
|
||||
index = 0;
|
||||
#include "outputs.h"
|
||||
#include "inouts.h"
|
||||
} while (!stop);
|
||||
|
||||
/* Update the shared simulation time on early exit. */
|
||||
|
||||
if (next < target)
|
||||
pinfo->vtime = next * tick;
|
||||
}
|
||||
|
||||
#endif /* WITH_TIMING */
|
||||
#undef VL_DATA
|
||||
|
||||
extern "C" void Cosim_setup(struct co_info *pinfo)
|
||||
|
|
@ -152,6 +222,10 @@ extern "C" void Cosim_setup(struct co_info *pinfo)
|
|||
pinfo->out_count = outs;
|
||||
pinfo->inout_count = inouts;
|
||||
pinfo->in_fn = accept_input;
|
||||
#ifdef WITH_TIMING
|
||||
pinfo->method = Both; // There may be immediate results from input.
|
||||
#else
|
||||
pinfo->method = After_input; // Verilator requires input to advance.
|
||||
#endif
|
||||
}
|
||||
#undef VL_DATA
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
*ng_script_with_params
|
||||
// This Ngspice interpreter script accepts arbitrary argiments to
|
||||
// This Ngspice interpreter script accepts arbitrary arguments to
|
||||
// the Verilator compiler (Verilog to C++) and builds a shared library
|
||||
// or DLL that can be loaded by the d_cosim XSPICE code model.
|
||||
// Instances of the model are then digital circuit elements whose
|
||||
|
|
@ -30,7 +30,7 @@ set noglob
|
|||
|
||||
// Compilation option for C/C++: -fpic is required by GCC for a shared library
|
||||
|
||||
if $oscompiled = 8 // VisualC++ - Verilator is a Perl script
|
||||
if $oscompiled = 8 // VisualC++
|
||||
setcs cflags="--CFLAGS -fpic --compiler msvc"
|
||||
else
|
||||
setcs cflags="--CFLAGS -fpic" // For g++
|
||||
|
|
@ -41,7 +41,7 @@ if $oscompiled = 2 | $oscompiled = 3 | $oscompiled = 8 // Windows
|
|||
set dirsep1="\\"
|
||||
set dirsep2="/"
|
||||
set vloc="C:/mingw64/bin/verilator" // Expected location on Windows
|
||||
set run_verilator="perl $vloc"
|
||||
set run_verilator="perl $vloc" // Verilator is a Perl script
|
||||
else
|
||||
set windows=0
|
||||
set dirsep1="/"
|
||||
|
|
@ -55,14 +55,32 @@ else
|
|||
set macos=0
|
||||
end
|
||||
|
||||
// Check for an input.h file in the current directory. If present it may
|
||||
// override the generated one with incorrect results. A previous failure
|
||||
// may create such files.
|
||||
|
||||
set silent_fileio
|
||||
fopen fh inputs.h
|
||||
if $fh >= 0
|
||||
echo File inputs.h (and any other header files) in current directory
|
||||
echo may interfere with compilation.
|
||||
quit
|
||||
end
|
||||
unset silent_fileio
|
||||
|
||||
// Loop through the arguments to find Verilog source: some_path/xxxx.v
|
||||
// The output file will have the same base name.
|
||||
|
||||
let index=1
|
||||
set off=1 // Avoid error in dowhile
|
||||
set timing=1
|
||||
repeat $argc
|
||||
set base="$argv[$&index]"
|
||||
let index = index + 1
|
||||
if $timing <> 0
|
||||
// Additional check for --timing option, preceeding any *.v files.
|
||||
strcmp timing "$base" "--timing"
|
||||
end
|
||||
strstr l "$base" ""
|
||||
if $l > 2 // Look for xxxx.v
|
||||
strslice tail "$base" -2 2
|
||||
|
|
@ -122,6 +140,9 @@ setcs prefix="Vlng"
|
|||
// Run Verilator on the given input files.
|
||||
|
||||
shell $run_verilator --Mdir $objdir --prefix $prefix $cflags --cc $argv
|
||||
if $shellstatus > 0
|
||||
quit
|
||||
end
|
||||
|
||||
// Parse the primary interface Class definition for members representing
|
||||
// the ports of the top-level Verilog module.
|
||||
|
|
@ -214,6 +235,8 @@ set shimfile=verilator_shim.cpp
|
|||
set shimobj=verilator_shim.o
|
||||
set mainfile=verilator_main.cpp
|
||||
set srcdir=src
|
||||
set hfile="cmtypes.h"
|
||||
set hpath="ngspice$dirsep1$hfile"
|
||||
set silent_fileio // Silences fopen complaints
|
||||
|
||||
let i=1
|
||||
|
|
@ -221,15 +244,22 @@ repeat $#sourcepath
|
|||
set stem="$sourcepath[$&i]"
|
||||
let i = i + 1
|
||||
set fn="$stem$dirsep1$shimfile"
|
||||
fopen fh $fn
|
||||
if $fh > 0
|
||||
break
|
||||
fopen fh "$fn"
|
||||
if $fh < 0
|
||||
// Look in any "src" subdirectory (probably in installed tree).
|
||||
set stem="$stem$dirsep1$srcdir"
|
||||
set fn="$stem$dirsep1$shimfile"
|
||||
fopen fh $fn
|
||||
end
|
||||
set stem="$stem$dirsep1$srcdir"
|
||||
set fn="$stem$dirsep1$shimfile"
|
||||
fopen fh $fn
|
||||
if $fh > 0
|
||||
break
|
||||
// Found verilator_shim.cpp, but it needs header files on relative path.
|
||||
fclose $fh
|
||||
set hn="$stem$dirsep1$hpath"
|
||||
fopen fh "$hn"
|
||||
if $fh > 0
|
||||
break
|
||||
end
|
||||
echo Ignoring source file "$fn" as "$hn" was not found.
|
||||
end
|
||||
end
|
||||
|
||||
|
|
@ -263,6 +293,11 @@ else
|
|||
end
|
||||
end
|
||||
|
||||
// verilator_shim.cpp has conditionally-compiled sections for --timing.
|
||||
|
||||
if $timing = 0
|
||||
setcs cflags="--CFLAGS -DWITH_TIMING ""$cflags"
|
||||
end
|
||||
|
||||
// Compile the code. Verilator only does that when building an executable,
|
||||
// so include verilator_main.cpp.
|
||||
|
|
@ -277,6 +312,9 @@ if $bad = 0
|
|||
// g++ must be available: make a shared library/DLL.
|
||||
|
||||
set v_objs="$objdir$dirsep1$shimobj $objdir/verilated.o $objdir/verilated_threads.o"
|
||||
if $timing = 0
|
||||
set v_objs="$v_objs $objdir/verilated_timing.o"
|
||||
end
|
||||
setcs tail="__ALL.a"
|
||||
setcs v_lib="$objdir/$prefix$tail" // Like Vlng__ALL.a
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue