mirror of https://github.com/KLayout/klayout.git
WIP: SPICE reader delegate, unit tests + debugging
This commit is contained in:
parent
d174fb73fd
commit
343e340e22
|
|
@ -55,6 +55,11 @@ void NetlistSpiceReaderDelegate::finish (db::Netlist * /*netlist*/)
|
|||
// .. nothing yet ..
|
||||
}
|
||||
|
||||
bool NetlistSpiceReaderDelegate::wants_subcircuit (const std::string & /*circuit_name*/)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
void NetlistSpiceReaderDelegate::error (const std::string &msg)
|
||||
{
|
||||
throw tl::Exception (msg);
|
||||
|
|
@ -160,8 +165,8 @@ bool NetlistSpiceReaderDelegate::element (db::Circuit *circuit, const std::strin
|
|||
|
||||
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)
|
||||
NetlistSpiceReader::NetlistSpiceReader (NetlistSpiceReaderDelegate *delegate)
|
||||
: mp_netlist (0), mp_stream (0), mp_delegate (delegate)
|
||||
{
|
||||
static NetlistSpiceReaderDelegate std_delegate;
|
||||
if (! delegate) {
|
||||
|
|
@ -297,6 +302,18 @@ void NetlistSpiceReader::unget_line (const std::string &l)
|
|||
m_stored_line = l;
|
||||
}
|
||||
|
||||
bool NetlistSpiceReader::subcircuit_captured (const std::string &nc_name)
|
||||
{
|
||||
std::map<std::string, bool>::const_iterator c = m_captured.find (nc_name);
|
||||
if (c != m_captured.end ()) {
|
||||
return c->second;
|
||||
} else {
|
||||
bool cap = mp_delegate->wants_subcircuit (nc_name);
|
||||
m_captured.insert (std::make_pair (nc_name, cap));
|
||||
return cap;
|
||||
}
|
||||
}
|
||||
|
||||
bool NetlistSpiceReader::read_card ()
|
||||
{
|
||||
std::string l = get_line ();
|
||||
|
|
@ -318,7 +335,12 @@ bool NetlistSpiceReader::read_card ()
|
|||
|
||||
} else if (ex.test_without_case ("subckt")) {
|
||||
|
||||
read_circuit (ex);
|
||||
std::string nc = read_name (ex);
|
||||
if (subcircuit_captured (nc)) {
|
||||
skip_circuit (ex);
|
||||
} else {
|
||||
read_circuit (ex, nc);
|
||||
}
|
||||
|
||||
} else if (ex.test_without_case ("ends")) {
|
||||
|
||||
|
|
@ -347,12 +369,12 @@ bool NetlistSpiceReader::read_card ()
|
|||
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)) {
|
||||
if (! read_element (ex, es, name)) {
|
||||
warn (tl::sprintf (tl::to_string (tr ("Element type '%c' ignored")), next_char));
|
||||
}
|
||||
|
||||
ex.expect_end ();
|
||||
|
||||
} else {
|
||||
warn (tl::to_string (tr ("Line ignored")));
|
||||
}
|
||||
|
|
@ -675,58 +697,67 @@ bool NetlistSpiceReader::read_element (tl::Extractor &ex, const std::string &ele
|
|||
nets.push_back (make_net (*i));
|
||||
}
|
||||
|
||||
return mp_delegate->element (mp_circuit, element, name, tl::to_upper_case (model), value, nets, pv);
|
||||
if (element == "X" && ! subcircuit_captured (model)) {
|
||||
if (! pv.empty ()) {
|
||||
warn (tl::to_string (tr ("Circuit parameters are not allowed currently")));
|
||||
}
|
||||
read_subcircuit (name, tl::to_upper_case (model), nets);
|
||||
return true;
|
||||
} else {
|
||||
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)
|
||||
void NetlistSpiceReader::read_subcircuit (const std::string &sc_name, const std::string &nc_name, const std::vector<db::Net *> &nets)
|
||||
{
|
||||
std::vector<std::string> nn;
|
||||
std::map<std::string, double> pv;
|
||||
read_pin_and_parameters (ex, nn, pv);
|
||||
|
||||
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 ()) {
|
||||
if (nets.empty ()) {
|
||||
error (tl::to_string (tr ("A circuit call needs at least one net")));
|
||||
}
|
||||
|
||||
db::Circuit *cc = mp_netlist->circuit_by_name (nc);
|
||||
db::Circuit *cc = mp_netlist->circuit_by_name (nc_name);
|
||||
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->set_name (nc_name);
|
||||
for (std::vector<db::Net *>::const_iterator i = nets.begin (); i != nets.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 ())));
|
||||
if (cc->pin_count () != nets.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 (nets.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);
|
||||
for (std::vector<db::Net *>::const_iterator i = nets.begin (); i != nets.end (); ++i) {
|
||||
sc->connect_pin (i - nets.begin (), *i);
|
||||
}
|
||||
|
||||
ex.expect_end ();
|
||||
}
|
||||
|
||||
void NetlistSpiceReader::read_circuit (tl::Extractor &ex)
|
||||
void NetlistSpiceReader::skip_circuit (tl::Extractor & /*ex*/)
|
||||
{
|
||||
std::string nc = read_name (ex);
|
||||
while (! at_end ()) {
|
||||
|
||||
std::string l = get_line ();
|
||||
tl::Extractor ex (l.c_str ());
|
||||
if (ex.test_without_case (".")) {
|
||||
|
||||
// control statement
|
||||
if (ex.test_without_case ("subckt")) {
|
||||
skip_circuit (ex);
|
||||
} else if (ex.test_without_case ("ends")) {
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
void NetlistSpiceReader::read_circuit (tl::Extractor &ex, const std::string &nc)
|
||||
{
|
||||
std::vector<std::string> nn;
|
||||
std::map<std::string, double> pv;
|
||||
read_pin_and_parameters (ex, nn, pv);
|
||||
|
|
|
|||
|
|
@ -26,7 +26,6 @@
|
|||
#include "dbCommon.h"
|
||||
#include "dbNetlistReader.h"
|
||||
#include "tlStream.h"
|
||||
#include "tlGlobPattern.h"
|
||||
|
||||
#include <string>
|
||||
#include <memory>
|
||||
|
|
@ -57,8 +56,8 @@ public:
|
|||
*
|
||||
* 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.
|
||||
* For receiving subcircuit elements, the delegate needs to indicate
|
||||
* this by returning true upon "wants_subcircuit".
|
||||
*/
|
||||
class DB_PUBLIC NetlistSpiceReaderDelegate
|
||||
: public tl::Object
|
||||
|
|
@ -77,16 +76,23 @@ public:
|
|||
*/
|
||||
virtual void finish (db::Netlist *netlist);
|
||||
|
||||
/**
|
||||
* @brief Returns true, if the delegate wants subcircuit elements with this name
|
||||
*
|
||||
* The name is always upper case.
|
||||
*/
|
||||
virtual bool wants_subcircuit (const std::string &circuit_name);
|
||||
|
||||
/**
|
||||
* @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.
|
||||
* @param circuit The circuit that is currently read.
|
||||
* @param element The upper-case element code ("M", "R", ...).
|
||||
* @param name The element's name.
|
||||
* @param model The upper-case model name (may be empty).
|
||||
* @param value The default value (e.g. registance for resistors) and may be zero.
|
||||
* @param nets The nets given in the element line.
|
||||
* @param parameters The parameters of the element statement (parameter names are upper case).
|
||||
*
|
||||
* The default implementation will create corresponding devices for
|
||||
* some known elements using the Spice writer's parameter conventions.
|
||||
|
|
@ -108,7 +114,7 @@ class DB_PUBLIC NetlistSpiceReader
|
|||
: public NetlistReader
|
||||
{
|
||||
public:
|
||||
NetlistSpiceReader (NetlistSpiceReaderDelegate *delegate = 0, const std::string &captured_subcircuits = std::string ());
|
||||
NetlistSpiceReader (NetlistSpiceReaderDelegate *delegate = 0);
|
||||
virtual ~NetlistSpiceReader ();
|
||||
|
||||
virtual void read (tl::InputStream &stream, db::Netlist &netlist);
|
||||
|
|
@ -121,15 +127,16 @@ private:
|
|||
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;
|
||||
std::map<std::string, bool> 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);
|
||||
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_subcircuit (const std::string &sc_name, const std::string &nc_name, const std::vector<db::Net *> &nets);
|
||||
void read_circuit (tl::Extractor &ex, const std::string &name);
|
||||
void skip_circuit (tl::Extractor &ex);
|
||||
bool read_card ();
|
||||
double read_value (tl::Extractor &ex);
|
||||
std::string read_name (tl::Extractor &ex);
|
||||
|
|
@ -143,6 +150,7 @@ private:
|
|||
void finish ();
|
||||
db::Net *make_net (const std::string &name);
|
||||
void ensure_circuit ();
|
||||
bool subcircuit_captured (const std::string &nc_name);
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -177,3 +177,80 @@ TEST(5_CircuitParameters)
|
|||
"end;\n"
|
||||
);
|
||||
}
|
||||
|
||||
class MyNetlistReaderDelegate
|
||||
: public db::NetlistSpiceReaderDelegate
|
||||
{
|
||||
public:
|
||||
MyNetlistReaderDelegate () : db::NetlistSpiceReaderDelegate () { }
|
||||
|
||||
bool wants_subcircuit (const std::string &circuit_name)
|
||||
{
|
||||
return circuit_name == "HVNMOS" || circuit_name == "HVPMOS";
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
if (element == "X") {
|
||||
|
||||
if (nets.size () != 4) {
|
||||
error (tl::sprintf ("Device subcircuit '%s' requires four nets", model));
|
||||
}
|
||||
|
||||
db::DeviceClass *cls = circuit->netlist ()->device_class_by_name (model);
|
||||
if (! cls) {
|
||||
cls = new db::DeviceClassMOS4Transistor ();
|
||||
cls->set_name (model);
|
||||
circuit->netlist ()->add_device_class (cls);
|
||||
}
|
||||
|
||||
db::Device *device = new db::Device (cls);
|
||||
device->set_name (name);
|
||||
circuit->add_device (device);
|
||||
|
||||
device->connect_terminal (db::DeviceClassMOS4Transistor::terminal_id_S, nets [0]);
|
||||
device->connect_terminal (db::DeviceClassMOS4Transistor::terminal_id_G, nets [1]);
|
||||
device->connect_terminal (db::DeviceClassMOS4Transistor::terminal_id_D, nets [2]);
|
||||
device->connect_terminal (db::DeviceClassMOS4Transistor::terminal_id_B, nets [3]);
|
||||
|
||||
const std::vector<db::DeviceParameterDefinition> &td = cls->parameter_definitions ();
|
||||
for (std::vector<db::DeviceParameterDefinition>::const_iterator i = td.begin (); i != td.end (); ++i) {
|
||||
std::map<std::string, double>::const_iterator pi = params.find (i->name ());
|
||||
if (pi != params.end ()) {
|
||||
device->set_parameter_value (i->id (), pi->second * 1.5);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
} else {
|
||||
return db::NetlistSpiceReaderDelegate::element (circuit, element, name, model, value, nets, params);
|
||||
}
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
TEST(6_ReaderWithDelegate)
|
||||
{
|
||||
db::Netlist nl;
|
||||
|
||||
std::string path = tl::combine_path (tl::combine_path (tl::combine_path (tl::testsrc (), "testdata"), "algo"), "nreader6.cir");
|
||||
|
||||
MyNetlistReaderDelegate delegate;
|
||||
db::NetlistSpiceReader reader (&delegate);
|
||||
tl::InputStream is (path);
|
||||
reader.read (is, nl);
|
||||
|
||||
EXPECT_EQ (nl.to_string (),
|
||||
"circuit SUBCKT ($1=$1,$2=A,$3=VDD,$4=Z,$5=gnd,$6=gnd$1);\n"
|
||||
" device HVPMOS $1 (S=VDD,G=$3,D=Z,B=$1) (L=0.3,W=1.5,AS=0.27,AD=0.27,PS=3.24,PD=3.24);\n"
|
||||
" device HVPMOS $2 (S=VDD,G=A,D=$3,B=$1) (L=0.3,W=1.5,AS=0.27,AD=0.27,PS=3.24,PD=3.24);\n"
|
||||
" device HVNMOS $3 (S=gnd,G=$3,D=gnd,B=gnd$1) (L=1.695,W=3.18,AS=0,AD=0,PS=9,PD=9);\n"
|
||||
" device HVNMOS $4 (S=gnd,G=$3,D=Z,B=gnd$1) (L=0.6,W=0.6,AS=0.285,AD=0.285,PS=1.74,PD=1.74);\n"
|
||||
" device HVNMOS $5 (S=gnd,G=A,D=$3,B=gnd$1) (L=0.6,W=0.6,AS=0.285,AD=0.285,PS=2.64,PD=2.64);\n"
|
||||
"end;\n"
|
||||
"circuit .TOP ();\n"
|
||||
" subcircuit SUBCKT SUBCKT ($1=(null),$2=(null),$3=(null),$4=(null),$5=VSS,$6=VSS);\n"
|
||||
"end;\n"
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,30 @@
|
|||
* Tests the ability to translate subcircuits into plain devices
|
||||
|
||||
.SUBCKT HVNMOS S G D B PARAMS: L=0 W=0 AS=0 AD=0 PS=0 PD=0
|
||||
MINT S G D B MODEL L=L W=W AS=AS AD=AD PS=PS PD=PD
|
||||
* parasitic stuff
|
||||
C1 S G 1.5F*L+5.5*L*W
|
||||
C2 D G 1.2F*L+5.5*L*W
|
||||
C3 S B 2.1F*AS+0.2*PS
|
||||
C4 D B 2.1F*AD+0.2*PD
|
||||
.ENDS HVNMOS
|
||||
|
||||
.SUBCKT HVPMOS S G D B PARAMS: L=0 W=0 AS=0 AD=0 PS=0 PD=0
|
||||
MINT S G D B MODEL L=L W=W AS=AS AD=AD
|
||||
* parasitic stuff
|
||||
C1 S G 0.8F*L+4.2*L*W
|
||||
C2 D G 0.8F*L+4.2*L*W
|
||||
C3 S B 2.5F*AS+0.3*PS
|
||||
C4 D B 2.5F*AD+0.3*PD
|
||||
.ENDS HVPMOS
|
||||
|
||||
.SUBCKT SUBCKT \$1 A VDD Z gnd gnd$1
|
||||
X$1 VDD \$3 Z \$1 HVPMOS PARAMS: L=0.2 W=1 AS=0.18 AD=0.18 PS=2.16 PD=2.16
|
||||
X$2 VDD A \$3 \$1 HVPMOS PARAMS: L=0.2 W=1 AS=0.18 AD=0.18 PS=2.16 PD=2.16
|
||||
X$3 gnd \$3 gnd gnd$1 HVNMOS PARAMS: L=1.13 W=2.12 PS=6 PD=6 AS=0 AD=0
|
||||
X$4 gnd \$3 Z gnd$1 HVNMOS PARAMS: L=0.4 W=0.4 PS=1.16 PD=1.16 AS=0.19 AD=0.19
|
||||
X$5 gnd A \$3 gnd$1 HVNMOS PARAMS: L=0.4 W=0.4 PS=1.76 PD=1.76 AS=0.19 AD=0.19
|
||||
.ENDS SUBCKT
|
||||
|
||||
XSUBCKT IN OUT VDD Z VSS VSS SUBCKT
|
||||
|
||||
Loading…
Reference in New Issue