WIP: preparations for SPICE reader delegate.

This commit is contained in:
Matthias Koefferlein 2019-06-22 18:37:32 +02:00
parent 4f41d99126
commit d174fb73fd
12 changed files with 400 additions and 216 deletions

View File

@ -124,7 +124,7 @@ public:
* @brief Creates an empty device parameter definition
*/
DeviceParameterDefinition ()
: m_name (), m_description (), m_default_value (0.0), m_id (0), m_is_primary (true)
: m_name (), m_description (), m_default_value (0.0), m_id (0), m_is_primary (true), m_si_scaling (1.0)
{
// .. nothing yet ..
}
@ -132,8 +132,8 @@ public:
/**
* @brief Creates a device parameter definition with the given name and description
*/
DeviceParameterDefinition (const std::string &name, const std::string &description, double default_value = 0.0, bool is_primary = true)
: m_name (name), m_description (description), m_default_value (default_value), m_id (0), m_is_primary (is_primary)
DeviceParameterDefinition (const std::string &name, const std::string &description, double default_value = 0.0, bool is_primary = true, double si_scaling = 1.0)
: m_name (name), m_description (description), m_default_value (default_value), m_id (0), m_is_primary (is_primary), m_si_scaling (si_scaling)
{
// .. nothing yet ..
}
@ -178,6 +178,17 @@ public:
return m_default_value;
}
/**
* @brief Gets the SI unit scaling factor
*
* Some parameters are given in micrometers for example. This
* scaling factor gives the translation to SI units (1e-6 for micrometers).
*/
double si_scaling () const
{
return m_si_scaling;
}
/**
* @brief Sets the parameter description
*/
@ -220,6 +231,7 @@ private:
double m_default_value;
size_t m_id;
bool m_is_primary;
double m_si_scaling;
void set_id (size_t id)
{

View File

@ -110,10 +110,10 @@ DeviceClassResistor::DeviceClassResistor ()
add_terminal_definition (db::DeviceTerminalDefinition ("B", "Terminal B"));
add_parameter_definition (db::DeviceParameterDefinition ("R", "Resistance (Ohm)", 0.0));
add_parameter_definition (db::DeviceParameterDefinition ("L", "Length (micrometer)", 0.0, false));
add_parameter_definition (db::DeviceParameterDefinition ("W", "Width (micrometer)", 0.0, false));
add_parameter_definition (db::DeviceParameterDefinition ("A", "Area (square micrometer)", 0.0, false));
add_parameter_definition (db::DeviceParameterDefinition ("P", "Perimeter (micrometer)", 0.0, false));
add_parameter_definition (db::DeviceParameterDefinition ("L", "Length (micrometer)", 0.0, false, 1e-6));
add_parameter_definition (db::DeviceParameterDefinition ("W", "Width (micrometer)", 0.0, false, 1e-6));
add_parameter_definition (db::DeviceParameterDefinition ("A", "Area (square micrometer)", 0.0, false, 1e-12));
add_parameter_definition (db::DeviceParameterDefinition ("P", "Perimeter (micrometer)", 0.0, false, 1e-6));
}
void DeviceClassResistor::parallel (Device *a, Device *b) const
@ -221,8 +221,8 @@ DeviceClassCapacitor::DeviceClassCapacitor ()
add_terminal_definition (db::DeviceTerminalDefinition ("B", "Terminal B"));
add_parameter_definition (db::DeviceParameterDefinition ("C", "Capacitance (Farad)", 0.0));
add_parameter_definition (db::DeviceParameterDefinition ("A", "Area (square micrometer)", 0.0, false));
add_parameter_definition (db::DeviceParameterDefinition ("P", "Perimeter (micrometer)", 0.0, false));
add_parameter_definition (db::DeviceParameterDefinition ("A", "Area (square micrometer)", 0.0, false, 1e-12));
add_parameter_definition (db::DeviceParameterDefinition ("P", "Perimeter (micrometer)", 0.0, false, 1e-6));
}
void DeviceClassCapacitor::serial (Device *a, Device *b) const
@ -325,8 +325,8 @@ DeviceClassDiode::DeviceClassDiode ()
add_terminal_definition (db::DeviceTerminalDefinition ("A", "Anode"));
add_terminal_definition (db::DeviceTerminalDefinition ("C", "Cathode"));
add_parameter_definition (db::DeviceParameterDefinition ("A", "Area (square micrometer)", 0.0, false));
add_parameter_definition (db::DeviceParameterDefinition ("P", "Perimeter (micrometer)", 0.0, false));
add_parameter_definition (db::DeviceParameterDefinition ("A", "Area (square micrometer)", 0.0, false, 1e-12));
add_parameter_definition (db::DeviceParameterDefinition ("P", "Perimeter (micrometer)", 0.0, false, 1e-6));
}
bool DeviceClassDiode::combine_devices (Device *a, Device *b) const
@ -372,12 +372,12 @@ DeviceClassMOS3Transistor::DeviceClassMOS3Transistor ()
add_terminal_definition (db::DeviceTerminalDefinition ("G", "Gate"));
add_terminal_definition (db::DeviceTerminalDefinition ("D", "Drain"));
add_parameter_definition (db::DeviceParameterDefinition ("L", "Gate length (micrometer)", 0.0));
add_parameter_definition (db::DeviceParameterDefinition ("W", "Gate width (micrometer)", 0.0));
add_parameter_definition (db::DeviceParameterDefinition ("AS", "Source area (square micrometer)", 0.0, false));
add_parameter_definition (db::DeviceParameterDefinition ("AD", "Drain area (square micrometer)", 0.0, false));
add_parameter_definition (db::DeviceParameterDefinition ("PS", "Source perimeter (micrometer)", 0.0, false));
add_parameter_definition (db::DeviceParameterDefinition ("PD", "Drain perimeter (micrometer)", 0.0, false));
add_parameter_definition (db::DeviceParameterDefinition ("L", "Gate length (micrometer)", 0.0, true, 1e-6));
add_parameter_definition (db::DeviceParameterDefinition ("W", "Gate width (micrometer)", 0.0, true, 1e-6));
add_parameter_definition (db::DeviceParameterDefinition ("AS", "Source area (square micrometer)", 0.0, false, 1e-12));
add_parameter_definition (db::DeviceParameterDefinition ("AD", "Drain area (square micrometer)", 0.0, false, 1e-12));
add_parameter_definition (db::DeviceParameterDefinition ("PS", "Source perimeter (micrometer)", 0.0, false, 1e-6));
add_parameter_definition (db::DeviceParameterDefinition ("PD", "Drain perimeter (micrometer)", 0.0, false, 1e-6));
}
bool DeviceClassMOS3Transistor::combine_devices (Device *a, Device *b) const
@ -496,12 +496,12 @@ DeviceClassBJT3Transistor::DeviceClassBJT3Transistor ()
add_terminal_definition (db::DeviceTerminalDefinition ("E", "Emitter"));
// NOTE: the emitter area and the emitter count are the primary parameters
add_parameter_definition (db::DeviceParameterDefinition ("AE", "Emitter area (square micrometer)", 0.0, true));
add_parameter_definition (db::DeviceParameterDefinition ("PE", "Emitter perimeter (micrometer)", 0.0, false));
add_parameter_definition (db::DeviceParameterDefinition ("AB", "Base area (square micrometer)", 0.0, false));
add_parameter_definition (db::DeviceParameterDefinition ("PB", "Base perimeter (micrometer)", 0.0, false));
add_parameter_definition (db::DeviceParameterDefinition ("AC", "Collector area (square micrometer)", 0.0, false));
add_parameter_definition (db::DeviceParameterDefinition ("PC", "Collector perimeter (micrometer)", 0.0, false));
add_parameter_definition (db::DeviceParameterDefinition ("AE", "Emitter area (square micrometer)", 0.0, true, 1e-12));
add_parameter_definition (db::DeviceParameterDefinition ("PE", "Emitter perimeter (micrometer)", 0.0, false, 1e-6));
add_parameter_definition (db::DeviceParameterDefinition ("AB", "Base area (square micrometer)", 0.0, false, 1e-12));
add_parameter_definition (db::DeviceParameterDefinition ("PB", "Base perimeter (micrometer)", 0.0, false, 1e-6));
add_parameter_definition (db::DeviceParameterDefinition ("AC", "Collector area (square micrometer)", 0.0, false, 1e-12));
add_parameter_definition (db::DeviceParameterDefinition ("PC", "Collector perimeter (micrometer)", 0.0, false, 1e-6));
add_parameter_definition (db::DeviceParameterDefinition ("NE", "Emitter count", 1.0, true));
}

