WIP: rerun LVS, partial LVS

Rerun LVS: a button is provided which allows re-running
the LVS or netlist extraction from the netlist browser.
TODO: a generic concept for triggering the generators

"Partial LVS" is a feature where it's possible to
select a layout subcell - running LVS then will only
compare against the corresponding schematic subcell, not
the whole tree. The magic is done by "align" which will
remove the upper hierarchy part.
This commit is contained in:
Matthias Koefferlein 2019-08-24 22:56:20 +02:00
parent c543fe7a44
commit 444e10d32f
16 changed files with 105 additions and 19 deletions

View File

@ -1148,7 +1148,7 @@ db::Region LayoutToNetlist::antenna_check (const db::Region &gate, const db::Reg
}
void db::LayoutToNetlist::save (const std::string &path, bool short_format)
void LayoutToNetlist::save (const std::string &path, bool short_format)
{
tl::OutputStream stream (path);
db::LayoutToNetlistStandardWriter writer (stream, short_format);
@ -1156,7 +1156,7 @@ void db::LayoutToNetlist::save (const std::string &path, bool short_format)
writer.write (this);
}
void db::LayoutToNetlist::load (const std::string &path)
void LayoutToNetlist::load (const std::string &path)
{
tl::InputStream stream (path);
db::LayoutToNetlistStandardReader reader (stream);
@ -1165,7 +1165,7 @@ void db::LayoutToNetlist::load (const std::string &path)
reader.read (this);
}
db::LayoutToNetlist *db::LayoutToNetlist::create_from_file (const std::string &path)
db::LayoutToNetlist *LayoutToNetlist::create_from_file (const std::string &path)
{
std::auto_ptr<db::LayoutToNetlist> db;
@ -1189,4 +1189,9 @@ db::LayoutToNetlist *db::LayoutToNetlist::create_from_file (const std::string &p
return db.release ();
}
void LayoutToNetlist::set_generator (const std::string &g)
{
m_generator = g;
}
}

View File

