Small enhancements for Spice reader

- Detects recursive subcircuit calls now
- Dismisses empty top level circuit which happened to be created
  when there were not top level elements and control statements
  were present (such as .param)
This commit is contained in:
Matthias Koefferlein 2024-04-18 21:27:09 +02:00
parent e8ecaa6472
commit cba126e9ee
6 changed files with 85 additions and 1 deletions

View File

@ -644,6 +644,11 @@ void Circuit::set_pin_ref_for_pin (size_t pin_id, Net::pin_iterator iter)
m_pin_refs [pin_id] = iter;
}
bool Circuit::is_empty () const
{
return m_nets.empty () && m_pins.empty () && m_devices.empty () && m_subcircuits.empty ();
}
void Circuit::blank ()
{
tl_assert (netlist () != 0);

View File

@ -759,6 +759,11 @@ public:
*/
void blank ();
/**
* @brief Gets a value indicating whether the circuit is empty
*/
bool is_empty () const;
/**
* @brief Generate memory statistics
*/

View File

@ -943,6 +943,12 @@ SpiceNetlistBuilder::circuit_for (const SpiceCachedCircuit *cc, const parameters
if (cp == c->second.end ()) {
return 0;
}
// a null pointer indicates that we are currently defining this circuit
if (cp->second == 0) {
error (tl::sprintf (tl::to_string (tr ("Subcircuit '%s' called recursively")), cc->name ()));
}
return cp->second;
}
@ -1055,7 +1061,8 @@ SpiceNetlistBuilder::build_circuit (const SpiceCachedCircuit *cc, const paramete
c->set_name (make_circuit_name (cc->name (), pv));
}
register_circuit_for (cc, pv, c, anonymous_top_level);
// pre-register the circuit - allows detecting recursive calls
register_circuit_for (cc, pv, 0, false);
std::unique_ptr<std::map<std::string, db::Net *> > n2n (mp_nets_by_name.release ());
mp_nets_by_name.reset (0);
@ -1095,6 +1102,14 @@ SpiceNetlistBuilder::build_circuit (const SpiceCachedCircuit *cc, const paramete
std::swap (c, mp_netlist_circuit);
std::swap (vars, m_variables);
// final registration if required
if (! anonymous_top_level || ! c->is_empty ()) {
register_circuit_for (cc, pv, c, anonymous_top_level);
} else {
mp_netlist->remove_circuit (c);
c = 0;
}
return c;
}

View File

@ -880,6 +880,40 @@ TEST(23_endl)
);
}
TEST(24_recursive_calls)
{
db::Netlist nl;
std::string path = tl::combine_path (tl::combine_path (tl::testdata (), "algo"), "nreader24.cir");
db::NetlistSpiceReader reader;
tl::InputStream is (path);
try {
reader.read (is, nl);
EXPECT_EQ (false, true);
} catch (tl::Exception &ex) {
EXPECT_EQ (ex.msg (), "Subcircuit 'C1' called recursively in /home/matthias/klayout/master/testdata/algo/nreader24.cir, line 8");
}
}
TEST(25_dismiss_top_level)
{
db::Netlist nl;
std::string path = tl::combine_path (tl::combine_path (tl::testdata (), "algo"), "nreader25.cir");
db::NetlistSpiceReader reader;
tl::InputStream is (path);
reader.read (is, nl);
EXPECT_EQ (nl.to_string (),
"circuit TOP (A=A,B=B);\n"
" device NMOS '1' (S=A,G=B,D=A,B=B) (L=100,W=100,AS=0,AD=0,PS=0,PD=0);\n"
"end;\n"
);
}
TEST(100_ExpressionParser)
{
std::map<std::string, tl::Variant> vars;

14
testdata/algo/nreader24.cir vendored Normal file
View File

@ -0,0 +1,14 @@
* Testing recursive call detection
.subckt c1 a b
x1 a b c2
.end
.subckt c2 a b
x1 a b c1
.ends
xtop vdd vss c2
.end

11
testdata/algo/nreader25.cir vendored Normal file
View File

@ -0,0 +1,11 @@
* Test: dismiss empty top level circuit
.subckt top a b
m1 a b a b nmos
.ends
* this triggered generation of a top level circuit
.param p1 17
.end