Merge pull request #800 from KLayout/issue-798

Issue 798 (Spice reader enhancements for 3-terminal R and C)
This commit is contained in:
Matthias Köfferlein 2021-05-21 00:52:03 +02:00 committed by GitHub
commit 1a2054b484
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
18 changed files with 1146 additions and 417 deletions

View File

@ -40,6 +40,62 @@ namespace db
// ------------------------------------------------------------------------------------------------------
static const char *allowed_name_chars = "_.:,!+$/&\\#[]|<>";
inline static int hex_num (char c)
{
if (c >= '0' && c <= '9') {
return (int (c - '0'));
} else if (c >= 'a' && c <= 'f') {
return (int (c - 'f') + 10);
} else {
return -1;
}
}
static std::string unescape_name (const std::string &n)
{
std::string nn;
nn.reserve (n.size ());
const char *cp = n.c_str ();
while (*cp) {
if (*cp == '\\' && cp[1]) {
if (tolower (cp[1]) == 'x') {
cp += 2;
char c = 0;
for (int i = 0; i < 2 && *cp; ++i) {
int n = hex_num (*cp);
if (n >= 0) {
++cp;
c = c * 16 + char (n);
} else {
break;
}
}
nn += c;
} else {
++cp;
nn += *cp++;
}
} else {
nn += *cp++;
}
}
return nn;
}
// ------------------------------------------------------------------------------------------------------
NetlistSpiceReaderDelegate::NetlistSpiceReaderDelegate ()
{
// .. nothing yet ..
@ -60,11 +116,21 @@ void NetlistSpiceReaderDelegate::finish (db::Netlist * /*netlist*/)
// .. nothing yet ..
}
bool NetlistSpiceReaderDelegate::control_statement(const std::string & /*line*/)
{
return false;
}
bool NetlistSpiceReaderDelegate::wants_subcircuit (const std::string & /*circuit_name*/)
{
return false;
}
std::string NetlistSpiceReaderDelegate::translate_net_name (const std::string &nn)
{
return unescape_name (nn);
}
void NetlistSpiceReaderDelegate::error (const std::string &msg)
{
throw tl::Exception (msg);
@ -87,6 +153,270 @@ static db::DeviceClass *make_device_class (db::Circuit *circuit, const std::stri
return cls;
}
static std::string parse_component (tl::Extractor &ex)
{
const char *cp = ex.skip ();
const char *cp0 = cp;
char quote = 0;
unsigned int brackets = 0;
while (*cp) {
if (quote) {
if (*cp == quote) {
quote = 0;
} else if (*cp == '\\' && cp[1]) {
++cp;
}
} else if ((isspace (*cp) || *cp == '=') && ! brackets) {
break;
} else if (*cp == '"' || *cp == '\'') {
quote = *cp;
} else if (*cp == '(') {
++brackets;
} else if (*cp == ')') {
if (brackets > 0) {
--brackets;
}
}
++cp;
}
ex = tl::Extractor (cp);
return std::string (cp0, cp - cp0);
}
void NetlistSpiceReaderDelegate::parse_element_components (const std::string &s, std::vector<std::string> &strings, std::map<std::string, double> &pv)
{
tl::Extractor ex (s.c_str ());
bool in_params = false;
while (! ex.at_end ()) {
if (ex.test_without_case ("params:")) {
in_params = true;
} else {
tl::Extractor ex0 = ex;
std::string n;
if (ex.try_read_word (n) && ex.test ("=")) {
// a parameter. Note that parameter names are always made upper case.
pv.insert (std::make_pair (tl::to_upper_case (n), read_value (ex)));
} else {
ex = ex0;
if (in_params) {
ex.error (tl::to_string (tr ("Invalid syntax for parameter assignment - needs keyword followed by '='")));
}
strings.push_back (parse_component (ex));
}
}
}
}
double NetlistSpiceReaderDelegate::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 NetlistSpiceReaderDelegate::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 NetlistSpiceReaderDelegate::read_dot_expr (tl::Extractor &ex)
{
double v = read_bar_expr (ex);
while (true) {
if (ex.test ("*")) {
double vv = read_bar_expr (ex);
v *= vv;
} else if (ex.test ("/")) {
double vv = read_bar_expr (ex);
v /= vv;
} else {
break;
}
}
return v;
}
double NetlistSpiceReaderDelegate::read_value (tl::Extractor &ex)
{
return read_dot_expr (ex);
}
bool NetlistSpiceReaderDelegate::try_read_value (const std::string &s, double &value)
{
tl::Extractor ve (s.c_str ());
double vv = 0;
if (ve.try_read (vv) || ve.test ("(")) {
ve = tl::Extractor (s.c_str ());
value = read_value (ve);
return true;
} else {
return false;
}
}
void NetlistSpiceReaderDelegate::parse_element (const std::string &s, const std::string &element, std::string &model, double &value, std::vector<std::string> &nn, std::map<std::string, double> &pv)
{
parse_element_components (s, nn, pv);
// interpret the parameters according to the code
if (element == "X") {
// subcircuit call:
// Xname n1 n2 ... nn circuit [params]
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 n3 value
// Rname n1 n2 value model [params]
// Rname n1 n2 n3 value model [params]
// Rname n1 n2 [params]
// Rname n1 n2 model [params]
// Rname n1 n2 n3 model [params]
// NOTE: there is no "Rname n1 n2 n3 [params]"!
// (same for C, L instead of R)
if (nn.size () < 2) {
error (tl::to_string (tr ("Not enough specs for a R, C or L device")));
}
std::map<std::string, double>::const_iterator rv = pv.find (element);
if (rv != pv.end ()) {
// value given by parameter
value = rv->second;
if (nn.size () >= 3) {
// Rname n1 n2 model [params]
// Rname n1 n2 n3 model [params]
model = nn.back ();
nn.pop_back ();
}
} else if (nn.size () >= 3) {
if (try_read_value (nn.back (), value)) {
// Rname n1 n2 value
// Rname n1 n2 n3 value
nn.pop_back ();
} else {
// Rname n1 n2 value model [params]
// Rname n1 n2 n3 value model [params]
model = nn.back ();
nn.pop_back ();
if (! try_read_value (nn.back (), value)) {
error (tl::to_string (tr ("Can't find a value for a R, C or L device")));
} else {
nn.pop_back ();
}
}
}
} else {
// others: n-terminal devices with a model (last node)
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?
}
}
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> &pv)
{
std::map<std::string, double> params = pv;
@ -106,15 +436,30 @@ bool NetlistSpiceReaderDelegate::element (db::Circuit *circuit, const std::strin
if (element == "R") {
if (cls) {
if (! dynamic_cast<db::DeviceClassResistor *>(cls)) {
error (tl::sprintf (tl::to_string (tr ("Class %s not a resistor device class as required by 'R' element")), cn));
if (nets.size () == 2) {
if (cls) {
if (! dynamic_cast<db::DeviceClassResistor *>(cls)) {
error (tl::sprintf (tl::to_string (tr ("Class %s is not a resistor device class as required by 'R' element")), cn));
}
} else {
if (cn.empty ()) {
cn = "RES";
}
cls = make_device_class<db::DeviceClassResistor> (circuit, cn);
}
} else if (nets.size () == 3) {
if (cls) {
if (! dynamic_cast<db::DeviceClassResistorWithBulk *>(cls)) {
error (tl::sprintf (tl::to_string (tr ("Class %s is not a three-terminal resistor device class as required by 'R' element")), cn));
}
} else {
if (cn.empty ()) {
cn = "RES3";
}
cls = make_device_class<db::DeviceClassResistorWithBulk> (circuit, cn);
}
} else {
if (cn.empty ()) {
cn = "RES";
}
cls = make_device_class<db::DeviceClassResistor> (circuit, cn);
error (tl::to_string (tr ("A 'R' element requires two or three nets")));
}
// Apply multiplier
@ -122,15 +467,19 @@ bool NetlistSpiceReaderDelegate::element (db::Circuit *circuit, const std::strin
} else if (element == "L") {
if (cls) {
if (! dynamic_cast<db::DeviceClassInductor *>(cls)) {
error (tl::sprintf (tl::to_string (tr ("Class %s not a inductor device class as required by 'L' element")), cn));
if (nets.size () == 2) {
if (cls) {
if (! dynamic_cast<db::DeviceClassInductor *>(cls)) {
error (tl::sprintf (tl::to_string (tr ("Class %s is not a inductor device class as required by 'L' element")), cn));
}
} else {
if (cn.empty ()) {
cn = "IND";
}
cls = make_device_class<db::DeviceClassInductor> (circuit, cn);
}
} else {
if (cn.empty ()) {
cn = "IND";
}
cls = make_device_class<db::DeviceClassInductor> (circuit, cn);
error (tl::to_string (tr ("A 'L' element requires two nets")));
}
// Apply multiplier
@ -138,15 +487,30 @@ bool NetlistSpiceReaderDelegate::element (db::Circuit *circuit, const std::strin
} else if (element == "C") {
if (cls) {
if (! dynamic_cast<db::DeviceClassCapacitor *>(cls)) {
error (tl::sprintf (tl::to_string (tr ("Class %s not a capacitor device class as required by 'C' element")), cn));
if (nets.size () == 2) {
if (cls) {
if (! dynamic_cast<db::DeviceClassCapacitor *>(cls)) {
error (tl::sprintf (tl::to_string (tr ("Class %s is not a capacitor device class as required by 'C' element")), cn));
}
} else {
if (cn.empty ()) {
cn = "CAP";
}
cls = make_device_class<db::DeviceClassCapacitor> (circuit, cn);
}
} else if (nets.size () == 3) {
if (cls) {
if (! dynamic_cast<db::DeviceClassCapacitorWithBulk *>(cls)) {
error (tl::sprintf (tl::to_string (tr ("Class %s is not a three-terminal capacitor device class as required by 'C' element")), cn));
}
} else {
if (cn.empty ()) {
cn = "CAP3";
}
cls = make_device_class<db::DeviceClassCapacitorWithBulk> (circuit, cn);
}
} else {
if (cn.empty ()) {
cn = "CAP";
}
cls = make_device_class<db::DeviceClassCapacitor> (circuit, cn);
error (tl::to_string (tr ("A 'C' element requires two or three nets")));
}
// Apply multiplier
@ -156,7 +520,7 @@ bool NetlistSpiceReaderDelegate::element (db::Circuit *circuit, const std::strin
if (cls) {
if (! dynamic_cast<db::DeviceClassDiode *>(cls)) {
error (tl::sprintf (tl::to_string (tr ("Class %s not a diode device class as required by 'D' element")), cn));
error (tl::sprintf (tl::to_string (tr ("Class %s is not a diode device class as required by 'D' element")), cn));
}
} else {
if (cn.empty ()) {
@ -179,11 +543,11 @@ bool NetlistSpiceReaderDelegate::element (db::Circuit *circuit, const std::strin
} else if (cls) {
if (nets.size () == 3) {
if (! dynamic_cast<db::DeviceClassBJT3Transistor *>(cls)) {
error (tl::sprintf (tl::to_string (tr ("Class %s not a 3-terminal BJT device class as required by 'Q' element")), cn));
error (tl::sprintf (tl::to_string (tr ("Class %s is not a 3-terminal BJT device class as required by 'Q' element")), cn));
}
} else {
if (! dynamic_cast<db::DeviceClassBJT4Transistor *>(cls)) {
error (tl::sprintf (tl::to_string (tr ("Class %s not a 4-terminal BJT device class as required by 'Q' element")), cn));
error (tl::sprintf (tl::to_string (tr ("Class %s is not a 4-terminal BJT device class as required by 'Q' element")), cn));
}
}
} else {
@ -211,7 +575,7 @@ bool NetlistSpiceReaderDelegate::element (db::Circuit *circuit, const std::strin
if (cls) {
if (! dynamic_cast<db::DeviceClassMOS4Transistor *>(cls)) {
error (tl::sprintf (tl::to_string (tr ("Class %s not a 4-terminal MOS device class as required by 'M' element")), cn));
error (tl::sprintf (tl::to_string (tr ("Class %s is not a 4-terminal MOS device class as required by 'M' element")), cn));
}
} else {
if (nets.size () == 4) {
@ -271,8 +635,6 @@ bool NetlistSpiceReaderDelegate::element (db::Circuit *circuit, const std::strin
// ------------------------------------------------------------------------------------------------------
static const char *allowed_name_chars = "_.:,!+$/&\\#[]|<>";
NetlistSpiceReader::NetlistSpiceReader (NetlistSpiceReaderDelegate *delegate)
: mp_netlist (0), mp_stream (), mp_delegate (delegate)
{
@ -450,7 +812,8 @@ std::string NetlistSpiceReader::get_line ()
tl::Extractor ex (l.c_str ());
if (ex.test_without_case (".include") || ex.test_without_case (".inc")) {
std::string path = read_name_with_case (ex);
std::string path;
ex.read_word_or_quoted (path, allowed_name_chars);
push_stream (path);
@ -504,7 +867,7 @@ bool NetlistSpiceReader::read_card ()
} else if (ex.test_without_case ("global")) {
while (! ex.at_end ()) {
std::string n = read_name (ex);
std::string n = mp_delegate->translate_net_name (read_name (ex));
if (m_global_net_names.find (n) == m_global_net_names.end ()) {
m_global_nets.push_back (n);
m_global_net_names.insert (n);
@ -528,7 +891,7 @@ bool NetlistSpiceReader::read_card ()
// ignore end statements
} else {
} else if (! mp_delegate->control_statement (l)) {
std::string s;
ex.read_word (s);
@ -551,8 +914,6 @@ bool NetlistSpiceReader::read_card ()
warn (tl::sprintf (tl::to_string (tr ("Element type '%c' ignored")), next_char));
}
ex.expect_end ();
} else {
warn (tl::to_string (tr ("Line ignored")));
}
@ -571,91 +932,6 @@ void NetlistSpiceReader::warn (const std::string &msg)
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) {
@ -693,92 +969,11 @@ db::Net *NetlistSpiceReader::make_net (const std::string &name)
return net;
}
void NetlistSpiceReader::read_pin_and_parameters (tl::Extractor &ex, std::vector<std::string> &nn, std::map<std::string, double> &pv)
{
bool in_params = false;
while (! ex.at_end ()) {
if (ex.test_without_case ("params:")) {
in_params = true;
} else {
std::string n = read_name (ex);
if (ex.test ("=")) {
// a parameter
pv.insert (std::make_pair (n, read_value (ex)));
} else {
if (in_params) {
error (tl::to_string (tr ("Missing '=' in parameter assignment")));
}
nn.push_back (n);
}
}
}
}
inline static int hex_num (char c)
{
if (c >= '0' && c <= '9') {
return (int (c - '0'));
} else if (c >= 'a' && c <= 'f') {
return (int (c - 'f') + 10);
} else {
return -1;
}
}
std::string NetlistSpiceReader::read_name_with_case (tl::Extractor &ex)
std::string NetlistSpiceReader::read_name (tl::Extractor &ex)
{
std::string n;
ex.read_word_or_quoted (n, allowed_name_chars);
std::string nn;
nn.reserve (n.size ());
const char *cp = n.c_str ();
while (*cp) {
if (*cp == '\\' && cp[1]) {
if (tolower (cp[1]) == 'x') {
cp += 2;
char c = 0;
for (int i = 0; i < 2 && *cp; ++i) {
int n = hex_num (*cp);
if (n >= 0) {
++cp;
c = c * 16 + char (n);
} else {
break;
}
}
nn += c;
} else {
++cp;
nn += *cp++;
}
} else {
nn += *cp++;
}
}
return nn;
}
std::string NetlistSpiceReader::read_name (tl::Extractor &ex)
{
return mp_netlist->normalize_name (read_name_with_case (ex));
return mp_netlist->normalize_name (n);
}
bool NetlistSpiceReader::read_element (tl::Extractor &ex, const std::string &element, const std::string &name)
@ -786,99 +981,16 @@ bool NetlistSpiceReader::read_element (tl::Extractor &ex, const std::string &ele
// 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") {
mp_delegate->parse_element (ex.skip (), element, model, value, nn, pv);
// 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 [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 [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?
}
model = mp_netlist->normalize_name (model);
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));
nets.push_back (make_net (mp_delegate->translate_net_name (mp_netlist->normalize_name (*i))));
}
if (element == "X" && ! subcircuit_captured (model)) {
@ -946,7 +1058,11 @@ 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);
mp_delegate->parse_element_components (ex.skip (), nn, pv);
for (std::vector<std::string>::iterator i = nn.begin (); i != nn.end (); ++i) {
*i = mp_delegate->translate_net_name (mp_netlist->normalize_name (*i));
}
if (! pv.empty ()) {
warn (tl::to_string (tr ("Circuit parameters are not allowed currently")));
@ -999,8 +1115,6 @@ void NetlistSpiceReader::read_circuit (tl::Extractor &ex, const std::string &nc)
mp_nets_by_name.reset (n2n.release ());
std::swap (cc, mp_circuit);
ex.expect_end ();
}
}

View File

@ -78,6 +78,13 @@ public:
*/
virtual void finish (db::Netlist *netlist);
/**
* @brief Called when an unknown control statement is encountered
*
* Returns true if the statement is understood.
*/
virtual bool control_statement (const std::string &line);
/**
* @brief Returns true, if the delegate wants subcircuit elements with this name
*
@ -85,6 +92,13 @@ public:
*/
virtual bool wants_subcircuit (const std::string &circuit_name);
/**
* @brief This method translates a raw net name to a valid net name
*
* The default implementation will unescape backslash sequences into plain characters.
*/
virtual std::string translate_net_name (const std::string &nn);
/**
* @brief Makes a device from an element line
*
@ -103,10 +117,42 @@ public:
*/
virtual bool element (db::Circuit *circuit, const std::string &element, const std::string &name, const std::string &model, double value, const std::vector<db::Net *> &nets, const std::map<std::string, double> &params);
/**
* @brief Parses an element from a line
*
* @param s The line to parse (the part after the element and name)
* @param model Out parameter: the model name if given
* @param value Out parameter: the value if given (for R, L, C)
* @param nn Out parameter: the net names
* @param pv Out parameter: the parameter values (key/value pairs)
*/
virtual void parse_element (const std::string &s, const std::string &element, std::string &model, double &value, std::vector<std::string> &nn, std::map<std::string, double> &pv);
/**
* @brief Produces an error with the given message
*/
virtual void error (const std::string &msg);
/**
* @brief Reads a set of string components and parameters from the string
* A special key "param:" is recognized for starting a parameter list.
*/
void parse_element_components (const std::string &s, std::vector<std::string> &strings, std::map<std::string, double> &pv);
/**
* @brief Reads a value from the extractor (with formula evaluation)
*/
double read_value (tl::Extractor &ex);
/**
* @brief Tries to read a value from the extractor (with formula evaluation)
*/
bool try_read_value (const std::string &s, double &v);
private:
double read_atomic_value (tl::Extractor &ex);
double read_dot_expr (tl::Extractor &ex);
double read_bar_expr (tl::Extractor &ex);
};
/**
@ -138,18 +184,12 @@ private:
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 (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_with_case (tl::Extractor &ex);
std::string read_name (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);

View File

@ -94,8 +94,10 @@ void NetlistSpiceWriterDelegate::write_device (const db::Device &dev) const
{
const db::DeviceClass *dc = dev.device_class ();
const db::DeviceClassCapacitor *cap = dynamic_cast<const db::DeviceClassCapacitor *> (dc);
const db::DeviceClassCapacitor *cap3 = dynamic_cast<const db::DeviceClassCapacitorWithBulk *> (dc);
const db::DeviceClassInductor *ind = dynamic_cast<const db::DeviceClassInductor *> (dc);
const db::DeviceClassResistor *res = dynamic_cast<const db::DeviceClassResistor *> (dc);
const db::DeviceClassResistor *res3 = dynamic_cast<const db::DeviceClassResistorWithBulk *> (dc);
const db::DeviceClassDiode *diode = dynamic_cast<const db::DeviceClassDiode *> (dc);
const db::DeviceClassMOS3Transistor *mos3 = dynamic_cast<const db::DeviceClassMOS3Transistor *> (dc);
const db::DeviceClassMOS4Transistor *mos4 = dynamic_cast<const db::DeviceClassMOS4Transistor *> (dc);
@ -104,13 +106,17 @@ void NetlistSpiceWriterDelegate::write_device (const db::Device &dev) const
std::ostringstream os;
if (cap) {
if (cap || cap3) {
os << "C";
os << format_name (dev.expanded_name ());
os << format_terminals (dev, size_t (2));
os << format_terminals (dev, size_t (3));
os << " ";
os << tl::sprintf ("%.12g", dev.parameter_value (db::DeviceClassCapacitor::param_id_C));
if (! dev.device_class ()->name ().empty ()) {
os << " ";
os << format_name (dev.device_class ()->name ());
}
} else if (ind) {
@ -119,14 +125,22 @@ void NetlistSpiceWriterDelegate::write_device (const db::Device &dev) const
os << format_terminals (dev, size_t (2));
os << " ";
os << tl::sprintf ("%.12g", dev.parameter_value (db::DeviceClassInductor::param_id_L));
if (! dev.device_class ()->name ().empty ()) {
os << " ";
os << format_name (dev.device_class ()->name ());
}
} else if (res) {
os << "R";
os << format_name (dev.expanded_name ());
os << format_terminals (dev, size_t (2));
os << format_terminals (dev, size_t (3));
os << " ";
os << tl::sprintf ("%.12g", dev.parameter_value (db::DeviceClassResistor::param_id_R));
if (! dev.device_class ()->name ().empty ()) {
os << " ";
os << format_name (dev.device_class ()->name ());
}
} else if (diode) {

View File

@ -2122,6 +2122,54 @@ Class<db::NetlistReader> db_NetlistReader ("db", "NetlistReader",
"@hide\n"
);
/**
* @brief A helper class wrapping the return values for NetlistSpiceReaderDelegateImpl::parse_element
*/
class ParseElementData
{
public:
ParseElementData () : m_value (0.0) { }
const std::string &model_name () const { return m_model; }
std::string &model_name_nc () { return m_model; }
void set_model_name (const std::string &model) { m_model = model; }
double value () const { return m_value; }
double &value_nc () { return m_value; }
void set_value (double value) { m_value = value; }
const std::vector<std::string> &net_names () const { return m_net_names; }
std::vector<std::string> &net_names_nc () { return m_net_names; }
void set_net_names (const std::vector<std::string> &nn) { m_net_names = nn; }
const std::map<std::string, double> &parameters () const { return m_parameters; }
std::map<std::string, double> &parameters_nc () { return m_parameters; }
void set_parameters (const std::map<std::string, double> &parameters) { m_parameters = parameters; }
private:
std::string m_model;
double m_value;
std::vector<std::string> m_net_names;
std::map<std::string, double> m_parameters;
};
/**
* @brief A helper class for the return values of NetlistSpiceReaderDelegateImpl::parse_element_components
*/
class ParseElementComponentsData
{
public:
ParseElementComponentsData () { }
const std::vector<std::string> &strings () const { return m_strings; }
std::vector<std::string> &strings_nc () { return m_strings; }
void set_strings (const std::vector<std::string> &nn) { m_strings = nn; }
const std::map<std::string, double> &parameters () const { return m_parameters; }
std::map<std::string, double> &parameters_nc () { return m_parameters; }
void set_parameters (const std::map<std::string, double> &parameters) { m_parameters = parameters; }
private:
std::vector<std::string> m_strings;
std::map<std::string, double> m_parameters;
};
/**
* @brief A SPICE reader delegate base class for reimplementation
*/
@ -2180,6 +2228,25 @@ public:
}
}
virtual bool control_statement (const std::string &line)
{
try {
m_error.clear ();
if (cb_control_statement.can_issue ()) {
return cb_control_statement.issue<db::NetlistSpiceReaderDelegate, bool, const std::string &> (&db::NetlistSpiceReaderDelegate::control_statement, line);
} else {
return db::NetlistSpiceReaderDelegate::control_statement (line);
}
} catch (tl::Exception &) {
if (! m_error.empty ()) {
db::NetlistSpiceReaderDelegate::error (m_error);
} else {
throw;
}
return false;
}
}
virtual bool wants_subcircuit (const std::string &circuit_name)
{
try {
@ -2199,6 +2266,59 @@ public:
}
}
virtual std::string translate_net_name (const std::string &nn)
{
try {
m_error.clear ();
if (cb_translate_net_name.can_issue ()) {
return cb_translate_net_name.issue<db::NetlistSpiceReaderDelegate, std::string, const std::string &> (&db::NetlistSpiceReaderDelegate::translate_net_name, nn);
} else {
return db::NetlistSpiceReaderDelegate::translate_net_name (nn);
}
} catch (tl::Exception &) {
if (! m_error.empty ()) {
db::NetlistSpiceReaderDelegate::error (m_error);
} else {
throw;
}
return std::string ();
}
}
ParseElementData parse_element_helper (const std::string &s, const std::string &element)
{
ParseElementData data;
db::NetlistSpiceReaderDelegate::parse_element (s, element, data.model_name_nc (), data.value_nc (), data.net_names_nc (), data.parameters_nc ());
return data;
}
virtual void parse_element (const std::string &s, const std::string &element, std::string &model, double &value, std::vector<std::string> &nn, std::map<std::string, double> &pv)
{
try {
m_error.clear ();
ParseElementData data;
if (cb_parse_element.can_issue ()) {
data = cb_parse_element.issue<NetlistSpiceReaderDelegateImpl, ParseElementData, const std::string &, const std::string &> (&NetlistSpiceReaderDelegateImpl::parse_element_helper, s, element);
} else {
data = parse_element_helper (s, element);
}
model = data.model_name ();
value = data.value ();
nn = data.net_names ();
pv = data.parameters ();
} catch (tl::Exception &) {
if (! m_error.empty ()) {
db::NetlistSpiceReaderDelegate::error (m_error);
} else {
throw;
}
}
}
virtual bool element (db::Circuit *circuit, const std::string &element, const std::string &name, const std::string &model, double value, const std::vector<db::Net *> &nets, const std::map<std::string, double> &params)
{
try {
@ -2220,38 +2340,128 @@ public:
gsi::Callback cb_start;
gsi::Callback cb_finish;
gsi::Callback cb_control_statement;
gsi::Callback cb_wants_subcircuit;
gsi::Callback cb_translate_net_name;
gsi::Callback cb_element;
gsi::Callback cb_parse_element;
private:
std::string m_error;
};
static void start_fb (db::NetlistSpiceReaderDelegate *delegate, db::Netlist *netlist)
static void start_fb (NetlistSpiceReaderDelegateImpl *delegate, db::Netlist *netlist)
{
delegate->db::NetlistSpiceReaderDelegate::start (netlist);
}
static void finish_fb (db::NetlistSpiceReaderDelegate *delegate, db::Netlist *netlist)
static void finish_fb (NetlistSpiceReaderDelegateImpl *delegate, db::Netlist *netlist)
{
delegate->db::NetlistSpiceReaderDelegate::finish (netlist);
}
static bool wants_subcircuit_fb (db::NetlistSpiceReaderDelegate *delegate, const std::string &model)
static bool wants_subcircuit_fb (NetlistSpiceReaderDelegateImpl *delegate, const std::string &model)
{
return delegate->db::NetlistSpiceReaderDelegate::wants_subcircuit (model);
}
static bool element_fb (db::NetlistSpiceReaderDelegate *delegate, db::Circuit *circuit, const std::string &element, const std::string &name, const std::string &model, double value, const std::vector<db::Net *> &nets, const std::map<std::string, double> &params)
static bool control_statement_fb (NetlistSpiceReaderDelegateImpl *delegate, const std::string &line)
{
return delegate->db::NetlistSpiceReaderDelegate::control_statement (line);
}
static std::string translate_net_name_fb (NetlistSpiceReaderDelegateImpl *delegate, const std::string &name)
{
return delegate->db::NetlistSpiceReaderDelegate::translate_net_name (name);
}
static bool element_fb (NetlistSpiceReaderDelegateImpl *delegate, db::Circuit *circuit, const std::string &element, const std::string &name, const std::string &model, double value, const std::vector<db::Net *> &nets, const std::map<std::string, double> &params)
{
return delegate->db::NetlistSpiceReaderDelegate::element (circuit, element, name, model, value, nets, params);
}
static ParseElementData parse_element_fb (NetlistSpiceReaderDelegateImpl *delegate, const std::string &s, const std::string &element)
{
return delegate->parse_element_helper (s, element);
}
static tl::Variant value_from_string (NetlistSpiceReaderDelegateImpl *delegate, const std::string &s)
{
tl::Variant res;
double v = 0.0;
if (delegate->try_read_value (s, v)) {
res = v;
}
return res;
}
static ParseElementComponentsData parse_element_components (NetlistSpiceReaderDelegateImpl *delegate, const std::string &s)
{
ParseElementComponentsData data;
delegate->parse_element_components (s, data.strings_nc (), data.parameters_nc ());
return data;
}
Class<ParseElementComponentsData> db_ParseElementComponentsData ("db", "ParseElementComponentsData",
gsi::method ("strings", &ParseElementComponentsData::strings,
"@brief Gets the string parameters\n"
) +
gsi::method ("strings=", &ParseElementComponentsData::set_strings, gsi::arg ("list"),
"@brief Sets the string parameters\n"
) +
gsi::method ("parameters", &ParseElementComponentsData::parameters,
"@brief Gets the (named) numerical parameters\n"
) +
gsi::method ("parameters=", &ParseElementComponentsData::set_parameters, gsi::arg ("dict"),
"@brief Sets the (named) numerical parameters\n"
),
"@brief Supplies the return value for \\NetlistSpiceReaderDelegate#parse_element_components.\n"
"This is a structure with two members: 'strings' for the string arguments and 'parameters' for the "
"named numerical arguments.\n"
"\n"
"This helper class has been introduced in version 0.27.1.\n"
);
Class<ParseElementData> db_ParseElementData ("db", "ParseElementData",
gsi::method ("value", &ParseElementData::value,
"@brief Gets the value\n"
) +
gsi::method ("value=", &ParseElementData::set_value, gsi::arg ("v"),
"@brief Sets the value\n"
) +
gsi::method ("model_name", &ParseElementData::model_name,
"@brief Gets the model name\n"
) +
gsi::method ("model_name=", &ParseElementData::set_model_name, gsi::arg ("m"),
"@brief Sets the model name\n"
) +
gsi::method ("net_names", &ParseElementData::net_names,
"@brief Gets the net names\n"
) +
gsi::method ("net_names=", &ParseElementData::set_net_names, gsi::arg ("list"),
"@brief Sets the net names\n"
) +
gsi::method ("parameters", &ParseElementData::parameters,
"@brief Gets the (named) numerical parameters\n"
) +
gsi::method ("parameters=", &ParseElementData::set_parameters, gsi::arg ("dict"),
"@brief Sets the (named) numerical parameters\n"
),
"@brief Supplies the return value for \\NetlistSpiceReaderDelegate#parse_element.\n"
"This is a structure with four members: 'model_name' for the model name, 'value' for the default numerical value, 'net_names' for the net names and 'parameters' for the "
"named numerical parameters.\n"
"\n"
"This helper class has been introduced in version 0.27.1.\n"
);
Class<NetlistSpiceReaderDelegateImpl> db_NetlistSpiceReaderDelegate ("db", "NetlistSpiceReaderDelegate",
gsi::method_ext ("start", &start_fb, "@hide") +
gsi::method_ext ("finish", &finish_fb, "@hide") +
gsi::method_ext ("wants_subcircuit", &wants_subcircuit_fb, "@hide") +
gsi::method_ext ("element", &element_fb, "@hide") +
gsi::method_ext ("parse_element", &parse_element_fb, "@hide") +
gsi::method_ext ("control_statement", &control_statement_fb, "@hide") +
gsi::method_ext ("translate_net_name", &translate_net_name_fb, "@hide") +
gsi::callback ("start", &NetlistSpiceReaderDelegateImpl::start, &NetlistSpiceReaderDelegateImpl::cb_start, gsi::arg ("netlist"),
"@brief This method is called when the reader starts reading a netlist\n"
) +
@ -2262,6 +2472,36 @@ Class<NetlistSpiceReaderDelegateImpl> db_NetlistSpiceReaderDelegate ("db", "Netl
"@brief Returns true, if the delegate wants subcircuit elements with this name\n"
"The name is always upper case.\n"
) +
gsi::callback ("control_statement", &NetlistSpiceReaderDelegateImpl::control_statement, &NetlistSpiceReaderDelegateImpl::cb_control_statement, gsi::arg ("line"),
"@brief Receives control statements not understood by the standard reader\n"
"When the reader encounters a control statement not understood by the parser, it will pass the line to the delegate using this method.\n"
"The delegate can decide if it wants to read this statement. It should return true in this case.\n"
"\n"
"This method has been introduced in version 0.27.1\n"
) +
gsi::callback ("translate_net_name", &NetlistSpiceReaderDelegateImpl::translate_net_name, &NetlistSpiceReaderDelegateImpl::cb_translate_net_name, gsi::arg ("net_name"),
"@brief Translates a net name from the raw net name to the true net name\n"
"The default implementation will replace backslash sequences by the corresponding character.\n"
"'translate_net_name' is called before a net name is turned into a net object.\n"
"The method can be reimplemented to supply a different translation scheme for net names. For example, to translate special characters.\n"
"\n"
"This method has been introduced in version 0.27.1\n"
) +
gsi::callback ("parse_element", &NetlistSpiceReaderDelegateImpl::parse_element_helper, &NetlistSpiceReaderDelegateImpl::cb_parse_element,
gsi::arg ("s"), gsi::arg ("element"),
"@brief Parses an element card\n"
"@param s The specification part of the element line (the part after element code and name).\n"
"@param element The upper-case element code (\"M\", \"R\", ...).\n"
"@return A \\ParseElementData object with the parts of the element.\n"
"\n"
"This method receives a string with the element specification and the element code. It is supposed to "
"parse the element line and return a model name, a value, a list of net names and a parameter value dictionary.\n"
"\n"
"'parse_element' is called one every element card. The results of this call go into the \\element method "
"to actually create the device. This method can be reimplemented to support other flavors of SPICE.\n"
"\n"
"This method has been introduced in version 0.27.1\n"
) +
gsi::callback ("element", &NetlistSpiceReaderDelegateImpl::element, &NetlistSpiceReaderDelegateImpl::cb_element,
gsi::arg ("circuit"), gsi::arg ("element"), gsi::arg ("name"), gsi::arg ("model"), gsi::arg ("value"), gsi::arg ("nets"), gsi::arg ("parameters"),
"@brief Makes a device from an element line\n"
@ -2281,6 +2521,23 @@ Class<NetlistSpiceReaderDelegateImpl> db_NetlistSpiceReaderDelegate ("db", "Netl
gsi::method ("error", &NetlistSpiceReaderDelegateImpl::error, gsi::arg ("msg"),
"@brief Issues an error with the given message.\n"
"Use this method to generate an error."
) +
gsi::method_ext ("value_from_string", &value_from_string, gsi::arg ("s"),
"@brief Translates a string into a value\n"
"This function simplifies the implementation of SPICE readers by providing a translation of a unit-annotated string "
"into double values. For example, '1k' is translated to 1000.0. In addition, simple formula evaluation is supported, e.g "
"'(1+3)*2' is translated into 8.0.\n"
"\n"
"This method has been introduced in version 0.27.1\n"
) +
gsi::method_ext ("parse_element_components", &parse_element_components, gsi::arg ("s"),
"@brief Parses a string into string and parameter components.\n"
"This method is provided for simplifying the implementation of 'parse_element'. It takes a string and splits it into "
"string arguments and parameter values. For example, 'a b c=6' renders two string arguments in 'nn' and one parameter in pv ('C'->6.0). "
"It returns data \\ParseElementComponentsData object with the strings and parameters.\n"
"The parameter names are already translated to upper case.\n"
"\n"
"This method has been introduced in version 0.27.1\n"
),
"@brief Provides a delegate for the SPICE reader for translating device statements\n"
"Supply a customized class to provide a specialized reading scheme for devices. "
@ -2396,7 +2653,21 @@ Class<db::NetlistSpiceReader> db_NetlistSpiceReader (db_NetlistReader, "db", "Ne
"nl.read(input_file, reader)\n"
"@/code\n"
"\n"
"This class has been introduced in version 0.26."
"A somewhat contrived example for using the delegate to translate net names is this:\n"
"\n"
"@code\n"
"class MyDelegate < RBA::NetlistSpiceReaderDelegate\n"
"\n"
" # translates 'VDD' to 'VXX' and leave all other net names as is:\n"
" alias translate_net_name_org translate_net_name\n"
" def translate_net_name(n)\n"
" return n == \"VDD\" ? \"VXX\" : translate_net_name_org(n)}\n"
" end\n"
"\n"
"end\n"
"@/code\n"
"\n"
"This class has been introduced in version 0.26. It has been extended in version 0.27.1."
);
}

View File

@ -374,12 +374,20 @@ TEST(9_DeviceMultipliers)
" device RES $2 (A='3',B='4') (R=1700,L=0,W=0,A=0,P=0);\n"
" device RMODEL $3 (A='1',B='2') (R=850,L=0,W=0,A=0,P=0);\n"
" device RMODEL $4 (A='3',B='4') (R=1700,L=0,W=0,A=0,P=0);\n"
" device RMODEL $5 (A='3',B='4') (R=1700,L=0,W=0,A=0,P=0);\n"
" device RMODEL2 $6 (A='3',B='4',W='5') (R=1700,L=0,W=0,A=0,P=0);\n"
" device RMODEL2 $7 (A='3',B='4',W='5') (R=1700,L=0,W=0,A=0,P=0);\n"
" device RES3 $8 (A='3',B='4',W='5') (R=1700,L=0,W=0,A=0,P=0);\n"
" device NMOS $1 (S='1',G='2',D='3',B='4') (L=7,W=4,AS=0,AD=0,PS=0,PD=0);\n"
" device PMOS $2 (S='1',G='2',D='3',B='4') (L=7,W=2,AS=0,AD=0,PS=0,PD=0);\n"
" device CAP $1 (A='1',B='2') (C=2e-09,A=0,P=0);\n"
" device CAP $2 (A='3',B='4') (C=1e-09,A=0,P=0);\n"
" device CMODEL $3 (A='1',B='2') (C=2e-09,A=0,P=0);\n"
" device CMODEL $4 (A='3',B='4') (C=1e-09,A=0,P=0);\n"
" device CMODEL $5 (A='3',B='4') (C=1e-09,A=0,P=0);\n"
" device CMODEL2 $6 (A='3',B='4',W='5') (C=1e-09,A=0,P=0);\n"
" device CAP3 $7 (A='3',B='4',W='5') (C=1e-09,A=0,P=0);\n"
" device CMODEL2 $8 (A='3',B='4',W='5') (C=1e-09,A=0,P=0);\n"
" device IND $1 (A='1',B='2') (L=5e-10);\n"
" device IND $2 (A='3',B='4') (L=1e-09);\n"
" device LMODEL $3 (A='1',B='2') (L=5e-10);\n"
@ -411,12 +419,20 @@ TEST(9_DeviceMultipliers)
" device RES $2 (A='3',B='4') (R=1700,L=0,W=0,A=0,P=0);\n"
" device RMODEL $3 (A='1',B='2') (R=850,L=0,W=0,A=0,P=0);\n"
" device RMODEL $4 (A='3',B='4') (R=1700,L=0,W=0,A=0,P=0);\n"
" device RMODEL $5 (A='3',B='4') (R=1700,L=0,W=0,A=0,P=0);\n"
" device RMODEL2 $6 (A='3',B='4',W='5') (R=1700,L=0,W=0,A=0,P=0);\n"
" device RMODEL2 $7 (A='3',B='4',W='5') (R=1700,L=0,W=0,A=0,P=0);\n"
" device RES3 $8 (A='3',B='4',W='5') (R=1700,L=0,W=0,A=0,P=0);\n"
" device NMOS $1 (S='1',G='2',D='3',B='4') (L=7,W=4,AS=0,AD=0,PS=0,PD=0);\n"
" device PMOS $2 (S='1',G='2',D='3',B='4') (L=7,W=2,AS=0,AD=0,PS=0,PD=0);\n"
" device CAP $1 (A='1',B='2') (C=2e-09,A=0,P=0);\n"
" device CAP $2 (A='3',B='4') (C=1e-09,A=0,P=0);\n"
" device CMODEL $3 (A='1',B='2') (C=2e-09,A=0,P=0);\n"
" device CMODEL $4 (A='3',B='4') (C=1e-09,A=0,P=0);\n"
" device CMODEL $5 (A='3',B='4') (C=1e-09,A=0,P=0);\n"
" device CMODEL2 $6 (A='3',B='4',W='5') (C=1e-09,A=0,P=0);\n"
" device CAP3 $7 (A='3',B='4',W='5') (C=1e-09,A=0,P=0);\n"
" device CMODEL2 $8 (A='3',B='4',W='5') (C=1e-09,A=0,P=0);\n"
" device IND $1 (A='1',B='2') (L=5e-10);\n"
" device IND $2 (A='3',B='4') (L=1e-09);\n"
" device LMODEL $3 (A='1',B='2') (L=5e-10);\n"

View File

@ -162,7 +162,7 @@ TEST(1_WriterResistorDevicesWithBulk)
writer.write (stream, nl, "written by unit test");
}
std::string au_path = tl::combine_path (tl::combine_path (tl::testdata (), "algo"), "nwriter1_au.txt");
std::string au_path = tl::combine_path (tl::combine_path (tl::testdata (), "algo"), "nwriter1b_au.txt");
compare_netlists (_this, path, au_path);
}
@ -224,6 +224,61 @@ TEST(2_WriterCapacitorDevices)
compare_netlists (_this, path, au_path);
}
TEST(2_WriterCapacitorDevicesNoName)
{
db::Netlist nl;
db::DeviceClass *ccls = new db::DeviceClassCapacitor ();
nl.add_device_class (ccls);
db::Circuit *circuit1 = new db::Circuit ();
circuit1->set_name ("C1");
nl.add_circuit (circuit1);
db::Net *n1, *n2, *n3;
n1 = new db::Net ();
n1->set_name ("n1");
circuit1->add_net (n1);
n2 = new db::Net ();
n2->set_name ("n2");
circuit1->add_net (n2);
n3 = new db::Net ();
n3->set_name ("n3");
circuit1->add_net (n3);
db::Device *cdev1 = new db::Device (ccls);
cdev1->set_parameter_value (db::DeviceClassCapacitor::param_id_C, 1.7e-12);
db::Device *cdev2 = new db::Device (ccls);
cdev2->set_parameter_value (db::DeviceClassCapacitor::param_id_C, 42e-15);
circuit1->add_device (cdev1);
circuit1->add_device (cdev2);
size_t pid1 = circuit1->add_pin ("p1").id ();
size_t pid2 = circuit1->add_pin ("p2").id ();
circuit1->connect_pin (pid1, n1);
circuit1->connect_pin (pid2, n2);
cdev1->connect_terminal (cdev1->device_class ()->terminal_id_for_name ("A"), n1);
cdev1->connect_terminal (cdev1->device_class ()->terminal_id_for_name ("B"), n3);
cdev2->connect_terminal (cdev2->device_class ()->terminal_id_for_name ("A"), n3);
cdev2->connect_terminal (cdev2->device_class ()->terminal_id_for_name ("B"), n2);
// verify against the input
std::string path = tmp_file ("tmp_nwriter2.txt");
{
tl::OutputStream stream (path);
db::NetlistSpiceWriter writer;
writer.write (stream, nl, "written by unit test");
}
std::string au_path = tl::combine_path (tl::combine_path (tl::testdata (), "algo"), "nwriter2b_au.txt");
compare_netlists (_this, path, au_path);
}
TEST(2_WriterCapacitorDevicesWithBulk)
{
db::Netlist nl;
@ -278,7 +333,64 @@ TEST(2_WriterCapacitorDevicesWithBulk)
writer.write (stream, nl, "written by unit test");
}
std::string au_path = tl::combine_path (tl::combine_path (tl::testdata (), "algo"), "nwriter2_au.txt");
std::string au_path = tl::combine_path (tl::combine_path (tl::testdata (), "algo"), "nwriter2c_au.txt");
compare_netlists (_this, path, au_path);
}
TEST(2_WriterCapacitorDevicesWithBulkNoName)
{
db::Netlist nl;
db::DeviceClass *ccls = new db::DeviceClassCapacitorWithBulk ();
nl.add_device_class (ccls);
db::Circuit *circuit1 = new db::Circuit ();
circuit1->set_name ("C1");
nl.add_circuit (circuit1);
db::Net *n1, *n2, *n3;
n1 = new db::Net ();
n1->set_name ("n1");
circuit1->add_net (n1);
n2 = new db::Net ();
n2->set_name ("n2");
circuit1->add_net (n2);
n3 = new db::Net ();
n3->set_name ("n3");
circuit1->add_net (n3);
db::Device *cdev1 = new db::Device (ccls);
cdev1->set_parameter_value (db::DeviceClassCapacitor::param_id_C, 1.7e-12);
db::Device *cdev2 = new db::Device (ccls);
cdev2->set_parameter_value (db::DeviceClassCapacitor::param_id_C, 42e-15);
circuit1->add_device (cdev1);
circuit1->add_device (cdev2);
size_t pid1 = circuit1->add_pin ("p1").id ();
size_t pid2 = circuit1->add_pin ("p2").id ();
circuit1->connect_pin (pid1, n1);
circuit1->connect_pin (pid2, n2);
cdev1->connect_terminal (cdev1->device_class ()->terminal_id_for_name ("A"), n1);
cdev1->connect_terminal (cdev1->device_class ()->terminal_id_for_name ("B"), n3);
cdev1->connect_terminal (cdev1->device_class ()->terminal_id_for_name ("W"), n3);
cdev2->connect_terminal (cdev2->device_class ()->terminal_id_for_name ("A"), n3);
cdev2->connect_terminal (cdev2->device_class ()->terminal_id_for_name ("B"), n2);
cdev2->connect_terminal (cdev2->device_class ()->terminal_id_for_name ("W"), n3);
// verify against the input
std::string path = tmp_file ("tmp_nwriter2.txt");
{
tl::OutputStream stream (path);
db::NetlistSpiceWriter writer;
writer.write (stream, nl, "written by unit test");
}
std::string au_path = tl::combine_path (tl::combine_path (tl::testdata (), "algo"), "nwriter2d_au.txt");
compare_netlists (_this, path, au_path);
}
@ -912,7 +1024,6 @@ TEST(10_WriterLongLines)
db::Netlist nl;
db::DeviceClass *rcls = new db::DeviceClassResistor ();
rcls->set_name ("RCLS");
nl.add_device_class (rcls);
db::Circuit *circuit1 = new db::Circuit ();

View File

@ -122,13 +122,13 @@ TEST(11_private)
TEST(12_private)
{
// test_is_long_runner ();
run_test (_this, "test_12.lvs", "test_12.cir.gz", "test_12.gds.gz", true);
run_test (_this, "test_12.lvs", "test_12b.cir.gz", "test_12.gds.gz", true);
}
TEST(13_private)
{
// test_is_long_runner ();
run_test (_this, "test_13.lvs", "test_13.cir.gz", "test_13.gds.gz", true);
run_test (_this, "test_13.lvs", "test_13b.cir.gz", "test_13.gds.gz", true);
}
TEST(14_private)
@ -152,7 +152,7 @@ TEST(16_private)
TEST(17_private)
{
test_is_long_runner ();
run_test (_this, "test_17.lylvs", "test_17.cir.gz", "test_17.gds.gz", true, "test_17.lvsdb");
run_test (_this, "test_17.lylvs", "test_17b.cir.gz", "test_17.gds.gz", true, "test_17.lvsdb");
}
TEST(18_private)

View File

@ -3,12 +3,20 @@ R$1 1 2 1.7k M=2
R$2 3 4 1.7k
R$3 1 2 1.7k RMODEL M=2
R$4 3 4 1.7k RMODEL
R$5 3 4 RMODEL R=1.7k
R$6 3 4 5 RMODEL2 R=1.7k
R$7 3 4 5 1.7k RMODEL2
R$8 3 4 5 1.7k
M$1 1 2 3 4 NMOS W=2u L=7u M=2
M$2 1 2 3 4 PMOS W=2u L=7u
C$1 1 2 1e-9 M=2
C$2 3 4 1e-9
C$3 1 2 1e-9 CMODEL M=2
C$4 3 4 1e-9 CMODEL
C$5 3 4 CMODEL C=1e-9
C$6 3 4 5 CMODEL2 C=1e-9
C$7 3 4 5 1e-9
C$8 3 4 5 1e-9 CMODEL2
L$1 1 2 1e-9 M=2
L$2 3 4 1e-9
L$3 1 2 1e-9 LMODEL M=2

View File

@ -209,205 +209,205 @@
* net 99 n98
* net 100 n99
* net 101 n100
* device instance $1 r0 *1 0,0 RCLS
* device instance $1 r0 *1 0,0
R$1 1 2 0
* device instance $2 r0 *1 0,0 RCLS
* device instance $2 r0 *1 0,0
R$2 1 3 0
* device instance $3 r0 *1 0,0 RCLS
* device instance $3 r0 *1 0,0
R$3 1 4 0
* device instance $4 r0 *1 0,0 RCLS
* device instance $4 r0 *1 0,0
R$4 1 5 0
* device instance $5 r0 *1 0,0 RCLS
* device instance $5 r0 *1 0,0
R$5 1 6 0
* device instance $6 r0 *1 0,0 RCLS
* device instance $6 r0 *1 0,0
R$6 1 7 0
* device instance $7 r0 *1 0,0 RCLS
* device instance $7 r0 *1 0,0
R$7 1 8 0
* device instance $8 r0 *1 0,0 RCLS
* device instance $8 r0 *1 0,0
R$8 1 9 0
* device instance $9 r0 *1 0,0 RCLS
* device instance $9 r0 *1 0,0
R$9 1 10 0
* device instance $10 r0 *1 0,0 RCLS
* device instance $10 r0 *1 0,0
R$10 1 11 0
* device instance $11 r0 *1 0,0 RCLS
* device instance $11 r0 *1 0,0
R$11 1 12 0
* device instance $12 r0 *1 0,0 RCLS
* device instance $12 r0 *1 0,0
R$12 1 13 0
* device instance $13 r0 *1 0,0 RCLS
* device instance $13 r0 *1 0,0
R$13 1 14 0
* device instance $14 r0 *1 0,0 RCLS
* device instance $14 r0 *1 0,0
R$14 1 15 0
* device instance $15 r0 *1 0,0 RCLS
* device instance $15 r0 *1 0,0
R$15 1 16 0
* device instance $16 r0 *1 0,0 RCLS
* device instance $16 r0 *1 0,0
R$16 1 17 0
* device instance $17 r0 *1 0,0 RCLS
* device instance $17 r0 *1 0,0
R$17 1 18 0
* device instance $18 r0 *1 0,0 RCLS
* device instance $18 r0 *1 0,0
R$18 1 19 0
* device instance $19 r0 *1 0,0 RCLS
* device instance $19 r0 *1 0,0
R$19 1 20 0
* device instance $20 r0 *1 0,0 RCLS
* device instance $20 r0 *1 0,0
R$20 1 21 0
* device instance $21 r0 *1 0,0 RCLS
* device instance $21 r0 *1 0,0
R$21 1 22 0
* device instance $22 r0 *1 0,0 RCLS
* device instance $22 r0 *1 0,0
R$22 1 23 0
* device instance $23 r0 *1 0,0 RCLS
* device instance $23 r0 *1 0,0
R$23 1 24 0
* device instance $24 r0 *1 0,0 RCLS
* device instance $24 r0 *1 0,0
R$24 1 25 0
* device instance $25 r0 *1 0,0 RCLS
* device instance $25 r0 *1 0,0
R$25 1 26 0
* device instance $26 r0 *1 0,0 RCLS
* device instance $26 r0 *1 0,0
R$26 1 27 0
* device instance $27 r0 *1 0,0 RCLS
* device instance $27 r0 *1 0,0
R$27 1 28 0
* device instance $28 r0 *1 0,0 RCLS
* device instance $28 r0 *1 0,0
R$28 1 29 0
* device instance $29 r0 *1 0,0 RCLS
* device instance $29 r0 *1 0,0
R$29 1 30 0
* device instance $30 r0 *1 0,0 RCLS
* device instance $30 r0 *1 0,0
R$30 1 31 0
* device instance $31 r0 *1 0,0 RCLS
* device instance $31 r0 *1 0,0
R$31 1 32 0
* device instance $32 r0 *1 0,0 RCLS
* device instance $32 r0 *1 0,0
R$32 1 33 0
* device instance $33 r0 *1 0,0 RCLS
* device instance $33 r0 *1 0,0
R$33 1 34 0
* device instance $34 r0 *1 0,0 RCLS
* device instance $34 r0 *1 0,0
R$34 1 35 0
* device instance $35 r0 *1 0,0 RCLS
* device instance $35 r0 *1 0,0
R$35 1 36 0
* device instance $36 r0 *1 0,0 RCLS
* device instance $36 r0 *1 0,0
R$36 1 37 0
* device instance $37 r0 *1 0,0 RCLS
* device instance $37 r0 *1 0,0
R$37 1 38 0
* device instance $38 r0 *1 0,0 RCLS
* device instance $38 r0 *1 0,0
R$38 1 39 0
* device instance $39 r0 *1 0,0 RCLS
* device instance $39 r0 *1 0,0
R$39 1 40 0
* device instance $40 r0 *1 0,0 RCLS
* device instance $40 r0 *1 0,0
R$40 1 41 0
* device instance $41 r0 *1 0,0 RCLS
* device instance $41 r0 *1 0,0
R$41 1 42 0
* device instance $42 r0 *1 0,0 RCLS
* device instance $42 r0 *1 0,0
R$42 1 43 0
* device instance $43 r0 *1 0,0 RCLS
* device instance $43 r0 *1 0,0
R$43 1 44 0
* device instance $44 r0 *1 0,0 RCLS
* device instance $44 r0 *1 0,0
R$44 1 45 0
* device instance $45 r0 *1 0,0 RCLS
* device instance $45 r0 *1 0,0
R$45 1 46 0
* device instance $46 r0 *1 0,0 RCLS
* device instance $46 r0 *1 0,0
R$46 1 47 0
* device instance $47 r0 *1 0,0 RCLS
* device instance $47 r0 *1 0,0
R$47 1 48 0
* device instance $48 r0 *1 0,0 RCLS
* device instance $48 r0 *1 0,0
R$48 1 49 0
* device instance $49 r0 *1 0,0 RCLS
* device instance $49 r0 *1 0,0
R$49 1 50 0
* device instance $50 r0 *1 0,0 RCLS
* device instance $50 r0 *1 0,0
R$50 1 51 0
* device instance $51 r0 *1 0,0 RCLS
* device instance $51 r0 *1 0,0
R$51 1 52 0
* device instance $52 r0 *1 0,0 RCLS
* device instance $52 r0 *1 0,0
R$52 1 53 0
* device instance $53 r0 *1 0,0 RCLS
* device instance $53 r0 *1 0,0
R$53 1 54 0
* device instance $54 r0 *1 0,0 RCLS
* device instance $54 r0 *1 0,0
R$54 1 55 0
* device instance $55 r0 *1 0,0 RCLS
* device instance $55 r0 *1 0,0
R$55 1 56 0
* device instance $56 r0 *1 0,0 RCLS
* device instance $56 r0 *1 0,0
R$56 1 57 0
* device instance $57 r0 *1 0,0 RCLS
* device instance $57 r0 *1 0,0
R$57 1 58 0
* device instance $58 r0 *1 0,0 RCLS
* device instance $58 r0 *1 0,0
R$58 1 59 0
* device instance $59 r0 *1 0,0 RCLS
* device instance $59 r0 *1 0,0
R$59 1 60 0
* device instance $60 r0 *1 0,0 RCLS
* device instance $60 r0 *1 0,0
R$60 1 61 0
* device instance $61 r0 *1 0,0 RCLS
* device instance $61 r0 *1 0,0
R$61 1 62 0
* device instance $62 r0 *1 0,0 RCLS
* device instance $62 r0 *1 0,0
R$62 1 63 0
* device instance $63 r0 *1 0,0 RCLS
* device instance $63 r0 *1 0,0
R$63 1 64 0
* device instance $64 r0 *1 0,0 RCLS
* device instance $64 r0 *1 0,0
R$64 1 65 0
* device instance $65 r0 *1 0,0 RCLS
* device instance $65 r0 *1 0,0
R$65 1 66 0
* device instance $66 r0 *1 0,0 RCLS
* device instance $66 r0 *1 0,0
R$66 1 67 0
* device instance $67 r0 *1 0,0 RCLS
* device instance $67 r0 *1 0,0
R$67 1 68 0
* device instance $68 r0 *1 0,0 RCLS
* device instance $68 r0 *1 0,0
R$68 1 69 0
* device instance $69 r0 *1 0,0 RCLS
* device instance $69 r0 *1 0,0
R$69 1 70 0
* device instance $70 r0 *1 0,0 RCLS
* device instance $70 r0 *1 0,0
R$70 1 71 0
* device instance $71 r0 *1 0,0 RCLS
* device instance $71 r0 *1 0,0
R$71 1 72 0
* device instance $72 r0 *1 0,0 RCLS
* device instance $72 r0 *1 0,0
R$72 1 73 0
* device instance $73 r0 *1 0,0 RCLS
* device instance $73 r0 *1 0,0
R$73 1 74 0
* device instance $74 r0 *1 0,0 RCLS
* device instance $74 r0 *1 0,0
R$74 1 75 0
* device instance $75 r0 *1 0,0 RCLS
* device instance $75 r0 *1 0,0
R$75 1 76 0
* device instance $76 r0 *1 0,0 RCLS
* device instance $76 r0 *1 0,0
R$76 1 77 0
* device instance $77 r0 *1 0,0 RCLS
* device instance $77 r0 *1 0,0
R$77 1 78 0
* device instance $78 r0 *1 0,0 RCLS
* device instance $78 r0 *1 0,0
R$78 1 79 0
* device instance $79 r0 *1 0,0 RCLS
* device instance $79 r0 *1 0,0
R$79 1 80 0
* device instance $80 r0 *1 0,0 RCLS
* device instance $80 r0 *1 0,0
R$80 1 81 0
* device instance $81 r0 *1 0,0 RCLS
* device instance $81 r0 *1 0,0
R$81 1 82 0
* device instance $82 r0 *1 0,0 RCLS
* device instance $82 r0 *1 0,0
R$82 1 83 0
* device instance $83 r0 *1 0,0 RCLS
* device instance $83 r0 *1 0,0
R$83 1 84 0
* device instance $84 r0 *1 0,0 RCLS
* device instance $84 r0 *1 0,0
R$84 1 85 0
* device instance $85 r0 *1 0,0 RCLS
* device instance $85 r0 *1 0,0
R$85 1 86 0
* device instance $86 r0 *1 0,0 RCLS
* device instance $86 r0 *1 0,0
R$86 1 87 0
* device instance $87 r0 *1 0,0 RCLS
* device instance $87 r0 *1 0,0
R$87 1 88 0
* device instance $88 r0 *1 0,0 RCLS
* device instance $88 r0 *1 0,0
R$88 1 89 0
* device instance $89 r0 *1 0,0 RCLS
* device instance $89 r0 *1 0,0
R$89 1 90 0
* device instance $90 r0 *1 0,0 RCLS
* device instance $90 r0 *1 0,0
R$90 1 91 0
* device instance $91 r0 *1 0,0 RCLS
* device instance $91 r0 *1 0,0
R$91 1 92 0
* device instance $92 r0 *1 0,0 RCLS
* device instance $92 r0 *1 0,0
R$92 1 93 0
* device instance $93 r0 *1 0,0 RCLS
* device instance $93 r0 *1 0,0
R$93 1 94 0
* device instance $94 r0 *1 0,0 RCLS
* device instance $94 r0 *1 0,0
R$94 1 95 0
* device instance $95 r0 *1 0,0 RCLS
* device instance $95 r0 *1 0,0
R$95 1 96 0
* device instance $96 r0 *1 0,0 RCLS
* device instance $96 r0 *1 0,0
R$96 1 97 0
* device instance $97 r0 *1 0,0 RCLS
* device instance $97 r0 *1 0,0
R$97 1 98 0
* device instance $98 r0 *1 0,0 RCLS
* device instance $98 r0 *1 0,0
R$98 1 99 0
* device instance $99 r0 *1 0,0 RCLS
* device instance $99 r0 *1 0,0
R$99 1 100 0
* device instance $100 r0 *1 0,0 RCLS
* device instance $100 r0 *1 0,0
R$100 1 101 0
.ENDS
+ C1withaverylongextensionthatgoesbeyondmultiplelinesunlessipasteeverythingtogetherwhichmakesithardtoreadbutexactlythatisthereasonwhyiwriteitthisway

View File

@ -8,7 +8,7 @@
* net 2 n2
* net 3 n3
* device instance $1 r0 *1 0,0 RCLS
R$1 1 3 1.7
R$1 1 3 1.7 RCLS
* device instance $2 r0 *1 0,0 RCLS
R$2 3 2 4.2e-05
R$2 3 2 4.2e-05 RCLS
.ENDS C1

14
testdata/algo/nwriter1b_au.txt vendored Normal file
View File

@ -0,0 +1,14 @@
* written by unit test
* cell C1
* pin p1
* pin p2
.SUBCKT C1 1 2
* net 1 n1
* net 2 n2
* net 3 n3
* device instance $1 r0 *1 0,0 RCLS
R$1 1 3 3 1.7 RCLS
* device instance $2 r0 *1 0,0 RCLS
R$2 3 2 3 4.2e-05 RCLS
.ENDS C1

View File

@ -16,10 +16,10 @@
* net 3 n3
* device instance $1 r0 *1 0,0 RCLS
*** Before device $1
R$1 1 3 1.7
R$1 1 3 1.7 RCLS
*** After device $1
* device instance $2 r0 *1 0,0 RCLS
*** Before device $2
R$2 3 2 4.2e-05
R$2 3 2 4.2e-05 RCLS
*** After device $2
.ENDS C1

View File

@ -8,7 +8,7 @@
* net 2 n2
* net 3 n3
* device instance $1 r0 *1 0,0 CCLS
C$1 1 3 1.7e-12
C$1 1 3 1.7e-12 CCLS
* device instance $2 r0 *1 0,0 CCLS
C$2 3 2 4.2e-14
C$2 3 2 4.2e-14 CCLS
.ENDS C1

14
testdata/algo/nwriter2b_au.txt vendored Normal file
View File

@ -0,0 +1,14 @@
* written by unit test
* cell C1
* pin p1
* pin p2
.SUBCKT C1 1 2
* net 1 n1
* net 2 n2
* net 3 n3
* device instance $1 r0 *1 0,0
C$1 1 3 1.7e-12
* device instance $2 r0 *1 0,0
C$2 3 2 4.2e-14
.ENDS C1

14
testdata/algo/nwriter2c_au.txt vendored Normal file
View File

@ -0,0 +1,14 @@
* written by unit test
* cell C1
* pin p1
* pin p2
.SUBCKT C1 1 2
* net 1 n1
* net 2 n2
* net 3 n3
* device instance $1 r0 *1 0,0 CCLS
C$1 1 3 3 1.7e-12 CCLS
* device instance $2 r0 *1 0,0 CCLS
C$2 3 2 3 4.2e-14 CCLS
.ENDS C1

14
testdata/algo/nwriter2d_au.txt vendored Normal file
View File

@ -0,0 +1,14 @@
* written by unit test
* cell C1
* pin p1
* pin p2
.SUBCKT C1 1 2
* net 1 n1
* net 2 n2
* net 3 n3
* device instance $1 r0 *1 0,0
C$1 1 3 3 1.7e-12
* device instance $2 r0 *1 0,0
C$2 3 2 3 4.2e-14
.ENDS C1

View File

@ -8,7 +8,7 @@
* net 2 n2
* net 3 n3
* device instance $1 r0 *1 0,0 LCLS
L$1 1 3 1.7e-10
L$1 1 3 1.7e-10 LCLS
* device instance $2 r0 *1 0,0 LCLS
L$2 3 2 4.2e-08
L$2 3 2 4.2e-08 LCLS
.ENDS C1

View File

@ -74,6 +74,30 @@ class MyNetlistSpiceReaderDelegate < RBA::NetlistSpiceReaderDelegate
end
class MyNetlistSpiceReaderDelegate2 < MyNetlistSpiceReaderDelegate
def start(netlist)
netlist.description = "Read by MyDelegate2"
end
def finish(netlist)
netlist.description = "Read by MyDelegate2 (sucessfully)"
end
def translate_net_name(n)
return n == "VDD" ? "VXX" : super
end
def parse_element(s, element)
data = super
if element == "R"
data.model_name = "WIDERSTAND"
end
data
end
end
class DBNetlistReaderTests_TestClass < TestBase
def test_1_Basic
@ -107,6 +131,37 @@ END
end
def test_1b_Basic
nl = RBA::Netlist::new
input = File.join($ut_testsrc, "testdata", "algo", "nreader6.cir")
mydelegate = MyNetlistSpiceReaderDelegate2::new
reader = RBA::NetlistSpiceReader::new(mydelegate)
# the delegate is kept by the SPICE writer ..
mydelegate = nil
GC.start
nl.read(input, reader)
assert_equal(nl.description, "Read by MyDelegate2 (sucessfully)")
assert_equal(nl.to_s, <<"END")
circuit SUBCKT ($1=$1,A=A,VXX=VXX,Z=Z,GND=GND,GND$1=GND$1);
device HVPMOS $1 (S=VXX,G=$3,D=Z,B=$1) (L=0.3,W=1.5,AS=0.27,AD=0.27,PS=3.24,PD=3.24);
device HVPMOS $2 (S=VXX,G=A,D=$3,B=$1) (L=0.3,W=1.5,AS=0.27,AD=0.27,PS=3.24,PD=3.24);
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);
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);
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);
device WIDERSTAND $1 (A=A,B=Z) (R=100000,L=0,W=0,A=0,P=0);
end;
circuit .TOP ();
subcircuit SUBCKT SUBCKT ($1=IN,A=OUT,VXX=VXX,Z=Z,GND=VSS,GND$1=VSS);
end;
END
end
def test_2_WithError
nl = RBA::Netlist::new
@ -136,6 +191,50 @@ END
end
def test_3_delegateHelpers
dg = RBA::NetlistSpiceReaderDelegate::new
assert_equal(dg.value_from_string("xy").inspect, "nil")
assert_equal(dg.value_from_string("17.5").inspect, "17.5")
assert_equal(dg.value_from_string("1k").inspect, "1000.0")
assert_equal(dg.value_from_string("1pF*2.5").inspect, "2.5e-12")
assert_equal(dg.value_from_string("(1+3)*2").inspect, "8.0")
end
def test_4_ParseElementData
pd = RBA::ParseElementData::new
pd.model_name = "a"
assert_equal(pd.model_name, "a")
pd.value = 42
assert_equal(pd.value, 42)
pd.net_names = [ "x", "y", "z" ]
assert_equal(pd.net_names.join(","), "x,y,z")
pd.parameters = { "A" => 17.5, "B" => 1 }
assert_equal(pd.parameters.inspect, "{\"A\"=>17.5, \"B\"=>1.0}")
end
def test_5_ParseElementComponentsData
pd = RBA::ParseElementComponentsData::new
pd.strings = [ "x", "y", "z" ]
assert_equal(pd.strings.join(","), "x,y,z")
pd.parameters = { "A" => 17.5, "B" => 1 }
assert_equal(pd.parameters.inspect, "{\"A\"=>17.5, \"B\"=>1.0}")
end
def test_6_delegateHelpers2
dg = RBA::NetlistSpiceReaderDelegate::new
pd = dg.parse_element_components("17 5 1e-9 a=17 b=1k")
assert_equal(pd.strings.join(","), "17,5,1e-9")
assert_equal(pd.parameters.inspect, "{\"A\"=>17.0, \"B\"=>1000.0}")
end
end
load("test_epilogue.rb")