@ -254,6 +254,19 @@ public:
*/
std::string name (unsigned int) const;
/**
* @brief Sets the generator string
*/
void set_generator (const std::string &g);
/**
* @brief Gets the generator string
*/
const std::string &generator () const
{
return m_generator;
}
/**
* @brief Returns true, if the region is a persisted region
* Persisted regions have a name and are kept inside the LayoutToNetlist
@ -730,6 +743,7 @@ private:
bool m_is_flat;
double m_device_scaling;
db::DeepLayer m_dummy_layer;
std::string m_generator;
struct CellReuseTableKey
{

View File

@ -86,7 +86,7 @@ db::NetlistCrossReference *LayoutVsSchematic::make_cross_ref ()
}
void db::LayoutVsSchematic::save (const std::string &path, bool short_format)
void LayoutVsSchematic::save (const std::string &path, bool short_format)
{
tl::OutputStream stream (path);
db::LayoutVsSchematicStandardWriter writer (stream, short_format);
@ -94,7 +94,7 @@ void db::LayoutVsSchematic::save (const std::string &path, bool short_format)
writer.write (this);
}
void db::LayoutVsSchematic::load (const std::string &path)
void LayoutVsSchematic::load (const std::string &path)
{
tl::InputStream stream (path);
db::LayoutVsSchematicStandardReader reader (stream);

View File

@ -2138,14 +2138,14 @@ NetlistComparer::unmatched_circuits (db::Netlist *a, db::Netlist *b, std::vector
for (db::Netlist::circuit_iterator i = a->begin_circuits (); i != a->end_circuits (); ++i) {
size_t cat = circuit_categorizer.cat_for_circuit (i.operator-> ());
if (cat && i->begin_refs () != i->end_refs ()) {
if (cat) {
cat2circuits[cat].first = i.operator-> ();
}
}
for (db::Netlist::circuit_iterator i = b->begin_circuits (); i != b->end_circuits (); ++i) {
size_t cat = circuit_categorizer.cat_for_circuit (i.operator-> ());
if (cat && i->begin_refs () != i->end_refs ()) {
if (cat) {
cat2circuits[cat].second = i.operator-> ();
}
}

View File

@ -169,6 +169,13 @@ Class<db::LayoutToNetlist> decl_dbLayoutToNetlist ("db", "LayoutToNetlist",
"The database unit is mandatory because the physical parameter extraction "
"for devices requires this unit for translation of layout to physical dimensions.\n"
) +
gsi::method ("generator", &db::LayoutToNetlist::generator,
"@brief Gets the generator string.\n"
"The generator is the script that created this database.\n"
) +
gsi::method ("generator=", &db::LayoutToNetlist::set_generator, gsi::arg ("generator"),
"@brief Sets the generator string.\n"
) +
gsi::method ("dss", (db::DeepShapeStore &(db::LayoutToNetlist::*) ()) &db::LayoutToNetlist::dss,
"@brief Gets a reference to the internal DSS object.\n"
) +

View File

@ -1106,7 +1106,7 @@ Class<db::Circuit> decl_dbCircuit ("db", "Circuit",
"@brief Iterates over the parent circuits of this circuit\n"
"Child circuits are the ones that are referencing this circuit via subcircuits."
) +
gsi::method ("has_refs", &db::Circuit::has_refs,
gsi::method ("has_refs?", &db::Circuit::has_refs,
"@brief Returns a value indicating whether the circuit has references\n"
"A circuit has references if there is at least one subcircuit referring to it."
) +
@ -1379,13 +1379,13 @@ Class<db::Netlist> decl_dbNetlist ("db", "Netlist",
gsi::method ("remove", &db::Netlist::remove_circuit, gsi::arg ("circuit"),
"@brief Removes the given circuit object from the netlist\n"
"After the circuit has been removed, the object becomes invalid and cannot be used further. "
"A circuit with references (see \\has_refs) should not be removed as the "
"A circuit with references (see \\has_refs?) should not be removed as the "
"subcircuits calling it would afterwards point to nothing."
) +
gsi::method ("purge_circuit", &db::Netlist::purge_circuit, gsi::arg ("circuit"),
"@brief Removes the given circuit object and all child circuits which are not used otherwise from the netlist\n"
"After the circuit has been removed, the object becomes invalid and cannot be used further. "
"A circuit with references (see \\has_refs) should not be removed as the "
"A circuit with references (see \\has_refs?) should not be removed as the "
"subcircuits calling it would afterwards point to nothing."
) +
gsi::method ("flatten_circuit", &db::Netlist::flatten_circuit, gsi::arg ("circuit"),

View File

@ -541,12 +541,10 @@ Class<db::NetlistComparer> decl_dbNetlistComparer ("db", "NetlistComparer",
gsi::method_ext ("unmatched_circuits_a", &unmatched_circuits_a, gsi::arg ("a"), gsi::arg ("b"),
"@brief Returns a list of circuits in A for which there is not corresponding circuit in B\n"
"This list can be used to flatten these circuits so they do not participate in the compare process.\n"
"Top level circuits are not included as they cannot be flattened.\n"
) +
gsi::method_ext ("unmatched_circuits_b", &unmatched_circuits_b, gsi::arg ("a"), gsi::arg ("b"),
"@brief Returns a list of circuits in B for which there is not corresponding circuit in A\n"
"This list can be used to flatten these circuits so they do not participate in the compare process.\n"
"Top level circuits are not included as they cannot be flattened.\n"
) +
gsi::method ("compare", (bool (db::NetlistComparer::*) (const db::Netlist *, const db::Netlist *) const) &db::NetlistComparer::compare, gsi::arg ("netlist_a"), gsi::arg ("netlist_b"),
"@brief Compares two netlists.\n"

View File

@ -407,6 +407,8 @@ module DRC
@l2n = RBA::LayoutToNetlist::new(layout.top_cell.name, layout.dbu)
end
@l2n.generator = $0
end
def register_layer(data)

View File

@ -74,6 +74,7 @@ module DRC
# Implements the execute method
def execute(macro)
$0 = macro.path
DRC::execute_drc(macro)
end

View File

@ -89,12 +89,12 @@ plugins.depends += lib rdb db
plugins.depends += lay ant
laybasic.depends += rdb
lym.depends += gsi $$LANG_DEPENDS
laybasic.depends += rdb lym
ant.depends += laybasic
img.depends += laybasic
edt.depends += laybasic
lym.depends += gsi $$LANG_DEPENDS
lay.depends += laybasic ant img edt lym
lay.depends += laybasic ant img edt
klayout_main.depends += plugins $$MAIN_DEPENDS
}

View File

@ -132,6 +132,20 @@
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="rerun_button">
<property name="text">
<string>...</string>
</property>
<property name="icon">
<iconset resource="../../lay/lay/layResources.qrc">
<normaloff>:/run.png</normaloff>:/run.png</iconset>
</property>
<property name="autoRaise">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_3">
<property name="orientation">

View File

@ -30,6 +30,7 @@
#include "layMarker.h"
#include "layNetInfoDialog.h"
#include "layNetExportDialog.h"
#include "lymMacro.h"
#include "tlProgress.h"
#include "tlExceptions.h"
#include "dbLayoutToNetlist.h"
@ -124,6 +125,7 @@ NetlistBrowserPage::NetlistBrowserPage (QWidget * /*parent*/)
m_update_needed (true),
mp_info_dialog (0),
dm_update_highlights (this, &NetlistBrowserPage::update_highlights),
dm_rerun_macro (this, &NetlistBrowserPage::rerun_macro),
m_cell_context_cache (0)
{
Ui::NetlistBrowserPage::setupUi (this);
@ -188,6 +190,7 @@ NetlistBrowserPage::NetlistBrowserPage (QWidget * /*parent*/)
connect (m_show_all_action, SIGNAL (triggered ()), this, SLOT (show_all_clicked ()));
connect (info_button, SIGNAL (pressed ()), this, SLOT (info_button_pressed ()));
connect (rerun_button, SIGNAL (pressed ()), this, SLOT (rerun_button_pressed ()));
connect (find_button, SIGNAL (pressed ()), this, SLOT (find_button_pressed ()));
connect (forward, SIGNAL (clicked ()), this, SLOT (navigate_forward ()));
connect (backward, SIGNAL (clicked ()), this, SLOT (navigate_back ()));
@ -585,6 +588,27 @@ NetlistBrowserPage::navigate_forward ()
}
}
void
NetlistBrowserPage::rerun_button_pressed ()
{
// NOTE: we use deferred execution, because otherwise the button won't get repainted properly
dm_rerun_macro ();
}
void
NetlistBrowserPage::rerun_macro ()
{
if (! mp_database->generator ().empty ()) {
lym::Macro *generator = lym::MacroCollection::root ().find_macro (mp_database->generator ());
if (! generator) {
throw tl::Exception (tl::sprintf (tl::to_string (tr ("Cannot find generator script: %s")), mp_database->generator()));
} else {
generator->run ();
}
}
}
void
NetlistBrowserPage::info_button_pressed ()
{
@ -741,6 +765,13 @@ NetlistBrowserPage::set_db (db::LayoutToNetlist *l2ndb)
db::LayoutVsSchematic *lvsdb = dynamic_cast<db::LayoutVsSchematic *> (l2ndb);
mp_database.reset (l2ndb);
rerun_button->setEnabled (mp_database.get () && ! mp_database->generator ().empty ());
if (rerun_button->isEnabled ()) {
rerun_button->setToolTip (tl::to_qstring (tl::to_string (tr ("Run ")) + mp_database->generator ()));
} else {
rerun_button->setToolTip (QString ());
}
show_netlist->setVisible (lvsdb != 0);
show_xref->setVisible (lvsdb != 0);

View File

@ -169,6 +169,7 @@ public slots:
private slots:
void show_all_clicked ();
void info_button_pressed ();
void rerun_button_pressed ();
void find_button_pressed ();
void anchor_clicked (const QString &url);
void navigate_back ();
@ -212,6 +213,7 @@ private:
std::vector<const db::Circuit *> m_current_circuits;
lay::NetInfoDialog *mp_info_dialog;
tl::DeferredMethod<NetlistBrowserPage> dm_update_highlights;
tl::DeferredMethod<NetlistBrowserPage> dm_rerun_macro;
db::ContextCache m_cell_context_cache;
void set_db (db::LayoutToNetlist *l2ndb);
@ -233,6 +235,7 @@ private:
bool produce_highlights_for_subcircuit (const db::SubCircuit *subcircuit, size_t &n_markers, const std::vector<db::DCplxTrans> &tv);
bool produce_highlights_for_circuit (const db::Circuit *circuit, size_t &n_markers, const std::vector<db::DCplxTrans> &tv);
void configure_marker (lay::Marker *marker, bool with_fill);
void rerun_macro ();
void export_nets (const std::vector<const db::Net *> *nets);
};

