Bug fixes

This commit is contained in:
Matthias Koefferlein 2026-06-13 16:51:52 +02:00
parent de052422d8
commit 36f147bcc1
3 changed files with 172 additions and 65 deletions

View File

@ -230,6 +230,7 @@ DeviceClass &DeviceClass::operator= (const DeviceClass &other)
m_supports_serial_combination = other.m_supports_serial_combination;
m_supports_parallel_combination = other.m_supports_parallel_combination;
m_equivalent_terminal_ids = other.m_equivalent_terminal_ids;
m_spice_profiles = other.m_spice_profiles;
}
return *this;

View File

@ -418,62 +418,6 @@ Class<NetlistSpiceReaderDelegateImpl> db_NetlistSpiceReaderDelegate ("db", "Netl
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::method ("read_all_parameters", &NetlistSpiceReaderDelegateImpl::read_all_parameters,
"@brief Gets a flag indicating whether to read all SPICE parameters\n"
"\n"
"With this flag set to false, unknown device parameters are ignored. Parameters are declared by the "
"device classes present in the netlist, before the reader is used. SPICE profiles can be used to "
"configure the parameters accepted by the SPICE reader, in addition to fixed and pre-defined parameters. "
"With 'read_all_parameters' set to true, all parameters are read, even if they are not declared as fixed "
"parameters by the device classes or as incoming parameters in their SPICE profiles.\n"
"\n"
"Note, that reimplementing \"element\" may change that behavior. Also, SPICE profiles can choose "
"to capture all parameters through a wildcard specification, basically emulation this flag.\n"
"\n"
"This attribute has been introduced in version 0.31.0."
) +
gsi::method ("read_all_parameters=", &NetlistSpiceReaderDelegateImpl::set_read_all_parameters, gsi::arg ("f"),
"@brief Sets a flag indicating whether to read all SPICE parameters\n"
"See \\read_all_parameters for details about this attribute.\n"
"\n"
"This attribute has been introduced in version 0.31.0."
) +
gsi::method ("legacy_mode=", &NetlistSpiceReaderDelegateImpl::legacy_mode,
"@brief Gets a flag indicating legacy mode\n"
"\n"
"This flag controls the parsing of SPICE lines in the default\n"
"implementation of \"parse_element\". With this flag set to true (default),\n"
"the elements are restricted to their original SPICE meaning, i.e. \"R\"\n"
"can have two or three nets and a direct value. With this flag set to\n"
"false, all elements can have any number of terminals, the last entry\n"
"is the model and a direct value is not allowed. In non-legacy mode, all\n"
"elements share the same notation and can be bound to devices through\n"
"SPICE profiles without restrictions.\n"
"\n"
"@code\n"
"* Allowed always:\n"
"R A B MODEL R=1k\n"
"\n"
"* Allowed with legacy_mode=true (default):\n"
"R A B 1k\n"
"\n"
"* Allowed with legacy_mode=false with a device class named 'MODEL' and\n"
"* accepting 4 terminals for the 'R' element in its SPICE profile:\n"
"R A B C D MODEL A=17.5 P=105\n"
"@/code\n"
"\n"
"This flag allows configuring the SPICE reading without\n"
"need for reimplementing a delegate. Note, that reimplementing\n"
"\"parse_element\" may change that behavior.\n"
"\n"
"This attribute has been introduced in version 0.31.0."
) +
gsi::method ("legacy_mode=", &NetlistSpiceReaderDelegateImpl::set_legacy_mode, gsi::arg ("f"),
"@brief Sets a flag indicating legacy mode\n"
"See \\legacy_mode for details about this attribute.\n"
"\n"
"This attribute has been introduced in version 0.31.0."
) +
gsi::callback ("start", &NetlistSpiceReaderDelegateImpl::start, &NetlistSpiceReaderDelegateImpl::cb_start, gsi::arg ("netlist"),
"@brief This method is called when the reader starts reading a netlist\n"
) +
@ -640,6 +584,18 @@ static void set_read_all_parameters (db::NetlistSpiceReader *reader, bool f)
}
}
static bool legacy_mode (const db::NetlistSpiceReader *reader)
{
return reader->delegate () ? reader->delegate ()->legacy_mode () : false;
}
static void set_legacy_mode (db::NetlistSpiceReader *reader, bool f)
{
if (reader->delegate ()) {
reader->delegate ()->set_legacy_mode (f);
}
}
Class<db::NetlistSpiceReader> db_NetlistSpiceReader (db_NetlistReader, "db", "NetlistSpiceReader",
gsi::constructor ("new", &new_spice_reader, gsi::arg ("profile", std::string ()),
@ -657,20 +613,58 @@ Class<db::NetlistSpiceReader> db_NetlistSpiceReader (db_NetlistReader, "db", "Ne
"The profile string argument has been added in version 0.31.0."
) +
gsi::method_ext ("read_all_parameters", &read_all_parameters,
"@brief Gets a flag indicating whether all parameters shall be read\n"
"@brief Gets a flag indicating whether to read all SPICE parameters\n"
"\n"
"With this flag set to false, only those parameters which\n"
"are declared in the device classes of the netlist are read.\n"
"If set to true (the default), additional parameters are added to the device classes\n"
"and their values are stored in the device objects.\n"
"Note that this behavior can be changed in reimplementations of the\n"
"SPICE reader delegate class.\n"
"With this flag set to false, unknown device parameters are ignored. Parameters are declared by the "
"device classes present in the netlist, before the reader is used. SPICE profiles can be used to "
"configure the parameters accepted by the SPICE reader, in addition to fixed and pre-defined parameters. "
"With 'read_all_parameters' set to true, all parameters are read, even if they are not declared as fixed "
"parameters by the device classes or as incoming parameters in their SPICE profiles.\n"
"\n"
"Note, that reimplementing \"element\" may change that behavior. Also, SPICE profiles can choose "
"to capture all parameters through a wildcard specification, basically emulation this flag.\n"
"\n"
"This attribute has been introduced in version 0.31.0."
) +
gsi::method_ext ("read_all_parameters=", &set_read_all_parameters, gsi::arg ("f"),
"@brief Sets a flag indicating whether all parameters shall be read\n"
"See \\read_all_parameters for details.\n"
"@brief Sets a flag indicating whether to read all SPICE parameters\n"
"See \\read_all_parameters for details about this attribute.\n"
"\n"
"This attribute has been introduced in version 0.31.0."
) +
gsi::method_ext ("legacy_mode=", &legacy_mode,
"@brief Gets a flag indicating legacy mode\n"
"\n"
"This flag controls the parsing of SPICE lines in the default\n"
"implementation of \"parse_element\". With this flag set to true (default),\n"
"the elements are restricted to their original SPICE meaning, i.e. \"R\"\n"
"can have two or three nets and a direct value. With this flag set to\n"
"false, all elements can have any number of terminals, the last entry\n"
"is the model and a direct value is not allowed. In non-legacy mode, all\n"
"elements share the same notation and can be bound to devices through\n"
"SPICE profiles without restrictions.\n"
"\n"
"@code\n"
"* Allowed always:\n"
"R A B MODEL R=1k\n"
"\n"
"* Allowed with legacy_mode=true (default):\n"
"R A B 1k\n"
"\n"
"* Allowed with legacy_mode=false with a device class named 'MODEL' and\n"
"* accepting 4 terminals for the 'R' element in its SPICE profile:\n"
"R A B C D MODEL A=17.5 P=105\n"
"@/code\n"
"\n"
"This flag allows configuring the SPICE reading without\n"
"need for reimplementing a delegate. Note, that reimplementing\n"
"\"parse_element\" may change that behavior.\n"
"\n"
"This attribute has been introduced in version 0.31.0."
) +
gsi::method_ext ("legacy_mode=", &set_legacy_mode, gsi::arg ("f"),
"@brief Sets a flag indicating legacy mode\n"
"See \\legacy_mode for details about this attribute.\n"
"\n"
"This attribute has been introduced in version 0.31.0."
),

View File

@ -970,6 +970,118 @@ TEST(27_MoreParameters)
);
}
TEST(28_SpiceProfiles1)
{
db::Netlist nl;
db::DeviceClass *dc1 = new db::DeviceClassMOS4Transistor ();
dc1->set_name ("NMOS");
nl.add_device_class (dc1);
db::DeviceClass *dc2 = new db::DeviceClassMOS4Transistor ();
dc2->set_name ("PMOS");
nl.add_device_class (dc2);
db::NetlistSpiceReader reader;
tl::InputStream is (
"text:\n"
".subckt TOP\n"
"M1 n1 n2 n3 n1 nmos w=1.5u l=0.25u m=2 x=5*16\n"
"M2 n5 n2 n3 n5 pmos w=4.0u l=0.25u y=hello\n"
".ends\n"
);
reader.read (is, nl);
EXPECT_EQ (nl.to_string (),
"circuit TOP ();\n"
" device NMOS '1' (S=N3,G=N2,D=N1,B=N1) (L=0.25,W=3,AS=0,AD=0,PS=0,PD=0);\n"
" device PMOS '2' (S=N3,G=N2,D=N5,B=N5) (L=0.25,W=4,AS=0,AD=0,PS=0,PD=0);\n"
"end;\n"
);
}
TEST(28_SpiceProfiles2)
{
db::Netlist nl;
db::DeviceClass *dc1 = new db::DeviceClassMOS4Transistor ();
dc1->set_name ("NMOS");
nl.add_device_class (dc1);
db::DeviceClass *dc2 = new db::DeviceClassMOS4Transistor ();
dc2->set_name ("PMOS");
nl.add_device_class (dc2);
db::NetlistSpiceReader reader;
reader.delegate ()->set_read_all_parameters (true);
tl::InputStream is (
"text:\n"
".subckt TOP\n"
"M1 n1 n2 n3 n1 nmos w=1.5u l=0.25u m=2 x=5*16\n"
"M2 n5 n2 n3 n5 pmos w=4.0u l=0.25u y=hello\n"
".ends\n"
);
reader.read (is, nl);
EXPECT_EQ (nl.to_string (),
// with "read_all_parameters" we also see the unknown ones
"circuit TOP ();\n"
" device NMOS '1' (S=N3,G=N2,D=N1,B=N1) (L=0.25,W=3,AS=0,AD=0,PS=0,PD=0,X=80);\n"
" device PMOS '2' (S=N3,G=N2,D=N5,B=N5) (L=0.25,W=4,AS=0,AD=0,PS=0,PD=0,Y=HELLO);\n"
"end;\n"
);
}
TEST(28_SpiceProfiles3)
{
db::Netlist nl;
db::DeviceClass *dc1 = new db::DeviceClassMOS4Transistor ();
dc1->set_name ("NMOS");
auto sp = dc1->spice_profile ("");
sp.incoming_parameters.insert (std::make_pair ("X", "X?X*2.5:42.0"));
sp.incoming_parameters.insert (std::make_pair ("Y", "Y+'!'"));
dc1->set_spice_profile ("NON_DEFAULT_PROFILE", sp);
EXPECT_EQ (dc1->has_spice_profile ("NON_DEFAULT_PROFILE"), true);
EXPECT_EQ (dc1->spice_profile ("NON_DEFAULT_PROFILE").incoming_parameters.size (), size_t (9));
EXPECT_EQ (dc1->spice_profile ("NON_DEFAULT_PROFILE").terminal_order.size (), size_t (4));
nl.add_device_class (dc1);
db::DeviceClass *dc2 = dc1->clone ();
EXPECT_EQ (dc2->has_spice_profile ("NON_DEFAULT_PROFILE"), true);
EXPECT_EQ (dc2->spice_profile ("NON_DEFAULT_PROFILE").incoming_parameters.size (), size_t (9));
EXPECT_EQ (dc2->spice_profile ("NON_DEFAULT_PROFILE").terminal_order.size (), size_t (4));
dc2->set_name ("PMOS");
nl.add_device_class (dc2);
db::NetlistSpiceReader reader;
reader.set_profile ("NON_DEFAULT_PROFILE");
EXPECT_EQ (reader.profile (), "NON_DEFAULT_PROFILE");
tl::InputStream is (
"text:\n"
".subckt TOP\n"
"M1 n1 n2 n3 n1 nmos w=1.5u l=0.25u m=2 x=5*16\n"
"M2 n5 n2 n3 n5 pmos w=4.0u l=0.25u y=hello\n"
".ends\n"
);
reader.read (is, nl);
EXPECT_EQ (nl.to_string (),
"circuit TOP ();\n"
" device NMOS '1' (S=N3,G=N2,D=N1,B=N1) (L=0.25,W=3,AS=0,AD=0,PS=0,PD=0,X=200);\n"
" device PMOS '2' (S=N3,G=N2,D=N5,B=N5) (L=0.25,W=4,AS=0,AD=0,PS=0,PD=0,Y=HELLO!);\n"
"end;\n"
);
}
TEST(100_ExpressionParser)
{
std::map<std::string, tl::Variant> vars;