WIP: a bit of simplification, renaming of methods, parents for subcircuits, devices. References for circuits pointing to subcircuits.

This commit is contained in:
Matthias Koefferlein 2018-12-28 22:51:11 +01:00
parent a5b9cbfe5b
commit 2f48479838
10 changed files with 482 additions and 263 deletions

View File

@ -46,7 +46,7 @@ Pin::Pin (const std::string &name)
// Device class implementation
Device::Device ()
: mp_device_class (0), m_id (0)
: mp_device_class (0), m_id (0), mp_circuit (0)
{
// .. nothing yet ..
}
@ -61,13 +61,13 @@ Device::~Device ()
}
Device::Device (DeviceClass *device_class, const std::string &name)
: mp_device_class (device_class), m_name (name), m_id (0)
: mp_device_class (device_class), m_name (name), m_id (0), mp_circuit (0)
{
// .. nothing yet ..
}
Device::Device (const Device &other)
: mp_device_class (0), m_id (0)
: mp_device_class (0), m_id (0), mp_circuit (0)
{
operator= (other);
}
@ -81,6 +81,11 @@ Device &Device::operator= (const Device &other)
return *this;
}
void Device::set_circuit (Circuit *circuit)
{
mp_circuit = circuit;
}
void Device::set_name (const std::string &n)
{
m_name = n;
@ -176,7 +181,7 @@ void Device::set_parameter_value (const std::string &name, double v)
// SubCircuit class implementation
SubCircuit::SubCircuit ()
: m_id (0)
: m_id (0), mp_circuit (0)
{
// .. nothing yet ..
}
@ -191,13 +196,13 @@ SubCircuit::~SubCircuit()
}
SubCircuit::SubCircuit (Circuit *circuit, const std::string &name)
: m_circuit (circuit), m_name (name), m_id (0)
: m_circuit_ref (0), m_name (name), m_id (0), mp_circuit (0)
{
// .. nothing yet ..
set_circuit_ref (circuit);
}
SubCircuit::SubCircuit (const SubCircuit &other)
: m_id (0)
: m_id (0), mp_circuit (0)
{
operator= (other);
}
@ -206,8 +211,8 @@ SubCircuit &SubCircuit::operator= (const SubCircuit &other)
{
if (this != &other) {
m_name = other.m_name;
m_circuit = other.m_circuit;
m_trans = other.m_trans;
set_circuit_ref (const_cast<Circuit *> (other.circuit_ref ()));
}
return *this;
}
@ -230,6 +235,17 @@ void SubCircuit::set_pin_ref_for_pin (size_t pin_id, Net::pin_iterator iter)
m_pin_refs [pin_id] = iter;
}
void SubCircuit::set_circuit_ref (Circuit *c)
{
if (m_circuit_ref.get ()) {
m_circuit_ref->unregister_ref (this);
}
m_circuit_ref.reset (c);
if (m_circuit_ref.get ()) {
m_circuit_ref->register_ref (this);
}
}
const Net *SubCircuit::net_for_pin (size_t pin_id) const
{
if (pin_id < m_pin_refs.size ()) {
@ -349,8 +365,8 @@ const Pin *NetPinRef::pin () const
if (mp_net && mp_net->circuit ()) {
return mp_net->circuit ()->pin_by_id (m_pin_id);
}
} else if (mp_subcircuit->circuit ()) {
return mp_subcircuit->circuit ()->pin_by_id (m_pin_id);
} else if (mp_subcircuit->circuit_ref ()) {
return mp_subcircuit->circuit_ref ()->pin_by_id (m_pin_id);
}
return 0;
}
@ -613,6 +629,8 @@ void Circuit::remove_net (Net *net)
void Circuit::add_device (Device *device)
{
device->set_circuit (this);
size_t id = 0;
if (! m_devices.empty ()) {
tl_assert (m_devices.back () != 0);
@ -658,6 +676,8 @@ Device *Circuit::device_by_id (size_t id)
void Circuit::add_subcircuit (SubCircuit *subcircuit)
{
subcircuit->set_circuit (this);
size_t id = 0;
if (! m_subcircuits.empty ()) {
tl_assert (m_subcircuits.back () != 0);
@ -701,12 +721,22 @@ SubCircuit *Circuit::subcircuit_by_id (size_t id)
return d != m_subcircuit_id_table.end () ? d->second : 0;
}
void Circuit::register_ref (SubCircuit *r)
{
m_refs.push_back (r);
}
void Circuit::unregister_ref (SubCircuit *r)
{
m_refs.erase (r);
}
void Circuit::translate_circuits (const std::map<const Circuit *, Circuit *> &map)
{
for (subcircuit_iterator i = m_subcircuits.begin (); i != m_subcircuits.end (); ++i) {
std::map<const Circuit *, Circuit *>::const_iterator m = map.find (i->circuit ());
std::map<const Circuit *, Circuit *>::const_iterator m = map.find (i->circuit_ref ());
tl_assert (m != map.end ());
i->set_circuit (m->second);
i->set_circuit_ref (m->second);
}
}

View File

@ -608,6 +608,24 @@ public:
return m_id;
}
/**
* @brief Gets the circuit the device lives in (const version)
* This pointer is 0 if the device isn't added to a circuit
*/
const Circuit *circuit () const
{
return mp_circuit;
}
/**
* @brief Gets the circuit the device lives in (non-const version)
* This pointer is 0 if the device isn't added to a circuit
*/
Circuit *circuit ()
{
return mp_circuit;
}
/**
* @brief Sets the name
*/
@ -675,6 +693,7 @@ private:
std::vector<Net::terminal_iterator> m_terminal_refs;
std::vector<double> m_parameters;
size_t m_id;
Circuit *mp_circuit;
/**
* @brief Sets the terminal reference for a specific terminal
@ -696,6 +715,11 @@ private:
{
m_id = id;
}
/**
* @brief Sets the circuit
*/
void set_circuit (Circuit *circuit);
};
/**
@ -727,7 +751,7 @@ public:
/**
* @brief Creates a subcircuit reference to the given circuit
*/
SubCircuit (Circuit *circuit, const std::string &name = std::string ());
SubCircuit (Circuit *circuit_ref, const std::string &name = std::string ());
/**
* @brief Destructor
@ -746,19 +770,37 @@ public:
}
/**
* @brief Gets the circuit the reference points to (const version)
* @brief Gets the circuit the subcircuit lives in (const version)
* This pointer is 0 if the subcircuit isn't added to a circuit
*/
const Circuit *circuit () const
{
return m_circuit.get ();
return mp_circuit;
}
/**
* @brief Gets the circuit the subcircuit lives in (non-const version)
* This pointer is 0 if the subcircuit isn't added to a circuit
*/
Circuit *circuit ()
{
return mp_circuit;
}
/**
* @brief Gets the circuit the reference points to (const version)
*/
const Circuit *circuit_ref () const
{
return m_circuit_ref.get ();
}
/**
* @brief Gets the circuit the reference points to (non-const version)
*/
Circuit *circuit ()
Circuit *circuit_ref ()
{
return m_circuit.get ();
return m_circuit_ref.get ();
}
/**
@ -820,11 +862,12 @@ private:
friend class Circuit;
friend class Net;
tl::weak_ptr<Circuit> m_circuit;
tl::weak_ptr<Circuit> m_circuit_ref;
std::string m_name;
db::DCplxTrans m_trans;
std::vector<Net::pin_iterator> m_pin_refs;
size_t m_id;
Circuit *mp_circuit;
/**
* @brief Sets the pin reference for a specific pin
@ -834,9 +877,14 @@ private:
/**
* @brief Sets the circuit reference
*/
void set_circuit_ref (Circuit *c);
/**
* @brief Sets the circuit the subcircuit belongs to
*/
void set_circuit (Circuit *c)
{
m_circuit.reset (c);
mp_circuit = c;
}
/**
@ -870,6 +918,8 @@ public:
typedef tl::shared_collection<SubCircuit> subcircuit_list;
typedef subcircuit_list::const_iterator const_subcircuit_iterator;
typedef subcircuit_list::iterator subcircuit_iterator;
typedef tl::weak_collection<SubCircuit>::const_iterator const_refs_iterator;
typedef tl::weak_collection<SubCircuit>::iterator refs_iterator;
/**
* @brief Constructor
@ -939,9 +989,44 @@ public:
return m_cell_index;
}
/**
* @brief Gets the references to this circuit (begin, non-const version)
* This iterator will deliver all subcircuits referencing this circuit
*/
refs_iterator begin_refs ()
{
return m_refs.begin ();
}
/**
* @brief Gets the references to this circuit (end, non-const version)
* This iterator will deliver all subcircuits referencing this circuit
*/
refs_iterator end_refs ()
{
return m_refs.end ();
}
/**
* @brief Gets the references to this circuit (begin, const version)
* This iterator will deliver all subcircuits referencing this circuit
*/
const_refs_iterator begin_refs () const
{
return m_refs.begin ();
}
/**
* @brief Gets the references to this circuit (end, const version)
* This iterator will deliver all subcircuits referencing this circuit
*/
const_refs_iterator end_refs () const
{
return m_refs.end ();
}
/**
* @brief Adds a pin to this circuit
*
* The circuit takes over ownership of the object.
*/
const Pin &add_pin(const Pin &pin);
@ -1200,6 +1285,7 @@ public:
private:
friend class Netlist;
friend class Net;
friend class SubCircuit;
std::string m_name;
db::cell_index_type m_cell_index;
@ -1213,12 +1299,16 @@ private:
std::map<size_t, Device *> m_device_id_table;
bool m_valid_subcircuit_id_table;
std::map<size_t, SubCircuit *> m_subcircuit_id_table;
tl::weak_collection<SubCircuit> m_refs;
/**
* @brief Sets the pin reference for a specific pin
*/
void set_pin_ref_for_pin (size_t ppin_id, Net::pin_iterator iter);
void register_ref (SubCircuit *sc);
void unregister_ref (SubCircuit *sc);
void translate_circuits (const std::map<const Circuit *, Circuit *> &map);
void translate_device_classes (const std::map<const DeviceClass *, DeviceClass *> &map);
void set_netlist (Netlist *netlist);

View File

@ -46,10 +46,10 @@ NetlistDeviceExtractorError::NetlistDeviceExtractorError (const std::string &cel
// ----------------------------------------------------------------------------------------
// NetlistDeviceExtractor implementation
NetlistDeviceExtractor::NetlistDeviceExtractor ()
NetlistDeviceExtractor::NetlistDeviceExtractor (const std::string &name)
: mp_layout (0), m_cell_index (0), mp_circuit (0)
{
m_device_name_index = 0;
m_name = name;
m_propname_id = 0;
}
@ -67,8 +67,7 @@ const tl::Variant &NetlistDeviceExtractor::terminal_property_name ()
void NetlistDeviceExtractor::initialize (db::Netlist *nl)
{
m_layer_definitions.clear ();
m_device_classes.clear ();
m_device_name_index = 0;
mp_device_class = 0;
m_propname_id = 0;
m_netlist.reset (nl);
@ -108,16 +107,16 @@ void NetlistDeviceExtractor::extract (db::DeepShapeStore &dss, const NetlistDevi
}
extract_without_initialize (dss.layout (), dss.initial_cell (), layers, nl);
extract_without_initialize (dss.layout (), dss.initial_cell (), layers);
}
void NetlistDeviceExtractor::extract (db::Layout &layout, db::Cell &cell, const std::vector<unsigned int> &layers, db::Netlist *nl)
{
initialize (nl);
extract_without_initialize (layout, cell, layers, nl);
extract_without_initialize (layout, cell, layers);
}
void NetlistDeviceExtractor::extract_without_initialize (db::Layout &layout, db::Cell &cell, const std::vector<unsigned int> &layers, db::Netlist *nl)
void NetlistDeviceExtractor::extract_without_initialize (db::Layout &layout, db::Cell &cell, const std::vector<unsigned int> &layers)
{
tl_assert (layers.size () == m_layer_definitions.size ());
@ -216,9 +215,16 @@ void NetlistDeviceExtractor::extract_devices (const std::vector<db::Region> & /*
void NetlistDeviceExtractor::register_device_class (DeviceClass *device_class)
{
if (mp_device_class != 0) {
throw tl::Exception (tl::to_string (tr ("Device class already set")));
}
tl_assert (device_class != 0);
mp_device_class = device_class;
mp_device_class->set_name (m_name);
tl_assert (m_netlist.get () != 0);
m_netlist->add_device_class (device_class);
m_device_classes.push_back (device_class);
}
void NetlistDeviceExtractor::define_layer (const std::string &name, const std::string &description)
@ -226,11 +232,14 @@ void NetlistDeviceExtractor::define_layer (const std::string &name, const std::s
m_layer_definitions.push_back (db::NetlistDeviceExtractorLayerDefinition (name, description, m_layer_definitions.size ()));
}
Device *NetlistDeviceExtractor::create_device (unsigned int device_class_index)
Device *NetlistDeviceExtractor::create_device ()
{
if (mp_device_class == 0) {
throw tl::Exception (tl::to_string (tr ("No device class registered")));
}
tl_assert (mp_circuit != 0);
tl_assert (device_class_index < m_device_classes.size ());
Device *device = new Device (m_device_classes[device_class_index], tl::to_string (++m_device_name_index));
Device *device = new Device (mp_device_class);
mp_circuit->add_device (device);
return device;
}

View File

@ -196,16 +196,24 @@ public:
typedef std::map<std::string, db::Region *> input_layers;
/**
* @brief Default constructor
* @brief Constructor
*
* The name is the name of the device class of the devices generated.
*/
NetlistDeviceExtractor ();
NetlistDeviceExtractor (const std::string &name);
/**
* @brief Destructor
*/
~NetlistDeviceExtractor ();
// TODO: Do we need to declare input layers?
/**
* @brief Gets the name of the extractor and the device class
*/
const std::string &name ()
{
return m_name;
}
/**
* @brief Gets the property name for the device terminal annotation
@ -292,8 +300,7 @@ protected:
* defining the device classes and setting up the device layers.
*
* At least one device class needs to be defined. Use "register_device_class" to register
* the device classes you need. The first device class registered has device class index 0,
* the further ones 1, 2, etc.
* the device class you need.
*
* The device layers need to be defined by calling "define_layer" once or several times.
*/
@ -323,7 +330,8 @@ protected:
/**
* @brief Registers a device class
* The device class object will become owned by the netlist and must not be deleted by
* the caller.
* the caller. The name of the device class will be changed to the name given to
* the device extractor.
* This method shall be used inside the implementation of "setup" to register
* the device classes.
*/
@ -343,7 +351,7 @@ protected:
* The device object returned can be configured by the caller, e.g. set parameters.
* It will be owned by the netlist and must not be deleted by the caller.
*/
Device *create_device (unsigned int device_class_index = 0);
Device *create_device ();
/**
* @brief Defines a device terminal in the layout (a polygon)
@ -436,19 +444,23 @@ private:
db::properties_id_type m_propname_id;
db::cell_index_type m_cell_index;
db::Circuit *mp_circuit;
std::vector<db::DeviceClass *> m_device_classes;
db::DeviceClass *mp_device_class;
std::string m_name;
layer_definitions m_layer_definitions;
std::vector<unsigned int> m_layers;
unsigned int m_device_name_index;
error_list m_errors;
// no copying
NetlistDeviceExtractor (const NetlistDeviceExtractor &);
NetlistDeviceExtractor &operator= (const NetlistDeviceExtractor &);
/**
* @brief Initializes the extractor
* This method will produce the device classes required for the device extraction.
*/
void initialize (db::Netlist *nl);
void extract_without_initialize (db::Layout &layout, db::Cell &cell, const std::vector<unsigned int> &layers, db::Netlist *nl);
void extract_without_initialize (db::Layout &layout, db::Cell &cell, const std::vector<unsigned int> &layers);
};
}

View File

@ -51,6 +51,9 @@ 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"
) +
gsi::method ("circuit", (db::Circuit *(db::Device::*) ()) &db::Device::circuit,
"@brief Gets the circuit the device lives in."
) +
gsi::method ("id", &db::Device::id,
"@brief Gets the device ID.\n"
"The ID is a unique integer which identifies the device.\n"
@ -125,9 +128,13 @@ static void subcircuit_disconnect_pin1 (db::SubCircuit *subcircuit, const db::Pi
}
Class<db::SubCircuit> decl_dbSubCircuit ("db", "SubCircuit",
gsi::method ("circuit", (db::Circuit *(db::SubCircuit::*) ()) &db::SubCircuit::circuit,
gsi::method ("circuit_ref", (db::Circuit *(db::SubCircuit::*) ()) &db::SubCircuit::circuit_ref,
"@brief Gets the circuit referenced by the subcircuit.\n"
) +
gsi::method ("circuit", (db::Circuit *(db::SubCircuit::*) ()) &db::SubCircuit::circuit,
"@brief Gets the circuit the subcircuit lives in.\n"
"This is NOT the circuit which is referenced. For getting the circuit that the subcircuit references, use \\circuit_ref."
) +
gsi::method ("id", &db::SubCircuit::id,
"@brief Gets the subcircuit ID.\n"
"The ID is a unique integer which identifies the subcircuit.\n"
@ -620,6 +627,9 @@ Class<db::Circuit> decl_dbCircuit ("db", "Circuit",
"to the outside through such a pin. The pin is added after all existing "
"pins. For more details see the \\Pin class."
) +
gsi::iterator ("each_ref", (db::Circuit::refs_iterator (db::Circuit::*) ()) &db::Circuit::begin_refs, (db::Circuit::refs_iterator (db::Circuit::*) ()) &db::Circuit::end_refs,
"@brief Iterates over the subcircuit objects referencing this circuit\n"
) +
gsi::iterator ("each_pin", (db::Circuit::pin_iterator (db::Circuit::*) ()) &db::Circuit::begin_pins, (db::Circuit::pin_iterator (db::Circuit::*) ()) &db::Circuit::end_pins,
"@brief Iterates over the pins of the circuit"
) +

View File

@ -54,6 +54,7 @@ namespace {
{
public:
DummyDeviceExtractor ()
: db::NetlistDeviceExtractor (std::string ("DUMMY"))
{
error ("msg1");
error ("msg2", db::Box (0, 1, 2, 3));

View File

@ -44,172 +44,6 @@
#include <memory>
#include <limits>
class MOSFETExtractor
: public db::NetlistDeviceExtractor
{
public:
MOSFETExtractor (db::Layout *debug_out)
: db::NetlistDeviceExtractor (), mp_debug_out (debug_out), m_ldiff (0), m_lgate (0)
{
if (mp_debug_out) {
m_ldiff = mp_debug_out->insert_layer (db::LayerProperties (100, 0));
m_lgate = mp_debug_out->insert_layer (db::LayerProperties (101, 0));
}
}
virtual void setup ()
{
define_layer ("PD", "P diffusion");
define_layer ("ND", "N diffusion");
define_layer ("G", "Gate");
define_layer ("P", "Poly");
db::DeviceClassMOS3Transistor *pmos_class = new db::DeviceClassMOS3Transistor ();
pmos_class->set_name ("PMOS");
register_device_class (pmos_class);
db::DeviceClassMOS3Transistor *nmos_class = new db::DeviceClassMOS3Transistor ();
nmos_class->set_name ("NMOS");
register_device_class (nmos_class);
}
virtual db::Connectivity get_connectivity (const db::Layout & /*layout*/, const std::vector<unsigned int> &layers) const
{
tl_assert (layers.size () == 4);
unsigned int lpdiff = layers [0];
unsigned int lndiff = layers [1];
unsigned int gate = layers [2];
// not used for device recognition: poly (3), but used for producing the gate terminals
// The layer definition is pdiff, ndiff, gate
db::Connectivity conn;
// collect all connected pdiff
conn.connect (lpdiff, lpdiff);
// collect all connected ndiff
conn.connect (lndiff, lndiff);
// collect all connected gate shapes
conn.connect (gate, gate);
// connect gate with pdiff
conn.connect (lpdiff, gate);
// connect gate with ndiff
conn.connect (lndiff, gate);
return conn;
}
virtual void extract_devices (const std::vector<db::Region> &layer_geometry)
{
const db::Region &rpdiff = layer_geometry [0];
const db::Region &rndiff = layer_geometry [1];
const db::Region &rgates = layer_geometry [2];
for (db::Region::const_iterator p = rgates.begin_merged (); !p.at_end (); ++p) {
db::Region rgate (*p);
db::Region rpdiff_on_gate = rpdiff.selected_interacting (rgate);
db::Region rndiff_on_gate = rndiff.selected_interacting (rgate);
if (! rpdiff_on_gate.empty () && ! rndiff_on_gate.empty ()) {
error (tl::to_string (tr ("Gate shape touches both ndiff and pdiff - ignored")), *p);
} else if (rpdiff_on_gate.empty () && rndiff_on_gate.empty ()) {
error (tl::to_string (tr ("Gate shape touches neither ndiff and pdiff - ignored")), *p);
} else {
bool is_pmos = ! rpdiff_on_gate.empty ();
db::Region &diff = (is_pmos ? rpdiff_on_gate : rndiff_on_gate);
unsigned int terminal_geometry_index = (is_pmos ? 0 : 1);
unsigned int gate_geometry_index = 3;
unsigned int device_class_index = (is_pmos ? 0 /*PMOS*/ : 1 /*NMOS*/);
if (diff.size () != 2) {
error (tl::sprintf (tl::to_string (tr ("Expected two polygons on diff interacting one gate shape (found %d) - gate shape ignored")), int (diff.size ())), *p);
continue;
}
db::Edges edges (rgate.edges () & diff.edges ());
if (edges.size () != 2) {
error (tl::sprintf (tl::to_string (tr ("Expected two edges interacting gate/diff (found %d) - width and length may be incorrect")), int (edges.size ())), *p);
continue;
}
if (! p->is_box ()) {
error (tl::to_string (tr ("Gate shape is not a box - width and length may be incorrect")), *p);
}
db::Device *device = create_device (device_class_index);
device->set_parameter_value ("W", dbu () * edges.length () * 0.5);
device->set_parameter_value ("L", dbu () * (p->perimeter () - edges.length ()) * 0.5);
int diff_index = 0;
for (db::Region::const_iterator d = diff.begin (); !d.at_end () && diff_index < 2; ++d, ++diff_index) {
// count the number of gate shapes attached to this shape and distribute the area of the
// diffusion region to the number of gates
int n = rgates.selected_interacting (db::Region (*d)).size ();
tl_assert (n > 0);
device->set_parameter_value (diff_index == 0 ? "AS" : "AD", dbu () * dbu () * d->area () / double (n));
define_terminal (device, device->device_class ()->terminal_id_for_name (diff_index == 0 ? "S" : "D"), terminal_geometry_index, *d);
}
define_terminal (device, device->device_class ()->terminal_id_for_name ("G"), gate_geometry_index, *p);
// output the device for debugging
device_out (device, diff, rgate);
}
}
}
private:
db::Layout *mp_debug_out;
unsigned int m_ldiff, m_lgate;
void device_out (const db::Device *device, const db::Region &diff, const db::Region &gate)
{
if (! mp_debug_out) {
return;
}
std::string cn = layout ()->cell_name (cell_index ());
std::pair<bool, db::cell_index_type> target_cp = mp_debug_out->cell_by_name (cn.c_str ());
tl_assert (target_cp.first);
db::cell_index_type dci = mp_debug_out->add_cell ((device->device_class ()->name () + "_" + device->name ()).c_str ());
mp_debug_out->cell (target_cp.second).insert (db::CellInstArray (db::CellInst (dci), db::Trans ()));
db::Cell &device_cell = mp_debug_out->cell (dci);
for (db::Region::const_iterator p = diff.begin (); ! p.at_end (); ++p) {
device_cell.shapes (m_ldiff).insert (*p);
}
for (db::Region::const_iterator p = gate.begin (); ! p.at_end (); ++p) {
device_cell.shapes (m_lgate).insert (*p);
}
std::string ps;
const std::vector<db::DeviceParameterDefinition> &pd = device->device_class ()->parameter_definitions ();
for (std::vector<db::DeviceParameterDefinition>::const_iterator i = pd.begin (); i != pd.end (); ++i) {
if (! ps.empty ()) {
ps += ",";
}
ps += i->name () + "=" + tl::to_string (device->parameter_value (i->id ()));
}
device_cell.shapes (m_ldiff).insert (db::Text (ps, db::Trans (diff.bbox ().center () - db::Point ())));
}
};
static unsigned int define_layer (db::Layout &ly, db::LayerMap &lmap, int gds_layer, int gds_datatype = 0)
{
unsigned int lid = ly.insert_layer (db::LayerProperties (gds_layer, gds_datatype));
lmap.map (ly.get_properties (lid), lid);
return lid;
}
static unsigned int layer_of (const db::Region &region)
{
return db::DeepLayer (region).layer ();
@ -248,6 +82,153 @@ static std::string pin_name (const db::Pin &pin)
}
}
class MOSFETExtractor
: public db::NetlistDeviceExtractor
{
public:
MOSFETExtractor (const std::string &name, db::Layout *debug_out)
: db::NetlistDeviceExtractor (name), mp_debug_out (debug_out), m_ldiff (0), m_lgate (0)
{
if (mp_debug_out) {
m_ldiff = mp_debug_out->insert_layer (db::LayerProperties (100, 0));
m_lgate = mp_debug_out->insert_layer (db::LayerProperties (101, 0));
}
}
virtual void setup ()
{
define_layer ("SD", "Source/drain diffusion");
define_layer ("G", "Gate");
define_layer ("P", "Poly");
register_device_class (new db::DeviceClassMOS3Transistor ());
}
virtual db::Connectivity get_connectivity (const db::Layout & /*layout*/, const std::vector<unsigned int> &layers) const
{
tl_assert (layers.size () == 3);
unsigned int diff = layers [0];
unsigned int gate = layers [1];
// not used for device recognition: poly (2), but used for producing the gate terminals
// The layer definition is diff, gate
db::Connectivity conn;
// collect all connected diffusion shapes
conn.connect (diff, diff);
// collect all connected gate shapes
conn.connect (gate, gate);
// connect gate with diff to detect gate/diffusion boundary
conn.connect (diff, gate);
return conn;
}
virtual void extract_devices (const std::vector<db::Region> &layer_geometry)
{
const db::Region &rdiff = layer_geometry [0];
const db::Region &rgates = layer_geometry [1];
for (db::Region::const_iterator p = rgates.begin_merged (); !p.at_end (); ++p) {
db::Region rgate (*p);
db::Region rdiff2gate = rdiff.selected_interacting (rgate);
if (rdiff2gate.empty ()) {
error (tl::to_string (tr ("Gate shape touches no diffusion - ignored")), *p);
} else {
unsigned int terminal_geometry_index = 0;
unsigned int gate_geometry_index = 2;
if (rdiff2gate.size () != 2) {
error (tl::sprintf (tl::to_string (tr ("Expected two polygons on diff interacting one gate shape (found %d) - gate shape ignored")), int (rdiff2gate.size ())), *p);
continue;
}
db::Edges edges (rgate.edges () & rdiff2gate.edges ());
if (edges.size () != 2) {
error (tl::sprintf (tl::to_string (tr ("Expected two edges interacting gate/diff (found %d) - width and length may be incorrect")), int (edges.size ())), *p);
continue;
}
if (! p->is_box ()) {
error (tl::to_string (tr ("Gate shape is not a box - width and length may be incorrect")), *p);
}
db::Device *device = create_device ();
device->set_parameter_value ("W", dbu () * edges.length () * 0.5);
device->set_parameter_value ("L", dbu () * (p->perimeter () - edges.length ()) * 0.5);
int diff_index = 0;
for (db::Region::const_iterator d = rdiff2gate.begin (); !d.at_end () && diff_index < 2; ++d, ++diff_index) {
// count the number of gate shapes attached to this shape and distribute the area of the
// diffusion region to the number of gates
int n = rgates.selected_interacting (db::Region (*d)).size ();
tl_assert (n > 0);
device->set_parameter_value (diff_index == 0 ? "AS" : "AD", dbu () * dbu () * d->area () / double (n));
define_terminal (device, device->device_class ()->terminal_id_for_name (diff_index == 0 ? "S" : "D"), terminal_geometry_index, *d);
}
define_terminal (device, device->device_class ()->terminal_id_for_name ("G"), gate_geometry_index, *p);
// output the device for debugging
device_out (device, rdiff2gate, rgate);
}
}
}
private:
db::Layout *mp_debug_out;
unsigned int m_ldiff, m_lgate;
void device_out (const db::Device *device, const db::Region &diff, const db::Region &gate)
{
if (! mp_debug_out) {
return;
}
std::string cn = layout ()->cell_name (cell_index ());
std::pair<bool, db::cell_index_type> target_cp = mp_debug_out->cell_by_name (cn.c_str ());
tl_assert (target_cp.first);
db::cell_index_type dci = mp_debug_out->add_cell ((device->device_class ()->name () + "_" + device->circuit ()->name () + "_" + device_name (*device)).c_str ());
mp_debug_out->cell (target_cp.second).insert (db::CellInstArray (db::CellInst (dci), db::Trans ()));
db::Cell &device_cell = mp_debug_out->cell (dci);
for (db::Region::const_iterator p = diff.begin (); ! p.at_end (); ++p) {
device_cell.shapes (m_ldiff).insert (*p);
}
for (db::Region::const_iterator p = gate.begin (); ! p.at_end (); ++p) {
device_cell.shapes (m_lgate).insert (*p);
}
std::string ps;
const std::vector<db::DeviceParameterDefinition> &pd = device->device_class ()->parameter_definitions ();
for (std::vector<db::DeviceParameterDefinition>::const_iterator i = pd.begin (); i != pd.end (); ++i) {
if (! ps.empty ()) {
ps += ",";
}
ps += i->name () + "=" + tl::to_string (device->parameter_value (i->id ()));
}
device_cell.shapes (m_ldiff).insert (db::Text (ps, db::Trans (diff.bbox ().center () - db::Point ())));
}
};
static unsigned int define_layer (db::Layout &ly, db::LayerMap &lmap, int gds_layer, int gds_datatype = 0)
{
unsigned int lid = ly.insert_layer (db::LayerProperties (gds_layer, gds_datatype));
lmap.map (ly.get_properties (lid), lid);
return lid;
}
// @@@ TODO: move this somewhere else
static void dump_nets (const db::Netlist &nl, const db::hier_clusters<db::PolygonRef> &clusters, db::Layout &ly, const std::map<unsigned int, unsigned int> &lmap, const db::CellMapping &cmap)
@ -329,14 +310,15 @@ static std::string netlist2string (const db::Netlist &nl)
for (db::Circuit::const_subcircuit_iterator sc = c->begin_subcircuits (); sc != c->end_subcircuits (); ++sc) {
std::string ps;
const db::SubCircuit &subcircuit = *sc;
for (db::Circuit::const_pin_iterator p = sc->circuit ()->begin_pins (); p != sc->circuit ()->end_pins (); ++p) {
if (p != sc->circuit ()->begin_pins ()) {
const db::Circuit *circuit = sc->circuit_ref ();
for (db::Circuit::const_pin_iterator p = circuit->begin_pins (); p != circuit->end_pins (); ++p) {
if (p != circuit->begin_pins ()) {
ps += ",";
}
const db::Pin &pin = *p;
ps += pin_name (pin) + "=" + net_name (subcircuit.net_for_pin (pin.id ()));
}
res += std::string (" X") + sc->circuit ()->name () + " " + subcircuit_name (*sc) + " (" + ps + ")\n";
res += std::string (" X") + circuit->name () + " " + subcircuit_name (*sc) + " (" + ps + ")\n";
}
}
@ -344,7 +326,7 @@ static std::string netlist2string (const db::Netlist &nl)
return res;
}
TEST(2_DeviceAndNetExtraction)
TEST(1_DeviceAndNetExtraction)
{
db::Layout ly;
db::LayerMap lmap;
@ -396,10 +378,14 @@ TEST(2_DeviceAndNetExtraction)
db::Region rmetal2_lbl (db::RecursiveShapeIterator (ly, tc, metal2_lbl), dss);
// derived regions
db::Region rgate = ractive & rpoly;
db::Region rsd = ractive - rgate;
db::Region rpdiff = rsd & rnwell;
db::Region rndiff = rsd - rnwell;
db::Region rpactive = ractive & rnwell;
db::Region rpgate = rpactive & rpoly;
db::Region rpsd = rpactive - rpgate;
db::Region rnactive = ractive - rnwell;
db::Region rngate = rnactive & rpoly;
db::Region rnsd = rnactive - rngate;
// return the computed layers into the original layout and write it for debugging purposes
@ -408,10 +394,12 @@ TEST(2_DeviceAndNetExtraction)
unsigned int lpdiff = ly.insert_layer (db::LayerProperties (12, 0)); // 12/0 -> P Diffusion
unsigned int lndiff = ly.insert_layer (db::LayerProperties (13, 0)); // 13/0 -> N Diffusion
rgate.insert_into (&ly, tc.cell_index (), lgate);
rsd.insert_into (&ly, tc.cell_index (), lsd);
rpdiff.insert_into (&ly, tc.cell_index (), lpdiff);
rndiff.insert_into (&ly, tc.cell_index (), lndiff);
rpgate.insert_into (&ly, tc.cell_index (), lgate);
rngate.insert_into (&ly, tc.cell_index (), lgate);
rpsd.insert_into (&ly, tc.cell_index (), lsd);
rnsd.insert_into (&ly, tc.cell_index (), lsd);
rpsd.insert_into (&ly, tc.cell_index (), lpdiff);
rnsd.insert_into (&ly, tc.cell_index (), lndiff);
// perform the extraction
@ -420,15 +408,20 @@ TEST(2_DeviceAndNetExtraction)
// NOTE: the device extractor will add more debug layers for the transistors:
// 20/0 -> Diffusion
// 21/0 -> Gate
MOSFETExtractor ex (&ly);
MOSFETExtractor pmos_ex ("PMOS", &ly);
MOSFETExtractor nmos_ex ("NMOS", &ly);
db::NetlistDeviceExtractor::input_layers dl;
dl["PD"] = &rpdiff;
dl["ND"] = &rndiff;
dl["G"] = &rgate;
dl["P"] = &rpoly;
ex.extract (dss, dl, &nl);
dl["SD"] = &rpsd;
dl["G"] = &rpgate;
dl["P"] = &rpoly; // not needed for extraction but to return terminal shapes
pmos_ex.extract (dss, dl, &nl);
dl["SD"] = &rnsd;
dl["G"] = &rngate;
dl["P"] = &rpoly; // not needed for extraction but to return terminal shapes
nmos_ex.extract (dss, dl, &nl);
// perform the net extraction
@ -436,8 +429,8 @@ TEST(2_DeviceAndNetExtraction)
db::Connectivity conn;
// Intra-layer
conn.connect (rpdiff);
conn.connect (rndiff);
conn.connect (rpsd);
conn.connect (rnsd);
conn.connect (rpoly);
conn.connect (rdiff_cont);
conn.connect (rpoly_cont);
@ -445,8 +438,8 @@ TEST(2_DeviceAndNetExtraction)
conn.connect (rvia1);
conn.connect (rmetal2);
// Inter-layer
conn.connect (rpdiff, rdiff_cont);
conn.connect (rndiff, rdiff_cont);
conn.connect (rpsd, rdiff_cont);
conn.connect (rnsd, rdiff_cont);
conn.connect (rpoly, rpoly_cont);
conn.connect (rpoly_cont, rmetal1);
conn.connect (rdiff_cont, rmetal1);
@ -468,9 +461,11 @@ TEST(2_DeviceAndNetExtraction)
// 206/0 -> Metal1
// 207/0 -> Via1
// 208/0 -> Metal2
// 210/0 -> N source/drain
// 211/0 -> P source/drain
std::map<unsigned int, unsigned int> dump_map;
dump_map [layer_of (rpdiff) ] = ly.insert_layer (db::LayerProperties (210, 0));
dump_map [layer_of (rndiff) ] = ly.insert_layer (db::LayerProperties (211, 0));
dump_map [layer_of (rpsd) ] = ly.insert_layer (db::LayerProperties (210, 0));
dump_map [layer_of (rnsd) ] = ly.insert_layer (db::LayerProperties (211, 0));
dump_map [layer_of (rpoly) ] = ly.insert_layer (db::LayerProperties (203, 0));
dump_map [layer_of (rdiff_cont)] = ly.insert_layer (db::LayerProperties (204, 0));
dump_map [layer_of (rpoly_cont)] = ly.insert_layer (db::LayerProperties (205, 0));
@ -496,10 +491,10 @@ TEST(2_DeviceAndNetExtraction)
" XINV2 $9 ($1=$I6,$2=$I45,$3=$I7,$4=VSS,$5=VDD)\n"
" XINV2 $10 ($1=$I7,$2=$I46,$3=$I8,$4=VSS,$5=VDD)\n"
"Circuit INV2 ($1=IN,$2=$2,$3=OUT,$4=$4,$5=$5):\n"
" DPMOS 1 (S=$2,G=IN,D=$5) [L=0.25,W=0.95,AS=0.49875,AD=0.26125]\n"
" DPMOS 2 (S=$5,G=$2,D=OUT) [L=0.25,W=0.95,AS=0.26125,AD=0.49875]\n"
" DNMOS 3 (S=$2,G=IN,D=$4) [L=0.25,W=0.95,AS=0.49875,AD=0.26125]\n"
" DNMOS 4 (S=$4,G=$2,D=OUT) [L=0.25,W=0.95,AS=0.26125,AD=0.49875]\n"
" DPMOS $1 (S=$2,G=IN,D=$5) [L=0.25,W=0.95,AS=0.49875,AD=0.26125]\n"
" DPMOS $2 (S=$5,G=$2,D=OUT) [L=0.25,W=0.95,AS=0.26125,AD=0.49875]\n"
" DNMOS $3 (S=$2,G=IN,D=$4) [L=0.25,W=0.95,AS=0.49875,AD=0.26125]\n"
" DNMOS $4 (S=$4,G=$2,D=OUT) [L=0.25,W=0.95,AS=0.26125,AD=0.49875]\n"
" XTRANS $1 ($1=$2,$2=$4,$3=IN)\n"
" XTRANS $2 ($1=$2,$2=$5,$3=IN)\n"
" XTRANS $3 ($1=$5,$2=OUT,$3=$2)\n"
@ -523,19 +518,9 @@ TEST(2_DeviceAndNetExtraction)
// An attempt to simplify things.
/*
- layers: use db::Region, or wrapper?
-> use regions, but test whether they are deep regions (?)
TODO:
- netlist query functions such as net_by_name, device_by_name, circuit_by_name
- terminal geometry (Polygon) for device, combined device geometry (all terminals)
- error interface for device extraction
// gets the device extraction errors
// device_extraction_error_iterator begin_device_extraction_errors () const;
// device_extraction_error_iterator end_device_extraction_errors () const;
// bool has_device_extraction_errors () const;
- device extractor needs to declare the layers to allow passing them by name
- netlist manipulation methods (i.e. flatten certain cells, purging etc.)
*/
@ -551,23 +536,25 @@ public:
// the iterator provides the hierarchical selection (enabling/disabling cells etc.)
LayoutToNetlist (const db::RecursiveShapeIterator &iter);
// --- preparation
// --- Step 0: configuration
// returns a new'd region
void set_threads (unsigned int n);
void set_area_ratio (double ar);
void set_max_vertex_count (size_t n);
// --- Step 1: preparation
// returns new'd regions
db::Region *make_layer (unsigned int layer_index);
db::Region *make_text_layer (unsigned int layer_index);
db::Region *make_polygon_layer (unsigned int layer_index);
// gets the internal layout and cell
const db::Layout &internal_layout () const;
const db::Cell &internal_top_cell () const;
// --- device extraction
// --- Step 2: device extraction
// after this, the device extractor will have errors if some occured.
void extract_devices (db::NetlistDeviceExtractor *extractor, const std::map<std::string, const db::Region *> &layers);
void extract_devices (db::NetlistDeviceExtractor &extractor, const std::map<std::string, db::Region *> &layers);
// --- net extraction
// --- Step 3: net extraction
// define connectivity for the netlist extraction
void connect (const db::Region &l);
@ -576,7 +563,11 @@ public:
// runs the netlist extraction
void extract_netlist ();
// --- retrieval
// --- Step 4: retrieval
// gets the internal layout and cell (0 if not available)
const db::Layout *internal_layout () const;
const db::Cell *internal_top_cell () const;
// gets the internal layer index of the given region
unsigned int layer_of (const db::Region &region) const;
@ -600,6 +591,11 @@ public:
// copies the shapes of the given net from a given layer
// (recursive true: include nets from subcircuits)
db::Region shapes_of_net (const db::Net &net, const db::Region &of_layer, bool recursive);
// finds the net by probing a specific location on the given layer looking through the
// hierarchy. Returns 0 if no net was found.
db::Net *probe_net (const db::Region &of_region, const db::DPoint &point);
db::Net *probe_net (const db::Region &of_region, const db::Point &point);
};
}

View File

@ -165,7 +165,7 @@ static std::string net2string (const db::Net &n)
res += ",";
}
if (i->subcircuit ()) {
res += i->subcircuit ()->circuit () ? i->subcircuit ()->circuit ()->name () : "(null)";
res += i->subcircuit ()->circuit_ref () ? i->subcircuit ()->circuit_ref ()->name () : "(null)";
res += ":";
} else {
res += "+";
@ -221,15 +221,15 @@ static std::string netlist2 (const db::Circuit &c)
}
for (db::Circuit::const_subcircuit_iterator s = c.begin_subcircuits (); s != c.end_subcircuits (); ++s) {
if (! s->circuit ()) {
if (! s->circuit_ref ()) {
continue;
}
pins.clear ();
for (size_t i = 0; i < s->circuit ()->pin_count (); ++i) {
for (size_t i = 0; i < s->circuit_ref ()->pin_count (); ++i) {
if (! pins.empty ()) {
pins += ",";
}
pins += s->circuit ()->pin_by_id (i)->name ();
pins += s->circuit_ref ()->pin_by_id (i)->name ();
pins += "=";
const db::Net *net = s->net_for_pin (i);
pins += net ? net->name () : std::string ("(null)");
@ -268,7 +268,11 @@ TEST(4_CircuitDevices)
db::Device *d1 = new db::Device (&dc1, "d1");
db::Device *d2a = new db::Device (&dc2, "d2a");
db::Device *d2b = new db::Device (&dc2, "d2b");
EXPECT_EQ (d1->circuit () == 0, true);
c->add_device (d1);
EXPECT_EQ (d1->circuit () == c.get (), true);
EXPECT_EQ (d1->id (), size_t (1));
EXPECT_EQ (c->device_by_id (d1->id ()) == d1, true);
c->add_device (dd);
@ -386,6 +390,18 @@ static std::string netlist2 (const db::Netlist &nl)
return res;
}
static std::string refs2string (const db::Circuit *c)
{
std::string res;
for (db::Circuit::const_refs_iterator r = c->begin_refs (); r != c->end_refs (); ++r) {
if (!res.empty ()) {
res += ",";
}
res += r->name ();
}
return res;
}
TEST(4_NetlistSubcircuits)
{
std::auto_ptr<db::Netlist> nl (new db::Netlist ());
@ -413,14 +429,19 @@ TEST(4_NetlistSubcircuits)
db::Device *d = new db::Device (dc, "D");
c2->add_device (d);
EXPECT_EQ (refs2string (c2), "");
db::SubCircuit *sc1 = new db::SubCircuit (c2);
sc1->set_name ("sc1");
EXPECT_EQ (refs2string (c2), "sc1");
EXPECT_EQ (sc1->circuit () == 0, true);
c1->add_subcircuit (sc1);
EXPECT_EQ (sc1->circuit () == c1, true);
EXPECT_EQ (sc1->id (), size_t (1));
EXPECT_EQ (c1->subcircuit_by_id (sc1->id ()) == sc1, true);
db::SubCircuit *sc2 = new db::SubCircuit (c2);
sc2->set_name ("sc2");
EXPECT_EQ (refs2string (c2), "sc1,sc2");
c1->add_subcircuit (sc2);
EXPECT_EQ (sc2->id (), size_t (2));
EXPECT_EQ (c1->subcircuit_by_id (sc2->id ()) == sc2, true);
@ -778,3 +799,38 @@ TEST(10_NetPinRefBasics)
EXPECT_EQ (db::NetPinRef (&d1, 1) < db::NetPinRef (&d1, 0), false);
EXPECT_NE ((db::NetPinRef (&d1, 0) < db::NetPinRef (&d2, 0)), (db::NetPinRef (&d2, 0) < db::NetPinRef (&d1, 0)));
}
TEST(11_NetlistCircuitRefs)
{
std::auto_ptr<db::Netlist> nl (new db::Netlist ());
db::Circuit *c1 = new db::Circuit ();
c1->set_name ("c1");
nl->add_circuit (c1);
db::Circuit *c2 = new db::Circuit ();
c2->set_name ("c2");
nl->add_circuit (c2);
db::SubCircuit *sc1 = new db::SubCircuit (c2);
sc1->set_name ("sc1");
EXPECT_EQ (refs2string (c2), "sc1");
c1->add_subcircuit (sc1);
db::SubCircuit *sc2 = new db::SubCircuit (c2);
sc2->set_name ("sc2");
EXPECT_EQ (refs2string (c2), "sc1,sc2");
c1->add_subcircuit (sc2);
*sc2 = db::SubCircuit ();
EXPECT_EQ (refs2string (c2), "sc1");
db::SubCircuit sc3 (c2);
sc3.set_name ("sc3");
*sc2 = sc3;
sc2->set_name ("sc2");
EXPECT_EQ (refs2string (c2), "sc1,sc3,sc2");
sc3 = db::SubCircuit ();
EXPECT_EQ (refs2string (c2), "sc1,sc2");
}

Binary file not shown.

View File

@ -258,22 +258,35 @@ class DBNetlist_TestClass < TestBase
sc1 = c.create_subcircuit(cc)
sc1.name = "SC1"
assert_equal(sc1.circuit.object_id, c.object_id)
assert_equal(sc1.name, "SC1")
assert_equal(sc1.circuit.name, "CC")
assert_equal(sc1.circuit_ref.name, "CC")
assert_equal(c.subcircuit_by_id(sc1.id).id, 1)
assert_equal(c.subcircuit_by_id(2).inspect, "nil")
refs = []
cc.each_ref { |r| refs << r.name }
assert_equal(refs.join(","), "SC1")
sc2 = c.create_subcircuit(cc)
sc2.name = "SC2"
refs = []
cc.each_ref { |r| refs << r.name }
assert_equal(refs.join(","), "SC1,SC2")
names = []
ccn = []
c.each_subcircuit { |sc| names << sc.name; ccn << sc.circuit.name }
c.each_subcircuit { |sc| names << sc.name; ccn << sc.circuit_ref.name }
assert_equal(names, [ "SC1", "SC2" ])
assert_equal(ccn, [ "CC", "CC" ])
c.remove_subcircuit(sc2)
refs = []
cc.each_ref { |r| refs << r.name }
assert_equal(refs.join(","), "SC1")
names = []
c.each_subcircuit { |sc| names << sc.name}
assert_equal(names, [ "SC1" ])
@ -282,7 +295,7 @@ class DBNetlist_TestClass < TestBase
names = []
ccn = []
c.each_subcircuit { |sc| names << sc.name; ccn << sc.circuit.name }
c.each_subcircuit { |sc| names << sc.name; ccn << sc.circuit_ref.name }
assert_equal(names, [ "SC1", "SC2" ])
assert_equal(ccn, [ "CC", "CC" ])
@ -531,6 +544,8 @@ class DBNetlist_TestClass < TestBase
nl.add(c)
d1 = c.create_device(dc)
assert_equal(d1.circuit.object_id, c.object_id)
assert_equal(d1.parameter(0), 1.0)
assert_equal(d1.parameter("U"), 1.0)
assert_equal(d1.parameter(1), 2.0)