mirror of https://github.com/KLayout/klayout.git
WIP: preparations for SPICE reader delegate.
This commit is contained in:
parent
4f41d99126
commit
d174fb73fd
|
|
@ -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)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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));
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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> ¶ms)
|
||||
{
|
||||
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 ();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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> ¶ms);
|
||||
|
||||
/**
|
||||
* @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);
|
||||
|
|
|
|||
|
|
@ -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 ();
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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 "
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Reference in New Issue