mirror of https://github.com/KLayout/klayout.git
Another testcase for Spice reader with parametric subcircuits, .param statements, bug fixes
This commit is contained in:
parent
2e3c859cd3
commit
3ac1385d87
|
|
@ -46,6 +46,31 @@ static const char *allowed_name_chars = "_.:,!+$/&\\#[]|<>";
|
|||
|
||||
// ------------------------------------------------------------------------------------------------------
|
||||
|
||||
void
|
||||
read_param_card (tl::Extractor &ex, const db::Netlist *netlist, std::map<std::string, tl::Variant> &variables)
|
||||
{
|
||||
// Syntax is:
|
||||
// .param <name> = <value> [ <name> = <value> ... ]
|
||||
// taken from:
|
||||
// https://nmg.gitlab.io/ngspice-manual/circuitdescription/paramparametricnetlists/paramline.html
|
||||
|
||||
while (! ex.at_end ()) {
|
||||
|
||||
std::string name;
|
||||
ex.read_word (name);
|
||||
|
||||
name = netlist->normalize_name (name);
|
||||
|
||||
ex.test ("=");
|
||||
|
||||
tl::Variant value = NetlistSpiceReaderExpressionParser (&variables).read (ex);
|
||||
variables [name] = value;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------------
|
||||
|
||||
class SpiceReaderStream
|
||||
{
|
||||
public:
|
||||
|
|
@ -453,7 +478,7 @@ SpiceCircuitDict::read (tl::InputStream &stream)
|
|||
read_card ();
|
||||
}
|
||||
|
||||
} catch (NetlistSpiceReaderError &ex) {
|
||||
} catch (tl::Exception &ex) {
|
||||
|
||||
// Translate the exception and add a location
|
||||
error (ex.msg ());
|
||||
|
|
@ -570,58 +595,43 @@ SpiceCircuitDict::read_card ()
|
|||
tl::Extractor ex (l.c_str ());
|
||||
std::string name;
|
||||
|
||||
if (ex.test_without_case (".")) {
|
||||
if (ex.test_without_case (".model")) {
|
||||
|
||||
// control statement
|
||||
if (ex.test_without_case ("model")) {
|
||||
// ignore model statements
|
||||
|
||||
// ignore model statements
|
||||
} else if (ex.test_without_case (".global")) {
|
||||
|
||||
} else if (ex.test_without_case ("global")) {
|
||||
|
||||
while (! ex.at_end ()) {
|
||||
std::string n = mp_delegate->translate_net_name (read_name (ex, mp_netlist));
|
||||
if (m_global_net_names.find (n) == m_global_net_names.end ()) {
|
||||
m_global_nets.push_back (n);
|
||||
m_global_net_names.insert (n);
|
||||
}
|
||||
while (! ex.at_end ()) {
|
||||
std::string n = mp_delegate->translate_net_name (read_name (ex, mp_netlist));
|
||||
if (m_global_net_names.find (n) == m_global_net_names.end ()) {
|
||||
m_global_nets.push_back (n);
|
||||
m_global_net_names.insert (n);
|
||||
}
|
||||
}
|
||||
|
||||
} else if (ex.test_without_case ("subckt")) {
|
||||
} else if (ex.test_without_case (".subckt")) {
|
||||
|
||||
std::string nc = read_name (ex, mp_netlist);
|
||||
read_circuit (ex, nc);
|
||||
std::string nc = read_name (ex, mp_netlist);
|
||||
read_circuit (ex, nc);
|
||||
|
||||
} else if (ex.test_without_case ("ends")) {
|
||||
} else if (ex.test_without_case (".ends")) {
|
||||
|
||||
return true;
|
||||
return true;
|
||||
|
||||
} else if (ex.test_without_case ("end")) {
|
||||
} else if (ex.test_without_case (".end")) {
|
||||
|
||||
// ignore end statements
|
||||
// ignore end statements
|
||||
|
||||
} else if (ex.test_without_case ("param")) {
|
||||
} else if (ex.test_without_case (".param")) {
|
||||
|
||||
// Syntax is:
|
||||
// .param <name> = <value> [ <name> = <value> ... ]
|
||||
// taken from:
|
||||
// https://nmg.gitlab.io/ngspice-manual/circuitdescription/paramparametricnetlists/paramline.html
|
||||
read_param_card (ex, mp_netlist, m_variables);
|
||||
|
||||
while (! ex.at_end ()) {
|
||||
ensure_circuit ();
|
||||
mp_circuit->add_card (SpiceCard (m_file_id, m_stream.line_number (), l));
|
||||
|
||||
std::string name;
|
||||
ex.read_word (name);
|
||||
} else if (ex.test (".")) {
|
||||
|
||||
name = mp_netlist->normalize_name (name);
|
||||
|
||||
ex.test ("=");
|
||||
|
||||
tl::Variant value = NetlistSpiceReaderExpressionParser (&m_variables).read (ex);
|
||||
m_variables [name] = value;
|
||||
|
||||
}
|
||||
|
||||
} else if (! mp_delegate->control_statement (l)) {
|
||||
if (! mp_delegate->control_statement (l)) {
|
||||
|
||||
std::string s;
|
||||
ex.read_word (s);
|
||||
|
|
@ -841,7 +851,7 @@ SpiceNetlistBuilder::build ()
|
|||
build_global_nets ();
|
||||
mp_delegate->do_finish ();
|
||||
|
||||
} catch (NetlistSpiceReaderError &ex) {
|
||||
} catch (tl::Exception &ex) {
|
||||
|
||||
// translate the error and add a source location
|
||||
error (ex.msg ());
|
||||
|
|
@ -861,30 +871,34 @@ make_circuit_name (const std::string &name, const NetlistSpiceReader::parameters
|
|||
}
|
||||
res += p->first;
|
||||
res += "=";
|
||||
double v = p->second.to_double ();
|
||||
double va = fabs (v);
|
||||
if (va < 1e-15) {
|
||||
res += tl::sprintf ("%g", v);
|
||||
} else if (va < 0.1e-12) {
|
||||
res += tl::sprintf ("%gF", v * 1e15);
|
||||
} else if (va < 0.1e-9) {
|
||||
res += tl::sprintf ("%gP", v * 1e12);
|
||||
} else if (va < 0.1e-6) {
|
||||
res += tl::sprintf ("%gN", v * 1e9);
|
||||
} else if (va < 0.1e-3) {
|
||||
res += tl::sprintf ("%gU", v * 1e6);
|
||||
} else if (va< 0.1) {
|
||||
res += tl::sprintf ("%gM", v * 1e3);
|
||||
} else if (va < 0.1e3) {
|
||||
res += tl::sprintf ("%g", v);
|
||||
} else if (va < 0.1e6) {
|
||||
res += tl::sprintf ("%gK", v * 1e-3);
|
||||
} else if (va < 0.1e9) {
|
||||
res += tl::sprintf ("%gMEG", v * 1e-6);
|
||||
} else if (va < 0.1e12) {
|
||||
res += tl::sprintf ("%gG", v * 1e-9);
|
||||
if (p->second.can_convert_to_double()) {
|
||||
double v = p->second.to_double ();
|
||||
double va = fabs (v);
|
||||
if (va < 1e-15) {
|
||||
res += tl::sprintf ("%g", v);
|
||||
} else if (va < 0.1e-12) {
|
||||
res += tl::sprintf ("%gF", v * 1e15);
|
||||
} else if (va < 0.1e-9) {
|
||||
res += tl::sprintf ("%gP", v * 1e12);
|
||||
} else if (va < 0.1e-6) {
|
||||
res += tl::sprintf ("%gN", v * 1e9);
|
||||
} else if (va < 0.1e-3) {
|
||||
res += tl::sprintf ("%gU", v * 1e6);
|
||||
} else if (va< 0.1) {
|
||||
res += tl::sprintf ("%gM", v * 1e3);
|
||||
} else if (va < 0.1e3) {
|
||||
res += tl::sprintf ("%g", v);
|
||||
} else if (va < 0.1e6) {
|
||||
res += tl::sprintf ("%gK", v * 1e-3);
|
||||
} else if (va < 0.1e9) {
|
||||
res += tl::sprintf ("%gMEG", v * 1e-6);
|
||||
} else if (va < 0.1e12) {
|
||||
res += tl::sprintf ("%gG", v * 1e-9);
|
||||
} else {
|
||||
res += tl::sprintf ("%g", v);
|
||||
}
|
||||
} else {
|
||||
res += tl::sprintf ("%g", v);
|
||||
res += p->second.to_string ();
|
||||
}
|
||||
}
|
||||
res += ")";
|
||||
|
|
@ -900,6 +914,12 @@ SpiceNetlistBuilder::build_circuit (const SpiceCachedCircuit *cc, const paramete
|
|||
return c;
|
||||
}
|
||||
|
||||
for (auto p = pv.begin (); p != pv.end (); ++p) {
|
||||
if (cc->parameters ().find (p->first) == cc->parameters ().end ()) {
|
||||
warn (tl::sprintf (tl::to_string (tr ("Not a known parameter for circuit '%s': '%s'")), cc->name (), p->first));
|
||||
}
|
||||
}
|
||||
|
||||
c = new db::Circuit ();
|
||||
mp_netlist->add_circuit (c);
|
||||
if (pv.empty ()) {
|
||||
|
|
@ -991,7 +1011,11 @@ SpiceNetlistBuilder::process_card (const SpiceCard &card)
|
|||
ex = tl::Extractor (card.text.c_str ());
|
||||
ex.skip ();
|
||||
|
||||
if (isalpha (*ex)) {
|
||||
if (ex.test_without_case (".param")) {
|
||||
|
||||
read_param_card (ex, mp_netlist, m_variables);
|
||||
|
||||
} else if (isalpha (*ex)) {
|
||||
|
||||
std::string prefix;
|
||||
prefix.push_back (toupper (*ex));
|
||||
|
|
|
|||
|
|
@ -39,18 +39,6 @@ namespace db
|
|||
class Netlist;
|
||||
class NetlistSpiceReaderDelegate;
|
||||
|
||||
/**
|
||||
* @brief A specialized exception class to handle netlist reader delegate errors
|
||||
*/
|
||||
class DB_PUBLIC NetlistSpiceReaderError
|
||||
: public tl::Exception
|
||||
{
|
||||
public:
|
||||
NetlistSpiceReaderError (const std::string &msg)
|
||||
: tl::Exception (msg)
|
||||
{ }
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief A SPICE format reader for netlists
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -124,7 +124,7 @@ std::string NetlistSpiceReaderDelegate::translate_net_name (const std::string &n
|
|||
|
||||
void NetlistSpiceReaderDelegate::error (const std::string &msg)
|
||||
{
|
||||
throw NetlistSpiceReaderError (msg);
|
||||
throw tl::Exception (msg);
|
||||
}
|
||||
|
||||
template <class Cls>
|
||||
|
|
|
|||
|
|
@ -518,22 +518,15 @@ tl::Variant NetlistSpiceReaderExpressionParser::read (const std::string &s) cons
|
|||
|
||||
tl::Variant NetlistSpiceReaderExpressionParser::read (tl::Extractor &ex) const
|
||||
{
|
||||
try {
|
||||
tl::Variant res;
|
||||
|
||||
tl::Variant res;
|
||||
|
||||
const char *endquote = start_quote (ex);
|
||||
res = read_tl_expr (ex, 0);
|
||||
if (endquote) {
|
||||
ex.test (endquote);
|
||||
}
|
||||
|
||||
return res;
|
||||
|
||||
} catch (tl::Exception &error) {
|
||||
// recast the exception, so we can process it later
|
||||
throw NetlistSpiceReaderError (error.msg ());
|
||||
const char *endquote = start_quote (ex);
|
||||
res = read_tl_expr (ex, 0);
|
||||
if (endquote) {
|
||||
ex.test (endquote);
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
bool NetlistSpiceReaderExpressionParser::try_read (const std::string &s, tl::Variant &value) const
|
||||
|
|
|
|||
|
|
@ -636,13 +636,15 @@ TEST(16_issue898)
|
|||
|
||||
TEST(17_RecursiveExpansion)
|
||||
{
|
||||
db::Netlist nl;
|
||||
db::Netlist nl, nl2;
|
||||
|
||||
std::string path = tl::combine_path (tl::combine_path (tl::testdata (), "algo"), "nreader17.cir");
|
||||
{
|
||||
std::string path = tl::combine_path (tl::combine_path (tl::testdata (), "algo"), "nreader17.cir");
|
||||
|
||||
db::NetlistSpiceReader reader;
|
||||
tl::InputStream is (path);
|
||||
reader.read (is, nl);
|
||||
db::NetlistSpiceReader reader;
|
||||
tl::InputStream is (path);
|
||||
reader.read (is, nl);
|
||||
}
|
||||
|
||||
EXPECT_EQ (nl.to_string (),
|
||||
"circuit .TOP ();\n"
|
||||
|
|
@ -670,6 +672,16 @@ TEST(17_RecursiveExpansion)
|
|||
" device NMOS NMOS (S=N1,G=N2,D=N3,B=N1) (L=250000,W=6000000,AS=0,AD=0,PS=0,PD=0);\n"
|
||||
"end;\n"
|
||||
);
|
||||
|
||||
{
|
||||
std::string path = tl::combine_path (tl::combine_path (tl::testdata (), "algo"), "nreader17b.cir");
|
||||
|
||||
db::NetlistSpiceReader reader;
|
||||
tl::InputStream is (path);
|
||||
reader.read (is, nl2);
|
||||
}
|
||||
|
||||
EXPECT_EQ (nl2.to_string (), nl.to_string ());
|
||||
}
|
||||
|
||||
TEST(18_XSchemOutput)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,24 @@
|
|||
|
||||
* recursive expansion of parametrized subcircuits
|
||||
|
||||
.param w1 1.5 w2 {w1*2}
|
||||
.param l1 0.15 l2 'l1+0.1'
|
||||
|
||||
Xsub1a a b c sub1 w=w1 l=l1
|
||||
Xsub1b a b c sub1 w=w2 l=l2
|
||||
|
||||
.subckt sub1 n1 n2 n3 w l
|
||||
* declares w and l parameters instead of nodes:
|
||||
w = 1.0
|
||||
l = 1.0
|
||||
.param w1 = w
|
||||
.param l1 = l
|
||||
Xsub2a n1 n2 n3 sub2 w=w1 l=l1 m=1
|
||||
.param w2 "w+0.0" l2 l*(0.5+0.5)
|
||||
Xsub2b n1 n2 n3 sub2 w=w2 l=l2 m=2
|
||||
.ends
|
||||
|
||||
.subckt sub2 n1 n2 n3 w=0.0 l=0.0 m=0
|
||||
Mnmos n1 n2 n3 n1 nmos w=w l=l m=m
|
||||
.ends
|
||||
|
||||
Loading…
Reference in New Issue