Parameter values of db::Device/db::DeviceClass

This commit is contained in:
Matthias Koefferlein 2018-12-24 13:39:19 +01:00
parent c5222c26e3
commit eb6b043c3b
5 changed files with 453 additions and 12 deletions

View File

@ -58,7 +58,7 @@ Device::~Device ()
}
Device::Device (DeviceClass *device_class, const std::string &name)
: m_device_class (device_class), m_name (name)
: mp_device_class (device_class), m_name (name)
{
// .. nothing yet ..
}
@ -72,7 +72,7 @@ Device &Device::operator= (const Device &other)
{
if (this != &other) {
m_name = other.m_name;
m_device_class = other.m_device_class;
mp_device_class = other.mp_device_class;
}
return *this;
}
@ -120,6 +120,41 @@ void Device::connect_port (size_t port_id, Net *net)
}
}
double Device::parameter_value (size_t param_id) const
{
if (m_parameters.size () > param_id) {
return m_parameters [param_id];
} else if (mp_device_class) {
const db::DeviceParameterDefinition *pd = mp_device_class->parameter_definition (param_id);
if (pd) {
return pd->default_value ();
}
}
return 0.0;
}
void Device::set_parameter_value (size_t param_id, double v)
{
if (m_parameters.size () <= param_id) {
// resize the parameter vector with default values
size_t from_size = m_parameters.size ();
m_parameters.resize (param_id + 1, 0.0);
if (mp_device_class) {
for (size_t n = from_size; n < param_id; ++n) {
const db::DeviceParameterDefinition *pd = mp_device_class->parameter_definition (n);
if (pd) {
m_parameters [n] = pd->default_value ();
}
}
}
}
m_parameters [param_id] = v;
}
// --------------------------------------------------------------------------------
// SubCircuit class implementation
@ -667,6 +702,27 @@ const DevicePortDefinition *DeviceClass::port_definition (size_t id) const
}
}
const DeviceParameterDefinition &DeviceClass::add_parameter_definition (const DeviceParameterDefinition &pd)
{
m_parameter_definitions.push_back (pd);
m_parameter_definitions.back ().set_id (m_parameter_definitions.size () - 1);
return m_parameter_definitions.back ();
}
void DeviceClass::clear_parameter_definitions ()
{
m_parameter_definitions.clear ();
}
const DeviceParameterDefinition *DeviceClass::parameter_definition (size_t id) const
{
if (id < m_parameter_definitions.size ()) {
return & m_parameter_definitions [id];
} else {
return 0;
}
}
// --------------------------------------------------------------------------------
// GenericDeviceClass class implementation

View File

