mirror of https://github.com/KLayout/klayout.git
Merge pull request #800 from KLayout/issue-798
Issue 798 (Spice reader enhancements for 3-terminal R and C)
This commit is contained in:
commit
1a2054b484
|
|
@ -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 ();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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> ¶ms);
|
||||
|
||||
/**
|
||||
* @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);
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
||||
|
|
|
|||
|
|
@ -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> ¶meters () const { return m_parameters; }
|
||||
std::map<std::string, double> ¶meters_nc () { return m_parameters; }
|
||||
void set_parameters (const std::map<std::string, double> ¶meters) { 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> ¶meters () const { return m_parameters; }
|
||||
std::map<std::string, double> ¶meters_nc () { return m_parameters; }
|
||||
void set_parameters (const std::map<std::string, double> ¶meters) { 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> ¶ms)
|
||||
{
|
||||
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> ¶ms)
|
||||
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> ¶ms)
|
||||
{
|
||||
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."
|
||||
);
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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 ();
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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")
|
||||
|
|
|
|||
Loading…
Reference in New Issue