Implemented #588: Netlist browser enhancements

1. The netlist browser now got events for
   selection and probe events
2. The netlist browser was overhauled to support
   "infinite depth" views
3. Ambiguities are not shown as errors now
4. New classes for support netlist object
   selections and the netlist browser.

Multiple selections for netlist browser.
This commit is contained in:
Matthias Koefferlein 2020-07-16 01:49:18 +02:00
parent c8a4d25b51
commit 3dff7549f2
5 changed files with 126 additions and 164 deletions

View File

@ -126,7 +126,7 @@ Class<lay::NetlistObjectPath> decl_NetlistObjectPath ("lay", "NetlistObjectPath"
gsi::method ("is_null?", &lay::NetlistObjectPath::is_null,
"@brief Returns a value indicating whether the path is an empty one.\n"
),
"@brief An object describing the instantiation of an object.\n"
"@brief An object describing the instantiation of a netlist object.\n"
"This class describes the instantiation of a net or a device or a circuit in terms of "
"a root circuit and a subcircuit chain leading to the indicated object.\n"
"\n"
@ -135,6 +135,39 @@ Class<lay::NetlistObjectPath> decl_NetlistObjectPath ("lay", "NetlistObjectPath"
"This class has been introduced in version 0.27.\n"
);
static lay::NetlistObjectPath first (const lay::NetlistObjectsPath *pp)
{
return pp->first ();
}
static lay::NetlistObjectPath second (const lay::NetlistObjectsPath *pp)
{
return pp->second ();
}
Class<lay::NetlistObjectsPath> decl_NetlistObjectsPath ("lay", "NetlistObjectsPath",
gsi::method_ext ("first", &first,
"@brief Gets the first object's path.\n"
"In cases of paired netlists (LVS database), the first path points to the layout netlist object.\n"
"For the single netlist, the first path is the only path supplied."
) +
gsi::method_ext ("second", &second,
"@brief Gets the second object's path.\n"
"In cases of paired netlists (LVS database), the first path points to the schematic netlist object.\n"
"For the single netlist, the scecond path is always a null path."
),
"@brief An object describing the instantiation of a single netlist object or a pair of those.\n"
"This class is basically a pair of netlist object paths (see \\NetlistObjectPath). When derived from a single netlist view, "
"only the first path is valid and will point to the selected object (a net, a device or a circuit). The second path is null.\n"
"\n"
"If the path is derived from a paired netlist view (a LVS report view), the first path corresponds to the object in the layout netlist, "
"the second one to the object in the schematic netlist.\n"
"If the selected object isn't a matched one, either the first or second path may be a null or a partial path without a final net or device object "
"or a partial path.\n"
"\n"
"This class has been introduced in version 0.27.\n"
);
static lay::NetlistObjectPath current_path_first (lay::NetlistBrowserDialog *dialog)
{
return dialog->current_path ().first ();
@ -169,18 +202,12 @@ Class<lay::NetlistBrowserDialog> decl_NetlistBrowserDialog ("lay", "NetlistBrows
gsi::method_ext ("current_path_second", &current_path_second,
"@brief Gets the path of the current object on the second (schematic in case of LVS database) side.\n"
) +
// @@@
gsi::method ("selected_nets", &lay::NetlistBrowserDialog::selected_nets,
"@brief Gets the nets currently selected in the netlist database browser.\n"
gsi::method ("current_path", &lay::NetlistBrowserDialog::current_path,
"@brief Gets the path of the current object as a path pair (combines layout and schematic object paths in case of a LVS database view).\n"
) +
gsi::method ("selected_devices", &lay::NetlistBrowserDialog::selected_devices,
"@brief Gets the devices currently selected in the netlist database browser.\n"
) +
gsi::method ("selected_subcircuits", &lay::NetlistBrowserDialog::selected_subcircuits,
"@brief Gets the subcircuits currently selected in the netlist database browser.\n"
) +
gsi::method ("selected_circuits", &lay::NetlistBrowserDialog::selected_circuits,
"@brief Gets the circuits currently selected in the netlist database browser.\n"
gsi::method ("selected_paths", &lay::NetlistBrowserDialog::selected_paths,
"@brief Gets the nets currently selected objects (paths) in the netlist database browser.\n"
"The result is an array of path pairs. See \\NetlistObjectsPath for details about these pairs."
),
"@brief Represents the netlist browser dialog.\n"
"This dialog is a part of the \\LayoutView class and can be obtained through \\LayoutView#netlist_browser.\n"

View File

@ -146,46 +146,13 @@ NetlistBrowserDialog::current_path () const
}
}
const std::vector<const db::Net *> &
NetlistBrowserDialog::selected_nets () const
const std::vector<lay::NetlistObjectsPath> &
NetlistBrowserDialog::selected_paths () const
{
if (browser_page) {
return browser_page->current_nets ();
return browser_page->selected_paths ();
} else {
static std::vector<const db::Net *> empty;
return empty;
}
}
const std::vector<const db::Device *> &
NetlistBrowserDialog::selected_devices () const
{
if (browser_page) {
return browser_page->current_devices ();
} else {
static std::vector<const db::Device *> empty;
return empty;
}
}
const std::vector<const db::SubCircuit *> &
NetlistBrowserDialog::selected_subcircuits () const
{
if (browser_page) {
return browser_page->current_subcircuits ();
} else {
static std::vector<const db::SubCircuit *> empty;
return empty;
}
}
const std::vector<const db::Circuit *> &
NetlistBrowserDialog::selected_circuits () const
{
if (browser_page) {
return browser_page->current_circuits ();
} else {
static std::vector<const db::Circuit *> empty;
static std::vector<lay::NetlistObjectsPath> empty;
return empty;
}
}

View File

@ -72,22 +72,7 @@ public:
/**
* @brief Gets the selected nets
*/
const std::vector<const db::Net *> &selected_nets () const;
/**
* @brief Gets the selected devices
*/
const std::vector<const db::Device *> &selected_devices () const;
/**
* @brief Gets the selected subcircuits
*/
const std::vector<const db::SubCircuit *> &selected_subcircuits () const;
/**
* @brief Gets the selected circuits
*/
const std::vector<const db::Circuit *> &selected_circuits () const;
const std::vector<lay::NetlistObjectsPath> &selected_paths () const;
/**
* @brief An event indicating that the selection has changed

View File

@ -471,8 +471,18 @@ NetlistBrowserPage::selection_changed ()
NetlistBrowserModel *model = dynamic_cast<NetlistBrowserModel *> (directory_tree->model ());
tl_assert (model != 0);
QModelIndexList selected = directory_tree->selectionModel ()->selectedIndexes ();
std::vector<lay::NetlistObjectsPath> selected_paths;
selected_paths.reserve (selected.size ());
for (QModelIndexList::const_iterator i = selected.begin (); i != selected.end (); ++i) {
if (i->column () == 0) {
selected_paths.push_back (model->path_from_index (*i));
}
}
QModelIndex current = directory_tree->selectionModel ()->currentIndex ();
highlight (model->path_from_index (current));
highlight (model->path_from_index (current), selected_paths);
selection_changed_event ();
}
@ -792,11 +802,6 @@ NetlistBrowserPage::set_db (db::LayoutToNetlist *l2ndb)
clear_markers ();
m_current_nets.clear ();
m_current_devices.clear ();
m_current_subcircuits.clear ();
m_current_circuits.clear ();
m_cell_context_cache = db::ContextCache (mp_database.get () ? mp_database->internal_layout () : 0);
setup_trees ();
@ -888,11 +893,12 @@ NetlistBrowserPage::setup_trees ()
}
void
NetlistBrowserPage::highlight (const NetlistObjectsPath &path)
NetlistBrowserPage::highlight (const NetlistObjectsPath &current_path, const std::vector<NetlistObjectsPath> &selected_paths)
{
if (path != m_current_path) {
if (current_path != m_current_path && selected_paths != m_selected_paths) {
m_current_path = path;
m_current_path = current_path;
m_selected_paths = selected_paths;
clear_markers ();
adjust_view ();
@ -961,24 +967,26 @@ NetlistBrowserPage::adjust_view ()
const db::Layout &original_layout = mp_view->cellview (m_cv_index)->layout ();
const db::Circuit *top_circuit = mp_database->netlist ()->circuit_by_name (original_layout.cell_name (mp_view->cellview (m_cv_index).cell_index ()));
const db::Circuit *circuit = m_current_path.root.first;
if (! circuit) {
return;
}
const db::Layout *layout = mp_database->internal_layout ();
const db::Cell *cell = (top_circuit && layout->is_valid_cell_index (top_circuit->cell_index ()) ? &layout->cell (top_circuit->cell_index ()) : mp_database->internal_top_cell ());
if (! layout || ! cell) {
return;
}
db::DCplxTrans trans;
db::DBox bbox;
if (circuit) {
for (std::vector<lay::NetlistObjectsPath>::const_iterator path = m_selected_paths.begin (); path != m_selected_paths.end (); ++path) {
const db::Circuit *circuit = path->root.first;
if (! circuit) {
continue;
}
db::DCplxTrans trans;
trans = trans_for (circuit, *layout, *cell, m_cell_context_cache, db::DCplxTrans ());
for (std::list<std::pair<const db::SubCircuit *, const db::SubCircuit *> >::const_iterator p = m_current_path.path.begin (); p != m_current_path.path.end () && circuit; ++p) {
for (std::list<std::pair<const db::SubCircuit *, const db::SubCircuit *> >::const_iterator p = path->path.begin (); p != path->path.end () && circuit; ++p) {
if (p->first) {
circuit = p->first->circuit_ref ();
trans = trans * p->first->trans ();
@ -987,16 +995,14 @@ NetlistBrowserPage::adjust_view ()
}
}
}
db::DBox bbox;
if (circuit) {
if (! circuit) {
continue;
}
db::Box ebox;
const db::Device *device = m_current_path.device.first;
const db::Net *net = m_current_path.net.first;
const db::Device *device = path->device.first;
const db::Net *net = path->net.first;
if (device) {
@ -1018,7 +1024,7 @@ NetlistBrowserPage::adjust_view ()
db::Box layer_bbox;
db::recursive_cluster_shape_iterator<db::NetShape> shapes (mp_database->net_clusters (), *layer, cell_index, cluster_id);
while (! shapes.at_end ()) {
layer_bbox += shapes->bbox ();
layer_bbox += shapes->bbox ().transformed (shapes.trans ());
++shapes;
}
@ -1030,7 +1036,7 @@ NetlistBrowserPage::adjust_view ()
ebox += bbox_for_circuit (layout, circuit);
}
bbox = trans * db::CplxTrans (layout->dbu ()) * ebox;
bbox += trans * db::CplxTrans (layout->dbu ()) * ebox;
}
@ -1253,38 +1259,12 @@ NetlistBrowserPage::update_highlights ()
const db::Layout &original_layout = mp_view->cellview (m_cv_index)->layout ();
const db::Circuit *top_circuit = mp_database->netlist ()->circuit_by_name (original_layout.cell_name (mp_view->cellview (m_cv_index).cell_index ()));
const db::Circuit *circuit = m_current_path.root.first;
if (! circuit) {
return;
}
const db::Layout *layout = mp_database->internal_layout ();
const db::Cell *cell = (top_circuit && layout->is_valid_cell_index (top_circuit->cell_index ()) ? &layout->cell (top_circuit->cell_index ()) : mp_database->internal_top_cell ());
if (! layout || ! cell) {
return;
}
// computes the transformation supplied by the path
db::DCplxTrans trans;
if (circuit) {
trans = trans_for (circuit, *layout, *cell, m_cell_context_cache, db::DCplxTrans ());
for (std::list<std::pair<const db::SubCircuit *, const db::SubCircuit *> >::const_iterator p = m_current_path.path.begin (); p != m_current_path.path.end () && circuit; ++p) {
if (p->first) {
circuit = p->first->circuit_ref ();
trans = trans * p->first->trans ();
} else {
circuit = 0;
}
}
}
// a map of display properties vs. layer properties
std::map<db::LayerProperties, lay::LayerPropertiesConstIterator> display_by_lp;
for (lay::LayerPropertiesConstIterator lp = mp_view->begin_layers (); ! lp.at_end (); ++lp) {
if (! lp->has_children () && lp->cellview_index () == int (m_cv_index) && lp->layer_index () >= 0 && (unsigned int) lp->layer_index () < original_layout.layers ()) {
@ -1292,22 +1272,55 @@ NetlistBrowserPage::update_highlights ()
}
}
std::vector<db::DCplxTrans> tv = mp_view->cv_transform_variants (m_cv_index);
// correct DBU differences between the storage layout and the original layout
for (std::vector<db::DCplxTrans>::iterator t = tv.begin (); t != tv.end (); ++t) {
*t = *t * trans * db::DCplxTrans (layout->dbu () / original_layout.dbu ());
}
size_t n_markers = 0;
bool not_all_shapes_are_shown = false;
if (m_current_path.net.first) {
not_all_shapes_are_shown = produce_highlights_for_net (m_current_path.net.first, n_markers, display_by_lp, tv);
} else if (m_current_path.device.first) {
not_all_shapes_are_shown = produce_highlights_for_device (m_current_path.device.first, n_markers, tv);
} else if (circuit) {
not_all_shapes_are_shown = produce_highlights_for_circuit (circuit, n_markers, tv);
for (std::vector<lay::NetlistObjectsPath>::const_iterator path = m_selected_paths.begin (); path != m_selected_paths.end (); ++path) {
const db::Circuit *circuit = path->root.first;
if (! circuit) {
continue;
}
// computes the transformation supplied by the path
db::DCplxTrans trans = trans_for (circuit, *layout, *cell, m_cell_context_cache, db::DCplxTrans ());
for (std::list<std::pair<const db::SubCircuit *, const db::SubCircuit *> >::const_iterator p = path->path.begin (); p != path->path.end () && circuit; ++p) {
if (p->first) {
circuit = p->first->circuit_ref ();
trans = trans * p->first->trans ();
} else {
circuit = 0;
}
}
if (! circuit) {
continue;
}
// a map of display properties vs. layer properties
// correct DBU differences between the storage layout and the original layout
std::vector<db::DCplxTrans> tv = mp_view->cv_transform_variants (m_cv_index);
for (std::vector<db::DCplxTrans>::iterator t = tv.begin (); t != tv.end (); ++t) {
*t = *t * trans * db::DCplxTrans (layout->dbu () / original_layout.dbu ());
}
if (path->net.first) {
if (produce_highlights_for_net (path->net.first, n_markers, display_by_lp, tv)) {
not_all_shapes_are_shown = true;
}
} else if (path->device.first) {
if (produce_highlights_for_device (path->device.first, n_markers, tv)) {
not_all_shapes_are_shown = true;
}
} else if (circuit) {
if (produce_highlights_for_circuit (circuit, n_markers, tv)) {
not_all_shapes_are_shown = true;
}
}
}
if (not_all_shapes_are_shown) {
@ -1318,7 +1331,6 @@ NetlistBrowserPage::update_highlights ()
} else {
info_label->hide ();
}
}
void

View File

@ -179,33 +179,9 @@ public:
/**
* @brief Gets the selected nets
*/
const std::vector<const db::Net *> &current_nets () const
const std::vector<lay::NetlistObjectsPath> &selected_paths () const
{
return m_current_nets;
}
/**
* @brief Gets the selected devices
*/
const std::vector<const db::Device *> &current_devices () const
{
return m_current_devices;
}
/**
* @brief Gets the selected subcircuits
*/
const std::vector<const db::SubCircuit *> &current_subcircuits () const
{
return m_current_subcircuits;
}
/**
* @brief Gets the selected circuits
*/
const std::vector<const db::Circuit *> &current_circuits () const
{
return m_current_circuits;
return m_selected_paths;
}
/**
@ -258,13 +234,8 @@ private:
std::vector <lay::Marker *> mp_markers;
bool m_enable_updates;
bool m_update_needed;
// @@@ TODO: make multiple ...
lay::NetlistObjectsPath m_current_path;
// @@@ TODO: remove
std::vector<const db::Net *> m_current_nets;
std::vector<const db::Device *> m_current_devices;
std::vector<const db::SubCircuit *> m_current_subcircuits;
std::vector<const db::Circuit *> m_current_circuits;
std::vector<lay::NetlistObjectsPath> m_selected_paths;
lay::NetInfoDialog *mp_info_dialog;
tl::DeferredMethod<NetlistBrowserPage> dm_update_highlights;
tl::DeferredMethod<NetlistBrowserPage> dm_rerun_macro;
@ -275,7 +246,7 @@ private:
void navigate_to (const QModelIndex &index, bool forward = true);
void adjust_view ();
void clear_markers ();
void highlight (const lay::NetlistObjectsPath &path);
void highlight (const NetlistObjectsPath &current_path, const std::vector<NetlistObjectsPath> &selected_paths);
std::vector<const db::Net *> selected_nets ();
std::vector<const db::Device *> selected_devices ();
std::vector<const db::SubCircuit *> selected_subcircuits ();