View File

@ -33,14 +33,142 @@
namespace db
{
static const char *allowed_name_chars = "_.:,!+$/&\\#[]|";
// ------------------------------------------------------------------------------------------------------
NetlistSpiceReader::NetlistSpiceReader ()
: mp_netlist (0), mp_stream (0)
NetlistSpiceReaderDelegate::NetlistSpiceReaderDelegate ()
{
// .. nothing yet ..
}
NetlistSpiceReaderDelegate::~NetlistSpiceReaderDelegate ()
{
// .. nothing yet ..
}
void NetlistSpiceReaderDelegate::start (db::Netlist * /*netlist*/)
{
// .. nothing yet ..
}
void NetlistSpiceReaderDelegate::finish (db::Netlist * /*netlist*/)
{
// .. nothing yet ..
}
void NetlistSpiceReaderDelegate::error (const std::string &msg)
{
throw tl::Exception (msg);
}
bool NetlistSpiceReaderDelegate::element (db::Circuit *circuit, const std::string &element, const std::string &name, const std::string &model, double value, const std::vector<db::Net *> &nets, const std::map<std::string, double> &params)
{
std::string cn = model;
db::DeviceClass *cls = circuit->netlist ()->device_class_by_name (cn);
db::DeviceClass *new_cls = 0;
if (cls) {
// use given class
} else if (element == "R") {
if (cn.empty ()) {
cn = "RES";
}
new_cls = new db::DeviceClassResistor ();
} else if (element == "L") {
if (cn.empty ()) {
cn = "IND";
}
new_cls = new db::DeviceClassInductor ();
} else if (element == "C") {
if (cn.empty ()) {
cn = "CAP";
}
new_cls = new db::DeviceClassCapacitor ();
} else if (element == "D") {
if (cn.empty ()) {
cn = "DIODE";
}
new_cls = new db::DeviceClassDiode ();
} else if (element == "Q") {
if (nets.size () == 3) {
if (cn.empty ()) {
cn = "BJT3";
}
new_cls = new db::DeviceClassBJT3Transistor ();
} else if (nets.size () == 4) {
if (cn.empty ()) {
cn = "BJT4";
}
new_cls = new db::DeviceClassBJT4Transistor ();
}
} else if (element == "M") {
if (nets.size () == 3) {
if (cn.empty ()) {
cn = "MOS3";
}
new_cls = new db::DeviceClassMOS3Transistor ();
} else if (nets.size () == 4) {
if (cn.empty ()) {
cn = "MOS4";
}
new_cls = new db::DeviceClassMOS4Transistor ();
}
}
if (new_cls) {
cls = new_cls;
cls->set_name (cn);
circuit->netlist ()->add_device_class (cls);
} else if (! cls) {
error (tl::sprintf (tl::to_string (tr ("Not a known element type: '%s'")), element));
}
const std::vector<db::DeviceTerminalDefinition> &td = cls->terminal_definitions ();
if (td.size () != nets.size ()) {
error (tl::sprintf (tl::to_string (tr ("Wrong number of terminals: class '%s' expects %d, but %d are given")), cn, int (td.size ()), int (nets.size ())));
}
db::Device *device = new db::Device (cls, name);
circuit->add_device (device);
for (std::vector<db::DeviceTerminalDefinition>::const_iterator t = td.begin (); t != td.end (); ++t) {
device->connect_terminal (t->id (), nets [t - td.begin ()]);
}
size_t defp = std::numeric_limits<size_t>::max ();
if (dynamic_cast<db::DeviceClassCapacitor *> (cls)) {
defp = db::DeviceClassCapacitor::param_id_C;
} else if (dynamic_cast<db::DeviceClassResistor *> (cls)) {
defp = db::DeviceClassResistor::param_id_R;
} else if (dynamic_cast<db::DeviceClassInductor *> (cls)) {
defp = db::DeviceClassInductor::param_id_L;
}
const std::vector<db::DeviceParameterDefinition> &pd = cls->parameter_definitions ();
for (std::vector<db::DeviceParameterDefinition>::const_iterator i = pd.begin (); i != pd.end (); ++i) {
std::map<std::string, double>::const_iterator v = params.find (i->name ());
if (v != params.end ()) {
device->set_parameter_value (i->id (), v->second / i->si_scaling ());
} else if (i->id () == defp) {
device->set_parameter_value (i->id (), value / i->si_scaling ());
}
}
return true;
}
// ------------------------------------------------------------------------------------------------------
static const char *allowed_name_chars = "_.:,!+$/&\\#[]|";
NetlistSpiceReader::NetlistSpiceReader (NetlistSpiceReaderDelegate *delegate, const std::string &captured_subcircuits)
: mp_netlist (0), mp_stream (0), mp_delegate (delegate), m_captured (captured_subcircuits)
{
static NetlistSpiceReaderDelegate std_delegate;
if (! delegate) {
mp_delegate.reset (&std_delegate);
}
}
NetlistSpiceReader::~NetlistSpiceReader ()
{
// .. nothing yet ..
@ -55,10 +183,13 @@ void NetlistSpiceReader::read (tl::InputStream &stream, db::Netlist &netlist)
try {
mp_delegate->start (&netlist);
while (! at_end ()) {
read_element ();
read_card ();
}
mp_delegate->finish (&netlist);
finish ();
} catch (tl::Exception &ex) {
@ -166,7 +297,7 @@ void NetlistSpiceReader::unget_line (const std::string &l)
m_stored_line = l;
}
bool NetlistSpiceReader::read_element ()
bool NetlistSpiceReader::read_card ()
{
std::string l = get_line ();
if (l.empty ()) {
@ -175,9 +306,8 @@ bool NetlistSpiceReader::read_element ()
tl::Extractor ex (l.c_str ());
const char *res_device_class_name = "RES";
const char *cap_device_class_name = "CAP";
const char *ind_device_class_name = "IND";
ex.skip ();
char next_char = toupper (*ex);
if (ex.test_without_case (".")) {
@ -207,59 +337,24 @@ bool NetlistSpiceReader::read_element ()
}
} else if (ex.test_without_case ("r")) {
} else if (isalpha (next_char)) {
db::DeviceClass *dev_cls = mp_netlist->device_class_by_name (res_device_class_name);
if (! dev_cls) {
dev_cls = new db::DeviceClassResistor ();
dev_cls->set_name (res_device_class_name);
mp_netlist->add_device_class (dev_cls);
++ex;
std::string name = read_name (ex);
ensure_circuit ();
std::string es;
es.push_back (next_char);
if (next_char == 'X' && ! m_captured.match (name)) {
read_subcircuit (ex, name);
} else if (! read_element (ex, es, name)) {
warn (tl::sprintf (tl::to_string (tr ("Element type '%c' ignored")), next_char));
}
ensure_circuit ();
read_device (dev_cls, db::DeviceClassResistor::param_id_R, ex);
} else if (ex.test_without_case ("c")) {
db::DeviceClass *dev_cls = mp_netlist->device_class_by_name (cap_device_class_name);
if (! dev_cls) {
dev_cls = new db::DeviceClassCapacitor ();
dev_cls->set_name (cap_device_class_name);
mp_netlist->add_device_class (dev_cls);
}
ensure_circuit ();
read_device (dev_cls, db::DeviceClassCapacitor::param_id_C, ex);
} else if (ex.test_without_case ("l")) {
db::DeviceClass *dev_cls = mp_netlist->device_class_by_name (ind_device_class_name);
if (! dev_cls) {
dev_cls = new db::DeviceClassInductor ();
dev_cls->set_name (ind_device_class_name);
mp_netlist->add_device_class (dev_cls);
}
ensure_circuit ();
read_device (dev_cls, db::DeviceClassInductor::param_id_L, ex);
} else if (ex.test_without_case ("m")) {
ensure_circuit ();
read_mos4_device (ex);
} else if (ex.test_without_case ("x")) {
ensure_circuit ();
read_subcircuit (ex);
} else {
char c = *ex.skip ();
if (c) {
warn (tl::sprintf (tl::to_string (tr ("Element type '%c' ignored")), c));
}
warn (tl::to_string (tr ("Line ignored")));
}
return false;
@ -480,10 +575,111 @@ std::string NetlistSpiceReader::read_name (tl::Extractor &ex)
return nn;
}
void NetlistSpiceReader::read_subcircuit (tl::Extractor &ex)
bool NetlistSpiceReader::read_element (tl::Extractor &ex, const std::string &element, const std::string &name)
{
std::string sc_name = read_name (ex);
// generic parse
std::vector<std::string> nn;
std::map<std::string, double> pv;
std::string model;
double value = 0.0;
// interpret the parameters according to the code
if (element == "X") {
// subcircuit call:
// Xname n1 n2 ... nn circuit [params]
read_pin_and_parameters (ex, nn, pv);
if (nn.empty ()) {
error (tl::to_string (tr ("No circuit name given for subcircuit call")));
}
model = nn.back ();
nn.pop_back ();
} else if (element == "R" || element == "C" || element == "L") {
// resistor, cap, inductor: two-terminal devices with a value
// Rname n1 n2 value
// Rname n1 n2 value model [params]
// Rname n1 n2 model [params]
// (same for C, L instead of R)
while (! ex.at_end () && nn.size () < 2) {
nn.push_back (read_name (ex));
}
if (nn.size () != 2) {
error (tl::to_string (tr ("Two-terminal device needs two nets")));
}
tl::Extractor ve (ex);
double vv = 0.0;
if (ve.try_read (vv) || ve.test ("(")) {
value = read_value (ex);
}
while (! ex.at_end ()) {
std::string n = read_name (ex);
if (ex.test ("=")) {
pv [tl::to_upper_case (n)] = read_value (ex);
} else if (! model.empty ()) {
error (tl::sprintf (tl::to_string (tr ("Too many arguments for two-terminal device (additional argumen is '%s')")), n));
} else {
model = n;
}
}
} else {
// others: n-terminal devices with a model (last node)
while (! ex.at_end ()) {
std::string n = read_name (ex);
if (ex.test ("=")) {
pv [tl::to_upper_case (n)] = read_value (ex);
} else {
nn.push_back (n);
}
}
if (nn.empty ()) {
error (tl::sprintf (tl::to_string (tr ("No model name given for element '%s'")), element));
}
model = nn.back ();
nn.pop_back ();
if (element == "M") {
if (nn.size () != 4) {
error (tl::to_string (tr ("'M' element must have four nodes")));
}
} else if (element == "Q") {
if (nn.size () != 3 && nn.size () != 4) {
error (tl::to_string (tr ("'Q' element must have three or four nodes")));
}
} else if (element == "D") {
if (nn.size () != 2) {
error (tl::to_string (tr ("'D' element must have two nodes")));
}
}
// TODO: other devices?
}
std::vector<db::Net *> nets;
for (std::vector<std::string>::const_iterator i = nn.begin (); i != nn.end (); ++i) {
nets.push_back (make_net (*i));
}
return mp_delegate->element (mp_circuit, element, name, tl::to_upper_case (model), value, nets, pv);
}
void NetlistSpiceReader::read_subcircuit (tl::Extractor &ex, const std::string &sc_name)
{
std::vector<std::string> nn;
std::map<std::string, double> pv;
read_pin_and_parameters (ex, nn, pv);
@ -564,7 +760,7 @@ void NetlistSpiceReader::read_circuit (tl::Extractor &ex)
}
while (! at_end ()) {
if (read_element ()) {
if (read_card ()) {
break;
}
}
@ -575,98 +771,4 @@ void NetlistSpiceReader::read_circuit (tl::Extractor &ex)
ex.expect_end ();
}
void NetlistSpiceReader::read_device (db::DeviceClass *dev_cls, size_t param_id, tl::Extractor &ex)
{
std::string dn = read_name (ex);
std::vector<std::string> nn;
while (! ex.at_end () && nn.size () < 2) {
nn.push_back (read_name (ex));
}
if (nn.size () != 2) {
error (tl::to_string (tr ("Two-terminal device needs two nets")));
}
double v = read_value (ex);
db::Device *dev = new db::Device (dev_cls, dn);
mp_circuit->add_device (dev);
for (std::vector<std::string>::const_iterator i = nn.begin (); i != nn.end (); ++i) {
db::Net *net = make_net (*i);
dev->connect_terminal (i - nn.begin (), net);
}
dev->set_parameter_value (param_id, v);
ex.expect_end ();
}
void NetlistSpiceReader::read_mos4_device (tl::Extractor &ex)
{
std::string dn = read_name (ex);
std::vector<std::string> nn;
std::map<std::string, double> pv;
while (! ex.at_end ()) {
std::string n = read_name (ex);
if (ex.test ("=")) {
// a parameter
pv.insert (std::make_pair (tl::to_upper_case (n), read_value (ex)));
} else {
nn.push_back (n);
}
}
if (nn.empty ()) {
error (tl::to_string (tr ("No model name given for MOS transistor element")));
}
std::string mn = nn.back ();
nn.pop_back ();
if (nn.size () != 4) {
error (tl::to_string (tr ("A MOS transistor needs four nets")));
}
db::DeviceClass *dev_cls = mp_netlist->device_class_by_name (mn);
if (! dev_cls) {
dev_cls = new db::DeviceClassMOS4Transistor ();
dev_cls->set_name (mn);
mp_netlist->add_device_class (dev_cls);
}
db::Device *dev = new db::Device (dev_cls, dn);
mp_circuit->add_device (dev);
for (std::vector<std::string>::const_iterator i = nn.begin (); i != nn.end (); ++i) {
db::Net *net = make_net (*i);
dev->connect_terminal (i - nn.begin (), net);
}
const std::vector<db::DeviceParameterDefinition> &pd = dev_cls->parameter_definitions ();
for (std::vector<db::DeviceParameterDefinition>::const_iterator i = pd.begin (); i != pd.end (); ++i) {
std::map<std::string, double>::const_iterator v = pv.find (i->name ());
if (v != pv.end ()) {
// by conventions, dimensions are in micrometer
if (i->id () == db::DeviceClassMOS4Transistor::param_id_AD || i->id () == db::DeviceClassMOS4Transistor::param_id_AS) {
dev->set_parameter_value (i->id (), v->second * 1e12);
} else if (i->id () == db::DeviceClassMOS4Transistor::param_id_W
|| i->id () == db::DeviceClassMOS4Transistor::param_id_L
|| i->id () == db::DeviceClassMOS4Transistor::param_id_PD
|| i->id () == db::DeviceClassMOS4Transistor::param_id_PS) {
dev->set_parameter_value (i->id (), v->second * 1e6);
}
}
}
ex.expect_end ();
}
}

View File

@ -26,6 +26,7 @@
#include "dbCommon.h"
#include "dbNetlistReader.h"
#include "tlStream.h"
#include "tlGlobPattern.h"
#include <string>
#include <memory>
@ -37,6 +38,68 @@ class Netlist;
class Net;
class Circuit;
class DeviceClass;
class Device;
/**
* @brief A specialized exception class to handle netlist reader delegate errors
*/
class DB_PUBLIC NetlistSpiceReaderDelegateError
: public tl::Exception
{
public:
NetlistSpiceReaderDelegateError (const std::string &msg)
: tl::Exception (msg)
{ }
};
/**
* @brief A delegate to handle various forms of devices and translates them
*
* The reader delegate can be configured to recieve subcircuit elements too.
* In this case, parameters are allowed.
* Such subcircuits must be included in the captured_subcircuits glob
* pattern when configuring the SPICE reader with the delegate.
*/
class DB_PUBLIC NetlistSpiceReaderDelegate
: public tl::Object
{
public:
NetlistSpiceReaderDelegate ();
virtual ~NetlistSpiceReaderDelegate ();
/**
* @brief Called when the netlist reading starts
*/
virtual void start (db::Netlist *netlist);
/**
* @brief Called when the netlist reading ends
*/
virtual void finish (db::Netlist *netlist);
/**
* @brief Makes a device from an element line
*
* @param circuit is the circuit that is currently read.
* @param element is the element code ("M", "R", ...).
* @param name is the element's name.
* @param model is the model name (may be empty).
* @param value is the default value (e.g. registance for resistors) and may be zero.
* @param nets are the nets given in the element line.
* @param parameters are the parameters of the element statement.
*
* The default implementation will create corresponding devices for
* some known elements using the Spice writer's parameter conventions.
*
* This return returns true, if the element was read.
*/
virtual bool element (db::Circuit *circuit, const std::string &element, const std::string &name, const std::string &model, double value, const std::vector<db::Net *> &nets, const std::map<std::string, double> &params);
/**
* @brief Produces an error with the given message
*/
void error (const std::string &msg);
};
/**
* @brief A SPICE format reader for netlists
@ -45,7 +108,7 @@ class DB_PUBLIC NetlistSpiceReader
: public NetlistReader
{
public:
NetlistSpiceReader ();
NetlistSpiceReader (NetlistSpiceReaderDelegate *delegate = 0, const std::string &captured_subcircuits = std::string ());
virtual ~NetlistSpiceReader ();
virtual void read (tl::InputStream &stream, db::Netlist &netlist);
@ -54,19 +117,20 @@ private:
db::Netlist *mp_netlist;
db::Circuit *mp_circuit;
std::auto_ptr<tl::TextInputStream> mp_stream;
tl::weak_ptr<NetlistSpiceReaderDelegate> mp_delegate;
std::vector<std::pair<tl::InputStream *, tl::TextInputStream *> > m_streams;
std::auto_ptr<std::map<std::string, db::Net *> > mp_nets_by_name;
std::string m_stored_line;
tl::GlobPattern m_captured;
void push_stream (const std::string &path);
void pop_stream ();
bool at_end ();
void read_pin_and_parameters (tl::Extractor &ex, std::vector<std::string> &nn, std::map<std::string, double> &pv);
void read_subcircuit (tl::Extractor &ex);
bool read_element (tl::Extractor &ex, const std::string &element, const std::string &name);
void read_subcircuit (tl::Extractor &ex, const std::string &sc_name);
void read_circuit (tl::Extractor &ex);
void read_device (db::DeviceClass *dev_cls, size_t param_id, tl::Extractor &ex);
void read_mos4_device (tl::Extractor &ex);
bool read_element ();
bool read_card ();
double read_value (tl::Extractor &ex);
std::string read_name (tl::Extractor &ex);
double read_atomic_value (tl::Extractor &ex);

View File

@ -135,9 +135,7 @@ void NetlistSpiceWriterDelegate::write_device (const db::Device &dev) const
// Use device class name for the model
os << " ";
os << format_name (dev.device_class ()->name ());
os << " A=" << tl::sprintf ("%.12gP", dev.parameter_value (db::DeviceClassDiode::param_id_A));
os << " P=" << tl::sprintf ("%.12gP", dev.parameter_value (db::DeviceClassDiode::param_id_P));
os << format_params (dev);
} else if (mos3 || mos4) {
@ -154,13 +152,7 @@ void NetlistSpiceWriterDelegate::write_device (const db::Device &dev) const
// Use device class name for the model
os << " ";
os << format_name (dev.device_class ()->name ());
os << " L=" << tl::sprintf ("%.12gU", dev.parameter_value (db::DeviceClassMOS3Transistor::param_id_L));
os << " W=" << tl::sprintf ("%.12gU", dev.parameter_value (db::DeviceClassMOS3Transistor::param_id_W));
os << " AS=" << tl::sprintf ("%.12gP", dev.parameter_value (db::DeviceClassMOS3Transistor::param_id_AS));
os << " AD=" << tl::sprintf ("%.12gP", dev.parameter_value (db::DeviceClassMOS3Transistor::param_id_AD));
os << " PS=" << tl::sprintf ("%.12gU", dev.parameter_value (db::DeviceClassMOS3Transistor::param_id_PS));
os << " PD=" << tl::sprintf ("%.12gU", dev.parameter_value (db::DeviceClassMOS3Transistor::param_id_PD));
os << format_params (dev);
} else if (bjt3 || bjt4) {
@ -171,14 +163,7 @@ void NetlistSpiceWriterDelegate::write_device (const db::Device &dev) const
// Use device class name for the model
os << " ";
os << format_name (dev.device_class ()->name ());
os << " AE=" << tl::sprintf ("%.12gP", dev.parameter_value (db::DeviceClassBJT3Transistor::param_id_AE));
os << " AB=" << tl::sprintf ("%.12gP", dev.parameter_value (db::DeviceClassBJT3Transistor::param_id_AB));
os << " AC=" << tl::sprintf ("%.12gP", dev.parameter_value (db::DeviceClassBJT3Transistor::param_id_AC));
os << " PE=" << tl::sprintf ("%.12gU", dev.parameter_value (db::DeviceClassBJT3Transistor::param_id_PE));
os << " PB=" << tl::sprintf ("%.12gU", dev.parameter_value (db::DeviceClassBJT3Transistor::param_id_PB));
os << " PC=" << tl::sprintf ("%.12gU", dev.parameter_value (db::DeviceClassBJT3Transistor::param_id_PC));
os << " NE=" << tl::sprintf ("%.0f", dev.parameter_value (db::DeviceClassBJT3Transistor::param_id_NE));
os << format_params (dev);
} else {
@ -207,13 +192,24 @@ std::string NetlistSpiceWriterDelegate::format_terminals (const db::Device &dev)
return os.str ();
}
std::string NetlistSpiceWriterDelegate::format_params (const db::Device &dev) const
std::string NetlistSpiceWriterDelegate::format_params (const db::Device &dev, size_t without_id) const
{
std::ostringstream os;
const std::vector<db::DeviceParameterDefinition> &pd = dev.device_class ()->parameter_definitions ();
for (std::vector<db::DeviceParameterDefinition>::const_iterator i = pd.begin (); i != pd.end (); ++i) {
os << " " << i->name () << "=" << tl::to_string (dev.parameter_value (i->id ()));
if (i->id () != without_id) {
double sis = i->si_scaling ();
os << " " << i->name () << "=";
// for compatibility
if (fabs (sis * 1e6 - 1.0) < 1e-10) {
os << tl::to_string (dev.parameter_value (i->id ())) << "U";
} else if (fabs (sis * 1e12 - 1.0) < 1e-10) {
os << tl::to_string (dev.parameter_value (i->id ())) << "P";
} else {
os << tl::to_string (dev.parameter_value (i->id ()) * sis);
}
}
}
return os.str ();

View File

@ -29,6 +29,7 @@
#include <string>
#include <map>
#include <limits>
namespace db
{
@ -62,7 +63,7 @@ public:
void emit_comment (const std::string &comment) const;
std::string format_name (const std::string &s) const;
std::string format_terminals (const db::Device &dev) const;
std::string format_params (const db::Device &dev) const;
std::string format_params (const db::Device &dev, size_t without_id = std::numeric_limits<size_t>::max ()) const;
private:
friend class NetlistSpiceWriter;

View File

@ -622,14 +622,19 @@ Class<db::DeviceTerminalDefinition> decl_dbDeviceTerminalDefinition ("db", "Devi
"This class has been added in version 0.26."
);
static db::DeviceParameterDefinition *new_parameter_definition (const std::string &name, const std::string &description, double default_value)
static db::DeviceParameterDefinition *new_parameter_definition (const std::string &name, const std::string &description, double default_value, bool is_primary, double si_scaling)
{
return new db::DeviceParameterDefinition (name, description, default_value);
return new db::DeviceParameterDefinition (name, description, default_value, is_primary, si_scaling);
}
Class<db::DeviceParameterDefinition> decl_dbDeviceParameterDefinition ("db", "DeviceParameterDefinition",
gsi::constructor ("new", &gsi::new_parameter_definition, gsi::arg ("name"), gsi::arg ("description", std::string ()), gsi::arg ("default_value", 0.0),
gsi::constructor ("new", &gsi::new_parameter_definition, gsi::arg ("name"), gsi::arg ("description", std::string ()), gsi::arg ("default_value", 0.0), gsi::arg ("is_primary", true), gsi::arg ("si_scaling", 1.0),
"@brief Creates a new parameter definition."
"@param name The name of the parameter\n"
"@param description The human-readable description\n"
"@param default_value The initial value\n"
"@param is_primary True, if the parameter is a primary parameter (see \\is_primary=)\n"
"@param si_scaling The scaling factor to SI units\n"
) +
gsi::method ("name", &db::DeviceParameterDefinition::name,
"@brief Gets the name of the parameter."
@ -659,6 +664,10 @@ Class<db::DeviceParameterDefinition> decl_dbDeviceParameterDefinition ("db", "De
"If this flag is set to true (the default), the parameter is considered a primary parameter.\n"
"Only primary parameters are compared by default.\n"
) +
gsi::method ("si_scaling", &db::DeviceParameterDefinition::si_scaling,
"@brief Gets the scaling factor to SI units.\n"
"For parameters in micrometers for example, this factor will be 1e-6."
) +
gsi::method ("id", &db::DeviceParameterDefinition::id,
"@brief Gets the ID of the parameter.\n"
"The ID of the parameter is used in some places to refer to a specific parameter (e.g. in "

View File

@ -308,9 +308,9 @@ TEST(4_WriterDiodeDevices)
circuit1->add_net (n3);
db::Device *ddev1 = new db::Device (dcls);
ddev1->set_parameter_value (db::DeviceClassDiode::param_id_A, 1.7e-10);
ddev1->set_parameter_value (db::DeviceClassDiode::param_id_A, 1.7);
db::Device *ddev2 = new db::Device (dcls);
ddev2->set_parameter_value (db::DeviceClassDiode::param_id_A, 42e-9);
ddev2->set_parameter_value (db::DeviceClassDiode::param_id_A, 0.42);
circuit1->add_device (ddev1);
circuit1->add_device (ddev2);

View File

@ -1,6 +1,6 @@
.subckt INVX1 1 2 3 4 5 6
m$1 1 5 2 4 MLVPMOS w=1.5um l=0.25um
m$2 3 5 2 6 MLVNMOS w=0.95um l=0.25um
m$1 1 5 2 4 mlvpmos w=1.5um l=0.25um
m$2 3 5 2 6 mlvnmos w=0.95um l=0.25um
.ends
.subckt ND2X1 1 2 3 4 5 6 7

View File

@ -10,7 +10,7 @@
* net 3 n3
* net 4 n4
* device instance $1 r0 *1 0,0 B3CLS
Q$1 3 4 1 B3CLS AE=0.25P AB=1.2P AC=1P PE=0.18U PB=0.75U PC=0.6U NE=1
Q$1 3 4 1 B3CLS AE=0.25P PE=0.18U AB=1.2P PB=0.75U AC=1P PC=0.6U NE=1
* device instance $2 r0 *1 0,0 B3CLS
Q$2 2 4 3 B3CLS AE=1.2P AB=1.4P AC=1.5P PE=2.5U PB=2.8U PC=3U NE=1
Q$2 2 4 3 B3CLS AE=1.2P PE=2.5U AB=1.4P PB=2.8U AC=1.5P PC=3U NE=1
.ENDS C1

View File

@ -12,7 +12,7 @@
* net 4 n4
* net 5 n5
* device instance $1 r0 *1 0,0 B4CLS
Q$1 3 4 1 5 B4CLS AE=0.25P AB=1.2P AC=1P PE=0.18U PB=0.75U PC=0.6U NE=1
Q$1 3 4 1 5 B4CLS AE=0.25P PE=0.18U AB=1.2P PB=0.75U AC=1P PC=0.6U NE=1
* device instance $2 r0 *1 0,0 B4CLS
Q$2 2 4 3 5 B4CLS AE=1.2P AB=1.4P AC=1.5P PE=2.5U PB=2.8U PC=3U NE=1
Q$2 2 4 3 5 B4CLS AE=1.2P PE=2.5U AB=1.4P PB=2.8U AC=1.5P PC=3U NE=1
.ENDS C1

View File

@ -8,7 +8,7 @@
* net 2 n2
* net 3 n3
* device instance $1 r0 *1 0,0 DCLS
D$1 1 3 DCLS A=1.7e-10P P=0P
D$1 1 3 DCLS A=1.7P P=0U
* device instance $2 r0 *1 0,0 DCLS
D$2 3 2 DCLS A=4.2e-08P P=0P
D$2 3 2 DCLS A=0.42P P=0U
.ENDS C1