From 36f147bcc18b40b67256d69ab0ca67a5def02fee Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sat, 13 Jun 2026 16:51:52 +0200 Subject: [PATCH] Bug fixes --- src/db/db/dbDeviceClass.cc | 1 + src/db/db/gsiDeclDbNetlistSpiceReader.cc | 124 ++++++++++------------ src/db/unit_tests/dbNetlistReaderTests.cc | 112 +++++++++++++++++++ 3 files changed, 172 insertions(+), 65 deletions(-) diff --git a/src/db/db/dbDeviceClass.cc b/src/db/db/dbDeviceClass.cc index e294b433f..38b266cfb 100644 --- a/src/db/db/dbDeviceClass.cc +++ b/src/db/db/dbDeviceClass.cc @@ -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; diff --git a/src/db/db/gsiDeclDbNetlistSpiceReader.cc b/src/db/db/gsiDeclDbNetlistSpiceReader.cc index 2fb80d257..b824567af 100644 --- a/src/db/db/gsiDeclDbNetlistSpiceReader.cc +++ b/src/db/db/gsiDeclDbNetlistSpiceReader.cc @@ -418,62 +418,6 @@ Class 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_NetlistReader, "db", "NetlistSpiceReader", gsi::constructor ("new", &new_spice_reader, gsi::arg ("profile", std::string ()), @@ -657,20 +613,58 @@ Class 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." ), diff --git a/src/db/unit_tests/dbNetlistReaderTests.cc b/src/db/unit_tests/dbNetlistReaderTests.cc index ca26e68f8..e1dccba7f 100644 --- a/src/db/unit_tests/dbNetlistReaderTests.cc +++ b/src/db/unit_tests/dbNetlistReaderTests.cc @@ -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 vars;