mirror of https://github.com/KLayout/klayout.git
WIP: bug fixing, new test cases
This commit is contained in:
parent
44e123f343
commit
be5e16c125
|
|
@ -453,7 +453,7 @@ SpiceCircuitDict::read (tl::InputStream &stream)
|
|||
read_card ();
|
||||
}
|
||||
|
||||
} catch (NetlistSpiceReaderDelegateError &ex) {
|
||||
} catch (NetlistSpiceReaderError &ex) {
|
||||
|
||||
// Translate the exception and add a location
|
||||
error (ex.msg ());
|
||||
|
|
@ -612,6 +612,8 @@ SpiceCircuitDict::read_card ()
|
|||
std::string name;
|
||||
ex.read_word (name);
|
||||
|
||||
name = mp_netlist->normalize_name (name);
|
||||
|
||||
ex.test ("=");
|
||||
|
||||
tl::Variant value = NetlistSpiceReaderExpressionParser (&m_variables).read (ex);
|
||||
|
|
@ -634,16 +636,14 @@ SpiceCircuitDict::read_card ()
|
|||
|
||||
if (ex.test ("=")) {
|
||||
|
||||
name = tl::to_upper_case (name);
|
||||
name = mp_netlist->normalize_name (name);
|
||||
|
||||
tl::Variant value = NetlistSpiceReaderDelegate::read_value (ex, m_variables);
|
||||
m_variables [name] = value;
|
||||
|
||||
mp_circuit->make_parameter (name, value);
|
||||
|
||||
}
|
||||
|
||||
if (name[0] == 'X') {
|
||||
} else if (name[0] == 'X') {
|
||||
|
||||
// register circuit calls so we can figure out the top level circuits
|
||||
|
||||
|
|
@ -653,7 +653,7 @@ SpiceCircuitDict::read_card ()
|
|||
|
||||
std::vector<std::string> nn;
|
||||
parameters_type pv;
|
||||
NetlistSpiceReaderDelegate::parse_element_components (ex2.get (), nn, pv, m_variables);
|
||||
mp_delegate->parse_element_components (ex2.get (), nn, pv, m_variables);
|
||||
|
||||
if (! nn.empty ()) {
|
||||
m_called_circuits.insert (nn.back ());
|
||||
|
|
@ -689,7 +689,7 @@ SpiceCircuitDict::read_circuit (tl::Extractor &ex, const std::string &nc)
|
|||
{
|
||||
std::vector<std::string> nn;
|
||||
NetlistSpiceReader::parameters_type pv;
|
||||
NetlistSpiceReaderDelegate::parse_element_components (ex.skip (), nn, pv, m_variables);
|
||||
mp_delegate->parse_element_components (ex.skip (), nn, pv, m_variables);
|
||||
|
||||
if (cached_circuit (nc)) {
|
||||
error (tl::sprintf (tl::to_string (tr ("Redefinition of circuit %s")), nc));
|
||||
|
|
@ -764,7 +764,7 @@ private:
|
|||
};
|
||||
|
||||
SpiceNetlistBuilder::SpiceNetlistBuilder (SpiceCircuitDict *dict, Netlist *netlist, NetlistSpiceReaderDelegate *delegate)
|
||||
: mp_dict (dict), mp_delegate (delegate), mp_netlist (netlist), m_strict (false)
|
||||
: mp_dict (dict), mp_delegate (delegate), mp_netlist (netlist), m_strict (true)
|
||||
{
|
||||
mp_circuit = 0;
|
||||
mp_netlist_circuit = 0;
|
||||
|
|
@ -829,7 +829,7 @@ SpiceNetlistBuilder::build ()
|
|||
mp_current_card = 0;
|
||||
m_captured.clear ();
|
||||
|
||||
mp_delegate->start (mp_netlist);
|
||||
mp_delegate->do_start ();
|
||||
|
||||
for (auto c = mp_dict->begin_circuits (); c != mp_dict->end_circuits (); ++c) {
|
||||
if (mp_dict->is_top_circuit (c->first) && ! subcircuit_captured (c->first)) {
|
||||
|
|
@ -839,9 +839,9 @@ SpiceNetlistBuilder::build ()
|
|||
}
|
||||
|
||||
build_global_nets ();
|
||||
mp_delegate->finish (mp_netlist);
|
||||
mp_delegate->do_finish ();
|
||||
|
||||
} catch (NetlistSpiceReaderDelegateError &ex) {
|
||||
} catch (NetlistSpiceReaderError &ex) {
|
||||
|
||||
// translate the error and add a source location
|
||||
error (ex.msg ());
|
||||
|
|
@ -913,7 +913,10 @@ SpiceNetlistBuilder::build_circuit (const SpiceCachedCircuit *cc, const paramete
|
|||
std::unique_ptr<std::map<std::string, db::Net *> > n2n (mp_nets_by_name.release ());
|
||||
mp_nets_by_name.reset (0);
|
||||
|
||||
NetlistSpiceReader::parameters_type vars;
|
||||
NetlistSpiceReader::parameters_type vars = cc->parameters ();
|
||||
for (auto p = pv.begin (); p != pv.end (); ++p) {
|
||||
vars [p->first] = p->second;
|
||||
}
|
||||
|
||||
std::swap (vars, m_variables);
|
||||
std::swap (c, mp_netlist_circuit);
|
||||
|
|
@ -981,7 +984,7 @@ SpiceNetlistBuilder::process_card (const SpiceCard &card)
|
|||
std::string name;
|
||||
if (ex.try_read_word (name) && ex.test ("=")) {
|
||||
|
||||
m_variables.insert (std::make_pair (tl::to_upper_case (name), NetlistSpiceReaderDelegate::read_value (ex, m_variables)));
|
||||
m_variables.insert (std::make_pair (mp_netlist->normalize_name (name), NetlistSpiceReaderDelegate::read_value (ex, m_variables)));
|
||||
|
||||
} else {
|
||||
|
||||
|
|
@ -1035,7 +1038,7 @@ SpiceNetlistBuilder::process_element (tl::Extractor &ex, const std::string &pref
|
|||
|
||||
std::vector<db::Net *> nets;
|
||||
for (std::vector<std::string>::const_iterator i = nn.begin (); i != nn.end (); ++i) {
|
||||
nets.push_back (make_net (mp_delegate->translate_net_name (mp_netlist->normalize_name (*i))));
|
||||
nets.push_back (make_net (mp_delegate->translate_net_name (*i)));
|
||||
}
|
||||
|
||||
if (prefix == "X" && ! subcircuit_captured (model)) {
|
||||
|
|
@ -1118,7 +1121,7 @@ SpiceNetlistBuilder::build_global_nets ()
|
|||
// ------------------------------------------------------------------------------------------------------
|
||||
|
||||
NetlistSpiceReader::NetlistSpiceReader (NetlistSpiceReaderDelegate *delegate)
|
||||
: mp_delegate (delegate)
|
||||
: mp_delegate (delegate), m_strict (false)
|
||||
{
|
||||
static NetlistSpiceReaderDelegate std_delegate;
|
||||
if (! delegate) {
|
||||
|
|
@ -1135,20 +1138,32 @@ void NetlistSpiceReader::read (tl::InputStream &stream, db::Netlist &netlist)
|
|||
{
|
||||
tl::SelfTimer timer (tl::verbosity () >= 21, tl::to_string (tr ("Reading netlist ")) + stream.source ());
|
||||
|
||||
// SPICE netlists are case insensitive
|
||||
netlist.set_case_sensitive (false);
|
||||
|
||||
SpiceCircuitDict dict (this, &netlist, mp_delegate.get ());
|
||||
try {
|
||||
dict.read (stream);
|
||||
dict.finish ();
|
||||
|
||||
mp_delegate->set_netlist (&netlist);
|
||||
|
||||
// SPICE netlists are case insensitive
|
||||
netlist.set_case_sensitive (false);
|
||||
|
||||
SpiceCircuitDict dict (this, &netlist, mp_delegate.get ());
|
||||
try {
|
||||
dict.read (stream);
|
||||
dict.finish ();
|
||||
} catch (...) {
|
||||
dict.finish ();
|
||||
throw;
|
||||
}
|
||||
|
||||
SpiceNetlistBuilder builder (&dict, &netlist, mp_delegate.get ());
|
||||
builder.set_strict (m_strict);
|
||||
builder.build ();
|
||||
|
||||
mp_delegate->set_netlist (0);
|
||||
|
||||
} catch (...) {
|
||||
dict.finish ();
|
||||
mp_delegate->set_netlist (0);
|
||||
throw;
|
||||
}
|
||||
|
||||
SpiceNetlistBuilder builder (&dict, &netlist, mp_delegate.get ());
|
||||
builder.build ();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -42,11 +42,11 @@ class NetlistSpiceReaderDelegate;
|
|||
/**
|
||||
* @brief A specialized exception class to handle netlist reader delegate errors
|
||||
*/
|
||||
class DB_PUBLIC NetlistSpiceReaderDelegateError
|
||||
class DB_PUBLIC NetlistSpiceReaderError
|
||||
: public tl::Exception
|
||||
{
|
||||
public:
|
||||
NetlistSpiceReaderDelegateError (const std::string &msg)
|
||||
NetlistSpiceReaderError (const std::string &msg)
|
||||
: tl::Exception (msg)
|
||||
{ }
|
||||
};
|
||||
|
|
@ -65,8 +65,18 @@ public:
|
|||
|
||||
virtual void read (tl::InputStream &stream, db::Netlist &netlist);
|
||||
|
||||
/**
|
||||
* @brief Sets or resets strict mode
|
||||
* In strict mode, all subcircuits need to be present in the net list for example.
|
||||
*/
|
||||
void set_strict (bool s)
|
||||
{
|
||||
m_strict = s;
|
||||
}
|
||||
|
||||
private:
|
||||
tl::weak_ptr<NetlistSpiceReaderDelegate> mp_delegate;
|
||||
bool m_strict;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -123,7 +123,7 @@ std::string NetlistSpiceReaderDelegate::translate_net_name (const std::string &n
|
|||
|
||||
void NetlistSpiceReaderDelegate::error (const std::string &msg)
|
||||
{
|
||||
throw NetlistSpiceReaderDelegateError (msg);
|
||||
throw NetlistSpiceReaderError (msg);
|
||||
}
|
||||
|
||||
template <class Cls>
|
||||
|
|
@ -194,8 +194,8 @@ void NetlistSpiceReaderDelegate::parse_element_components (const std::string &s,
|
|||
|
||||
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, variables)));
|
||||
// a parameter
|
||||
pv [mp_netlist ? mp_netlist->normalize_name (n) : n] = read_value (ex, variables);
|
||||
|
||||
} else {
|
||||
|
||||
|
|
@ -207,13 +207,24 @@ void NetlistSpiceReaderDelegate::parse_element_components (const std::string &s,
|
|||
|
||||
std::string comp_name = parse_component (ex);
|
||||
|
||||
// resolve variables if string type
|
||||
auto v = variables.find (comp_name);
|
||||
if (v != variables.end () && v->second.is_a_string ()) {
|
||||
comp_name = v->second.to_string ();
|
||||
if (mp_netlist) {
|
||||
comp_name = mp_netlist->normalize_name (comp_name);
|
||||
}
|
||||
|
||||
strings.push_back (comp_name);
|
||||
// resolve variables if string type
|
||||
auto v = variables.find (comp_name);
|
||||
if (v != variables.end ()) {
|
||||
if (v->second.is_a_string ()) {
|
||||
strings.push_back (v->second.to_string ());
|
||||
} else if (v->second.can_convert_to_double ()) {
|
||||
// NOTE: this allows using a variable name "x" instead of "x=x"
|
||||
pv [comp_name] = v->second;
|
||||
} else {
|
||||
strings.push_back (comp_name);
|
||||
}
|
||||
} else {
|
||||
strings.push_back (comp_name);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
|
@ -536,11 +547,11 @@ bool NetlistSpiceReaderDelegate::element (db::Circuit *circuit, const std::strin
|
|||
return true;
|
||||
}
|
||||
|
||||
double
|
||||
tl::Variant
|
||||
NetlistSpiceReaderDelegate::read_value (tl::Extractor &ex, const std::map<std::string, tl::Variant> &variables)
|
||||
{
|
||||
NetlistSpiceReaderExpressionParser parser (&variables);
|
||||
return parser.read (ex).to_double ();
|
||||
return parser.read (ex);
|
||||
}
|
||||
|
||||
bool
|
||||
|
|
@ -552,6 +563,9 @@ NetlistSpiceReaderDelegate::try_read_value (const std::string &s, double &v, con
|
|||
tl::Extractor ex (s.c_str ());
|
||||
bool res = parser.try_read (ex, vv);
|
||||
|
||||
if (res && ! vv.can_convert_to_double ()) {
|
||||
res = false;
|
||||
}
|
||||
if (res) {
|
||||
v = vv.to_double ();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -124,17 +124,44 @@ public:
|
|||
* @brief Reads a set of string components and parameters from the string
|
||||
* A special key "param:" is recognized for starting a parameter list.
|
||||
*/
|
||||
static void parse_element_components (const std::string &s, std::vector<std::string> &strings, std::map<std::string, tl::Variant> &pv, const std::map<std::string, tl::Variant> &variables);
|
||||
void parse_element_components (const std::string &s, std::vector<std::string> &strings, std::map<std::string, tl::Variant> &pv, const std::map<std::string, tl::Variant> &variables);
|
||||
|
||||
/**
|
||||
* @brief Reads a value from the extractor (with formula evaluation)
|
||||
*/
|
||||
static double read_value (tl::Extractor &ex, const std::map<std::string, tl::Variant> &variables);
|
||||
static tl::Variant read_value(tl::Extractor &ex, const std::map<std::string, tl::Variant> &variables);
|
||||
|
||||
/**
|
||||
* @brief Tries to read a value from the extractor (with formula evaluation)
|
||||
*/
|
||||
static bool try_read_value (const std::string &s, double &v, const std::map<std::string, tl::Variant> &variables);
|
||||
|
||||
/**
|
||||
* @brief External interface for start
|
||||
*/
|
||||
void do_start ()
|
||||
{
|
||||
start (mp_netlist);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief External interface for finish
|
||||
*/
|
||||
void do_finish ()
|
||||
{
|
||||
finish (mp_netlist);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Sets the netlist
|
||||
*/
|
||||
void set_netlist (db::Netlist *netlist)
|
||||
{
|
||||
mp_netlist = netlist;
|
||||
}
|
||||
|
||||
private:
|
||||
db::Netlist *mp_netlist;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -30,6 +30,20 @@ namespace db
|
|||
|
||||
// ------------------------------------------------------------------------------------------------------
|
||||
|
||||
static bool to_bool (const tl::Variant &v)
|
||||
{
|
||||
if (v.is_bool ()) {
|
||||
return v.to_bool ();
|
||||
} else if (v.is_nil ()) {
|
||||
return false;
|
||||
} else if (v.can_convert_to_double ()) {
|
||||
return v.to_double () != 0.0;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
// ------------------------------------------------------------------------------------------------------
|
||||
|
||||
NetlistSpiceReaderExpressionParser::NetlistSpiceReaderExpressionParser (const variables_type *vars)
|
||||
{
|
||||
static variables_type empty_variables;
|
||||
|
|
@ -40,55 +54,55 @@ NetlistSpiceReaderExpressionParser::NetlistSpiceReaderExpressionParser (const va
|
|||
// https://nmg.gitlab.io/ngspice-manual/circuitdescription/paramparametricnetlists/syntaxofexpressions.html
|
||||
|
||||
static double sqrt_f (double v) { return sqrt (v); }
|
||||
static double sin_f (double v) { return sin (v); }
|
||||
static double cos_f (double v) { return cos (v); }
|
||||
static double tan_f (double v) { return tan (v); }
|
||||
static double sinh_f (double v) { return sinh (v); }
|
||||
static double cosh_f (double v) { return cosh (v); }
|
||||
static double tanh_f (double v) { return tanh (v); }
|
||||
static double asin_f (double v) { return asin (v); }
|
||||
static double acos_f (double v) { return acos (v); }
|
||||
static double atan_f (double v) { return atan (v); }
|
||||
static double asinh_f (double v) { return asinh (v); }
|
||||
static double acosh_f (double v) { return acosh (v); }
|
||||
static double atanh_f (double v) { return atanh (v); }
|
||||
static double exp_f (double v) { return exp (v); }
|
||||
static double ln_f (double v) { return log (v); }
|
||||
static double log_f (double v) { return log10 (v); }
|
||||
static double abs_f (double v) { return abs (v); }
|
||||
static double nint_f (double v) { return round (v); }
|
||||
static double floor_f (double v) { return floor (v); }
|
||||
static double ceil_f (double v) { return ceil (v); }
|
||||
static double sgn_f (double v) { return v == 0.0 ? 0.0 : (v < 0.0 ? -1.0 : 1.0); }
|
||||
static double int_f (double v) { return sgn_f (v) * floor (sgn_f (v) * v); }
|
||||
static double sin_f (double v) { return sin (v); }
|
||||
static double cos_f (double v) { return cos (v); }
|
||||
static double tan_f (double v) { return tan (v); }
|
||||
static double sinh_f (double v) { return sinh (v); }
|
||||
static double cosh_f (double v) { return cosh (v); }
|
||||
static double tanh_f (double v) { return tanh (v); }
|
||||
static double asin_f (double v) { return asin (v); }
|
||||
static double acos_f (double v) { return acos (v); }
|
||||
static double atan_f (double v) { return atan (v); }
|
||||
static double asinh_f (double v) { return asinh (v); }
|
||||
static double acosh_f (double v) { return acosh (v); }
|
||||
static double atanh_f (double v) { return atanh (v); }
|
||||
static double exp_f (double v) { return exp (v); }
|
||||
static double ln_f (double v) { return log (v); }
|
||||
static double log_f (double v) { return log10 (v); }
|
||||
static double abs_f (double v) { return abs (v); }
|
||||
static double nint_f (double v) { return nearbyint (v); } // we basically should we the rounding mode before ...
|
||||
static double floor_f (double v) { return floor (v); }
|
||||
static double ceil_f (double v) { return ceil (v); }
|
||||
static double sgn_f (double v) { return v == 0.0 ? 0.0 : (v < 0.0 ? -1.0 : 1.0); }
|
||||
static double int_f (double v) { return sgn_f (v) * floor (sgn_f (v) * v); }
|
||||
|
||||
tl::Variant
|
||||
NetlistSpiceReaderExpressionParser::eval_func (const std::string &name, const std::vector<tl::Variant> ¶ms, bool * /*status*/) const
|
||||
{
|
||||
double (*f) (double) = 0;
|
||||
|
||||
if (name == "sqrt") { f = sqrt_f; } else
|
||||
if (name == "sin") { f = sin_f; } else
|
||||
if (name == "cos") { f = cos_f; } else
|
||||
if (name == "tan") { f = tan_f; } else
|
||||
if (name == "sinh") { f = sinh_f; } else
|
||||
if (name == "cosh") { f = cosh_f; } else
|
||||
if (name == "tanh") { f = tanh_f; } else
|
||||
if (name == "asin") { f = asin_f; } else
|
||||
if (name == "acos") { f = acos_f; } else
|
||||
if (name == "atan" || name == "arctan") { f = atan_f; } else
|
||||
if (name == "asinh") { f = asinh_f; } else
|
||||
if (name == "acosh") { f = acosh_f; } else
|
||||
if (name == "atanh") { f = atanh_f; } else
|
||||
if (name == "exp") { f = exp_f; } else
|
||||
if (name == "ln") { f = ln_f; } else
|
||||
if (name == "log") { f = log_f; } else
|
||||
if (name == "abs") { f = abs_f; } else
|
||||
if (name == "nint") { f = nint_f; } else
|
||||
if (name == "floor") { f = floor_f; } else
|
||||
if (name == "ceil") { f = ceil_f; } else
|
||||
if (name == "sgn") { f = sgn_f; } else
|
||||
if (name == "int") { f = int_f; }
|
||||
if (name == "SQRT") { f = sqrt_f; } else
|
||||
if (name == "SIN") { f = sin_f; } else
|
||||
if (name == "COS") { f = cos_f; } else
|
||||
if (name == "TAN") { f = tan_f; } else
|
||||
if (name == "SINH") { f = sinh_f; } else
|
||||
if (name == "COSH") { f = cosh_f; } else
|
||||
if (name == "TANH") { f = tanh_f; } else
|
||||
if (name == "ASIN") { f = asin_f; } else
|
||||
if (name == "ACOS") { f = acos_f; } else
|
||||
if (name == "ATAN" || name == "arctan") { f = atan_f; } else
|
||||
if (name == "ASINH") { f = asinh_f; } else
|
||||
if (name == "ACOSH") { f = acosh_f; } else
|
||||
if (name == "ATANH") { f = atanh_f; } else
|
||||
if (name == "EXP") { f = exp_f; } else
|
||||
if (name == "LN") { f = ln_f; } else
|
||||
if (name == "LOG") { f = log_f; } else
|
||||
if (name == "ABS") { f = abs_f; } else
|
||||
if (name == "NINT") { f = nint_f; } else
|
||||
if (name == "FLOOR") { f = floor_f; } else
|
||||
if (name == "CEIL") { f = ceil_f; } else
|
||||
if (name == "SGN") { f = sgn_f; } else
|
||||
if (name == "INT") { f = int_f; }
|
||||
|
||||
if (f != 0) {
|
||||
|
||||
|
|
@ -98,7 +112,7 @@ NetlistSpiceReaderExpressionParser::eval_func (const std::string &name, const st
|
|||
return tl::Variant ((*f) (params.front ().to_double ()));
|
||||
}
|
||||
|
||||
} else if (name == "pwr" || name == "pow") {
|
||||
} else if (name == "PWR" || name == "POW") {
|
||||
|
||||
if (params.size () < 2 || ! params [0].can_convert_to_double () || ! params [1].can_convert_to_double ()) {
|
||||
return tl::Variant ();
|
||||
|
|
@ -106,15 +120,15 @@ NetlistSpiceReaderExpressionParser::eval_func (const std::string &name, const st
|
|||
return tl::Variant (pow (params [0].to_double (), params [1].to_double ()));
|
||||
}
|
||||
|
||||
} else if (name == "ternary_fcn") {
|
||||
} else if (name == "TERNERY_FCN") {
|
||||
|
||||
if (params.size () < 3) {
|
||||
return tl::Variant ();
|
||||
} else {
|
||||
return params [0].to_bool () ? params [1] : params [2];
|
||||
return to_bool (params [0]) ? params [1] : params [2];
|
||||
}
|
||||
|
||||
} else if (name == "min") {
|
||||
} else if (name == "MIN") {
|
||||
|
||||
if (params.size () < 1) {
|
||||
return tl::Variant ();
|
||||
|
|
@ -128,7 +142,7 @@ NetlistSpiceReaderExpressionParser::eval_func (const std::string &name, const st
|
|||
}
|
||||
return v;
|
||||
|
||||
} else if (name == "max") {
|
||||
} else if (name == "MAX") {
|
||||
|
||||
if (params.size () < 1) {
|
||||
return tl::Variant ();
|
||||
|
|
@ -167,7 +181,7 @@ NetlistSpiceReaderExpressionParser::read_atomic_value (tl::Extractor &ex, bool *
|
|||
} else if (ex.test ("!")) {
|
||||
|
||||
tl::Variant v = read_atomic_value (ex, status);
|
||||
return tl::Variant (! v.to_bool ());
|
||||
return tl::Variant (! to_bool (v));
|
||||
|
||||
} else if (ex.test ("(")) {
|
||||
|
||||
|
|
@ -220,6 +234,8 @@ NetlistSpiceReaderExpressionParser::read_atomic_value (tl::Extractor &ex, bool *
|
|||
|
||||
} else if (ex.try_read_word (var)) {
|
||||
|
||||
var = tl::to_upper_case (var);
|
||||
|
||||
if (ex.test ("(")) {
|
||||
|
||||
// a function
|
||||
|
|
@ -247,7 +263,7 @@ NetlistSpiceReaderExpressionParser::read_atomic_value (tl::Extractor &ex, bool *
|
|||
|
||||
} else {
|
||||
|
||||
auto vi = mp_variables->find (tl::to_upper_case (var));
|
||||
auto vi = mp_variables->find (var);
|
||||
if (vi != mp_variables->end ()) {
|
||||
return vi->second;
|
||||
} else {
|
||||
|
|
@ -434,13 +450,13 @@ tl::Variant NetlistSpiceReaderExpressionParser::read_logical_op (tl::Extractor &
|
|||
if (status && !*status) {
|
||||
return tl::Variant ();
|
||||
}
|
||||
v = tl::Variant (v.to_bool () && vv.to_bool ());
|
||||
v = tl::Variant (to_bool (v) && to_bool (vv));
|
||||
} else if (ex.test ("||")) {
|
||||
tl::Variant vv = read_compare_expr (ex, status);
|
||||
if (status && !*status) {
|
||||
return tl::Variant ();
|
||||
}
|
||||
v = tl::Variant (v.to_bool () && vv.to_bool ());
|
||||
v = tl::Variant (to_bool (v) || to_bool (vv));
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
|
|
@ -470,7 +486,7 @@ tl::Variant NetlistSpiceReaderExpressionParser::read_ternary_op (tl::Extractor &
|
|||
if (status && !*status) {
|
||||
return tl::Variant ();
|
||||
}
|
||||
v = v.to_bool () ? vv1 : vv2;
|
||||
v = to_bool (v) ? vv1 : vv2;
|
||||
}
|
||||
|
||||
return v;
|
||||
|
|
@ -494,6 +510,12 @@ static const char *start_quote (tl::Extractor &ex)
|
|||
}
|
||||
}
|
||||
|
||||
tl::Variant NetlistSpiceReaderExpressionParser::read (const std::string &s) const
|
||||
{
|
||||
tl::Extractor ex (s.c_str ());
|
||||
return read (ex);
|
||||
}
|
||||
|
||||
tl::Variant NetlistSpiceReaderExpressionParser::read (tl::Extractor &ex) const
|
||||
{
|
||||
try {
|
||||
|
|
@ -509,10 +531,17 @@ tl::Variant NetlistSpiceReaderExpressionParser::read (tl::Extractor &ex) const
|
|||
return res;
|
||||
|
||||
} catch (tl::Exception &error) {
|
||||
throw NetlistSpiceReaderDelegateError (error.msg ());
|
||||
// recast the exception, so we can process it later
|
||||
throw NetlistSpiceReaderError (error.msg ());
|
||||
}
|
||||
}
|
||||
|
||||
bool NetlistSpiceReaderExpressionParser::try_read (const std::string &s, tl::Variant &value) const
|
||||
{
|
||||
tl::Extractor ex (s.c_str ());
|
||||
return try_read (ex, value);
|
||||
}
|
||||
|
||||
bool NetlistSpiceReaderExpressionParser::try_read (tl::Extractor &ex, tl::Variant &value) const
|
||||
{
|
||||
tl::Extractor ex_saved = ex;
|
||||
|
|
|
|||
|
|
@ -48,7 +48,9 @@ public:
|
|||
NetlistSpiceReaderExpressionParser (const variables_type *vars);
|
||||
|
||||
tl::Variant read (tl::Extractor &ex) const;
|
||||
tl::Variant read (const std::string &s) const;
|
||||
bool try_read (tl::Extractor &ex, tl::Variant &v) const;
|
||||
bool try_read (const std::string &s, tl::Variant &v) const;
|
||||
|
||||
private:
|
||||
const variables_type *mp_variables;
|
||||
|
|
|
|||
|
|
@ -2688,10 +2688,10 @@ static tl::Variant value_from_string (NetlistSpiceReaderDelegateImpl * /*delegat
|
|||
return res;
|
||||
}
|
||||
|
||||
static ParseElementComponentsData parse_element_components (NetlistSpiceReaderDelegateImpl * /*delegate*/, const std::string &s, const db::NetlistSpiceReader::parameters_type &variables)
|
||||
static ParseElementComponentsData parse_element_components (NetlistSpiceReaderDelegateImpl *delegate, const std::string &s, const db::NetlistSpiceReader::parameters_type &variables)
|
||||
{
|
||||
ParseElementComponentsData data;
|
||||
db::NetlistSpiceReaderDelegate::parse_element_components (s, data.strings_nc (), data.parameters_nc (), variables);
|
||||
delegate->parse_element_components (s, data.strings_nc (), data.parameters_nc (), variables);
|
||||
return data;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@
|
|||
|
||||
#include "dbNetlistSpiceReader.h"
|
||||
#include "dbNetlistSpiceReaderDelegate.h"
|
||||
#include "dbNetlistSpiceReaderExpressionParser.h"
|
||||
#include "dbNetlist.h"
|
||||
#include "dbNetlistDeviceClasses.h"
|
||||
|
||||
|
|
@ -176,11 +177,23 @@ TEST(4_ReaderWithUnconnectedPins)
|
|||
|
||||
TEST(5_CircuitParameters)
|
||||
{
|
||||
db::Netlist nl;
|
||||
|
||||
std::string path = tl::combine_path (tl::combine_path (tl::testdata (), "algo"), "nreader5.cir");
|
||||
|
||||
db::NetlistSpiceReader reader;
|
||||
reader.set_strict (true);
|
||||
|
||||
try {
|
||||
db::Netlist nl;
|
||||
tl::InputStream is (path);
|
||||
reader.read (is, nl);
|
||||
// strict mode makes this sample fail
|
||||
EXPECT_EQ (true, false);
|
||||
} catch (...) {
|
||||
// ..
|
||||
}
|
||||
|
||||
db::Netlist nl;
|
||||
reader.set_strict (false);
|
||||
tl::InputStream is (path);
|
||||
reader.read (is, nl);
|
||||
|
||||
|
|
@ -620,3 +633,186 @@ TEST(16_issue898)
|
|||
"end;\n"
|
||||
);
|
||||
}
|
||||
|
||||
TEST(17_RecursiveExpansion)
|
||||
{
|
||||
db::Netlist nl;
|
||||
|
||||
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);
|
||||
|
||||
EXPECT_EQ (nl.to_string (),
|
||||
"circuit .TOP ();\n"
|
||||
" subcircuit 'SUB1(L=0.15,W=1.5)' SUB1A (N1=A,N2=B,N3=C);\n"
|
||||
" subcircuit 'SUB1(L=0.25,W=3)' SUB1B (N1=A,N2=B,N3=C);\n"
|
||||
"end;\n"
|
||||
"circuit 'SUB1(L=0.15,W=1.5)' (N1=N1,N2=N2,N3=N3);\n"
|
||||
" subcircuit 'SUB2(L=0.15,M=1,W=1.5)' SUB2A (N1=N1,N2=N2,N3=N3);\n"
|
||||
" subcircuit 'SUB2(L=0.15,M=2,W=1.5)' SUB2B (N1=N1,N2=N2,N3=N3);\n"
|
||||
"end;\n"
|
||||
"circuit 'SUB2(L=0.15,M=1,W=1.5)' (N1=N1,N2=N2,N3=N3);\n"
|
||||
" device NMOS NMOS (S=N1,G=N2,D=N3,B=N1) (L=150000,W=1500000,AS=0,AD=0,PS=0,PD=0);\n"
|
||||
"end;\n"
|
||||
"circuit 'SUB2(L=0.15,M=2,W=1.5)' (N1=N1,N2=N2,N3=N3);\n"
|
||||
" device NMOS NMOS (S=N1,G=N2,D=N3,B=N1) (L=150000,W=3000000,AS=0,AD=0,PS=0,PD=0);\n"
|
||||
"end;\n"
|
||||
"circuit 'SUB1(L=0.25,W=3)' (N1=N1,N2=N2,N3=N3);\n"
|
||||
" subcircuit 'SUB2(L=0.25,M=1,W=3)' SUB2A (N1=N1,N2=N2,N3=N3);\n"
|
||||
" subcircuit 'SUB2(L=0.25,M=2,W=3)' SUB2B (N1=N1,N2=N2,N3=N3);\n"
|
||||
"end;\n"
|
||||
"circuit 'SUB2(L=0.25,M=1,W=3)' (N1=N1,N2=N2,N3=N3);\n"
|
||||
" device NMOS NMOS (S=N1,G=N2,D=N3,B=N1) (L=250000,W=3000000,AS=0,AD=0,PS=0,PD=0);\n"
|
||||
"end;\n"
|
||||
"circuit 'SUB2(L=0.25,M=2,W=3)' (N1=N1,N2=N2,N3=N3);\n"
|
||||
" 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"
|
||||
);
|
||||
}
|
||||
|
||||
TEST(18_XSchemOutput)
|
||||
{
|
||||
db::Netlist nl;
|
||||
|
||||
std::string path = tl::combine_path (tl::combine_path (tl::testdata (), "algo"), "nreader18.cir");
|
||||
|
||||
db::NetlistSpiceReader reader;
|
||||
tl::InputStream is (path);
|
||||
reader.read (is, nl);
|
||||
|
||||
EXPECT_EQ (nl.to_string (),
|
||||
"circuit .TOP ();\n"
|
||||
" subcircuit 'PMOS4_STANDARD(L=0.15U,NF=4,W=1.5U)' XPMOS (D=Q,G=I,S=VDD,B=VDD);\n"
|
||||
" subcircuit 'NMOS4_STANDARD(L=0.15U,NF=4,W=1.5U)' XNMOS (D=Q,G=I,S=VSS,B=VSS);\n"
|
||||
" subcircuit 'NMOS4_STANDARD(L=0.15U,NF=2,W=1.5U)' XDUMMY0 (D=VSS,G=VSS,S=VSS,B=VSS);\n"
|
||||
" subcircuit 'NMOS4_STANDARD(L=0.15U,NF=2,W=1.5U)' XDUMMY1 (D=VSS,G=VSS,S=VSS,B=VSS);\n"
|
||||
" subcircuit 'PMOS4_STANDARD(L=0.15U,NF=2,W=1.5U)' XDUMMY2 (D=VDD,G=VDD,S=VDD,B=VDD);\n"
|
||||
" subcircuit 'PMOS4_STANDARD(L=0.15U,NF=2,W=1.5U)' XDUMMY3 (D=VDD,G=VDD,S=VDD,B=VDD);\n"
|
||||
"end;\n"
|
||||
"circuit 'PMOS4_STANDARD(L=0.15U,NF=4,W=1.5U)' (D=D,G=G,S=S,B=B);\n"
|
||||
" device SKY130_FD_PR__PFET_01V8 M1 (S=D,G=G,D=S,B=B) (L=0.15,W=6,AS=0.32625,AD=0.2175,PS=2.685,PD=1.79);\n"
|
||||
"end;\n"
|
||||
"circuit 'NMOS4_STANDARD(L=0.15U,NF=4,W=1.5U)' (D=D,G=G,S=S,B=B);\n"
|
||||
" device SKY130_FD_PR__NFET_01V8 M1 (S=D,G=G,D=S,B=B) (L=0.15,W=6,AS=0.32625,AD=0.2175,PS=2.685,PD=1.79);\n"
|
||||
"end;\n"
|
||||
"circuit 'NMOS4_STANDARD(L=0.15U,NF=2,W=1.5U)' (D=D,G=G,S=S,B=B);\n"
|
||||
" device SKY130_FD_PR__NFET_01V8 M1 (S=D,G=G,D=S,B=B) (L=0.15,W=3,AS=0.435,AD=0.2175,PS=3.58,PD=1.79);\n"
|
||||
"end;\n"
|
||||
"circuit 'PMOS4_STANDARD(L=0.15U,NF=2,W=1.5U)' (D=D,G=G,S=S,B=B);\n"
|
||||
" device SKY130_FD_PR__PFET_01V8 M1 (S=D,G=G,D=S,B=B) (L=0.15,W=3,AS=0.435,AD=0.2175,PS=3.58,PD=1.79);\n"
|
||||
"end;\n"
|
||||
);
|
||||
}
|
||||
|
||||
TEST(100_ExpressionParser)
|
||||
{
|
||||
std::map<std::string, tl::Variant> vars;
|
||||
vars["A"] = 17.5;
|
||||
vars["B"] = 42;
|
||||
vars["S"] = "string";
|
||||
|
||||
tl::Variant v;
|
||||
|
||||
db::NetlistSpiceReaderExpressionParser parser (&vars);
|
||||
|
||||
EXPECT_EQ (parser.read ("1.75").to_string (), "1.75");
|
||||
EXPECT_EQ (parser.read ("-1.75").to_string (), "-1.75");
|
||||
EXPECT_EQ (parser.read ("-a*0.1").to_string (), "-1.75");
|
||||
EXPECT_EQ (parser.read ("-A*0.1").to_string (), "-1.75");
|
||||
EXPECT_EQ (parser.read ("b/6").to_string (), "7");
|
||||
EXPECT_EQ (parser.read ("B/6").to_string (), "7");
|
||||
EXPECT_EQ (parser.read ("s").to_string (), "string");
|
||||
EXPECT_EQ (parser.read ("S").to_string (), "string");
|
||||
EXPECT_EQ (parser.read ("!0").to_string (), "true");
|
||||
EXPECT_EQ (parser.read ("!1").to_string (), "false");
|
||||
EXPECT_EQ (parser.read ("4*2+1").to_string (), "9");
|
||||
EXPECT_EQ (parser.read ("4*2-1").to_string (), "7");
|
||||
EXPECT_EQ (parser.read ("4/2-1").to_string (), "1");
|
||||
EXPECT_EQ (parser.read ("4%2-1").to_string (), "-1");
|
||||
EXPECT_EQ (parser.read ("5%2-1").to_string (), "0");
|
||||
EXPECT_EQ (parser.read ("2**2*2+1").to_string (), "9");
|
||||
EXPECT_EQ (parser.read ("2**2*(2+1)").to_string (), "12");
|
||||
EXPECT_EQ (parser.read ("pow(2,2)*(2+1)").to_string (), "12");
|
||||
EXPECT_EQ (parser.read ("POW(2,2)*(2+1)").to_string (), "12");
|
||||
EXPECT_EQ (parser.read ("pwr(2,2)*(2+1)").to_string (), "12");
|
||||
EXPECT_EQ (parser.read ("PWR(2,2)*(2+1)").to_string (), "12");
|
||||
EXPECT_EQ (parser.read ("3==2+1").to_string (), "true");
|
||||
EXPECT_EQ (parser.read ("4==2+1").to_string (), "false");
|
||||
EXPECT_EQ (parser.read ("3!=2+1").to_string (), "false");
|
||||
EXPECT_EQ (parser.read ("4!=2+1").to_string (), "true");
|
||||
EXPECT_EQ (parser.read ("2<2+1").to_string (), "true");
|
||||
EXPECT_EQ (parser.read ("3<2+1").to_string (), "false");
|
||||
EXPECT_EQ (parser.read ("4<2+1").to_string (), "false");
|
||||
EXPECT_EQ (parser.read ("2<=2+1").to_string (), "true");
|
||||
EXPECT_EQ (parser.read ("3<=2+1").to_string (), "true");
|
||||
EXPECT_EQ (parser.read ("4<=2+1").to_string (), "false");
|
||||
EXPECT_EQ (parser.read ("2>2+1").to_string (), "false");
|
||||
EXPECT_EQ (parser.read ("3>2+1").to_string (), "false");
|
||||
EXPECT_EQ (parser.read ("4>2+1").to_string (), "true");
|
||||
EXPECT_EQ (parser.read ("2>=2+1").to_string (), "false");
|
||||
EXPECT_EQ (parser.read ("3>=2+1").to_string (), "true");
|
||||
EXPECT_EQ (parser.read ("4>=2+1").to_string (), "true");
|
||||
EXPECT_EQ (parser.read ("1==2||2==2").to_string (), "true");
|
||||
EXPECT_EQ (parser.read ("1==2||3==2").to_string (), "false");
|
||||
EXPECT_EQ (parser.read ("1==2&&2==2").to_string (), "false");
|
||||
EXPECT_EQ (parser.read ("1==1&&2==2").to_string (), "true");
|
||||
EXPECT_EQ (parser.read ("1==2?2:3").to_string (), "3");
|
||||
EXPECT_EQ (parser.read ("ternery_fcn(1==2,2,3)").to_string (), "3");
|
||||
EXPECT_EQ (parser.read ("1==1?2:3").to_string (), "2");
|
||||
EXPECT_EQ (parser.read ("ternery_fcn(1==1,2,3)").to_string (), "2");
|
||||
|
||||
EXPECT_EQ (parser.read ("sin(0)").to_string (), "0");
|
||||
EXPECT_EQ (parser.read ("sin(atan(1.0)*2)").to_string (), "1");
|
||||
EXPECT_EQ (parser.read ("cos(0)").to_string (), "1");
|
||||
EXPECT_EQ (parser.read ("cos(atan(1.0)*2)").to_string (), "0");
|
||||
EXPECT_EQ (parser.read ("tan(0)").to_string (), "0");
|
||||
EXPECT_EQ (parser.read ("tan(atan(1.0))").to_string (), "1");
|
||||
EXPECT_EQ (parser.read ("sin(asin(0.5))").to_string (), "0.5");
|
||||
EXPECT_EQ (parser.read ("cos(acos(0.5))").to_string (), "0.5");
|
||||
EXPECT_EQ (parser.read ("ln(exp(0.5))").to_string (), "0.5");
|
||||
EXPECT_EQ (parser.read ("exp(0.0)").to_string (), "1");
|
||||
EXPECT_EQ (parser.read ("log(10**0.5)").to_string (), "0.5");
|
||||
EXPECT_EQ (parser.read ("int(-0.5)").to_string (), "0");
|
||||
EXPECT_EQ (parser.read ("int(-1.5)").to_string (), "-1");
|
||||
EXPECT_EQ (parser.read ("int(0.5)").to_string (), "0");
|
||||
EXPECT_EQ (parser.read ("int(1.5)").to_string (), "1");
|
||||
EXPECT_EQ (parser.read ("floor(-0.5)").to_string (), "-1");
|
||||
EXPECT_EQ (parser.read ("floor(-1.5)").to_string (), "-2");
|
||||
EXPECT_EQ (parser.read ("floor(0.5)").to_string (), "0");
|
||||
EXPECT_EQ (parser.read ("floor(1.5)").to_string (), "1");
|
||||
EXPECT_EQ (parser.read ("ceil(-0.5)").to_string (), "0");
|
||||
EXPECT_EQ (parser.read ("ceil(-1.5)").to_string (), "-1");
|
||||
EXPECT_EQ (parser.read ("ceil(0.5)").to_string (), "1");
|
||||
EXPECT_EQ (parser.read ("ceil(1.5)").to_string (), "2");
|
||||
EXPECT_EQ (parser.read ("nint(-0.5)").to_string (), "0");
|
||||
EXPECT_EQ (parser.read ("nint(-1.5)").to_string (), "-2");
|
||||
EXPECT_EQ (parser.read ("nint(0.5)").to_string (), "0");
|
||||
EXPECT_EQ (parser.read ("nint(1.5)").to_string (), "2");
|
||||
EXPECT_EQ (parser.read ("min(4,1,3)").to_string (), "1");
|
||||
EXPECT_EQ (parser.read ("min(4,3)").to_string (), "3");
|
||||
EXPECT_EQ (parser.read ("min(4)").to_string (), "4");
|
||||
EXPECT_EQ (parser.read ("max(1,4,3)").to_string (), "4");
|
||||
EXPECT_EQ (parser.read ("max(4,3)").to_string (), "4");
|
||||
EXPECT_EQ (parser.read ("max(4)").to_string (), "4");
|
||||
EXPECT_EQ (parser.read ("max(a,b)").to_string (), "42");
|
||||
|
||||
EXPECT_EQ (parser.try_read ("a syntax error", v), false);
|
||||
v = tl::Variant ();
|
||||
EXPECT_EQ (parser.try_read ("1+2*(2+1)-1", v), true);
|
||||
EXPECT_EQ (v.to_string (), "6");
|
||||
EXPECT_EQ (parser.try_read ("{1+2*(2+1)-1)", v), false);
|
||||
EXPECT_EQ (parser.try_read ("'1+2*(2+1)-1)", v), false);
|
||||
EXPECT_EQ (parser.try_read ("\"1+2*(2+1)-1)", v), false);
|
||||
EXPECT_EQ (parser.try_read ("\"1+2*(2+1)-1'", v), false);
|
||||
v = tl::Variant ();
|
||||
EXPECT_EQ (parser.try_read ("{1+2*(2+1)-1}", v), true);
|
||||
EXPECT_EQ (v.to_string (), "6");
|
||||
v = tl::Variant ();
|
||||
EXPECT_EQ (parser.try_read ("'1+2*(2+1)-1'", v), true);
|
||||
EXPECT_EQ (v.to_string (), "6");
|
||||
v = tl::Variant ();
|
||||
EXPECT_EQ (parser.try_read ("\"1+2*(2+1)-1\"", v), true);
|
||||
EXPECT_EQ (v.to_string (), "6");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,15 @@
|
|||
|
||||
* recursive expansion of parametrized subcircuits
|
||||
|
||||
Xsub1a a b c sub1 w=1.5 l=0.15
|
||||
Xsub1b a b c sub1 w=3.0 l=0.25
|
||||
|
||||
.subckt sub1 n1 n2 n3 w=1.0 l=0.5
|
||||
Xsub2a n1 n2 n3 sub2 w l m=1
|
||||
Xsub2b n1 n2 n3 sub2 w l 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
|
||||
|
||||
|
|
@ -0,0 +1,59 @@
|
|||
** sch_path:
|
||||
*+ /home/matthias/dev/bag/mosaic_bag/opensource_db_template/inverter1_generated_sky130_fd_pr/inverter1/inverter1.sch
|
||||
**.subckt inverter1 I Q VDD VSS
|
||||
*.ipin I
|
||||
*.opin Q
|
||||
*.iopin VDD
|
||||
*.iopin VSS
|
||||
XXpmos Q I VDD VDD pmos4_standard w=1.5u l=150n nf=4
|
||||
XXnmos Q I VSS VSS nmos4_standard w=1.5u l=150n nf=4
|
||||
XXDUMMY0 VSS VSS VSS VSS nmos4_standard w=1.5u l=150n nf=2
|
||||
XXDUMMY1 VSS VSS VSS VSS nmos4_standard w=1.5u l=150n nf=2
|
||||
XXDUMMY2 VDD VDD VDD VDD pmos4_standard w=1.5u l=150n nf=2
|
||||
XXDUMMY3 VDD VDD VDD VDD pmos4_standard w=1.5u l=150n nf=2
|
||||
**.ends
|
||||
|
||||
* expanding symbol: BAG_prim/pmos4_standard/pmos4_standard.sym # of pins=4
|
||||
** sym_path:
|
||||
*+ /home/matthias/dev/bag/mosaic_bag/opensource_db_template/BAG2_technology_definition/BAG_prim/pmos4_standard/pmos4_standard.sym
|
||||
** sch_path:
|
||||
*+ /home/matthias/dev/bag/mosaic_bag/opensource_db_template/BAG2_technology_definition/BAG_prim/pmos4_standard/pmos4_standard.sch
|
||||
.subckt pmos4_standard D G S B model w l nf
|
||||
w=100n
|
||||
l=18n
|
||||
nf=4
|
||||
model=pmos4_standard
|
||||
spiceprefix=X
|
||||
|
||||
*.iopin D
|
||||
*.iopin G
|
||||
*.iopin S
|
||||
*.iopin B
|
||||
MM1 D G S B sky130_fd_pr__pfet_01v8 L=l W=w ad='int((nf+1)/2) * W/nf * 0.29u' as='int((nf+2)/2) * W/nf * 0.29u'
|
||||
+ pd='2*int((nf+1)/2) * (W + 0.29u)/nf' ps='2*int((nf+2)/2) * (W + 0.29u)/nf' nrd='0.29u / W' nrs='0.29u / W'
|
||||
+ sa=0 sb=0 sd=0 mult=1 m=nf
|
||||
.ends
|
||||
|
||||
|
||||
* expanding symbol: BAG_prim/nmos4_standard/nmos4_standard.sym # of pins=4
|
||||
** sym_path:
|
||||
*+ /home/matthias/dev/bag/mosaic_bag/opensource_db_template/BAG2_technology_definition/BAG_prim/nmos4_standard/nmos4_standard.sym
|
||||
** sch_path:
|
||||
*+ /home/matthias/dev/bag/mosaic_bag/opensource_db_template/BAG2_technology_definition/BAG_prim/nmos4_standard/nmos4_standard.sch
|
||||
.subckt nmos4_standard D G S B model w l nf
|
||||
w=100n
|
||||
l=18n
|
||||
nf=4
|
||||
model=nmos4_standard
|
||||
spiceprefix=X
|
||||
|
||||
*.iopin D
|
||||
*.iopin G
|
||||
*.iopin S
|
||||
*.iopin B
|
||||
MM1 D G S B sky130_fd_pr__nfet_01v8 L=l W=w ad='int((nf+1)/2) * W/nf * 0.29u' as='int((nf+2)/2) * W/nf * 0.29u'
|
||||
+ pd='2*int((nf+1)/2) * (W + 0.29u)/nf' ps='2*int((nf+2)/2) * (W + 0.29u)/nf' nrd='0.29u / W' nrs='0.29u / W'
|
||||
+ sa=0 sb=0 sd=0 mult=1 m=nf
|
||||
.ends
|
||||
|
||||
.end
|
||||
Loading…
Reference in New Issue