@ -553,7 +553,7 @@ public:
*/
const DeviceClass *device_class () const
{
return m_device_class.get ();
return mp_device_class;
}
/**
@ -592,13 +592,24 @@ public:
*/
void connect_port (size_t port_id, Net *net);
/**
* @brief Gets the value for the parameter with the given ID
*/
double parameter_value (size_t param_id) const;
/**
* @brief Sets the value for the parameter with the given ID
*/
void set_parameter_value (size_t param_id, double v);
private:
friend class Circuit;
friend class Net;
tl::weak_ptr<DeviceClass> m_device_class;
DeviceClass *mp_device_class;
std::string m_name;
std::vector<Net::port_iterator> m_port_refs;
std::vector<double> m_parameters;
/**
* @brief Sets the port reference for a specific port
@ -610,7 +621,7 @@ private:
*/
void set_device_class (DeviceClass *dc)
{
m_device_class.reset (dc);
mp_device_class = dc;
}
};
@ -1143,6 +1154,99 @@ private:
}
};
/**
* @brief A device parameter definition
*/
class DB_PUBLIC DeviceParameterDefinition
{
public:
/**
* @brief Creates an empty device parameter definition
*/
DeviceParameterDefinition ()
: m_name (), m_description (), m_default_value (0.0), m_id (0)
{
// .. nothing yet ..
}
/**
* @brief Creates a device parameter definition with the given name and description
*/
DeviceParameterDefinition (const std::string &name, const std::string &description, double default_value = 0.0)
: m_name (name), m_description (description), m_default_value (default_value), m_id (0)
{
// .. nothing yet ..
}
/**
* @brief Gets the parameter name
*/
const std::string &name () const
{
return m_name;
}
/**
* @brief Sets the parameter name
*/
void set_name (const std::string &n)
{
m_name = n;
}
/**
* @brief Gets the parameter description
*/
const std::string &description () const
{
return m_description;
}
/**
* @brief Sets the parameter description
*/
void set_description (const std::string &d)
{
m_description = d;
}
/**
* @brief Gets the parameter default value
*/
double default_value () const
{
return m_default_value;
}
/**
* @brief Sets the parameter description
*/
void set_default_value (double d)
{
m_default_value = d;
}
/**
* @brief Gets the parameter ID
*/
size_t id () const
{
return m_id;
}
private:
friend class DeviceClass;
std::string m_name, m_description;
double m_default_value;
size_t m_id;
void set_id (size_t id)
{
m_id = id;
}
};
/**
* @brief A device class
*
@ -1205,7 +1309,7 @@ public:
* The number of ports is constant per class. The index of the port
* is used as an ID of the port, hence the order must be static.
*/
virtual const std::vector<DevicePortDefinition> &port_definitions () const
const std::vector<DevicePortDefinition> &port_definitions () const
{
return m_port_definitions;
}
@ -1225,8 +1329,32 @@ public:
*/
const DevicePortDefinition *port_definition (size_t id) const;
/**
* @brief Gets the parameter definitions
*/
const std::vector<DeviceParameterDefinition> &parameter_definitions () const
{
return m_parameter_definitions;
}
/**
* @brief Adds a parameter definition
*/
const DeviceParameterDefinition &add_parameter_definition (const DeviceParameterDefinition &pd);
/**
* @brief Clears the parameter definition
*/
void clear_parameter_definitions ();
/**
* @brief Gets the parameter definition from the ID
*/
const DeviceParameterDefinition *parameter_definition (size_t id) const;
private:
std::vector<DevicePortDefinition> m_port_definitions;
std::vector<DeviceParameterDefinition> m_parameter_definitions;
};
/**

View File

@ -22,6 +22,8 @@
#include "gsiDecl.h"
#include "dbNetlist.h"
#include "tlException.h"
#include "tlInternational.h"
namespace gsi
{
@ -45,6 +47,40 @@ static void device_disconnect_port (db::Device *device, size_t port_id)
device->connect_port (port_id, 0);
}
static bool device_has_param_with_name (const db::DeviceClass *device_class, const std::string &name)
{
const std::vector<db::DeviceParameterDefinition> &pd = device_class->parameter_definitions ();
for (std::vector<db::DeviceParameterDefinition>::const_iterator i = pd.begin (); i != pd.end (); ++i) {
if (i->name () == name) {
return true;
}
}
return false;
}
static size_t device_param_id_for_name (const db::DeviceClass *device_class, const std::string &name)
{
if (device_class) {
const std::vector<db::DeviceParameterDefinition> &pd = device_class->parameter_definitions ();
for (std::vector<db::DeviceParameterDefinition>::const_iterator i = pd.begin (); i != pd.end (); ++i) {
if (i->name () == name) {
return i->id ();
}
}
}
throw tl::Exception (tl::to_string (tr ("Invalid parameter name")) + ": '" + name + "'");
}
static double device_parameter_value (const db::Device *device, const std::string &name)
{
return device->parameter_value (device_param_id_for_name (device->device_class (), name));
}
static void device_set_parameter_value (db::Device *device, const std::string &name, double value)
{
return device->set_parameter_value (device_param_id_for_name (device->device_class (), name), value);
}
Class<db::Device> decl_dbDevice ("db", "Device",
gsi::method ("device_class", &db::Device::device_class,
"@brief Gets the device class the device belongs to.\n"
@ -66,6 +102,20 @@ Class<db::Device> decl_dbDevice ("db", "Device",
) +
gsi::method_ext ("disconnect_port", &device_disconnect_port, gsi::arg ("port_id"),
"@brief Disconnects the given port from any net.\n"
) +
gsi::method ("parameter", &db::Device::parameter_value, gsi::arg ("param_id"),
"@brief Gets the parameter value for the given parameter ID."
) +
gsi::method ("set_parameter", &db::Device::set_parameter_value, gsi::arg ("param_id"), gsi::arg ("value"),
"@brief Sets the parameter value for the given parameter ID."
) +
gsi::method_ext ("parameter", &gsi::device_parameter_value, gsi::arg ("param_name"),
"@brief Gets the parameter value for the given parameter name.\n"
"If the parameter name is not valid, an exception is thrown."
) +
gsi::method_ext ("set_parameter", &gsi::device_set_parameter_value, gsi::arg ("param_name"), gsi::arg ("value"),
"@brief Sets the parameter value for the given parameter name.\n"
"If the parameter name is not valid, an exception is thrown."
),
"@brief A device inside a circuit.\n"
"Device object represent atomic devices such as resistors, diodes or transistors. "
@ -243,7 +293,15 @@ Class<db::Net> decl_dbNet ("db", "Net",
"This class has been added in version 0.26."
);
static db::DevicePortDefinition *new_port_definition (const std::string &name, const std::string &description)
{
return new db::DevicePortDefinition (name, description);
}
Class<db::DevicePortDefinition> decl_dbDevicePortDefinition ("db", "DevicePortDefinition",
gsi::constructor ("new", &gsi::new_port_definition, gsi::arg ("name"), gsi::arg ("description", std::string ()),
"@brief Creates a new port definition."
) +
gsi::method ("name", &db::DevicePortDefinition::name,
"@brief Gets the name of the port."
) +
@ -267,6 +325,45 @@ Class<db::DevicePortDefinition> decl_dbDevicePortDefinition ("db", "DevicePortDe
"This class has been added in version 0.26."
);
static db::DeviceParameterDefinition *new_parameter_definition (const std::string &name, const std::string &description, double default_value)
{
return new db::DeviceParameterDefinition (name, description, default_value);
}
Class<db::DeviceParameterDefinition> decl_dbDeviceParameterDefinition ("db", "DeviceParameterDefinition",
gsi::constructor ("new", &gsi::new_parameter_definition, gsi::arg ("name"), gsi::arg ("description", std::string ()), gsi::arg ("default_value", 0.0),
"@brief Creates a new parameter definition."
) +
gsi::method ("name", &db::DeviceParameterDefinition::name,
"@brief Gets the name of the parameter."
) +
gsi::method ("name=", &db::DeviceParameterDefinition::set_name, gsi::arg ("name"),
"@brief Sets the name of the parameter."
) +
gsi::method ("description", &db::DeviceParameterDefinition::description,
"@brief Gets the description of the parameter."
) +
gsi::method ("description=", &db::DeviceParameterDefinition::set_description, gsi::arg ("description"),
"@brief Sets the description of the parameter."
) +
gsi::method ("default_value", &db::DeviceParameterDefinition::default_value,
"@brief Gets the default value of the parameter."
) +
gsi::method ("default_value=", &db::DeviceParameterDefinition::set_default_value, gsi::arg ("default_value"),
"@brief Sets the default value of the parameter.\n"
"The default value is used to initialize parameters of \\Device objects."
) +
gsi::method ("id", &db::DeviceParameterDefinition::id,
"@brief Gets the ID of the parameter.\n"
"The ID of the parameter is used in some places to refer to a specific parameter (e.g. in "
"the \\NetParameterRef object)."
),
"@brief A parameter descriptor\n"
"This class is used inside the \\DeviceClass class to describe a parameter of the device.\n"
"\n"
"This class has been added in version 0.26."
);
Class<db::DeviceClass> decl_dbDeviceClass ("db", "DeviceClass",
gsi::method ("name", &db::DeviceClass::name,
"@brief Gets the name of the device class."
@ -276,12 +373,29 @@ Class<db::DeviceClass> decl_dbDeviceClass ("db", "DeviceClass",
) +
gsi::method ("port_definitions", &db::DeviceClass::port_definitions,
"@brief Gets the list of port definitions of the device.\n"
"See the \\PortDefinition class description for details."
"See the \\DevicePortDefinition class description for details."
) +
gsi::method ("port_definition", &db::DeviceClass::port_definition, gsi::arg ("port_id"),
"@brief Gets the port definition object for a given ID.\n"
"Port definition IDs are used in some places to reference a specific port of a device. "
"This method obtains the corresponding definition object."
) +
gsi::method ("parameter_definitions", &db::DeviceClass::parameter_definitions,
"@brief Gets the list of parameter definitions of the device.\n"
"See the \\DeviceParameterDefinition class description for details."
) +
gsi::method ("parameter_definition", &db::DeviceClass::parameter_definition, gsi::arg ("parameter_id"),
"@brief Gets the parameter definition object for a given ID.\n"
"Parameter definition IDs are used in some places to reference a specific parameter of a device. "
"This method obtains the corresponding definition object."
) +
gsi::method_ext ("has_parameter", &gsi::device_has_param_with_name, gsi::arg ("name"),
"@brief Returns true, if the device class has a parameter with the given name.\n"
) +
gsi::method_ext ("parameter_id", &gsi::device_param_id_for_name, gsi::arg ("name"),
"@brief Returns the parameter ID of the parameter with the given name.\n"
"An exception is thrown if there is no parameter with the given name. Use \\has_parameter to check "
"whether the name is a valid parameter name."
),
"@brief A class describing a specific type of device.\n"
"Device class objects live in the context of a \\Netlist object. After a "
@ -301,16 +415,38 @@ static void gdc_add_port_definition (db::GenericDeviceClass *cls, db::DevicePort
}
}
static void gdc_add_parameter_definition (db::GenericDeviceClass *cls, db::DeviceParameterDefinition *parameter_def)
{
if (parameter_def) {
*parameter_def = cls->add_parameter_definition (*parameter_def);
}
}
Class<db::GenericDeviceClass> decl_dbGenericDeviceClass (decl_dbDeviceClass, "db", "GenericDeviceClass",
gsi::method_ext ("add_port", &gsi::gdc_add_port_definition, gsi::arg ("port_def"),
"@brief Adds the given port definition to the device class\n"
"This method will define a new port. The new port is added at the end of existing ports. "
"The port definition object passed as the argument is modified to contain the "
"new ID of the port."
"new ID of the port.\n"
"\n"
"The port is copied into the device class. Modifying the port object later "
"does not have the effect of changing the port definition."
) +
gsi::method ("clear_ports", &db::GenericDeviceClass::clear_port_definitions,
"@brief Clears the list of ports\n"
) +
gsi::method_ext ("add_parameter", &gsi::gdc_add_parameter_definition, gsi::arg ("parameter_def"),
"@brief Adds the given parameter definition to the device class\n"
"This method will define a new parameter. The new parameter is added at the end of existing parameters. "
"The parameter definition object passed as the argument is modified to contain the "
"new ID of the parameter."
"\n"
"The parameter is copied into the device class. Modifying the parameter object later "
"does not have the effect of changing the parameter definition."
) +
gsi::method ("clear_parameters", &db::GenericDeviceClass::clear_parameter_definitions,
"@brief Clears the list of parameters\n"
) +
gsi::method ("name=", &db::GenericDeviceClass::set_name, gsi::arg ("name"),
"@brief Sets the name of the device\n"
) +
@ -324,6 +460,9 @@ Class<db::GenericDeviceClass> decl_dbGenericDeviceClass (decl_dbDeviceClass, "db
"specify the ports. Then add this new device class to the \\Netlist object where it will live "
"and be used to define device instances (\\Device objects).\n"
"\n"
"In addition, parameters can be defined which correspond to values stored inside the "
"specific device instance (\\Device object)."
"\n"
"This class has been added in version 0.26."
);
@ -545,7 +684,9 @@ Class<db::Netlist> decl_dbNetlist ("db", "Netlist",
) +
gsi::method ("remove", &db::Netlist::remove_device_class, gsi::arg ("device_class"),
"@brief Removes the given device class object from the netlist\n"
"After the object has been removed, it becomes invalid and cannot be used further."
"After the object has been removed, it becomes invalid and cannot be used further. "
"Use this method with care as it may corrupt the internal structure of the netlist. "
"Only use this method when device refers to this device class."
) +
gsi::iterator ("each_device_class", (db::Netlist::device_class_iterator (db::Netlist::*) ()) &db::Netlist::begin_device_classes, (db::Netlist::device_class_iterator (db::Netlist::*) ()) &db::Netlist::end_device_classes,
"@brief Iterates over the device classes of the netlist"

View File

@ -33,6 +33,11 @@ static std::string pd2string (const db::DevicePortDefinition &pd)
return pd.name () + "(" + pd.description () + ") #" + tl::to_string (pd.id ());
}
static std::string pd2string (const db::DeviceParameterDefinition &pd)
{
return pd.name () + "(" + pd.description () + ")=" + tl::to_string (pd.default_value ()) + " #" + tl::to_string (pd.id ());
}
TEST(1_DevicePortDefinition)
{
db::DevicePortDefinition pd;
@ -54,6 +59,22 @@ TEST(1_DevicePortDefinition)
dc.add_port_definition (pd2);
EXPECT_EQ (pd2string (dc.port_definitions ()[0]), "name(nothing yet) #0");
EXPECT_EQ (pd2string (dc.port_definitions ()[1]), "name2(now it has something) #1");
dc.clear_port_definitions ();
EXPECT_EQ (dc.port_definitions ().empty (), true);
db::DeviceParameterDefinition ppd ("P1", "Parameter 1", 1.0);
dc.add_parameter_definition (ppd);
db::DeviceParameterDefinition ppd2 ("P2", "Parameter 2");
dc.add_parameter_definition (ppd2);
EXPECT_EQ (pd2string (dc.parameter_definitions ()[0]), "P1(Parameter 1)=1 #0");
EXPECT_EQ (pd2string (dc.parameter_definitions ()[1]), "P2(Parameter 2)=0 #1");
dc.clear_parameter_definitions ();
EXPECT_EQ (dc.parameter_definitions ().empty (), true);
}
TEST(2_DeviceClass)
@ -226,11 +247,15 @@ TEST(4_CircuitDevices)
dc1.add_port_definition (db::DevicePortDefinition ("S", "Source"));
dc1.add_port_definition (db::DevicePortDefinition ("G", "Gate"));
dc1.add_port_definition (db::DevicePortDefinition ("D", "Drain"));
dc1.add_parameter_definition (db::DeviceParameterDefinition ("U", "", 1.0));
dc1.add_parameter_definition (db::DeviceParameterDefinition ("V", "", 2.0));
db::GenericDeviceClass dc2;
dc2.set_name ("dc2");
dc2.add_port_definition (db::DevicePortDefinition ("A", ""));
dc2.add_port_definition (db::DevicePortDefinition ("B", ""));
dc2.add_parameter_definition (db::DeviceParameterDefinition ("U", "", 2.0));
dc2.add_parameter_definition (db::DeviceParameterDefinition ("V", "", 1.0));
std::auto_ptr<db::Circuit> c (new db::Circuit ());
c->set_name ("c");
@ -246,6 +271,21 @@ TEST(4_CircuitDevices)
c->add_device (d2a);
c->add_device (d2b);
EXPECT_EQ (d1->parameter_value (0), 1.0);
EXPECT_EQ (d1->parameter_value (1), 2.0);
EXPECT_EQ (d2a->parameter_value (0), 2.0);
EXPECT_EQ (d2a->parameter_value (1), 1.0);
d1->set_parameter_value (1, 1.5);
EXPECT_EQ (d1->parameter_value (0), 1.0);
EXPECT_EQ (d1->parameter_value (1), 1.5);
d1->set_parameter_value (0, 0.5);
EXPECT_EQ (d1->parameter_value (0), 0.5);
EXPECT_EQ (d1->parameter_value (1), 1.5);
d2a->set_parameter_value (0, -1.0);
EXPECT_EQ (d2a->parameter_value (0), -1.0);
EXPECT_EQ (d2a->parameter_value (1), 1.0);
EXPECT_EQ (netlist2 (*c),
"c:\n"
" Dd1:S=(null),G=(null),D=(null)\n"

View File

@ -317,9 +317,7 @@ class DBNetlist_TestClass < TestBase
dc.description = "A device class"
assert_equal(dc.description, "A device class")
pd = RBA::DevicePortDefinition::new
pd.name = "A"
pd.description = "Port A"
pd = RBA::DevicePortDefinition::new("A", "Port A")
dc.add_port(pd)
assert_equal(pd.id, 0)
@ -345,6 +343,38 @@ class DBNetlist_TestClass < TestBase
dc.port_definitions.each { |pd| names << pd.name }
assert_equal(names, [])
pd = RBA::DeviceParameterDefinition::new("P1", "Parameter 1", 2.0)
assert_equal(pd.default_value, 2.0)
pd.default_value = 1.0
assert_equal(pd.default_value, 1.0)
dc.add_parameter(pd)
assert_equal(pd.id, 0)
assert_equal(pd.name, "P1")
assert_equal(pd.description, "Parameter 1")
assert_equal(pd.default_value, 1.0)
pd = RBA::DeviceParameterDefinition::new("", "")
pd.name = "P2"
pd.description = "Parameter 2"
dc.add_parameter(pd)
assert_equal(pd.id, 1)
assert_equal(pd.name, "P2")
assert_equal(pd.description, "Parameter 2")
assert_equal(pd.default_value, 0.0)
names = []
dc.parameter_definitions.each { |pd| names << pd.name }
assert_equal(names, [ "P1", "P2" ])
dc.clear_parameters
names = []
dc.parameter_definitions.each { |pd| names << pd.name }
assert_equal(names, [])
end
def test_8_Circuit
@ -425,6 +455,52 @@ class DBNetlist_TestClass < TestBase
end
def test_9_DeviceParameters
nl = RBA::Netlist::new
dc = RBA::GenericDeviceClass::new
dc.name = "DC"
nl.add(dc)
dc.add_parameter(RBA::DeviceParameterDefinition::new("U", "Parameter U", 1.0))
dc.add_parameter(RBA::DeviceParameterDefinition::new("V", "Parameter V", 2.0))
assert_equal(dc.has_parameter("U"), true)
assert_equal(dc.has_parameter("V"), true)
assert_equal(dc.has_parameter("X"), false)
assert_equal(dc.parameter_id("U"), 0)
assert_equal(dc.parameter_id("V"), 1)
error = false
begin
dc.parameter_id("X") # raises an exception
rescue => ex
error = true
end
assert_equal(error, true)
c = RBA::Circuit::new
c.name = "C"
nl.add(c)
d1 = c.create_device(dc)
assert_equal(d1.parameter(0), 1.0)
assert_equal(d1.parameter("U"), 1.0)
assert_equal(d1.parameter(1), 2.0)
assert_equal(d1.parameter("V"), 2.0)
d1.set_parameter(0, 0.5)
assert_equal(d1.parameter(0), 0.5)
assert_equal(d1.parameter(1), 2.0)
d1.set_parameter("U", -0.5)
assert_equal(d1.parameter(0), -0.5)
assert_equal(d1.parameter(1), 2.0)
d1.set_parameter("V", 42)
assert_equal(d1.parameter(0), -0.5)
assert_equal(d1.parameter(1), 42)
end
end
load("test_epilogue.rb")