mirror of https://github.com/KLayout/klayout.git
WIP: Simple SPICE reader.
This commit is contained in:
parent
9613ad72c8
commit
89ffd7e3da
|
|
@ -173,7 +173,9 @@ SOURCES = \
|
|||
dbRegionUtils.cc \
|
||||
dbEdgesUtils.cc \
|
||||
dbRegionProcessors.cc \
|
||||
dbNetlistCompare.cc
|
||||
dbNetlistCompare.cc \
|
||||
dbNetlistReader.cc \
|
||||
dbNetlistSpiceReader.cc
|
||||
|
||||
HEADERS = \
|
||||
dbArray.h \
|
||||
|
|
@ -312,7 +314,9 @@ HEADERS = \
|
|||
dbEdgesUtils.h \
|
||||
dbRegionProcessors.h \
|
||||
gsiDeclDbHelpers.h \
|
||||
dbNetlistCompare.h
|
||||
dbNetlistCompare.h \
|
||||
dbNetlistReader.h \
|
||||
dbNetlistSpiceReader.h
|
||||
|
||||
!equals(HAVE_QT, "0") {
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,31 @@
|
|||
|
||||
/*
|
||||
|
||||
KLayout Layout Viewer
|
||||
Copyright (C) 2006-2019 Matthias Koefferlein
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
|
||||
*/
|
||||
|
||||
|
||||
#include "dbNetlistReader.h"
|
||||
|
||||
namespace db
|
||||
{
|
||||
|
||||
// .. nothing yet ..
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,68 @@
|
|||
|
||||
/*
|
||||
|
||||
KLayout Layout Viewer
|
||||
Copyright (C) 2006-2019 Matthias Koefferlein
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
|
||||
*/
|
||||
|
||||
#ifndef HDR_dbNetlistReader
|
||||
#define HDR_dbNetlistReader
|
||||
|
||||
#include "dbCommon.h"
|
||||
#include "tlTypeTraits.h"
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace tl
|
||||
{
|
||||
class InputStream;
|
||||
}
|
||||
|
||||
namespace db
|
||||
{
|
||||
|
||||
class Netlist;
|
||||
|
||||
/**
|
||||
* @brief A common base class for netlist writers
|
||||
*/
|
||||
class DB_PUBLIC NetlistReader
|
||||
{
|
||||
public:
|
||||
NetlistReader () { }
|
||||
virtual ~NetlistReader () { }
|
||||
|
||||
virtual void read (tl::InputStream &stream, db::Netlist &netlist) = 0;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
namespace tl
|
||||
{
|
||||
|
||||
template <>
|
||||
struct type_traits<db::NetlistReader>
|
||||
: public tl::type_traits<void>
|
||||
{
|
||||
typedef tl::false_tag has_default_constructor;
|
||||
typedef tl::false_tag has_copy_constructor;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
@ -0,0 +1,588 @@
|
|||
|
||||
/*
|
||||
|
||||
KLayout Layout Viewer
|
||||
Copyright (C) 2006-2019 Matthias Koefferlein
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
|
||||
*/
|
||||
|
||||
#include "dbNetlistSpiceReader.h"
|
||||
#include "dbNetlist.h"
|
||||
#include "dbNetlistDeviceClasses.h"
|
||||
|
||||
#include "tlStream.h"
|
||||
#include "tlLog.h"
|
||||
|
||||
#include <sstream>
|
||||
#include <cctype>
|
||||
|
||||
namespace db
|
||||
{
|
||||
|
||||
NetlistSpiceReader::NetlistSpiceReader ()
|
||||
: mp_netlist (0), mp_stream (0)
|
||||
{
|
||||
// .. nothing yet ..
|
||||
}
|
||||
|
||||
NetlistSpiceReader::~NetlistSpiceReader ()
|
||||
{
|
||||
// .. nothing yet ..
|
||||
}
|
||||
|
||||
void NetlistSpiceReader::read (tl::InputStream &stream, db::Netlist &netlist)
|
||||
{
|
||||
mp_stream.reset (new tl::TextInputStream (stream));
|
||||
mp_netlist = &netlist;
|
||||
mp_circuit = 0;
|
||||
|
||||
try {
|
||||
|
||||
while (! at_end ()) {
|
||||
read_element ();
|
||||
}
|
||||
|
||||
finish ();
|
||||
|
||||
} catch (tl::Exception &ex) {
|
||||
|
||||
// NOTE: because we do a peek to capture the "+" line continuation character, we're
|
||||
// one line ahead.
|
||||
std::string fmt_msg = tl::sprintf ("%s in %s, line %d", ex.msg (), mp_stream->source (), mp_stream->line_number () - 1);
|
||||
finish ();
|
||||
throw tl::Exception (fmt_msg);
|
||||
|
||||
} catch (...) {
|
||||
|
||||
finish ();
|
||||
throw;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
void NetlistSpiceReader::finish ()
|
||||
{
|
||||
while (! m_streams.empty ()) {
|
||||
pop_stream ();
|
||||
}
|
||||
|
||||
mp_stream.reset (0);
|
||||
mp_netlist = 0;
|
||||
mp_circuit = 0;
|
||||
}
|
||||
|
||||
void NetlistSpiceReader::push_stream (const std::string &path)
|
||||
{
|
||||
tl::InputStream *istream = new tl::InputStream (path);
|
||||
m_streams.push_back (std::make_pair (istream, mp_stream.release ()));
|
||||
mp_stream.reset (new tl::TextInputStream (*istream));
|
||||
}
|
||||
|
||||
void NetlistSpiceReader::pop_stream ()
|
||||
{
|
||||
if (! m_streams.empty ()) {
|
||||
|
||||
mp_stream.reset (m_streams.back ().second);
|
||||
delete m_streams.back ().first;
|
||||
|
||||
m_streams.pop_back ();
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
bool NetlistSpiceReader::at_end ()
|
||||
{
|
||||
return mp_stream->at_end () && m_streams.empty ();
|
||||
}
|
||||
|
||||
std::string NetlistSpiceReader::get_line ()
|
||||
{
|
||||
if (! m_stored_line.empty ()) {
|
||||
std::string l;
|
||||
l.swap (m_stored_line);
|
||||
return l;
|
||||
}
|
||||
|
||||
std::string l;
|
||||
|
||||
do {
|
||||
|
||||
while (mp_stream->at_end ()) {
|
||||
if (m_streams.empty ()) {
|
||||
return std::string ();
|
||||
}
|
||||
pop_stream ();
|
||||
}
|
||||
|
||||
l = mp_stream->get_line ();
|
||||
while (! mp_stream->at_end () && mp_stream->peek_char () == '+') {
|
||||
mp_stream->get_char ();
|
||||
l += mp_stream->get_line ();
|
||||
}
|
||||
|
||||
tl::Extractor ex (l.c_str ());
|
||||
if (ex.test_without_case (".include")) {
|
||||
|
||||
std::string path;
|
||||
ex.read_word_or_quoted (path, "_.:,!+$/\\");
|
||||
|
||||
push_stream (path);
|
||||
|
||||
l.clear ();
|
||||
|
||||
} else if (ex.at_end () || ex.test ("*")) {
|
||||
l.clear ();
|
||||
}
|
||||
|
||||
} while (l.empty ());
|
||||
|
||||
return l;
|
||||
}
|
||||
|
||||
void NetlistSpiceReader::unget_line (const std::string &l)
|
||||
{
|
||||
m_stored_line = l;
|
||||
}
|
||||
|
||||
bool NetlistSpiceReader::read_element ()
|
||||
{
|
||||
std::string l = get_line ();
|
||||
if (l.empty ()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
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";
|
||||
|
||||
if (ex.test_without_case (".")) {
|
||||
|
||||
// control statement
|
||||
if (ex.test_without_case ("model")) {
|
||||
|
||||
// ignore model statements
|
||||
|
||||
} else if (ex.test_without_case ("subckt")) {
|
||||
|
||||
read_circuit (ex);
|
||||
|
||||
} else if (ex.test_without_case ("ends")) {
|
||||
|
||||
return true;
|
||||
|
||||
} else if (ex.test_without_case ("end")) {
|
||||
|
||||
// ignore end statements
|
||||
|
||||
} else {
|
||||
|
||||
std::string s;
|
||||
ex.read_word (s);
|
||||
s = tl::to_lower_case (s);
|
||||
warn (tl::to_string (tr ("Control statement ignored: ")) + s);
|
||||
|
||||
}
|
||||
|
||||
} else if (ex.test_without_case ("r")) {
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void NetlistSpiceReader::error (const std::string &msg)
|
||||
{
|
||||
throw tl::Exception (msg);
|
||||
}
|
||||
|
||||
void NetlistSpiceReader::warn (const std::string &msg)
|
||||
{
|
||||
std::string fmt_msg = tl::sprintf ("%s in %s, line %d", msg, mp_stream->source (), mp_stream->line_number ());
|
||||
tl::warn << fmt_msg;
|
||||
}
|
||||
|
||||
double NetlistSpiceReader::read_atomic_value (tl::Extractor &ex)
|
||||
{
|
||||
if (ex.test ("(")) {
|
||||
|
||||
double v = read_dot_expr (ex);
|
||||
ex.expect (")");
|
||||
return v;
|
||||
|
||||
} else {
|
||||
|
||||
double v = 0.0;
|
||||
ex.read (v);
|
||||
|
||||
double f = 1.0;
|
||||
if (*ex == 't' || *ex == 'T') {
|
||||
f = 1e12;
|
||||
} else if (*ex == 'g' || *ex == 'G') {
|
||||
f = 1e9;
|
||||
} else if (*ex == 'k' || *ex == 'K') {
|
||||
f = 1e3;
|
||||
} else if (*ex == 'm' || *ex == 'M') {
|
||||
f = 1e-3;
|
||||
if (ex.test_without_case ("meg")) {
|
||||
f = 1e6;
|
||||
}
|
||||
} else if (*ex == 'u' || *ex == 'U') {
|
||||
f = 1e-6;
|
||||
} else if (*ex == 'n' || *ex == 'N') {
|
||||
f = 1e-9;
|
||||
} else if (*ex == 'p' || *ex == 'P') {
|
||||
f = 1e-12;
|
||||
} else if (*ex == 'f' || *ex == 'F') {
|
||||
f = 1e-15;
|
||||
} else if (*ex == 'a' || *ex == 'A') {
|
||||
f = 1e-18;
|
||||
}
|
||||
while (*ex && isalpha (*ex)) {
|
||||
++ex;
|
||||
}
|
||||
|
||||
v *= f;
|
||||
return v;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
double NetlistSpiceReader::read_bar_expr (tl::Extractor &ex)
|
||||
{
|
||||
double v = read_atomic_value (ex);
|
||||
while (true) {
|
||||
if (ex.test ("+")) {
|
||||
double vv = read_atomic_value (ex);
|
||||
v += vv;
|
||||
} else if (ex.test ("+")) {
|
||||
double vv = read_atomic_value (ex);
|
||||
v -= vv;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return v;
|
||||
}
|
||||
|
||||
double NetlistSpiceReader::read_dot_expr (tl::Extractor &ex)
|
||||
{
|
||||
double v = read_atomic_value (ex);
|
||||
while (true) {
|
||||
if (ex.test ("*")) {
|
||||
double vv = read_atomic_value (ex);
|
||||
v *= vv;
|
||||
} else if (ex.test ("/")) {
|
||||
double vv = read_atomic_value (ex);
|
||||
v /= vv;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return v;
|
||||
}
|
||||
|
||||
double NetlistSpiceReader::read_value (tl::Extractor &ex)
|
||||
{
|
||||
return read_dot_expr (ex);
|
||||
}
|
||||
|
||||
void NetlistSpiceReader::ensure_circuit ()
|
||||
{
|
||||
if (! mp_circuit) {
|
||||
|
||||
mp_circuit = new db::Circuit ();
|
||||
// TODO: make top name configurable
|
||||
mp_circuit->set_name (".TOP");
|
||||
mp_netlist->add_circuit (mp_circuit);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
db::Net *NetlistSpiceReader::make_net (const std::string &name)
|
||||
{
|
||||
db::Net *net = mp_circuit->net_by_name (name);
|
||||
if (! net) {
|
||||
net = new db::Net ();
|
||||
net->set_name (name);
|
||||
mp_circuit->add_net (net);
|
||||
}
|
||||
return net;
|
||||
}
|
||||
|
||||
void NetlistSpiceReader::read_subcircuit (tl::Extractor &ex)
|
||||
{
|
||||
std::string sc_name;
|
||||
ex.read_word_or_quoted (sc_name, "_.:,!+$/\\");
|
||||
|
||||
std::vector<std::string> nn;
|
||||
std::map<std::string, double> pv;
|
||||
|
||||
while (! ex.at_end ()) {
|
||||
|
||||
std::string n;
|
||||
ex.read_word_or_quoted (n, "_.:,!+$/\\");
|
||||
|
||||
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 circuit name given for subcircuit call")));
|
||||
}
|
||||
if (! pv.empty ()) {
|
||||
warn (tl::to_string (tr ("Circuit parameters are not allowed currently")));
|
||||
}
|
||||
|
||||
std::string nc = nn.back ();
|
||||
nn.pop_back ();
|
||||
|
||||
if (nn.empty ()) {
|
||||
error (tl::to_string (tr ("A circuit call needs at least one net")));
|
||||
}
|
||||
|
||||
db::Circuit *cc = mp_netlist->circuit_by_name (nc);
|
||||
if (! cc) {
|
||||
cc = new db::Circuit ();
|
||||
mp_netlist->add_circuit (cc);
|
||||
cc->set_name (nc);
|
||||
for (std::vector<std::string>::const_iterator i = nn.begin (); i != nn.end (); ++i) {
|
||||
cc->add_pin (std::string ());
|
||||
}
|
||||
} else {
|
||||
if (cc->pin_count () != nn.size ()) {
|
||||
error (tl::sprintf (tl::to_string (tr ("Pin count mismatch between circuit definition and circuit call: %d expected, got %d")), int (cc->pin_count ()), int (nn.size ())));
|
||||
}
|
||||
}
|
||||
|
||||
db::SubCircuit *sc = new db::SubCircuit (cc, sc_name);
|
||||
mp_circuit->add_subcircuit (sc);
|
||||
|
||||
for (std::vector<std::string>::const_iterator i = nn.begin (); i != nn.end (); ++i) {
|
||||
db::Net *net = make_net (*i);
|
||||
sc->connect_pin (i - nn.begin (), net);
|
||||
}
|
||||
|
||||
ex.expect_end ();
|
||||
}
|
||||
|
||||
void NetlistSpiceReader::read_circuit (tl::Extractor &ex)
|
||||
{
|
||||
std::string nc;
|
||||
ex.read_word_or_quoted (nc, "_.:,!+$/\\");
|
||||
|
||||
std::vector<std::string> nn;
|
||||
std::map<std::string, double> pv;
|
||||
|
||||
while (! ex.at_end ()) {
|
||||
|
||||
std::string n;
|
||||
ex.read_word_or_quoted (n, "_.:,!+$/\\");
|
||||
|
||||
if (ex.test ("=")) {
|
||||
// a parameter
|
||||
pv.insert (std::make_pair (tl::to_upper_case (n), read_value (ex)));
|
||||
} else {
|
||||
nn.push_back (n);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (! pv.empty ()) {
|
||||
warn (tl::to_string (tr ("Circuit parameters are not allowed currently")));
|
||||
}
|
||||
|
||||
db::Circuit *cc = mp_netlist->circuit_by_name (nc);
|
||||
if (! cc) {
|
||||
cc = new db::Circuit ();
|
||||
mp_netlist->add_circuit (cc);
|
||||
cc->set_name (nc);
|
||||
for (std::vector<std::string>::const_iterator i = nn.begin (); i != nn.end (); ++i) {
|
||||
cc->add_pin (std::string ());
|
||||
}
|
||||
} else {
|
||||
if (cc->pin_count () != nn.size ()) {
|
||||
error (tl::sprintf (tl::to_string (tr ("Pin count mismatch between implicit (through call) and explicit circuit definition: %d expected, got %d")), int (cc->pin_count ()), int (nn.size ())));
|
||||
}
|
||||
}
|
||||
|
||||
std::swap (cc, mp_circuit);
|
||||
|
||||
for (std::vector<std::string>::const_iterator i = nn.begin (); i != nn.end (); ++i) {
|
||||
db::Net *net = make_net (*i);
|
||||
mp_circuit->connect_pin (i - nn.begin (), net);
|
||||
}
|
||||
|
||||
while (! at_end ()) {
|
||||
if (read_element ()) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
std::swap (cc, mp_circuit);
|
||||
|
||||
ex.expect_end ();
|
||||
}
|
||||
|
||||
void NetlistSpiceReader::read_device (db::DeviceClass *dev_cls, size_t param_id, tl::Extractor &ex)
|
||||
{
|
||||
std::string dn;
|
||||
ex.read_word_or_quoted (dn, "_.:,!+$/\\");
|
||||
|
||||
std::vector<std::string> nn;
|
||||
|
||||
while (! ex.at_end () && nn.size () < 2) {
|
||||
nn.push_back (std::string ());
|
||||
ex.read_word_or_quoted (nn.back (), "_.:,!+$/\\");
|
||||
}
|
||||
|
||||
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;
|
||||
ex.read_word_or_quoted (dn, "_.:,!+$/\\");
|
||||
|
||||
std::vector<std::string> nn;
|
||||
std::map<std::string, double> pv;
|
||||
|
||||
while (! ex.at_end ()) {
|
||||
|
||||
std::string n;
|
||||
ex.read_word_or_quoted (n, "_.:,!+$/\\");
|
||||
|
||||
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 ()) {
|
||||
dev->set_parameter_value (i->id (), v->second);
|
||||
}
|
||||
}
|
||||
|
||||
ex.expect_end ();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,96 @@
|
|||
|
||||
/*
|
||||
|
||||
KLayout Layout Viewer
|
||||
Copyright (C) 2006-2019 Matthias Koefferlein
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
|
||||
*/
|
||||
|
||||
#ifndef HDR_dbNetlistSpiceReader
|
||||
#define HDR_dbNetlistSpiceReader
|
||||
|
||||
#include "dbCommon.h"
|
||||
#include "dbNetlistReader.h"
|
||||
#include "tlStream.h"
|
||||
|
||||
#include <string>
|
||||
#include <memory>
|
||||
|
||||
namespace db
|
||||
{
|
||||
|
||||
class Netlist;
|
||||
class Net;
|
||||
class Circuit;
|
||||
class DeviceClass;
|
||||
|
||||
/**
|
||||
* @brief A SPICE format reader for netlists
|
||||
*/
|
||||
class DB_PUBLIC NetlistSpiceReader
|
||||
: public NetlistReader
|
||||
{
|
||||
public:
|
||||
NetlistSpiceReader ();
|
||||
virtual ~NetlistSpiceReader ();
|
||||
|
||||
virtual void read (tl::InputStream &stream, db::Netlist &netlist);
|
||||
|
||||
private:
|
||||
db::Netlist *mp_netlist;
|
||||
db::Circuit *mp_circuit;
|
||||
std::auto_ptr<tl::TextInputStream> mp_stream;
|
||||
std::vector<std::pair<tl::InputStream *, tl::TextInputStream *> > m_streams;
|
||||
std::string m_stored_line;
|
||||
|
||||
void push_stream (const std::string &path);
|
||||
void pop_stream ();
|
||||
bool at_end ();
|
||||
void read_subcircuit (tl::Extractor &ex);
|
||||
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 ();
|
||||
double read_value (tl::Extractor &ex);
|
||||
double read_atomic_value (tl::Extractor &ex);
|
||||
double read_dot_expr (tl::Extractor &ex);
|
||||
double read_bar_expr (tl::Extractor &ex);
|
||||
std::string get_line ();
|
||||
void unget_line (const std::string &l);
|
||||
void error (const std::string &msg);
|
||||
void warn (const std::string &msg);
|
||||
void finish ();
|
||||
db::Net *make_net (const std::string &name);
|
||||
void ensure_circuit ();
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
namespace tl
|
||||
{
|
||||
|
||||
template <>
|
||||
struct type_traits<db::NetlistSpiceReader>
|
||||
: public tl::type_traits<void>
|
||||
{
|
||||
typedef tl::false_tag has_default_constructor;
|
||||
typedef tl::false_tag has_copy_constructor;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
@ -1,26 +1,4 @@
|
|||
|
||||
/*
|
||||
|
||||
KLayout Layout Viewer
|
||||
Copyright (C) 2006-2019 Matthias Koefferlein
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
|
||||
*/
|
||||
|
||||
|
||||
/*
|
||||
|
||||
KLayout Layout Viewer
|
||||
|
|
|
|||
|
|
@ -24,6 +24,8 @@
|
|||
#include "dbNetlist.h"
|
||||
#include "dbNetlistWriter.h"
|
||||
#include "dbNetlistSpiceWriter.h"
|
||||
#include "dbNetlistReader.h"
|
||||
#include "dbNetlistSpiceReader.h"
|
||||
#include "tlException.h"
|
||||
#include "tlInternational.h"
|
||||
#include "tlStream.h"
|
||||
|
|
@ -943,6 +945,13 @@ static void write_netlist (const db::Netlist *nl, const std::string &file, db::N
|
|||
writer->write (os, *nl, description);
|
||||
}
|
||||
|
||||
static void read_netlist (db::Netlist *nl, const std::string &file, db::NetlistReader *reader)
|
||||
{
|
||||
tl_assert (reader != 0);
|
||||
tl::InputStream os (file);
|
||||
reader->read (os, *nl);
|
||||
}
|
||||
|
||||
Class<db::Netlist> decl_dbNetlist ("db", "Netlist",
|
||||
gsi::method_ext ("add", &gsi::add_circuit, gsi::arg ("circuit"),
|
||||
"@brief Adds the circuit to the netlist\n"
|
||||
|
|
@ -1024,6 +1033,10 @@ Class<db::Netlist> decl_dbNetlist ("db", "Netlist",
|
|||
"Floating nets can be created as effect of reconnections of devices or pins. "
|
||||
"This method will eliminate all nets that make less than two connections."
|
||||
) +
|
||||
gsi::method_ext ("read", &read_netlist, gsi::arg ("file"), gsi::arg ("reader"),
|
||||
"@brief Writes the netlist to the given file using the given reader object to parse the file\n"
|
||||
"See \\NetlistSpiceReader for an example for a parser. "
|
||||
) +
|
||||
gsi::method_ext ("write", &write_netlist, gsi::arg ("file"), gsi::arg ("writer"), gsi::arg ("description", std::string ()),
|
||||
"@brief Writes the netlist to the given file using the given writer object to format the file\n"
|
||||
"See \\NetlistSpiceWriter for an example for a formatter. "
|
||||
|
|
@ -1255,4 +1268,31 @@ Class<db::NetlistSpiceWriter> db_NetlistSpiceWriter (db_NetlistWriter, "db", "Ne
|
|||
"This class has been introduced in version 0.26."
|
||||
);
|
||||
|
||||
Class<db::NetlistReader> db_NetlistReader ("db", "NetlistReader",
|
||||
gsi::Methods (),
|
||||
"@hide\n"
|
||||
);
|
||||
|
||||
db::NetlistSpiceReader *new_spice_reader ()
|
||||
{
|
||||
return new db::NetlistSpiceReader ();
|
||||
}
|
||||
|
||||
Class<db::NetlistSpiceReader> db_NetlistSpiceReader (db_NetlistReader, "db", "NetlistSpiceReader",
|
||||
gsi::constructor ("new", &new_spice_reader,
|
||||
"@brief Creates a new reader.\n"
|
||||
),
|
||||
"@brief Implements a netlist Reader for the SPICE format.\n"
|
||||
"Use the SPICE reader like this:\n"
|
||||
"\n"
|
||||
"@code\n"
|
||||
"writer = RBA::NetlistSpiceReader::new\n"
|
||||
"netlist = RBA::Netlist::new\n"
|
||||
"netlist.read(path, reader)\n"
|
||||
"@endcode\n"
|
||||
"\n"
|
||||
"This class has been introduced in version 0.26."
|
||||
);
|
||||
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,125 @@
|
|||
|
||||
/*
|
||||
|
||||
KLayout Layout Viewer
|
||||
Copyright (C) 2006-2019 Matthias Koefferlein
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
|
||||
*/
|
||||
|
||||
#include "dbNetlistSpiceReader.h"
|
||||
#include "dbNetlist.h"
|
||||
#include "dbNetlistDeviceClasses.h"
|
||||
|
||||
#include "tlUnitTest.h"
|
||||
#include "tlStream.h"
|
||||
#include "tlFileUtils.h"
|
||||
|
||||
TEST(1_BasicReader)
|
||||
{
|
||||
db::Netlist nl;
|
||||
|
||||
std::string path = tl::combine_path (tl::combine_path (tl::combine_path (tl::testsrc (), "testdata"), "algo"), "nreader1.cir");
|
||||
|
||||
db::NetlistSpiceReader reader;
|
||||
tl::InputStream is (path);
|
||||
reader.read (is, nl);
|
||||
|
||||
EXPECT_EQ (nl.to_string (),
|
||||
"circuit TOP ();\n"
|
||||
" device RES $1 (A='6',B='1') (R=7650);\n"
|
||||
" device RES $2 (A='3',B='1') (R=7650);\n"
|
||||
" device RES $3 (A='3',B='2') (R=2670);\n"
|
||||
" device MHVPMOS $4 (S='6',G='4',D='7',B='7') (L=2.5e-07,W=1.5e-06,AS=0,AD=0,PS=3.84e-06,PD=3.84e-06);\n"
|
||||
"end;\n"
|
||||
);
|
||||
}
|
||||
|
||||
TEST(2_ReaderWithSubcircuits)
|
||||
{
|
||||
db::Netlist nl;
|
||||
|
||||
std::string path = tl::combine_path (tl::combine_path (tl::combine_path (tl::testsrc (), "testdata"), "algo"), "nreader2.cir");
|
||||
|
||||
db::NetlistSpiceReader reader;
|
||||
tl::InputStream is (path);
|
||||
reader.read (is, nl);
|
||||
|
||||
EXPECT_EQ (nl.to_string (),
|
||||
"circuit RINGO ($1='11',$2='12',$3='13',$4='14',$5='15');\n"
|
||||
" subcircuit ND2X1 $1 ($1='12',$2='1',$3='15',$4='12',$5='11',$6='14',$7='15');\n"
|
||||
" subcircuit INVX1 $2 ($1='12',$2='2',$3='15',$4='12',$5='1',$6='15');\n"
|
||||
" subcircuit INVX1 $3 ($1='12',$2='3',$3='15',$4='12',$5='2',$6='15');\n"
|
||||
" subcircuit INVX1 $4 ($1='12',$2='4',$3='15',$4='12',$5='3',$6='15');\n"
|
||||
" subcircuit INVX1 $5 ($1='12',$2='5',$3='15',$4='12',$5='4',$6='15');\n"
|
||||
" subcircuit INVX1 $6 ($1='12',$2='6',$3='15',$4='12',$5='5',$6='15');\n"
|
||||
" subcircuit INVX1 $7 ($1='12',$2='7',$3='15',$4='12',$5='6',$6='15');\n"
|
||||
" subcircuit INVX1 $8 ($1='12',$2='8',$3='15',$4='12',$5='7',$6='15');\n"
|
||||
" subcircuit INVX1 $9 ($1='12',$2='9',$3='15',$4='12',$5='8',$6='15');\n"
|
||||
" subcircuit INVX1 $10 ($1='12',$2='10',$3='15',$4='12',$5='9',$6='15');\n"
|
||||
" subcircuit INVX1 $11 ($1='12',$2='11',$3='15',$4='12',$5='10',$6='15');\n"
|
||||
" subcircuit INVX1 $12 ($1='12',$2='13',$3='15',$4='12',$5='11',$6='15');\n"
|
||||
"end;\n"
|
||||
"circuit ND2X1 ($1='1',$2='2',$3='3',$4='4',$5='5',$6='6',$7='7');\n"
|
||||
" device MLVPMOS $1 (S='2',G='6',D='1',B='4') (L=2.5e-07,W=1.5e-06,AS=0,AD=0,PS=3.85e-06,PD=1.95e-06);\n"
|
||||
" device MLVPMOS $2 (S='1',G='5',D='2',B='4') (L=2.5e-07,W=1.5e-06,AS=0,AD=0,PS=1.95e-06,PD=3.85e-06);\n"
|
||||
" device MLVNMOS $3 (S='3',G='6',D='8',B='7') (L=2.5e-07,W=9.5e-07,AS=0,AD=0,PS=2.75e-06,PD=1.4e-06);\n"
|
||||
" device MLVNMOS $4 (S='8',G='5',D='2',B='7') (L=2.5e-07,W=9.5e-07,AS=0,AD=0,PS=1.4e-06,PD=2.75e-06);\n"
|
||||
"end;\n"
|
||||
"circuit INVX1 ($1='1',$2='2',$3='3',$4='4',$5='5',$6='6');\n"
|
||||
" device MLVPMOS $1 (S='1',G='5',D='2',B='4') (L=2.5e-07,W=1.5e-06,AS=0,AD=0,PS=3.85e-06,PD=3.85e-06);\n"
|
||||
" device MLVNMOS $2 (S='3',G='5',D='2',B='6') (L=2.5e-07,W=9.5e-07,AS=0,AD=0,PS=2.75e-06,PD=2.75e-06);\n"
|
||||
"end;\n"
|
||||
);
|
||||
}
|
||||
|
||||
TEST(3_ReaderWithSubcircuitsAltOrder)
|
||||
{
|
||||
db::Netlist nl;
|
||||
|
||||
std::string path = tl::combine_path (tl::combine_path (tl::combine_path (tl::testsrc (), "testdata"), "algo"), "nreader3.cir");
|
||||
|
||||
db::NetlistSpiceReader reader;
|
||||
tl::InputStream is (path);
|
||||
reader.read (is, nl);
|
||||
|
||||
EXPECT_EQ (nl.to_string (),
|
||||
"circuit INVX1 ($1='1',$2='2',$3='3',$4='4',$5='5',$6='6');\n"
|
||||
" device MLVPMOS $1 (S='1',G='5',D='2',B='4') (L=2.5e-07,W=1.5e-06,AS=0,AD=0,PS=0,PD=0);\n"
|
||||
" device MLVNMOS $2 (S='3',G='5',D='2',B='6') (L=2.5e-07,W=9.5e-07,AS=0,AD=0,PS=0,PD=0);\n"
|
||||
"end;\n"
|
||||
"circuit ND2X1 ($1='1',$2='2',$3='3',$4='4',$5='5',$6='6',$7='7');\n"
|
||||
" device MLVPMOS $1 (S='2',G='6',D='1',B='4') (L=2.5e-07,W=1.5e-06,AS=0,AD=0,PS=0,PD=0);\n"
|
||||
" device MLVPMOS $2 (S='1',G='5',D='2',B='4') (L=2.5e-07,W=1.5e-06,AS=0,AD=0,PS=0,PD=0);\n"
|
||||
" device MLVNMOS $3 (S='3',G='6',D='8',B='7') (L=2.5e-07,W=9.5e-07,AS=0,AD=0,PS=0,PD=0);\n"
|
||||
" device MLVNMOS $4 (S='8',G='5',D='2',B='7') (L=2.5e-07,W=9.5e-07,AS=0,AD=0,PS=0,PD=0);\n"
|
||||
"end;\n"
|
||||
"circuit RINGO ($1='11',$2='12',$3='13',$4='14',$5='15');\n"
|
||||
" subcircuit ND2X1 $1 ($1='12',$2='1',$3='15',$4='12',$5='11',$6='14',$7='15');\n"
|
||||
" subcircuit INVX1 $2 ($1='12',$2='2',$3='15',$4='12',$5='1',$6='15');\n"
|
||||
" subcircuit INVX1 $3 ($1='12',$2='3',$3='15',$4='12',$5='2',$6='15');\n"
|
||||
" subcircuit INVX1 $4 ($1='12',$2='4',$3='15',$4='12',$5='3',$6='15');\n"
|
||||
" subcircuit INVX1 $5 ($1='12',$2='5',$3='15',$4='12',$5='4',$6='15');\n"
|
||||
" subcircuit INVX1 $6 ($1='12',$2='6',$3='15',$4='12',$5='5',$6='15');\n"
|
||||
" subcircuit INVX1 $7 ($1='12',$2='7',$3='15',$4='12',$5='6',$6='15');\n"
|
||||
" subcircuit INVX1 $8 ($1='12',$2='8',$3='15',$4='12',$5='7',$6='15');\n"
|
||||
" subcircuit INVX1 $9 ($1='12',$2='9',$3='15',$4='12',$5='8',$6='15');\n"
|
||||
" subcircuit INVX1 $10 ($1='12',$2='10',$3='15',$4='12',$5='9',$6='15');\n"
|
||||
" subcircuit INVX1 $11 ($1='12',$2='11',$3='15',$4='12',$5='10',$6='15');\n"
|
||||
" subcircuit INVX1 $12 ($1='12',$2='13',$3='15',$4='12',$5='11',$6='15');\n"
|
||||
"end;\n"
|
||||
);
|
||||
}
|
||||
|
|
@ -70,7 +70,8 @@ SOURCES = \
|
|||
dbCellVariantsTests.cc \
|
||||
dbDeepEdgesTests.cc \
|
||||
dbDeepEdgePairsTests.cc \
|
||||
dbNetlistCompareTests.cc
|
||||
dbNetlistCompareTests.cc \
|
||||
dbNetlistReaderTests.cc
|
||||
|
||||
INCLUDEPATH += $$TL_INC $$DB_INC $$GSI_INC
|
||||
DEPENDPATH += $$TL_INC $$DB_INC $$GSI_INC
|
||||
|
|
|
|||
|
|
@ -42,7 +42,7 @@ namespace tl
|
|||
static std::locale c_locale ("C");
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// lower and upper case for wchar_t
|
||||
// lower and upper case for wchar_t and uint32_t
|
||||
|
||||
#include "utf_casefolding.h"
|
||||
|
||||
|
|
@ -66,9 +66,44 @@ wchar_t wupcase (wchar_t c)
|
|||
}
|
||||
}
|
||||
|
||||
uint32_t utf32_downcase (uint32_t c32)
|
||||
{
|
||||
if (sizeof (wchar_t) == 2 && c32 >= 0x10000) {
|
||||
return c32;
|
||||
} else {
|
||||
return uint32_t (wdowncase (wchar_t (c32)));
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t utf32_upcase (uint32_t c32)
|
||||
{
|
||||
if (sizeof (wchar_t) == 2 && c32 >= 0x10000) {
|
||||
return c32;
|
||||
} else {
|
||||
return uint32_t (wupcase (wchar_t (c32)));
|
||||
}
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Conversion of UTF8 to wchar_t
|
||||
|
||||
uint32_t utf32_from_utf8 (const char *&cp, const char *cpe = 0)
|
||||
{
|
||||
uint32_t c32 = (unsigned char) *cp++;
|
||||
if (c32 >= 0xf0 && ((cpe && cp + 2 < cpe) || (! cpe && cp [0] && cp [1] && cp [2]))) {
|
||||
c32 = ((c32 & 0x7) << 18) | ((uint32_t (cp [0]) & 0x3f) << 12) | ((uint32_t (cp [1]) & 0x3f) << 6) | (uint32_t (cp [2]) & 0x3f);
|
||||
cp += 3;
|
||||
} else if (c32 >= 0xe0 && ((cpe && cp + 1 < cpe) || (! cpe && cp [0] && cp [1]))) {
|
||||
c32 = ((c32 & 0xf) << 12) | ((uint32_t (cp [0]) & 0x3f) << 6) | (uint32_t (cp [1]) & 0x3f);
|
||||
cp += 2;
|
||||
} else if (c32 >= 0xc0 && ((cpe && cp < cpe) || (! cpe && cp [0]))) {
|
||||
c32 = ((c32 & 0x1f) << 6) | (uint32_t (*cp) & 0x3f);
|
||||
++cp;
|
||||
}
|
||||
|
||||
return c32;
|
||||
}
|
||||
|
||||
std::wstring to_wstring (const std::string &s)
|
||||
{
|
||||
std::wstring ws;
|
||||
|
|
@ -76,17 +111,7 @@ std::wstring to_wstring (const std::string &s)
|
|||
const char *cpe = s.c_str () + s.size ();
|
||||
for (const char *cp = s.c_str (); cp < cpe; ) {
|
||||
|
||||
uint32_t c32 = (unsigned char) *cp++;
|
||||
if (c32 >= 0xf0 && cp + 2 < cpe) {
|
||||
c32 = ((c32 & 0x7) << 18) | ((uint32_t (cp [0]) & 0x3f) << 12) | ((uint32_t (cp [1]) & 0x3f) << 6) | (uint32_t (cp [2]) & 0x3f);
|
||||
cp += 3;
|
||||
} else if (c32 >= 0xe0 && cp + 1 < cpe) {
|
||||
c32 = ((c32 & 0xf) << 12) | ((uint32_t (cp [0]) & 0x3f) << 6) | (uint32_t (cp [1]) & 0x3f);
|
||||
cp += 2;
|
||||
} else if (c32 >= 0xc0 && cp < cpe) {
|
||||
c32 = ((c32 & 0x1f) << 6) | (uint32_t (*cp) & 0x3f);
|
||||
++cp;
|
||||
}
|
||||
uint32_t c32 = utf32_from_utf8 (cp, cpe);
|
||||
|
||||
if (sizeof (wchar_t) == 2 && c32 >= 0x10000) {
|
||||
c32 -= 0x10000;
|
||||
|
|
@ -1324,6 +1349,28 @@ Extractor::test (const char *token)
|
|||
}
|
||||
}
|
||||
|
||||
bool
|
||||
Extractor::test_without_case (const char *token)
|
||||
{
|
||||
skip ();
|
||||
|
||||
const char *cp = m_cp;
|
||||
while (*cp && *token) {
|
||||
uint32_t c = utf32_downcase (utf32_from_utf8 (cp));
|
||||
uint32_t ct = utf32_downcase (utf32_from_utf8 (token));
|
||||
if (c != ct) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (! *token) {
|
||||
m_cp = cp;
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
const char *
|
||||
Extractor::skip ()
|
||||
{
|
||||
|
|
|
|||
|
|
@ -669,6 +669,13 @@ public:
|
|||
*/
|
||||
bool test (const char *token);
|
||||
|
||||
/**
|
||||
* @brief Test for a token (a certain string) in case-insensitive mode
|
||||
*
|
||||
* If the token is not present, return false.
|
||||
*/
|
||||
bool test_without_case (const char *token);
|
||||
|
||||
/**
|
||||
* @brief Skip blanks
|
||||
*
|
||||
|
|
|
|||
|
|
@ -388,6 +388,33 @@ TEST(8)
|
|||
EXPECT_EQ (x.try_read_quoted (s), true);
|
||||
EXPECT_EQ (s, "a_word\'!");
|
||||
EXPECT_EQ (x.at_end (), true);
|
||||
|
||||
x = Extractor (" foobar");
|
||||
EXPECT_EQ (x.test ("foo"), true);
|
||||
EXPECT_EQ (x.test ("bar"), true);
|
||||
|
||||
x = Extractor (" foo bar");
|
||||
EXPECT_EQ (x.test ("foo"), true);
|
||||
EXPECT_EQ (x.test ("bar"), true);
|
||||
|
||||
x = Extractor (" FOObar");
|
||||
EXPECT_EQ (x.test ("foo"), false);
|
||||
EXPECT_EQ (x.test ("BAR"), false);
|
||||
|
||||
x = Extractor (" FOObar");
|
||||
EXPECT_EQ (x.test_without_case ("foo"), true);
|
||||
EXPECT_EQ (x.test_without_case ("BAR"), true);
|
||||
|
||||
x = Extractor (" µm");
|
||||
EXPECT_EQ (x.test ("µm"), true);
|
||||
|
||||
x = Extractor (" µM");
|
||||
EXPECT_EQ (x.test ("µm"), false);
|
||||
EXPECT_EQ (x.test_without_case ("µm"), true);
|
||||
|
||||
x = Extractor (" µm");
|
||||
EXPECT_EQ (x.test ("µM"), false);
|
||||
EXPECT_EQ (x.test_without_case ("µM"), true);
|
||||
}
|
||||
|
||||
TEST(9)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,17 @@
|
|||
* VDIV netlist before simplification
|
||||
|
||||
* cell TOP
|
||||
.SUBCKT TOP
|
||||
* net 1 OUT
|
||||
* net 2 GND
|
||||
* net 4 IN
|
||||
* net 7 VDD
|
||||
* device instance $1 1.025,0.335 RES
|
||||
R$1 6 1 7650
|
||||
* device instance $2 2.85,0.335 RES
|
||||
R$2 3 1 7650
|
||||
* device instance $3 4.665,0.335 RES
|
||||
R$3 3 2 2670
|
||||
* device instance $4 1.765,7.485 HVPMOS
|
||||
M$4 6 4 7 7 MHVPMOS L=0.25U W=1.5U AS=0.63P AD=0.63P PS=3.84U PD=3.84U
|
||||
.ENDS TOP
|
||||
|
|
@ -0,0 +1,83 @@
|
|||
* RINGO netlist after simplification
|
||||
|
||||
* cell RINGO
|
||||
* pin FB
|
||||
* pin VDD
|
||||
* pin OUT
|
||||
* pin ENABLE
|
||||
* pin BULK,VSS
|
||||
.SUBCKT RINGO 11 12 13 14 15
|
||||
* net 11 FB
|
||||
* net 12 VDD
|
||||
* net 13 OUT
|
||||
* net 14 ENABLE
|
||||
* net 15 BULK,VSS
|
||||
* cell instance $1 r0 *1 1.8,0
|
||||
X$1 12 1 15 12 11 14 15 ND2X1
|
||||
* cell instance $2 r0 *1 4.2,0
|
||||
X$2 12 2 15 12 1 15 INVX1
|
||||
* cell instance $3 r0 *1 6,0
|
||||
X$3 12 3 15 12 2 15 INVX1
|
||||
* cell instance $4 r0 *1 7.8,0
|
||||
X$4 12 4 15 12 3 15 INVX1
|
||||
* cell instance $5 r0 *1 9.6,0
|
||||
X$5 12 5 15 12 4 15 INVX1
|
||||
* cell instance $6 r0 *1 11.4,0
|
||||
X$6 12 6 15 12 5 15 INVX1
|
||||
* cell instance $7 r0 *1 13.2,0
|
||||
X$7 12 7 15 12 6 15 INVX1
|
||||
* cell instance $8 r0 *1 15,0
|
||||
X$8 12 8 15 12 7 15 INVX1
|
||||
* cell instance $9 r0 *1 16.8,0
|
||||
X$9 12 9 15 12 8 15 INVX1
|
||||
* cell instance $10 r0 *1 18.6,0
|
||||
X$10 12 10 15 12 9 15 INVX1
|
||||
* cell instance $11 r0 *1 20.4,0
|
||||
X$11 12 11 15 12 10 15 INVX1
|
||||
* cell instance $12 r0 *1 22.2,0
|
||||
X$12 12 13 15 12 11 15 INVX1
|
||||
.ENDS RINGO
|
||||
|
||||
* cell ND2X1
|
||||
* pin VDD
|
||||
* pin OUT
|
||||
* pin VSS
|
||||
* pin
|
||||
* pin B
|
||||
* pin A
|
||||
* pin BULK
|
||||
.SUBCKT ND2X1 1 2 3 4 5 6 7
|
||||
* net 1 VDD
|
||||
* net 2 OUT
|
||||
* net 3 VSS
|
||||
* net 5 B
|
||||
* net 6 A
|
||||
* net 7 BULK
|
||||
* device instance $1 0.85,5.8 LVPMOS
|
||||
M$1 2 6 1 4 MLVPMOS L=0.25U W=1.5U AS=0.6375P AD=0.3375P PS=3.85U PD=1.95U
|
||||
* device instance $2 1.55,5.8 LVPMOS
|
||||
M$2 1 5 2 4 MLVPMOS L=0.25U W=1.5U AS=0.3375P AD=0.6375P PS=1.95U PD=3.85U
|
||||
* device instance $3 0.85,2.135 LVNMOS
|
||||
M$3 3 6 8 7 MLVNMOS L=0.25U W=0.95U AS=0.40375P AD=0.21375P PS=2.75U PD=1.4U
|
||||
* device instance $4 1.55,2.135 LVNMOS
|
||||
M$4 8 5 2 7 MLVNMOS L=0.25U W=0.95U AS=0.21375P AD=0.40375P PS=1.4U PD=2.75U
|
||||
.ENDS ND2X1
|
||||
|
||||
* cell INVX1
|
||||
* pin VDD
|
||||
* pin OUT
|
||||
* pin VSS
|
||||
* pin
|
||||
* pin IN
|
||||
* pin BULK
|
||||
.SUBCKT INVX1 1 2 3 4 5 6
|
||||
* net 1 VDD
|
||||
* net 2 OUT
|
||||
* net 3 VSS
|
||||
* net 5 IN
|
||||
* net 6 BULK
|
||||
* device instance $1 0.85,5.8 LVPMOS
|
||||
M$1 1 5 2 4 MLVPMOS L=0.25U W=1.5U AS=0.6375P AD=0.6375P PS=3.85U PD=3.85U
|
||||
* device instance $2 0.85,2.135 LVNMOS
|
||||
M$2 3 5 2 6 MLVNMOS L=0.25U W=0.95U AS=0.40375P AD=0.40375P PS=2.75U PD=2.75U
|
||||
.ENDS INVX1
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
.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
|
||||
.ends
|
||||
|
||||
.subckt ND2X1 1 2 3 4 5 6 7
|
||||
m$1 2 6 1 4 MLVPMOS L=0.25um W=1.5um
|
||||
m$2 1 5 2 4 MLVPMOS L=0.25um W=1.5um
|
||||
m$3 3 6 8 7 MLVNMOS L=0.25um W=0.95um
|
||||
m$4 8 5 2 7 MLVNMOS L=0.25um W=0.95um
|
||||
.ends ND2X1
|
||||
|
||||
.subckt RINGO 11 12 13 14 15
|
||||
x$1 12 1 15 12 11 14 15 ND2X1
|
||||
x$2 12 2 15 12 1 15 INVX1
|
||||
x$3 12 3 15 12 2 15 INVX1
|
||||
x$4 12 4 15 12 3 15 INVX1
|
||||
x$5 12 5 15 12 4 15 INVX1
|
||||
x$6 12 6 15 12 5 15 INVX1
|
||||
x$7 12 7 15 12 6 15 INVX1
|
||||
x$8 12 8 15 12 7 15 INVX1
|
||||
x$9 12 9 15 12 8 15 INVX1
|
||||
x$10 12 10 15 12 9 15 INVX1
|
||||
x$11 12 11 15 12 10 15 INVX1
|
||||
x$12 12 13 15 12 11 15 INVX1
|
||||
.ends RINGO
|
||||
|
||||
Loading…
Reference in New Issue