mirror of https://github.com/KLayout/klayout.git
WIP: global nets integration in cluster builder.
This commit is contained in:
parent
a4f0fd665e
commit
c80e335cd6
|
|
@ -104,17 +104,8 @@ Connectivity::end_global_connections (unsigned int l) const
|
|||
size_t
|
||||
Connectivity::connect_global (unsigned int l, const std::string &gn)
|
||||
{
|
||||
for (std::vector<std::string>::const_iterator i = m_global_net_names.begin (); i != m_global_net_names.end (); ++i) {
|
||||
if (*i == gn) {
|
||||
size_t id = i - m_global_net_names.begin ();
|
||||
m_global_connections [l].insert (id);
|
||||
return id;
|
||||
}
|
||||
}
|
||||
|
||||
size_t id = m_global_net_names.size ();
|
||||
size_t id = global_net_id (gn);
|
||||
m_global_connections [l].insert (id);
|
||||
m_global_net_names.push_back (gn);
|
||||
return id;
|
||||
}
|
||||
|
||||
|
|
@ -131,6 +122,21 @@ Connectivity::global_net_name (size_t id) const
|
|||
return m_global_net_names [id];
|
||||
}
|
||||
|
||||
size_t
|
||||
Connectivity::global_net_id (const std::string &gn)
|
||||
{
|
||||
for (std::vector<std::string>::const_iterator i = m_global_net_names.begin (); i != m_global_net_names.end (); ++i) {
|
||||
if (*i == gn) {
|
||||
size_t id = i - m_global_net_names.begin ();
|
||||
return id;
|
||||
}
|
||||
}
|
||||
|
||||
size_t id = m_global_net_names.size ();
|
||||
m_global_net_names.push_back (gn);
|
||||
return id;
|
||||
}
|
||||
|
||||
Connectivity::layer_iterator
|
||||
Connectivity::begin_layers () const
|
||||
{
|
||||
|
|
@ -225,6 +231,7 @@ local_cluster<T>::clear ()
|
|||
m_size = 0;
|
||||
m_bbox = box_type ();
|
||||
m_attrs.clear ();
|
||||
m_global_nets.clear ();
|
||||
}
|
||||
|
||||
template <class T>
|
||||
|
|
@ -262,6 +269,7 @@ local_cluster<T>::join_with (const local_cluster<T> &other)
|
|||
}
|
||||
|
||||
m_attrs.insert (other.m_attrs.begin (), other.m_attrs.end ());
|
||||
m_global_nets.insert (other.m_global_nets.begin (), other.m_global_nets.end ());
|
||||
m_size += other.size ();
|
||||
|
||||
m_needs_update = true;
|
||||
|
|
@ -1450,95 +1458,7 @@ private:
|
|||
*/
|
||||
ClusterInstance make_path (id_type id, const std::vector<db::InstElement> &path) const
|
||||
{
|
||||
std::vector<db::InstElement>::const_iterator p = path.end ();
|
||||
tl_assert (p != path.begin ());
|
||||
|
||||
while (true) {
|
||||
|
||||
--p;
|
||||
|
||||
ClusterInstance ci (id, *p);
|
||||
if (p == path.begin ()) {
|
||||
|
||||
// if we're attaching to a child which is root yet, we need to promote the
|
||||
// cluster to the parent in all places
|
||||
connected_clusters<T> &child_cc = mp_tree->clusters_per_cell (p->inst_ptr.cell_index ());
|
||||
if (child_cc.is_root (id)) {
|
||||
|
||||
const db::Cell &child_cell = mp_layout->cell (p->inst_ptr.cell_index ());
|
||||
for (db::Cell::parent_inst_iterator pi = child_cell.begin_parent_insts (); ! pi.at_end (); ++pi) {
|
||||
|
||||
connected_clusters<T> &parent_cc = mp_tree->clusters_per_cell (pi->parent_cell_index ());
|
||||
for (db::CellInstArray::iterator pii = pi->child_inst ().begin (); ! pii.at_end (); ++pii) {
|
||||
|
||||
ClusterInstance ci2 (id, db::InstElement (pi->child_inst (), pii));
|
||||
if (mp_cell->cell_index () != pi->parent_cell_index () || ci != ci2) {
|
||||
|
||||
id_type id_dummy = parent_cc.insert_dummy ();
|
||||
parent_cc.add_connection (id_dummy, ci2);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
child_cc.reset_root (id);
|
||||
|
||||
}
|
||||
|
||||
return ci;
|
||||
|
||||
}
|
||||
|
||||
db::cell_index_type pci = p [-1].inst_ptr.cell_index ();
|
||||
connected_clusters<T> &target_cc = mp_tree->clusters_per_cell (pci);
|
||||
id_type parent_cluster = target_cc.find_cluster_with_connection (ci);
|
||||
|
||||
if (parent_cluster > 0) {
|
||||
|
||||
// taken parent
|
||||
id = parent_cluster;
|
||||
|
||||
} else {
|
||||
|
||||
id_type id_new = 0;
|
||||
|
||||
// if we're attaching to a child which is root yet, we need to promote the
|
||||
// cluster to the parent in all places
|
||||
connected_clusters<T> &child_cc = mp_tree->clusters_per_cell (p->inst_ptr.cell_index ());
|
||||
if (child_cc.is_root (id)) {
|
||||
|
||||
const db::Cell &child_cell = mp_layout->cell (p->inst_ptr.cell_index ());
|
||||
for (db::Cell::parent_inst_iterator pi = child_cell.begin_parent_insts (); ! pi.at_end (); ++pi) {
|
||||
|
||||
connected_clusters<T> &parent_cc = mp_tree->clusters_per_cell (pi->parent_cell_index ());
|
||||
for (db::CellInstArray::iterator pii = pi->child_inst ().begin (); ! pii.at_end (); ++pii) {
|
||||
|
||||
id_type id_dummy = parent_cc.insert_dummy ();
|
||||
ClusterInstance ci2 (id, db::InstElement (pi->child_inst (), pii));
|
||||
parent_cc.add_connection (id_dummy, ci2);
|
||||
|
||||
if (pci == pi->parent_cell_index () && ci == ci2) {
|
||||
id_new = id_dummy;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
child_cc.reset_root (id);
|
||||
|
||||
}
|
||||
|
||||
// no parent -> create vertical connector
|
||||
id = id_new;
|
||||
tl_assert (id != 0);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return mp_tree->make_path (*mp_layout, *mp_cell, id, path);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -1565,6 +1485,120 @@ private:
|
|||
|
||||
}
|
||||
|
||||
template <class T>
|
||||
ClusterInstance
|
||||
hier_clusters<T>::make_path (const db::Layout &layout, const db::Cell &cell, size_t id, const std::vector<db::InstElement> &path)
|
||||
{
|
||||
std::vector<db::InstElement>::const_iterator p = path.end ();
|
||||
tl_assert (p != path.begin ());
|
||||
|
||||
while (true) {
|
||||
|
||||
--p;
|
||||
|
||||
ClusterInstance ci (id, *p);
|
||||
if (p == path.begin ()) {
|
||||
|
||||
// if we're attaching to a child which is root yet, we need to promote the
|
||||
// cluster to the parent in all places
|
||||
connected_clusters<T> &child_cc = clusters_per_cell (p->inst_ptr.cell_index ());
|
||||
if (child_cc.is_root (id)) {
|
||||
|
||||
const db::Cell &child_cell = layout.cell (p->inst_ptr.cell_index ());
|
||||
for (db::Cell::parent_inst_iterator pi = child_cell.begin_parent_insts (); ! pi.at_end (); ++pi) {
|
||||
|
||||
connected_clusters<T> &parent_cc = clusters_per_cell (pi->parent_cell_index ());
|
||||
for (db::CellInstArray::iterator pii = pi->child_inst ().begin (); ! pii.at_end (); ++pii) {
|
||||
|
||||
ClusterInstance ci2 (id, db::InstElement (pi->child_inst (), pii));
|
||||
if (cell.cell_index () != pi->parent_cell_index () || ci != ci2) {
|
||||
|
||||
size_t id_dummy;
|
||||
|
||||
const typename db::local_cluster<T>::global_nets &gn = child_cc.cluster_by_id (id).get_global_nets ();
|
||||
if (gn.empty ()) {
|
||||
id_dummy = parent_cc.insert_dummy ();
|
||||
} else {
|
||||
local_cluster<T> *lc = parent_cc.insert ();
|
||||
lc->set_global_nets (gn);
|
||||
id_dummy = lc->id ();
|
||||
}
|
||||
|
||||
parent_cc.add_connection (id_dummy, ci2);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
child_cc.reset_root (id);
|
||||
|
||||
}
|
||||
|
||||
return ci;
|
||||
|
||||
}
|
||||
|
||||
db::cell_index_type pci = p [-1].inst_ptr.cell_index ();
|
||||
connected_clusters<T> &target_cc = clusters_per_cell (pci);
|
||||
size_t parent_cluster = target_cc.find_cluster_with_connection (ci);
|
||||
|
||||
if (parent_cluster > 0) {
|
||||
|
||||
// taken parent
|
||||
id = parent_cluster;
|
||||
|
||||
} else {
|
||||
|
||||
size_t id_new = 0;
|
||||
|
||||
// if we're attaching to a child which is root yet, we need to promote the
|
||||
// cluster to the parent in all places
|
||||
connected_clusters<T> &child_cc = clusters_per_cell (p->inst_ptr.cell_index ());
|
||||
if (child_cc.is_root (id)) {
|
||||
|
||||
const db::Cell &child_cell = layout.cell (p->inst_ptr.cell_index ());
|
||||
for (db::Cell::parent_inst_iterator pi = child_cell.begin_parent_insts (); ! pi.at_end (); ++pi) {
|
||||
|
||||
connected_clusters<T> &parent_cc = clusters_per_cell (pi->parent_cell_index ());
|
||||
for (db::CellInstArray::iterator pii = pi->child_inst ().begin (); ! pii.at_end (); ++pii) {
|
||||
|
||||
size_t id_dummy;
|
||||
|
||||
const typename db::local_cluster<T>::global_nets &gn = child_cc.cluster_by_id (id).get_global_nets ();
|
||||
if (gn.empty ()) {
|
||||
id_dummy = parent_cc.insert_dummy ();
|
||||
} else {
|
||||
local_cluster<T> *lc = parent_cc.insert ();
|
||||
lc->set_global_nets (gn);
|
||||
id_dummy = lc->id ();
|
||||
}
|
||||
|
||||
ClusterInstance ci2 (id, db::InstElement (pi->child_inst (), pii));
|
||||
parent_cc.add_connection (id_dummy, ci2);
|
||||
|
||||
if (pci == pi->parent_cell_index () && ci == ci2) {
|
||||
id_new = id_dummy;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
child_cc.reset_root (id);
|
||||
|
||||
}
|
||||
|
||||
// no parent -> create vertical connector
|
||||
id = id_new;
|
||||
tl_assert (id != 0);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
template <class T>
|
||||
void
|
||||
hier_clusters<T>::do_build (cell_clusters_box_converter<T> &cbc, const db::Layout &layout, const db::Cell &cell, db::ShapeIterator::flags_type shape_flags, const db::Connectivity &conn)
|
||||
|
|
@ -1648,6 +1682,83 @@ hier_clusters<T>::build_hier_connections_for_cells (cell_clusters_box_converter<
|
|||
}
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
class GlobalNetClusterMaker
|
||||
{
|
||||
public:
|
||||
typedef std::pair<std::set<size_t>, std::set<ClusterInstance> > entry_type;
|
||||
typedef std::list<entry_type> entry_list;
|
||||
typedef entry_list::const_iterator entry_iterator;
|
||||
|
||||
void
|
||||
add (const std::set<size_t> &global_nets, size_t cluster_id)
|
||||
{
|
||||
add (global_nets, ClusterInstance (cluster_id, db::InstElement ()));
|
||||
}
|
||||
|
||||
void
|
||||
add (const std::set<size_t> &global_nets, const ClusterInstance &inst)
|
||||
{
|
||||
if (global_nets.empty ()) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::set<size_t>::const_iterator g0 = global_nets.begin ();
|
||||
|
||||
std::map<size_t, entry_list::iterator>::iterator k = m_global_net_to_entries.find (*g0);
|
||||
if (k == m_global_net_to_entries.end ()) {
|
||||
|
||||
m_entries.push_back (entry_type ());
|
||||
m_entries.back ().first.insert (*g0);
|
||||
|
||||
k = m_global_net_to_entries.insert (std::make_pair (*g0, --m_entries.end ())).first;
|
||||
|
||||
}
|
||||
|
||||
k->second->second.insert (inst);
|
||||
|
||||
for (std::set<size_t>::const_iterator g = ++g0; g != global_nets.end (); ++g) {
|
||||
|
||||
std::map<size_t, entry_list::iterator>::iterator j = m_global_net_to_entries.find (*g);
|
||||
if (j == m_global_net_to_entries.end ()) {
|
||||
|
||||
k->second->first.insert (*g);
|
||||
k->second->second.insert (inst);
|
||||
|
||||
m_global_net_to_entries.insert (std::make_pair (*g, k->second));
|
||||
|
||||
} else if (k->second != j->second) {
|
||||
|
||||
// joining required
|
||||
k->second->first.insert (j->second->first.begin (), j->second->first.end ());
|
||||
k->second->second.insert (j->second->second.begin (), j->second->second.end ());
|
||||
|
||||
m_entries.erase (j->second);
|
||||
j->second = k->second;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
entry_iterator begin () const
|
||||
{
|
||||
return m_entries.begin ();
|
||||
}
|
||||
|
||||
entry_iterator end () const
|
||||
{
|
||||
return m_entries.end ();
|
||||
}
|
||||
|
||||
private:
|
||||
entry_list m_entries;
|
||||
std::map<size_t, entry_list::iterator> m_global_net_to_entries;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
template <class T>
|
||||
void
|
||||
hier_clusters<T>::build_hier_connections (cell_clusters_box_converter<T> &cbc, const db::Layout &layout, const db::Cell &cell, const db::Connectivity &conn)
|
||||
|
|
@ -1662,7 +1773,7 @@ hier_clusters<T>::build_hier_connections (cell_clusters_box_converter<T> &cbc, c
|
|||
|
||||
// NOTE: this is a receiver for both the child-to-child and
|
||||
// local to child interactions.
|
||||
hc_receiver<T> rec (layout, cell, local, *this, cbc, conn);
|
||||
std::auto_ptr<hc_receiver<T> > rec (new hc_receiver<T> (layout, cell, local, *this, cbc, conn));
|
||||
cell_inst_clusters_box_converter<T> cibc (cbc);
|
||||
|
||||
// The box scanner needs pointers so we have to first store the instances
|
||||
|
|
@ -1694,7 +1805,7 @@ hier_clusters<T>::build_hier_connections (cell_clusters_box_converter<T> &cbc, c
|
|||
bs.insert (inst.operator-> (), 0);
|
||||
}
|
||||
|
||||
bs.process (rec, 1 /*touching*/, cibc);
|
||||
bs.process (*rec, 1 /*touching*/, cibc);
|
||||
}
|
||||
|
||||
// handle local to instance connections
|
||||
|
|
@ -1729,11 +1840,79 @@ hier_clusters<T>::build_hier_connections (cell_clusters_box_converter<T> &cbc, c
|
|||
bs2.insert2 (inst.operator-> (), 0);
|
||||
}
|
||||
|
||||
bs2.process (rec, 1 /*touching*/, local_cluster_box_convert<T> (), cibc);
|
||||
bs2.process (*rec, 1 /*touching*/, local_cluster_box_convert<T> (), cibc);
|
||||
}
|
||||
|
||||
// finally join local clusters which got connected by child clusters
|
||||
rec.join_superclusters ();
|
||||
// join local clusters which got connected by child clusters
|
||||
rec->join_superclusters ();
|
||||
rec.reset (0);
|
||||
|
||||
// finally connect global nets
|
||||
{
|
||||
static std::string desc = tl::to_string (tr ("Global net treatment"));
|
||||
tl::SelfTimer timer (tl::verbosity () >= 51, desc);
|
||||
|
||||
GlobalNetClusterMaker global_net_clusters;
|
||||
|
||||
// insert the global nets from the subcircuits which need connection
|
||||
|
||||
for (std::vector<db::Instance>::const_iterator inst = inst_storage.begin (); inst != inst_storage.end (); ++inst) {
|
||||
|
||||
const db::connected_clusters<T> &cc = m_per_cell_clusters [inst->cell_index ()];
|
||||
for (typename db::connected_clusters<T>::const_iterator cl = cc.begin (); cl != cc.end (); ++cl) {
|
||||
|
||||
if (! cl->get_global_nets ().empty () && cc.is_root (cl->id ())) {
|
||||
for (db::Instance::cell_inst_array_type::iterator i = inst->begin (); !i.at_end (); ++i) {
|
||||
global_net_clusters.add (cl->get_global_nets (), db::ClusterInstance (cl->id (), db::InstElement (*inst, i)));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// insert the global nets from here
|
||||
|
||||
for (typename db::connected_clusters<T>::const_iterator cl = local.begin (); cl != local.end (); ++cl) {
|
||||
if (! cl->get_global_nets ().empty ()) {
|
||||
global_net_clusters.add (cl->get_global_nets (), db::ClusterInstance (cl->id (), db::InstElement ()));
|
||||
}
|
||||
}
|
||||
|
||||
// now global_net_clusters knows what clusters need to be made for the global nets
|
||||
|
||||
for (GlobalNetClusterMaker::entry_iterator ge = global_net_clusters.begin (); ge != global_net_clusters.end (); ++ge) {
|
||||
|
||||
db::local_cluster<T> *gc = local.insert ();
|
||||
gc->set_global_nets (ge->first);
|
||||
|
||||
for (std::set<ClusterInstance>::const_iterator ci = ge->second.begin (); ci != ge->second.end (); ++ci) {
|
||||
|
||||
if (ci->inst ().array_inst.at_end ()) {
|
||||
|
||||
local.join_cluster_with (gc->id (), ci->id ());
|
||||
local.remove_cluster (ci->id ());
|
||||
|
||||
} else {
|
||||
|
||||
std::vector<db::InstElement> p;
|
||||
p.push_back (ci->inst ());
|
||||
ClusterInstance k = make_path (layout, cell, ci->id (), p);
|
||||
|
||||
size_t other_id = local.find_cluster_with_connection (k);
|
||||
if (other_id) {
|
||||
local.join_cluster_with (gc->id (), other_id);
|
||||
local.remove_cluster (other_id);
|
||||
} else {
|
||||
local.add_connection (gc->id (), k);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
template <class T>
|
||||
|
|
|
|||
|
|
@ -99,6 +99,11 @@ public:
|
|||
*/
|
||||
const std::string &global_net_name (size_t id) const;
|
||||
|
||||
/**
|
||||
* @brief Gets the global net ID for the given name
|
||||
*/
|
||||
size_t global_net_id (const std::string &gn);
|
||||
|
||||
/**
|
||||
* @brief Begin iterator for the layers involved
|
||||
*/
|
||||
|
|
@ -306,6 +311,14 @@ public:
|
|||
return m_global_nets.end ();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Gets the global nets set
|
||||
*/
|
||||
const global_nets &get_global_nets () const
|
||||
{
|
||||
return m_global_nets;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Sets the global nets
|
||||
*/
|
||||
|
|
@ -767,6 +780,14 @@ public:
|
|||
*/
|
||||
void clear ();
|
||||
|
||||
/**
|
||||
* @brief Makes a valid path to a child cluster
|
||||
*
|
||||
* Cluster connections can only cross one level of hierarchy. This method
|
||||
* creates necessary dummy entries for the given path.
|
||||
*/
|
||||
ClusterInstance make_path (const db::Layout &layout, const db::Cell &cell, size_t id, const std::vector<db::InstElement> &path);
|
||||
|
||||
private:
|
||||
void build_local_cluster (const db::Layout &layout, const db::Cell &cell, db::ShapeIterator::flags_type shape_flags, const db::Connectivity &conn);
|
||||
void build_hier_connections (cell_clusters_box_converter<T> &cbc, const db::Layout &layout, const db::Cell &cell, const db::Connectivity &conn);
|
||||
|
|
|
|||
|
|
@ -170,6 +170,11 @@ const std::string &LayoutToNetlist::global_net_name (size_t id) const
|
|||
return m_conn.global_net_name (id);
|
||||
}
|
||||
|
||||
size_t LayoutToNetlist::global_net_id (const std::string &name)
|
||||
{
|
||||
return m_conn.global_net_id (name);
|
||||
}
|
||||
|
||||
void LayoutToNetlist::extract_netlist ()
|
||||
{
|
||||
if (m_netlist_extracted) {
|
||||
|
|
@ -275,7 +280,6 @@ static void deliver_shapes_of_net_nonrecursive (const db::NetlistExtractor &nete
|
|||
tl_assert (circuit != 0);
|
||||
|
||||
db::cell_index_type ci = circuit->cell_index ();
|
||||
|
||||
const db::local_cluster<db::PolygonRef> &lc = netex.clusters ().clusters_per_cell (ci).cluster_by_id (net.cluster_id ());
|
||||
|
||||
for (db::local_cluster<db::PolygonRef>::shape_iterator s = lc.begin (layer_id); !s.at_end (); ++s) {
|
||||
|
|
|
|||
|
|
@ -176,6 +176,11 @@ public:
|
|||
*/
|
||||
const std::string &global_net_name (size_t id) const;
|
||||
|
||||
/**
|
||||
* @brief Gets the global net ID for a given name name
|
||||
*/
|
||||
size_t global_net_id (const std::string &name);
|
||||
|
||||
/**
|
||||
* @brief Runs the netlist extraction
|
||||
* See the class description for more details.
|
||||
|
|
|
|||
|
|
@ -113,22 +113,8 @@ const Net *Device::net_for_terminal (size_t terminal_id) const
|
|||
return 0;
|
||||
}
|
||||
|
||||
void Device::connect_terminal_global (size_t terminal_id, size_t global_net_id)
|
||||
{
|
||||
connect_terminal (terminal_id, 0);
|
||||
m_global_connections.push_back (std::make_pair (terminal_id, global_net_id));
|
||||
}
|
||||
|
||||
void Device::connect_terminal (size_t terminal_id, Net *net)
|
||||
{
|
||||
for (size_t i = 0; i < m_global_connections.size (); ) {
|
||||
if (m_global_connections [i].first == terminal_id) {
|
||||
m_global_connections.erase (m_global_connections.begin () + i);
|
||||
} else {
|
||||
++i;
|
||||
}
|
||||
}
|
||||
|
||||
if (net_for_terminal (terminal_id) == net) {
|
||||
return;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -884,31 +884,6 @@ public:
|
|||
return m_name;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Gets the global connections iterator (begin)
|
||||
* Global connections are terminals attached to a global net.
|
||||
* This iterator delivers a pair of terminal ID (first)
|
||||
* and global net ID (second).
|
||||
* See Connectivity for the definition of the global net ID.
|
||||
*/
|
||||
global_connections_iterator begin_global_connections () const
|
||||
{
|
||||
return m_global_connections.begin ();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Gets the global connections iterator (end)
|
||||
*/
|
||||
global_connections_iterator end_global_connections () const
|
||||
{
|
||||
return m_global_connections.end ();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Connects the given terminal to the given global net
|
||||
*/
|
||||
void connect_terminal_global (size_t terminal_id, size_t global_net_id);
|
||||
|
||||
/**
|
||||
* @brief Gets the net attached to a specific terminal
|
||||
* Returns 0 if no net is attached.
|
||||
|
|
@ -966,7 +941,6 @@ private:
|
|||
std::vector<double> m_parameters;
|
||||
size_t m_id;
|
||||
Circuit *mp_circuit;
|
||||
global_connections m_global_connections;
|
||||
|
||||
/**
|
||||
* @brief Sets the terminal reference for a specific terminal
|
||||
|
|
|
|||
|
|
@ -250,27 +250,11 @@ bool DeviceClassMOS3Transistor::combine_devices (Device *a, Device *b) const
|
|||
// ------------------------------------------------------------------------------------
|
||||
// DeviceClassMOS4Transistor implementation
|
||||
|
||||
DB_PUBLIC size_t DeviceClassMOS4Transistor::param_id_L = 0;
|
||||
DB_PUBLIC size_t DeviceClassMOS4Transistor::param_id_W = 1;
|
||||
DB_PUBLIC size_t DeviceClassMOS4Transistor::param_id_AS = 2;
|
||||
DB_PUBLIC size_t DeviceClassMOS4Transistor::param_id_AD = 3;
|
||||
|
||||
DB_PUBLIC size_t DeviceClassMOS4Transistor::terminal_id_S = 0;
|
||||
DB_PUBLIC size_t DeviceClassMOS4Transistor::terminal_id_G = 1;
|
||||
DB_PUBLIC size_t DeviceClassMOS4Transistor::terminal_id_D = 2;
|
||||
DB_PUBLIC size_t DeviceClassMOS4Transistor::terminal_id_B = 3;
|
||||
|
||||
DeviceClassMOS4Transistor::DeviceClassMOS4Transistor ()
|
||||
{
|
||||
add_terminal_definition (db::DeviceTerminalDefinition ("S", "Source"));
|
||||
add_terminal_definition (db::DeviceTerminalDefinition ("G", "Gate"));
|
||||
add_terminal_definition (db::DeviceTerminalDefinition ("D", "Drain"));
|
||||
add_terminal_definition (db::DeviceTerminalDefinition ("B", "Bulk"));
|
||||
|
||||
add_parameter_definition (db::DeviceParameterDefinition ("L", "Gate length (micrometer)", 0.0));
|
||||
add_parameter_definition (db::DeviceParameterDefinition ("W", "Gate width (micrometer)", 0.0));
|
||||
add_parameter_definition (db::DeviceParameterDefinition ("AS", "Source area (square micrometer)", 0.0));
|
||||
add_parameter_definition (db::DeviceParameterDefinition ("AD", "Drain area (square micrometer)", 0.0));
|
||||
}
|
||||
|
||||
bool DeviceClassMOS4Transistor::combine_devices (Device *a, Device *b) const
|
||||
|
|
|
|||
|
|
@ -181,19 +181,11 @@ public:
|
|||
* terminal for the bulk.
|
||||
*/
|
||||
class DB_PUBLIC DeviceClassMOS4Transistor
|
||||
: public db::DeviceClass
|
||||
: public DeviceClassMOS3Transistor
|
||||
{
|
||||
public:
|
||||
DeviceClassMOS4Transistor ();
|
||||
|
||||
static size_t param_id_L;
|
||||
static size_t param_id_W;
|
||||
static size_t param_id_AS;
|
||||
static size_t param_id_AD;
|
||||
|
||||
static size_t terminal_id_S;
|
||||
static size_t terminal_id_G;
|
||||
static size_t terminal_id_D;
|
||||
static size_t terminal_id_B;
|
||||
|
||||
virtual db::DeviceClass *clone () const
|
||||
|
|
@ -202,7 +194,6 @@ public:
|
|||
}
|
||||
|
||||
virtual bool combine_devices (Device *a, Device *b) const;
|
||||
virtual bool supports_parallel_combination () const { return true; }
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -24,12 +24,12 @@
|
|||
#include "dbNetlistDeviceExtractorClasses.h"
|
||||
#include "dbNetlistDeviceClasses.h"
|
||||
|
||||
// ---------------------------------------------------------------------------------
|
||||
// NetlistDeviceExtractorMOS3Transistor implementation
|
||||
|
||||
namespace db
|
||||
{
|
||||
|
||||
// ---------------------------------------------------------------------------------
|
||||
// NetlistDeviceExtractorMOS3Transistor implementation
|
||||
|
||||
NetlistDeviceExtractorMOS3Transistor::NetlistDeviceExtractorMOS3Transistor (const std::string &name)
|
||||
: db::NetlistDeviceExtractor (name)
|
||||
{
|
||||
|
|
@ -47,7 +47,7 @@ void NetlistDeviceExtractorMOS3Transistor::setup ()
|
|||
|
||||
db::Connectivity NetlistDeviceExtractorMOS3Transistor::get_connectivity (const db::Layout & /*layout*/, const std::vector<unsigned int> &layers) const
|
||||
{
|
||||
tl_assert (layers.size () == 3);
|
||||
tl_assert (layers.size () >= 3);
|
||||
|
||||
unsigned int diff = layers [0];
|
||||
unsigned int gate = layers [1];
|
||||
|
|
@ -117,6 +117,9 @@ void NetlistDeviceExtractorMOS3Transistor::extract_devices (const std::vector<db
|
|||
|
||||
define_terminal (device, db::DeviceClassMOS3Transistor::terminal_id_G, gate_geometry_index, *p);
|
||||
|
||||
// allow derived classes to modify the device
|
||||
modify_device (*p, layer_geometry, device);
|
||||
|
||||
// output the device for debugging
|
||||
device_out (device, rdiff2gate, rgate);
|
||||
|
||||
|
|
@ -125,4 +128,29 @@ void NetlistDeviceExtractorMOS3Transistor::extract_devices (const std::vector<db
|
|||
}
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------------
|
||||
// NetlistDeviceExtractorMOS4Transistor implementation
|
||||
|
||||
NetlistDeviceExtractorMOS4Transistor::NetlistDeviceExtractorMOS4Transistor (const std::string &name)
|
||||
: NetlistDeviceExtractorMOS3Transistor (name)
|
||||
{
|
||||
// .. nothing yet ..
|
||||
}
|
||||
|
||||
void NetlistDeviceExtractorMOS4Transistor::setup ()
|
||||
{
|
||||
define_layer ("SD", "Source/drain diffusion");
|
||||
define_layer ("G", "Gate");
|
||||
define_layer ("P", "Poly");
|
||||
define_layer ("W", "Well");
|
||||
|
||||
register_device_class (new db::DeviceClassMOS4Transistor ());
|
||||
}
|
||||
|
||||
void NetlistDeviceExtractorMOS4Transistor::modify_device (const db::Polygon &rgate, const std::vector<db::Region> & /*layer_geometry*/, db::Device *device)
|
||||
{
|
||||
unsigned int well_geometry_index = 3;
|
||||
define_terminal (device, db::DeviceClassMOS4Transistor::terminal_id_B, well_geometry_index, rgate);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -35,7 +35,7 @@ namespace db
|
|||
* The device is defined by two basic input layers: the diffusion area
|
||||
* (source and drain) and the gate area. It requires a third layer
|
||||
* (poly) to put the gate terminals on. The separation between poly
|
||||
* and allows separating the device recognition layer (gate) from the
|
||||
* and gate allows separating the device recognition layer (gate) from the
|
||||
* conductive layer.
|
||||
*
|
||||
* The device class produced by this extractor is DeviceClassMOS3Transistor.
|
||||
|
|
@ -56,13 +56,43 @@ public:
|
|||
|
||||
protected:
|
||||
/**
|
||||
* @brief A cappback when the device is produced
|
||||
* @brief A callback when the device is produced
|
||||
* This callback is provided as a debugging port
|
||||
*/
|
||||
virtual void device_out (const db::Device * /*device*/, const db::Region & /*diff*/, const db::Region & /*gate*/)
|
||||
{
|
||||
// .. no specific implementation ..
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Allow derived classes to modify the device
|
||||
*/
|
||||
virtual void modify_device (const db::Polygon & /*rgate*/, const std::vector<db::Region> & /*layer_geometry*/, db::Device * /*device*/)
|
||||
{
|
||||
// .. no specific implementation ..
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief A device extractor for a four-terminal MOS transistor
|
||||
*
|
||||
* This class is like the MOS3Transistor extractor, but requires a forth
|
||||
* input layer (Well). This layer will be used to output the bulk terminal.
|
||||
*
|
||||
* The device class produced by this extractor is DeviceClassMOS4Transistor.
|
||||
* The extractor extracts the four parameters of this class: L, W, AS and AD.
|
||||
*/
|
||||
class DB_PUBLIC NetlistDeviceExtractorMOS4Transistor
|
||||
: public NetlistDeviceExtractorMOS3Transistor
|
||||
{
|
||||
public:
|
||||
NetlistDeviceExtractorMOS4Transistor (const std::string &name);
|
||||
|
||||
virtual void setup ();
|
||||
|
||||
private:
|
||||
virtual void modify_device (const db::Polygon &rgate, const std::vector<db::Region> &layer_geometry, db::Device *device);
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -65,8 +65,6 @@ NetlistExtractor::extract_nets (const db::DeepShapeStore &dss, const db::Connect
|
|||
}
|
||||
|
||||
std::map<db::cell_index_type, std::map<size_t, size_t> > pins_per_cluster_per_cell;
|
||||
std::map<db::cell_index_type, std::map<size_t, db::Net *> > global_nets_per_cell;
|
||||
|
||||
for (db::Layout::bottom_up_const_iterator cid = layout.begin_bottom_up (); cid != layout.end_bottom_up (); ++cid) {
|
||||
|
||||
const connected_clusters_type &clusters = m_net_clusters.clusters_per_cell (*cid);
|
||||
|
|
@ -89,7 +87,6 @@ NetlistExtractor::extract_nets (const db::DeepShapeStore &dss, const db::Connect
|
|||
circuit = k->second;
|
||||
}
|
||||
|
||||
std::map<size_t, db::Net *> &global_nets = global_nets_per_cell [*cid];
|
||||
std::map<size_t, size_t> &c2p = pins_per_cluster_per_cell [*cid];
|
||||
|
||||
std::map<db::InstElement, db::SubCircuit *> subcircuits;
|
||||
|
|
@ -100,12 +97,8 @@ NetlistExtractor::extract_nets (const db::DeepShapeStore &dss, const db::Connect
|
|||
net->set_cluster_id (*c);
|
||||
circuit->add_net (net);
|
||||
|
||||
const db::local_cluster<db::PolygonRef> &cluster = clusters.cluster_by_id (*c);
|
||||
|
||||
// collect global net assignments from clusters
|
||||
for (std::set<size_t>::const_iterator g = cluster.begin_global_nets (); g != cluster.end_global_nets (); ++g) {
|
||||
tl_assert (global_nets.find (*g) == global_nets.end ());
|
||||
global_nets.insert (std::make_pair (*g, net));
|
||||
const db::local_cluster<db::PolygonRef>::global_nets &gn = clusters.cluster_by_id (*c).get_global_nets ();
|
||||
for (db::local_cluster<db::PolygonRef>::global_nets::const_iterator g = gn.begin (); g != gn.end (); ++g) {
|
||||
assign_net_name (conn.global_net_name (*g), net);
|
||||
}
|
||||
|
||||
|
|
@ -134,73 +127,6 @@ NetlistExtractor::extract_nets (const db::DeepShapeStore &dss, const db::Connect
|
|||
|
||||
}
|
||||
|
||||
// make global net connections for devices
|
||||
for (db::Circuit::device_iterator d = circuit->begin_devices (); d != circuit->end_devices (); ++d) {
|
||||
|
||||
for (db::Device::global_connections_iterator g = d->begin_global_connections (); g != d->end_global_connections (); ++g) {
|
||||
|
||||
db::Net *&net = global_nets [g->second];
|
||||
if (! net) {
|
||||
net = new db::Net (conn.global_net_name (g->second));
|
||||
circuit->add_net (net);
|
||||
}
|
||||
|
||||
net->add_terminal (db::NetTerminalRef (d.operator-> (), g->first));
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// if any of the subcircuits has global nets which this circuit doesn't have, propagate them
|
||||
|
||||
std::set<db::Circuit *> seen;
|
||||
std::set<size_t> global_nets_of_subcircuits;
|
||||
for (db::Circuit::subcircuit_iterator sc = circuit->begin_subcircuits (); sc != circuit->end_subcircuits (); ++sc) {
|
||||
|
||||
db::Circuit *subcircuit = sc->circuit_ref ();
|
||||
if (seen.find (subcircuit) == seen.end ()) {
|
||||
|
||||
seen.insert (subcircuit);
|
||||
|
||||
const std::map<size_t, db::Net *> &sc_gn = global_nets_per_cell [subcircuit->cell_index ()];
|
||||
for (std::map<size_t, db::Net *>::const_iterator g = sc_gn.begin (); g != sc_gn.end (); ++g) {
|
||||
global_nets_of_subcircuits.insert (g->first);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
for (std::set<size_t>::const_iterator g = global_nets_of_subcircuits.begin (); g != global_nets_of_subcircuits.end (); ++g) {
|
||||
|
||||
}
|
||||
|
||||
// make the global net connections into subcircuits - if necessary by creating pins into the subcircuit
|
||||
for (db::Circuit::subcircuit_iterator sc = circuit->begin_subcircuits (); sc != circuit->end_subcircuits (); ++sc) {
|
||||
|
||||
db::Circuit *subcircuit = sc->circuit_ref ();
|
||||
|
||||
const std::map<size_t, db::Net *> &sc_gn = global_nets_per_cell [subcircuit->cell_index ()];
|
||||
for (std::map<size_t, db::Net *>::const_iterator g = global_nets.begin (); g != global_nets.end (); ++g) {
|
||||
|
||||
std::map<size_t, db::Net *>::const_iterator gg = sc_gn.find (g->first);
|
||||
if (gg != sc_gn.end ()) {
|
||||
|
||||
size_t pin_id = 0;
|
||||
if (gg->second->pin_count () > 0) {
|
||||
pin_id = gg->second->begin_pins ()->pin_id ();
|
||||
} else {
|
||||
pin_id = subcircuit->add_pin (conn.global_net_name (gg->first)).id ();
|
||||
subcircuit->connect_pin (pin_id, gg->second);
|
||||
}
|
||||
g->second->add_subcircuit_pin (db::NetSubcircuitPinRef (sc.operator-> (), pin_id));
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -37,8 +37,11 @@ Class<db::Connectivity> decl_dbConnectivity ("db", "Connectivity",
|
|||
"@brief Connects the given layer to the global net given by name.\n"
|
||||
"Returns the ID of the global net."
|
||||
) +
|
||||
gsi::method ("global", &db::Connectivity::global_net_name, gsi::arg ("global_net_id"),
|
||||
gsi::method ("global_net_name", &db::Connectivity::global_net_name, gsi::arg ("global_net_id"),
|
||||
"@brief Gets the name for a given global net ID.\n"
|
||||
) +
|
||||
gsi::method ("global_net_id", &db::Connectivity::global_net_id, gsi::arg ("global_net_name"),
|
||||
"@brief Gets the ID for a given global net name.\n"
|
||||
),
|
||||
"@brief This class specifies connections between different layers."
|
||||
"Connections are build using \\connect. There are basically two flavours of connections: intra-layer and inter-layer.\n"
|
||||
|
|
|
|||
|
|
@ -61,44 +61,6 @@ static void device_disconnect_terminal_by_name (db::Device *device, const std::s
|
|||
device_connect_terminal_by_name (device, terminal_name, 0);
|
||||
}
|
||||
|
||||
static tl::Variant device_terminal_for_global_net (const db::Device *device, size_t global_net)
|
||||
{
|
||||
for (db::Device::global_connections_iterator g = device->begin_global_connections (); g != device->end_global_connections (); ++g) {
|
||||
if (g->second == global_net) {
|
||||
return tl::Variant (g->first);
|
||||
}
|
||||
}
|
||||
return tl::Variant ();
|
||||
}
|
||||
|
||||
static tl::Variant device_global_net_for_terminal (const db::Device *device, size_t terminal_id)
|
||||
{
|
||||
for (db::Device::global_connections_iterator g = device->begin_global_connections (); g != device->end_global_connections (); ++g) {
|
||||
if (g->first == terminal_id) {
|
||||
return tl::Variant (g->second);
|
||||
}
|
||||
}
|
||||
return tl::Variant ();
|
||||
}
|
||||
|
||||
static tl::Variant device_global_net_for_terminal_name (const db::Device *device, const std::string &terminal_name)
|
||||
{
|
||||
if (! device->device_class ()) {
|
||||
throw tl::Exception (tl::to_string (tr ("Device does not have a device class")));
|
||||
}
|
||||
size_t terminal_id = device->device_class ()->terminal_id_for_name (terminal_name);
|
||||
return device_global_net_for_terminal (device, terminal_id);
|
||||
}
|
||||
|
||||
static void device_connect_terminal_global_by_name (db::Device *device, const std::string &terminal_name, size_t global_net)
|
||||
{
|
||||
if (! device->device_class ()) {
|
||||
throw tl::Exception (tl::to_string (tr ("Device does not have a device class")));
|
||||
}
|
||||
size_t terminal_id = device->device_class ()->terminal_id_for_name (terminal_name);
|
||||
device->connect_terminal_global (terminal_id, global_net);
|
||||
}
|
||||
|
||||
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"
|
||||
|
|
@ -140,26 +102,6 @@ Class<db::Device> decl_dbDevice ("db", "Device",
|
|||
"@brief Disconnects the given terminal from any net.\n"
|
||||
"This version accepts a terminal name. If the name is not a valid terminal name, an exception is raised."
|
||||
) +
|
||||
gsi::method ("connect_terminal_global", &db::Device::connect_terminal_global, gsi::arg ("terminal_id"), gsi::arg ("global_net_id"),
|
||||
"@brief Connects the given terminal to the given global net.\n"
|
||||
"The global net ID is taken from \\Connectivity (connect_global, etc.).\n"
|
||||
"If the terminal was already connected to another net, it will be disconnected from there."
|
||||
) +
|
||||
gsi::method_ext ("connect_terminal_global", &device_connect_terminal_global_by_name, gsi::arg ("terminal_name"), gsi::arg ("global_net_id"),
|
||||
"@brief Connects the given terminal to the given global net.\n"
|
||||
"This version accepts a terminal name. If the name is not a valid terminal name, an exception is raised."
|
||||
) +
|
||||
gsi::method_ext ("terminal_on_global_net", &device_terminal_for_global_net, gsi::arg ("global_net_id"),
|
||||
"@brief Gets the terminal ID for the given global net or nil if no terminal is not that global net.\n"
|
||||
"The global net ID is managed by the \\Connectivity object."
|
||||
) +
|
||||
gsi::method_ext ("global_net_on_terminal", &device_global_net_for_terminal, gsi::arg ("terminal_id"),
|
||||
"@brief Gets the global net ID for the given terminal ID or nil if the terminal is not connected to a global net.\n"
|
||||
) +
|
||||
gsi::method_ext ("global_net_on_terminal", &device_global_net_for_terminal_name, gsi::arg ("terminal_name"),
|
||||
"@brief Gets the global net ID for the given terminal name or nil if the terminal is not connected to a global net.\n"
|
||||
"If the name is not a valid terminal name, an exception is raised."
|
||||
) +
|
||||
gsi::method ("parameter", (double (db::Device::*) (size_t) const) &db::Device::parameter_value, gsi::arg ("param_id"),
|
||||
"@brief Gets the parameter value for the given parameter ID."
|
||||
) +
|
||||
|
|
|
|||
|
|
@ -877,17 +877,12 @@ static void copy_cluster_shapes (const std::string *&attrs, db::Shapes &out, db:
|
|||
static void run_hc_test (tl::TestBase *_this, const std::string &file, const std::string &au_file)
|
||||
{
|
||||
db::Layout ly;
|
||||
unsigned int l0 = 0, l1 = 0, l2 = 0, l3 = 0;
|
||||
unsigned int l1 = 0, l2 = 0, l3 = 0, l4 = 0;
|
||||
|
||||
{
|
||||
db::LayerProperties p;
|
||||
db::LayerMap lmap;
|
||||
|
||||
p.layer = 0;
|
||||
p.datatype = 0;
|
||||
lmap.map (db::LDPair (p.layer, p.datatype), l0 = ly.insert_layer ());
|
||||
ly.set_properties (l0, p);
|
||||
|
||||
p.layer = 1;
|
||||
p.datatype = 0;
|
||||
lmap.map (db::LDPair (p.layer, p.datatype), l1 = ly.insert_layer ());
|
||||
|
|
@ -903,6 +898,11 @@ static void run_hc_test (tl::TestBase *_this, const std::string &file, const std
|
|||
lmap.map (db::LDPair (p.layer, p.datatype), l3 = ly.insert_layer ());
|
||||
ly.set_properties (l3, p);
|
||||
|
||||
p.layer = 4;
|
||||
p.datatype = 0;
|
||||
lmap.map (db::LDPair (p.layer, p.datatype), l4 = ly.insert_layer ());
|
||||
ly.set_properties (l4, p);
|
||||
|
||||
db::LoadLayoutOptions options;
|
||||
options.get_options<db::CommonReaderOptions> ().layer_map = lmap;
|
||||
options.get_options<db::CommonReaderOptions> ().create_other_layers = false;
|
||||
|
|
@ -919,6 +919,7 @@ static void run_hc_test (tl::TestBase *_this, const std::string &file, const std
|
|||
normalize_layer (ly, strings, l1);
|
||||
normalize_layer (ly, strings, l2);
|
||||
normalize_layer (ly, strings, l3);
|
||||
normalize_layer (ly, strings, l4);
|
||||
|
||||
// connect 1 to 1, 1 to 2 and 1 to 3, but *not* 2 to 3
|
||||
db::Connectivity conn;
|
||||
|
|
@ -927,6 +928,9 @@ static void run_hc_test (tl::TestBase *_this, const std::string &file, const std
|
|||
conn.connect (l3, l3);
|
||||
conn.connect (l1, l2);
|
||||
conn.connect (l1, l3);
|
||||
conn.connect (l1, l4);
|
||||
|
||||
conn.connect_global (l4, "BULK");
|
||||
|
||||
db::hier_clusters<db::PolygonRef> hc;
|
||||
hc.build (ly, ly.cell (*ly.begin_top_down ()), db::ShapeIterator::Polygons, conn);
|
||||
|
|
@ -989,7 +993,7 @@ static void run_hc_test (tl::TestBase *_this, const std::string &file, const std
|
|||
static void run_hc_test_with_backannotation (tl::TestBase *_this, const std::string &file, const std::string &au_file)
|
||||
{
|
||||
db::Layout ly;
|
||||
unsigned int l1 = 0, l2 = 0, l3 = 0;
|
||||
unsigned int l1 = 0, l2 = 0, l3 = 0, l4 = 0;
|
||||
|
||||
{
|
||||
db::LayerProperties p;
|
||||
|
|
@ -1010,6 +1014,11 @@ static void run_hc_test_with_backannotation (tl::TestBase *_this, const std::str
|
|||
lmap.map (db::LDPair (p.layer, p.datatype), l3 = ly.insert_layer ());
|
||||
ly.set_properties (l3, p);
|
||||
|
||||
p.layer = 4;
|
||||
p.datatype = 0;
|
||||
lmap.map (db::LDPair (p.layer, p.datatype), l4 = ly.insert_layer ());
|
||||
ly.set_properties (l4, p);
|
||||
|
||||
db::LoadLayoutOptions options;
|
||||
options.get_options<db::CommonReaderOptions> ().layer_map = lmap;
|
||||
options.get_options<db::CommonReaderOptions> ().create_other_layers = false;
|
||||
|
|
@ -1026,6 +1035,7 @@ static void run_hc_test_with_backannotation (tl::TestBase *_this, const std::str
|
|||
normalize_layer (ly, strings, l1);
|
||||
normalize_layer (ly, strings, l2);
|
||||
normalize_layer (ly, strings, l3);
|
||||
normalize_layer (ly, strings, l4);
|
||||
|
||||
// connect 1 to 1, 1 to 2 and 1 to 3, but *not* 2 to 3
|
||||
db::Connectivity conn;
|
||||
|
|
@ -1034,6 +1044,9 @@ static void run_hc_test_with_backannotation (tl::TestBase *_this, const std::str
|
|||
conn.connect (l3, l3);
|
||||
conn.connect (l1, l2);
|
||||
conn.connect (l1, l3);
|
||||
conn.connect (l1, l4);
|
||||
|
||||
conn.connect_global (l4, "BULK");
|
||||
|
||||
db::hier_clusters<db::PolygonRef> hc;
|
||||
hc.build (ly, ly.cell (*ly.begin_top_down ()), db::ShapeIterator::Polygons, conn);
|
||||
|
|
@ -1042,6 +1055,7 @@ static void run_hc_test_with_backannotation (tl::TestBase *_this, const std::str
|
|||
lm[l1] = ly.insert_layer (db::LayerProperties (101, 0));
|
||||
lm[l2] = ly.insert_layer (db::LayerProperties (102, 0));
|
||||
lm[l3] = ly.insert_layer (db::LayerProperties (103, 0));
|
||||
lm[l4] = ly.insert_layer (db::LayerProperties (104, 0));
|
||||
hc.return_to_hierarchy (ly, lm);
|
||||
|
||||
CHECKPOINT();
|
||||
|
|
@ -1138,3 +1152,9 @@ TEST(115_HierClusters)
|
|||
run_hc_test_with_backannotation (_this, "hc_test_l15.gds", "hc_test_au15b.gds");
|
||||
}
|
||||
|
||||
TEST(116_HierClusters)
|
||||
{
|
||||
run_hc_test (_this, "hc_test_l16.gds", "hc_test_au16.gds");
|
||||
run_hc_test_with_backannotation (_this, "hc_test_l16.gds", "hc_test_au16b.gds");
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -49,11 +49,39 @@ static std::string device_name (const db::Device &device)
|
|||
}
|
||||
}
|
||||
|
||||
class MOSFETExtractor
|
||||
static void mos2layout (const db::Layout *layout, db::cell_index_type cell_index, db::Layout *debug_out, const db::Device *device, unsigned int ldiff, const db::Region &diff, unsigned int lgate, const db::Region &gate)
|
||||
{
|
||||
std::string cn = layout->cell_name (cell_index);
|
||||
std::pair<bool, db::cell_index_type> target_cp = debug_out->cell_by_name (cn.c_str ());
|
||||
tl_assert (target_cp.first);
|
||||
|
||||
db::cell_index_type dci = debug_out->add_cell ((device->device_class ()->name () + "_" + device->circuit ()->name () + "_" + device_name (*device)).c_str ());
|
||||
debug_out->cell (target_cp.second).insert (db::CellInstArray (db::CellInst (dci), db::Trans ()));
|
||||
|
||||
db::Cell &device_cell = debug_out->cell (dci);
|
||||
for (db::Region::const_iterator p = diff.begin (); ! p.at_end (); ++p) {
|
||||
device_cell.shapes (ldiff).insert (*p);
|
||||
}
|
||||
for (db::Region::const_iterator p = gate.begin (); ! p.at_end (); ++p) {
|
||||
device_cell.shapes (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 (ldiff).insert (db::Text (ps, db::Trans (diff.bbox ().center () - db::Point ())));
|
||||
}
|
||||
|
||||
class MOSFET3Extractor
|
||||
: public db::NetlistDeviceExtractorMOS3Transistor
|
||||
{
|
||||
public:
|
||||
MOSFETExtractor (const std::string &name, db::Layout *debug_out)
|
||||
MOSFET3Extractor (const std::string &name, db::Layout *debug_out)
|
||||
: db::NetlistDeviceExtractorMOS3Transistor (name), mp_debug_out (debug_out), m_ldiff (0), m_lgate (0)
|
||||
{
|
||||
if (mp_debug_out) {
|
||||
|
|
@ -68,34 +96,34 @@ private:
|
|||
|
||||
void device_out (const db::Device *device, const db::Region &diff, const db::Region &gate)
|
||||
{
|
||||
if (! mp_debug_out) {
|
||||
return;
|
||||
if (mp_debug_out) {
|
||||
mos2layout (layout (), cell_index (), mp_debug_out, device, m_ldiff, diff, m_lgate, gate);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
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);
|
||||
class MOSFET4Extractor
|
||||
: public db::NetlistDeviceExtractorMOS4Transistor
|
||||
{
|
||||
public:
|
||||
MOSFET4Extractor (const std::string &name, db::Layout *debug_out)
|
||||
: db::NetlistDeviceExtractorMOS4Transistor (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));
|
||||
}
|
||||
}
|
||||
|
||||
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 ()));
|
||||
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) {
|
||||
mos2layout (layout (), cell_index (), mp_debug_out, device, m_ldiff, diff, m_lgate, gate);
|
||||
}
|
||||
device_cell.shapes (m_ldiff).insert (db::Text (ps, db::Trans (diff.bbox ().center () - db::Point ())));
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -261,8 +289,8 @@ TEST(1_Basic)
|
|||
// NOTE: the device extractor will add more debug layers for the transistors:
|
||||
// 20/0 -> Diffusion
|
||||
// 21/0 -> Gate
|
||||
MOSFETExtractor pmos_ex ("PMOS", &ly);
|
||||
MOSFETExtractor nmos_ex ("NMOS", &ly);
|
||||
MOSFET3Extractor pmos_ex ("PMOS", &ly);
|
||||
MOSFET3Extractor nmos_ex ("NMOS", &ly);
|
||||
|
||||
// device extraction
|
||||
|
||||
|
|
@ -617,8 +645,8 @@ TEST(2_Probing)
|
|||
// NOTE: the device extractor will add more debug layers for the transistors:
|
||||
// 20/0 -> Diffusion
|
||||
// 21/0 -> Gate
|
||||
MOSFETExtractor pmos_ex ("PMOS", &ly);
|
||||
MOSFETExtractor nmos_ex ("NMOS", &ly);
|
||||
MOSFET3Extractor pmos_ex ("PMOS", &ly);
|
||||
MOSFET3Extractor nmos_ex ("NMOS", &ly);
|
||||
|
||||
// device extraction
|
||||
|
||||
|
|
@ -871,8 +899,8 @@ TEST(3_GlobalNetConnections)
|
|||
// NOTE: the device extractor will add more debug layers for the transistors:
|
||||
// 20/0 -> Diffusion
|
||||
// 21/0 -> Gate
|
||||
MOSFETExtractor pmos_ex ("PMOS", &ly);
|
||||
MOSFETExtractor nmos_ex ("NMOS", &ly);
|
||||
MOSFET3Extractor pmos_ex ("PMOS", &ly);
|
||||
MOSFET3Extractor nmos_ex ("NMOS", &ly);
|
||||
|
||||
// device extraction
|
||||
|
||||
|
|
@ -1054,3 +1082,277 @@ TEST(3_GlobalNetConnections)
|
|||
EXPECT_EQ (qnet_name (l2n.probe_net (*rmetal1, db::DPoint (2.6, 1.0))), "INV2PAIR:$I8");
|
||||
EXPECT_EQ (qnet_name (l2n.probe_net (*rmetal1, db::DPoint (6.4, 1.0))), "INV2PAIR:$I4");
|
||||
}
|
||||
|
||||
TEST(4_GlobalNetDeviceExtraction)
|
||||
{
|
||||
db::Layout ly;
|
||||
db::LayerMap lmap;
|
||||
|
||||
unsigned int nwell = define_layer (ly, lmap, 1);
|
||||
unsigned int active = define_layer (ly, lmap, 2);
|
||||
unsigned int pplus = define_layer (ly, lmap, 10);
|
||||
unsigned int nplus = define_layer (ly, lmap, 11);
|
||||
unsigned int poly = define_layer (ly, lmap, 3);
|
||||
unsigned int poly_lbl = define_layer (ly, lmap, 3, 1);
|
||||
unsigned int diff_cont = define_layer (ly, lmap, 4);
|
||||
unsigned int poly_cont = define_layer (ly, lmap, 5);
|
||||
unsigned int metal1 = define_layer (ly, lmap, 6);
|
||||
unsigned int metal1_lbl = define_layer (ly, lmap, 6, 1);
|
||||
unsigned int via1 = define_layer (ly, lmap, 7);
|
||||
unsigned int metal2 = define_layer (ly, lmap, 8);
|
||||
unsigned int metal2_lbl = define_layer (ly, lmap, 8, 1);
|
||||
|
||||
{
|
||||
db::LoadLayoutOptions options;
|
||||
options.get_options<db::CommonReaderOptions> ().layer_map = lmap;
|
||||
options.get_options<db::CommonReaderOptions> ().create_other_layers = false;
|
||||
|
||||
std::string fn (tl::testsrc ());
|
||||
fn = tl::combine_path (fn, "testdata");
|
||||
fn = tl::combine_path (fn, "algo");
|
||||
fn = tl::combine_path (fn, "device_extract_l3.gds");
|
||||
|
||||
tl::InputStream stream (fn);
|
||||
db::Reader reader (stream);
|
||||
reader.read (ly, options);
|
||||
}
|
||||
|
||||
db::Cell &tc = ly.cell (*ly.begin_top_down ());
|
||||
db::LayoutToNetlist l2n (db::RecursiveShapeIterator (ly, tc, std::set<unsigned int> ()));
|
||||
|
||||
std::auto_ptr<db::Region> rbulk (l2n.make_layer (ly.insert_layer ()));
|
||||
std::auto_ptr<db::Region> rnwell (l2n.make_layer (nwell));
|
||||
std::auto_ptr<db::Region> ractive (l2n.make_layer (active));
|
||||
std::auto_ptr<db::Region> rpplus (l2n.make_layer (pplus));
|
||||
std::auto_ptr<db::Region> rnplus (l2n.make_layer (nplus));
|
||||
std::auto_ptr<db::Region> rpoly (l2n.make_polygon_layer (poly));
|
||||
std::auto_ptr<db::Region> rpoly_lbl (l2n.make_text_layer (poly_lbl));
|
||||
std::auto_ptr<db::Region> rdiff_cont (l2n.make_polygon_layer (diff_cont));
|
||||
std::auto_ptr<db::Region> rpoly_cont (l2n.make_polygon_layer (poly_cont));
|
||||
std::auto_ptr<db::Region> rmetal1 (l2n.make_polygon_layer (metal1));
|
||||
std::auto_ptr<db::Region> rmetal1_lbl (l2n.make_text_layer (metal1_lbl));
|
||||
std::auto_ptr<db::Region> rvia1 (l2n.make_polygon_layer (via1));
|
||||
std::auto_ptr<db::Region> rmetal2 (l2n.make_polygon_layer (metal2));
|
||||
std::auto_ptr<db::Region> rmetal2_lbl (l2n.make_text_layer (metal2_lbl));
|
||||
|
||||
// derived regions
|
||||
|
||||
db::Region ractive_in_nwell = *ractive & *rnwell;
|
||||
db::Region rpactive = ractive_in_nwell & *rpplus;
|
||||
db::Region rntie = ractive_in_nwell & *rnplus;
|
||||
db::Region rpgate = rpactive & *rpoly;
|
||||
db::Region rpsd = rpactive - rpgate;
|
||||
|
||||
db::Region ractive_outside_nwell = *ractive - *rnwell;
|
||||
db::Region rnactive = ractive_outside_nwell & *rnplus;
|
||||
db::Region rptie = ractive_outside_nwell & *rpplus;
|
||||
db::Region rngate = rnactive & *rpoly;
|
||||
db::Region rnsd = rnactive - rngate;
|
||||
|
||||
// return the computed layers into the original layout and write it for debugging purposes
|
||||
|
||||
unsigned int lgate = ly.insert_layer (db::LayerProperties (20, 0)); // 20/0 -> Gate
|
||||
unsigned int lsd = ly.insert_layer (db::LayerProperties (21, 0)); // 21/0 -> Source/Drain
|
||||
unsigned int lpdiff = ly.insert_layer (db::LayerProperties (22, 0)); // 22/0 -> P Diffusion
|
||||
unsigned int lndiff = ly.insert_layer (db::LayerProperties (23, 0)); // 23/0 -> N Diffusion
|
||||
unsigned int lptie = ly.insert_layer (db::LayerProperties (24, 0)); // 24/0 -> P Tie
|
||||
unsigned int lntie = ly.insert_layer (db::LayerProperties (25, 0)); // 25/0 -> N Tie
|
||||
|
||||
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);
|
||||
rpsd.insert_into (&ly, tc.cell_index (), lptie);
|
||||
rnsd.insert_into (&ly, tc.cell_index (), lntie);
|
||||
|
||||
// NOTE: the device extractor will add more debug layers for the transistors:
|
||||
// 20/0 -> Diffusion
|
||||
// 21/0 -> Gate
|
||||
MOSFET4Extractor pmos_ex ("PMOS", &ly);
|
||||
MOSFET4Extractor nmos_ex ("NMOS", &ly);
|
||||
|
||||
// device extraction
|
||||
|
||||
db::NetlistDeviceExtractor::input_layers dl;
|
||||
|
||||
dl["SD"] = &rpsd;
|
||||
dl["G"] = &rpgate;
|
||||
dl["P"] = rpoly.get (); // not needed for extraction but to return terminal shapes
|
||||
dl["W"] = rnwell.get ();
|
||||
l2n.extract_devices (pmos_ex, dl);
|
||||
|
||||
dl["SD"] = &rnsd;
|
||||
dl["G"] = &rngate;
|
||||
dl["P"] = rpoly.get (); // not needed for extraction but to return terminal shapes
|
||||
dl["W"] = rbulk.get ();
|
||||
l2n.extract_devices (nmos_ex, dl);
|
||||
|
||||
// net extraction
|
||||
|
||||
// Intra-layer
|
||||
l2n.connect (rpsd);
|
||||
l2n.connect (rnsd);
|
||||
l2n.connect (*rnwell);
|
||||
l2n.connect (*rpoly);
|
||||
l2n.connect (*rdiff_cont);
|
||||
l2n.connect (*rpoly_cont);
|
||||
l2n.connect (*rmetal1);
|
||||
l2n.connect (*rvia1);
|
||||
l2n.connect (*rmetal2);
|
||||
l2n.connect (rptie);
|
||||
l2n.connect (rntie);
|
||||
// Inter-layer
|
||||
l2n.connect (rpsd, *rdiff_cont);
|
||||
l2n.connect (rnsd, *rdiff_cont);
|
||||
l2n.connect (*rpoly, *rpoly_cont);
|
||||
l2n.connect (*rpoly_cont, *rmetal1);
|
||||
l2n.connect (*rdiff_cont, *rmetal1);
|
||||
l2n.connect (*rdiff_cont, rptie);
|
||||
l2n.connect (*rdiff_cont, rntie);
|
||||
l2n.connect (*rnwell, rntie);
|
||||
l2n.connect (*rmetal1, *rvia1);
|
||||
l2n.connect (*rvia1, *rmetal2);
|
||||
l2n.connect (*rpoly, *rpoly_lbl); // attaches labels
|
||||
l2n.connect (*rmetal1, *rmetal1_lbl); // attaches labels
|
||||
l2n.connect (*rmetal2, *rmetal2_lbl); // attaches labels
|
||||
// Global
|
||||
l2n.connect_global (rptie, "BULK");
|
||||
l2n.connect_global (*rbulk, "BULK");
|
||||
|
||||
// create some mess - we have to keep references to the layers to make them not disappear
|
||||
rmetal1_lbl.reset (0);
|
||||
rmetal2_lbl.reset (0);
|
||||
rpoly_lbl.reset (0);
|
||||
|
||||
l2n.extract_netlist ();
|
||||
|
||||
// debug layers produced for nets
|
||||
// 201/0 -> Well
|
||||
// 203/0 -> Poly
|
||||
// 204/0 -> Diffusion contacts
|
||||
// 205/0 -> Poly contacts
|
||||
// 206/0 -> Metal1
|
||||
// 207/0 -> Via1
|
||||
// 208/0 -> Metal2
|
||||
// 210/0 -> N source/drain
|
||||
// 211/0 -> P source/drain
|
||||
// 212/0 -> N tie
|
||||
// 213/0 -> P tie
|
||||
std::map<const db::Region *, unsigned int> dump_map;
|
||||
dump_map [&rpsd ] = ly.insert_layer (db::LayerProperties (210, 0));
|
||||
dump_map [&rnsd ] = ly.insert_layer (db::LayerProperties (211, 0));
|
||||
dump_map [&rptie ] = ly.insert_layer (db::LayerProperties (212, 0));
|
||||
dump_map [&rntie ] = ly.insert_layer (db::LayerProperties (213, 0));
|
||||
dump_map [rnwell.get () ] = ly.insert_layer (db::LayerProperties (201, 0));
|
||||
dump_map [rpoly.get () ] = ly.insert_layer (db::LayerProperties (203, 0));
|
||||
dump_map [rdiff_cont.get ()] = ly.insert_layer (db::LayerProperties (204, 0));
|
||||
dump_map [rpoly_cont.get ()] = ly.insert_layer (db::LayerProperties (205, 0));
|
||||
dump_map [rmetal1.get () ] = ly.insert_layer (db::LayerProperties (206, 0));
|
||||
dump_map [rvia1.get () ] = ly.insert_layer (db::LayerProperties (207, 0));
|
||||
dump_map [rmetal2.get () ] = ly.insert_layer (db::LayerProperties (208, 0));
|
||||
|
||||
// write nets to layout
|
||||
db::CellMapping cm = l2n.cell_mapping_into (ly, tc);
|
||||
dump_nets_to_layout (l2n, ly, dump_map, cm);
|
||||
|
||||
dump_map.clear ();
|
||||
dump_map [&rpsd ] = ly.insert_layer (db::LayerProperties (310, 0));
|
||||
dump_map [&rnsd ] = ly.insert_layer (db::LayerProperties (311, 0));
|
||||
dump_map [&rptie ] = ly.insert_layer (db::LayerProperties (312, 0));
|
||||
dump_map [&rntie ] = ly.insert_layer (db::LayerProperties (313, 0));
|
||||
dump_map [rnwell.get () ] = ly.insert_layer (db::LayerProperties (301, 0));
|
||||
dump_map [rpoly.get () ] = ly.insert_layer (db::LayerProperties (303, 0));
|
||||
dump_map [rdiff_cont.get ()] = ly.insert_layer (db::LayerProperties (304, 0));
|
||||
dump_map [rpoly_cont.get ()] = ly.insert_layer (db::LayerProperties (305, 0));
|
||||
dump_map [rmetal1.get () ] = ly.insert_layer (db::LayerProperties (306, 0));
|
||||
dump_map [rvia1.get () ] = ly.insert_layer (db::LayerProperties (307, 0));
|
||||
dump_map [rmetal2.get () ] = ly.insert_layer (db::LayerProperties (308, 0));
|
||||
|
||||
dump_recursive_nets_to_layout (l2n, ly, dump_map, cm);
|
||||
|
||||
// compare netlist as string
|
||||
EXPECT_EQ (l2n.netlist ()->to_string (),
|
||||
"Circuit RINGO ():\n"
|
||||
" XINV2PAIR $1 ($1=VSS,$2=VSS,$3=FB,$4=VDD,$5=VSS,$6=$I7,$7=OSC,$8=VDD)\n"
|
||||
" XINV2PAIR $2 ($1=VSS,$2=VSS,$3=$I22,$4=VDD,$5=VSS,$6=FB,$7=$I13,$8=VDD)\n"
|
||||
" XINV2PAIR $3 ($1=VSS,$2=VSS,$3=$I23,$4=VDD,$5=VSS,$6=$I13,$7=$I5,$8=VDD)\n"
|
||||
" XINV2PAIR $4 ($1=VSS,$2=VSS,$3=$I24,$4=VDD,$5=VSS,$6=$I5,$7=$I6,$8=VDD)\n"
|
||||
" XINV2PAIR $5 ($1=VSS,$2=VSS,$3=$I25,$4=VDD,$5=VSS,$6=$I6,$7=$I7,$8=VDD)\n"
|
||||
"Circuit INV2PAIR ($1=$I10,$2=$I9,$3=$I8,$4=$I6,$5=$I5,$6=$I3,$7=$I2,$8=$I1):\n"
|
||||
" XINV2 $1 ($1=$I1,IN=$I4,$3=$I8,BULK=$I10,OUT=$I2,VSS=$I5,VDD=$I6)\n"
|
||||
" XINV2 $2 ($1=$I1,IN=$I3,$3=$I7,BULK=$I9,OUT=$I4,VSS=$I5,VDD=$I6)\n"
|
||||
"Circuit INV2 ($1=$1,IN=IN,$3=$3,BULK=BULK,OUT=OUT,VSS=VSS,VDD=VDD):\n"
|
||||
" DPMOS $1 (S=$3,G=IN,D=VDD) [L=0.25,W=0.95,AS=0.49875,AD=0.26125]\n"
|
||||
" DPMOS $2 (S=VDD,G=$3,D=OUT) [L=0.25,W=0.95,AS=0.26125,AD=0.49875]\n"
|
||||
" DNMOS $3 (S=$3,G=IN,D=VSS) [L=0.25,W=0.95,AS=0.49875,AD=0.26125]\n"
|
||||
" DNMOS $4 (S=VSS,G=$3,D=OUT) [L=0.25,W=0.95,AS=0.26125,AD=0.49875]\n"
|
||||
" XTRANS $1 ($1=$3,$2=VSS,$3=IN)\n"
|
||||
" XTRANS $2 ($1=$3,$2=VDD,$3=IN)\n"
|
||||
" XTRANS $3 ($1=VDD,$2=OUT,$3=$3)\n"
|
||||
" XTRANS $4 ($1=VSS,$2=OUT,$3=$3)\n"
|
||||
"Circuit TRANS ($1=$1,$2=$2,$3=$3):\n"
|
||||
);
|
||||
|
||||
// compare the collected test data
|
||||
|
||||
std::string au = tl::testsrc ();
|
||||
au = tl::combine_path (au, "testdata");
|
||||
au = tl::combine_path (au, "algo");
|
||||
au = tl::combine_path (au, "device_extract_au3_with_rec_nets.gds");
|
||||
|
||||
db::compare_layouts (_this, ly, au);
|
||||
|
||||
// do some probing before purging
|
||||
|
||||
// top level
|
||||
EXPECT_EQ (qnet_name (l2n.probe_net (*rmetal2, db::DPoint (0.0, 1.8))), "RINGO:FB");
|
||||
EXPECT_EQ (qnet_name (l2n.probe_net (*rmetal2, db::Point (0, 1800))), "RINGO:FB");
|
||||
EXPECT_EQ (qnet_name (l2n.probe_net (*rmetal2, db::DPoint (-2.0, 1.8))), "(null)");
|
||||
EXPECT_EQ (qnet_name (l2n.probe_net (*rmetal1, db::DPoint (-1.5, 1.8))), "RINGO:FB");
|
||||
EXPECT_EQ (qnet_name (l2n.probe_net (*rmetal1, db::DPoint (24.5, 1.8))), "RINGO:OSC");
|
||||
EXPECT_EQ (qnet_name (l2n.probe_net (*rmetal1, db::DPoint (5.3, 0.0))), "RINGO:VSS");
|
||||
|
||||
EXPECT_EQ (qnet_name (l2n.probe_net (*rmetal1, db::DPoint (2.6, 1.0))), "RINGO:$I22");
|
||||
EXPECT_EQ (qnet_name (l2n.probe_net (*rmetal1, db::DPoint (6.4, 1.0))), "INV2PAIR:$I4");
|
||||
|
||||
// doesn't do anything here, but we test that this does not destroy anything:
|
||||
l2n.netlist ()->combine_devices ();
|
||||
|
||||
// make pins for named nets of top-level circuits - this way they are not purged
|
||||
l2n.netlist ()->make_top_level_pins ();
|
||||
l2n.netlist ()->purge ();
|
||||
|
||||
// compare netlist as string
|
||||
EXPECT_EQ (l2n.netlist ()->to_string (),
|
||||
"Circuit RINGO (FB=FB,OSC=OSC,VDD=VDD,VSS=VSS):\n"
|
||||
" XINV2PAIR $1 ($1=VSS,$2=VSS,$3=FB,$4=VDD,$5=VSS,$6=$I7,$7=OSC,$8=VDD)\n"
|
||||
" XINV2PAIR $2 ($1=VSS,$2=VSS,$3=(null),$4=VDD,$5=VSS,$6=FB,$7=$I13,$8=VDD)\n"
|
||||
" XINV2PAIR $3 ($1=VSS,$2=VSS,$3=(null),$4=VDD,$5=VSS,$6=$I13,$7=$I5,$8=VDD)\n"
|
||||
" XINV2PAIR $4 ($1=VSS,$2=VSS,$3=(null),$4=VDD,$5=VSS,$6=$I5,$7=$I6,$8=VDD)\n"
|
||||
" XINV2PAIR $5 ($1=VSS,$2=VSS,$3=(null),$4=VDD,$5=VSS,$6=$I6,$7=$I7,$8=VDD)\n"
|
||||
"Circuit INV2PAIR ($1=$I10,$2=$I9,$3=$I8,$4=$I6,$5=$I5,$6=$I3,$7=$I2,$8=$I1):\n"
|
||||
" XINV2 $1 ($1=$I1,IN=$I4,$3=$I8,BULK=$I10,OUT=$I2,VSS=$I5,VDD=$I6)\n"
|
||||
" XINV2 $2 ($1=$I1,IN=$I3,$3=(null),BULK=$I9,OUT=$I4,VSS=$I5,VDD=$I6)\n"
|
||||
"Circuit INV2 ($1=(null),IN=IN,$3=$3,BULK=(null),OUT=OUT,VSS=VSS,VDD=VDD):\n"
|
||||
" DPMOS $1 (S=$3,G=IN,D=VDD) [L=0.25,W=0.95,AS=0.49875,AD=0.26125]\n"
|
||||
" DPMOS $2 (S=VDD,G=$3,D=OUT) [L=0.25,W=0.95,AS=0.26125,AD=0.49875]\n"
|
||||
" DNMOS $3 (S=$3,G=IN,D=VSS) [L=0.25,W=0.95,AS=0.49875,AD=0.26125]\n"
|
||||
" DNMOS $4 (S=VSS,G=$3,D=OUT) [L=0.25,W=0.95,AS=0.26125,AD=0.49875]\n"
|
||||
);
|
||||
|
||||
// do some probing after purging
|
||||
|
||||
// top level
|
||||
EXPECT_EQ (qnet_name (l2n.probe_net (*rmetal2, db::DPoint (0.0, 1.8))), "RINGO:FB");
|
||||
EXPECT_EQ (qnet_name (l2n.probe_net (*rmetal2, db::Point (0, 1800))), "RINGO:FB");
|
||||
EXPECT_EQ (qnet_name (l2n.probe_net (*rmetal2, db::DPoint (-2.0, 1.8))), "(null)");
|
||||
EXPECT_EQ (qnet_name (l2n.probe_net (*rmetal1, db::DPoint (-1.5, 1.8))), "RINGO:FB");
|
||||
EXPECT_EQ (qnet_name (l2n.probe_net (*rmetal1, db::DPoint (24.5, 1.8))), "RINGO:OSC");
|
||||
// the transistor which supplies this probe target has been optimized away by "purge".
|
||||
EXPECT_EQ (qnet_name (l2n.probe_net (*rmetal1, db::DPoint (5.3, 0.0))), "(null)");
|
||||
|
||||
EXPECT_EQ (qnet_name (l2n.probe_net (*rmetal1, db::DPoint (2.6, 1.0))), "INV2PAIR:$I8");
|
||||
EXPECT_EQ (qnet_name (l2n.probe_net (*rmetal1, db::DPoint (6.4, 1.0))), "INV2PAIR:$I4");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -63,11 +63,11 @@ static std::string device_name (const db::Device &device)
|
|||
namespace
|
||||
{
|
||||
|
||||
class MOSFETExtractor
|
||||
class MOSFET3Extractor
|
||||
: public db::NetlistDeviceExtractorMOS3Transistor
|
||||
{
|
||||
public:
|
||||
MOSFETExtractor (const std::string &name, db::Layout *debug_out)
|
||||
MOSFET3Extractor (const std::string &name, db::Layout *debug_out)
|
||||
: db::NetlistDeviceExtractorMOS3Transistor (name), mp_debug_out (debug_out), m_ldiff (0), m_lgate (0)
|
||||
{
|
||||
if (mp_debug_out) {
|
||||
|
|
@ -239,8 +239,8 @@ TEST(1_DeviceAndNetExtraction)
|
|||
// NOTE: the device extractor will add more debug layers for the transistors:
|
||||
// 20/0 -> Diffusion
|
||||
// 21/0 -> Gate
|
||||
MOSFETExtractor pmos_ex ("PMOS", &ly);
|
||||
MOSFETExtractor nmos_ex ("NMOS", &ly);
|
||||
MOSFET3Extractor pmos_ex ("PMOS", &ly);
|
||||
MOSFET3Extractor nmos_ex ("NMOS", &ly);
|
||||
|
||||
db::NetlistDeviceExtractor::input_layers dl;
|
||||
|
||||
|
|
|
|||
|
|
@ -1024,53 +1024,3 @@ TEST(12_NetlistTopology)
|
|||
EXPECT_EQ (td2string (nl.get ()), "c1,c3,c2");
|
||||
EXPECT_EQ (bu2string (nl.get ()), "c2,c3,c1");
|
||||
}
|
||||
|
||||
TEST(13_DeviceGlobalNets)
|
||||
{
|
||||
db::DeviceTerminalDefinition pd;
|
||||
pd.set_name ("name");
|
||||
pd.set_description ("nothing yet");
|
||||
|
||||
db::DeviceTerminalDefinition pd2;
|
||||
pd2.set_name ("name2");
|
||||
pd2.set_description ("now it has something");
|
||||
|
||||
db::DeviceClass dc;
|
||||
dc.set_name ("devname");
|
||||
dc.set_description ("devdesc");
|
||||
dc.add_terminal_definition (pd);
|
||||
dc.add_terminal_definition (pd2);
|
||||
|
||||
db::Device d (&dc);
|
||||
db::Net n;
|
||||
|
||||
d.connect_terminal_global (0, 17);
|
||||
|
||||
db::Device::global_connections_iterator g;
|
||||
|
||||
g = d.begin_global_connections ();
|
||||
EXPECT_EQ (g != d.end_global_connections (), true);
|
||||
EXPECT_EQ (g->first, size_t (0));
|
||||
EXPECT_EQ (g->second, size_t (17));
|
||||
|
||||
++g;
|
||||
EXPECT_EQ (g == d.end_global_connections (), true);
|
||||
|
||||
d.connect_terminal (0, &n);
|
||||
g = d.begin_global_connections ();
|
||||
EXPECT_EQ (g == d.end_global_connections (), true);
|
||||
EXPECT_EQ (d.net_for_terminal (0) == &n, true);
|
||||
|
||||
d.connect_terminal_global (0, 17);
|
||||
EXPECT_EQ (d.net_for_terminal (0) == 0, true);
|
||||
|
||||
g = d.begin_global_connections ();
|
||||
EXPECT_EQ (g != d.end_global_connections (), true);
|
||||
EXPECT_EQ (g->first, size_t (0));
|
||||
EXPECT_EQ (g->second, size_t (17));
|
||||
|
||||
d.connect_terminal (0, 0);
|
||||
g = d.begin_global_connections ();
|
||||
EXPECT_EQ (g == d.end_global_connections (), true);
|
||||
EXPECT_EQ (d.net_for_terminal (0) == 0, true);
|
||||
}
|
||||
|
|
|
|||
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
|
@ -259,13 +259,6 @@ class DBNetlist_TestClass < TestBase
|
|||
|
||||
d2.connect_terminal(0, net)
|
||||
assert_equal(net.terminal_count, 1)
|
||||
d2.connect_terminal_global(0, 1)
|
||||
assert_equal(net.terminal_count, 0)
|
||||
|
||||
assert_equal(d2.terminal_on_global_net(1), 0)
|
||||
assert_equal(d2.terminal_on_global_net(17).inspect, "nil")
|
||||
assert_equal(d2.global_net_on_terminal(0), 1)
|
||||
assert_equal(d2.global_net_on_terminal(1).inspect, "nil")
|
||||
|
||||
end
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue