From 2aaec56adb4409b972c40f98ccce3764f887855c Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Fri, 3 May 2019 23:33:37 +0200 Subject: [PATCH] WIP: netlist browser - extended the net export scheme of build_net to support net annotation and flattening. --- src/db/db/dbLayoutToNetlist.cc | 116 +++++--- src/db/db/dbLayoutToNetlist.h | 80 ++++-- src/db/db/gsiDeclDbLayoutToNetlist.cc | 110 +++++--- .../dbLayoutToNetlistReaderTests.cc | 4 +- src/db/unit_tests/dbLayoutToNetlistTests.cc | 35 ++- .../dbLayoutToNetlistWriterTests.cc | 4 +- src/klayout.pro | 4 +- src/lay/lay/layMainWindow.cc | 10 +- src/lay/lay/layMainWindow.h | 8 +- src/laybasic/laybasic/NetExportDialog.ui | 257 ++++++++++++------ src/laybasic/laybasic/layNetExportDialog.cc | 64 +++++ src/laybasic/laybasic/layNetExportDialog.h | 9 + src/laybasic/laybasic/layNetlistBrowser.cc | 4 + .../laybasic/layNetlistBrowserPage.cc | 79 +++++- src/laybasic/laybasic/layQtTools.cc | 11 +- src/laybasic/laybasic/layQtTools.h | 9 +- .../LEFDEFTechnologyComponentEditor.ui | 32 +-- .../algo/device_extract_au1_rebuild_pf.gds | Bin 0 -> 49364 bytes 18 files changed, 621 insertions(+), 215 deletions(-) create mode 100644 testdata/algo/device_extract_au1_rebuild_pf.gds diff --git a/src/db/db/dbLayoutToNetlist.cc b/src/db/db/dbLayoutToNetlist.cc index 34d23c88e..26ab0ebca 100644 --- a/src/db/db/dbLayoutToNetlist.cc +++ b/src/db/db/dbLayoutToNetlist.cc @@ -484,13 +484,13 @@ namespace } template -static bool deliver_shape (const db::PolygonRef &, StopOnFirst, const Tr &) +static bool deliver_shape (const db::PolygonRef &, StopOnFirst, const Tr &, db::properties_id_type) { return false; } template -static bool deliver_shape (const db::PolygonRef &pr, db::Region ®ion, const Tr &tr) +static bool deliver_shape (const db::PolygonRef &pr, db::Region ®ion, const Tr &tr, db::properties_id_type /*propid*/) { if (pr.obj ().is_box ()) { region.insert (pr.obj ().box ().transformed (pr.trans ()).transformed (tr)); @@ -501,27 +501,41 @@ static bool deliver_shape (const db::PolygonRef &pr, db::Region ®ion, const T } template -static bool deliver_shape (const db::PolygonRef &pr, db::Shapes &shapes, const Tr &tr) +static bool deliver_shape (const db::PolygonRef &pr, db::Shapes &shapes, const Tr &tr, db::properties_id_type propid) { if (pr.obj ().is_box ()) { - shapes.insert (pr.obj ().box ().transformed (pr.trans ()).transformed (tr)); + if (propid) { + shapes.insert (db::BoxWithProperties (pr.obj ().box ().transformed (pr.trans ()).transformed (tr), propid)); + } else { + shapes.insert (pr.obj ().box ().transformed (pr.trans ()).transformed (tr)); + } } else { db::Layout *layout = shapes.layout (); if (layout) { - shapes.insert (db::PolygonRef (pr.obj ().transformed (pr.trans ()).transformed (tr), layout->shape_repository ())); + db::PolygonRef polygon_ref (pr.obj ().transformed (pr.trans ()).transformed (tr), layout->shape_repository ()); + if (propid) { + shapes.insert (db::PolygonRefWithProperties (polygon_ref, propid)); + } else { + shapes.insert (polygon_ref); + } } else { - shapes.insert (pr.obj ().transformed (pr.trans ()).transformed (tr)); + db::Polygon polygon (pr.obj ().transformed (pr.trans ()).transformed (tr)); + if (propid) { + shapes.insert (db::PolygonWithProperties (polygon, propid)); + } else { + shapes.insert (polygon); + } } } return true; } template -static bool deliver_shapes_of_net_recursive (const db::Netlist * /*nl*/, const db::hier_clusters &clusters, db::cell_index_type ci, size_t cid, unsigned int layer_id, const db::ICplxTrans &tr, To &to) +static bool deliver_shapes_of_net_recursive (const db::Netlist * /*nl*/, const db::hier_clusters &clusters, db::cell_index_type ci, size_t cid, unsigned int layer_id, const db::ICplxTrans &tr, To &to, db::properties_id_type propid) { // deliver the net shapes for (db::recursive_cluster_shape_iterator rci (clusters, layer_id, ci, cid); !rci.at_end (); ++rci) { - if (! deliver_shape (*rci, to, tr * rci.trans ())) { + if (! deliver_shape (*rci, to, tr * rci.trans (), propid)) { return false; } } @@ -529,7 +543,7 @@ static bool deliver_shapes_of_net_recursive (const db::Netlist * /*nl*/, const d } template -static bool deliver_shapes_of_net_nonrecursive (const db::Netlist *nl, const db::hier_clusters &clusters, db::cell_index_type ci, size_t cid, unsigned int layer_id, const db::ICplxTrans &tr, To &to) +static bool deliver_shapes_of_net_nonrecursive (const db::Netlist *nl, const db::hier_clusters &clusters, db::cell_index_type ci, size_t cid, unsigned int layer_id, const db::ICplxTrans &tr, To &to, db::properties_id_type propid) { // NOTE: this scheme will deliver the shapes from the cell, including (!) // subcells that are purged @@ -546,7 +560,7 @@ static bool deliver_shapes_of_net_nonrecursive (const db::Netlist *nl, const db: } else { - if (! deliver_shape (*rci, to, tr * rci.trans ())) { + if (! deliver_shape (*rci, to, tr * rci.trans (), propid)) { return false; } prev_ci = cci; @@ -560,17 +574,24 @@ static bool deliver_shapes_of_net_nonrecursive (const db::Netlist *nl, const db: return true; } -void LayoutToNetlist::shapes_of_net (const db::Net &net, const db::Region &of_layer, bool recursive, db::Shapes &to) const +template +static bool deliver_shapes_of_net (bool recursive, const db::Netlist *nl, const db::hier_clusters &clusters, db::cell_index_type ci, size_t cid, unsigned int layer_id, const db::ICplxTrans &tr, To &to, db::properties_id_type propid) +{ + if (recursive) { + return deliver_shapes_of_net_recursive (nl, clusters, ci, cid, layer_id, tr, to, propid); + } else { + return deliver_shapes_of_net_nonrecursive (nl, clusters, ci, cid, layer_id, tr, to, propid); + } +} + + +void LayoutToNetlist::shapes_of_net (const db::Net &net, const db::Region &of_layer, bool recursive, db::Shapes &to, db::properties_id_type propid) const { unsigned int lid = layer_of (of_layer); const db::Circuit *circuit = net.circuit (); tl_assert (circuit != 0); - if (! recursive) { - deliver_shapes_of_net_nonrecursive (mp_netlist.get (), m_net_clusters, circuit->cell_index (), net.cluster_id (), lid, db::ICplxTrans (), to); - } else { - deliver_shapes_of_net_recursive (mp_netlist.get (), m_net_clusters, circuit->cell_index (), net.cluster_id (), lid, db::ICplxTrans (), to); - } + deliver_shapes_of_net (recursive, mp_netlist.get (), m_net_clusters, circuit->cell_index (), net.cluster_id (), lid, db::ICplxTrans (), to, propid); } db::Region *LayoutToNetlist::shapes_of_net (const db::Net &net, const db::Region &of_layer, bool recursive) const @@ -581,26 +602,22 @@ db::Region *LayoutToNetlist::shapes_of_net (const db::Net &net, const db::Region std::auto_ptr res (new db::Region ()); - if (! recursive) { - deliver_shapes_of_net_nonrecursive (mp_netlist.get (), m_net_clusters, circuit->cell_index (), net.cluster_id (), lid, db::ICplxTrans (), *res); - } else { - deliver_shapes_of_net_recursive (mp_netlist.get (), m_net_clusters, circuit->cell_index (), net.cluster_id (), lid, db::ICplxTrans (), *res); - } + deliver_shapes_of_net (recursive, mp_netlist.get (), m_net_clusters, circuit->cell_index (), net.cluster_id (), lid, db::ICplxTrans (), *res, 0); return res.release (); } void -LayoutToNetlist::build_net_rec (const db::Net &net, db::Layout &target, db::Cell &target_cell, const std::map &lmap, const char *net_cell_name_prefix, const char *cell_name_prefix, const char *device_cell_name_prefix, std::map, db::cell_index_type> &cmap, const db::ICplxTrans &tr) const +LayoutToNetlist::build_net_rec (const db::Net &net, db::Layout &target, db::Cell &target_cell, const std::map &lmap, const char *net_cell_name_prefix, db::properties_id_type netname_propid, BuildNetHierarchyMode hier_mode, const char *cell_name_prefix, const char *device_cell_name_prefix, std::map, db::cell_index_type> &cmap, const db::ICplxTrans &tr) const { const db::Circuit *circuit = net.circuit (); tl_assert (circuit != 0); - build_net_rec (circuit->cell_index (), net.cluster_id (), target, target_cell, lmap, &net, net_cell_name_prefix, cell_name_prefix, device_cell_name_prefix, cmap, tr); + build_net_rec (circuit->cell_index (), net.cluster_id (), target, target_cell, lmap, &net, net_cell_name_prefix, netname_propid, hier_mode, cell_name_prefix, device_cell_name_prefix, cmap, tr); } void -LayoutToNetlist::build_net_rec (db::cell_index_type ci, size_t cid, db::Layout &target, db::Cell &tc, const std::map &lmap, const db::Net *net, const char *net_cell_name_prefix, const char *circuit_cell_name_prefix, const char *device_cell_name_prefix, std::map, db::cell_index_type> &cmap, const db::ICplxTrans &tr) const +LayoutToNetlist::build_net_rec (db::cell_index_type ci, size_t cid, db::Layout &target, db::Cell &tc, const std::map &lmap, const db::Net *net, const char *net_cell_name_prefix, db::properties_id_type netname_propid, BuildNetHierarchyMode hier_mode, const char *circuit_cell_name_prefix, const char *device_cell_name_prefix, std::map, db::cell_index_type> &cmap, const db::ICplxTrans &tr) const { db::Cell *target_cell = &tc; @@ -615,7 +632,7 @@ LayoutToNetlist::build_net_rec (db::cell_index_type ci, size_t cid, db::Layout & for (std::map::const_iterator l = lmap.begin (); l != lmap.end () && !consider_cell; ++l) { if (l->second) { StopOnFirst sof; - consider_cell = !deliver_shapes_of_net_nonrecursive (mp_netlist.get (), m_net_clusters, ci, cid, layer_of (*l->second), tr, sof); + consider_cell = !deliver_shapes_of_net (hier_mode == BNH_Flatten, mp_netlist.get (), m_net_clusters, ci, cid, layer_of (*l->second), tr, sof, 0); } } @@ -635,11 +652,11 @@ LayoutToNetlist::build_net_rec (db::cell_index_type ci, size_t cid, db::Layout & for (std::map::const_iterator l = lmap.begin (); l != lmap.end (); ++l) { if (l->second) { - deliver_shapes_of_net_nonrecursive (mp_netlist.get (), m_net_clusters, ci, cid, layer_of (*l->second), tr, target_cell->shapes (l->first)); + deliver_shapes_of_net (hier_mode == BNH_Flatten, mp_netlist.get (), m_net_clusters, ci, cid, layer_of (*l->second), tr, target_cell->shapes (l->first), netname_propid); } } - if (! circuit_cell_name_prefix && ! device_cell_name_prefix) { + if (hier_mode != BNH_SubcircuitCells && ! device_cell_name_prefix) { return; } @@ -673,7 +690,7 @@ LayoutToNetlist::build_net_rec (db::cell_index_type ci, size_t cid, db::Layout & db::cell_index_type target_ci = target.add_cell ((std::string (name_prefix) + cell_name).c_str ()); cm = cmap.insert (std::make_pair (std::make_pair (subci, subcid), target_ci)).first; - build_net_rec (subci, subcid, target, target.cell (target_ci), lmap, 0, 0, circuit_cell_name_prefix, device_cell_name_prefix, cmap, tr_mag); + build_net_rec (subci, subcid, target, target.cell (target_ci), lmap, 0, 0, netname_propid, hier_mode, circuit_cell_name_prefix, device_cell_name_prefix, cmap, tr_mag); } else { cm = cmap.insert (std::make_pair (std::make_pair (subci, subcid), std::numeric_limits::max ())).first; @@ -690,8 +707,24 @@ LayoutToNetlist::build_net_rec (db::cell_index_type ci, size_t cid, db::Layout & } } +db::properties_id_type +LayoutToNetlist::make_netname_propid (db::Layout &ly, const tl::Variant &netname_prop, const db::Net &net) const +{ + if (! netname_prop.is_nil ()) { + + db::property_names_id_type name_propnameid = ly.properties_repository ().prop_name_id (netname_prop); + db::PropertiesRepository::properties_set propset; + propset.insert (std::make_pair (name_propnameid, tl::Variant (net.expanded_name ()))); + + return ly.properties_repository ().properties_id (propset); + + } else { + return 0; + } +} + void -LayoutToNetlist::build_net (const db::Net &net, db::Layout &target, db::Cell &target_cell, const std::map &lmap, const char *cell_name_prefix, const char *device_cell_name_prefix) const +LayoutToNetlist::build_net (const db::Net &net, db::Layout &target, db::Cell &target_cell, const std::map &lmap, const tl::Variant &netname_prop, BuildNetHierarchyMode hier_mode, const char *cell_name_prefix, const char *device_cell_name_prefix) const { if (! m_netlist_extracted) { throw tl::Exception (tl::to_string (tr ("The netlist has not been extracted yet"))); @@ -700,11 +733,13 @@ LayoutToNetlist::build_net (const db::Net &net, db::Layout &target, db::Cell &ta std::map, db::cell_index_type> cell_map; double mag = internal_layout ()->dbu () / target.dbu (); - build_net_rec (net, target, target_cell, lmap, 0, cell_name_prefix, device_cell_name_prefix, cell_map, db::ICplxTrans (mag)); + + db::properties_id_type netname_propid = make_netname_propid (target, netname_prop, net); + build_net_rec (net, target, target_cell, lmap, 0, netname_propid, hier_mode, cell_name_prefix, device_cell_name_prefix, cell_map, db::ICplxTrans (mag)); } void -LayoutToNetlist::build_all_nets (const db::CellMapping &cmap, db::Layout &target, const std::map &lmap, const char *net_cell_name_prefix, const char *circuit_cell_name_prefix, const char *device_cell_name_prefix) const +LayoutToNetlist::build_all_nets (const db::CellMapping &cmap, db::Layout &target, const std::map &lmap, const char *net_cell_name_prefix, const tl::Variant &netname_prop, BuildNetHierarchyMode hier_mode, const char *circuit_cell_name_prefix, const char *device_cell_name_prefix) const { if (! m_netlist_extracted) { throw tl::Exception (tl::to_string (tr ("The netlist has not been extracted yet"))); @@ -727,15 +762,16 @@ LayoutToNetlist::build_all_nets (const db::CellMapping &cmap, db::Layout &target for (db::Circuit::const_net_iterator n = c->begin_nets (); n != c->end_nets (); ++n) { // exlude local nets in recursive mode - if (circuit_cell_name_prefix && ! is_top_circuit && n->pin_count () > 0) { + if (hier_mode != BNH_Disconnected && ! is_top_circuit && n->pin_count () > 0) { continue; } - build_net_rec (*n, target, target.cell (target_ci), lmap, net_cell_name_prefix, circuit_cell_name_prefix, device_cell_name_prefix, cell_map, db::ICplxTrans (mag)); + db::properties_id_type netname_propid = make_netname_propid (target, netname_prop, *n); + build_net_rec (*n, target, target.cell (target_ci), lmap, net_cell_name_prefix, netname_propid, hier_mode, circuit_cell_name_prefix, device_cell_name_prefix, cell_map, db::ICplxTrans (mag)); } - if (circuit_cell_name_prefix) { + if (hier_mode != BNH_Disconnected) { // with recursive nets we skip nets in subcircuits which are connected upwards. This means, nets will // get lost if there is no connection to this pin from the outside. Hence we need to deliver nets from @@ -756,11 +792,13 @@ LayoutToNetlist::build_all_nets (const db::CellMapping &cmap, db::Layout &target double dbu = target.dbu (); db::ICplxTrans tr = db::ICplxTrans (mag) * (db::CplxTrans (dbu).inverted () * subcircuit.trans () * db::CplxTrans (dbu)); + db::properties_id_type netname_propid = make_netname_propid (target, netname_prop, *n); + if (net_cell_name_prefix) { std::string ncn = std::string (net_cell_name_prefix) + subcircuit.expanded_name () + ":"; - build_net_rec (*n, target, target.cell (target_ci), lmap, ncn.c_str (), circuit_cell_name_prefix, device_cell_name_prefix, cell_map, tr); + build_net_rec (*n, target, target.cell (target_ci), lmap, ncn.c_str (), netname_propid, hier_mode, circuit_cell_name_prefix, device_cell_name_prefix, cell_map, tr); } else { - build_net_rec (*n, target, target.cell (target_ci), lmap, net_cell_name_prefix, circuit_cell_name_prefix, device_cell_name_prefix, cell_map, tr); + build_net_rec (*n, target, target.cell (target_ci), lmap, net_cell_name_prefix, netname_propid, hier_mode, circuit_cell_name_prefix, device_cell_name_prefix, cell_map, tr); } } @@ -922,8 +960,8 @@ db::Region LayoutToNetlist::antenna_check (const db::Region &gate, const db::Reg db::Region rgate, rmetal; - deliver_shapes_of_net_recursive (0, m_net_clusters, *cid, *c, layer_of (gate), db::ICplxTrans (), rgate); - deliver_shapes_of_net_recursive (0, m_net_clusters, *cid, *c, layer_of (metal), db::ICplxTrans (), rmetal); + deliver_shapes_of_net_recursive (0, m_net_clusters, *cid, *c, layer_of (gate), db::ICplxTrans (), rgate, 0); + deliver_shapes_of_net_recursive (0, m_net_clusters, *cid, *c, layer_of (metal), db::ICplxTrans (), rmetal, 0); double agate = rgate.area () * dbu * dbu; double ametal = rmetal.area () * dbu * dbu; @@ -934,7 +972,7 @@ db::Region LayoutToNetlist::antenna_check (const db::Region &gate, const db::Reg for (std::vector >::const_iterator d = diodes.begin (); d != diodes.end () && ! skip; ++d) { db::Region rdiode; - deliver_shapes_of_net_recursive (0, m_net_clusters, *cid, *c, layer_of (*d->first), db::ICplxTrans (), rdiode); + deliver_shapes_of_net_recursive (0, m_net_clusters, *cid, *c, layer_of (*d->first), db::ICplxTrans (), rdiode, 0); if (fabs (d->second) < db::epsilon) { if (rdiode.area () > 0) { diff --git a/src/db/db/dbLayoutToNetlist.h b/src/db/db/dbLayoutToNetlist.h index 53c048c15..fc5cabc77 100644 --- a/src/db/db/dbLayoutToNetlist.h +++ b/src/db/db/dbLayoutToNetlist.h @@ -491,32 +491,64 @@ public: * * This methods returns a new'd Region. It's the responsibility of the caller * to delete this object. + * + * propid is an optional properties ID which is attached to the shapes if not 0. */ - void shapes_of_net (const db::Net &net, const db::Region &of_layer, bool recursive, db::Shapes &to) const; + void shapes_of_net (const db::Net &net, const db::Region &of_layer, bool recursive, db::Shapes &to, properties_id_type propid = 0) const; + + /** + * @brief An enum describing the way the net hierarchy is mapped + */ + enum BuildNetHierarchyMode + { + /** + * @brief Flatten the net + * Collects all shapes of a net and puts that into the net cell or circuit cell + */ + BNH_Flatten = 0, + /** + * @brief Build a net hierarchy adding cells for each subcircuit on the net + * Uses the circuit_cell_prefix to build the subcircuit cell names + */ + BNH_SubcircuitCells = 1, + /** + * @brief No hierarchy + * Just output the shapes of the net belonging to the circuit cell. + * Connections are not indicated! + */ + BNH_Disconnected = 2 + }; /** * @brief Builds a net representation in the given layout and cell * - * This method has two modes: recursive and top-level mode. In recursive mode, - * it will create a proper hierarchy below the given target cell to hold all subcircuits the - * net connects to. It will copy the net's parts from this subcircuits into these cells. + * This method puts the shapes of a net into the given target cell using a variety of options + * to represent the net name and the hierarchy of the net. * - * In top-level mode, only the shapes from the net inside it's circuit are copied to - * the given target cell. No other cells are created. + * If the netname_prop name is not nil, a property with the given name is created and assigned + * the net name. * - * Recursive mode is picked when a cell name prefix is given. The new cells will be - * named like cell_name_prefix + circuit name. + * Net hierarchy is covered in three ways: + * * No connection indicated (hier_mode == BNH_Disconnected: the net shapes are simply put into their + * respective circuits. The connections are not indicated. + * * Subnet hierarchy (hier_mode == BNH_SubcircuitCells): for each root net, a full hierarchy is built + * to accommodate the subnets (see build_net in recursive mode). + * * Flat (hier_mode == BNH_Flatten): each net is flattened and put into the circuit it + * belongs to. * * If a device cell name prefix is given, cells will be produced for each device abstract - * using a name like device_cell_name_prefix + device name. + * using a name like device_cell_name_prefix + device name. Otherwise the device shapes are + * treated as part of the net. * * @param target The target layout * @param target_cell The target cell * @param lmap Target layer indexes (keys) and net regions (values) + * @param hier_mode See description of this method + * @param netname_prop An (optional) property name to which to attach the net name * @param cell_name_prefix Chooses recursive mode if non-null * @param device_cell_name_prefix See above */ - void build_net (const db::Net &net, db::Layout &target, db::Cell &target_cell, const std::map &lmap, const char *cell_name_prefix, const char *device_cell_name_prefix) const; + void build_net (const db::Net &net, db::Layout &target, db::Cell &target_cell, const std::map &lmap, const tl::Variant &netname_prop, BuildNetHierarchyMode hier_mode, const char *cell_name_prefix, const char *device_cell_name_prefix) const; /** * @brief Builds a full hierarchical representation of the nets @@ -525,28 +557,37 @@ public: * object to determine the target cell (create them with "cell_mapping_into" or "const_cell_mapping_into"). * If no mapping is requested, the specific circuit it skipped. * - * The method has two net annotation modes: - * * No annotation (net_cell_name_prefix == 0): the shapes will be put into the target cell simply + * The method has three net annotation modes: + * * No annotation (net_cell_name_prefix == 0 and netname_prop == nil): the shapes will be put + * into the target cell simply + * * Net name property (net_cell_name_prefix == 0 and netname_prop != nil): the shapes will be + * annotated with a property named with netname_prop and containing the net name string. * * Individual subcells per net (net_cell_name_prefix != 0): for each net, a subcell is created * and the net shapes will be put there (name of the subcell = net_cell_name_prefix + net name). + * (this mode can be combined with netname_prop too). * - * In addition, net hierarchy is covered in two ways: - * * No connection indicated (circuit_cell_name_prefix == 0: the net shapes are simply put into their + * In addition, net hierarchy is covered in three ways: + * * No connection indicated (hier_mode == BNH_Disconnected: the net shapes are simply put into their * respective circuits. The connections are not indicated. - * * Subnet hierarchy (circuit_cell_name_prefix != 0): for each root net, a full hierarchy is built + * * Subnet hierarchy (hier_mode == BNH_SubcircuitCells): for each root net, a full hierarchy is built * to accommodate the subnets (see build_net in recursive mode). + * * Flat (hier_mode == BNH_Flatten): each net is flattened and put into the circuit it + * belongs to. * * If a device cell name prefix is given, cells will be produced for each device abstract - * using a name like device_cell_name_prefix + device name. + * using a name like device_cell_name_prefix + device name. Otherwise the device shapes are + * treated as part of the net. * * @param cmap The mapping of internal layout to target layout for the circuit mapping * @param target The target layout * @param lmap Target layer indexes (keys) and net regions (values) + * @param hier_mode See description of this method + * @param netname_prop An (optional) property name to which to attach the net name * @param circuit_cell_name_prefix See method description * @param net_cell_name_prefix See method description * @param device_cell_name_prefix See above */ - void build_all_nets (const db::CellMapping &cmap, db::Layout &target, const std::map &lmap, const char *net_cell_name_prefix, const char *circuit_cell_name_prefix, const char *device_cell_name_prefix) const; + void build_all_nets (const db::CellMapping &cmap, db::Layout &target, const std::map &lmap, const char *net_cell_name_prefix, const tl::Variant &netname_prop, BuildNetHierarchyMode hier_mode, const char *circuit_cell_name_prefix, const char *device_cell_name_prefix) const; /** * @brief Finds the net by probing a specific location on the given layer @@ -636,11 +677,12 @@ private: void init (); size_t search_net (const db::ICplxTrans &trans, const db::Cell *cell, const db::local_cluster &test_cluster, std::vector &rev_inst_path); - void build_net_rec (const db::Net &net, db::Layout &target, db::Cell &target_cell, const std::map &lmap, const char *net_cell_name_prefix, const char *cell_name_prefix, const char *device_cell_name_prefix, std::map, db::cell_index_type> &cmap, const ICplxTrans &tr) const; - void build_net_rec (db::cell_index_type ci, size_t cid, db::Layout &target, db::Cell &target_cell, const std::map &lmap, const Net *net, const char *net_cell_name_prefix, const char *cell_name_prefix, const char *device_cell_name_prefix, std::map, db::cell_index_type> &cmap, const ICplxTrans &tr) const; + void build_net_rec (const db::Net &net, db::Layout &target, db::Cell &target_cell, const std::map &lmap, const char *net_cell_name_prefix, db::properties_id_type netname_propid, BuildNetHierarchyMode hier_mode, const char *cell_name_prefix, const char *device_cell_name_prefix, std::map, db::cell_index_type> &cmap, const ICplxTrans &tr) const; + void build_net_rec (db::cell_index_type ci, size_t cid, db::Layout &target, db::Cell &target_cell, const std::map &lmap, const Net *net, const char *net_cell_name_prefix, db::properties_id_type netname_propid, BuildNetHierarchyMode hier_mode, const char *cell_name_prefix, const char *device_cell_name_prefix, std::map, db::cell_index_type> &cmap, const ICplxTrans &tr) const; db::DeepLayer deep_layer_of (const db::Region ®ion) const; void ensure_layout () const; std::string make_new_name (const std::string &stem = std::string ()); + db::properties_id_type make_netname_propid (db::Layout &ly, const tl::Variant &netname_prop, const db::Net &net) const; }; } diff --git a/src/db/db/gsiDeclDbLayoutToNetlist.cc b/src/db/db/gsiDeclDbLayoutToNetlist.cc index 4ad93e599..d419b4111 100644 --- a/src/db/db/gsiDeclDbLayoutToNetlist.cc +++ b/src/db/db/gsiDeclDbLayoutToNetlist.cc @@ -65,19 +65,34 @@ static db::Cell *l2n_internal_top_cell (db::LayoutToNetlist *l2n) return const_cast (l2n->internal_top_cell ()); } -static void build_net (const db::LayoutToNetlist *l2n, const db::Net &net, db::Layout &target, db::Cell &target_cell, const std::map &lmap, const tl::Variant &circuit_cell_name_prefix, const tl::Variant &device_cell_name_prefix) +static int bnh_flatten () +{ + return int (db::LayoutToNetlist::BNH_Flatten); +} + +static int bnh_disconnected () +{ + return int (db::LayoutToNetlist::BNH_Disconnected); +} + +static int bnh_subcircuit_cells () +{ + return int (db::LayoutToNetlist::BNH_SubcircuitCells); +} + +static void build_net (const db::LayoutToNetlist *l2n, const db::Net &net, db::Layout &target, db::Cell &target_cell, const std::map &lmap, const tl::Variant &netname_prop, int hier_mode, const tl::Variant &circuit_cell_name_prefix, const tl::Variant &device_cell_name_prefix) { std::string p = circuit_cell_name_prefix.to_string (); std::string dp = device_cell_name_prefix.to_string (); - l2n->build_net (net, target, target_cell, lmap, circuit_cell_name_prefix.is_nil () ? 0 : p.c_str (), device_cell_name_prefix.is_nil () ? 0 : dp.c_str ()); + l2n->build_net (net, target, target_cell, lmap, netname_prop, (db::LayoutToNetlist::BuildNetHierarchyMode) hier_mode, circuit_cell_name_prefix.is_nil () ? 0 : p.c_str (), device_cell_name_prefix.is_nil () ? 0 : dp.c_str ()); } -static void build_all_nets (const db::LayoutToNetlist *l2n, const db::CellMapping &cmap, db::Layout &target, const std::map &lmap, const tl::Variant &net_cell_name_prefix, const tl::Variant &circuit_cell_name_prefix, const tl::Variant &device_cell_name_prefix) +static void build_all_nets (const db::LayoutToNetlist *l2n, const db::CellMapping &cmap, db::Layout &target, const std::map &lmap, const tl::Variant &net_cell_name_prefix, const tl::Variant &netname_prop, int hier_mode, const tl::Variant &circuit_cell_name_prefix, const tl::Variant &device_cell_name_prefix) { std::string cp = circuit_cell_name_prefix.to_string (); std::string np = net_cell_name_prefix.to_string (); std::string dp = device_cell_name_prefix.to_string (); - l2n->build_all_nets (cmap, target, lmap, net_cell_name_prefix.is_nil () ? 0 : np.c_str (), circuit_cell_name_prefix.is_nil () ? 0 : cp.c_str (), device_cell_name_prefix.is_nil () ? 0 : dp.c_str ()); + l2n->build_all_nets (cmap, target, lmap, net_cell_name_prefix.is_nil () ? 0 : np.c_str (), netname_prop, (db::LayoutToNetlist::BuildNetHierarchyMode) hier_mode, circuit_cell_name_prefix.is_nil () ? 0 : cp.c_str (), device_cell_name_prefix.is_nil () ? 0 : dp.c_str ()); } static std::vector l2n_layer_names (const db::LayoutToNetlist *l2n) @@ -349,66 +364,91 @@ Class decl_dbLayoutToNetlist ("db", "LayoutToNetlist", "If 'recursive'' is true, the returned region will contain the shapes of\n" "all subcircuits too.\n" ) + - gsi::method ("shapes_of_net", (void (db::LayoutToNetlist::*) (const db::Net &, const db::Region &, bool, db::Shapes &) const) &db::LayoutToNetlist::shapes_of_net, gsi::arg ("net"), gsi::arg ("of_layer"), gsi::arg ("recursive"), gsi::arg ("to"), + gsi::method ("shapes_of_net", (void (db::LayoutToNetlist::*) (const db::Net &, const db::Region &, bool, db::Shapes &, db::properties_id_type) const) &db::LayoutToNetlist::shapes_of_net, gsi::arg ("net"), gsi::arg ("of_layer"), gsi::arg ("recursive"), gsi::arg ("to"), gsi::arg ("propid", db::properties_id_type (0), "0"), "@brief Sends all shapes of a specific net and layer to the given Shapes container.\n" "If 'recursive'' is true, the returned region will contain the shapes of\n" "all subcircuits too.\n" + "\"prop_id\" is an optional properties ID. If given, this property set will be attached to the shapes." ) + - gsi::method_ext ("build_net", &build_net, gsi::arg ("net"), gsi::arg ("target"), gsi::arg ("target_cell"), gsi::arg ("lmap"), gsi::arg ("circuit_cell_name_prefix", tl::Variant (), "nil"), gsi::arg ("device_cell_name_prefix", tl::Variant (), "nil"), + gsi::constant ("BNH_Flatten", &bnh_flatten, + "@brief This constant tells \\build_net and \\build_all_nets to flatten the nets (used for the \"hier_mode\" parameter)." + ) + + gsi::constant ("BNH_Disconnected", &bnh_disconnected, + "@brief This constant tells \\build_net and \\build_all_nets to produce local nets without connections to subcircuits (used for the \"hier_mode\" parameter)." + ) + + gsi::constant ("BNH_SubcircuitCells", &bnh_subcircuit_cells, + "@brief This constant tells \\build_net and \\build_all_nets to produce a hierarchy of subcircuit cells per net (used for the \"hier_mode\" parameter)." + ) + + gsi::method_ext ("build_net", &build_net, gsi::arg ("net"), gsi::arg ("target"), gsi::arg ("target_cell"), gsi::arg ("lmap"), gsi::arg ("netname_prop", tl::Variant (), "nil"), gsi::arg ("hier_mode", int (db::LayoutToNetlist::BNH_Flatten), "BNH_Flatten"), gsi::arg ("circuit_cell_name_prefix", tl::Variant (), "nil"), gsi::arg ("device_cell_name_prefix", tl::Variant (), "nil"), "@brief Builds a net representation in the given layout and cell\n" "\n" - "This method has two modes: recursive and top-level mode. In recursive mode,\n" - "it will create a proper hierarchy below the given target cell to hold all subcircuits the\n" - "net connects to. It will copy the net's parts from this subcircuits into these cells.\n" + "This method puts the shapes of a net into the given target cell using a variety of options\n" + "to represent the net name and the hierarchy of the net.\n" "\n" - "In top-level mode, only the shapes from the net inside it's circuit are copied to\n" - "the given target cell. No other cells are created.\n" + "If the netname_prop name is not nil, a property with the given name is created and assigned\n" + "the net name.\n" "\n" - "Recursive mode is picked when a circuit cell name prefix is given. The new cells will be\n" - "named like circuit_cell_name_prefix + circuit name.\n" - "\n" - "If a device cell name prefix is given, device shapes will be output on device cells named\n" - "like device_cell_name_prefix + device name.\n" + "Net hierarchy is covered in three ways:\n" + "@ul\n" + " @li No connection indicated (hier_mode == \\BNH_Disconnected: the net shapes are simply put into their\n" + " respective circuits. The connections are not indicated. @/li\n" + " @li Subnet hierarchy (hier_mode == \\BNH_SubcircuitCells): for each root net, a full hierarchy is built\n" + " to accommodate the subnets (see build_net in recursive mode). @/li\n" + " @li Flat (hier_mode == \\BNH_Flatten): each net is flattened and put into the circuit it\n" + " belongs to. @/li\n" + "@/ul\n" + "If a device cell name prefix is given, cells will be produced for each device abstract\n" + "using a name like device_cell_name_prefix + device name. Otherwise the device shapes are\n" + "treated as part of the net.\n" "\n" "@param target The target layout\n" "@param target_cell The target cell\n" "@param lmap Target layer indexes (keys) and net regions (values)\n" - "@param circuit_cell_name_prefix Chooses recursive mode if non-nil\n" - "@param device_cell_name_prefix If given, devices will be output as separate cells\n" + "@param hier_mode See description of this method\n" + "@param netname_prop An (optional) property name to which to attach the net name\n" + "@param cell_name_prefix Chooses recursive mode if non-null\n" + "@param device_cell_name_prefix See above\n" ) + - gsi::method_ext ("build_all_nets", &build_all_nets, gsi::arg ("cmap"), gsi::arg ("target"), gsi::arg ("lmap"), gsi::arg ("net_cell_name_prefix", tl::Variant (), "nil"), gsi::arg ("circuit_cell_name_prefix", tl::Variant (), "nil"), gsi::arg ("device_cell_name_prefix", tl::Variant (), "nil"), + gsi::method_ext ("build_all_nets", &build_all_nets, gsi::arg ("cmap"), gsi::arg ("target"), gsi::arg ("lmap"), gsi::arg ("net_cell_name_prefix", tl::Variant (), "nil"), gsi::arg ("netname_prop", tl::Variant (), "nil"), gsi::arg ("hier_mode", int (db::LayoutToNetlist::BNH_Flatten), "BNH_Flatten"), gsi::arg ("circuit_cell_name_prefix", tl::Variant (), "nil"), gsi::arg ("device_cell_name_prefix", tl::Variant (), "nil"), "@brief Builds a full hierarchical representation of the nets\n" "\n" "This method copies all nets into cells corresponding to the circuits. It uses the cmap\n" - "object to determine the target cell (create them with \\cell_mapping_into or \\const_cell_mapping_into.\n" + "object to determine the target cell (create them with \"cell_mapping_into\" or \"const_cell_mapping_into\").\n" "If no mapping is requested, the specific circuit it skipped.\n" "\n" - "The method has two net annotation modes:\n" - "\n" + "The method has three net annotation modes:\n" "@ul\n" - "@li 'No annotation'' (net_cell_name_prefix == 0): the shapes will be put into the target cell simply @/li\n" - "@li Individual subcells per net (net_cell_name_prefix != 0): for each net, a subcell is created\n" - " and the net shapes will be put there (name of the subcell = net_cell_name_prefix + net name). @/li\n" + " @li No annotation (net_cell_name_prefix == nil and netname_prop == nil): the shapes will be put\n" + " into the target cell simply. @/li\n" + " @li Net name property (net_cell_name_prefix == nil and netname_prop != nil): the shapes will be\n" + " annotated with a property named with netname_prop and containing the net name string. @/li\n" + " @li Individual subcells per net (net_cell_name_prefix != 0): for each net, a subcell is created\n" + " and the net shapes will be put there (name of the subcell = net_cell_name_prefix + net name).\n" + " (this mode can be combined with netname_prop too). @/li\n" "@/ul\n" "\n" - "In addition, net hierarchy is covered in two ways:\n" - "\n" + "In addition, net hierarchy is covered in three ways:\n" "@ul\n" - "@li No connection indicated (circuit_cell_name_prefix == 0: the net shapes are simply put into their\n" - " respective circuits. The connections are not indicated. @/li\n" - "@li Subnet hierarchy (circuit_cell_name_prefix != 0): for each root net, a full hierarchy is built\n" - " to accommodate the subnets (see build_net in recursive mode). @/li\n" + " @li No connection indicated (hier_mode == \\BNH_Disconnected: the net shapes are simply put into their\n" + " respective circuits. The connections are not indicated. @/li\n" + " @li Subnet hierarchy (hier_mode == \\BNH_SubcircuitCells): for each root net, a full hierarchy is built\n" + " to accommodate the subnets (see build_net in recursive mode). @/li\n" + " @li Flat (hier_mode == \\BNH_Flatten): each net is flattened and put into the circuit it\n" + " belongs to. @/li\n" "@/ul\n" "\n" - "If a device name prefix is given, device shapes will be output on device cells named\n" - "like device_name_prefix + device name.\n" + "If a device cell name prefix is given, cells will be produced for each device abstract\n" + "using a name like device_cell_name_prefix + device name. Otherwise the device shapes are\n" + "treated as part of the net.\n" "\n" "@param cmap The mapping of internal layout to target layout for the circuit mapping\n" "@param target The target layout\n" "@param lmap Target layer indexes (keys) and net regions (values)\n" - "@param net_cell_name_prefix See method description\n" + "@param hier_mode See description of this method\n" + "@param netname_prop An (optional) property name to which to attach the net name\n" "@param circuit_cell_name_prefix See method description\n" - "@param device_cell_name_prefix If given, devices will be output as separate cells\n" + "@param net_cell_name_prefix See method description\n" + "@param device_cell_name_prefix See above\n" ) + gsi::method ("probe_net", (db::Net *(db::LayoutToNetlist::*) (const db::Region &, const db::DPoint &)) &db::LayoutToNetlist::probe_net, gsi::arg ("of_layer"), gsi::arg ("point"), "@brief Finds the net by probing a specific location on the given layer\n" diff --git a/src/db/unit_tests/dbLayoutToNetlistReaderTests.cc b/src/db/unit_tests/dbLayoutToNetlistReaderTests.cc index c7dc80031..b9bcb8629 100644 --- a/src/db/unit_tests/dbLayoutToNetlistReaderTests.cc +++ b/src/db/unit_tests/dbLayoutToNetlistReaderTests.cc @@ -81,7 +81,7 @@ TEST(1_ReaderBasic) lmap [ly2.insert_layer (db::LayerProperties (7, 0)) ] = l2n.layer_by_name ("via1"); lmap [ly2.insert_layer (db::LayerProperties (8, 0)) ] = l2n.layer_by_name ("metal2"); - l2n.build_all_nets (cm, ly2, lmap, "NET_", 0, "DEVICE_"); + l2n.build_all_nets (cm, ly2, lmap, "NET_", tl::Variant (), db::LayoutToNetlist::BNH_Disconnected, 0, "DEVICE_"); std::string au = tl::testsrc (); au = tl::combine_path (au, "testdata"); @@ -177,7 +177,7 @@ TEST(2_ReaderWithGlobalNets) lmap [ly2.insert_layer (db::LayerProperties (7, 0)) ] = l2n.layer_by_name ("via1"); lmap [ly2.insert_layer (db::LayerProperties (8, 0)) ] = l2n.layer_by_name ("metal2"); - l2n.build_all_nets (cm, ly2, lmap, "NET_", "CIRCUIT_", "DEVICE_"); + l2n.build_all_nets (cm, ly2, lmap, "NET_", tl::Variant (), db::LayoutToNetlist::BNH_SubcircuitCells, "CIRCUIT_", "DEVICE_"); std::string au = tl::testsrc (); au = tl::combine_path (au, "testdata"); diff --git a/src/db/unit_tests/dbLayoutToNetlistTests.cc b/src/db/unit_tests/dbLayoutToNetlistTests.cc index b2d8b8fe9..9d4a39438 100644 --- a/src/db/unit_tests/dbLayoutToNetlistTests.cc +++ b/src/db/unit_tests/dbLayoutToNetlistTests.cc @@ -397,7 +397,7 @@ TEST(1_BasicExtraction) lmap [ly2.insert_layer (db::LayerProperties (7, 0)) ] = rvia1.get (); lmap [ly2.insert_layer (db::LayerProperties (8, 0)) ] = rmetal2.get (); - l2n.build_all_nets (cm, ly2, lmap, 0, 0, 0); + l2n.build_all_nets (cm, ly2, lmap, 0, tl::Variant (), db::LayoutToNetlist::BNH_Disconnected, 0, 0); std::string au = tl::testsrc (); au = tl::combine_path (au, "testdata"); @@ -424,7 +424,7 @@ TEST(1_BasicExtraction) lmap [ly2.insert_layer (db::LayerProperties (7, 0)) ] = rvia1.get (); lmap [ly2.insert_layer (db::LayerProperties (8, 0)) ] = rmetal2.get (); - l2n.build_all_nets (cm, ly2, lmap, "NET_", 0, 0); + l2n.build_all_nets (cm, ly2, lmap, "NET_", tl::Variant (), db::LayoutToNetlist::BNH_Disconnected, 0, 0); std::string au = tl::testsrc (); au = tl::combine_path (au, "testdata"); @@ -451,7 +451,7 @@ TEST(1_BasicExtraction) lmap [ly2.insert_layer (db::LayerProperties (7, 0)) ] = rvia1.get (); lmap [ly2.insert_layer (db::LayerProperties (8, 0)) ] = rmetal2.get (); - l2n.build_all_nets (cm, ly2, lmap, 0, "CIRCUIT_", 0); + l2n.build_all_nets (cm, ly2, lmap, 0, tl::Variant (), db::LayoutToNetlist::BNH_SubcircuitCells, "CIRCUIT_", 0); std::string au = tl::testsrc (); au = tl::combine_path (au, "testdata"); @@ -478,7 +478,34 @@ TEST(1_BasicExtraction) lmap [ly2.insert_layer (db::LayerProperties (7, 0)) ] = rvia1.get (); lmap [ly2.insert_layer (db::LayerProperties (8, 0)) ] = rmetal2.get (); - l2n.build_all_nets (cm, ly2, lmap, "NET_", "CIRCUIT_", "DEVICE_"); + l2n.build_all_nets (cm, ly2, lmap, 0, tl::Variant (42), db::LayoutToNetlist::BNH_Flatten, 0, 0); + + std::string au = tl::testsrc (); + au = tl::combine_path (au, "testdata"); + au = tl::combine_path (au, "algo"); + au = tl::combine_path (au, "device_extract_au1_rebuild_pf.gds"); + + db::compare_layouts (_this, ly2, au); + } + + { + db::Layout ly2; + ly2.dbu (ly.dbu ()); + db::Cell &top2 = ly2.cell (ly2.add_cell ("TOP")); + + db::CellMapping cm = l2n.cell_mapping_into (ly2, top2, true /*with device cells*/); + + std::map lmap; + lmap [ly2.insert_layer (db::LayerProperties (10, 0))] = &rpsd; + lmap [ly2.insert_layer (db::LayerProperties (11, 0))] = &rnsd; + lmap [ly2.insert_layer (db::LayerProperties (3, 0)) ] = rpoly.get (); + lmap [ly2.insert_layer (db::LayerProperties (4, 0)) ] = rdiff_cont.get (); + lmap [ly2.insert_layer (db::LayerProperties (5, 0)) ] = rpoly_cont.get (); + lmap [ly2.insert_layer (db::LayerProperties (6, 0)) ] = rmetal1.get (); + lmap [ly2.insert_layer (db::LayerProperties (7, 0)) ] = rvia1.get (); + lmap [ly2.insert_layer (db::LayerProperties (8, 0)) ] = rmetal2.get (); + + l2n.build_all_nets (cm, ly2, lmap, "NET_", tl::Variant (), db::LayoutToNetlist::BNH_SubcircuitCells, "CIRCUIT_", "DEVICE_"); std::string au = tl::testsrc (); au = tl::combine_path (au, "testdata"); diff --git a/src/db/unit_tests/dbLayoutToNetlistWriterTests.cc b/src/db/unit_tests/dbLayoutToNetlistWriterTests.cc index 426e69980..3deaa0e77 100644 --- a/src/db/unit_tests/dbLayoutToNetlistWriterTests.cc +++ b/src/db/unit_tests/dbLayoutToNetlistWriterTests.cc @@ -226,7 +226,7 @@ TEST(1_WriterBasic) lmap [ly2.insert_layer (db::LayerProperties (7, 0)) ] = rvia1.get (); lmap [ly2.insert_layer (db::LayerProperties (8, 0)) ] = rmetal2.get (); - l2n.build_all_nets (cm, ly2, lmap, "NET_", 0, "DEVICE_"); + l2n.build_all_nets (cm, ly2, lmap, "NET_", tl::Variant (), db::LayoutToNetlist::BNH_Disconnected, 0, "DEVICE_"); std::string au = tl::testsrc (); au = tl::combine_path (au, "testdata"); @@ -452,7 +452,7 @@ TEST(2_WriterWithGlobalNets) lmap [ly2.insert_layer (db::LayerProperties (7, 0)) ] = rvia1.get (); lmap [ly2.insert_layer (db::LayerProperties (8, 0)) ] = rmetal2.get (); - l2n.build_all_nets (cm, ly2, lmap, "NET_", "CIRCUIT_", "DEVICE_"); + l2n.build_all_nets (cm, ly2, lmap, "NET_", tl::Variant (), db::LayoutToNetlist::BNH_SubcircuitCells, "CIRCUIT_", "DEVICE_"); std::string au = tl::testsrc (); au = tl::combine_path (au, "testdata"); diff --git a/src/klayout.pro b/src/klayout.pro index 196fd9638..2c465c722 100644 --- a/src/klayout.pro +++ b/src/klayout.pro @@ -101,4 +101,6 @@ unit_tests.depends += plugins $$MAIN_DEPENDS RESOURCES += \ laybasic/laybasic/layResources.qrc \ - laybasic/laybasic/layResources.qrc + laybasic/laybasic/layResources.qrc \ + plugins/streamers/lay/lay/layResources.qrc \ + plugins/streamers/lay/lay/layResources.qrc diff --git a/src/lay/lay/layMainWindow.cc b/src/lay/lay/layMainWindow.cc index c308eeeb4..d30277913 100644 --- a/src/lay/lay/layMainWindow.cc +++ b/src/lay/lay/layMainWindow.cc @@ -466,7 +466,7 @@ MainWindow::MainWindow (QApplication *app, lay::Plugin *plugin_parent, const cha init_menu (); - lay::register_help_handler (this, SLOT (show_help (const QString &))); + lay::register_help_handler (this, SLOT (show_help (const QString &)), SLOT (show_modal_help (const QString &))); mp_assistant = new lay::HelpDialog (this); @@ -697,7 +697,7 @@ MainWindow::~MainWindow () } tl::DeferredMethodScheduler::instance ()->enable (false); - lay::register_help_handler (0, 0); + lay::register_help_handler (0, 0, 0); // since the configuration actions unregister themselves, we need to do this before the main // window is gone: @@ -4783,6 +4783,12 @@ MainWindow::show_help (const QString &url) show_assistant_url (tl::to_string (url), false); } +void +MainWindow::show_modal_help (const QString &url) +{ + show_assistant_url (tl::to_string (url), true); +} + void MainWindow::show_assistant_url (const std::string &url, bool modal) { diff --git a/src/lay/lay/layMainWindow.h b/src/lay/lay/layMainWindow.h index 2d18a1d01..6cc2017cd 100644 --- a/src/lay/lay/layMainWindow.h +++ b/src/lay/lay/layMainWindow.h @@ -832,11 +832,17 @@ public slots: void cm_navigator_close (); /** - * @brief shows the given URL as a model help window + * @brief shows the given URL as a non-modal help window * Intended as a connection target for QLabel linkVisisted signals. */ void show_help (const QString &url); + /** + * @brief shows the given URL as a modal help window + * Intended as a connection target for QLabel linkVisisted signals. + */ + void show_modal_help (const QString &url); + /** * @brief visibility of one of the dock widgets changed */ diff --git a/src/laybasic/laybasic/NetExportDialog.ui b/src/laybasic/laybasic/NetExportDialog.ui index c21cb805b..3d35742f0 100644 --- a/src/laybasic/laybasic/NetExportDialog.ui +++ b/src/laybasic/laybasic/NetExportDialog.ui @@ -6,94 +6,45 @@ 0 0 - 531 - 321 + 508 + 454 Export Net Options - - - - - - - Produce cells for devices + + + + Qt::Vertical - + + QSizePolicy::Fixed + + + + 20 + 10 + + + - + Produce cells for circuits - - - - - - - Qt::Horizontal - - - QSizePolicy::Fixed - - - - 10 - 20 - - - - - - - - Qt::Horizontal - - - QSizePolicy::Fixed - - - - 10 - 20 - - - - - - + + - Net cell name prefix + Cell name prefix - - - - - - - If this options is selected, each device will be represented by one cell made from the device name and the given prefix. Otherwise, device parts are exported as shapes inside the net. - - - true - - - - - - - QDialogButtonBox::Cancel|QDialogButtonBox::Ok - - - - + Qt::Vertical @@ -106,21 +57,34 @@ - - - - Cell name prefix + + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok - - + + + + + - Cell name prefix + Produce cells for devices - + + + + If this option is selected, the subcircuits on a net are represented by individual cells. Otherwise the net is exported as a whole into a single cell (flattened). + + + true + + + + Qt::Vertical @@ -136,17 +100,96 @@ - - + + - If this option is selected, the subcircuits on a net are represented by individual cells. Otherwise the net is exported as a whole into a single cell (flattened). + This feature will export the nets to a new layout. This layout will contain the original hierarchy as far as required. Within each cell, that corresponds to a circuit, a cell is generated for each net. Each net has it's own hierarchy which can be tailored with the options below. true - + + + + Cell name prefix + + + + + + + + + + Starting layer number for unknown layers + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 10 + 20 + + + + + + + + If this options is selected, each device will be represented by one cell made from the device name and the given prefix. Otherwise, device parts are exported as shapes inside the net. + + + true + + + + + + + + + + Net cell name prefix + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 10 + 20 + + + + + + + + + 0 + 0 + + + + + Qt::Vertical @@ -162,8 +205,58 @@ + + + + QFrame::NoFrame + + + QFrame::Raised + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + + + <html><body>(<a href="int:/about/variant_notation.xml">See here for the name notation</a>)</body></html> + + + + + + + + + + Net property name + + + + + net_cell_prefix + net_propname + produce_circuit_cells_cb + circuit_cell_prefix + produce_device_cells_cb + device_cell_prefix + layernum + diff --git a/src/laybasic/laybasic/layNetExportDialog.cc b/src/laybasic/laybasic/layNetExportDialog.cc index 7a8da60c9..810dd8eb0 100644 --- a/src/laybasic/laybasic/layNetExportDialog.cc +++ b/src/laybasic/laybasic/layNetExportDialog.cc @@ -22,6 +22,9 @@ #include "layNetExportDialog.h" #include "layPlugin.h" +#include "layQtTools.h" + +#include "tlExceptions.h" #include "ui_NetExportDialog.h" @@ -31,10 +34,12 @@ namespace lay { extern std::string cfg_l2ndb_net_cell_prefix; +extern std::string cfg_l2ndb_net_propname; extern std::string cfg_l2ndb_circuit_cell_prefix; extern std::string cfg_l2ndb_produce_circuit_cells; extern std::string cfg_l2ndb_device_cell_prefix; extern std::string cfg_l2ndb_produce_device_cells; +extern std::string cfg_l2ndb_start_layer_number; NetExportDialog::NetExportDialog (QWidget *parent) @@ -42,6 +47,8 @@ NetExportDialog::NetExportDialog (QWidget *parent) { ui = new Ui::NetExportDialog (); ui->setupUi (this); + + lay::activate_modal_help_links (ui->help_label); } NetExportDialog::~NetExportDialog () @@ -62,6 +69,29 @@ NetExportDialog::net_prefix () return tl::to_string (ui->net_cell_prefix->text ()); } +void +NetExportDialog::set_net_propname (const tl::Variant &net_propname) +{ + if (net_propname.is_nil ()) { + ui->net_propname->setText (QString ()); + } else { + ui->net_propname->setText (tl::to_qstring (net_propname.to_parsable_string ())); + } +} + +tl::Variant +NetExportDialog::net_propname () +{ + std::string np = tl::to_string (ui->net_propname->text ()); + tl::Extractor ex (np.c_str ()); + tl::Variant v; + if (! ex.at_end ()) { + ex.read (v); + ex.expect_end (); + } + return v; +} + void NetExportDialog::set_produce_circuit_cells (bool f) { @@ -112,6 +142,30 @@ NetExportDialog::device_cell_prefix () return tl::to_string (ui->device_cell_prefix->text ()); } +void +NetExportDialog::set_start_layer_number (int ln) +{ + ui->layernum->setText (tl::to_qstring (tl::to_string (ln))); +} + +int +NetExportDialog::start_layer_number () +{ + int ln = 0; + tl::from_string (tl::to_string (ui->layernum->text ()), ln); + return ln; +} + +void +NetExportDialog::accept () +{ +BEGIN_PROTECTED + start_layer_number (); + net_propname (); + QDialog::accept (); +END_PROTECTED +} + int NetExportDialog::exec (lay::PluginRoot *plugin_root) { @@ -119,6 +173,10 @@ NetExportDialog::exec (lay::PluginRoot *plugin_root) plugin_root->config_get (cfg_l2ndb_net_cell_prefix, v); set_net_prefix (v); + tl::Variant var; + plugin_root->config_get (cfg_l2ndb_net_propname, var); + set_net_propname (var); + bool f = false; plugin_root->config_get (cfg_l2ndb_produce_circuit_cells, f); set_produce_circuit_cells (f); @@ -135,10 +193,16 @@ NetExportDialog::exec (lay::PluginRoot *plugin_root) plugin_root->config_get (cfg_l2ndb_device_cell_prefix, v); set_device_cell_prefix (v); + int ln = 0; + plugin_root->config_get (cfg_l2ndb_start_layer_number, ln); + set_start_layer_number (ln); + int ret = QDialog::exec (); if (ret) { plugin_root->config_set (cfg_l2ndb_net_cell_prefix, net_prefix ()); + plugin_root->config_set (cfg_l2ndb_net_propname, net_propname ()); + plugin_root->config_set (cfg_l2ndb_start_layer_number, tl::to_string (start_layer_number ())); plugin_root->config_set (cfg_l2ndb_produce_circuit_cells, tl::to_string (produce_circuit_cells ())); plugin_root->config_set (cfg_l2ndb_circuit_cell_prefix, circuit_cell_prefix ()); plugin_root->config_set (cfg_l2ndb_produce_device_cells, tl::to_string (produce_device_cells ())); diff --git a/src/laybasic/laybasic/layNetExportDialog.h b/src/laybasic/laybasic/layNetExportDialog.h index ab296445b..696ccf867 100644 --- a/src/laybasic/laybasic/layNetExportDialog.h +++ b/src/laybasic/laybasic/layNetExportDialog.h @@ -56,6 +56,9 @@ public: void set_net_prefix (const std::string &net_prefix); std::string net_prefix (); + void set_net_propname (const tl::Variant &net_propname); + tl::Variant net_propname (); + void set_produce_circuit_cells (bool f); bool produce_circuit_cells (); @@ -68,8 +71,14 @@ public: void set_device_cell_prefix (const std::string &net_prefix); std::string device_cell_prefix (); + void set_start_layer_number (int ln); + int start_layer_number (); + int exec (lay::PluginRoot *mp_plugin_root); +protected: + void accept (); + private: Ui::NetExportDialog *ui; }; diff --git a/src/laybasic/laybasic/layNetlistBrowser.cc b/src/laybasic/laybasic/layNetlistBrowser.cc index cebce2f33..d04164b7d 100644 --- a/src/laybasic/laybasic/layNetlistBrowser.cc +++ b/src/laybasic/laybasic/layNetlistBrowser.cc @@ -51,6 +51,8 @@ extern const std::string cfg_l2ndb_max_shapes_highlighted ("l2ndb-max-shapes-hig extern const std::string cfg_l2ndb_show_all ("l2ndb-show-all"); extern const std::string cfg_l2ndb_window_state ("l2ndb-window-state"); extern const std::string cfg_l2ndb_net_cell_prefix ("l2ndb-net-cell-prefix"); +extern const std::string cfg_l2ndb_net_propname ("l2ndb-net-propname"); +extern const std::string cfg_l2ndb_start_layer_number ("l2ndb-start-layer-number"); extern const std::string cfg_l2ndb_circuit_cell_prefix ("l2ndb-circuit-cell-prefix"); extern const std::string cfg_l2ndb_produce_circuit_cells ("l2ndb-produce-circuit-cells"); extern const std::string cfg_l2ndb_device_cell_prefix ("l2ndb-device-cell-prefix"); @@ -340,7 +342,9 @@ public: options.push_back (std::pair (cfg_l2ndb_marker_intensity, "50")); options.push_back (std::pair (cfg_l2ndb_show_all, "true")); options.push_back (std::pair (cfg_l2ndb_window_state, "")); + options.push_back (std::pair (cfg_l2ndb_net_propname, "")); options.push_back (std::pair (cfg_l2ndb_net_cell_prefix, "NET_")); + options.push_back (std::pair (cfg_l2ndb_start_layer_number, "1000")); options.push_back (std::pair (cfg_l2ndb_produce_circuit_cells, "false")); options.push_back (std::pair (cfg_l2ndb_circuit_cell_prefix, "CIRCUIT_")); options.push_back (std::pair (cfg_l2ndb_produce_device_cells, "false")); diff --git a/src/laybasic/laybasic/layNetlistBrowserPage.cc b/src/laybasic/laybasic/layNetlistBrowserPage.cc index 344dadc1e..8bbda7fa2 100644 --- a/src/laybasic/laybasic/layNetlistBrowserPage.cc +++ b/src/laybasic/laybasic/layNetlistBrowserPage.cc @@ -31,6 +31,8 @@ #include "tlProgress.h" #include "dbLayoutToNetlist.h" #include "dbNetlistDeviceClasses.h" +#include "dbCellMapping.h" +#include "dbLayerMapping.h" #include #include @@ -2490,15 +2492,6 @@ NetlistBrowserPage::clear_markers () mp_markers.clear (); } -void -NetlistBrowserPage::export_all () -{ - std::auto_ptr dialog (new lay::NetExportDialog (this)); - if (dialog->exec (mp_plugin_root)) { - // @@@ - } -} - void NetlistBrowserPage::export_selected () { @@ -2508,5 +2501,73 @@ NetlistBrowserPage::export_selected () } } +void +NetlistBrowserPage::export_all () +{ + if (! mp_view || ! mp_database.get () || ! mp_database->internal_layout ()) { + return; + } + + const db::Layout &source_layout = *mp_database->internal_layout (); + if (source_layout.begin_top_down () == source_layout.end_top_cells ()) { + // nothing to export + return; + } + + const db::Cell &source_top = source_layout.cell (*source_layout.begin_top_down ()); + + std::auto_ptr dialog (new lay::NetExportDialog (this)); + if (dialog->exec (mp_plugin_root)) { + + // NOTE: mp_view and database might get reset to 0 in create_layout + lay::LayoutView *view = mp_view; + db::LayoutToNetlist *database = mp_database.get (); + + unsigned int cv_index = view->create_layout (true); + db::Layout &target_layout = view->cellview (cv_index)->layout (); + + db::cell_index_type target_top_index = target_layout.add_cell (source_layout.cell_name (source_top.cell_index ())); + + db::CellMapping cm = database->cell_mapping_into (target_layout, target_layout.cell (target_top_index)); + + std::map lm; + { + // create a layer mapping + + std::set layers_to_copy; + const db::Connectivity &conn = database->connectivity (); + for (db::Connectivity::layer_iterator layer = conn.begin_layers (); layer != conn.end_layers (); ++layer) { + layers_to_copy.insert (*layer); + } + + int ln = dialog->start_layer_number (); + + for (std::set::const_iterator l = layers_to_copy.begin (); l != layers_to_copy.end (); ++l) { + const db::LayerProperties &lp = source_layout.get_properties (*l); + unsigned int tl; + if (! lp.is_null ()) { + tl = target_layout.insert_layer (lp); + } else { + tl = target_layout.insert_layer (db::LayerProperties (ln++, 0, database->name (*l))); + } + lm.insert (std::make_pair (tl, database->layer_by_index (*l))); + } + } + + database->build_all_nets (cm, target_layout, lm, + dialog->net_prefix ().empty () ? 0 : dialog->net_prefix ().c_str (), + dialog->net_propname (), + dialog->produce_circuit_cells () ? db::LayoutToNetlist::BNH_SubcircuitCells : db::LayoutToNetlist::BNH_Flatten, + dialog->produce_circuit_cells () ? dialog->circuit_cell_prefix ().c_str () : 0, + dialog->produce_device_cells () ? dialog->device_cell_prefix ().c_str () : 0); + + view->zoom_fit (); + view->max_hier (); + view->add_missing_layers (); + view->select_cell (target_top_index, cv_index); + + } +} + } diff --git a/src/laybasic/laybasic/layQtTools.cc b/src/laybasic/laybasic/layQtTools.cc index 220f2c8d9..4b3847131 100644 --- a/src/laybasic/laybasic/layQtTools.cc +++ b/src/laybasic/laybasic/layQtTools.cc @@ -42,6 +42,7 @@ namespace lay QObject *s_help_handler = 0; const char *s_help_slot = 0; +const char *s_modal_help_slot = 0; void activate_help_links (QLabel *label) { @@ -50,10 +51,18 @@ void activate_help_links (QLabel *label) } } -void register_help_handler (QObject *object, const char *slot) +void activate_modal_help_links (QLabel *label) +{ + if (s_help_handler) { + QObject::connect (label, SIGNAL (linkActivated (const QString &)), s_help_handler, s_modal_help_slot); + } +} + +void register_help_handler (QObject *object, const char *slot, const char *modal_slot) { s_help_handler = object; s_help_slot = slot; + s_modal_help_slot = modal_slot; } // -------------------------------------------------------------------------------- diff --git a/src/laybasic/laybasic/layQtTools.h b/src/laybasic/laybasic/layQtTools.h index 5d6465533..3e66e4f89 100644 --- a/src/laybasic/laybasic/layQtTools.h +++ b/src/laybasic/laybasic/layQtTools.h @@ -53,9 +53,14 @@ LAYBASIC_PUBLIC void restore_dialog_state (QWidget *dialog, const std::string &s LAYBASIC_PUBLIC void activate_help_links (QLabel *label); /** - * @brief Register the help handler (object and slot) + * @brief A utility function connecting a label's linkActivated event with the help browser (modal help dialogs) */ -LAYBASIC_PUBLIC void register_help_handler (QObject *object, const char *slot); +LAYBASIC_PUBLIC void activate_modal_help_links (QLabel *label); + +/** + * @brief Register the help handler (object and slots for non-modal and modal help dialogs) + */ +LAYBASIC_PUBLIC void register_help_handler (QObject *object, const char *slot, const char *modal_slot); } // namespace lay diff --git a/src/plugins/streamers/lefdef/lay_plugin/LEFDEFTechnologyComponentEditor.ui b/src/plugins/streamers/lefdef/lay_plugin/LEFDEFTechnologyComponentEditor.ui index 7ec26182d..f72dc2a35 100644 --- a/src/plugins/streamers/lefdef/lay_plugin/LEFDEFTechnologyComponentEditor.ui +++ b/src/plugins/streamers/lefdef/lay_plugin/LEFDEFTechnologyComponentEditor.ui @@ -113,7 +113,7 @@ ... - + :/clear.png:/clear.png @@ -140,7 +140,7 @@ ... - + :/down.png:/down.png @@ -167,7 +167,7 @@ ... - + :/add.png:/add.png @@ -181,7 +181,7 @@ ... - + :/up.png:/up.png @@ -270,7 +270,7 @@ - :/right.png + :/right.png @@ -292,7 +292,7 @@ - :/right.png + :/right.png @@ -318,7 +318,7 @@ - :/right.png + :/right.png @@ -447,7 +447,7 @@ - :/right.png + :/right.png @@ -493,7 +493,7 @@ - :/right.png + :/right.png @@ -509,7 +509,7 @@ - :/right.png + :/right.png @@ -525,7 +525,7 @@ - :/right.png + :/right.png @@ -541,7 +541,7 @@ - :/right.png + :/right.png @@ -585,7 +585,7 @@ - :/right.png + :/right.png @@ -604,7 +604,7 @@ - :/right.png + :/right.png @@ -641,7 +641,7 @@ - :/right.png + :/right.png @@ -774,7 +774,7 @@ read_all_cbx - + diff --git a/testdata/algo/device_extract_au1_rebuild_pf.gds b/testdata/algo/device_extract_au1_rebuild_pf.gds new file mode 100644 index 0000000000000000000000000000000000000000..ac89085ecffce4c416b4c8550d63feb50fa4910e GIT binary patch literal 49364 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 K`rRo%x%K~bRL(g7 literal 0 HcmV?d00001