Presentation
The Spice format was developped at the University of California, Berkeley. This parser/driver consists in a subset of SPICE3 netlist format. (see http://en.wikipedia.org/wiki/SPICE for more informations).
Author
Damien Dupuis: damien.dupuis(at)lip6(.)fr
Stand alone database structure
The database consists in several objects:
Using the parser
Simply load an Spice netlist file using the static function SPICE::Circuit::readFromFile().
Using the driver
Using the driver is very simple, user has to create a SPICE::Circuit object and simply add others Spice objects like SPICE::Subckt or SPICE::Instance to it. Includes, libraries and parameters can also be added to SPICE::Circuit. Finally use the SPICE::Circuit::writeToFile() method to dump the database to file.
Examples
As said is the global presentation, VLSI SAPD project provides C++ libraries and Python modules for each supported format. In this section we present simple code examples to parse and drive a SPICE file using C++ or Python. The SPICE file considered describes a simple Miller OTA: OTA_miller.spi
* Single-ended two-stage amplifier
.PARAM CC_VALUE=2.8794pF
.PARAM L_VALUE=0.340e-6
.SUBCKT currentMirrorPMOS d1 d2 s1 s2 param: l_val=0.0 w_val=0.0 nf_val=1 aeq_val=100e-6 temp_val=27
MP3 d1 d1 s1 s1 psvt l=l_val wf={w_val/nf_val} nf=nf_val aeq=aeq_val tempsimu=temp_val
MP4 d2 d1 s2 s2 psvt l=l_val wf={w_val/nf_val} nf=nf_val aeq=aeq_val tempsimu=temp_val
.ENDS currentMirrorPMOS
.SUBCKT diffPairNMOS d1 d2 g1 g2 s b param: l_val=0.0 w_val=0.0 nf_val=1 aeq_val=100e-6 temp_val=27
MN1 d1 g1 s b nsvt l=l_val wf={w_val/nf_val} nf=nf_val aeq=aeq_val tempsimu=temp_val
MN2 d2 g2 s b nsvt l=l_val wf={w_val/nf_val} nf=nf_val aeq=aeq_val tempsimu=temp_val
.ENDS diffPairNMOS
XCM 1 2 vdd vdd currentMirrorPMOS l_val=L_VALUE w_val=3.889618e-06 nf_val=2
XDP 1 2 vim vip 3 vss diffPairNMOS l_val=L_VALUE w_val=7.683346e-07 nf_val=4
MP6 vout 2 vdd vdd psvt l_val=L_VALUE w_val=3.558995e-05 nf_val=20
MN5 3 4 vss vss nsvt l_val=L_VALUE w_val=2.536703e-06 nf_val=4
MN7 vout 4 vss vss nsvt l_val=L_VALUE w_val=1.069083e-05 nf_val=16
MN8 4 4 vss vss nsvt l_val=L_VALUE w_val=2.536703e-06 nf_val=4
CC1 vout 2 CC_VALUE
.END
All source codes are available in the examples
directory.
C++
Parser
The following code (parseSpice.cpp
) is an example of how to parse a SPICE file using C++ library.
#include <cstdlib>
#include <iostream>
#include <string>
#include <map>
#include <vector>
#include "vlsisapd/spice/Circuit.h"
#include "vlsisapd/spice/SpiceException.h"
#include "vlsisapd/spice/Sources.h"
#include "vlsisapd/spice/Subckt.h"
#include "vlsisapd/spice/Instances.h"
int main(int argc, char * argv[]) {
string file = "";
if (argc == 1)
file = "./OTA.cir";
else if (argc == 2)
file = argv[1];
else {
cerr << "Usage: parseSpice [filename]" << endl;
exit(1);
}
try {
circuit = SPICE::Circuit::readFromFile(file);
cerr << e.what() << endl;
exit(48);
}
cerr <<
"+ " << circuit->
getTitle() << endl;
if (includes.size()) {
cerr << "| + includes" << endl;
for (size_t i = 0 ; i < includes.size() ; i++)
cerr << "| | " << includes[i] << endl;
}
vector<pair<string, string> > libs = circuit->
getLibraries();
if (libs.size()) {
cerr << "| + libraries" << endl;
for (size_t i = 0 ; i < libs.size() ; i++)
cerr << "| | " << libs[i].first << " " << libs[i].second << endl;
}
if (params.size()) {
cerr << "| + parameters" << endl;
for (map<string, string>::const_iterator it = params.begin() ; it != params.end() ; ++it)
cerr << "| | " << (*it).first << " = " << (*it).second << endl;
}
if (opts.size()) {
cerr << "| + options" << endl;
for (map<string, string>::const_iterator it = opts.begin() ; it != opts.end() ; ++it)
cerr << "| | " << (*it).first << " = " << (*it).second << endl;
}
vector<SPICE::Source*> sources = circuit->
getSources();
if (sources.size()) {
cerr << "| + sources" << endl;
for (size_t i = 0 ; i < sources.size() ; i++) {
}
}
vector<SPICE::Subckt*> subs = circuit->
getSubckts();
if (subs.size()) {
cerr << "| + subckts" << endl;
for (size_t i = 0 ; i < subs.size() ; i++) {
cerr <<
"| | + " << sub->
getName();
cerr << " param:";
cerr << " " << (*it).first << "=" << (*it).second;
}
cerr << endl;
cerr <<
"| | | + " << inst->
getName();
if (dynamic_cast<SPICE::Mosfet*>(inst)) {
int k = 0;
if (k%6 == 0)
cerr << endl << "| | | | +";
cerr << " " << (*it).first << "=" << (*it).second;
}
} else if (dynamic_cast<SPICE::Resistor*>(inst)) {
} else if (dynamic_cast<SPICE::Capacitor*>(inst)) {
} else {
int l = 0;
if (l%6 == 0)
cerr << endl << "| | | | +";
cerr << " " << (*it).first << "=" << (*it).second;
}
}
cerr << endl;
}
}
}
if (insts.size()) {
cerr << "| + instances" << endl;
for (size_t i = 0 ; i < insts.size() ; i++) {
cerr <<
"| | + " << inst->
getName();
if (dynamic_cast<SPICE::Mosfet*>(inst)) {
int j = 0;
if (j%6 == 0)
cerr << endl << "| | | | +";
cerr << " " << (*it).first << "=" << (*it).second;
}
} else if (dynamic_cast<SPICE::Resistor*>(inst)) {
} else if (dynamic_cast<SPICE::Capacitor*>(inst)) {
} else {
int l = 0;
if (l%6 == 0)
cerr << endl << "| | | +";
cerr << " " << (*it).first << "=" << (*it).second;
}
}
cerr << endl;
}
}
return 0;
}
Driver
This C++ code (driveSpice.cpp
) generates an myOTA.spi file equivalent to the included one.
#include <string>
#include "vlsisapd/spice/Circuit.h"
#include "vlsisapd/spice/Subckt.h"
#include "vlsisapd/spice/Instances.h"
int main(int argc, char * argv[]) {
circuit->
setTitle(
"* Single-ended two-stage amplifier");
circuit->writeToFile("./myOTA.spi");
return 0;
}
- Note
- In order to compile these codes, a CMakeLists.txt file is provided. User must set the $VLSISAPD_TOP variable before running these commands in the directory containing the CMakeLists.txt file:
%> mkdir build; cd build
%> cmake ..
%> make
Python
Parser
The following python script (parseSpice.py
) is an example of how to parse a SPICE file using python module.
import sys
from SPICE import *
def printContents(circuit):
print "+", circuit.title
if len(circuit.getIncludes()):
print "| + includes"
for include in circuit.getIncludes():
print "| |", include
if len(circuit.getLibraries()):
print "| + libraries"
for (lib, typ) in circuit.getLibraries():
print "| |", lib, typ
if len(circuit.getParameters()):
print "| + parameters"
for (name, value) in circuit.getParameters().items():
print "| | %s=%s"%(name, value)
if len(circuit.getOptions()):
print "| + options"
for (name, value) in circuit.getOptions().items():
print "| | %s=%s"%(name, value)
if len(circuit.getSources()):
print "| + sources"
for source in circuit.getSources():
print "| |", source.getName(), source.getPositive(), source.getNegative(), source.getValue()
if len(circuit.getSubckts()):
print "| + subckts"
for sub in circuit.getSubckts():
print "| | +", sub.getName(),
for interf in sub.getInterfaces():
print interf,
if len(sub.getParameters()):
print "param:",
for (name, value) in sub.getParameters().items():
print "%s=%s"%(name,value),
print
for inst in sub.getInstances():
print "| | | +", inst.getName(),
if isinstance(inst, Mosfet):
print inst.getDrain(), inst.getGrid(), inst.getSource(), inst.getBulk(), inst.getModel(),
i = 0
for (name, value) in inst.getParameters().items():
if i%6 == 0:
print
print "| | | | +",
print "%s=%s"%(name, value),
i += 1
elif isinstance(inst, Resistor):
print inst.getFirst(), inst.getSecond(), inst.getValue(),
elif isinstance(inst, Capacitor):
print inst.getPositive(), inst.getNegative(), inst.getValue(),
else:
for conn in inst.getConnectors():
print conn,
print inst.getModel(),
i = 0
for (name, value) in inst.getParameters().items():
if i%6 == 0:
print
print "| | | | +",
print "%s=%s"%(name, value),
i += 1
print
if len(circuit.getInstances()):
print "| + instances"
for inst in circuit.getInstances():
print "| | | +", inst.getName(),
if isinstance(inst, Mosfet):
print inst.getDrain(), inst.getGrid(), inst.getSource(), inst.getBulk(), inst.getModel(),
i = 0
for (name, value) in inst.getParameters().items():
if i%6 == 0:
print
print "| | | | +",
print "%s=%s"%(name, value),
i += 1
elif isinstance(inst, Resistor):
print inst.getFirst(), inst.getSecond(), inst.getValue(),
elif isinstance(inst, Capacitor):
print inst.getPositive(), inst.getNegative(), inst.getValue(),
else:
for conn in inst.getConnectors():
print conn,
print inst.getModel(),
i = 0
for (name, value) in inst.getParameters().items():
if i%6 == 0:
print
print "| | | | +",
print "%s=%s"%(name, value),
i += 1
print
def usage():
print "usage:", sys.argv[0], "[filename]"
sys.exit(48)
def main():
if len(sys.argv) == 1:
filename = "./OTA_miller.spi"
elif len(sys.argv) == 2:
filename = sys.argv[1]
else:
usage()
circuit = Circuit.readFromFile(filename)
printContents(circuit)
if __name__ == "__main__":
main()
Driver
This python script (driveSpice.py
) generates an myOTA.spi file equivalent to the included one.
from SPICE import *
circuit = Circuit()
circuit.title = '* Single-ended two-stage amplifier'
circuit.addParameter("CC_VALUE", "2.8794pF");
circuit.addParameter("L_VALUE" , "0.340e-6");
CM = circuit.addSubckt("currentMirrorPMOS");
CM.addInterface("d1");
CM.addInterface("d2");
CM.addInterface("s1");
CM.addInterface("s2");
CM.addParameter("l_val" , "0.0" );
CM.addParameter("w_val" , "0.0" );
CM.addParameter("nf_val" , "1" );
CM.addParameter("aeq_val" , "100e-6");
CM.addParameter("temp_val", "27" );
cmP3 = Mosfet("P3", "d1", "d1", "s1", "s1", "psvt");
cmP3.addParameter("l" , "l_val" );
cmP3.addParameter("wf" , "{w_val/nf_val}");
cmP3.addParameter("nf" , "nf_val" );
cmP3.addParameter("aeq" , "aeq_val" );
cmP3.addParameter("tempsimu", "temp_val" );
CM.addInstance(cmP3);
cmP4 = Mosfet("P4", "d2", "d1", "s2", "s2", "psvt");
cmP4.addParameter("l" , "l_val" );
cmP4.addParameter("wf" , "{w_val/nf_val}");
cmP4.addParameter("nf" , "nf_val" );
cmP4.addParameter("aeq" , "aeq_val" );
cmP4.addParameter("tempsimu", "temp_val" );
CM.addInstance(cmP4);
DP = circuit.addSubckt("diffPairNMOS");
DP.addInterface("d1");
DP.addInterface("d2");
DP.addInterface("g1");
DP.addInterface("g2");
DP.addInterface("s");
DP.addInterface("b");
DP.addParameter("l_val" , "0.0" );
DP.addParameter("w_val" , "0.0" );
DP.addParameter("nf_val" , "1" );
DP.addParameter("aeq_val" , "100e-6");
DP.addParameter("temp_val", "27" );
dpN1 = Mosfet("N1", "d1", "g1", "s", "b", "nsvt");
dpN1.addParameter("l" , "l_val" );
dpN1.addParameter("wf" , "{w_val/nf_val}");
dpN1.addParameter("nf" , "nf_val" );
dpN1.addParameter("aeq" , "aeq_val" );
dpN1.addParameter("tempsimu", "temp_val" );
DP.addInstance(dpN1);
dpN2 = Mosfet("N2", "d2", "g2", "s", "b", "nsvt");
dpN2.addParameter("l" , "l_val" );
dpN2.addParameter("wf" , "{w_val/nf_val}");
dpN2.addParameter("nf" , "nf_val" );
dpN2.addParameter("aeq" , "aeq_val" );
dpN2.addParameter("tempsimu", "temp_val" );
DP.addInstance(dpN2);
iCM = Instance("CM", "currentMirrorPMOS");
iCM.addConnector("1");
iCM.addConnector("2");
iCM.addConnector("vdd");
iCM.addConnector("vdd");
iCM.addParameter("l_val" , "L_VALUE" );
iCM.addParameter("w_val" , "3.889618e-06");
iCM.addParameter("nf_val", "2" );
circuit.addInstance(iCM);
iDP = Instance("DP", "diffPairNMOS");
iDP.addConnector("1");
iDP.addConnector("2");
iDP.addConnector("vim");
iDP.addConnector("vip");
iDP.addConnector("3");
iDP.addConnector("vss");
iDP.addParameter("l_val" , "L_VALUE" );
iDP.addParameter("w_val" , "7.683346e-07");
iDP.addParameter("nf_val", "4" );
circuit.addInstance(iDP);
iP6 = Mosfet("P6", "vout", "2", "vdd", "vdd", "psvt");
iP6.addParameter("l_val" , "L_VALUE" );
iP6.addParameter("w_val" , "3.558995e-05");
iP6.addParameter("nf_val", "20" );
circuit.addInstance(iP6);
iN5 = Mosfet("N5", "3", "4", "vss", "vss", "nsvt");
iN5.addParameter("l_val" , "L_VALUE" );
iN5.addParameter("w_val" , "2.536703e-06");
iN5.addParameter("nf_val", "4" );
circuit.addInstance(iN5);
iN7 = Mosfet("N7", "vout", "4", "vss", "vss", "nsvt");
iN7.addParameter("l_val" , "L_VALUE" );
iN7.addParameter("w_val" , "1.069083e-05");
iN7.addParameter("nf_val", "16" );
circuit.addInstance(iN7);
iN8 = Mosfet("N8", "4", "4", "vss", "vss", "nsvt");
iN8.addParameter("l_val" , "L_VALUE" );
iN8.addParameter("w_val" , "2.536703e-06");
iN8.addParameter("nf_val", "4" );
circuit.addInstance(iN8);
capa = Capacitor("C1", "vout", "2", "CC_VALUE")
circuit.addInstance(capa);
circuit.writeToFile("./myOTA.spi");
- Note
- In order to run these two scripts (
parseSpice.py
& driveSpice.py), user must ensure that $PYTHONPATH variable points to the directory containing SPICE.so module.