mirror of https://github.com/KLayout/klayout.git
WIP: debugging, development
- LVS DSL debugging, enhancements - Allow polygons with holes in L2N - Spice Reader: was creating too many class objects - Device class categorizer: allow associating A->C,B-C - ...
This commit is contained in:
parent
37012efba0
commit
0cbfa698f0
|
|
@ -23,6 +23,7 @@
|
|||
#include "dbLayoutToNetlistWriter.h"
|
||||
#include "dbLayoutToNetlist.h"
|
||||
#include "dbLayoutToNetlistFormatDefs.h"
|
||||
#include "dbPolygonTools.h"
|
||||
|
||||
namespace db
|
||||
{
|
||||
|
|
@ -341,7 +342,7 @@ void std_writer_impl<Keys>::write (const db::PolygonRef *s, const db::ICplxTrans
|
|||
|
||||
*mp_stream << Keys::polygon_key << "(" << lname;
|
||||
if (poly.holes () > 0) {
|
||||
db::SimplePolygon sp (poly);
|
||||
db::SimplePolygon sp = db::polygon_to_simple_polygon (poly);
|
||||
write_points (*mp_stream, sp, t, m_ref, relative);
|
||||
} else {
|
||||
write_points (*mp_stream, poly, t, m_ref, relative);
|
||||
|
|
|
|||
|
|
@ -275,9 +275,40 @@ public:
|
|||
|
||||
void same_class (const db::DeviceClass *ca, const db::DeviceClass *cb)
|
||||
{
|
||||
++m_next_cat;
|
||||
m_cat_by_ptr.insert (std::make_pair (ca, m_next_cat));
|
||||
m_cat_by_ptr.insert (std::make_pair (cb, m_next_cat));
|
||||
// reuse existing category if one is assigned already -> this allows associating
|
||||
// multiple categories to other ones (A->C, B->C)
|
||||
std::map<const db::DeviceClass *, size_t>::const_iterator cpa = m_cat_by_ptr.find (ca);
|
||||
std::map<const db::DeviceClass *, size_t>::const_iterator cpb = m_cat_by_ptr.find (cb);
|
||||
|
||||
if (cpa != m_cat_by_ptr.end () && cpb != m_cat_by_ptr.end ()) {
|
||||
|
||||
if (cpa->second != cpb->second) {
|
||||
// join categories (cat(B)->cat(A))
|
||||
for (std::map<const db::DeviceClass *, size_t>::iterator cp = m_cat_by_ptr.begin (); cp != m_cat_by_ptr.end (); ++cp) {
|
||||
if (cp->second == cpb->second) {
|
||||
cp->second = cpa->second;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} else if (cpb != m_cat_by_ptr.end ()) {
|
||||
|
||||
// reuse cat(B) category
|
||||
m_cat_by_ptr.insert (std::make_pair (ca, cpb->second));
|
||||
|
||||
} else if (cpa != m_cat_by_ptr.end ()) {
|
||||
|
||||
// reuse cat(A) category
|
||||
m_cat_by_ptr.insert (std::make_pair (cb, cpa->second));
|
||||
|
||||
} else {
|
||||
|
||||
// new category
|
||||
++m_next_cat;
|
||||
m_cat_by_ptr.insert (std::make_pair (ca, m_next_cat));
|
||||
m_cat_by_ptr.insert (std::make_pair (cb, m_next_cat));
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
size_t cat_for_device (const db::Device *device)
|
||||
|
|
|
|||
|
|
@ -65,11 +65,27 @@ void NetlistSpiceReaderDelegate::error (const std::string &msg)
|
|||
throw tl::Exception (msg);
|
||||
}
|
||||
|
||||
template <class Cls>
|
||||
static db::DeviceClass *make_device_class (db::Circuit *circuit, const std::string &name)
|
||||
{
|
||||
if (! circuit || ! circuit->netlist ()) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
db::DeviceClass *cls = circuit->netlist ()->device_class_by_name (name);
|
||||
if (! cls) {
|
||||
cls = new Cls ();
|
||||
cls->set_name (name);
|
||||
circuit->netlist ()->add_device_class (cls);
|
||||
}
|
||||
|
||||
return cls;
|
||||
}
|
||||
|
||||
bool NetlistSpiceReaderDelegate::element (db::Circuit *circuit, const std::string &element, const std::string &name, const std::string &model, double value, const std::vector<db::Net *> &nets, const std::map<std::string, double> ¶ms)
|
||||
{
|
||||
std::string cn = model;
|
||||
db::DeviceClass *cls = circuit->netlist ()->device_class_by_name (cn);
|
||||
db::DeviceClass *new_cls = 0;
|
||||
|
||||
if (cls) {
|
||||
// use given class
|
||||
|
|
@ -77,53 +93,46 @@ bool NetlistSpiceReaderDelegate::element (db::Circuit *circuit, const std::strin
|
|||
if (cn.empty ()) {
|
||||
cn = "RES";
|
||||
}
|
||||
new_cls = new db::DeviceClassResistor ();
|
||||
cls = make_device_class<db::DeviceClassResistor> (circuit, cn);
|
||||
} else if (element == "L") {
|
||||
if (cn.empty ()) {
|
||||
cn = "IND";
|
||||
}
|
||||
new_cls = new db::DeviceClassInductor ();
|
||||
cls = make_device_class<db::DeviceClassInductor> (circuit, cn);
|
||||
} else if (element == "C") {
|
||||
if (cn.empty ()) {
|
||||
cn = "CAP";
|
||||
}
|
||||
new_cls = new db::DeviceClassCapacitor ();
|
||||
cls = make_device_class<db::DeviceClassCapacitor> (circuit, cn);
|
||||
} else if (element == "D") {
|
||||
if (cn.empty ()) {
|
||||
cn = "DIODE";
|
||||
}
|
||||
new_cls = new db::DeviceClassDiode ();
|
||||
cls = make_device_class<db::DeviceClassDiode> (circuit, cn);
|
||||
} else if (element == "Q") {
|
||||
if (nets.size () == 3) {
|
||||
if (cn.empty ()) {
|
||||
cn = "BJT3";
|
||||
}
|
||||
new_cls = new db::DeviceClassBJT3Transistor ();
|
||||
cls = make_device_class<db::DeviceClassBJT3Transistor> (circuit, cn);
|
||||
} else if (nets.size () == 4) {
|
||||
if (cn.empty ()) {
|
||||
cn = "BJT4";
|
||||
}
|
||||
new_cls = new db::DeviceClassBJT4Transistor ();
|
||||
cls = make_device_class<db::DeviceClassBJT4Transistor> (circuit, cn);
|
||||
} else {
|
||||
error (tl::to_string (tr ("'Q' element needs to have 3 or 4 terminals")));
|
||||
}
|
||||
} else if (element == "M") {
|
||||
if (nets.size () == 3) {
|
||||
if (cn.empty ()) {
|
||||
cn = "MOS3";
|
||||
}
|
||||
new_cls = new db::DeviceClassMOS3Transistor ();
|
||||
} else if (nets.size () == 4) {
|
||||
if (nets.size () == 4) {
|
||||
if (cn.empty ()) {
|
||||
cn = "MOS4";
|
||||
}
|
||||
new_cls = new db::DeviceClassMOS4Transistor ();
|
||||
cls = make_device_class<db::DeviceClassMOS4Transistor> (circuit, cn);
|
||||
} else {
|
||||
error (tl::to_string (tr ("'M' element needs to have 4 terminals")));
|
||||
}
|
||||
}
|
||||
|
||||
if (new_cls) {
|
||||
cls = new_cls;
|
||||
cls->set_name (cn);
|
||||
circuit->netlist ()->add_device_class (cls);
|
||||
} else if (! cls) {
|
||||
} else {
|
||||
error (tl::sprintf (tl::to_string (tr ("Not a known element type: '%s'")), element));
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -480,11 +480,11 @@ Class<db::LayoutToNetlist> decl_dbLayoutToNetlist ("db", "LayoutToNetlist",
|
|||
"This variant accepts a database-unit location. The location is given in the\n"
|
||||
"coordinate space of the initial cell.\n"
|
||||
) +
|
||||
gsi::method ("write", &db::LayoutToNetlist::save, gsi::arg ("path"), gsi::arg ("short_format", false),
|
||||
gsi::method ("write|write_l2n", &db::LayoutToNetlist::save, gsi::arg ("path"), gsi::arg ("short_format", false),
|
||||
"@brief Writes the extracted netlist to a file.\n"
|
||||
"This method employs the native format of KLayout.\n"
|
||||
) +
|
||||
gsi::method ("read", &db::LayoutToNetlist::load, gsi::arg ("path"),
|
||||
gsi::method ("read|read_l2n", &db::LayoutToNetlist::load, gsi::arg ("path"),
|
||||
"@brief Reads the extracted netlist from the file.\n"
|
||||
"This method employs the native format of KLayout.\n"
|
||||
) +
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@ module DRC
|
|||
@output_rdb = nil
|
||||
@output_rdb_file = nil
|
||||
@output_rdb_cell = nil
|
||||
@output_l2ndb = nil
|
||||
@show_l2ndb = nil
|
||||
@output_l2ndb_file = nil
|
||||
@target_netlist_file = nil
|
||||
@target_netlist_format = nil
|
||||
|
|
@ -42,6 +42,7 @@ module DRC
|
|||
@dss = nil
|
||||
@deep = false
|
||||
@netter = nil
|
||||
@netter_data = nil
|
||||
|
||||
@verbose = false
|
||||
|
||||
|
|
@ -770,7 +771,7 @@ module DRC
|
|||
# will also be written to the given file.
|
||||
|
||||
def report_netlist(filename = nil)
|
||||
@output_l2ndb = true
|
||||
@show_l2ndb = true
|
||||
if filename
|
||||
filename.is_a?(String) || raise("Argument must be string in report_netlist")
|
||||
end
|
||||
|
|
@ -1337,7 +1338,7 @@ CODE
|
|||
|
||||
# save the report database if requested
|
||||
if @output_rdb_file
|
||||
rdb_file = make_path(@output_rdb_file)
|
||||
rdb_file = _make_path(@output_rdb_file)
|
||||
info("Writing report database: #{rdb_file} ..")
|
||||
@output_rdb.save(rdb_file)
|
||||
end
|
||||
|
|
@ -1403,35 +1404,34 @@ CODE
|
|||
|
||||
writer = @target_netlist_format || RBA::NetlistSpiceWriter::new
|
||||
|
||||
netlist_file = make_path(@target_netlist_file)
|
||||
netlist_file = _make_path(@target_netlist_file)
|
||||
info("Writing netlist: #{netlist_file} ..")
|
||||
self.netlist.write(netlist_file, writer, @target_netlist_comment || "")
|
||||
|
||||
end
|
||||
|
||||
# save the netlist database if requested
|
||||
if @output_l2ndb && @netter && @netter.l2n_data
|
||||
if @output_l2ndb_file && @netter && @netter.l2n_data
|
||||
|
||||
if @output_l2ndb_file
|
||||
l2ndb_file = make_path(@output_l2ndb_file)
|
||||
info("Writing netlist database: #{l2ndb_file} ..")
|
||||
@netter.l2n_data.save(l2ndb_file)
|
||||
end
|
||||
if @output_l2ndb && final && view
|
||||
# NOTE: to prevent the netter destroying the database, we need to take it
|
||||
l2ndb = @netter._take_l2n_data
|
||||
# we also need to make the extractor take over ownership over the DSS
|
||||
# because otherwise we can't free the resources.
|
||||
if l2ndb.dss == @dss
|
||||
l2ndb.keep_dss
|
||||
@dss = nil
|
||||
end
|
||||
l2ndb_index = view.add_l2ndb(l2ndb)
|
||||
view.show_l2ndb(l2ndb_index, view.active_cellview_index)
|
||||
end
|
||||
l2ndb_file = _make_path(@output_l2ndb_file)
|
||||
info("Writing netlist database: #{l2ndb_file} ..")
|
||||
@netter.l2n_data.write_l2n(l2ndb_file)
|
||||
|
||||
end
|
||||
|
||||
# give derived classes to perform actions
|
||||
_before_cleanup
|
||||
|
||||
# show the data in the browser
|
||||
if @show_l2ndb && @netter && @netter.l2n_data
|
||||
|
||||
# NOTE: to prevent the netter destroying the database, we need to take it
|
||||
l2ndb = _take_data
|
||||
l2ndb_index = view.add_l2ndb(l2ndb)
|
||||
view.show_l2ndb(l2ndb_index, view.active_cellview_index)
|
||||
|
||||
end
|
||||
|
||||
@output_layout = nil
|
||||
@output_layout_file = nil
|
||||
@output_cell = nil
|
||||
|
|
@ -1439,12 +1439,15 @@ CODE
|
|||
@output_rdb_cell = nil
|
||||
@output_rdb = nil
|
||||
@output_rdb_index = nil
|
||||
@output_l2ndb = nil
|
||||
@show_l2ndb = nil
|
||||
@output_l2ndb_file = nil
|
||||
|
||||
# clean up temp data
|
||||
@dss && @dss._destroy
|
||||
@dss = nil
|
||||
@netter && @netter._finish
|
||||
@netter = nil
|
||||
@netter_data = nil
|
||||
|
||||
if final && @log_file
|
||||
@log_file.close
|
||||
|
|
@ -1453,6 +1456,33 @@ CODE
|
|||
|
||||
end
|
||||
|
||||
def _take_data
|
||||
|
||||
if ! @netter
|
||||
return nil
|
||||
end
|
||||
|
||||
if ! @netter_data
|
||||
|
||||
@netter_data = @netter._take_data
|
||||
|
||||
# we also need to make the extractor take over ownership over the DSS
|
||||
# because otherwise we can't free the resources.
|
||||
if @netter_data.dss == @dss
|
||||
@netter_data.keep_dss
|
||||
@dss = nil
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
@netter_data
|
||||
|
||||
end
|
||||
|
||||
def _before_cleanup
|
||||
# nothing yet
|
||||
end
|
||||
|
||||
def _dss
|
||||
@dss
|
||||
end
|
||||
|
|
@ -1461,9 +1491,7 @@ CODE
|
|||
@netter ||= DRC::DRCNetter::new(self)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def make_path(file)
|
||||
def _make_path(file)
|
||||
# resolves the file path relative to the source's path
|
||||
sp = self.source.path
|
||||
if sp
|
||||
|
|
@ -1473,6 +1501,8 @@ CODE
|
|||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def _make_string(v)
|
||||
if v.class.respond_to?(:from_s)
|
||||
v.class.to_s + "::from_s(" + v.to_s.inspect + ")"
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
module DRC
|
||||
|
||||
# The netter object
|
||||
# The DRC netter object
|
||||
|
||||
# %DRC%
|
||||
# @scope
|
||||
|
|
@ -13,6 +13,17 @@ module DRC
|
|||
# as global functions too where they act on a default incarnation
|
||||
# of the netter. Usually it's not required to instantiate a Netter
|
||||
# object, but it serves as a container for this functionality.
|
||||
|
||||
# %LVS%
|
||||
# @scope
|
||||
# @name Netter
|
||||
# @brief LVS Reference: Netter object
|
||||
# The Netter object provides services related to network extraction
|
||||
# from a layout plus comparison against a reference netlist.
|
||||
# Similar to the DRC netter (which lacks the compare ability), the
|
||||
# relevant method of this object are available as global functions too
|
||||
# where they act on a default incarnation. Usually it's not required
|
||||
# to instantiate a Netter object explicitly.
|
||||
#
|
||||
# An individual netter object can be created, if the netter results
|
||||
# need to be kept for multiple extractions. If you really need
|
||||
|
|
@ -162,7 +173,7 @@ module DRC
|
|||
|
||||
def extract_devices(devex, layer_selection)
|
||||
|
||||
ensure_l2n
|
||||
ensure_data
|
||||
|
||||
devex.is_a?(RBA::DeviceExtractorBase) || raise("First argument of Netter#extract_devices must be a device extractor instance in the two-arguments form")
|
||||
|
||||
|
|
@ -188,8 +199,7 @@ module DRC
|
|||
def clear_connections
|
||||
@netlisted = false
|
||||
@connect_implicit = ""
|
||||
@l2n && @l2n._destroy
|
||||
@l2n = nil
|
||||
_clear_data
|
||||
end
|
||||
|
||||
# %DRC%
|
||||
|
|
@ -336,7 +346,7 @@ module DRC
|
|||
|
||||
def l2n_data
|
||||
|
||||
ensure_l2n
|
||||
ensure_data
|
||||
|
||||
# run extraction in a timed environment
|
||||
if ! @netlisted
|
||||
|
|
@ -364,7 +374,12 @@ module DRC
|
|||
clear_connections
|
||||
end
|
||||
|
||||
def _take_l2n_data
|
||||
def _clear_data
|
||||
@l2n && @l2n._destroy
|
||||
@l2n = nil
|
||||
end
|
||||
|
||||
def _take_data
|
||||
l2ndb = self.l2n_data
|
||||
@l2n = nil
|
||||
l2ndb
|
||||
|
|
@ -376,17 +391,16 @@ module DRC
|
|||
@netlisted && clear_connections
|
||||
end
|
||||
|
||||
def ensure_l2n
|
||||
@l2n || make_l2n
|
||||
def ensure_data
|
||||
if !@l2n
|
||||
@layers = {}
|
||||
make_data
|
||||
end
|
||||
end
|
||||
|
||||
def make_l2n
|
||||
|
||||
@layers = {}
|
||||
def make_data
|
||||
|
||||
if @engine._dss
|
||||
# TODO: check whether all layers are deep and come from the dss and layout index,
|
||||
# then use this layout index. This will remove the need for this check:
|
||||
@engine._dss.is_singular? || raise("The DRC script features more than one or no layout source - network extraction cannot be performed in such configurations")
|
||||
@l2n = RBA::LayoutToNetlist::new(@engine._dss)
|
||||
else
|
||||
|
|
@ -405,7 +419,7 @@ module DRC
|
|||
return
|
||||
end
|
||||
|
||||
ensure_l2n
|
||||
ensure_data
|
||||
|
||||
@layers[id] = data
|
||||
|
||||
|
|
|
|||
|
|
@ -23,7 +23,59 @@ module LVS
|
|||
def initialize
|
||||
super
|
||||
end
|
||||
|
||||
def _netter
|
||||
@netter ||= LVS::LVSNetter::new(self)
|
||||
end
|
||||
|
||||
def _before_cleanup
|
||||
|
||||
# save the netlist database if requested
|
||||
if @output_lvsdb_file && @netter && @netter.lvs_data && @netter.lvs_data.xref
|
||||
|
||||
lvsdb_file = _make_path(@output_lvsdb_file)
|
||||
info("Writing LVS database: #{lvsdb_file} ..")
|
||||
@netter.lvs_data.write(lvsdb_file)
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
# %DRC%
|
||||
# @name report_lvs
|
||||
# @brief Specifies an LVS report for output
|
||||
# @synopsis report_lvs([ filename ])
|
||||
# After the comparison step, the LVS database will be shown
|
||||
# in the netlist database browser in a cross-reference view.
|
||||
# If a filename is given, the LVS database is also written to
|
||||
# this file.
|
||||
#
|
||||
# If this method is called together with report_netlist and two files each, two
|
||||
# files can be generated - one for the extracted netlist (L2N database) and one for the
|
||||
# LVS database. However, report_netlist will only write the extracted netlist
|
||||
# while report_lvs will write the LVS database which also includes the
|
||||
# extracted netlist.
|
||||
#
|
||||
# report_lvs is only effective if a comparison step is included.
|
||||
|
||||
def report_lvs(filename = nil)
|
||||
@show_l2ndb = true
|
||||
if filename
|
||||
filename.is_a?(String) || raise("Argument must be string in report_lvs")
|
||||
end
|
||||
@output_lvsdb_file = filename
|
||||
end
|
||||
|
||||
# ...
|
||||
|
||||
%w(schematic compare same_nets same_circuits same_device_classes equivalent_pins min_caps max_res max_depth max_branch_complexity).each do |f|
|
||||
eval <<"CODE"
|
||||
def #{f}(*args)
|
||||
_netter.#{f}(*args)
|
||||
end
|
||||
CODE
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
|
|
|||
|
|
@ -0,0 +1,203 @@
|
|||
# $autorun-early
|
||||
|
||||
module LVS
|
||||
|
||||
include DRC
|
||||
|
||||
# The LVS netter object
|
||||
|
||||
# %LVS%
|
||||
# @scope
|
||||
# @name Netter
|
||||
# @brief LVS Reference: Netter object
|
||||
# The Netter object provides services related to network extraction
|
||||
# from a layout plus comparison against a reference netlist.
|
||||
# Similar to the DRC netter (which lacks the compare ability), the
|
||||
# relevant method of this object are available as global functions too
|
||||
# where they act on a default incarnation. Usually it's not required
|
||||
# to instantiate a Netter object explicitly.
|
||||
#
|
||||
# An individual netter object can be created, if the netter results
|
||||
# need to be kept for multiple extractions. If you really need
|
||||
# a Netter object, use the global \netter function:
|
||||
#
|
||||
# @code
|
||||
# # create a new Netter object:
|
||||
# nx = netter
|
||||
# nx.connect(poly, contact)
|
||||
# ...
|
||||
# @/code
|
||||
#
|
||||
# ...
|
||||
|
||||
class LVSNetter < DRCNetter
|
||||
|
||||
def initialize(engine)
|
||||
super
|
||||
end
|
||||
|
||||
def make_data
|
||||
|
||||
if @engine._dss
|
||||
@engine._dss.is_singular? || raise("The LVS script features more than one or no layout source - network extraction cannot be performed in such configurations")
|
||||
@lvs = RBA::LayoutVsSchematic::new(@engine._dss)
|
||||
else
|
||||
layout = @engine.source.layout
|
||||
@lvs = RBA::LayoutVsSchematic::new(layout.top_cell.name, layout.dbu)
|
||||
end
|
||||
|
||||
@l2n = @lvs
|
||||
@comparer = RBA::NetlistComparer::new
|
||||
|
||||
end
|
||||
|
||||
def lvs_data
|
||||
l2n_data
|
||||
@lvs
|
||||
end
|
||||
|
||||
def _clear_data
|
||||
super
|
||||
@lvs = nil
|
||||
end
|
||||
|
||||
def _take_data
|
||||
data = super
|
||||
@lvs = nil
|
||||
data
|
||||
end
|
||||
|
||||
def compare
|
||||
@lvs.compare(@comparer)
|
||||
end
|
||||
|
||||
def _ensure_two_netlists
|
||||
|
||||
netlist || raise("No netlist present (not extracted?)")
|
||||
lvs_data.reference || raise("No reference schematic present (no set with 'schematic'?)")
|
||||
|
||||
[ netlist, lvs_data.reference ]
|
||||
|
||||
end
|
||||
|
||||
def same_nets(*args)
|
||||
|
||||
pins.each do |a|
|
||||
a.is_a?(String) || raise("All arguments of 'same_nets' need to be strings")
|
||||
end
|
||||
if args.size < 3
|
||||
raise("Too few arguments to 'same_nets' (need at least 3)")
|
||||
end
|
||||
if args.size > 4
|
||||
raise("Too many arguments to 'same_nets' (need max 4)")
|
||||
end
|
||||
|
||||
if args.size == 3
|
||||
( ca, a, b ) = args
|
||||
cb = ca
|
||||
else
|
||||
( ca, a, cb, b ) = args
|
||||
end
|
||||
|
||||
( nl_a, nl_b ) = _ensure_two_netlists
|
||||
|
||||
circuit_a = nl_a.circuit_by_name(ca) || raise("Not a valid circuit name in extracted netlist: #{ca}")
|
||||
circuit_b = nl_b.circuit_by_name(cb) || raise("Not a valid circuit name in reference netlist: #{cb}")
|
||||
|
||||
net_a = circuit_a.net_by_name(a) || raise("Not a valid net name in extracted netlist: #{a} (for circuit #{circuit_a})")
|
||||
net_b = circuit_b.net_by_name(b) || raise("Not a valid net name in reference netlist: #{b} (for circuit #{circuit_b})")
|
||||
|
||||
@comparer.same_nets(net_a, net_b)
|
||||
|
||||
end
|
||||
|
||||
def same_circuits(a, b)
|
||||
|
||||
a.is_a?(String) || b.is_a?(String) || raise("Both arguments of 'same_circuits' need to be strings")
|
||||
|
||||
( nl_a, nl_b ) = _ensure_two_netlists
|
||||
|
||||
circuit_a = nl_a.circuit_by_name(a) || raise("Not a valid circuit name in extracted netlist: #{a}")
|
||||
circuit_b = nl_b.circuit_by_name(b) || raise("Not a valid circuit name in reference netlist: #{b}")
|
||||
|
||||
@comparer.same_circuits(circuit_a, circuit_b)
|
||||
|
||||
end
|
||||
|
||||
def same_device_classes(a, b)
|
||||
|
||||
a.is_a?(String) || b.is_a?(String) || raise("Both arguments of 'same_device_classes' need to be strings")
|
||||
|
||||
( nl_a, nl_b ) = _ensure_two_netlists
|
||||
|
||||
dc_a = nl_a.device_class_by_name(a) || raise("Not a valid device class in extracted netlist: #{a}")
|
||||
dc_b = nl_b.device_class_by_name(b) || raise("Not a valid device class in reference netlist: #{b}")
|
||||
|
||||
@comparer.same_device_classes(dc_a, dc_b)
|
||||
|
||||
end
|
||||
|
||||
def equivalent_pins(circuit, *pins)
|
||||
|
||||
circuit.is_a?(String) || raise("Circuit arguments of 'equivalent_pins' needs to be a string")
|
||||
pins.each do |a|
|
||||
a.is_a?(String) || raise("All pin arguments of 'equivalent_pins' need to be strings")
|
||||
end
|
||||
|
||||
( nl_a, nl_b ) = _ensure_two_netlists
|
||||
|
||||
circuit_b = nl_b.circuit_by_name(circuit) || raise("Not a valid circuit name in reference netlist: #{circuit}")
|
||||
|
||||
pin_ids_b = pins.collect do |p|
|
||||
pin = circuit_b.pin_by_name(p) || raise("Not a valid pin name in circuit '#{circuit}': #{p}")
|
||||
pin.id
|
||||
end
|
||||
|
||||
@comparer.equivalent_pins(circuit, pin_ids_b)
|
||||
|
||||
end
|
||||
|
||||
def schematic(filename, reader = nil)
|
||||
|
||||
filename.is_a?(String) || raise("First argument must be string in 'schematic'")
|
||||
|
||||
if reader
|
||||
reader.is_a?(RBA::NetlistReader) || raise("Second argument must be netlist reader object in 'schematic'")
|
||||
else
|
||||
reader = RBA::NetlistSpiceReader::new
|
||||
end
|
||||
|
||||
netlist_file = @engine._make_path(filename)
|
||||
@engine.info("Reading netlist: #{netlist_file} ..")
|
||||
|
||||
netlist = RBA::Netlist::new
|
||||
netlist.read(netlist_file, reader)
|
||||
|
||||
lvs_data.reference = netlist
|
||||
|
||||
end
|
||||
|
||||
def min_caps(value)
|
||||
lvs_data
|
||||
@comparer.min_capacitance = value.to_f
|
||||
end
|
||||
|
||||
def max_res(value)
|
||||
lvs_data
|
||||
@comparer.max_resistance = value.to_f
|
||||
end
|
||||
|
||||
def max_depth(value)
|
||||
lvs_data
|
||||
@comparer.max_depth = value.to_i
|
||||
end
|
||||
|
||||
def max_branch_complexity(value)
|
||||
lvs_data
|
||||
@comparer.max_branch_complexity = value.to_i
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
|
|
@ -7,6 +7,7 @@
|
|||
<file alias="_drc_source.rb">built-in-macros/_drc_source.rb</file>
|
||||
<file alias="_drc_tags.rb">built-in-macros/_drc_tags.rb</file>
|
||||
<file alias="drc_interpreters.lym">built-in-macros/drc_interpreters.lym</file>
|
||||
<file alias="_lvs_netter.rb">built-in-macros/_lvs_netter.rb</file>
|
||||
<file alias="_lvs_engine.rb">built-in-macros/_lvs_engine.rb</file>
|
||||
<file alias="lvs_interpreters.lym">built-in-macros/lvs_interpreters.lym</file>
|
||||
<file alias="install.lym">built-in-macros/install.lym</file>
|
||||
|
|
|
|||
Loading…
Reference in New Issue