From e3377066349adca84311ba33e50d9eb9196bff1b Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 14 Jun 2026 19:26:29 +0200 Subject: [PATCH] Reworking parameter scaling --- src/db/db/dbNetlistSpiceReaderDelegate.cc | 53 +++++++++----- src/db/db/dbNetlistSpiceWriter.cc | 85 ++++++++++++++++++----- src/db/db/gsiDeclDbNetlistDeviceClass.cc | 10 +-- src/db/unit_tests/dbNetlistWriterTests.cc | 9 ++- testdata/algo/nwriter4_au.txt | 4 +- testdata/lvs/res_combine1.cir.1 | 2 +- testdata/lvs/res_combine1.cir.2 | 2 +- testdata/lvs/res_combine1.cir.3 | 2 +- 8 files changed, 117 insertions(+), 50 deletions(-) diff --git a/src/db/db/dbNetlistSpiceReaderDelegate.cc b/src/db/db/dbNetlistSpiceReaderDelegate.cc index 6aaecc619..70be6ff58 100644 --- a/src/db/db/dbNetlistSpiceReaderDelegate.cc +++ b/src/db/db/dbNetlistSpiceReaderDelegate.cc @@ -373,6 +373,15 @@ void NetlistSpiceReaderDelegate::parse_element (const std::string &s, std::strin } } +static tl::Variant si_unscale (const tl::Variant &value, double sis) +{ + if (value.is_double () && sis != 0 && sis != 1.0) { + return tl::Variant (value.to_double () / sis); + } else { + return value; + } +} + namespace { class SPICEParameterEval @@ -393,22 +402,27 @@ protected: return; } - auto p = m_params.find (name == "_" ? m_name : name); - if (p != m_params.end ()) { - value = &p->second; - return; - } + std::string n = name == "_" ? m_name : name; + auto h = m_heap.find (n); + if (h != m_heap.end ()) { - auto pp = m_all_params.find (name == "_" ? m_name : name); - if (pp != m_all_params.end ()) { - if (pp->second) { - value = &pp->second->default_value (); - return; + value = &h->second; + + } else { + + auto p = m_params.find (n); + auto pp = m_all_params.find (n); + + tl::Variant v; + if (pp != m_all_params.end () && pp->second) { + v = si_unscale (p == m_params.end () ? pp->second->default_value () : p->second, pp->second->si_scaling ()); + } else if (p != m_params.end ()) { + v = p->second; } - } - static tl::Variant nil; - value = &nil; + value = &m_heap.insert (std::make_pair (n, v)).first->second; + + } } private: @@ -416,6 +430,7 @@ private: const std::map &m_params; const std::map &m_all_params; tl::Variant m_value; + std::map m_heap; }; } @@ -431,7 +446,13 @@ eval_parameter_expression (const std::string &name, const std::string &expr, con } else if (expr == "_") { auto p = params.find (name); - return p != params.end () ? p->second : tl::Variant (); + auto pp = all_params.find (name); + + if (p != params.end () && pp != all_params.end () && pp->second) { + return si_unscale (p->second, pp->second->si_scaling ()); + } else { + return p != params.end () ? p->second : tl::Variant (); + } } else if (expr == "$") { @@ -659,7 +680,7 @@ bool NetlistSpiceReaderDelegate::element (db::Circuit *circuit, const std::strin tl_assert (pp != params.end ()); if (p->second) { - device->set_parameter_value (p->second->id (), pp->second); + device->set_parameter_value (p->second->id (), si_unscale (pp->second, p->second->si_scaling ())); } else { device->set_parameter_value_create (p->first, pp->second, false, default_from_value (pp->second)); } @@ -684,7 +705,7 @@ NetlistSpiceReaderDelegate::apply_parameter_scaling (db::Device *device) const for (auto i = pd.begin (); i != pd.end (); ++i) { const tl::Variant &pv = device->parameter_value (i->id ()); if (pv.is_double ()) { - device->set_parameter_value (i->id (), pv.to_double () / i->si_scaling () * pow (m_options.scale, i->geo_scaling_exponent ())); + device->set_parameter_value (i->id (), pv.to_double () * pow (m_options.scale, i->geo_scaling_exponent ())); } } } diff --git a/src/db/db/dbNetlistSpiceWriter.cc b/src/db/db/dbNetlistSpiceWriter.cc index f72005846..c6f524aec 100644 --- a/src/db/db/dbNetlistSpiceWriter.cc +++ b/src/db/db/dbNetlistSpiceWriter.cc @@ -113,13 +113,36 @@ write_parameter_value (std::ostringstream &os, const tl::Variant &v, double sis) { if (v.is_double ()) { + double d = v.to_double () * sis; + double da = fabs (d); + // for compatibility - if (fabs (sis * 1e6 - 1.0) < db::epsilon) { - os << tl::to_string (v.to_double ()) << "U"; - } else if (fabs (sis * 1e12 - 1.0) < db::epsilon) { - os << tl::to_string (v.to_double ()) << "P"; - } else { - os << tl::to_string (v.to_double () * sis); + if (da > 1e15) { + os << tl::to_string (d); + } else if (da > 1e11 * (1.0 - db::epsilon)) { + os << tl::to_string (d * 1e-12) << "T"; + } else if (da > 1e8 * (1.0 - db::epsilon)) { + os << tl::to_string (d * 1e-9) << "G"; + } else if (da > 1e5 * (1.0 - db::epsilon)) { + os << tl::to_string (d * 1e-6) << "MEG"; + } else if (da > 1e2 * (1.0 - db::epsilon)) { + os << tl::to_string (d * 1e-3) << "K"; + } else if (da > 0.1 * (1.0 - db::epsilon)) { + os << tl::to_string (d); + } else if (da > 1e-4 * (1.0 - db::epsilon)) { + os << tl::to_string (d * 1e3) << "M"; + } else if (da > 1e-7 * (1.0 - db::epsilon)) { + os << tl::to_string (d * 1e6) << "U"; + } else if (da > 1e-10 * (1.0 - db::epsilon)) { + os << tl::to_string (d * 1e9) << "N"; + } else if (da > 1e-13 * (1.0 - db::epsilon)) { + os << tl::to_string (d * 1e12) << "P"; + } else if (da > 1e-16 * (1.0 - db::epsilon)) { + os << tl::to_string (d * 1e15) << "F"; + } else if (da > 1e-19 * (1.0 - db::epsilon)) { + os << tl::to_string (d * 1e18) << "A"; + } else if (da < 1e-21) { + os << tl::to_string (d); } } else if (v.is_long () || v.is_ulong ()) { @@ -129,6 +152,15 @@ write_parameter_value (std::ostringstream &os, const tl::Variant &v, double sis) } } +static tl::Variant si_scaling (const tl::Variant &value, double sis) +{ + if (sis == 1.0 || ! value.is_double ()) { + return value; + } else { + return tl::Variant (sis * value.to_double ()); + } +} + namespace { class SPICEParameterEval @@ -145,18 +177,32 @@ protected: virtual void resolve_name (const std::string &name, const tl::EvalFunction *& /*function*/, const tl::Variant *&value, tl::Variant *& /*var*/) { std::string n = (name == "_" ? m_name : name); - if (mp_dev->device_class ()->has_parameter_with_name (n)) { - value = &mp_dev->parameter_value (mp_dev->device_class ()->parameter_id_for_name (n)); - return; - } - static tl::Variant nil; - value = &nil; + const auto *cls = mp_dev->device_class (); + if (cls->has_parameter_with_name (n)) { + + size_t id = cls->parameter_id_for_name (n); + + auto h = m_heap.find (id); + if (h != m_heap.end ()) { + value = &h->second; + } else { + tl::Variant v = si_scaling (mp_dev->parameter_value (id), cls->parameter_definition (id)->si_scaling ()); + value = &m_heap.insert (std::make_pair (id, v)).first->second; + } + + } else { + + static tl::Variant nil; + value = &nil; + + } } private: const std::string m_name; const db::Device *mp_dev; + std::map m_heap; }; } @@ -171,9 +217,10 @@ eval_parameter_expression (const std::string &name, const std::string &expr, con } else if (expr == "_") { - if (dev.device_class ()->has_parameter_with_name (name)) { - auto id = dev.device_class ()->parameter_id_for_name (name); - return dev.parameter_value (id); + const auto *cls = dev.device_class (); + if (cls->has_parameter_with_name (name)) { + auto id = cls->parameter_id_for_name (name); + return si_scaling (dev.parameter_value (id), cls->parameter_definition (id)->si_scaling ()); } else { return tl::Variant (); } @@ -200,7 +247,7 @@ void NetlistSpiceWriterDelegate::write_device_profile (const db::Device &dev, co const std::map &dict = profile.outgoing_parameters; std::vector > all_params; - std::vector > > param_values; + std::vector > param_values; const db::DeviceClass *cls = dev.device_class (); tl_assert (cls != 0); @@ -233,14 +280,14 @@ void NetlistSpiceWriterDelegate::write_device_profile (const db::Device &dev, co if (id != dict.end ()) { pv = eval_parameter_expression (p->first, id->second, dev); } else if (p->second) { - pv = dev.parameter_value (p->second->id ()); + pv = si_scaling (dev.parameter_value (p->second->id ()), p->second->si_scaling ()); } if (! pv.is_nil ()) { if (p->first == "$") { direct_value = pv; } else { - param_values.push_back (std::make_pair (p->first, std::make_pair (pv, p->second))); + param_values.push_back (std::make_pair (p->first, pv)); } } @@ -260,7 +307,7 @@ void NetlistSpiceWriterDelegate::write_device_profile (const db::Device &dev, co for (auto p = param_values.begin (); p != param_values.end (); ++p) { os << " " << p->first << "="; - write_parameter_value (os, p->second.first, p->second.second ? p->second.second->si_scaling () : 1.0); + write_parameter_value (os, p->second, 1.0); } emit_line (os.str ()); diff --git a/src/db/db/gsiDeclDbNetlistDeviceClass.cc b/src/db/db/gsiDeclDbNetlistDeviceClass.cc index bce181252..bcb5aabbe 100644 --- a/src/db/db/gsiDeclDbNetlistDeviceClass.cc +++ b/src/db/db/gsiDeclDbNetlistDeviceClass.cc @@ -339,9 +339,9 @@ static void set_outgoing_parameters (db::DeviceClass::SpiceProfile *profile, con Class decl_dbDeviceClassSpiceProfile ("db", "DeviceClassSpiceProfile", gsi::method_ext ("element", &get_element, "@brief Gets the SPICE element to use for this device.\n" - "The element is the SPICE code for the element - i.e. 'X', 'M', 'R' etc.\n" + "The element is the single-character SPICE code for the element - i.e. 'X', 'M', 'R' etc.\n" ) + - gsi::method_ext ("element=", &set_element, + gsi::method_ext ("element=", &set_element, gsi::arg ("e"), "@brief Sets the SPICE element to use for this device.\n" "See \\element for a description of this attribute.\n" ) + @@ -376,7 +376,7 @@ Class decl_dbDeviceClassSpiceProfile ("db", "Devi "So, the forth element mimics the bulk pin of a MOS4 device. It is optional on reading (the net is ignored)\n" "and upon writing it is connected to 'S'.\n" ) + - gsi::method_ext ("terminal_order=", &set_terminal_order, + gsi::method_ext ("terminal_order=", &set_terminal_order, gsi::arg ("to"), "@brief Sets the terminal order to use for this device.\n" "See \\terminal_order for a description of this attribute.\n" ) + @@ -430,7 +430,7 @@ Class decl_dbDeviceClassSpiceProfile ("db", "Devi "cls.set_spice_profile(sp)\n" "@/code\n" ) + - gsi::method_ext ("incoming_parameters=", &set_incoming_parameters, + gsi::method_ext ("incoming_parameters=", &set_incoming_parameters, gsi::arg ("par"), "@brief Sets the mapping of incoming parameters from SPICE files\n" "See \\incoming_parameters for a description of this attribute.\n" ) + @@ -468,7 +468,7 @@ Class decl_dbDeviceClassSpiceProfile ("db", "Devi "cls.set_spice_profile(sp)\n" "@/code\n" ) + - gsi::method_ext ("outgoing_parameters=", &set_outgoing_parameters, + gsi::method_ext ("outgoing_parameters=", &set_outgoing_parameters, gsi::arg ("par"), "@brief Sets the mapping tables for outgoing parameters\n" "See \\outgoing_parameters for a description of this attribute.\n" ), diff --git a/src/db/unit_tests/dbNetlistWriterTests.cc b/src/db/unit_tests/dbNetlistWriterTests.cc index 88b66e2d8..e228e96e2 100644 --- a/src/db/unit_tests/dbNetlistWriterTests.cc +++ b/src/db/unit_tests/dbNetlistWriterTests.cc @@ -1502,7 +1502,7 @@ TEST(15_SpiceProfiles) EXPECT_EQ (std::string (om.data (), om.size ()), "* written by unit test\n\n" ".SUBCKT TOP 1 2 3\n" - "X1 3 2 1 1 NMOS L=1U WW=5 X=42\n" + "X1 3 2 1 1 NMOS L=1U WW=5U X=42\n" ".ENDS TOP\n" ); } @@ -1522,8 +1522,7 @@ TEST(15_SpiceProfiles) EXPECT_EQ (std::string (om.data (), om.size ()), "* written by unit test\n\n" ".SUBCKT TOP 1 2 3\n" - // @@@ not "5U"? - "X1 3 2 1 1 NMOS L=1U AS=0P AD=1.25P PS=0U PD=0U WW=5 X=42\n" + "X1 3 2 1 1 NMOS L=1U AS=0 AD=1.25P PS=0 PD=0 WW=5U X=42\n" ".ENDS TOP\n" ); } @@ -1567,7 +1566,7 @@ TEST(15_SpiceProfiles) EXPECT_EQ (std::string (om.data (), om.size ()), "* written by unit test\n\n" ".SUBCKT TOP 1 2 3\n" - "X1 3 2 1 1 NMOS L=1U AS=0P AD=1.25P PS=0U PD=0U\n" + "X1 3 2 1 1 NMOS L=1U AS=0 AD=1.25P PS=0 PD=0\n" ".ENDS TOP\n" ); } @@ -1589,7 +1588,7 @@ TEST(15_SpiceProfiles) EXPECT_EQ (std::string (om.data (), om.size ()), "* written by unit test\n\n" ".SUBCKT TOP 1 2 3\n" - "X1 3 2 1 1 NMOS L=1U W=2.5U AS=0P AD=1.25P PS=0U PD=0U\n" + "X1 3 2 1 1 NMOS L=1U W=2.5U AS=0 AD=1.25P PS=0 PD=0\n" ".ENDS TOP\n" ); } diff --git a/testdata/algo/nwriter4_au.txt b/testdata/algo/nwriter4_au.txt index f93844b69..81ad447c9 100644 --- a/testdata/algo/nwriter4_au.txt +++ b/testdata/algo/nwriter4_au.txt @@ -8,7 +8,7 @@ * net 2 n2 * net 3 n3 * device instance $1 r0 *1 0,0 DCLS -D$1 1 3 DCLS A=1.7P P=0U +D$1 1 3 DCLS A=1.7P P=0 * device instance $2 r0 *1 0,0 DCLS -D$2 3 2 DCLS A=0.42P P=0U +D$2 3 2 DCLS A=0.42P P=0 .ENDS C1 diff --git a/testdata/lvs/res_combine1.cir.1 b/testdata/lvs/res_combine1.cir.1 index edae0acfc..8e15eadf7 100644 --- a/testdata/lvs/res_combine1.cir.1 +++ b/testdata/lvs/res_combine1.cir.1 @@ -3,5 +3,5 @@ * cell Res2 .SUBCKT Res2 * device instance $1 r0 *1 110.14,51.795 RPP1 -R$1 1 2 95 RPP1 L=420U W=2.21052631579U +R$1 1 2 95 RPP1 L=0.42M W=2.21052631579U .ENDS Res2 diff --git a/testdata/lvs/res_combine1.cir.2 b/testdata/lvs/res_combine1.cir.2 index 45771ddbf..399eec77a 100644 --- a/testdata/lvs/res_combine1.cir.2 +++ b/testdata/lvs/res_combine1.cir.2 @@ -3,5 +3,5 @@ * cell Res2 .SUBCKT Res2 * device instance $6 r0 *1 110.14,51.795 RPP1 -R$6 1 2 95 RPP1 L=420U W=2.21052631579U +R$6 1 2 95 RPP1 L=0.42M W=2.21052631579U .ENDS Res2 diff --git a/testdata/lvs/res_combine1.cir.3 b/testdata/lvs/res_combine1.cir.3 index aa8016e4d..190d04c74 100644 --- a/testdata/lvs/res_combine1.cir.3 +++ b/testdata/lvs/res_combine1.cir.3 @@ -3,5 +3,5 @@ * cell Res2 .SUBCKT Res2 * device instance $1 r0 *1 110.14,51.795 RPP1 -R$1 2 1 95 RPP1 L=420U W=2.21052631579U +R$1 2 1 95 RPP1 L=0.42M W=2.21052631579U .ENDS Res2