View File

@ -275,9 +275,9 @@ HEADERS = \
layNetlistBrowserTreeModel.h \
layLibrariesView.h
INCLUDEPATH += $$TL_INC $$GSI_INC $$DB_INC $$RDB_INC
DEPENDPATH += $$TL_INC $$GSI_INC $$DB_INC $$RDB_INC
LIBS += -L$$DESTDIR -lklayout_tl -lklayout_gsi -lklayout_db -lklayout_rdb
INCLUDEPATH += $$TL_INC $$GSI_INC $$DB_INC $$RDB_INC $$LYM_INC
DEPENDPATH += $$TL_INC $$GSI_INC $$DB_INC $$RDB_INC $$LYM_INC
LIBS += -L$$DESTDIR -lklayout_tl -lklayout_gsi -lklayout_db -lklayout_rdb -lklayout_lym
INCLUDEPATH += $$QTBASIC_INC
DEPENDPATH += $$QTBASIC_INC

View File

@ -61,6 +61,8 @@ module LVS
@lvs = RBA::LayoutVsSchematic::new(cell.name, layout.dbu)
end
@lvs.generator = $0
@l2n = @lvs
@comparer = RBA::NetlistComparer::new
@ -117,8 +119,16 @@ module LVS
nl = _ensure_two_netlists
unmatched_a = @comparer.unmatched_circuits_a(*nl)
# check whether we're about to flatten away the internal top cell - that's bad
top_cell = l2n_data.internal_top_cell.name
if unmatched_a.find { |c| c.name == top_cell }
raise("Can't find a schematic counterpart for the top cell #{top_cell} - use 'same_circuit' to establish a correspondence")
end
# flatten layout cells for which there is no corresponding schematic circuit
@comparer.unmatched_circuits_a(*nl).each do |c|
unmatched_a.each do |c|
@engine.info("Flatten layout cell (no schematic): #{c.name}")
nl[0].flatten_circuit(c)
end

View File

@ -80,6 +80,7 @@ module LVS
# Implements the execute method
def execute(macro)
$0 = macro.path
LVS::execute_lvs(macro)
end