This repository demonstrates a co-simulation setup between an open source P-Spice simulator(NGSpice) and an RTL simulator(Questa).
Figure: Connecting Questa to NGSpice
For the demo an ADC component is implemented in Pspice and simulated by NGSpice embedded via the FLI. The input voltage and results are handled by a VHDL component. The demo uses Questa Base running under Windows11.
** from the ngspice website: **
NGSpice is the open source spice simulator for electric and electronic circuits. Such a circuit may comprise of JFETs, bipolar and MOS transistors, passive elements like R, L, or C, diodes, transmission lines and other devices, all interconnected in a netlist. Digital circuits are simulated as well, event driven and fast, from single gates to complex circuits. And you may enter the combination of both analog and digital as a mixed-signal circuit. ngspice offers a wealth of device models for active, passive, analog, and digital elements. Model parameters are provided by our collections, by the semiconductor device manufacturers, or from semiconductor foundries. The user adds her circuits as a netlist, and the output is one or more graphs of currents, voltages and other electrical quantities.
For this demo I used a pre-compiled NGSpice windows library (file ngspice-45.2_dll_64.7z) as I failed to compile the library with Visual Studio (win_bison gave errors). Compiling under mingw64/msys worked fine but this adds more steps for those that don't have mingw64/msys installed.
For the obvious security reasons I would recommend not using the supplied DLL files but compile your own (see ngspice documentation for instructions).
The VHDL ADC component is shown below. The START signal starts the Pspice conversion and a DONE signals when the result is available. I use an 8bits DBUSI for the input voltage and DBUSO for the binary results.
The NGSpice circuit file path is specified by the CIRFILE generic. This circuit file path is read by the FLI interface and loaded into NGSpice during elaboration. Note that you need to use a Windows path (with double back slashes) if the file is not located in the current simulation working directory.
entity adc_fli is
generic(
CIRFILE : string := ".\\model\\adc.cir");
port(
CLK : in std_logic;
RESETN : in std_logic; -- async active low reset
DBUSI : in std_logic_vector(7 downto 0); -- vin voltage 0..3.3v in 256 steps
START : in std_logic;
DBUSO : out std_logic_vector(7 downto 0); -- ngspice adc results
DONE : out std_logic);
end entity adc_fli;
architecture rtl of adc_fli is
attribute foreign : string;
attribute foreign of rtl: architecture is "cif_init ./ngspice_fli.dll";
begin
end rtl;To compile the FLI DLL first navigate to the FLI directory and open ngspice_fli.c, around line 157/158 correct the path to the 2 coremodels as shown below.
ngSpice_Command("codemodel H:\\GitHub\\Questa_NGSpice\\Spice64_dll\\lib\\ngspice\\analog.cm");
ngSpice_Command("codemodel H:\\GitHub\\Questa_NGSpice\\Spice64_dll\\lib\\ngspice\\digital.cm");For some reason absolute path can not be used in ngspice, forward slashes are also not accepted.
After making the modifications run build.bat. This batch file uses the gcc compiler as supplied with Questa but other compilers from mingw64 or Visual Studio (different CLI) should work as well. The resulting dll (ngspice_fli.dll) is written to the sim directory.
The sim directory also contains the NGSpice ngspice.dll and libomp140.x86_64.dll files which are copied from the Spice64_dll directory (created by ngspice-45.2_dll_64.7z) so no search path adjustment is required.
The underlying pspice model for the ADC (adc.cir) is shown below, this is AI generated (Anthropic Claude) as I have no knowledge on how to write pspice code.
The Vin value is driven by the DBUSI signal using the formula Vin=(DBUSI/256)*3.3. The output signal (bits) are extracted from the NGSpice results which are written to stdout via the ng_getchar() function.
* =========================================================
* 8-bit Flash ADC with MOSFET Sample & Hold
* =========================================================
* ========= SUPPLY =========
Vdd vdd 0 DC 5
* ========= INPUT =========
Vin vin 0 DC 1.60 ; sample input
* ========= CLOCK =========
Vclk clk 0 PULSE(0 5 10n 1n 1n 500n 1u)
Vclkb clkb 0 PULSE(5 0 10n 1n 1n 500n 1u)
* ========= SAMPLE & HOLD =========
* Transmission gate sample switch
Mn_sw vin_sampled clk vin 0 NMOS L=0.18u W=3u
Mp_sw vin_sampled clkb vin vdd PMOS L=0.18u W=6u
* Hold capacitor and DC bleed path
Chold vin_sampled 0 1p
Rbleed vin_sampled 0 1G
* ========= REFERENCE LEVELS =========
Vref7 vref7 0 DC 3.0
Vref6 vref6 0 DC 2.5
Vref5 vref5 0 DC 2.0
Vref4 vref4 0 DC 1.5
Vref3 vref3 0 DC 1.0
Vref2 vref2 0 DC 0.5
Vref1 vref1 0 DC 0.25
Vref0 vref0 0 DC 0.125
* ========= MOSFET MODELS =========
.model NMOS NMOS (LEVEL=1 VTO=0.7 KP=120u)
.model PMOS PMOS (LEVEL=1 VTO=-0.7 KP=40u)
* ========= BEHAVIORAL COMPARATORS =========
B_d7 d7 0 V={1*(V(vin_sampled) > V(vref7))}
B_d6 d6 0 V={1*(V(vin_sampled) > V(vref6))}
B_d5 d5 0 V={1*(V(vin_sampled) > V(vref5))}
B_d4 d4 0 V={1*(V(vin_sampled) > V(vref4))}
B_d3 d3 0 V={1*(V(vin_sampled) > V(vref3))}
B_d2 d2 0 V={1*(V(vin_sampled) > V(vref2))}
B_d1 d1 0 V={1*(V(vin_sampled) > V(vref1))}
B_d0 d0 0 V={1*(V(vin_sampled) > V(vref0))}
* ========= SIMULATION CONTROL =========
.tran 10n 5u
* Print outputs for C interface
.print tran v(d7) v(d6) v(d5) v(d4) v(d3) v(d2) v(d1) v(d0)
.control
run
print v(d7) v(d6) v(d5) v(d4) v(d3) v(d2) v(d1) v(d0)
.endc
.endTo run the demo navigate to the SIM directory and execute run.bat in a CMD prompt. The output is shown below (stripped). Before the simulation starts NGSpice prints the full transfer table, I have not yet found an elegant way to suppress this other than using text filtering in ng_getchar().
The testbench is very basic, it simply applies two input voltages (0.258/2.991) and display the resulting decimal value d7..d0 (3/127).
# ** Note: (vsim-3812) Design is being optimized...
# // QuestaSim Base Edition-64
# // Version 2025.1 win64 Jan 17 2025
# Loading work.adc_fli(rtl)#1
# Loading ./ngspice_fli.dll
# Starting NGSPICE FLI
stdout ******
stdout ** ngspice-45.2 shared library
stdout ** Creation Date: Sep 6 2025 16:59:47
stdout ******
stdout ******
stdout ** ngspice-45.2 : Circuit level simulation program
stdout ** Compiled with KLU Direct Linear Solver
stdout ** The U. C. Berkeley CAD Group
stdout ** Copyright 1985-1994, Regents of the University of California.
stdout ** Copyright 2001-2025, The ngspice team.
stdout ** Please get your ngspice manual from https://ngspice.sourceforge.io/docs.html
stdout ** Please file your bug-reports at https://ngspice.sourceforge.io/bugrep.html
stdout ** Creation Date: Sep 6 2025 16:59:47
stdout ******
# Generic: cirfile = adc.cir
stdout Note: No compatibility mode selected!
stdout Circuit: * =========================================================
stdout Doing analysis at TEMP = 27.000000 and TNOM = 27.000000
stdout Using SPARSE 1.3 as Direct Linear Solver
stdout Initial Transient Solution
stdout --------------------------
stdout Node Voltage
stdout ---- -------
stdout vdd 5
stdout vin 1.6
stdout clk 0
stdout clkb 5
stdout vin_sampled 0.00499825
stdout vref7 3
stdout vref6 2.5
stdout vref5 2
stdout vref4 1.5
stdout vref3 1
stdout vref2 0.5
stdout vref1 0.25
stdout vref0 0.125
stdout vclkb#branch 0
stdout vclk#branch 0
stdout vin#branch 1.8e-12
stdout vdd#branch -8.415e-12
stdout No. of Data Rows : 611
stdout * =========================================================
stdout Transient Analysis Sun Oct 12 16:36:36 2025
stdout --------------------------------------------------------------------------------
stdout Index time v(d7) v(d6) v(d5)
stdout --------------------------------------------------------------------------------
stdout 0 0.000000e+00 0.000000e+00 0.000000e+00 0.000000e+00
stdout 1 1.000000e-10 0.000000e+00 0.000000e+00 0.000000e+00
..snip
stdout Reset re-loads circuit * =========================================================
stdout Circuit: * =========================================================
# *** alter Vin = 2.991 ***
stdout Doing analysis at TEMP = 27.000000 and TNOM = 27.000000
stdout Using SPARSE 1.3 as Direct Linear Solver
stdout Initial Transient Solution
stdout --------------------------
stdout Node Voltage
stdout ---- -------
stdout vdd 5
stdout vin 2.991
stdout clk 0
stdout clkb 5
stdout vin_sampled 0.00499825
stdout vref7 3
stdout vref6 2.5
stdout vref5 2
stdout vref4 1.5
stdout vref3 1
stdout vref2 0.5
stdout vref1 0.25
stdout vref0 0.125
stdout vclkb#branch 0
stdout vclk#branch 0
stdout vin#branch -9.82e-13
stdout vdd#branch -7.024e-12
stdout No. of Data Rows : 2035
stdout d7[1500] = 0.000000e+00
stdout d6[1500] = 1.000000e+00
stdout d5[1500] = 1.000000e+00
stdout d4[1500] = 1.000000e+00
stdout d3[1500] = 1.000000e+00
stdout d2[1500] = 1.000000e+00
stdout d1[1500] = 1.000000e+00
stdout d0[1500] = 1.000000e+00
#
# *** Results: 127 ***
stdout Reset re-loads circuit * =========================================================
stdout Circuit: * =========================================================
# Cleaning up.....
# End time: 16:36:36 on Oct 12,2025, Elapsed time: 0:00:02
# Errors: 0, Warnings: 0- The current NGSpice setup is blocking (ngSpice_Command("tran..")), this means that it blocks Questa until the result is available. There is a non-blocking option (ngSpice_Command("bg_run..")) which runs the simulation in a background thread. This should be used if you have long simulations runs. The callback can be hooked up to the FLI.
- For serious/commercial analog simulation work I would recommend Questa ADMS which merges Eldo(analog sim) with Questa(digital sim), this gives the user access not only access to a very fast analog simulator but also other easier(?) input languages such as Verilog-AMS/VHDL-AMS.
See the LICENSE file for details for this demo. NGSpice code is (with few exceptions) licensed according to the new (modified) BSD license.
All logos, trademarks and graphics used herein are the property of their respective owners.
