mirror of https://github.com/KLayout/klayout.git
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:
parent
c543fe7a44
commit
444e10d32f
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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-> ();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
) +
|
||||
|
|
|
|||
|
|
@ -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"),
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -74,6 +74,7 @@ module DRC
|
|||
|
||||
# Implements the execute method
|
||||
def execute(macro)
|
||||
$0 = macro.path
|
||||
DRC::execute_drc(macro)
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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">
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -80,6 +80,7 @@ module LVS
|
|||
|
||||
# Implements the execute method
|
||||
def execute(macro)
|
||||
$0 = macro.path
|
||||
LVS::execute_lvs(macro)
|
||||
end
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue