Reworking parameter scaling

This commit is contained in:
Matthias Koefferlein 2026-06-14 19:26:29 +02:00
parent 48078b79e3
commit e337706634
8 changed files with 117 additions and 50 deletions

View File

@ -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<std::string, tl::Variant> &m_params;
const std::map<std::string, const db::DeviceParameterDefinition *> &m_all_params;
tl::Variant m_value;
std::map<std::string, tl::Variant> 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 ()));
}
}
}

View File

@ -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<size_t, tl::Variant> 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<std::string, std::string> &dict = profile.outgoing_parameters;
std::vector<std::pair<std::string, const db::DeviceParameterDefinition *> > all_params;
std::vector<std::pair<std::string, std::pair<tl::Variant, const db::DeviceParameterDefinition *> > > param_values;
std::vector<std::pair<std::string, tl::Variant> > 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 ());

View File

@ -339,9 +339,9 @@ static void set_outgoing_parameters (db::DeviceClass::SpiceProfile *profile, con
Class<db::DeviceClass::SpiceProfile> 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<db::DeviceClass::SpiceProfile> 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<db::DeviceClass::SpiceProfile> 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<db::DeviceClass::SpiceProfile> 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"
),

View File

@ -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"
);
}

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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