Merge pull request #1657 from KLayout/1598-support-for-soft-connections

1598 support for soft connections
This commit is contained in:
Matthias Köfferlein 2024-03-29 18:27:14 +01:00 committed by GitHub
commit 4f67ae5261
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
71 changed files with 5256 additions and 286 deletions

27
samples/lvs/RINGO.cir Normal file
View File

@ -0,0 +1,27 @@
.SUBCKT RINGO VSS VDD FB ENABLE OUT
X$1 VDD VSS 1 FB ENABLE ND2X1
X$2 VDD VSS 2 1 INVX1
X$3 VDD VSS 3 2 INVX1
X$4 VDD VSS 4 3 INVX1
X$5 VDD VSS 5 4 INVX1
X$6 VDD VSS 6 5 INVX1
X$7 VDD VSS 7 6 INVX1
X$8 VDD VSS 8 7 INVX1
X$9 VDD VSS 9 8 INVX1
X$10 VDD VSS 10 9 INVX1
X$11 VDD VSS FB 10 INVX1
X$12 VDD VSS OUT FB INVX1
.ENDS RINGO
.SUBCKT ND2X1 VDD VSS OUT B A
M$1 OUT A VDD VDD LVPMOS L=0.25U W=1.5U
M$2 VDD B OUT VDD LVPMOS L=0.25U W=1.5U
M$3 VSS A 1 VSS LVNMOS L=0.25U W=0.95U
M$4 1 B OUT VSS LVNMOS L=0.25U W=0.95U
.ENDS ND2X1
.SUBCKT INVX1 VDD VSS OUT IN
M$1 VDD IN OUT VDD LVPMOS L=0.25U W=1.5U
M$2 VSS IN OUT VSS LVNMOS L=0.25U W=0.95U
.ENDS INVX1

Binary file not shown.

View File

@ -1,27 +0,0 @@
.SUBCKT RINGO VSS VDD FB ENABLE OUT
X$1 VDD 1 VSS VDD FB ENABLE VSS ND2X1
X$2 VDD 2 VSS VDD 1 VSS INVX1
X$3 VDD 3 VSS VDD 2 VSS INVX1
X$4 VDD 4 VSS VDD 3 VSS INVX1
X$5 VDD 5 VSS VDD 4 VSS INVX1
X$6 VDD 6 VSS VDD 5 VSS INVX1
X$7 VDD 7 VSS VDD 6 VSS INVX1
X$8 VDD 8 VSS VDD 7 VSS INVX1
X$9 VDD 9 VSS VDD 8 VSS INVX1
X$10 VDD 10 VSS VDD 9 VSS INVX1
X$11 VDD FB VSS VDD 10 VSS INVX1
X$12 VDD OUT VSS VDD FB VSS INVX1
.ENDS RINGO
.SUBCKT ND2X1 VDD OUT VSS NWELL B A BULK
M$1 OUT A VDD NWELL PMOS L=0.25U W=1.5U
M$2 VDD B OUT NWELL PMOS L=0.25U W=1.5U
M$3 VSS A 1 BULK NMOS L=0.25U W=0.95U
M$4 1 B OUT BULK NMOS L=0.25U W=0.95U
.ENDS ND2X1
.SUBCKT INVX1 VDD OUT VSS NWELL IN BULK
M$1 VDD IN OUT NWELL PMOS L=0.25U W=1.5U
M$2 VSS IN OUT BULK NMOS L=0.25U W=0.95U
.ENDS INVX1

128
samples/lvs/si4all.lvs Normal file
View File

@ -0,0 +1,128 @@
# Hierarchical mode
deep
# Print details
verbose
# Output generation (dialog only)
report_lvs
# Enable this to produce a L2N database
# report_netlist("extracted.l2n")
# True to write the extracted netlist
if false
# true: use net names instead of numbers
# false: use numbers for nets
spice_with_net_names = true
# true: put in comments with details
# false: no comments
spice_with_comments = false
# Extracted netlist
target_netlist(File.join(File.dirname(File.absolute_path(source.path || ".")), source.cell_name + "_extracted.cir"), write_spice(spice_with_net_names, spice_with_comments), "Extracted by KLayout on : #{Time.now.strftime("%d/%m/%Y %H:%M")}")
end
# Specify the schematic netlist
# (looks for a file called <cell name>.cir where <cell name>
# is the current cell name). The file is looked up relative to
# the layout file name.
schematic(File.join(File.dirname(File.absolute_path(source.path || ".")), source.cell_name + ".cir"))
# layers definitions
########################
nwell = input(1, 0)
diff = input(2, 0)
pplus = input(3, 0)
nplus = input(4, 0)
poly = input(5, 0)
thickox = input(6, 0)
polyres = input(7, 0)
contact = input(8, 0)
metal1 = input(9, 0)
via = input(10, 0)
metal2 = input(11, 0)
pad = input(12, 0)
border = input(13, 0)
# Special layer for bulk terminals
bulk = make_layer
# Computed layers
diff_in_nwell = diff & nwell
pdiff = diff_in_nwell - nplus
ntie = diff_in_nwell & nplus
pgate = pdiff & poly
psd = pdiff - pgate
hv_pgate = pgate & thickox
lv_pgate = pgate - hv_pgate
hv_psd = psd & thickox
lv_psd = psd - thickox
diff_outside_nwell = diff - nwell
ndiff = diff_outside_nwell - pplus
ptie = diff_outside_nwell & pplus
ngate = ndiff & poly
nsd = ndiff - ngate
hv_ngate = ngate & thickox
lv_ngate = ngate - hv_ngate
hv_nsd = nsd & thickox
lv_nsd = nsd - thickox
# PMOS transistor device extraction
hvpmos_ex = RBA::DeviceExtractorMOS4Transistor::new("HVPMOS")
extract_devices(hvpmos_ex, { "SD" => psd, "G" => hv_pgate, "P" => poly, "W" => nwell })
lvpmos_ex = RBA::DeviceExtractorMOS4Transistor::new("LVPMOS")
extract_devices(lvpmos_ex, { "SD" => psd, "G" => lv_pgate, "P" => poly, "W" => nwell })
# NMOS transistor device extraction
lvnmos_ex = RBA::DeviceExtractorMOS4Transistor::new("LVNMOS")
extract_devices(lvnmos_ex, { "SD" => nsd, "G" => lv_ngate, "P" => poly, "W" => bulk })
hvnmos_ex = RBA::DeviceExtractorMOS4Transistor::new("HVNMOS")
extract_devices(hvnmos_ex, { "SD" => nsd, "G" => hv_ngate, "P" => poly, "W" => bulk })
# Define connectivity for netlist extraction
# Inter-layer
connect(contact, ntie)
connect(contact, ptie)
connect(nwell, ntie)
connect(psd, contact)
connect(nsd, contact)
connect(poly, contact)
connect(contact, metal1)
connect(metal1, via)
connect(via, metal2)
# Make "must-connect" connections between NWELL and VDD and BULK and VSS
connect_explicit(["NWELL", "VDD"])
connect_explicit(["BULK", "VSS"])
# Global connections
connect_global(ptie, "BULK")
connect_global(bulk, "BULK")
# Actually performs the extraction
netlist
# Flatten cells which are present in one netlist only
align
# Simplication of the netlist
netlist.simplify
# LVS compare
if compare
puts "Congratulations! Netlists match."
else
puts "LVS ERROR: netlists do not match!"
end

View File

@ -45,6 +45,7 @@ SOURCES = \
dbLayoutDiff.cc \
dbLayoutQuery.cc \
dbLayoutStateModel.cc \
dbLayoutToNetlistSoftConnections.cc \
dbLayoutUtils.cc \
dbLibrary.cc \
dbLibraryManager.cc \
@ -270,6 +271,7 @@ HEADERS = \
dbLayoutQuery.h \
dbLayoutStateModel.h \
dbLayoutToNetlistEnums.h \
dbLayoutToNetlistSoftConnections.h \
dbLayoutUtils.h \
dbLibrary.h \
dbLibraryManager.h \

File diff suppressed because it is too large Load Diff

View File

@ -60,9 +60,11 @@ class DeepLayer;
class DB_PUBLIC Connectivity
{
public:
typedef std::set<unsigned int> layers_type;
typedef std::set<unsigned int> all_layers_type;
typedef all_layers_type::const_iterator all_layer_iterator;
typedef std::map<unsigned int, int> layers_type;
typedef layers_type::const_iterator layer_iterator;
typedef std::set<size_t> global_nets_type;
typedef std::map<unsigned int, int> global_nets_type;
typedef global_nets_type::const_iterator global_nets_iterator;
/**
@ -101,11 +103,26 @@ public:
*/
void connect (unsigned int la, unsigned int lb);
/**
* @brief Adds inter-layer connectivity of the soft type
*
* Soft connections are directed and are reported during "interacts"
* by the "soft" output argument. "la" is the "upper" layer and "lb" is the lower layer.
*/
void soft_connect (unsigned int la, unsigned int lb);
/**
* @brief Adds a connection to a global net
*/
size_t connect_global (unsigned int l, const std::string &gn);
/**
* @brief Adds a soft connection to a global net
*
* The global net is always the "lower" layer.
*/
size_t soft_connect_global (unsigned int l, const std::string &gn);
/**
* @brief Adds intra-layer connectivity for layer l
* This is a convenience method that takes a db::DeepLayer object.
@ -120,11 +137,28 @@ public:
*/
void connect (const db::DeepLayer &la, const db::DeepLayer &lb);
/**
* @brief Adds inter-layer connectivity
* This is a convenience method that takes a db::DeepLayer object.
* It is assumed that all those layers originate from the same deep shape store.
*
* Soft connections are directed and are reported during "interacts"
* by the "soft" output argument. "la" is the "upper" layer and "lb" is the lower layer.
*/
void soft_connect (const db::DeepLayer &la, const db::DeepLayer &lb);
/**
* @brief Adds a connection to a global net
*/
size_t connect_global (const db::DeepLayer &la, const std::string &gn);
/**
* @brief Adds a soft connection to a global net
*
* The global net is always the "lower" layer.
*/
size_t soft_connect_global (const db::DeepLayer &la, const std::string &gn);
/**
* @brief Gets the global net name per ID
*/
@ -143,15 +177,19 @@ public:
/**
* @brief Begin iterator for the layers involved
*/
layer_iterator begin_layers () const;
all_layer_iterator begin_layers () const;
/**
* @brief End iterator for the layers involved
*/
layer_iterator end_layers () const;
all_layer_iterator end_layers () const;
/**
* @brief Begin iterator for the layers connected to a specific layer
*
* The iterator returned is over a map of target layers and soft mode
* (an int, being 0 for a hard connection, +1 for an upward soft connection
* and -1 for a downward soft connection).
*/
layer_iterator begin_connected (unsigned int layer) const;
@ -162,6 +200,10 @@ public:
/**
* @brief Begin iterator for the global connections for a specific layer
*
* The iterator returned is over a map of global net ID and soft mode
* (an int, being 0 for a hard connection, +1 for an upward soft connection
* and -1 for a downward soft connection).
*/
global_nets_iterator begin_global_connections (unsigned int layer) const;
@ -175,17 +217,21 @@ public:
*
* This method accepts a transformation. This transformation is applied
* to the b shape before checking against a.
*
* The "soft" output argument will deliver the soft mode that applies
* to the connection - 0: hard connection, -1: a is the lower layer, +1: a is
* the upper layer.
*/
template <class T, class Trans>
bool interacts (const T &a, unsigned int la, const T &b, unsigned int lb, const Trans &trans) const;
bool interacts (const T &a, unsigned int la, const T &b, unsigned int lb, const Trans &trans, int &soft) const;
/**
* @brief Returns true, if the given shapes on the given layers interact
*/
template <class T>
bool interacts (const T &a, unsigned int la, const T &b, unsigned int lb) const
bool interacts (const T &a, unsigned int la, const T &b, unsigned int lb, int &soft) const
{
return interacts (a, la, b, lb, UnitTrans ());
return interacts (a, la, b, lb, UnitTrans (), soft);
}
/**
@ -195,17 +241,21 @@ public:
/**
* @brief Returns true, if two cells basically (without considering transformation) interact
*
* This is a pretty basic check based on the cell's bounding boxes
*/
bool interact (const db::Cell &a, const db::Cell &b) const;
/**
* @brief Returns true, if two cells with the given transformations interact
*
* This is a pretty basic check based on the cell's bounding boxes
*/
template <class T>
bool interact (const db::Cell &a, const T &ta, const db::Cell &b, const T &tb) const;
private:
layers_type m_all_layers;
all_layers_type m_all_layers;
std::map<unsigned int, layers_type> m_connected;
std::vector<std::string> m_global_net_names;
std::map<unsigned int, global_nets_type> m_global_connections;
@ -277,7 +327,7 @@ public:
* "trans" is the transformation which is applied to the other cluster before
* the test.
*/
bool interacts (const local_cluster<T> &other, const db::ICplxTrans &trans, const Connectivity &conn) const;
bool interacts (const local_cluster<T> &other, const db::ICplxTrans &trans, const Connectivity &conn, int &soft) const;
/**
* @brief Tests whether this cluster interacts with the given cell
@ -288,7 +338,7 @@ public:
bool interacts (const db::Cell &cell, const db::ICplxTrans &trans, const Connectivity &conn) const;
/**
* @brief Gets the bounding box of this cluster
* @brief Gets the bounding box ofF this cluster
*/
const box_type &bbox () const
{
@ -572,6 +622,21 @@ public:
return id > m_clusters.size ();
}
/**
* @brief Makes a soft connection between clusters a and b (a: upper, b: lower)
*/
void make_soft_connection (typename local_cluster<T>::id_type a, typename local_cluster<T>::id_type b);
/**
* @brief Get the downward soft connections for a given cluster
*/
const std::set<size_t> &downward_soft_connections (typename local_cluster<T>::id_type id) const;
/**
* @brief Get the upward soft connections for a given cluster
*/
const std::set<size_t> &upward_soft_connections (typename local_cluster<T>::id_type id) const;
/**
* @brief Gets the number of clusters
*/
@ -592,8 +657,11 @@ private:
box_type m_bbox;
tree_type m_clusters;
size_t m_next_dummy_id;
std::map<size_t, std::set<size_t> > m_soft_connections;
std::map<size_t, std::set<size_t> > m_soft_connections_rev;
void apply_attr_equivalences (const tl::equivalence_clusters<size_t> &attr_equivalence);
void remove_soft_connections_for (typename local_cluster<T>::id_type id);
};
/**
@ -794,7 +862,61 @@ private:
size_t m_id;
};
typedef std::list<std::pair<ClusterInstance, ClusterInstance> > cluster_instance_pair_list_type;
struct ClusterInstancePair
{
ClusterInstancePair (const ClusterInstance &_a, const ClusterInstance &_b, int _soft)
: a (_a), b (_b), soft (_soft)
{ }
bool operator== (const ClusterInstancePair &other) const
{
return a == other.a && b == other.b && soft == other.soft;
}
bool operator< (const ClusterInstancePair &other) const
{
if (!(a == other.a)) {
return a < other.a;
}
if (!(b == other.b)) {
return b < other.b;
}
return soft < other.soft;
}
ClusterInstance a, b;
int soft;
};
typedef std::list<ClusterInstancePair> cluster_instance_pair_list_type;
struct ClusterIDPair
{
typedef size_t id_type;
ClusterIDPair (id_type _a, id_type _b, int _soft)
: a (_a), b (_b), soft (_soft)
{ }
bool operator== (const ClusterIDPair &other) const
{
return a == other.a && b == other.b && soft == other.soft;
}
bool operator< (const ClusterIDPair &other) const
{
if (!(a == other.a)) {
return a < other.a;
}
if (!(b == other.b)) {
return b < other.b;
}
return soft < other.soft;
}
id_type a, b;
int soft;
};
inline bool equal_array_delegates (const db::ArrayBase *a, const db::ArrayBase *b)
{
@ -1106,7 +1228,7 @@ public:
* The "with_id" cluster is removed. All connections of "with_id" are transferred to the
* first one. All shapes of "with_id" are transferred to "id".
*/
void join_cluster_with(typename local_cluster<T>::id_type id, typename local_cluster<T>::id_type with_id);
void join_cluster_with (typename local_cluster<T>::id_type id, typename local_cluster<T>::id_type with_id);
/**
* @brief An iterator delivering all clusters (even the connectors)

View File

@ -32,7 +32,9 @@
#include "dbLayoutVsSchematic.h"
#include "dbLayoutToNetlistFormatDefs.h"
#include "dbLayoutVsSchematicFormatDefs.h"
#include "dbLayoutToNetlistSoftConnections.h"
#include "dbShapeProcessor.h"
#include "dbNetlistDeviceClasses.h"
#include "dbLog.h"
#include "tlGlobPattern.h"
@ -45,7 +47,7 @@ namespace db
// Note: the iterator provides the hierarchical selection (enabling/disabling cells etc.)
LayoutToNetlist::LayoutToNetlist (const db::RecursiveShapeIterator &iter)
: m_iter (iter), m_layout_index (0), m_netlist_extracted (false), m_is_flat (false), m_device_scaling (1.0), m_include_floating_subcircuits (false), m_top_level_mode (false)
: m_iter (iter), m_layout_index (0), m_netlist_extracted (false), m_is_flat (false), m_device_scaling (1.0), m_include_floating_subcircuits (false), m_top_level_mode (false), m_make_soft_connection_diodes (false)
{
// check the iterator
if (iter.has_complex_region () || iter.region () != db::Box::world ()) {
@ -65,7 +67,7 @@ LayoutToNetlist::LayoutToNetlist (const db::RecursiveShapeIterator &iter)
}
LayoutToNetlist::LayoutToNetlist (db::DeepShapeStore *dss, unsigned int layout_index)
: mp_dss (dss), m_layout_index (layout_index), m_netlist_extracted (false), m_is_flat (false), m_device_scaling (1.0), m_include_floating_subcircuits (false), m_top_level_mode (false)
: mp_dss (dss), m_layout_index (layout_index), m_netlist_extracted (false), m_is_flat (false), m_device_scaling (1.0), m_include_floating_subcircuits (false), m_top_level_mode (false), m_make_soft_connection_diodes (false)
{
if (dss->is_valid_layout_index (m_layout_index)) {
m_iter = db::RecursiveShapeIterator (dss->layout (m_layout_index), dss->initial_cell (m_layout_index), std::set<unsigned int> ());
@ -73,7 +75,7 @@ LayoutToNetlist::LayoutToNetlist (db::DeepShapeStore *dss, unsigned int layout_i
}
LayoutToNetlist::LayoutToNetlist (const std::string &topcell_name, double dbu)
: m_iter (), m_netlist_extracted (false), m_is_flat (true), m_device_scaling (1.0), m_include_floating_subcircuits (false), m_top_level_mode (false)
: m_iter (), m_netlist_extracted (false), m_is_flat (true), m_device_scaling (1.0), m_include_floating_subcircuits (false), m_top_level_mode (false), m_make_soft_connection_diodes (false)
{
mp_internal_dss.reset (new db::DeepShapeStore (topcell_name, dbu));
mp_dss.reset (mp_internal_dss.get ());
@ -84,7 +86,7 @@ LayoutToNetlist::LayoutToNetlist (const std::string &topcell_name, double dbu)
LayoutToNetlist::LayoutToNetlist ()
: m_iter (), mp_internal_dss (new db::DeepShapeStore ()), mp_dss (mp_internal_dss.get ()), m_layout_index (0),
m_netlist_extracted (false), m_is_flat (false), m_device_scaling (1.0), m_include_floating_subcircuits (false), m_top_level_mode (false)
m_netlist_extracted (false), m_is_flat (false), m_device_scaling (1.0), m_include_floating_subcircuits (false), m_top_level_mode (false), m_make_soft_connection_diodes (false)
{
init ();
}
@ -298,6 +300,26 @@ void LayoutToNetlist::connect_impl (const db::ShapeCollection &a, const db::Shap
m_conn.connect (dla.layer (), dlb.layer ());
}
void LayoutToNetlist::soft_connect_impl (const db::ShapeCollection &a, const db::ShapeCollection &b)
{
reset_extracted ();
if (! is_persisted (a)) {
register_layer (a);
}
if (! is_persisted (b)) {
register_layer (b);
}
// we need to keep a reference, so we can safely delete the region
db::DeepLayer dla = deep_layer_of (a);
db::DeepLayer dlb = deep_layer_of (b);
m_dlrefs.insert (dla);
m_dlrefs.insert (dlb);
m_conn.soft_connect (dla.layer (), dlb.layer ());
}
size_t LayoutToNetlist::connect_global_impl (const db::ShapeCollection &l, const std::string &gn)
{
reset_extracted ();
@ -313,6 +335,21 @@ size_t LayoutToNetlist::connect_global_impl (const db::ShapeCollection &l, const
return m_conn.connect_global (dl.layer (), gn);
}
size_t LayoutToNetlist::soft_connect_global_impl (const db::ShapeCollection &l, const std::string &gn)
{
reset_extracted ();
if (! is_persisted (l)) {
register_layer (l);
}
// we need to keep a reference, so we can safely delete the region
db::DeepLayer dl = deep_layer_of (l);
m_dlrefs.insert (dl);
return m_conn.soft_connect_global (dl.layer (), gn);
}
const std::string &LayoutToNetlist::global_net_name (size_t id) const
{
return m_conn.global_net_name (id);
@ -360,6 +397,24 @@ void LayoutToNetlist::join_nets (const tl::GlobPattern &cell, const std::set<std
m_joined_nets_per_cell.push_back (std::make_pair (cell, gp));
}
#if defined(_DEBUG)
static bool check_many_pins (const db::Netlist *netlist)
{
bool ok = true;
for (auto c = netlist->begin_circuits (); c != netlist->end_circuits (); ++c) {
const db::Circuit &circuit = *c;
for (auto n = circuit.begin_nets (); n != circuit.end_nets (); ++n) {
const db::Net &net = *n;
if (net.pin_count () > 1) {
ok = false;
tl::error << "Many pins on net " << net.expanded_name () << " in circuit " << circuit.name ();
}
}
}
return ok;
}
#endif
void LayoutToNetlist::extract_netlist ()
{
if (m_netlist_extracted) {
@ -371,6 +426,17 @@ void LayoutToNetlist::extract_netlist ()
netex.set_include_floating_subcircuits (m_include_floating_subcircuits);
netex.extract_nets (dss (), m_layout_index, m_conn, *mp_netlist, m_net_clusters);
// treat soft connections
do_soft_connections ();
// implement the "join_nets" (aka "must connect") feature
#if defined(_DEBUG)
// NOTE: the join_nets feature only works for "one pin per net" case
// TODO: either fix that or make sure we do not get multiple pins per net.
// Right now, there no known case that produces multiple pins on a net at
// this stage.
tl_assert (check_many_pins (mp_netlist.get ()));
#endif
do_join_nets ();
if (tl::verbosity () >= 41) {
@ -477,7 +543,7 @@ void LayoutToNetlist::check_must_connect (const db::Circuit &c, const db::Net &a
check_must_connect_impl (c, a, b, c, a, b, path);
}
static std::string path_msg (const std::vector<const db::SubCircuit *> &path, const db::Circuit &c_org)
static std::string path_msg (const std::vector<const db::SubCircuit *> &path)
{
if (path.empty ()) {
return std::string ();
@ -485,15 +551,13 @@ static std::string path_msg (const std::vector<const db::SubCircuit *> &path, co
std::string msg (".\n" + tl::to_string (tr ("Instance path: ")));
for (auto p = path.rbegin (); p != path.rend (); ++p) {
if (p != path.rbegin ()) {
msg += "/";
}
msg += (*p)->circuit ()->name () + ":" + (*p)->expanded_name () + "[" + (*p)->trans ().to_string () + "]";
}
auto p0 = path.rbegin ();
msg += (*p0)->circuit ()->name ();
msg += "/";
msg += c_org.name ();
for (auto p = p0; p != path.rend (); ++p) {
msg += "/";
msg += (*p)->circuit_ref ()->name () + "[" + (*p)->trans ().to_string (true /*short*/) + "]" + ":" + (*p)->expanded_name ();
}
return msg;
}
@ -519,12 +583,12 @@ void LayoutToNetlist::check_must_connect_impl (const db::Circuit &c, const db::N
if (a_org.expanded_name () == b_org.expanded_name ()) {
if (path.empty ()) {
db::LogEntryData warn (m_top_level_mode ? db::Error : db::Warning, tl::sprintf (tl::to_string (tr ("Must-connect nets %s must be connected further up in the hierarchy - this is an error at chip top level")), a_org.expanded_name ()) + path_msg (path, c_org));
db::LogEntryData warn (m_top_level_mode ? db::Error : db::Warning, tl::sprintf (tl::to_string (tr ("Must-connect nets %s must be connected further up in the hierarchy - this is an error at chip top level")), a_org.expanded_name ()) + path_msg (path));
warn.set_cell_name (c.name ());
warn.set_category_name ("must-connect");
log_entry (warn);
} else {
db::LogEntryData warn (m_top_level_mode ? db::Error : db::Warning, tl::sprintf (tl::to_string (tr ("Must-connect nets %s of circuit %s must be connected further up in the hierarchy - this is an error at chip top level")), a_org.expanded_name (), c_org.name ()) + path_msg (path, c_org));
db::LogEntryData warn (m_top_level_mode ? db::Error : db::Warning, tl::sprintf (tl::to_string (tr ("Must-connect nets %s of circuit %s must be connected further up in the hierarchy - this is an error at chip top level")), a_org.expanded_name (), c_org.name ()) + path_msg (path));
warn.set_cell_name (c.name ());
warn.set_geometry (subcircuit_geometry (*path.back (), internal_layout ()));
warn.set_category_name ("must-connect");
@ -532,12 +596,12 @@ void LayoutToNetlist::check_must_connect_impl (const db::Circuit &c, const db::N
}
} else {
if (path.empty ()) {
db::LogEntryData warn (m_top_level_mode ? db::Error : db::Warning, tl::sprintf (tl::to_string (tr ("Must-connect nets %s and %s must be connected further up in the hierarchy - this is an error at chip top level")), a_org.expanded_name (), b_org.expanded_name ()) + path_msg (path, c_org));
db::LogEntryData warn (m_top_level_mode ? db::Error : db::Warning, tl::sprintf (tl::to_string (tr ("Must-connect nets %s and %s must be connected further up in the hierarchy - this is an error at chip top level")), a_org.expanded_name (), b_org.expanded_name ()) + path_msg (path));
warn.set_cell_name (c.name ());
warn.set_category_name ("must-connect");
log_entry (warn);
} else {
db::LogEntryData warn (m_top_level_mode ? db::Error : db::Warning, tl::sprintf (tl::to_string (tr ("Must-connect nets %s and %s of circuit %s must be connected further up in the hierarchy - this is an error at chip top level")), a_org.expanded_name (), b_org.expanded_name (), c_org.name ()) + path_msg (path, c_org));
db::LogEntryData warn (m_top_level_mode ? db::Error : db::Warning, tl::sprintf (tl::to_string (tr ("Must-connect nets %s and %s of circuit %s must be connected further up in the hierarchy - this is an error at chip top level")), a_org.expanded_name (), b_org.expanded_name (), c_org.name ()) + path_msg (path));
warn.set_cell_name (c.name ());
warn.set_geometry (subcircuit_geometry (*path.back (), internal_layout ()));
warn.set_category_name ("must-connect");
@ -558,7 +622,7 @@ void LayoutToNetlist::check_must_connect_impl (const db::Circuit &c, const db::N
const db::Net *net_b = sc.net_for_pin (b.begin_pins ()->pin_id ());
if (net_a == 0) {
db::LogEntryData error (db::Error, tl::sprintf (tl::to_string (tr ("Must-connect net %s of circuit %s is not connected at all%s")), a_org.expanded_name (), c_org.name (), subcircuit_to_string (sc)) + path_msg (path, c_org));
db::LogEntryData error (db::Error, tl::sprintf (tl::to_string (tr ("Must-connect net %s of circuit %s is not connected at all%s")), a_org.expanded_name (), c_org.name (), subcircuit_to_string (sc)) + path_msg (path));
error.set_cell_name (sc.circuit ()->name ());
error.set_geometry (subcircuit_geometry (sc, internal_layout ()));
error.set_category_name ("must-connect");
@ -566,7 +630,7 @@ void LayoutToNetlist::check_must_connect_impl (const db::Circuit &c, const db::N
}
if (net_b == 0) {
db::LogEntryData error (db::Error, tl::sprintf (tl::to_string (tr ("Must-connect net %s of circuit %s is not connected at all%s")), b_org.expanded_name (), c_org.name (), subcircuit_to_string (sc)) + path_msg (path, c_org));
db::LogEntryData error (db::Error, tl::sprintf (tl::to_string (tr ("Must-connect net %s of circuit %s is not connected at all%s")), b_org.expanded_name (), c_org.name (), subcircuit_to_string (sc)) + path_msg (path));
error.set_cell_name (sc.circuit ()->name ());
error.set_geometry (subcircuit_geometry (sc, internal_layout ()));
error.set_category_name ("must-connect");
@ -584,6 +648,54 @@ void LayoutToNetlist::check_must_connect_impl (const db::Circuit &c, const db::N
}
}
void LayoutToNetlist::place_soft_connection_diodes ()
{
db::DeviceClassDiode *soft_diode = 0;
for (auto c = mp_netlist->begin_bottom_up (); c != mp_netlist->end_bottom_up (); ++c) {
auto clusters = net_clusters ().clusters_per_cell (c->cell_index ());
for (auto n = c->begin_nets (); n != c->end_nets (); ++n) {
auto soft_connections = clusters.upward_soft_connections (n->cluster_id ());
for (auto sc = soft_connections.begin (); sc != soft_connections.end (); ++sc) {
if (! soft_diode) {
soft_diode = new db::DeviceClassDiode ();
soft_diode->set_name ("SOFT");
mp_netlist->add_device_class (soft_diode);
}
db::Device *sc_device = new db::Device (soft_diode);
c->add_device (sc_device);
auto nn = c->net_by_cluster_id (*sc);
if (nn) {
sc_device->connect_terminal (db::DeviceClassDiode::terminal_id_C, n.operator-> ());
sc_device->connect_terminal (db::DeviceClassDiode::terminal_id_A, nn);
}
}
}
}
}
void LayoutToNetlist::do_soft_connections ()
{
SoftConnectionInfo sc_info;
sc_info.build (*netlist (), net_clusters ());
sc_info.report (*this);
if (m_make_soft_connection_diodes) {
place_soft_connection_diodes ();
} else {
sc_info.join_soft_connections (*netlist ());
}
}
void LayoutToNetlist::do_join_nets ()
{
if (! mp_netlist) {
@ -915,7 +1027,7 @@ LayoutToNetlist::create_layermap (db::Layout &target_layout, int ln) const
std::set<unsigned int> layers_to_copy;
const db::Connectivity &conn = connectivity ();
for (db::Connectivity::layer_iterator layer = conn.begin_layers (); layer != conn.end_layers (); ++layer) {
for (db::Connectivity::all_layer_iterator layer = conn.begin_layers (); layer != conn.end_layers (); ++layer) {
layers_to_copy.insert (*layer);
}
@ -1162,7 +1274,8 @@ size_t LayoutToNetlist::search_net (const db::ICplxTrans &trans, const db::Cell
const db::local_clusters<db::NetShape> &lcc = net_clusters ().clusters_per_cell (cell->cell_index ());
for (db::local_clusters<db::NetShape>::touching_iterator i = lcc.begin_touching (local_box); ! i.at_end (); ++i) {
const db::local_cluster<db::NetShape> &lc = *i;
if (lc.interacts (test_cluster, trans, m_conn)) {
int soft = 0;
if (lc.interacts (test_cluster, trans, m_conn, soft)) {
return lc.id ();
}
}

View File

@ -494,6 +494,39 @@ public:
connect_impl (b, a);
}
/**
* @brief Defines an inter-layer soft connection for the given layers.
* The conditions mentioned with intra-layer "connect" apply for this method too.
* The "a" layer is the "upper" and the "b" layer the "lower" layer of the
* soft connection.
*/
void soft_connect (const db::Region &a, const db::Region &b)
{
soft_connect_impl (a, b);
}
/**
* @brief Defines an inter-layer connection for the given layers.
* As one layer is a texts layer, this connection will basically add net labels.
* The "a" layer is the "upper" and the "b" layer the "lower" layer of the
* soft connection.
*/
void soft_connect (const db::Region &a, const db::Texts &b)
{
soft_connect_impl (a, b);
}
/**
* @brief Defines an inter-layer connection for the given layers.
* As one layer is a texts layer, this connection will basically add net labels.
* The "a" layer is the "upper" and the "b" layer the "lower" layer of the
* soft connection.
*/
void soft_connect (const db::Texts &a, const db::Region &b)
{
soft_connect_impl (b, a);
}
/**
* @brief Connects the given layer with a global net with the given name
* Returns the global net ID
@ -512,6 +545,26 @@ public:
return connect_global_impl (l, gn);
}
/**
* @brief Soft-connects the given layer with a global net with the given name
* Returns the global net ID.
* The global layer is the "lower" layer of the soft connection.
*/
size_t soft_connect_global (const db::Region &l, const std::string &gn)
{
return soft_connect_global_impl (l, gn);
}
/**
* @brief Soft-connects the given text layer with a global net with the given name
* Returns the global net ID.
* The global layer is the "lower" layer of the soft connection.
*/
size_t soft_connect_global (const db::Texts &l, const std::string &gn)
{
return soft_connect_global_impl (l, gn);
}
/**
* @brief Gets the global net name for a given global net ID
*/
@ -992,6 +1045,18 @@ public:
*/
void mem_stat (MemStatistics *stat, MemStatistics::purpose_t purpose, int cat, bool no_self = false, void *parent = 0) const;
// for debugging and testing
bool make_soft_connection_diodes () const
{
return m_make_soft_connection_diodes;
}
// for debugging and testing
void set_make_soft_connection_diodes (bool f)
{
m_make_soft_connection_diodes = f;
}
private:
// no copying
LayoutToNetlist (const db::LayoutToNetlist &other);
@ -1021,6 +1086,7 @@ private:
std::string m_generator;
bool m_include_floating_subcircuits;
bool m_top_level_mode;
bool m_make_soft_connection_diodes;
std::list<tl::GlobPattern> m_joined_net_names;
std::list<std::pair<tl::GlobPattern, tl::GlobPattern> > m_joined_net_names_per_cell;
std::list<std::set<std::string> > m_joined_nets;
@ -1035,14 +1101,20 @@ private:
db::CellMapping make_cell_mapping_into (db::Layout &layout, db::Cell &cell, const std::vector<const db::Net *> *nets, bool with_device_cells);
void connect_impl (const db::ShapeCollection &a, const db::ShapeCollection &b);
size_t connect_global_impl (const db::ShapeCollection &l, const std::string &gn);
void soft_connect_impl (const db::ShapeCollection &a, const db::ShapeCollection &b);
size_t soft_connect_global_impl (const db::ShapeCollection &l, const std::string &gn);
bool is_persisted_impl (const db::ShapeCollection &coll) const;
void do_join_nets (db::Circuit &c, const std::vector<Net *> &nets);
void do_join_nets ();
void do_soft_connections ();
void join_nets_from_pattern (db::Circuit &c, const tl::GlobPattern &p);
void join_nets_from_pattern (db::Circuit &c, const std::set<std::string> &p);
void check_must_connect (const db::Circuit &c, const db::Net &a, const db::Net &b);
void check_must_connect_impl (const db::Circuit &c, const db::Net &a, const db::Net &b, const db::Circuit &c_org, const db::Net &a_org, const db::Net &b_org, std::vector<const db::SubCircuit *> &path);
// for debugging and testing
void place_soft_connection_diodes ();
// implementation of NetlistManipulationCallbacks
virtual size_t link_net_to_parent_circuit (const Net *subcircuit_net, Circuit *parent_circuit, const DCplxTrans &trans);
virtual void link_nets (const db::Net *net, const db::Net *with);

View File

@ -39,7 +39,9 @@ namespace l2n_std_format
DB_PUBLIC std::string LongKeys::layer_key ("layer");
DB_PUBLIC std::string LongKeys::class_key ("class");
DB_PUBLIC std::string LongKeys::connect_key ("connect");
DB_PUBLIC std::string LongKeys::softconnect_key ("softconnect");
DB_PUBLIC std::string LongKeys::global_key ("global");
DB_PUBLIC std::string LongKeys::softglobal_key ("softglobal");
DB_PUBLIC std::string LongKeys::circuit_key ("circuit");
DB_PUBLIC std::string LongKeys::net_key ("net");
DB_PUBLIC std::string LongKeys::name_key ("name");
@ -72,7 +74,9 @@ namespace l2n_std_format
DB_PUBLIC std::string ShortKeys::layer_key ("L");
DB_PUBLIC std::string ShortKeys::class_key ("K");
DB_PUBLIC std::string ShortKeys::connect_key ("C");
DB_PUBLIC std::string ShortKeys::softconnect_key ("CS");
DB_PUBLIC std::string ShortKeys::global_key ("G");
DB_PUBLIC std::string ShortKeys::softglobal_key ("GS");
DB_PUBLIC std::string ShortKeys::circuit_key ("X");
DB_PUBLIC std::string ShortKeys::net_key ("N");
DB_PUBLIC std::string ShortKeys::name_key ("I");

View File

@ -66,11 +66,14 @@ namespace db
*
* [connect]:
* connect(<layer1> <name> ...) - connects layer1 with the following layers [short key: C]
* softconnect(<upper> <lower> ...)
* - specifies soft connection between lower and upper layer [short key: CS]
*
* [global]:
* global(<layer> <net-name> ...)
* - connects the shapes of the layer with the given global
* nets [short key: G]
* - connects the shapes of the layer with the given global nets [short key: G]
* softglobal(<layer> <net-name> ...)
* - soft-connects the shapes of the layer with the given global net [shoft key: GS]
*
* [circuit]:
* circuit(<name> [circuit-def]) - circuit (cell) [short key: X]
@ -222,7 +225,9 @@ namespace l2n_std_format
static std::string layer_key;
static std::string class_key;
static std::string connect_key;
static std::string softconnect_key;
static std::string global_key;
static std::string softglobal_key;
static std::string circuit_key;
static std::string net_key;
static std::string name_key;
@ -261,7 +266,9 @@ namespace l2n_std_format
static std::string layer_key;
static std::string class_key;
static std::string connect_key;
static std::string softconnect_key;
static std::string global_key;
static std::string softglobal_key;
static std::string circuit_key;
static std::string net_key;
static std::string name_key;

View File

@ -462,12 +462,17 @@ void LayoutToNetlistStandardReader::read_netlist (db::Netlist *netlist, db::Layo
}
br.done ();
} else if (l2n && (test (skeys::message_key) || test (lkeys::message_key))) {
} else if (l2n && (test (skeys::softconnect_key) || test (lkeys::softconnect_key))) {
db::LogEntryData data;
read_message_entry (data);
l2n->log_entry (data);
Brace br (this);
std::string l1;
read_word_or_quoted (l1);
while (br) {
std::string l2;
read_word_or_quoted (l2);
l2n->soft_connect (layer_by_name (l2n, l1), layer_by_name (l2n, l2));
}
br.done ();
} else if (l2n && (test (skeys::global_key) || test (lkeys::global_key))) {
@ -481,6 +486,25 @@ void LayoutToNetlistStandardReader::read_netlist (db::Netlist *netlist, db::Layo
}
br.done ();
} else if (l2n && (test (skeys::softglobal_key) || test (lkeys::softglobal_key))) {
Brace br (this);
std::string l1;
read_word_or_quoted (l1);
while (br) {
std::string g;
read_word_or_quoted (g);
l2n->soft_connect_global (layer_by_name (l2n, l1), g);
}
br.done ();
} else if (l2n && (test (skeys::message_key) || test (lkeys::message_key))) {
db::LogEntryData data;
read_message_entry (data);
l2n->log_entry (data);
} else if (test (skeys::circuit_key) || test (lkeys::circuit_key)) {
Brace br (this);

View File

@ -0,0 +1,422 @@
/*
KLayout Layout Viewer
Copyright (C) 2006-2024 Matthias Koefferlein
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "dbCommon.h"
#include "dbLayoutToNetlistSoftConnections.h"
#include "dbLayoutToNetlist.h"
#include "dbHierNetworkProcessor.h"
#include "dbNetlist.h"
namespace db
{
// -------------------------------------------------------------------------------
// SoftConnectionNetGraph implementation
SoftConnectionNetGraph::SoftConnectionNetGraph ()
: m_partial_net_count (0)
{
// .. nothing yet ..
}
void SoftConnectionNetGraph::add (const db::Net *net, SoftConnectionPinDir dir, const db::Pin *pin, size_t partial_net_count)
{
m_partial_net_count += partial_net_count;
// this is where we make the decision about the partial nets ...
if (! pin && dir == SoftConnectionPinDir::down ()) {
m_partial_net_count += 1;
}
if (pin) {
m_pin_ids.insert (pin->id ());
}
m_cluster_dir.insert (std::make_pair (net->cluster_id (), dir));
}
// -------------------------------------------------------------------------------
// SoftConnectionCircuitInfo implementation
SoftConnectionCircuitInfo::SoftConnectionCircuitInfo (const db::Circuit *circuit)
: mp_circuit (circuit)
{
// .. nothing yet ..
}
SoftConnectionNetGraph &SoftConnectionCircuitInfo::make_net_graph ()
{
m_net_graphs.push_back (SoftConnectionNetGraph ());
return m_net_graphs.back ();
}
void SoftConnectionCircuitInfo::add_pin_info (const db::Pin *pin, SoftConnectionPinDir dir, SoftConnectionNetGraph *graph_info)
{
if (pin) {
m_pin_info.insert (std::make_pair (pin->id (), std::make_pair (dir, graph_info)));
}
}
SoftConnectionPinDir SoftConnectionCircuitInfo::direction_per_pin (const db::Pin *pin) const
{
if (! pin) {
return SoftConnectionPinDir ();
}
auto p = m_pin_info.find (pin->id ());
return p != m_pin_info.end () ? p->second.first : SoftConnectionPinDir ();
}
const SoftConnectionNetGraph *SoftConnectionCircuitInfo::get_net_graph_per_pin (const db::Pin *pin) const
{
if (! pin) {
return 0;
}
auto p = m_pin_info.find (pin->id ());
return p != m_pin_info.end () ? p->second.second : 0;
}
// -------------------------------------------------------------------------------
// SoftConnectionInfo implementation
SoftConnectionInfo::SoftConnectionInfo ()
{
// .. nothing yet ..
}
void SoftConnectionInfo::build (const db::Netlist &netlist, const db::hier_clusters<db::NetShape> &shape_clusters)
{
for (auto c = netlist.begin_bottom_up (); c != netlist.end_bottom_up (); ++c) {
build_graphs_for_circuit (c.operator-> (), shape_clusters.clusters_per_cell (c->cell_index ()));
}
}
void SoftConnectionInfo::join_soft_connections (db::Netlist &netlist)
{
if (tl::verbosity () >= 20) {
tl::info << "Joining soft-connected net graphs ..";
}
size_t nnet_graphs_tot = 0;
size_t npartial_tot = 0;
for (auto c = netlist.begin_top_down (); c != netlist.end_top_down (); ++c) {
size_t nnet_graphs = 0;
size_t npartial = 0;
auto scc = m_scc_per_circuit.find (c.operator-> ());
if (scc == m_scc_per_circuit.end ()) {
continue;
}
const SoftConnectionCircuitInfo &sc_info = scc->second;
for (auto sc = sc_info.begin (); sc != sc_info.end (); ++sc) {
auto cc = sc->begin_clusters ();
if (cc != sc->end_clusters ()) {
db::Net *net0 = c->net_by_cluster_id (cc->first);
tl_assert (net0 != 0);
++nnet_graphs;
while (++cc != sc->end_clusters ()) {
// TODO: logging?
c->join_nets (net0, c->net_by_cluster_id (cc->first));
++npartial;
}
}
}
nnet_graphs_tot += nnet_graphs;
npartial_tot += npartial;
if (nnet_graphs > 0 && tl::verbosity () >= 30) {
tl::info << "Circuit " << c->name () << ": joined " << nnet_graphs << " soft-connected net clusters with " << npartial << " partial nets.";
}
}
if (tl::verbosity () >= 20) {
tl::info << "Joined " << nnet_graphs_tot << " soft-connected net clusters with " << npartial_tot << " partial nets in total.";
}
m_scc_per_circuit.clear ();
}
db::DPolygon
SoftConnectionInfo::representative_polygon (const db::Net *net, const db::LayoutToNetlist &l2n, const db::CplxTrans &trans)
{
const db::Connectivity &conn = l2n.connectivity ();
const db::hier_clusters<db::NetShape> &net_clusters = l2n.net_clusters ();
db::DBox bbox;
for (auto l = conn.begin_layers (); l != conn.end_layers (); ++l) {
db::recursive_cluster_shape_iterator<db::NetShape> si (net_clusters, *l, net->circuit ()->cell_index (), net->cluster_id ());
while (! si.at_end ()) {
if (si->type () == db::NetShape::Polygon) {
bbox += trans * (si.trans () * si->polygon_ref ().box ());
}
++si;
}
}
return db::DPolygon (bbox);
}
void SoftConnectionInfo::report_partial_nets (const db::Circuit *circuit, const SoftConnectionNetGraph &net_graph, db::LayoutToNetlist &l2n, const std::string &path, const db::DCplxTrans &trans, const std::string &top_cell, int &index, std::set<std::pair<const db::Net *, db::DCplxTrans> > &seen)
{
for (auto cc = net_graph.begin_clusters (); cc != net_graph.end_clusters (); ++cc) {
const db::Net *net = circuit->net_by_cluster_id (cc->first);
if (! seen.insert (std::make_pair (net, trans)).second) {
continue;
}
if (cc->second == SoftConnectionPinDir::down () && ! net->is_floating () && net->begin_pins () == net->end_pins ()) {
std::string msg = tl::sprintf (tl::to_string (tr ("\tPartial net #%d: %s - %s")), ++index, path, net->expanded_name ());
db::LogEntryData entry (db::NoSeverity, top_cell, msg);
entry.set_geometry (representative_polygon (net, l2n, trans * db::CplxTrans (l2n.internal_layout ()->dbu ())));
l2n.log_entry (entry);
}
for (auto sc = net->begin_subcircuit_pins (); sc != net->end_subcircuit_pins (); ++sc) {
const db::SubCircuit *subcircuit = sc->subcircuit ();
const db::Circuit *circuit_ref = subcircuit->circuit_ref ();
auto scc = m_scc_per_circuit.find (circuit_ref);
if (scc == m_scc_per_circuit.end ()) {
continue;
}
const SoftConnectionCircuitInfo &sci = scc->second;
const SoftConnectionNetGraph *scci = sci.get_net_graph_per_pin (sc->pin ());
if (! scci || scci->partial_net_count () == 0) {
continue;
}
std::string p = path;
p += std::string ("/") + circuit_ref->name ();
p += std::string ("[") + subcircuit->trans ().to_string (true /*short*/) + "]:" + subcircuit->expanded_name ();
report_partial_nets (circuit_ref, *scci, l2n, p, trans * subcircuit->trans (), top_cell, index, seen);
}
}
}
void SoftConnectionInfo::report (db::LayoutToNetlist &l2n)
{
const db::Netlist *netlist = l2n.netlist ();
if (! netlist) {
return;
}
for (auto c = netlist->begin_bottom_up (); c != netlist->end_bottom_up (); ++c) {
auto scc = m_scc_per_circuit.find (c.operator-> ());
if (scc == m_scc_per_circuit.end ()) {
continue;
}
const SoftConnectionCircuitInfo &sc_info = scc->second;
for (auto sc = sc_info.begin (); sc != sc_info.end (); ++sc) {
if (sc->partial_net_count () < 2) {
continue;
}
db::LogEntryData log_entry (l2n.top_level_mode () ? db::Error : db::Warning, c->name (), tl::to_string (tr ("Net with incomplete wiring (soft-connected partial nets)")));
log_entry.set_category_name ("soft-connection-check");
l2n.log_entry (log_entry);
int index = 0;
std::set<std::pair<const db::Net *, db::DCplxTrans> > seen;
report_partial_nets (c.operator-> (), *sc, l2n, c->name (), db::DCplxTrans (), c->name (), index, seen);
}
}
}
void SoftConnectionInfo::build_graphs_for_circuit (const db::Circuit *circuit, const db::connected_clusters<db::NetShape> &shape_clusters)
{
SoftConnectionCircuitInfo &sc_circuit_info = m_scc_per_circuit.insert (std::make_pair (circuit, SoftConnectionCircuitInfo (circuit))).first->second;
std::set<size_t> seen;
for (auto c = shape_clusters.begin (); c != shape_clusters.end (); ++c) {
if (seen.find (c->id ()) != seen.end ()) {
continue;
}
// incrementally collect further connected nets (shape clusters)
std::set<size_t> connected;
connected.insert (c->id ());
seen.insert (c->id ());
SoftConnectionNetGraph *sc_net_graph = 0;
while (! connected.empty ()) {
std::set<size_t> next_connected;
for (auto cc = connected.begin (); cc != connected.end (); ++cc) {
const db::Net *net = circuit->net_by_cluster_id (*cc);
if (! net) {
continue;
}
// the direction of a net is 0 for "no connections" or "both up and down"
// and -1 for "down-only" connections and +1 for "up-only" connections:
SoftConnectionPinDir dir;
// direct soft connections to other nets
for (int up = 0; up < 2; ++up) {
std::set<size_t> next = up ? shape_clusters.upward_soft_connections (*cc) : shape_clusters.downward_soft_connections (*cc);
if (! next.empty () || net_has_up_or_down_subcircuit_connections (net, up)) {
dir |= up ? SoftConnectionPinDir::up () : SoftConnectionPinDir::down ();
}
for (auto i = next.begin (); i != next.end (); ++i) {
if (seen.insert (*i).second) {
next_connected.insert (*i);
}
}
}
// collect soft connections via subcircuits
size_t sc_partial_net_count = 0;
std::set<size_t> next = net_connections_through_subcircuits (net, sc_partial_net_count);
for (auto i = next.begin (); i != next.end (); ++i) {
if (seen.insert (*i).second) {
next_connected.insert (*i);
}
}
// is this net associated with a pin?
const db::Pin *pin = 0;
if (net->begin_pins () != net->end_pins ()) {
// TODO: multiple pins per net need to be supported?
tl_assert (net->pin_count () == 1);
pin = net->begin_pins ()->pin ();
}
if (! sc_net_graph) {
sc_net_graph = &sc_circuit_info.make_net_graph ();
}
// we do not count floating nets as they cannot make a functional connection
if (! net->is_floating ()) {
sc_net_graph->add (net, dir, pin, sc_partial_net_count);
}
sc_circuit_info.add_pin_info (pin, dir, sc_net_graph);
}
connected.swap (next_connected);
}
}
}
bool SoftConnectionInfo::net_has_up_or_down_subcircuit_connections (const db::Net *net, bool up)
{
SoftConnectionPinDir look_for_dir = up ? SoftConnectionPinDir::up () : SoftConnectionPinDir::down ();
for (auto sc = net->begin_subcircuit_pins (); sc != net->end_subcircuit_pins (); ++sc) {
const db::Pin *pin = sc->pin ();
const db::Circuit *ref = sc->subcircuit ()->circuit_ref ();
auto scc_ref = m_scc_per_circuit.find (ref);
if (scc_ref != m_scc_per_circuit.end ()) {
SoftConnectionPinDir dir = scc_ref->second.direction_per_pin (pin);
if (dir & look_for_dir) {
return true;
}
}
}
return false;
}
void SoftConnectionInfo::get_net_connections_through_subcircuit (const db::SubCircuit *subcircuit, const db::Pin *pin, std::set<size_t> &ids, size_t &partial_net_count)
{
auto scc = m_scc_per_circuit.find (subcircuit->circuit_ref ());
if (scc != m_scc_per_circuit.end ()) {
const SoftConnectionCircuitInfo &sci = scc->second;
const SoftConnectionNetGraph *scci = sci.get_net_graph_per_pin (pin);
if (scci) {
// NOTE: limiting the partial net count here means we do report a partially connected once in the
// hierarchy, not on every level.
// Say, if you have two subcircuits, one (A) having 2 partial nets and the other (B) none. Then
// (A) would be reported to partial nets only and when combining (A) and (B) we just need to check
// whether B would also have partial nets. By not taking 2 + 0, but 1 + 0 the combination of (A)
// and (B) does not give an error (error = number of partial nets > 1).
partial_net_count += std::min (size_t (1), scci->partial_net_count ());
for (auto p = scci->begin_pins (); p != scci->end_pins (); ++p) {
if (*p != pin->id ()) {
const NetSubcircuitPinRef *netref = subcircuit->netref_for_pin (*p);
if (netref && netref->net ()) {
ids.insert (netref->net ()->cluster_id ());
}
}
}
}
}
}
std::set<size_t> SoftConnectionInfo::net_connections_through_subcircuits (const db::Net *net, size_t &partial_net_count)
{
std::set<size_t> ids;
for (auto sc = net->begin_subcircuit_pins (); sc != net->end_subcircuit_pins (); ++sc) {
get_net_connections_through_subcircuit (sc->subcircuit (), sc->pin (), ids, partial_net_count);
}
return ids;
}
}

View File

@ -0,0 +1,353 @@
/*
KLayout Layout Viewer
Copyright (C) 2006-2024 Matthias Koefferlein
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef _HDR_dbLayoutToNetlistSoftConnections
#define _HDR_dbLayoutToNetlistSoftConnections
#include "dbCommon.h"
#include "dbPolygon.h"
#include <set>
#include <map>
#include <list>
#include <cstddef>
#include <string>
namespace db
{
class Net;
class Pin;
class Circuit;
class Netlist;
class SubCircuit;
class LayoutToNetlist;
template <class T> class hier_clusters;
template <class T> class connected_clusters;
class NetShape;
/**
* @brief A small struct representing a direction value for a pin
*
* The pin can be upward, downward connected, connected in both ways or not connected at all.
*/
class SoftConnectionPinDir
{
public:
/**
* @brief Constructs from a single direction (+1: up, -1: down)
*/
explicit SoftConnectionPinDir (int dir = 0)
: m_flags (0)
{
if (dir > 0) {
m_flags = 1;
} else if (dir < 0) {
m_flags = 2;
}
}
/**
* @brief Equality
*/
bool operator== (SoftConnectionPinDir other) const
{
return m_flags == other.m_flags;
}
/**
* @brief Inequality
*/
bool operator!= (SoftConnectionPinDir other) const
{
return m_flags != other.m_flags;
}
/**
* @brief Join two value
*/
SoftConnectionPinDir operator| (SoftConnectionPinDir other) const
{
SoftConnectionPinDir res = *this;
res.m_flags |= other.m_flags;
return res;
}
SoftConnectionPinDir &operator|= (SoftConnectionPinDir other)
{
m_flags |= other.m_flags;
return *this;
}
/**
* @brief Test for one direction
*/
bool operator& (SoftConnectionPinDir other) const
{
return (m_flags & other.m_flags) != 0;
}
/**
* @brief Static getters for the constants
*/
inline static SoftConnectionPinDir none () { return SoftConnectionPinDir (0); }
inline static SoftConnectionPinDir up () { return SoftConnectionPinDir (1); }
inline static SoftConnectionPinDir down () { return SoftConnectionPinDir (-1); }
inline static SoftConnectionPinDir both () { return up () | down (); }
private:
unsigned int m_flags;
};
/**
* @brief Describes a soft-connected net graph
*
* Such a graph is a collection of nets/shape clusters that are connected via
* soft connections.
* There is also some information about the count of "down-only" nets. With
* this, this object can serve as a representative model for a circuit's
* content as embedded into a larger graph through subcircuits.
*
* A circuit in general can be made from a number of such net graphs.
*/
class DB_PUBLIC SoftConnectionNetGraph
{
public:
typedef std::set<size_t> pin_set;
typedef pin_set::const_iterator pin_iterator;
typedef std::map<size_t, SoftConnectionPinDir> dir_map;
typedef dir_map::const_iterator dir_map_iterator;
SoftConnectionNetGraph ();
/**
* @brief Enters information about a specific net
*
* @param net The Net for which we are entering information
* @param dir The direction code of the net
* @param pin A pin that might leading outside our current circuit from this net (0 if there is none)
* @param partial_net_count The partial net count of nets attached to this net inside subcircuits
*/
void add (const db::Net *net, SoftConnectionPinDir dir, const db::Pin *pin, size_t partial_net_count);
/**
* @brief Gets the partial net count
*
* The partial net count is the number of nets definitely isolated.
* This is the count of "down-only" connected nets on the cluster.
* This may also involve nets from subcircuits.
* Only non-trivial (floating) nets are counted.
*
* A partial net count of more than one indicates a soft connection
* between nets.
*/
size_t partial_net_count () const
{
return m_partial_net_count;
}
/**
* @brief Gets the outside pins on the net graph (begin iterator)
*
* The iterator delivers Pin IDs of pins leading outside the circuit this graph lives in.
*/
pin_iterator begin_pins () const
{
return m_pin_ids.begin ();
}
/**
* @brief Gets the pins on the net graph (end iterator)
*/
pin_iterator end_pins () const
{
return m_pin_ids.end ();
}
/**
* @brief Gets the shape clusters + dir information (begin iterator)
*/
dir_map_iterator begin_clusters () const
{
return m_cluster_dir.begin ();
}
/**
* @brief Gets the shape clusters + dir information (end iterator)
*/
dir_map_iterator end_clusters () const
{
return m_cluster_dir.end ();
}
private:
pin_set m_pin_ids;
size_t m_partial_net_count;
dir_map m_cluster_dir;
};
/**
* @brief Provides temporary soft connection information for a circuit
*
* Soft connection information is the soft-connected net graphs that are formed inside
* the circuit and how these graphs connect to pins from the circuit leading outside.
*/
class DB_PUBLIC SoftConnectionCircuitInfo
{
public:
typedef std::list<SoftConnectionNetGraph> net_graph_list;
typedef net_graph_list::const_iterator net_graph_list_iterator;
/**
* @brief Constructor
*/
SoftConnectionCircuitInfo (const db::Circuit *circuit);
/**
* @brief Gets the circuit for this info object
*/
const db::Circuit *circuit () const
{
return mp_circuit;
}
/**
* @brief Creates a new graph info object
*/
SoftConnectionNetGraph &make_net_graph ();
/**
* @brief Adds information about a pin
*
* @param pin The pin
* @param dir The direction of connections from the pin
* @param graph_info The soft-connected net graph info object
*/
void add_pin_info (const db::Pin *pin, SoftConnectionPinDir dir, SoftConnectionNetGraph *graph_info);
/**
* @brief Gets the direction attribute of the pin
*/
SoftConnectionPinDir direction_per_pin (const db::Pin *pin) const;
/**
* @brief Gets the soft-connected net graph object the pin connects to
*/
const SoftConnectionNetGraph *get_net_graph_per_pin (const db::Pin *pin) const;
/**
* @brief List of per-circuit net graph objects, begin iterator
*/
net_graph_list_iterator begin () const
{
return m_net_graphs.begin ();
}
/**
* @brief List of per-circuit net graph objects, end iterator
*/
net_graph_list_iterator end () const
{
return m_net_graphs.end ();
}
private:
const db::Circuit *mp_circuit;
net_graph_list m_net_graphs;
std::map<size_t, std::pair<SoftConnectionPinDir, const SoftConnectionNetGraph *> > m_pin_info;
};
/**
* @brief Provides temporary soft connection information for a netlist
*/
class DB_PUBLIC SoftConnectionInfo
{
public:
SoftConnectionInfo ();
/**
* @brief Builds the soft connection information for the given netlist and shape clusters
*/
void build (const db::Netlist &netlist, const db::hier_clusters<db::NetShape> &shape_clusters);
/**
* @brief Joins nets connected by soft connections
*
* This method will clear the information from this object
* as the clusters will no longer be valid.
*/
void join_soft_connections (db::Netlist &netlist);
/**
* @brief Create log entries
*/
void report (db::LayoutToNetlist &l2n);
private:
std::map<const db::Circuit *, SoftConnectionCircuitInfo> m_scc_per_circuit;
/**
* @brief Builds the per-circuit net graphs
*
* First of all, this method creates a SoftConnectionCircuitInfo object for the circuit.
*
* Inside this per-circuit object, it will create a number of SoftConnectionNetGraph objects - each one
* for a cluster of soft-connected nets.
*
* Call this method bottom-up as it needs SoftConnectionCircuitInfo objects for called circuits.
*/
void build_graphs_for_circuit (const db::Circuit *circuit, const db::connected_clusters<db::NetShape> &shape_clusters);
/**
* @brief Gets a value indicating whether the given net connects to subcircuits with up or down connections inside
*/
bool net_has_up_or_down_subcircuit_connections (const db::Net *net, bool up);
/**
* @brief Gets connections to other nets / shape clusters through the given subcircuit from the given pin
*
* As a side effect, this method will also collect the partial net count - that is the number
* of defintively disconnected (down-only) nets.
* More that one such a net will render an error.
*/
void get_net_connections_through_subcircuit (const db::SubCircuit *subcircuit, const db::Pin *pin, std::set<size_t> &ids, size_t &partial_net_count);
/**
* @brief Gets connections to other nets / shape clusters through the subcircuits on the net
*
* As a side effect, this method will also collect the partial net count - that is the number
* of defintively disconnected (down-only) nets.
* More that one such a net will render an error.
*
* The return value is a set of nets shape cluster IDs.
*/
std::set<size_t> net_connections_through_subcircuits (const db::Net *net, size_t &partial_net_count);
void report_partial_nets (const db::Circuit *circuit, const SoftConnectionNetGraph &cluster_info, LayoutToNetlist &l2n, const std::string &path, const db::DCplxTrans &trans, const std::string &top_cell, int &index, std::set<std::pair<const db::Net *, db::DCplxTrans> > &seen);
db::DPolygon representative_polygon (const db::Net *net, const db::LayoutToNetlist &l2n, const db::CplxTrans &trans);
};
}
#endif

View File

@ -351,7 +351,7 @@ void std_writer_impl<Keys>::write (bool nested, TokenizedOutput &stream, std::ma
if (! Keys::is_short ()) {
stream << endl << "# Mask layers" << endl;
}
for (db::Connectivity::layer_iterator l = mp_l2n->connectivity ().begin_layers (); l != mp_l2n->connectivity ().end_layers (); ++l) {
for (db::Connectivity::all_layer_iterator l = mp_l2n->connectivity ().begin_layers (); l != mp_l2n->connectivity ().end_layers (); ++l) {
TokenizedOutput out (stream, Keys::layer_key);
out << name_for_layer (mp_l2n, *l);
db::LayerProperties lp = ly->get_properties (*l);
@ -364,15 +364,32 @@ void std_writer_impl<Keys>::write (bool nested, TokenizedOutput &stream, std::ma
if (! Keys::is_short ()) {
stream << endl << "# Mask layer connectivity" << endl;
}
for (db::Connectivity::layer_iterator l = mp_l2n->connectivity ().begin_layers (); l != mp_l2n->connectivity ().end_layers (); ++l) {
for (db::Connectivity::all_layer_iterator l = mp_l2n->connectivity ().begin_layers (); l != mp_l2n->connectivity ().end_layers (); ++l) {
db::Connectivity::layer_iterator ce = mp_l2n->connectivity ().end_connected (*l);
db::Connectivity::layer_iterator cb = mp_l2n->connectivity ().begin_connected (*l);
if (cb != ce) {
TokenizedOutput out (stream, Keys::connect_key);
out << name_for_layer (mp_l2n, *l);
for (db::Connectivity::layer_iterator c = mp_l2n->connectivity ().begin_connected (*l); c != ce; ++c) {
out << name_for_layer (mp_l2n, *c);
bool any_soft = false;
{
TokenizedOutput out (stream, Keys::connect_key);
out << name_for_layer (mp_l2n, *l);
for (db::Connectivity::layer_iterator c = mp_l2n->connectivity ().begin_connected (*l); c != ce; ++c) {
if (c->second < 0) {
any_soft = true;
}
out << name_for_layer (mp_l2n, c->first);
}
}
// add soft connections in addition and as overrides to stay backward compatible with older versions
// (these will ignore these statements)
if (any_soft) {
TokenizedOutput out (stream, Keys::softconnect_key);
out << name_for_layer (mp_l2n, *l);
for (db::Connectivity::layer_iterator c = mp_l2n->connectivity ().begin_connected (*l); c != ce; ++c) {
if (c->second < 0) {
out << name_for_layer (mp_l2n, c->first);
}
}
}
m_progress.set (mp_stream->pos ());
}
@ -380,7 +397,7 @@ void std_writer_impl<Keys>::write (bool nested, TokenizedOutput &stream, std::ma
}
any = false;
for (db::Connectivity::layer_iterator l = mp_l2n->connectivity ().begin_layers (); l != mp_l2n->connectivity ().end_layers (); ++l) {
for (db::Connectivity::all_layer_iterator l = mp_l2n->connectivity ().begin_layers (); l != mp_l2n->connectivity ().end_layers (); ++l) {
db::Connectivity::global_nets_iterator ge = mp_l2n->connectivity ().end_global_connections (*l);
db::Connectivity::global_nets_iterator gb = mp_l2n->connectivity ().begin_global_connections (*l);
@ -391,10 +408,27 @@ void std_writer_impl<Keys>::write (bool nested, TokenizedOutput &stream, std::ma
}
any = true;
}
TokenizedOutput out (stream, Keys::global_key);
out << name_for_layer (mp_l2n, *l);
for (db::Connectivity::global_nets_iterator g = gb; g != ge; ++g) {
out << tl::to_word_or_quoted_string (mp_l2n->connectivity ().global_net_name (*g));
bool any_soft = false;
{
TokenizedOutput out (stream, Keys::global_key);
out << name_for_layer (mp_l2n, *l);
for (db::Connectivity::global_nets_iterator g = gb; g != ge; ++g) {
if (g->second < 0) {
any_soft = true;
}
out << tl::to_word_or_quoted_string (mp_l2n->connectivity ().global_net_name (g->first));
}
}
// add soft connections in addition and as overrides to stay backward compatible with older versions
// (these will ignore these statements)
if (any_soft) {
TokenizedOutput out (stream, Keys::softglobal_key);
out << name_for_layer (mp_l2n, *l);
for (db::Connectivity::global_nets_iterator g = gb; g != ge; ++g) {
if (g->second < 0) {
out << tl::to_word_or_quoted_string (mp_l2n->connectivity ().global_net_name (g->first));
}
}
}
m_progress.set (mp_stream->pos ());
}
@ -634,7 +668,7 @@ void std_writer_impl<Keys>::write (TokenizedOutput &stream, const db::Net &net,
reset_geometry_ref ();
for (db::Connectivity::layer_iterator l = conn.begin_layers (); l != conn.end_layers (); ++l) {
for (db::Connectivity::all_layer_iterator l = conn.begin_layers (); l != conn.end_layers (); ++l) {
db::cell_index_type cci = circuit->cell_index ();
db::cell_index_type prev_ci = cci;
@ -757,7 +791,7 @@ void std_writer_impl<Keys>::write (TokenizedOutput &stream, const db::DeviceAbst
bool any = false;
for (db::Connectivity::layer_iterator l = conn.begin_layers (); l != conn.end_layers (); ++l) {
for (db::Connectivity::all_layer_iterator l = conn.begin_layers (); l != conn.end_layers (); ++l) {
size_t cid = device_abstract.cluster_id_for_terminal (t->id ());
if (cid == 0) {

View File

@ -161,22 +161,33 @@ std::string
LogEntryData::to_string (bool with_geometry) const
{
std::string res;
const std::string &msg = message ();
if (m_category_name != 0) {
if (m_category_description == 0) {
res += "[" + category_name () + "] ";
} else {
res += "[" + category_description () + "] ";
// a message that starts with a tab is a continuation or detail entry
if (! msg.empty () && msg [0] == '\t') {
res = " ";
res += std::string (msg, 1);
} else {
if (m_category_name != 0) {
if (m_category_description == 0) {
res += "[" + category_name () + "] ";
} else {
res += "[" + category_description () + "] ";
}
}
}
if (m_cell_name != 0) {
res += tl::to_string (tr ("In cell "));
res += cell_name ();
res += ": ";
}
if (m_cell_name != 0) {
res += tl::to_string (tr ("In cell "));
res += cell_name ();
res += ": ";
}
res += message ();
res += msg;
}
if (with_geometry && ! m_geometry.box ().empty ()) {
res += tl::to_string (tr (", shape: ")) + m_geometry.to_string ();

View File

@ -288,7 +288,9 @@ NetlistExtractor::extract_nets (const db::DeepShapeStore &dss, unsigned int layo
const db::local_cluster<db::NetShape> &lc = clusters.cluster_by_id (*c);
const connected_clusters_type::connections_type &cc = clusters.connections_for_cluster (*c);
if (cc.empty () && lc.empty ()) {
const std::set<size_t> &sc_up = clusters.upward_soft_connections (*c);
const std::set<size_t> &sc_down = clusters.downward_soft_connections (*c);
if (cc.empty () && sc_up.empty () && sc_down.empty () && lc.empty ()) {
// this is an entirely empty cluster so we skip it.
// Such clusters are left over when joining clusters.
continue;

View File

@ -26,22 +26,106 @@
namespace gsi
{
static std::string
l2s (db::Connectivity::layer_iterator b, db::Connectivity::layer_iterator e)
{
std::string s;
for (db::Connectivity::layer_iterator i = b; i != e; ++i) {
if (! s.empty ()) {
s += ",";
}
s += tl::to_string (i->first);
if (i->second < 0) {
s += "-S";
} else if (i->second > 0) {
s += "+S";
}
}
return s;
}
static std::string
gn2s (db::Connectivity::global_nets_iterator b, db::Connectivity::global_nets_iterator e)
{
std::string s;
for (db::Connectivity::global_nets_iterator i = b; i != e; ++i) {
if (! s.empty ()) {
s += ",";
}
s += tl::to_string (i->first);
if (i->second < 0) {
s += "-S";
} else if (i->second > 0) {
s += "+S";
}
}
return s;
}
static std::string
connectivity_to_string (const db::Connectivity *conn)
{
std::string res;
for (auto l = conn->begin_layers (); l != conn->end_layers (); ++l) {
if (conn->begin_connected (*l) != conn->end_connected (*l)) {
if (! res.empty ()) {
res += "\n";
}
res += tl::to_string (*l) + ":" + l2s (conn->begin_connected (*l), conn->end_connected (*l));
}
if (conn->begin_global_connections (*l) != conn->end_global_connections (*l)) {
if (! res.empty ()) {
res += "\n";
}
res += "G" + tl::to_string (*l) + ":" + gn2s (conn->begin_global_connections (*l), conn->end_global_connections (*l));
}
}
return res;
}
Class<db::Connectivity> decl_dbConnectivity ("db", "Connectivity",
gsi::method ("connect", (void (db::Connectivity::*) (unsigned int)) &db::Connectivity::connect, gsi::arg ("layer"),
"@brief Specifies intra-layer connectivity.\n"
"This method specifies a hard connection between shapes on the given layer. "
"Without specifying such a connection, shapes on that layer do not form connection regions."
) +
gsi::method ("connect", (void (db::Connectivity::*) (unsigned int, unsigned int)) &db::Connectivity::connect, gsi::arg ("layer_a"), gsi::arg ("layer_b"),
"@brief Specifies inter-layer connectivity.\n"
"This method specifies a hard connection between shapes on layer_a and layer_b."
) +
gsi::method ("soft_connect", (void (db::Connectivity::*) (unsigned int, unsigned int)) &db::Connectivity::soft_connect, gsi::arg ("layer_a"), gsi::arg ("layer_b"),
"@brief Specifies a soft connection between layer_a and layer_b.\n"
"@param layer_a The 'upper' layer\n"
"@param layer_b The 'lower' layer\n"
"Soft connections are made between a lower and an upper layer. The lower layer conceptually is a high-ohmic "
"(i.e. substrate, diffusion) region that is not intended for signal wiring. The netlist extraction will check "
"that no routing happens over such regions.\n"
"\n"
"Soft connections have in introduced in version 0.29."
) +
gsi::method ("connect_global", (size_t (db::Connectivity::*) (unsigned int, const std::string &)) &db::Connectivity::connect_global, gsi::arg ("layer"), gsi::arg ("global_net_name"),
"@brief Connects the given layer to the global net given by name.\n"
"Returns the ID of the global net."
) +
gsi::method ("soft_connect_global", (size_t (db::Connectivity::*) (unsigned int, const std::string &)) &db::Connectivity::soft_connect_global, gsi::arg ("layer"), gsi::arg ("global_net_name"),
"@brief Soft-connects the given layer to the global net given by name.\n"
"Returns the ID of the global net.\n"
"See \\soft_connect for a description of the soft connection feature. The global net is always the "
"'lower' (i.e. high-ohmic, substrate) part of the soft connection.\n"
"\n"
"Soft connections have in introduced in version 0.29."
) +
gsi::method ("global_net_name", &db::Connectivity::global_net_name, gsi::arg ("global_net_id"),
"@brief Gets the name for a given global net ID.\n"
) +
gsi::method ("global_net_id", &db::Connectivity::global_net_id, gsi::arg ("global_net_name"),
"@brief Gets the ID for a given global net name.\n"
) +
// provided for testing purposes mainly.
gsi::method_ext ("to_s", &connectivity_to_string,
"@hide\n"
),
"@brief This class specifies connections between different layers.\n"
"Connections are build using \\connect. There are basically two flavours of connections: intra-layer and inter-layer.\n"
@ -60,6 +144,10 @@ Class<db::Connectivity> decl_dbConnectivity ("db", "Connectivity",
"Global nets are defined by name and are managed through IDs. To get the name for a given ID, use "
"\\global_net_name."
"\n"
"Starting with version 0.29, soft connections are supported. Soft connections attach to high-ohmic substrate or diffusion "
"layers (the 'lower' layer) are upon netlist extraction it will be checked that no wiring is routed over such connections. "
"See \\soft_connect and \\soft_global_connect for details.\n"
"\n"
"This class has been introduced in version 0.26.\n"
);

View File

@ -433,6 +433,32 @@ Class<db::LayoutToNetlist> decl_dbLayoutToNetlist ("db", "LayoutToNetlist",
"\n"
"This variant has been introduced in version 0.27.\n"
) +
gsi::method ("soft_connect", (void (db::LayoutToNetlist::*) (const db::Region &, const db::Region &)) &db::LayoutToNetlist::soft_connect, gsi::arg ("a"), gsi::arg ("b"),
"@brief Defines an inter-layer connection for the given layers in soft mode.\n"
"Connects two layers through a soft connection.\n"
"Soft connections cannot make connections between two different nets.\n"
"These are directional connections where 'b' is the 'lower' layer (typically high-ohmic substrate or diffusion).\n"
"\n"
"Soft connections have been introduced in version 0.29.\n"
) +
gsi::method ("soft_connect", (void (db::LayoutToNetlist::*) (const db::Region &, const db::Texts &)) &db::LayoutToNetlist::soft_connect, gsi::arg ("a"), gsi::arg ("b"),
"@brief Defines an inter-layer connection for the given layers in soft mode.\n"
"Connects two layers through a soft connection.\n"
"Soft connections cannot make connections between two different nets.\n"
"These are directional connections where 'b' is the 'lower' layer (typically high-ohmic substrate or diffusion).\n"
"As one argument is a (hierarchical) text collection, this method is used to attach net labels to polygons.\n"
"\n"
"Soft connections have been introduced in version 0.29.\n"
) +
gsi::method ("soft_connect", (void (db::LayoutToNetlist::*) (const db::Texts &, const db::Region &)) &db::LayoutToNetlist::soft_connect, gsi::arg ("a"), gsi::arg ("b"),
"@brief Defines an inter-layer connection for the given layers in soft mode.\n"
"Connects two layers through a soft connection.\n"
"Soft connections cannot make connections between two different nets.\n"
"These are directional connections where 'b' is the 'lower' layer (typically high-ohmic substrate or diffusion).\n"
"As one argument is a (hierarchical) text collection, this method is used to attach net labels to polygons.\n"
"\n"
"Soft connections have been introduced in version 0.29.\n"
) +
gsi::method ("connect_global", (size_t (db::LayoutToNetlist::*) (const db::Region &, const std::string &)) &db::LayoutToNetlist::connect_global, gsi::arg ("l"), gsi::arg ("global_net_name"),
"@brief Defines a connection of the given layer with a global net.\n"
"This method returns the ID of the global net. Use \\global_net_name to get "
@ -441,10 +467,26 @@ Class<db::LayoutToNetlist> decl_dbLayoutToNetlist ("db", "LayoutToNetlist",
gsi::method ("connect_global", (size_t (db::LayoutToNetlist::*) (const db::Texts &, const std::string &)) &db::LayoutToNetlist::connect_global, gsi::arg ("l"), gsi::arg ("global_net_name"),
"@brief Defines a connection of the given text layer with a global net.\n"
"This method returns the ID of the global net. Use \\global_net_name to get "
"the name back from the ID."
"the name back from the ID.\n"
"\n"
"This variant has been introduced in version 0.27.\n"
) +
gsi::method ("soft_connect_global", (size_t (db::LayoutToNetlist::*) (const db::Region &, const std::string &)) &db::LayoutToNetlist::soft_connect_global, gsi::arg ("l"), gsi::arg ("global_net_name"),
"@brief Defines a connection of the given layer with a global net in soft mode.\n"
"This method returns the ID of the global net. Use \\global_net_name to get "
"the name back from the ID.\n"
"Soft connections are directional, where the global net is the 'lower' layer (typically high-ohmic substrate or diffusion).\n"
"\n"
"Soft connections have been introduced in version 0.29.\n"
) +
gsi::method ("soft_connect_global", (size_t (db::LayoutToNetlist::*) (const db::Texts &, const std::string &)) &db::LayoutToNetlist::soft_connect_global, gsi::arg ("l"), gsi::arg ("global_net_name"),
"@brief Defines a connection of the given text layer with a global net in soft mode.\n"
"This method returns the ID of the global net. Use \\global_net_name to get "
"the name back from the ID.\n"
"Soft connections are directional, where the global net is the 'lower' layer (typically high-ohmic substrate or diffusion).\n"
"\n"
"Soft connections have been introduced in version 0.29.\n"
) +
gsi::method ("global_net_name", &db::LayoutToNetlist::global_net_name, gsi::arg ("global_net_id"),
"@brief Gets the global net name for the given global net ID."
) +
@ -464,6 +506,12 @@ Class<db::LayoutToNetlist> decl_dbLayoutToNetlist ("db", "LayoutToNetlist",
"\n"
"This attribute has been introduced in version 0.27.\n"
) +
gsi::method ("make_soft_connection_diodes=", &db::LayoutToNetlist::set_make_soft_connection_diodes, gsi::arg ("flag"),
"@hide"
) +
gsi::method ("make_soft_connection_diodes", &db::LayoutToNetlist::make_soft_connection_diodes,
"@hide"
) +
gsi::method ("top_level_mode=", &db::LayoutToNetlist::set_top_level_mode, gsi::arg ("flag"),
"@brief Sets a flag indicating whether top level mode is enabled.\n"
"\n"

View File

@ -36,6 +36,23 @@ static std::string l2s (db::Connectivity::layer_iterator b, db::Connectivity::la
{
std::string s;
for (db::Connectivity::layer_iterator i = b; i != e; ++i) {
if (! s.empty ()) {
s += ",";
}
s += tl::to_string (i->first);
if (i->second < 0) {
s += "-S";
} else if (i->second > 0) {
s += "+S";
}
}
return s;
}
static std::string al2s (db::Connectivity::all_layer_iterator b, db::Connectivity::all_layer_iterator e)
{
std::string s;
for (db::Connectivity::all_layer_iterator i = b; i != e; ++i) {
if (! s.empty ()) {
s += ",";
}
@ -51,7 +68,12 @@ static std::string gn2s (db::Connectivity::global_nets_iterator b, db::Connectiv
if (! s.empty ()) {
s += ",";
}
s += tl::to_string (*i);
s += tl::to_string (i->first);
if (i->second < 0) {
s += "-S";
} else if (i->second > 0) {
s += "+S";
}
}
return s;
}
@ -60,15 +82,15 @@ TEST(1_Connectivity)
{
db::Connectivity conn;
EXPECT_EQ (l2s (conn.begin_layers (), conn.end_layers ()), "");
EXPECT_EQ (al2s (conn.begin_layers (), conn.end_layers ()), "");
conn.connect (0);
EXPECT_EQ (l2s (conn.begin_layers (), conn.end_layers ()), "0");
EXPECT_EQ (al2s (conn.begin_layers (), conn.end_layers ()), "0");
EXPECT_EQ (l2s (conn.begin_connected (0), conn.end_connected (0)), "0");
EXPECT_EQ (l2s (conn.begin_connected (1), conn.end_connected (1)), "");
conn.connect (0, 1);
EXPECT_EQ (l2s (conn.begin_layers (), conn.end_layers ()), "0,1");
EXPECT_EQ (al2s (conn.begin_layers (), conn.end_layers ()), "0,1");
EXPECT_EQ (l2s (conn.begin_connected (0), conn.end_connected (0)), "0,1");
EXPECT_EQ (l2s (conn.begin_connected (1), conn.end_connected (1)), "0");
@ -104,6 +126,60 @@ TEST(1_Connectivity)
EXPECT_EQ (conn2.global_net_name (1), "GLOBAL2");
}
TEST(1_ConnectivitySoft)
{
db::Connectivity conn;
EXPECT_EQ (al2s (conn.begin_layers (), conn.end_layers ()), "");
conn.connect (0);
EXPECT_EQ (al2s (conn.begin_layers (), conn.end_layers ()), "0");
EXPECT_EQ (l2s (conn.begin_connected (0), conn.end_connected (0)), "0");
EXPECT_EQ (l2s (conn.begin_connected (1), conn.end_connected (1)), "");
conn.soft_connect (0, 1);
EXPECT_EQ (al2s (conn.begin_layers (), conn.end_layers ()), "0,1");
EXPECT_EQ (l2s (conn.begin_connected (0), conn.end_connected (0)), "0,1-S");
EXPECT_EQ (l2s (conn.begin_connected (1), conn.end_connected (1)), "0+S");
conn.connect (1);
EXPECT_EQ (l2s (conn.begin_connected (1), conn.end_connected (1)), "0+S,1");
conn.soft_connect (2, 0);
conn.connect (2);
EXPECT_EQ (l2s (conn.begin_connected (0), conn.end_connected (0)), "0,1-S,2+S");
EXPECT_EQ (l2s (conn.begin_connected (1), conn.end_connected (1)), "0+S,1");
EXPECT_EQ (l2s (conn.begin_connected (2), conn.end_connected (2)), "0-S,2");
conn.connect (2, 0);
EXPECT_EQ (l2s (conn.begin_connected (0), conn.end_connected (0)), "0,1-S,2");
EXPECT_EQ (l2s (conn.begin_connected (1), conn.end_connected (1)), "0+S,1");
EXPECT_EQ (l2s (conn.begin_connected (2), conn.end_connected (2)), "0,2");
EXPECT_EQ (conn.soft_connect_global (0, "GLOBAL"), size_t (0));
EXPECT_EQ (gn2s (conn.begin_global_connections (2), conn.end_global_connections (2)), "");
EXPECT_EQ (gn2s (conn.begin_global_connections (0), conn.end_global_connections (0)), "0-S");
EXPECT_EQ (conn.soft_connect_global (2, "GLOBAL2"), size_t (1));
EXPECT_EQ (gn2s (conn.begin_global_connections (2), conn.end_global_connections (2)), "1-S");
EXPECT_EQ (conn.connect_global (0, "GLOBAL2"), size_t (1));
EXPECT_EQ (gn2s (conn.begin_global_connections (0), conn.end_global_connections (0)), "0-S,1");
EXPECT_EQ (conn.global_net_name (0), "GLOBAL");
EXPECT_EQ (conn.global_net_name (1), "GLOBAL2");
db::Connectivity conn2 = conn;
EXPECT_EQ (l2s (conn2.begin_connected (0), conn2.end_connected (0)), "0,1-S,2");
EXPECT_EQ (l2s (conn2.begin_connected (1), conn2.end_connected (1)), "0+S,1");
EXPECT_EQ (l2s (conn2.begin_connected (2), conn2.end_connected (2)), "0,2");
EXPECT_EQ (gn2s (conn2.begin_global_connections (0), conn2.end_global_connections (0)), "0-S,1");
EXPECT_EQ (conn2.global_net_name (0), "GLOBAL");
EXPECT_EQ (conn2.global_net_name (1), "GLOBAL2");
}
TEST(2_ShapeInteractions)
{
db::Connectivity conn;
@ -121,18 +197,19 @@ TEST(2_ShapeInteractions)
db::ICplxTrans t3 (db::Trans (db::Vector (0, 2000)));
db::PolygonRef ref3 (poly.transformed (t3), repo);
EXPECT_EQ (conn.interacts (ref1, 0, ref2, 0), true);
EXPECT_EQ (conn.interacts (ref1, 0, ref1, 0, t2), true); // t2*ref1 == ref2
EXPECT_EQ (conn.interacts (ref1, 0, ref2, 1), true);
EXPECT_EQ (conn.interacts (ref1, 0, ref1, 1, t2), true);
EXPECT_EQ (conn.interacts (ref1, 1, ref2, 0), true);
EXPECT_EQ (conn.interacts (ref1, 1, ref1, 0, t2), true);
EXPECT_EQ (conn.interacts (ref1, 0, ref3, 0), false);
EXPECT_EQ (conn.interacts (ref1, 0, ref1, 0, t3), false); // t3*ref1 == ref3
EXPECT_EQ (conn.interacts (ref1, 0, ref3, 1), false);
EXPECT_EQ (conn.interacts (ref1, 0, ref1, 1, t3), false);
EXPECT_EQ (conn.interacts (ref1, 1, ref2, 2), false);
EXPECT_EQ (conn.interacts (ref1, 1, ref1, 2, t2), false);
int soft = std::numeric_limits<int>::max ();
EXPECT_EQ (conn.interacts (ref1, 0, ref2, 0, soft), true);
EXPECT_EQ (conn.interacts (ref1, 0, ref1, 0, t2, soft), true); // t2*ref1 == ref2
EXPECT_EQ (conn.interacts (ref1, 0, ref2, 1, soft), true);
EXPECT_EQ (conn.interacts (ref1, 0, ref1, 1, t2, soft), true);
EXPECT_EQ (conn.interacts (ref1, 1, ref2, 0, soft), true);
EXPECT_EQ (conn.interacts (ref1, 1, ref1, 0, t2, soft), true);
EXPECT_EQ (conn.interacts (ref1, 0, ref3, 0, soft), false);
EXPECT_EQ (conn.interacts (ref1, 0, ref1, 0, t3, soft), false); // t3*ref1 == ref3
EXPECT_EQ (conn.interacts (ref1, 0, ref3, 1, soft), false);
EXPECT_EQ (conn.interacts (ref1, 0, ref1, 1, t3, soft), false);
EXPECT_EQ (conn.interacts (ref1, 1, ref2, 2, soft), false);
EXPECT_EQ (conn.interacts (ref1, 1, ref1, 2, t2, soft), false);
}
TEST(2_ShapeInteractionsRealPolygon)
@ -154,20 +231,21 @@ TEST(2_ShapeInteractionsRealPolygon)
db::ICplxTrans t4 (db::Trans (db::Vector (0, 1500)));
db::PolygonRef ref4 (poly.transformed (t4), repo);
EXPECT_EQ (conn.interacts (ref1, 0, ref2, 0), true);
EXPECT_EQ (conn.interacts (ref1, 0, ref1, 0, t2), true); // t2*ref1 == ref2
EXPECT_EQ (conn.interacts (ref1, 0, ref2, 1), true);
EXPECT_EQ (conn.interacts (ref1, 0, ref1, 1, t2), true);
EXPECT_EQ (conn.interacts (ref1, 1, ref2, 0), true);
EXPECT_EQ (conn.interacts (ref1, 1, ref1, 0, t2), true);
EXPECT_EQ (conn.interacts (ref1, 0, ref3, 0), false);
EXPECT_EQ (conn.interacts (ref1, 0, ref1, 0, t3), false);
EXPECT_EQ (conn.interacts (ref1, 0, ref4, 0), true);
EXPECT_EQ (conn.interacts (ref1, 0, ref1, 0, t4), true);
EXPECT_EQ (conn.interacts (ref1, 0, ref3, 1), false);
EXPECT_EQ (conn.interacts (ref1, 0, ref1, 1, t3), false);
EXPECT_EQ (conn.interacts (ref1, 1, ref2, 2), false);
EXPECT_EQ (conn.interacts (ref1, 1, ref1, 2, t2), false);
int soft = std::numeric_limits<int>::max ();
EXPECT_EQ (conn.interacts (ref1, 0, ref2, 0, soft), true);
EXPECT_EQ (conn.interacts (ref1, 0, ref1, 0, t2, soft), true); // t2*ref1 == ref2
EXPECT_EQ (conn.interacts (ref1, 0, ref2, 1, soft), true);
EXPECT_EQ (conn.interacts (ref1, 0, ref1, 1, t2, soft), true);
EXPECT_EQ (conn.interacts (ref1, 1, ref2, 0, soft), true);
EXPECT_EQ (conn.interacts (ref1, 1, ref1, 0, t2, soft), true);
EXPECT_EQ (conn.interacts (ref1, 0, ref3, 0, soft), false);
EXPECT_EQ (conn.interacts (ref1, 0, ref1, 0, t3, soft), false);
EXPECT_EQ (conn.interacts (ref1, 0, ref4, 0, soft), true);
EXPECT_EQ (conn.interacts (ref1, 0, ref1, 0, t4, soft), true);
EXPECT_EQ (conn.interacts (ref1, 0, ref3, 1, soft), false);
EXPECT_EQ (conn.interacts (ref1, 0, ref1, 1, t3, soft), false);
EXPECT_EQ (conn.interacts (ref1, 1, ref2, 2, soft), false);
EXPECT_EQ (conn.interacts (ref1, 1, ref1, 2, t2, soft), false);
}
TEST(10_LocalClusterBasic)
@ -215,21 +293,22 @@ TEST(11_LocalClusterInteractBasic)
db::local_cluster<db::PolygonRef> cluster;
db::local_cluster<db::PolygonRef> cluster2;
int soft;
EXPECT_EQ (cluster.interacts (cluster2, db::ICplxTrans (), conn), false);
EXPECT_EQ (cluster.interacts (cluster2, db::ICplxTrans (), conn, soft), false);
cluster.add (db::PolygonRef (poly, repo), 0);
EXPECT_EQ (cluster.interacts (cluster2, db::ICplxTrans (), conn), false);
EXPECT_EQ (cluster.interacts (cluster2, db::ICplxTrans (), conn, soft), false);
cluster2.add (db::PolygonRef (poly, repo), 0);
EXPECT_EQ (cluster.interacts (cluster2, db::ICplxTrans (), conn), true);
EXPECT_EQ (cluster.interacts (cluster2, db::ICplxTrans (db::Trans (db::Vector (10, 20))), conn), true);
EXPECT_EQ (cluster.interacts (cluster2, db::ICplxTrans (db::Trans (db::Vector (0, 1000))), conn), true);
EXPECT_EQ (cluster.interacts (cluster2, db::ICplxTrans (db::Trans (db::Vector (0, 1001))), conn), false);
EXPECT_EQ (cluster.interacts (cluster2, db::ICplxTrans (db::Trans (db::Vector (0, 2000))), conn), false);
EXPECT_EQ (cluster.interacts (cluster2, db::ICplxTrans (), conn, soft), true);
EXPECT_EQ (cluster.interacts (cluster2, db::ICplxTrans (db::Trans (db::Vector (10, 20))), conn, soft), true);
EXPECT_EQ (cluster.interacts (cluster2, db::ICplxTrans (db::Trans (db::Vector (0, 1000))), conn, soft), true);
EXPECT_EQ (cluster.interacts (cluster2, db::ICplxTrans (db::Trans (db::Vector (0, 1001))), conn, soft), false);
EXPECT_EQ (cluster.interacts (cluster2, db::ICplxTrans (db::Trans (db::Vector (0, 2000))), conn, soft), false);
cluster.clear ();
EXPECT_EQ (cluster.interacts (cluster2, db::ICplxTrans (), conn), false);
EXPECT_EQ (cluster.interacts (cluster2, db::ICplxTrans (), conn, soft), false);
}
TEST(11_LocalClusterInteractDifferentLayers)
@ -248,28 +327,29 @@ TEST(11_LocalClusterInteractDifferentLayers)
db::local_cluster<db::PolygonRef> cluster;
db::local_cluster<db::PolygonRef> cluster2;
int soft;
EXPECT_EQ (cluster.interacts (cluster2, db::ICplxTrans (), conn), false);
EXPECT_EQ (cluster.interacts (cluster2, db::ICplxTrans (), conn, soft), false);
cluster.add (db::PolygonRef (poly, repo), 0);
EXPECT_EQ (cluster.interacts (cluster2, db::ICplxTrans (), conn), false);
EXPECT_EQ (cluster.interacts (cluster2, db::ICplxTrans (), conn, soft), false);
cluster2.add (db::PolygonRef (poly, repo), 1);
EXPECT_EQ (cluster.interacts (cluster2, db::ICplxTrans (), conn), true);
EXPECT_EQ (cluster.interacts (cluster2, db::ICplxTrans (db::Trans (db::Vector (10, 20))), conn), true);
EXPECT_EQ (cluster.interacts (cluster2, db::ICplxTrans (db::Trans (db::Vector (0, 1000))), conn), true);
EXPECT_EQ (cluster.interacts (cluster2, db::ICplxTrans (db::Trans (db::Vector (0, 1001))), conn), false);
EXPECT_EQ (cluster.interacts (cluster2, db::ICplxTrans (db::Trans (db::Vector (0, 2000))), conn), false);
EXPECT_EQ (cluster.interacts (cluster2, db::ICplxTrans (), conn, soft), true);
EXPECT_EQ (cluster.interacts (cluster2, db::ICplxTrans (db::Trans (db::Vector (10, 20))), conn, soft), true);
EXPECT_EQ (cluster.interacts (cluster2, db::ICplxTrans (db::Trans (db::Vector (0, 1000))), conn, soft), true);
EXPECT_EQ (cluster.interacts (cluster2, db::ICplxTrans (db::Trans (db::Vector (0, 1001))), conn, soft), false);
EXPECT_EQ (cluster.interacts (cluster2, db::ICplxTrans (db::Trans (db::Vector (0, 2000))), conn, soft), false);
cluster.clear ();
EXPECT_EQ (cluster.interacts (cluster2, db::ICplxTrans (), conn), false);
EXPECT_EQ (cluster.interacts (cluster2, db::ICplxTrans (), conn, soft), false);
cluster.add (db::PolygonRef (poly, repo), 2);
EXPECT_EQ (cluster.interacts (cluster2, db::ICplxTrans (), conn), false); // not connected
EXPECT_EQ (cluster.interacts (cluster2, db::ICplxTrans (), conn, soft), false); // not connected
cluster.clear ();
EXPECT_EQ (cluster.interacts (cluster2, db::ICplxTrans (), conn), false);
EXPECT_EQ (cluster.interacts (cluster2, db::ICplxTrans (), conn, soft), false);
cluster.add (db::PolygonRef (poly, repo), 1);
EXPECT_EQ (cluster.interacts (cluster2, db::ICplxTrans (), conn), true);
EXPECT_EQ (cluster.interacts (cluster2, db::ICplxTrans (), conn, soft), true);
}
static std::string obj2string (const db::PolygonRef &ref)
@ -286,7 +366,7 @@ template <class T>
static std::string local_cluster_to_string (const db::local_cluster<T> &cluster, const db::Connectivity &conn)
{
std::string res;
for (db::Connectivity::layer_iterator l = conn.begin_layers (); l != conn.end_layers (); ++l) {
for (db::Connectivity::all_layer_iterator l = conn.begin_layers (); l != conn.end_layers (); ++l) {
for (typename db::local_cluster<T>::shape_iterator s = cluster.begin (*l); ! s.at_end (); ++s) {
if (! res.empty ()) {
res += ";";
@ -313,6 +393,15 @@ static std::string local_clusters_to_string (const db::local_clusters<T> &cluste
}
s += "#" + tl::to_string (c->id ()) + ":" + local_cluster_to_string (*c, conn);
}
for (typename db::local_clusters<T>::const_iterator c = clusters.begin (); c != clusters.end (); ++c) {
auto sc = clusters.upward_soft_connections (c->id ());
for (auto i = sc.begin (); i != sc.end (); ++i) {
if (! s.empty ()) {
s += "\n";
}
s += "(#" + tl::to_string (*i) + "->#" + tl::to_string (c->id ()) + ")";
}
}
return s;
}
@ -590,6 +679,90 @@ TEST(23_LocalClustersWithEdges)
}
}
TEST(24_LocalClustersWithSoftConnections)
{
db::Layout layout;
db::Cell &cell = layout.cell (layout.add_cell ("TOP"));
db::GenericRepository &repo = layout.shape_repository ();
auto dbu = db::CplxTrans (layout.dbu ()).inverted ();
unsigned int nwell = 0;
unsigned int ntie = 1;
unsigned int ptie = 2;
unsigned int contact = 3;
unsigned int metal1 = 4;
cell.shapes (nwell).insert (db::PolygonRef (dbu * db::DPolygon (db::DBox (0.0, 4.0, 2.0, 8.0)), repo));
cell.shapes (ntie).insert (db::PolygonRef (dbu * db::DPolygon (db::DBox (0.5, 5.0, 1.5, 7.0)), repo));
cell.shapes (contact).insert (db::PolygonRef (dbu * db::DPolygon (db::DBox (0.8, 6.0, 1.2, 6.5)), repo));
cell.shapes (metal1).insert (db::PolygonRef (dbu * db::DPolygon (db::DBox (0.0, 5.0, 2.0, 7.0)), repo));
cell.shapes (ptie).insert (db::PolygonRef (dbu * db::DPolygon (db::DBox (0.5, 1.0, 1.5, 3.0)), repo));
cell.shapes (contact).insert (db::PolygonRef (dbu * db::DPolygon (db::DBox (0.8, 2.0, 1.2, 2.5)), repo));
cell.shapes (metal1).insert (db::PolygonRef (dbu * db::DPolygon (db::DBox (0.0, 1.0, 2.0, 3.0)), repo));
db::Connectivity conn;
conn.connect (nwell);
conn.connect (ntie);
conn.connect (ptie);
conn.connect (contact);
conn.connect (metal1);
conn.soft_connect (ntie, nwell);
conn.soft_connect (contact, ntie);
conn.connect (metal1, contact);
{
db::local_clusters<db::PolygonRef> clusters;
clusters.build_clusters (cell, conn);
EXPECT_EQ (local_clusters_to_string (clusters, conn),
"#1:[0](0,4000;0,8000;2000,8000;2000,4000)\n"
"#2:[1](500,5000;500,7000;1500,7000;1500,5000)\n"
"#3:[3](800,6000;800,6500;1200,6500;1200,6000);[4](0,5000;0,7000;2000,7000;2000,5000)\n"
"#4:[3](800,2000;800,2500;1200,2500;1200,2000);[4](0,1000;0,3000;2000,3000;2000,1000)\n"
"#5:[2](500,1000;500,3000;1500,3000;1500,1000)\n"
"(#2->#1)\n"
"(#3->#2)"
);
}
conn.soft_connect (contact, ptie);
{
db::local_clusters<db::PolygonRef> clusters;
clusters.build_clusters (cell, conn);
EXPECT_EQ (local_clusters_to_string (clusters, conn),
"#1:[0](0,4000;0,8000;2000,8000;2000,4000)\n"
"#2:[1](500,5000;500,7000;1500,7000;1500,5000)\n"
"#3:[3](800,6000;800,6500;1200,6500;1200,6000);[4](0,5000;0,7000;2000,7000;2000,5000)\n"
"#4:[2](500,1000;500,3000;1500,3000;1500,1000)\n"
"#5:[3](800,2000;800,2500;1200,2500;1200,2000);[4](0,1000;0,3000;2000,3000;2000,1000)\n"
"(#2->#1)\n"
"(#3->#2)\n"
"(#5->#4)"
);
}
conn.soft_connect_global (ptie, "BULK");
{
db::local_clusters<db::PolygonRef> clusters;
clusters.build_clusters (cell, conn);
EXPECT_EQ (local_clusters_to_string (clusters, conn),
"#1:[0](0,4000;0,8000;2000,8000;2000,4000)\n"
"#2:[1](500,5000;500,7000;1500,7000;1500,5000)\n"
"#3:[3](800,6000;800,6500;1200,6500;1200,6000);[4](0,5000;0,7000;2000,7000;2000,5000)\n"
"#4:[2](500,1000;500,3000;1500,3000;1500,1000)\n"
"#5:[3](800,2000;800,2500;1200,2500;1200,2000);[4](0,1000;0,3000;2000,3000;2000,1000)\n"
"#6:+BULK\n"
"(#2->#1)\n"
"(#3->#2)\n"
"(#5->#4)\n"
"(#4->#6)"
);
}
}
TEST(30_LocalConnectedClusters)
{
db::Layout layout;
@ -920,7 +1093,7 @@ static void copy_cluster_shapes (const std::string *&attrs, db::Shapes &out, db:
const db::local_cluster<db::PolygonRef> &lc = clusters.cluster_by_id (cluster_id);
// copy the shapes from this cell
for (db::Connectivity::layer_iterator l = conn.begin_layers (); l != conn.end_layers (); ++l) {
for (db::Connectivity::all_layer_iterator l = conn.begin_layers (); l != conn.end_layers (); ++l) {
for (db::local_cluster<db::PolygonRef>::shape_iterator s = lc.begin (*l); ! s.at_end (); ++s) {
db::Polygon poly = s->obj ().transformed (trans * db::ICplxTrans (s->trans ()));
out.insert (db::PolygonWithProperties (poly, cell_and_attr_pid > 0 ? cell_and_attr_pid : cell_pid));

View File

@ -101,6 +101,11 @@ static void dump_nets_to_layout (const db::Netlist &nl, const db::hier_clusters<
for (db::Net::const_terminal_iterator t = n->begin_terminals (); t != n->end_terminals (); ++t) {
const db::NetTerminalRef &tref = *t;
if (! tref.device ()->device_abstract ()) {
continue;
}
db::cell_index_type dci = tref.device ()->device_abstract ()->cell_index ();
db::DCplxTrans dtr = tref.device ()->trans ();
@ -121,6 +126,10 @@ static void dump_nets_to_layout (const db::Netlist &nl, const db::hier_clusters<
for (db::Circuit::const_device_iterator d = c->begin_devices (); d != c->end_devices (); ++d) {
if (! d->device_abstract ()) {
continue;
}
std::vector<db::cell_index_type> original_device_cells;
db::cell_index_type dci = d->device_abstract ()->cell_index ();
@ -3176,6 +3185,334 @@ TEST(14_JoinNets)
db::compare_layouts (_this, ly, au);
}
static
void annotate_soft_connections (db::Netlist &netlist, const db::hier_clusters<db::NetShape> &net_clusters)
{
db::DeviceClassDiode *soft_diode = new db::DeviceClassDiode ();
soft_diode->set_name ("SOFT");
netlist.add_device_class (soft_diode);
for (auto c = netlist.begin_bottom_up (); c != netlist.end_bottom_up (); ++c) {
auto clusters = net_clusters.clusters_per_cell (c->cell_index ());
for (auto n = c->begin_nets (); n != c->end_nets (); ++n) {
auto soft_connections = clusters.upward_soft_connections (n->cluster_id ());
for (auto sc = soft_connections.begin (); sc != soft_connections.end (); ++sc) {
db::Device *sc_device = new db::Device (soft_diode);
c->add_device (sc_device);
auto nn = c->net_by_cluster_id (*sc);
if (nn) {
sc_device->connect_terminal (db::DeviceClassDiode::terminal_id_C, n.operator-> ());
sc_device->connect_terminal (db::DeviceClassDiode::terminal_id_A, nn);
}
}
}
}
}
TEST(15_SoftConnections)
{
db::Layout ly;
db::LayerMap lmap;
unsigned int nwell = define_layer (ly, lmap, 1);
unsigned int diff = define_layer (ly, lmap, 2);
unsigned int pplus = define_layer (ly, lmap, 3);
unsigned int nplus = define_layer (ly, lmap, 4);
unsigned int poly = define_layer (ly, lmap, 5);
unsigned int contact = define_layer (ly, lmap, 8);
unsigned int metal1 = define_layer (ly, lmap, 9);
unsigned int via1 = define_layer (ly, lmap, 10);
unsigned int metal2 = define_layer (ly, lmap, 11);
{
db::LoadLayoutOptions options;
options.get_options<db::CommonReaderOptions> ().layer_map = lmap;
options.get_options<db::CommonReaderOptions> ().create_other_layers = false;
std::string fn (tl::testdata ());
fn = tl::combine_path (fn, "algo");
fn = tl::combine_path (fn, "soft_connections.gds");
tl::InputStream stream (fn);
db::Reader reader (stream);
reader.read (ly, options);
}
db::Cell &tc = ly.cell (*ly.begin_top_down ());
db::DeepShapeStore dss;
dss.set_text_enlargement (1);
dss.set_text_property_name (tl::Variant ("LABEL"));
// original layers
db::Region rnwell (db::RecursiveShapeIterator (ly, tc, nwell), dss);
db::Region rdiff (db::RecursiveShapeIterator (ly, tc, diff), dss);
db::Region rpplus (db::RecursiveShapeIterator (ly, tc, pplus), dss);
db::Region rnplus (db::RecursiveShapeIterator (ly, tc, nplus), dss);
db::Region rpoly (db::RecursiveShapeIterator (ly, tc, poly), dss);
db::Region rcontact (db::RecursiveShapeIterator (ly, tc, contact), dss);
db::Region rmetal1 (db::RecursiveShapeIterator (ly, tc, metal1), dss);
db::Region rvia1 (db::RecursiveShapeIterator (ly, tc, via1), dss);
db::Region rmetal2 (db::RecursiveShapeIterator (ly, tc, metal2), dss);
// derived regions
db::Region rdiff_in_nwell = rdiff & rnwell;
db::Region rpdiff = rdiff_in_nwell - rnplus;
db::Region rntie = rdiff_in_nwell & rnplus;
db::Region rpgate = rpdiff & rpoly;
db::Region rpsd = rpdiff - rpgate;
db::Region rdiff_outside_nwell = rdiff - rnwell;
db::Region rndiff = rdiff_outside_nwell - rpplus;
db::Region rptie = rdiff_outside_nwell & rpplus;
db::Region rngate = rndiff & rpoly;
db::Region rnsd = rndiff - rngate;
// Global
db::Region bulk (dss);
// return the computed layers into the original layout and write it for debugging purposes
unsigned int lpgate = ly.insert_layer (db::LayerProperties (10, 0)); // 10/0 -> Gate (p)
unsigned int lngate = ly.insert_layer (db::LayerProperties (11, 0)); // 11/0 -> Gate (n)
unsigned int lpsd = ly.insert_layer (db::LayerProperties (12, 0)); // 12/0 -> Source/Drain (p)
unsigned int lnsd = ly.insert_layer (db::LayerProperties (13, 0)); // 13/0 -> Source/Drain (n)
unsigned int lpdiff = ly.insert_layer (db::LayerProperties (14, 0)); // 14/0 -> P Diffusion
unsigned int lndiff = ly.insert_layer (db::LayerProperties (15, 0)); // 15/0 -> N Diffusion
unsigned int lptie = ly.insert_layer (db::LayerProperties (16, 0)); // 16/0 -> P Tie
unsigned int lntie = ly.insert_layer (db::LayerProperties (17, 0)); // 17/0 -> N Tie
rpgate.insert_into (&ly, tc.cell_index (), lpgate);
rngate.insert_into (&ly, tc.cell_index (), lngate);
rpsd.insert_into (&ly, tc.cell_index (), lpsd);
rnsd.insert_into (&ly, tc.cell_index (), lnsd);
rpdiff.insert_into (&ly, tc.cell_index (), lpdiff);
rndiff.insert_into (&ly, tc.cell_index (), lndiff);
rptie.insert_into (&ly, tc.cell_index (), lptie);
rntie.insert_into (&ly, tc.cell_index (), lntie);
// perform the extraction
db::Netlist nl;
db::hier_clusters<db::NetShape> cl;
db::NetlistDeviceExtractorMOS4Transistor pmos_ex ("PMOS");
db::NetlistDeviceExtractorMOS4Transistor nmos_ex ("NMOS");
db::NetlistDeviceExtractor::input_layers dl;
dl["SD"] = &rpsd;
dl["G"] = &rpgate;
dl["W"] = &rnwell;
dl["P"] = &rpoly; // not needed for extraction but to return terminal shapes
pmos_ex.extract (dss, 0, dl, nl, cl);
dl["SD"] = &rnsd;
dl["G"] = &rngate;
dl["W"] = &bulk;
dl["P"] = &rpoly; // not needed for extraction but to return terminal shapes
nmos_ex.extract (dss, 0, dl, nl, cl);
// perform the net extraction
db::NetlistExtractor net_ex;
db::Connectivity conn;
// Global nets
conn.connect_global (bulk, "BULK");
conn.soft_connect_global (rptie, "BULK");
// Intra-layer
conn.connect (rpsd);
conn.connect (rnsd);
conn.connect (rptie);
conn.connect (rntie);
conn.connect (rnwell);
conn.connect (rpoly);
conn.connect (rcontact);
conn.connect (rmetal1);
conn.connect (rvia1);
conn.connect (rmetal2);
// Inter-layer
conn.soft_connect (rcontact, rpsd);
conn.soft_connect (rcontact, rnsd);
conn.soft_connect (rntie, rnwell);
conn.soft_connect (rcontact, rptie);
conn.soft_connect (rcontact, rntie);
conn.soft_connect (rcontact, rpoly);
conn.connect (rcontact, rmetal1);
conn.connect (rvia1, rmetal1);
conn.connect (rvia1, rmetal2);
// extract the nets
std::list<std::set<std::string> > jn;
jn.push_back (std::set<std::string> ());
jn.back ().insert ("BULK");
jn.back ().insert ("VSS");
jn.push_back (std::set<std::string> ());
jn.back ().insert ("NWELL");
jn.back ().insert ("VDD");
net_ex.set_joined_nets ("INV2", jn);
std::list<tl::GlobPattern> gp;
gp.push_back (tl::GlobPattern ("NEXT"));
gp.push_back (tl::GlobPattern ("FB"));
net_ex.set_joined_net_names (gp);
net_ex.extract_nets (dss, 0, conn, nl, cl);
annotate_soft_connections (nl, cl);
EXPECT_EQ (all_net_names_unique (nl), true);
// debug layers produced for nets
std::map<unsigned int, unsigned int> dump_map;
dump_map [layer_of (bulk) ] = ly.insert_layer (db::LayerProperties (200, 0));
dump_map [layer_of (rnwell) ] = ly.insert_layer (db::LayerProperties (201, 0));
dump_map [layer_of (rpsd) ] = ly.insert_layer (db::LayerProperties (202, 0));
dump_map [layer_of (rnsd) ] = ly.insert_layer (db::LayerProperties (203, 0));
dump_map [layer_of (rpoly) ] = ly.insert_layer (db::LayerProperties (204, 0));
dump_map [layer_of (rcontact) ] = ly.insert_layer (db::LayerProperties (205, 0));
dump_map [layer_of (rmetal1) ] = ly.insert_layer (db::LayerProperties (206, 0));
dump_map [layer_of (rvia1) ] = ly.insert_layer (db::LayerProperties (207, 0));
dump_map [layer_of (rmetal2) ] = ly.insert_layer (db::LayerProperties (208, 0));
// write nets to layout
db::CellMapping cm = dss.cell_mapping_to_original (0, &ly, tc.cell_index ());
dump_nets_to_layout (nl, cl, ly, dump_map, cm, true);
// compare netlist as string
CHECKPOINT ();
db::compare_netlist (_this, nl,
"circuit RINGO ();\n"
" subcircuit ND2X1 $1 (NWELL=$4,B=FB,A=ENABLE,VDD=VDD,OUT=$5,VSS=VSS,BULK=BULK);\n"
" subcircuit INVX1 $2 (NWELL=$4,IN=$14,VDD=VDD,OUT=FB,VSS=VSS,BULK=BULK);\n"
" subcircuit INVX1 $3 (NWELL=$4,IN=FB,VDD=$I17,OUT=OUT,VSS=$I27,BULK=BULK);\n"
" subcircuit TIE $4 (NWELL=$4,VSS=$I27,VDD=$I17,BULK=BULK);\n"
" subcircuit EMPTY $5 ($1=$4,$2=$I27,$3=$I17);\n"
" subcircuit TIE $6 (NWELL=$4,VSS=VSS,VDD=VDD,BULK=BULK);\n"
" subcircuit INVX1 $7 (NWELL=$4,IN=$5,VDD=VDD,OUT=$6,VSS=VSS,BULK=BULK);\n"
" subcircuit EMPTY $8 ($1=$4,$2=VSS,$3=VDD);\n"
" subcircuit INVX1 $9 (NWELL=$4,IN=$6,VDD=VDD,OUT=$7,VSS=VSS,BULK=BULK);\n"
" subcircuit INVX1 $10 (NWELL=$4,IN=$7,VDD=VDD,OUT=$8,VSS=VSS,BULK=BULK);\n"
" subcircuit INVX1 $11 (NWELL=$4,IN=$8,VDD=VDD,OUT=$9,VSS=VSS,BULK=BULK);\n"
" subcircuit INVX1 $12 (NWELL=$4,IN=$9,VDD=VDD,OUT=$10,VSS=VSS,BULK=BULK);\n"
" subcircuit INVX1 $13 (NWELL=$4,IN=$10,VDD=VDD,OUT=$11,VSS=VSS,BULK=BULK);\n"
" subcircuit INVX1 $14 (NWELL=$4,IN=$11,VDD=VDD,OUT=$12,VSS=VSS,BULK=BULK);\n"
" subcircuit INVX1 $15 (NWELL=$4,IN=$12,VDD=VDD,OUT=$15,VSS=VSS,BULK=BULK);\n"
" subcircuit INVX1 $16 (NWELL=$4,IN=$15,VDD=VDD,OUT=$14,VSS=VSS,BULK=BULK);\n"
"end;\n"
"circuit ND2X1 (NWELL=NWELL,B=B,A=A,VDD=VDD,OUT=OUT,VSS=VSS,BULK=BULK);\n"
" device PMOS $1 (S=$7,G=$4,D=$9,B=NWELL) (L=0.25,W=1.5,AS=0.6375,AD=0.3375,PS=3.85,PD=1.95);\n"
" device PMOS $2 (S=$9,G=$2,D=$6,B=NWELL) (L=0.25,W=1.5,AS=0.3375,AD=0.6375,PS=1.95,PD=3.85);\n"
" device NMOS $3 (S=$13,G=$4,D=$14,B=BULK) (L=0.25,W=0.95,AS=0.40375,AD=0.21375,PS=2.75,PD=1.4);\n"
" device NMOS $4 (S=$14,G=$2,D=$11,B=BULK) (L=0.25,W=0.95,AS=0.21375,AD=0.40375,PS=1.4,PD=2.75);\n"
" device SOFT $5 (A=B,C=$2) (A=0,P=0);\n"
" device SOFT $6 (A=A,C=$4) (A=0,P=0);\n"
" device SOFT $7 (A=OUT,C=$6) (A=0,P=0);\n"
" device SOFT $8 (A=OUT,C=$7) (A=0,P=0);\n"
" device SOFT $9 (A=VDD,C=$9) (A=0,P=0);\n"
" device SOFT $10 (A=OUT,C=$11) (A=0,P=0);\n"
" device SOFT $11 (A=VSS,C=$13) (A=0,P=0);\n"
"end;\n"
"circuit TIE (NWELL=NWELL,VSS=VSS,VDD=VDD,BULK=BULK);\n"
" device SOFT $1 (A=$2,C=NWELL) (A=0,P=0);\n"
" device SOFT $2 (A=VDD,C=$2) (A=0,P=0);\n"
" device SOFT $3 (A=VSS,C=$3) (A=0,P=0);\n"
" device SOFT $4 (A=$3,C=BULK) (A=0,P=0);\n"
"end;\n"
"circuit EMPTY ($1=$1,$2=$2,$3=$3);\n"
"end;\n"
"circuit INVX1 (NWELL=NWELL,IN=IN,VDD=VDD,OUT=OUT,VSS=VSS,BULK=BULK);\n"
" device PMOS $1 (S=$5,G=$2,D=$7,B=NWELL) (L=0.25,W=1.5,AS=0.6375,AD=0.6375,PS=3.85,PD=3.85);\n"
" device NMOS $2 (S=$10,G=$2,D=$8,B=BULK) (L=0.25,W=0.95,AS=0.40375,AD=0.40375,PS=2.75,PD=2.75);\n"
" device SOFT $3 (A=IN,C=$2) (A=0,P=0);\n"
" device SOFT $4 (A=VDD,C=$5) (A=0,P=0);\n"
" device SOFT $5 (A=OUT,C=$7) (A=0,P=0);\n"
" device SOFT $6 (A=OUT,C=$8) (A=0,P=0);\n"
" device SOFT $7 (A=VSS,C=$10) (A=0,P=0);\n"
"end;\n"
);
// doesn't do anything here, but we test that this does not destroy anything:
nl.combine_devices ();
// make pins for named nets of top-level circuits - this way they are not purged
nl.make_top_level_pins ();
nl.purge ();
// compare netlist as string
CHECKPOINT ();
db::compare_netlist (_this, nl,
"circuit RINGO (FB=FB,ENABLE=ENABLE,OUT=OUT,VDD=VDD,VSS=VSS,BULK=BULK);\n"
" subcircuit ND2X1 $1 (NWELL=$4,B=FB,A=ENABLE,VDD=VDD,OUT=$5,VSS=VSS,BULK=BULK);\n"
" subcircuit INVX1 $2 (NWELL=$4,IN=$14,VDD=VDD,OUT=FB,VSS=VSS,BULK=BULK);\n"
" subcircuit INVX1 $3 (NWELL=$4,IN=FB,VDD=$I17,OUT=OUT,VSS=$I27,BULK=BULK);\n"
" subcircuit TIE $4 (NWELL=$4,VSS=$I27,VDD=$I17,BULK=BULK);\n"
" subcircuit TIE $5 (NWELL=$4,VSS=VSS,VDD=VDD,BULK=BULK);\n"
" subcircuit INVX1 $6 (NWELL=$4,IN=$5,VDD=VDD,OUT=$6,VSS=VSS,BULK=BULK);\n"
" subcircuit INVX1 $7 (NWELL=$4,IN=$6,VDD=VDD,OUT=$7,VSS=VSS,BULK=BULK);\n"
" subcircuit INVX1 $8 (NWELL=$4,IN=$7,VDD=VDD,OUT=$8,VSS=VSS,BULK=BULK);\n"
" subcircuit INVX1 $9 (NWELL=$4,IN=$8,VDD=VDD,OUT=$9,VSS=VSS,BULK=BULK);\n"
" subcircuit INVX1 $10 (NWELL=$4,IN=$9,VDD=VDD,OUT=$10,VSS=VSS,BULK=BULK);\n"
" subcircuit INVX1 $11 (NWELL=$4,IN=$10,VDD=VDD,OUT=$11,VSS=VSS,BULK=BULK);\n"
" subcircuit INVX1 $12 (NWELL=$4,IN=$11,VDD=VDD,OUT=$12,VSS=VSS,BULK=BULK);\n"
" subcircuit INVX1 $13 (NWELL=$4,IN=$12,VDD=VDD,OUT=$15,VSS=VSS,BULK=BULK);\n"
" subcircuit INVX1 $14 (NWELL=$4,IN=$15,VDD=VDD,OUT=$14,VSS=VSS,BULK=BULK);\n"
"end;\n"
"circuit ND2X1 (NWELL=NWELL,B=B,A=A,VDD=VDD,OUT=OUT,VSS=VSS,BULK=BULK);\n"
" device PMOS $1 (S=$7,G=$4,D=$9,B=NWELL) (L=0.25,W=1.5,AS=0.6375,AD=0.3375,PS=3.85,PD=1.95);\n"
" device PMOS $2 (S=$9,G=$2,D=$6,B=NWELL) (L=0.25,W=1.5,AS=0.3375,AD=0.6375,PS=1.95,PD=3.85);\n"
" device NMOS $3 (S=$13,G=$4,D=$14,B=BULK) (L=0.25,W=0.95,AS=0.40375,AD=0.21375,PS=2.75,PD=1.4);\n"
" device NMOS $4 (S=$14,G=$2,D=$11,B=BULK) (L=0.25,W=0.95,AS=0.21375,AD=0.40375,PS=1.4,PD=2.75);\n"
" device SOFT $5 (A=B,C=$2) (A=0,P=0);\n"
" device SOFT $6 (A=A,C=$4) (A=0,P=0);\n"
" device SOFT $7 (A=OUT,C=$6) (A=0,P=0);\n"
" device SOFT $8 (A=OUT,C=$7) (A=0,P=0);\n"
" device SOFT $9 (A=VDD,C=$9) (A=0,P=0);\n"
" device SOFT $10 (A=OUT,C=$11) (A=0,P=0);\n"
" device SOFT $11 (A=VSS,C=$13) (A=0,P=0);\n"
"end;\n"
"circuit TIE (NWELL=NWELL,VSS=VSS,VDD=VDD,BULK=BULK);\n"
" device SOFT $1 (A=$2,C=NWELL) (A=0,P=0);\n"
" device SOFT $2 (A=VDD,C=$2) (A=0,P=0);\n"
" device SOFT $3 (A=VSS,C=$3) (A=0,P=0);\n"
" device SOFT $4 (A=$3,C=BULK) (A=0,P=0);\n"
"end;\n"
"circuit INVX1 (NWELL=NWELL,IN=IN,VDD=VDD,OUT=OUT,VSS=VSS,BULK=BULK);\n"
" device PMOS $1 (S=$5,G=$2,D=$7,B=NWELL) (L=0.25,W=1.5,AS=0.6375,AD=0.6375,PS=3.85,PD=3.85);\n"
" device NMOS $2 (S=$10,G=$2,D=$8,B=BULK) (L=0.25,W=0.95,AS=0.40375,AD=0.40375,PS=2.75,PD=2.75);\n"
" device SOFT $3 (A=IN,C=$2) (A=0,P=0);\n"
" device SOFT $4 (A=VDD,C=$5) (A=0,P=0);\n"
" device SOFT $5 (A=OUT,C=$7) (A=0,P=0);\n"
" device SOFT $6 (A=OUT,C=$8) (A=0,P=0);\n"
" device SOFT $7 (A=VSS,C=$10) (A=0,P=0);\n"
"end;\n"
);
// compare the collected test data
std::string au = tl::testdata ();
au = tl::combine_path (au, "algo");
au = tl::combine_path (au, "soft_connections_au.gds");
db::compare_layouts (_this, ly, au);
}
TEST(100_issue954)
{

View File

@ -1838,6 +1838,36 @@ is equivalent to "layer.smoothed" (see <a href="/about/drc_ref_layer.xml#smoothe
argument, "smoothed" represents the polygon smoother on primary shapes within
<a href="/about/drc_ref_drc.xml">DRC</a> expressions (see <a href="/about/drc_ref_layer.xml#drc">Layer#drc</a> and <a href="/about/drc_ref_global.xml#smoothed">smoothed</a> for more details).
</p>
<a name="soft_connect"/><h2>"soft_connect" - Specifies a soft connection between two layers</h2>
<keyword name="soft_connect"/>
<p>Usage:</p>
<ul>
<li><tt>soft_connect(a, b)</tt></li>
</ul>
<p>
A "soft connection" is made between two layers and
is a directional connection (like an ideal diode).
Soft connections allow detecting if nets are connected
via a high-ohmic substrate or diffusion layer (the
"lower" layer).
"b" is the "lower" and "a" the upper layer.
</p><p>
See <a href="/about/drc_ref_netter.xml#connect">Netter#connect</a> for a more detailed description of that function.
</p>
<a name="soft_connect_global"/><h2>"soft_connect_global" - Specifies a soft connection to a global net</h2>
<keyword name="soft_connect_global"/>
<p>Usage:</p>
<ul>
<li><tt>soft_connect_global(l, name)</tt></li>
</ul>
<p>
Like <a href="#soft_connect">soft_connect</a>, a soft connection is made between
a layer and a global net (e.g. substrate). The global net
is always the "lower" net of the soft connection.
</p><p>
See <a href="/about/drc_ref_netter.xml#soft_connect_global">Netter#soft_connect_global</a> for a more detailed
description of that function.
</p>
<a name="source"/><h2>"source" - Specifies a source layout</h2>
<keyword name="source"/>
<p>Usage:</p>

View File

@ -434,6 +434,49 @@ layout analysis. Hence, all <a href="#connect">connect</a>, <a href="#connect_gl
calls must have been made before this method is used. Further <a href="#connect">connect</a>
statements will clear the netlist and re-extract it again.
</p>
<a name="soft_connect"/><h2>"soft_connect" - Specifies a soft connection between two layers</h2>
<keyword name="soft_connect"/>
<p>Usage:</p>
<ul>
<li><tt>soft_connect(a, b)</tt></li>
</ul>
<p>
a and b must be polygon or text layers. After calling this function, the
Netter considers shapes from layer a and b connected in "soft mode".
Typically, b is a high-ohmic layer such as diffusion, implant for substate
material, also called the "lower" layer.
</p><p>
A soft connection between shapes from layer a and b forms a directional
connection like an ideal diode: current can flow down, but now up
(not meant in the physical sense, this is a concept).
</p><p>
Hence, two nets are disconnected, if they both connect to the same lower layer,
but do not have a connection between them.
</p><p>
The netlist extractor will use this scheme to identify nets that are
connected only via such a high-ohmic region. Such a case is typically
bad for the functionality of a device and reported as an error.
Once, the check has been made and no error is found, soft-connected
nets are joined the same way than hard connections are made.
</p><p>
Beside this, soft connections follow the same rules than hard connections
(see <a href="#connect">connect</a>).
</p>
<a name="soft_connect_global"/><h2>"soft_connect_global" - Soft-connects a layer with a global net</h2>
<keyword name="soft_connect_global"/>
<p>Usage:</p>
<ul>
<li><tt>soft-connect_global(l, name)</tt></li>
</ul>
<p>
Connects the shapes from the given layer l to a global net with the given name
in "soft mode".
</p><p>
See <a href="#connect_global">connect_global</a> for details about the concepts of global nets.
See <a href="#soft_connect">soft_connect</a> for details about the concept of soft connections.
In global net soft connections, the global net (typically a substrate)
is always the "lower" layer.
</p>
<a name="top_level"/><h2>"top_level" - Specifies top level mode</h2>
<keyword name="top_level"/>
<p>Usage:</p>

View File

@ -10,6 +10,7 @@
<h2-index/>
<h2>Intra- and inter-layer connections</h2>
<keyword name="connect"/>
<p>
The connectivity setup of a LVS script determines how the connections are made.
@ -77,6 +78,7 @@ connect(metal2, metal2_labels)</pre>
</p>
<h2>Global connections</h2>
<keyword name="connect_global"/>
<p>
KLayout supports implicit connections made across all polygons on
@ -98,6 +100,7 @@ connect(metal2, metal2_labels)</pre>
</p>
<h2>Implicit connections</h2>
<keyword name="connect_implicit"/>
<p>
Implicit connections can be useful to supply preliminary connections
@ -193,6 +196,7 @@ connect(metal2, metal2_labels)</pre>
</p>
<h2>Explicit connections</h2>
<keyword name="connect_explicit"/>
<p>
Explicit connections can be useful to enforce a connection in the layout
@ -280,4 +284,219 @@ connect_explicit("INV", [ "BULK", "VSS" ])
statements.
</p>
<h2>Soft connections</h2>
<keyword name="soft_connect"/>
<keyword name="soft_connect_global"/>
<p>
Soft connections are a way to find wiring issues where signals
or even power is routed over high-ohmic paths.
High-ohmic paths can be established through connections via
poly silicon, implant, well or substrate areas. Such areas
can easily show resistance values which are a hundred times
higher than that of metal connections. We have to make sure
that for routing power or critical signals, connections are
not made through such areas, but primarily through metal
connections.
</p>
<p>
Here is an example:
</p>
<p>
<img src="/manual/soft_connections.png"/>
</p>
<p>
In this case, we have a standard textbook planar CMOS technology with two PMOS devices
sitting in a n-well. These could be the two PMOS of two inverter pairs for example.
Both PMOS need to be connected to VDD at their sources. In addition, the n-well
area also needs to be tied to VDD in order to provide reverse bias for the p+ drain
areas and the body potential for the transistors, forming the opposite
electrode of the gate capacity.
</p>
<p>Such a technology stack can be described by the following connectivity:</p>
<pre># Input layers
nwell = ...
active = ...
pplus = ...
nplus = ...
poly = ...
contact = ...
metal1 = ...
via1 = ...
metal2 = ...
# computed layers
(nactive, pactive) = active.and_not(nwell)
# PMOS and NMOS source/drain regions
psd = (nactive &amp; pplus) - poly
nsd = (pactive &amp; nplus) - poly
# n tie and p tie (nwell and substrate contact)
ntie = nactive &amp; nplus
ptie = pactive &amp; pplus
# connections
# nwell connections
connect(ntie, nwell)
connect(contact, ntie)
# substrate connections
connect_global(ptie, "BULK")
connect(contact, ptie)
# device connections
connect(contact, psd)
connect(contact, nsd)
connect(contact, poly)
# metal connections
connect(metal1, contact)
connect(via1, metal1)
connect(metal2, via1)
</pre>
<p>
However, there is an issue:
As shown in the picture, the left PMOS source is properly connected to VDD.
The right one however lacks the metal connection to VDD. From the perspective
of pure connectivity, this transistor's source is connected to VDD, but only
through a weak n-well connection. Such a device will not work - or at least, badly.
</p>
<p>
The solution is to introduce soft connections. Soft connections are made
by replacing "connect" with "soft_connect" and "connect_global" with "soft_connect_global"
(see <a href="/about/drc_ref_global.xml#soft_connect">soft_connect</a> and
<a href="/about/drc_ref_global.xml#soft_connect_global">soft_connect_global</a>).
The first layer is the "upper" layer while the second layer is the "lower" layer.
The lower layer is the high-ohmic one. In global connections, the global net is
always the high-ohmic one.
</p>
<p>
Soft connections can be visualized as directional connections: current can only flow from the
upper to the lower layer, but not the other way. So, a real connection is only made,
if both upper terminals of the soft connections are connected to the same physical net.
</p>
<p>
To solve the n-well issue we have to substitute the n-tie to n-well connection statement
by a "soft_connect" statement:
</p>
<pre>soft_connect(ntie, nwell)</pre>
<p>
The above picture now looks like this:
</p>
<p>
<img src="/manual/soft_connections2.png"/>
</p>
<p>
With this definition, the netlist extractor is able to detect the fault
and raise a warning or an error (in top level mode).
The warning is shown on the log tab and indicates incomplete wiring plus
details about the subnets, separated by the soft connections.
</p>
<p>
The complete the setup we also need to include other soft connections,
such as connections via substrate (a global soft connect to the BULK net),
via source/drain implants, via the tie implants and via poly:
</p>
<pre># Input layers
nwell = ...
active = ...
pplus = ...
nplus = ...
poly = ...
contact = ...
metal1 = ...
via1 = ...
metal2 = ...
# computed layers
(nactive, pactive) = active.and_not(nwell)
# PMOS and NMOS source/drain regions
psd = (nactive &amp; pplus) - poly
nsd = (pactive &amp; nplus) - poly
# n tie and p tie (nwell and substrate contact)
ntie = nactive &amp; nplus
ptie = pactive &amp; pplus
# connections
# nwell connections
soft_connect(ntie, nwell)
soft_connect(contact, ntie)
# substrate connections
soft_connect_global(ptie, "BULK")
soft_connect(contact, ptie)
# device connections
soft_connect(contact, psd)
soft_connect(contact, nsd)
soft_connect(contact, poly)
# metal connections
connect(metal1, contact)
connect(via1, metal1)
connect(metal2, via1)
</pre>
<p>
As this code demonstrates, multiple soft connections can be specified.
From the perspective of the check, all soft connections are of the same kind.
</p>
<p>
Note, that two opposite soft connections cancel, so this would eventually
make a hard connection:
</p>
<pre>soft_connect(a, b)
soft_connect(b, a)
</pre>
<p>
<b>NOTE:</b> It is therefore important to observe the direction of soft connections:
upper and high-conductive / low-ohmic layer first, and lower and low-conductive / high-ohmic
layer second.
</p>
<h3>Soft connections and "must connect" nets</h3>
<p>
Soft connections and must connect nets (aka "connect_explicit" and "connect_implicit")
serve the same purpose - to detect incomplete wiring. Typically they are used
together, such as doing "connect_implicit" on VDD nets and "connect_explicit"
on VDD and NWELL nets if the schematic circuits do not feature an explicit
NWELL pin.
</p>
<p>
Soft connections are checked before connect_explicit and connect_implicit
are executed. This means, that soft connection errors cannot be masked by
declaring them "must connect". On the other hand, that is not a real issue
as both checks would raise an warning or error (in the top-level case).
It would only be a different kind of warning.
</p>
</doc>

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

View File

@ -244,6 +244,8 @@
<file alias="drc_raw1.png">doc/images/drc_raw1.png</file>
<file alias="drc_raw2.png">doc/images/drc_raw2.png</file>
<file alias="drc_raw3.png">doc/images/drc_raw3.png</file>
<file alias="soft_connections.png">doc/manual/soft_connections.png</file>
<file alias="soft_connections2.png">doc/manual/soft_connections2.png</file>
</qresource>
<qresource prefix="/help/programming">
<file alias="introduction.xml">doc/programming/introduction.xml</file>

View File

@ -2284,12 +2284,36 @@ CODE
# @synopsis connect(a, b)
# See \Netter#connect for a description of that function.
# %DRC%
# @name soft_connect
# @brief Specifies a soft connection between two layers
# @synopsis soft_connect(a, b)
# A "soft connection" is made between two layers and
# is a directional connection (like an ideal diode).
# Soft connections allow detecting if nets are connected
# via a high-ohmic substrate or diffusion layer (the
# "lower" layer).
# "b" is the "lower" and "a" the upper layer.
#
# See \Netter#connect for a more detailed description of that function.
# %DRC%
# @name connect_global
# @brief Specifies a connection to a global net
# @synopsis connect_global(l, name)
# See \Netter#connect_global for a description of that function.
# %DRC%
# @name soft_connect_global
# @brief Specifies a soft connection to a global net
# @synopsis soft_connect_global(l, name)
# Like \soft_connect, a soft connection is made between
# a layer and a global net (e.g. substrate). The global net
# is always the "lower" net of the soft connection.
#
# See \Netter#soft_connect_global for a more detailed
# description of that function.
# %DRC%
# @name clear_connections
# @brief Clears all connections stored so far
@ -2360,14 +2384,18 @@ CODE
clear_connections
connect
connect_global
soft_connect
soft_connect_global
connect_implicit
connect_explicit
device_scaling
top_level
ignore_extraction_errors
extract_devices
l2n_data
netlist
l2n_data
_l2n_object
_make_soft_connection_diodes
).each do |f|
eval <<"CODE"
def #{f}(*args)

View File

@ -116,6 +116,51 @@ module DRC
end
# %DRC%
# @name soft_connect
# @brief Specifies a soft connection between two layers
# @synopsis soft_connect(a, b)
# a and b must be polygon or text layers. After calling this function, the
# Netter considers shapes from layer a and b connected in "soft mode".
# Typically, b is a high-ohmic layer such as diffusion, implant for substate
# material, also called the "lower" layer.
#
# A soft connection between shapes from layer a and b forms a directional
# connection like an ideal diode: current can flow down, but now up
# (not meant in the physical sense, this is a concept).
#
# Hence, two nets are disconnected, if they both connect to the same lower layer,
# but do not have a connection between them.
#
# The netlist extractor will use this scheme to identify nets that are
# connected only via such a high-ohmic region. Such a case is typically
# bad for the functionality of a device and reported as an error.
# Once, the check has been made and no error is found, soft-connected
# nets are joined the same way than hard connections are made.
#
# Beside this, soft connections follow the same rules than hard connections
# (see \connect).
def soft_connect(a, b)
@engine._context("soft_connect") do
a.is_a?(DRC::DRCLayer) || raise("First argument must be a layer")
b.is_a?(DRC::DRCLayer) || raise("Second argument must be a layer")
a.requires_texts_or_region
b.requires_texts_or_region
register_layer(a.data)
register_layer(b.data)
# soft connections imply hard intra-layer connections
a.data.is_a?(RBA::Region) && @l2n.connect(a.data)
b.data.is_a?(RBA::Region) && @l2n.connect(b.data)
@l2n.soft_connect(a.data, b.data)
end
end
# %DRC%
# @name connect_global
# @brief Connects a layer with a global net
@ -140,6 +185,33 @@ module DRC
end
# %DRC%
# @name soft_connect_global
# @brief Soft-connects a layer with a global net
# @synopsis soft-connect_global(l, name)
# Connects the shapes from the given layer l to a global net with the given name
# in "soft mode".
#
# See \connect_global for details about the concepts of global nets.
# See \soft_connect for details about the concept of soft connections.
# In global net soft connections, the global net (typically a substrate)
# is always the "lower" layer.
def soft_connect_global(l, name)
@engine._context("soft_connect_global") do
l.is_a?(DRC::DRCLayer) || raise("Layer argument must be a layer")
l.requires_texts_or_region
register_layer(l.data)
l.data.is_a?(RBA::Region) && @l2n.connect(l.data)
@l2n.soft_connect_global(l.data, name)
end
end
# %DRC%
# @name extract_devices
# @brief Extracts devices based on the given extractor class, name and device layer selection
@ -681,6 +753,14 @@ module DRC
@l2n && @l2n.is_extracted? && self.l2n_data
end
def _l2n_object
@l2n
end
def _make_soft_connection_diodes(f)
@l2n.make_soft_connection_diodes = f
end
private
def cleanup

View File

@ -519,7 +519,7 @@ HelpSource::get_image (const std::string &u)
{
QResource res (resource_url (QUrl::fromEncoded (u.c_str ()).path ()));
if (res.size () == 0) {
throw tl::Exception (tl::to_string (QObject::tr ("ERROR: no data found for resource ")) + u);
throw tl::Exception (tl::to_string (QObject::tr ("No data found for resource ")) + u);
}
QByteArray data;
@ -552,7 +552,7 @@ HelpSource::get_css (const std::string &u)
QResource res (resource_url (QUrl::fromEncoded (u.c_str ()).path ()));
if (res.size () == 0) {
throw tl::Exception (tl::to_string (QObject::tr ("ERROR: no data found for resource ")) + u);
throw tl::Exception (tl::to_string (QObject::tr ("No data found for resource ")) + u);
}
QByteArray data;

View File

@ -54,7 +54,7 @@ ResourceHelpProvider::get (lay::HelpSource * /*src*/, const std::string &path) c
QString qpath = tl::to_qstring (path);
QResource res (resource_url (qpath));
if (res.size () == 0) {
throw tl::Exception (tl::to_string (QObject::tr ("ERROR: no data found for resource ")) + tl::to_string (res.fileName ()));
throw tl::Exception (tl::to_string (QObject::tr ("No data found for resource ")) + tl::to_string (res.fileName ()));
}
QByteArray data;

View File

@ -134,7 +134,7 @@
<string>...</string>
</property>
<property name="icon">
<iconset>
<iconset resource="../../icons/icons.qrc">
<normaloff>:/run_16px.png</normaloff>:/run_16px.png</iconset>
</property>
<property name="shortcut">
@ -576,6 +576,9 @@
<property name="contextMenuPolicy">
<enum>Qt::ActionsContextMenu</enum>
</property>
<property name="selectionMode">
<enum>QAbstractItemView::ExtendedSelection</enum>
</property>
<property name="headerHidden">
<bool>true</bool>
</property>

View File

@ -100,7 +100,7 @@ size_t count_shapes (db::LayoutToNetlist *l2ndb, db::Net *net)
size_t n = 0;
const db::Connectivity &conn = l2ndb->connectivity ();
for (db::Connectivity::layer_iterator layer = conn.begin_layers (); layer != conn.end_layers (); ++layer) {
for (db::Connectivity::all_layer_iterator layer = conn.begin_layers (); layer != conn.end_layers (); ++layer) {
n += count_shapes (l2ndb, net, *layer);
}
@ -276,7 +276,7 @@ void NetInfoDialog::update_info_text ()
bool incomplete = false;
const db::Connectivity &conn = mp_l2ndb->connectivity ();
for (db::Connectivity::layer_iterator layer = conn.begin_layers (); layer != conn.end_layers (); ++layer) {
for (db::Connectivity::all_layer_iterator layer = conn.begin_layers (); layer != conn.end_layers (); ++layer) {
std::string l = layer_string (mp_l2ndb.get (), *layer);

View File

@ -306,7 +306,7 @@ NetlistBrowserDialog::probe_net (const db::DPoint &p, bool trace_path)
std::vector<db::Region *> regions;
const db::Connectivity &conn = l2ndb->connectivity ();
for (db::Connectivity::layer_iterator layer = conn.begin_layers (); layer != conn.end_layers (); ++layer) {
for (db::Connectivity::all_layer_iterator layer = conn.begin_layers (); layer != conn.end_layers (); ++layer) {
db::LayerProperties lp = l2ndb->internal_layout ()->get_properties (*layer);
if (! lp.is_null ()) {
db::Region *region = l2ndb->layer_by_index (*layer);

View File

@ -235,6 +235,8 @@ NetlistBrowserPage::NetlistBrowserPage (QWidget * /*parent*/)
connect (actionExportAll, SIGNAL (triggered ()), this, SLOT (export_all ()));
connect (actionExportSelected, SIGNAL (triggered ()), this, SLOT (export_selected ()));
connect (mode_tab, SIGNAL (currentChanged (int)), this, SLOT (mode_tab_changed (int)));
forward->setEnabled (false);
backward->setEnabled (false);
}
@ -330,6 +332,13 @@ NetlistBrowserPage::eventFilter (QObject *watched, QEvent *event)
}
}
void
NetlistBrowserPage::mode_tab_changed (int)
{
clear_highlights ();
dm_update_highlights ();
}
void
NetlistBrowserPage::layer_list_changed (int)
{
@ -1072,11 +1081,10 @@ NetlistBrowserPage::set_db (db::LayoutToNetlist *l2ndb)
mode_tab->setTabEnabled (0, true);
mode_tab->setTabEnabled (1, is_lvsdb);
mode_tab->setTabEnabled (2, is_lvsdb);
mode_tab->setTabEnabled (3, is_lvsdb);
mode_tab->setTabEnabled (3, true);
#if QT_VERSION >= 0x50F00
mode_tab->setTabVisible (1, is_lvsdb);
mode_tab->setTabVisible (2, is_lvsdb);
mode_tab->setTabVisible (3, is_lvsdb);
#endif
if (is_lvsdb) {
@ -1166,7 +1174,7 @@ NetlistBrowserPage::setup_trees ()
if ((lvsdb && lvsdb->cross_ref ()) || (l2ndb && ! l2ndb->log_entries ().empty ())) {
NetlistLogModel *new_model = new NetlistLogModel (log_view, lvsdb->cross_ref (), l2ndb);
NetlistLogModel *new_model = new NetlistLogModel (log_view, lvsdb ? lvsdb->cross_ref () : 0, l2ndb);
delete log_view->model ();
log_view->setModel (new_model);
@ -1421,7 +1429,7 @@ NetlistBrowserPage::adjust_view ()
size_t cluster_id = net->cluster_id ();
const db::Connectivity &conn = mp_database->connectivity ();
for (db::Connectivity::layer_iterator layer = conn.begin_layers (); layer != conn.end_layers (); ++layer) {
for (db::Connectivity::all_layer_iterator layer = conn.begin_layers (); layer != conn.end_layers (); ++layer) {
db::Box layer_bbox;
db::recursive_cluster_shape_iterator<db::NetShape> shapes (mp_database->net_clusters (), *layer, cell_index, cluster_id);
@ -1580,7 +1588,7 @@ NetlistBrowserPage::produce_highlights_for_net (const db::Net *net, size_t &n_ma
tl::Color fallback_color = make_valid_color (m_colorizer.marker_color ());
const db::Connectivity &conn = mp_database->connectivity ();
for (db::Connectivity::layer_iterator layer = conn.begin_layers (); layer != conn.end_layers (); ++layer) {
for (db::Connectivity::all_layer_iterator layer = conn.begin_layers (); layer != conn.end_layers (); ++layer) {
db::LayerProperties lp = layout->get_properties (*layer);
std::map<db::LayerProperties, lay::LayerPropertiesConstIterator>::const_iterator display = display_by_lp.find (lp);

View File

@ -214,6 +214,7 @@ private slots:
void log_selection_changed ();
void browse_color_for_net ();
void select_color_for_net ();
void mode_tab_changed (int);
protected:
bool eventFilter (QObject *watched, QEvent *event);

View File

@ -29,7 +29,7 @@ namespace lay
static void build_top_circuit_list (const db::NetlistCrossReference *cross_ref, std::vector<NetlistCrossReferenceModel::circuit_pair> &top_level_circuits)
{
if (! top_level_circuits.empty ()) {
if (! top_level_circuits.empty () || ! cross_ref) {
return;
}
@ -139,48 +139,80 @@ size_t NetlistCrossReferenceModel::top_circuit_count () const
size_t NetlistCrossReferenceModel::child_circuit_count (const circuit_pair &circuits) const
{
if (! mp_cross_ref.get ()) {
return 0;
}
build_child_circuit_map (mp_cross_ref.get (), m_child_circuits);
return m_child_circuits [circuits].size ();
}
size_t NetlistCrossReferenceModel::net_count (const circuit_pair &circuits) const
{
if (! mp_cross_ref.get ()) {
return 0;
}
const db::NetlistCrossReference::PerCircuitData *data = mp_cross_ref->per_circuit_data_for (circuits);
return data ? data->nets.size () : 0;
}
size_t NetlistCrossReferenceModel::net_terminal_count (const net_pair &nets) const
{
if (! mp_cross_ref.get ()) {
return 0;
}
const db::NetlistCrossReference::PerNetData *data = mp_cross_ref->per_net_data_for (nets);
return data ? data->terminals.size () : 0;
}
size_t NetlistCrossReferenceModel::net_subcircuit_pin_count (const net_pair &nets) const
{
if (! mp_cross_ref.get ()) {
return 0;
}
const db::NetlistCrossReference::PerNetData *data = mp_cross_ref->per_net_data_for (nets);
return data ? data->subcircuit_pins.size () : 0;
}
size_t NetlistCrossReferenceModel::net_pin_count (const net_pair &nets) const
{
if (! mp_cross_ref.get ()) {
return 0;
}
const db::NetlistCrossReference::PerNetData *data = mp_cross_ref->per_net_data_for (nets);
return data ? data->pins.size () : 0;
}
size_t NetlistCrossReferenceModel::device_count (const circuit_pair &circuits) const
{
if (! mp_cross_ref.get ()) {
return 0;
}
const db::NetlistCrossReference::PerCircuitData *data = mp_cross_ref->per_circuit_data_for (circuits);
return data ? data->devices.size () : 0;
}
size_t NetlistCrossReferenceModel::pin_count (const circuit_pair &circuits) const
{
if (! mp_cross_ref.get ()) {
return 0;
}
const db::NetlistCrossReference::PerCircuitData *data = mp_cross_ref->per_circuit_data_for (circuits);
return data ? data->pins.size () : 0;
}
size_t NetlistCrossReferenceModel::subcircuit_count (const circuit_pair &circuits) const
{
if (! mp_cross_ref.get ()) {
return 0;
}
const db::NetlistCrossReference::PerCircuitData *data = mp_cross_ref->per_circuit_data_for (circuits);
return data ? data->subcircuits.size () : 0;
}
@ -218,6 +250,10 @@ struct DataGetter<const db::SubCircuit *>
template <class Pair>
static IndexedNetlistModel::circuit_pair get_parent_of (const Pair &pair, const db::NetlistCrossReference *cross_ref, std::map<Pair, IndexedNetlistModel::circuit_pair> &cache)
{
if (! cross_ref) {
return IndexedNetlistModel::circuit_pair ((const db::Circuit *) 0, (const db::Circuit *) 0);
}
typename std::map<Pair, IndexedNetlistModel::circuit_pair>::iterator i = cache.find (pair);
if (i == cache.end ()) {
@ -266,6 +302,7 @@ IndexedNetlistModel::circuit_pair NetlistCrossReferenceModel::parent_of (const I
std::pair<IndexedNetlistModel::circuit_pair, std::pair<NetlistCrossReferenceModel::Status, std::string> > NetlistCrossReferenceModel::top_circuit_from_index (size_t index) const
{
tl_assert (mp_cross_ref.get ());
build_top_circuit_list (mp_cross_ref.get (), m_top_level_circuits);
IndexedNetlistModel::circuit_pair cp = m_top_level_circuits [index];
@ -276,6 +313,7 @@ std::pair<IndexedNetlistModel::circuit_pair, std::pair<NetlistCrossReferenceMode
std::pair<IndexedNetlistModel::circuit_pair, std::pair<NetlistCrossReferenceModel::Status, std::string> > NetlistCrossReferenceModel::child_circuit_from_index (const circuit_pair &circuits, size_t index) const
{
tl_assert (mp_cross_ref.get ());
build_child_circuit_map (mp_cross_ref.get (), m_child_circuits);
IndexedNetlistModel::circuit_pair cp = m_child_circuits [circuits][index];
@ -286,6 +324,7 @@ std::pair<IndexedNetlistModel::circuit_pair, std::pair<NetlistCrossReferenceMode
std::pair<IndexedNetlistModel::circuit_pair, std::pair<NetlistCrossReferenceModel::Status, std::string> > NetlistCrossReferenceModel::circuit_from_index (size_t index) const
{
tl_assert (mp_cross_ref.get ());
IndexedNetlistModel::circuit_pair cp = mp_cross_ref->begin_circuits () [index];
const db::NetlistCrossReference::PerCircuitData *data = mp_cross_ref->per_circuit_data_for (cp);
tl_assert (data != 0);
@ -294,6 +333,7 @@ std::pair<IndexedNetlistModel::circuit_pair, std::pair<NetlistCrossReferenceMode
std::pair<IndexedNetlistModel::net_pair, std::pair<NetlistCrossReferenceModel::Status, std::string> > NetlistCrossReferenceModel::net_from_index (const circuit_pair &circuits, size_t index) const
{
tl_assert (mp_cross_ref.get ());
const db::NetlistCrossReference::PerCircuitData *data = mp_cross_ref->per_circuit_data_for (circuits);
tl_assert (data != 0);
return std::make_pair (data->nets [index].pair, std::make_pair (data->nets [index].status, data->nets [index].msg));
@ -301,11 +341,13 @@ std::pair<IndexedNetlistModel::net_pair, std::pair<NetlistCrossReferenceModel::S
const db::Net *NetlistCrossReferenceModel::second_net_for (const db::Net *first) const
{
tl_assert (mp_cross_ref.get ());
return mp_cross_ref->other_net_for (first);
}
const db::Circuit *NetlistCrossReferenceModel::second_circuit_for (const db::Circuit *first) const
{
tl_assert (mp_cross_ref.get ());
return mp_cross_ref->other_circuit_for (first);
}
@ -382,7 +424,7 @@ namespace {
void NetlistCrossReferenceModel::ensure_subcircuit_data_built () const
{
if (! m_per_subcircuit_data.empty ()) {
if (! m_per_subcircuit_data.empty () || ! mp_cross_ref.get ()) {
return;
}
@ -466,6 +508,7 @@ IndexedNetlistModel::net_subcircuit_pin_pair NetlistCrossReferenceModel::subcirc
IndexedNetlistModel::net_subcircuit_pin_pair NetlistCrossReferenceModel::net_subcircuit_pinref_from_index (const net_pair &nets, size_t index) const
{
tl_assert (mp_cross_ref.get ());
const db::NetlistCrossReference::PerNetData *data = mp_cross_ref->per_net_data_for (nets);
tl_assert (data != 0);
return data->subcircuit_pins [index];
@ -473,6 +516,7 @@ IndexedNetlistModel::net_subcircuit_pin_pair NetlistCrossReferenceModel::net_sub
IndexedNetlistModel::net_terminal_pair NetlistCrossReferenceModel::net_terminalref_from_index (const net_pair &nets, size_t index) const
{
tl_assert (mp_cross_ref.get ());
const db::NetlistCrossReference::PerNetData *data = mp_cross_ref->per_net_data_for (nets);
tl_assert (data != 0);
return data->terminals [index];
@ -480,6 +524,7 @@ IndexedNetlistModel::net_terminal_pair NetlistCrossReferenceModel::net_terminalr
IndexedNetlistModel::net_pin_pair NetlistCrossReferenceModel::net_pinref_from_index (const net_pair &nets, size_t index) const
{
tl_assert (mp_cross_ref.get ());
const db::NetlistCrossReference::PerNetData *data = mp_cross_ref->per_net_data_for (nets);
tl_assert (data != 0);
return data->pins [index];
@ -487,6 +532,7 @@ IndexedNetlistModel::net_pin_pair NetlistCrossReferenceModel::net_pinref_from_in
std::pair<IndexedNetlistModel::device_pair, std::pair<NetlistCrossReferenceModel::Status, std::string> > NetlistCrossReferenceModel::device_from_index (const circuit_pair &circuits, size_t index) const
{
tl_assert (mp_cross_ref.get ());
const db::NetlistCrossReference::PerCircuitData *data = mp_cross_ref->per_circuit_data_for (circuits);
tl_assert (data != 0);
return std::make_pair (data->devices [index].pair, std::make_pair (data->devices [index].status, data->devices [index].msg));
@ -494,6 +540,7 @@ std::pair<IndexedNetlistModel::device_pair, std::pair<NetlistCrossReferenceModel
std::pair<IndexedNetlistModel::pin_pair, std::pair<NetlistCrossReferenceModel::Status, std::string> > NetlistCrossReferenceModel::pin_from_index (const circuit_pair &circuits, size_t index) const
{
tl_assert (mp_cross_ref.get ());
const db::NetlistCrossReference::PerCircuitData *data = mp_cross_ref->per_circuit_data_for (circuits);
tl_assert (data != 0);
return std::make_pair (data->pins [index].pair, std::make_pair (data->pins [index].status, data->pins [index].msg));
@ -501,6 +548,7 @@ std::pair<IndexedNetlistModel::pin_pair, std::pair<NetlistCrossReferenceModel::S
std::pair<IndexedNetlistModel::subcircuit_pair, std::pair<NetlistCrossReferenceModel::Status, std::string> > NetlistCrossReferenceModel::subcircuit_from_index (const circuit_pair &circuits, size_t index) const
{
tl_assert (mp_cross_ref.get ());
const db::NetlistCrossReference::PerCircuitData *data = mp_cross_ref->per_circuit_data_for (circuits);
tl_assert (data != 0);
return std::make_pair (data->subcircuits [index].pair, std::make_pair (data->subcircuits [index].status, data->subcircuits [index].msg));
@ -536,6 +584,10 @@ static size_t get_index_of (const Pair &pair, Iter begin, Iter end, std::map<Pai
size_t NetlistCrossReferenceModel::circuit_index (const circuit_pair &circuits) const
{
if (! mp_cross_ref.get ()) {
return lay::no_netlist_index;
}
std::map<circuit_pair, size_t>::iterator i = m_index_of_circuits.find (circuits);
if (i == m_index_of_circuits.end ()) {
@ -562,6 +614,10 @@ size_t NetlistCrossReferenceModel::circuit_index (const circuit_pair &circuits)
size_t NetlistCrossReferenceModel::net_index (const net_pair &nets) const
{
if (! mp_cross_ref.get ()) {
return lay::no_netlist_index;
}
circuit_pair circuits = parent_of (nets);
const db::NetlistCrossReference::PerCircuitData *org_data = mp_cross_ref->per_circuit_data_for (circuits);
@ -575,6 +631,10 @@ size_t NetlistCrossReferenceModel::net_index (const net_pair &nets) const
size_t NetlistCrossReferenceModel::device_index (const device_pair &devices) const
{
if (! mp_cross_ref.get ()) {
return lay::no_netlist_index;
}
circuit_pair circuits = parent_of (devices);
const db::NetlistCrossReference::PerCircuitData *org_data = mp_cross_ref->per_circuit_data_for (circuits);
@ -588,6 +648,10 @@ size_t NetlistCrossReferenceModel::device_index (const device_pair &devices) con
size_t NetlistCrossReferenceModel::pin_index (const pin_pair &pins, const circuit_pair &circuits) const
{
if (! mp_cross_ref.get ()) {
return lay::no_netlist_index;
}
const db::NetlistCrossReference::PerCircuitData *org_data = mp_cross_ref->per_circuit_data_for (circuits);
if (! org_data) {
return lay::no_netlist_index;
@ -599,6 +663,10 @@ size_t NetlistCrossReferenceModel::pin_index (const pin_pair &pins, const circui
size_t NetlistCrossReferenceModel::subcircuit_index (const subcircuit_pair &subcircuits) const
{
if (! mp_cross_ref.get ()) {
return lay::no_netlist_index;
}
circuit_pair circuits = parent_of (subcircuits);
const db::NetlistCrossReference::PerCircuitData *org_data = mp_cross_ref->per_circuit_data_for (circuits);
@ -612,6 +680,10 @@ size_t NetlistCrossReferenceModel::subcircuit_index (const subcircuit_pair &subc
std::string NetlistCrossReferenceModel::circuit_pair_status_hint (const std::pair<IndexedNetlistModel::circuit_pair, std::pair<NetlistCrossReferenceModel::Status, std::string> > &cps) const
{
if (! mp_cross_ref.get ()) {
return std::string ();
}
std::string msg;
if (cps.second.first == db::NetlistCrossReference::Mismatch || cps.second.first == db::NetlistCrossReference::NoMatch) {
@ -655,6 +727,10 @@ std::string NetlistCrossReferenceModel::circuit_status_hint (size_t index) const
std::string NetlistCrossReferenceModel::child_circuit_status_hint (const circuit_pair &circuits, size_t index) const
{
if (! mp_cross_ref.get ()) {
return std::string ();
}
std::string msg;
std::pair<IndexedNetlistModel::circuit_pair, std::pair<NetlistCrossReferenceModel::Status, std::string> > cps = child_circuit_from_index (circuits, index);
@ -685,6 +761,10 @@ std::string NetlistCrossReferenceModel::child_circuit_status_hint (const circuit
std::string NetlistCrossReferenceModel::net_status_hint (const circuit_pair &circuits, size_t index) const
{
if (! mp_cross_ref.get ()) {
return std::string ();
}
std::string msg;
std::pair<IndexedNetlistModel::net_pair, std::pair<NetlistCrossReferenceModel::Status, std::string> > cps = net_from_index (circuits, index);
@ -713,6 +793,10 @@ std::string NetlistCrossReferenceModel::net_status_hint (const circuit_pair &cir
std::string NetlistCrossReferenceModel::device_status_hint (const circuit_pair &circuits, size_t index) const
{
if (! mp_cross_ref.get ()) {
return std::string ();
}
std::string msg;
std::pair<IndexedNetlistModel::device_pair, std::pair<NetlistCrossReferenceModel::Status, std::string> > cps = device_from_index (circuits, index);
@ -748,6 +832,10 @@ std::string NetlistCrossReferenceModel::device_status_hint (const circuit_pair &
std::string NetlistCrossReferenceModel::pin_status_hint (const circuit_pair &circuits, size_t index) const
{
if (! mp_cross_ref.get ()) {
return std::string ();
}
std::string msg;
std::pair<IndexedNetlistModel::pin_pair, std::pair<NetlistCrossReferenceModel::Status, std::string> > cps = pin_from_index (circuits, index);
@ -772,6 +860,10 @@ std::string NetlistCrossReferenceModel::pin_status_hint (const circuit_pair &cir
std::string NetlistCrossReferenceModel::subcircuit_status_hint (const circuit_pair &circuits, size_t index) const
{
if (! mp_cross_ref.get ()) {
return std::string ();
}
std::string msg;
std::pair<IndexedNetlistModel::subcircuit_pair, std::pair<NetlistCrossReferenceModel::Status, std::string> > cps = subcircuit_from_index (circuits, index);

View File

@ -301,3 +301,38 @@ TEST(40_DeviceExtractorErrors)
run_test (_this, "custom_resistors", "custom_resistors.gds", true, false /*no LVS*/);
}
// Basic soft connection
TEST(50_BasicSoftConnection)
{
run_test (_this, "soft_connect1", "soft_connect1.gds", true, false /*no LVS*/);
}
// No errors
TEST(51_SoftConnectionNoErrors)
{
run_test (_this, "soft_connect2", "soft_connect2.gds", true, false /*no LVS*/);
}
// Simple hierarchy
TEST(52_SoftConnectionSimpleHierarchy)
{
run_test (_this, "soft_connect3", "soft_connect3.gds", true, false /*no LVS*/);
}
// Soft connected nets from different subcircuits
TEST(53_SoftConnectionFromSubcircuits)
{
run_test (_this, "soft_connect4", "soft_connect4.gds", true, false /*no LVS*/);
}
// Soft connected nets from different subcircuits (propagated)
TEST(54_SoftConnectionFromSubcircuits2)
{
run_test (_this, "soft_connect5", "soft_connect5.gds", true, false /*no LVS*/);
}
// Level 2 soft connection
TEST(55_SoftConnectionSecondLevel)
{
run_test (_this, "soft_connect6", "soft_connect6.gds", true, false /*no LVS*/);
}

View File

@ -97,6 +97,12 @@ TEST(2_fullWithAlign)
run_test (_this, "vexriscv_align.lvs", "vexriscv.cir.gz", "vexriscv.oas.gz");
}
TEST(3_fullSoft)
{
test_is_long_runner ();
run_test (_this, "vexriscv_soft.lvs", "vexriscv.cir.gz", "vexriscv.oas.gz");
}
TEST(10_private)
{
// test_is_long_runner ();

View File

@ -102,6 +102,7 @@ RUBYTEST (dbEdgePairTest, "dbEdgePairTest.rb")
RUBYTEST (dbEdgesTest, "dbEdgesTest.rb")
RUBYTEST (dbEdgeTest, "dbEdgeTest.rb")
RUBYTEST (dbGlyphs, "dbGlyphs.rb")
RUBYTEST (dbHierNetworkProcessorTests, "dbHierNetworkProcessorTests.rb")
RUBYTEST (dbInstanceTest, "dbInstanceTest.rb")
RUBYTEST (dbInstElementTest, "dbInstElementTest.rb")
RUBYTEST (dbLayerMapping, "dbLayerMapping.rb")

BIN
testdata/algo/soft_connections.gds vendored Normal file

Binary file not shown.

BIN
testdata/algo/soft_connections_au.gds vendored Normal file

Binary file not shown.

View File

@ -29,7 +29,7 @@ J(
G(l14 SUBSTRATE)
H(W B('Must-connect nets GND must be connected further up in the hierarchy - this is an error at chip top level') C(INVCHAIN) X('must-connect'))
H(W B('Must-connect nets R must be connected further up in the hierarchy - this is an error at chip top level') C(INVCHAIN) X('must-connect'))
H(W B('Must-connect nets R of circuit INV2 must be connected further up in the hierarchy - this is an error at chip top level.\nInstance path: INVCHAIN:$1[r0 *1 0,0]/INV2') C(INVCHAIN) X('must-connect') Q('(0,0;0,9.2;3,9.2;3,0)'))
H(W B('Must-connect nets R of circuit INV2 must be connected further up in the hierarchy - this is an error at chip top level.\nInstance path: INVCHAIN/INV2[r0 0,0]:$1') C(INVCHAIN) X('must-connect') Q('(0,0;0,9.2;3,9.2;3,0)'))
K(PMOS MOS3)
K(NMOS MOS3)
D(D$PMOS PMOS

View File

@ -29,7 +29,7 @@ J(
G(l14 SUBSTRATE)
H(W B('Must-connect nets GND must be connected further up in the hierarchy - this is an error at chip top level') C(INVCHAIN) X('must-connect'))
H(W B('Must-connect nets R must be connected further up in the hierarchy - this is an error at chip top level') C(INVCHAIN) X('must-connect'))
H(W B('Must-connect nets R of circuit INV2 must be connected further up in the hierarchy - this is an error at chip top level.\nInstance path: INVCHAIN:$1[r0 *1 0,0]/INV2') C(INVCHAIN) X('must-connect') Q('(0,0;0,9.2;3,9.2;3,0)'))
H(W B('Must-connect nets R of circuit INV2 must be connected further up in the hierarchy - this is an error at chip top level.\nInstance path: INVCHAIN/INV2[r0 0,0]:$1') C(INVCHAIN) X('must-connect') Q('(0,0;0,9.2;3,9.2;3,0)'))
K(PMOS MOS3)
K(NMOS MOS3)
D(D$PMOS PMOS

View File

@ -29,7 +29,7 @@ J(
G(l14 SUBSTRATE)
H(W B('Must-connect nets VSSTOP must be connected further up in the hierarchy - this is an error at chip top level') C(TOP) X('must-connect'))
H(W B('Must-connect nets VDD must be connected further up in the hierarchy - this is an error at chip top level') C(TOP) X('must-connect'))
H(W B('Must-connect nets VSS of circuit INV2 must be connected further up in the hierarchy - this is an error at chip top level.\nInstance path: INVCHAIN:$2[r0 *1 0,0]/INV2') C(INVCHAIN) X('must-connect') Q('(0,0;0,9.2;3,9.2;3,0)'))
H(W B('Must-connect nets VSS of circuit INV2 must be connected further up in the hierarchy - this is an error at chip top level.\nInstance path: INVCHAIN/INV2[r0 0,0]:$2') C(INVCHAIN) X('must-connect') Q('(0,0;0,9.2;3,9.2;3,0)'))
K(PMOS MOS3)
K(NMOS MOS3)
D(D$PMOS PMOS

13
testdata/lvs/soft_connect1.cir vendored Normal file
View File

@ -0,0 +1,13 @@
* Extracted by KLayout
.SUBCKT TOP A Q VDD SUBSTRATE|VSS
X$1 SUBSTRATE|VSS VDD VDD \$1 Q SUBSTRATE|VSS INV
X$2 SUBSTRATE|VSS VDD VDD A \$1 SUBSTRATE|VSS INV
.ENDS TOP
.SUBCKT INV \$1 \$2 \$3 \$4 \$5 SUBSTRATE
M$1 \$2 \$4 \$5 \$3 PMOS L=0.25U W=0.95U AS=0.73625P AD=0.73625P PS=3.45U
+ PD=3.45U
M$2 \$1 \$4 \$5 SUBSTRATE NMOS L=0.25U W=0.95U AS=0.73625P AD=0.73625P PS=3.45U
+ PD=3.45U
.ENDS INV

BIN
testdata/lvs/soft_connect1.gds vendored Normal file

Binary file not shown.

217
testdata/lvs/soft_connect1.l2n vendored Normal file
View File

@ -0,0 +1,217 @@
#%l2n-klayout
W(TOP)
U(0.001)
L(l3 '1/0')
L(l4 '3/0')
L(l15 '3/1')
L(l8 '4/0')
L(l11 '5/0')
L(l12 '6/0')
L(l16 '6/1')
L(l13 '7/0')
L(l14 '8/0')
L(l17 '8/1')
L(l7)
L(l10)
L(l2)
L(l9)
L(l6)
C(l3 l3 l10)
C(l4 l4 l15 l11)
C(l15 l4 l15)
C(l8 l8 l12 l10 l2 l9 l6)
CS(l8 l10 l2 l9 l6)
C(l11 l4 l11 l12)
CS(l11 l4)
C(l12 l8 l11 l12 l16 l13)
C(l16 l12 l16)
C(l13 l12 l13 l14)
C(l14 l13 l14 l17)
C(l17 l14 l17)
C(l7 l7)
C(l10 l3 l8 l10)
CS(l10 l3)
C(l2 l8 l2)
C(l9 l8 l9)
C(l6 l8 l6)
G(l7 SUBSTRATE)
G(l9 SUBSTRATE)
GS(l9 SUBSTRATE)
H(W B('Net with incomplete wiring (soft-connected partial nets)') C(TOP) X('soft-connection-check'))
H(B('\tPartial net #1: TOP - VDD') C(TOP) Q('(0.6,3.95;0.6,4.85;4.5,4.85;4.5,3.95)'))
H(B('\tPartial net #2: TOP - $I4') C(TOP) Q('(5.1,3.95;5.1,4.85;9,4.85;9,3.95)'))
H(W B('Net with incomplete wiring (soft-connected partial nets)') C(TOP) X('soft-connection-check'))
H(B('\tPartial net #1: TOP - VSS') C(TOP) Q('(0.6,1.15;0.6,2.05;4.5,2.05;4.5,1.15)'))
H(B('\tPartial net #2: TOP - $I1') C(TOP) Q('(5.1,1.15;5.1,2.05;9,2.05;9,1.15)'))
K(PMOS MOS4)
K(NMOS MOS4)
D(D$PMOS PMOS
T(S
R(l2 (-900 -475) (775 950))
)
T(G
R(l4 (-125 -475) (250 950))
)
T(D
R(l2 (125 -475) (775 950))
)
T(B
R(l3 (-125 -475) (250 950))
)
)
D(D$NMOS NMOS
T(S
R(l6 (-900 -475) (775 950))
)
T(G
R(l4 (-125 -475) (250 950))
)
T(D
R(l6 (125 -475) (775 950))
)
T(B
R(l7 (-125 -475) (250 950))
)
)
X(INV
R((-1500 -800) (3000 4600))
N(1
R(l8 (290 -310) (220 220))
R(l8 (-220 180) (220 220))
R(l12 (-290 -690) (360 760))
R(l13 (-305 -705) (250 250))
R(l13 (-250 150) (250 250))
R(l14 (-2025 -775) (3000 900))
R(l6 (-1375 -925) (775 950))
)
N(2
R(l8 (290 2490) (220 220))
R(l8 (-220 180) (220 220))
R(l12 (-290 -690) (360 760))
R(l13 (-305 -705) (250 250))
R(l13 (-250 150) (250 250))
R(l14 (-2025 -775) (3000 900))
R(l2 (-1375 -925) (775 950))
)
N(3
R(l3 (-1500 1800) (3000 2000))
)
N(4
R(l4 (-125 -250) (250 2500))
R(l4 (-250 -3050) (250 1600))
R(l4 (-250 1200) (250 1600))
)
N(5
R(l8 (-510 -310) (220 220))
R(l8 (-220 180) (220 220))
R(l8 (-220 2180) (220 220))
R(l8 (-220 180) (220 220))
R(l12 (-290 -3530) (360 2840))
R(l12 (-360 -2800) (360 760))
R(l12 (-360 2040) (360 760))
R(l2 (-680 -855) (775 950))
R(l6 (-775 -3750) (775 950))
)
N(6 I(SUBSTRATE))
P(1)
P(2)
P(3)
P(4)
P(5)
P(6 I(SUBSTRATE))
D(1 D$PMOS
Y(0 2800)
E(L 0.25)
E(W 0.95)
E(AS 0.73625)
E(AD 0.73625)
E(PS 3.45)
E(PD 3.45)
T(S 5)
T(G 4)
T(D 2)
T(B 3)
)
D(2 D$NMOS
Y(0 0)
E(L 0.25)
E(W 0.95)
E(AS 0.73625)
E(AD 0.73625)
E(PS 3.45)
E(PD 3.45)
T(S 5)
T(G 4)
T(D 1)
T(B 6)
)
)
X(TOP
R((600 800) (8880 4600))
N(1
R(l4 (2920 2600) (2880 400))
R(l11 (-300 -300) (200 200))
R(l12 (-300 -300) (690 400))
)
N(2 I(A)
R(l4 (6600 2600) (2880 400))
R(l15 (-2380 -200) (0 0))
)
N(3 I(Q)
R(l12 (1810 2600) (690 400))
R(l16 (-400 -200) (0 0))
)
N(4 I(VDD)
R(l3 (4000 3400) (1600 2000))
R(l3 (-5000 -2000) (1000 2000))
R(l3 (6400 -2000) (1000 2000))
R(l8 (-8000 -900) (200 200))
R(l8 (-200 -600) (200 200))
R(l8 (7200 200) (200 200))
R(l8 (-200 -600) (200 200))
R(l12 (-7900 -350) (800 900))
R(l12 (6600 -900) (800 900))
R(l13 (-7900 -350) (200 200))
R(l13 (-200 -600) (200 200))
R(l13 (7200 200) (200 200))
R(l13 (-200 -600) (200 200))
R(l14 (-8000 -350) (1000 900))
R(l14 (6400 -900) (1000 900))
R(l17 (-4800 -450) (0 0))
)
N(5 I('SUBSTRATE,VSS')
R(l8 (1000 1700) (200 200))
R(l8 (-200 -600) (200 200))
R(l8 (7200 200) (200 200))
R(l8 (-200 -600) (200 200))
R(l12 (-7900 -350) (800 900))
R(l12 (6600 -900) (800 900))
R(l13 (-7900 -350) (200 200))
R(l13 (-200 -600) (200 200))
R(l13 (7200 200) (200 200))
R(l13 (-200 -600) (200 200))
R(l14 (-8000 -350) (1000 900))
R(l14 (6400 -900) (1000 900))
R(l17 (-4800 -550) (0 0))
)
P(2 I(A))
P(3 I(Q))
P(4 I(VDD))
P(5 I('SUBSTRATE,VSS'))
X(1 INV Y(3000 1600)
P(0 5)
P(1 4)
P(2 4)
P(3 1)
P(4 3)
P(5 5)
)
X(2 INV Y(6600 1600)
P(0 5)
P(1 4)
P(2 4)
P(3 2)
P(4 1)
P(5 5)
)
)

92
testdata/lvs/soft_connect1.lvs vendored Normal file
View File

@ -0,0 +1,92 @@
$lvs_test_source && source($lvs_test_source)
if $lvs_test_target_l2n
report_netlist($lvs_test_target_l2n)
else
report_netlist
end
writer = write_spice(true, false)
$lvs_test_target_cir && target_netlist($lvs_test_target_cir, writer, "Extracted by KLayout")
deep
# Drawing layers
nwell = input(1, 0)
active = input(2, 0)
nplus = input(2, 1)
pplus = input(2, 2)
poly = input(3, 0)
poly_lbl = input(3, 1)
diff_cont = input(4, 0)
poly_cont = input(5, 0)
metal1 = input(6, 0)
metal1_lbl = input(6, 1)
via1 = input(7, 0)
metal2 = input(8, 0)
metal2_lbl = input(8, 1)
# Bulk layer for terminal provisioning
bulk = polygon_layer
psd = nil
nsd = nil
# Computed layers
active_in_nwell = active & nwell
pactive = active_in_nwell & pplus
ntie = active_in_nwell & nplus
pgate = pactive & poly
psd = pactive - pgate
active_outside_nwell = active - nwell
nactive = active_outside_nwell & nplus
ptie = active_outside_nwell & pplus
ngate = nactive & poly
nsd = nactive - ngate
# Device extraction
# PMOS transistor device extraction
extract_devices(mos4("PMOS"), { "SD" => psd, "G" => pgate, "W" => nwell,
"tS" => psd, "tD" => psd, "tG" => poly })
# NMOS transistor device extraction
extract_devices(mos4("NMOS"), { "SD" => nsd, "G" => ngate, "W" => bulk,
"tS" => nsd, "tD" => nsd, "tG" => poly })
# Define connectivity for netlist extraction
# Inter-layer
soft_connect(diff_cont, psd)
soft_connect(diff_cont, nsd)
soft_connect(diff_cont, ptie)
soft_connect(diff_cont, ntie)
soft_connect(ntie, nwell)
soft_connect(poly_cont, poly)
connect(diff_cont, metal1)
connect(poly_cont, metal1)
connect(metal1, via1)
connect(via1, metal2)
# attach labels
connect(poly, poly_lbl)
connect(metal1, metal1_lbl)
connect(metal2, metal2_lbl)
# Global
connect_global(bulk, "SUBSTRATE")
soft_connect_global(ptie, "SUBSTRATE")
# Netlist section (NOTE: we only check log here)
netlist
netlist.simplify

13
testdata/lvs/soft_connect2.cir vendored Normal file
View File

@ -0,0 +1,13 @@
* Extracted by KLayout
.SUBCKT TOP A Q VDD SUBSTRATE|VSS
X$1 SUBSTRATE|VSS VDD VDD \$1 Q SUBSTRATE|VSS INV
X$2 SUBSTRATE|VSS VDD VDD A \$1 SUBSTRATE|VSS INV
.ENDS TOP
.SUBCKT INV \$1 \$2 \$3 \$4 \$5 SUBSTRATE
M$1 \$2 \$4 \$5 \$3 PMOS L=0.25U W=0.95U AS=0.73625P AD=0.73625P PS=3.45U
+ PD=3.45U
M$2 \$1 \$4 \$5 SUBSTRATE NMOS L=0.25U W=0.95U AS=0.73625P AD=0.73625P PS=3.45U
+ PD=3.45U
.ENDS INV

BIN
testdata/lvs/soft_connect2.gds vendored Normal file

Binary file not shown.

213
testdata/lvs/soft_connect2.l2n vendored Normal file
View File

@ -0,0 +1,213 @@
#%l2n-klayout
W(TOP)
U(0.001)
L(l3 '1/0')
L(l4 '3/0')
L(l15 '3/1')
L(l8 '4/0')
L(l11 '5/0')
L(l12 '6/0')
L(l16 '6/1')
L(l13 '7/0')
L(l14 '8/0')
L(l17 '8/1')
L(l7)
L(l10)
L(l2)
L(l9)
L(l6)
C(l3 l3 l10)
C(l4 l4 l15 l11)
C(l15 l4 l15)
C(l8 l8 l12 l10 l2 l9 l6)
CS(l8 l10 l2 l9 l6)
C(l11 l4 l11 l12)
CS(l11 l4)
C(l12 l8 l11 l12 l16 l13)
C(l16 l12 l16)
C(l13 l12 l13 l14)
C(l14 l13 l14 l17)
C(l17 l14 l17)
C(l7 l7)
C(l10 l3 l8 l10)
CS(l10 l3)
C(l2 l8 l2)
C(l9 l8 l9)
C(l6 l8 l6)
G(l7 SUBSTRATE)
G(l9 SUBSTRATE)
GS(l9 SUBSTRATE)
K(PMOS MOS4)
K(NMOS MOS4)
D(D$PMOS PMOS
T(S
R(l2 (-900 -475) (775 950))
)
T(G
R(l4 (-125 -475) (250 950))
)
T(D
R(l2 (125 -475) (775 950))
)
T(B
R(l3 (-125 -475) (250 950))
)
)
D(D$NMOS NMOS
T(S
R(l6 (-900 -475) (775 950))
)
T(G
R(l4 (-125 -475) (250 950))
)
T(D
R(l6 (125 -475) (775 950))
)
T(B
R(l7 (-125 -475) (250 950))
)
)
X(INV
R((-1500 -800) (3000 4600))
N(1
R(l8 (290 -310) (220 220))
R(l8 (-220 180) (220 220))
R(l12 (-290 -690) (360 760))
R(l13 (-305 -705) (250 250))
R(l13 (-250 150) (250 250))
R(l14 (-2025 -775) (3000 900))
R(l6 (-1375 -925) (775 950))
)
N(2
R(l8 (290 2490) (220 220))
R(l8 (-220 180) (220 220))
R(l12 (-290 -690) (360 760))
R(l13 (-305 -705) (250 250))
R(l13 (-250 150) (250 250))
R(l14 (-2025 -775) (3000 900))
R(l2 (-1375 -925) (775 950))
)
N(3
R(l3 (-1500 1800) (3000 2000))
)
N(4
R(l4 (-125 -250) (250 2500))
R(l4 (-250 -3050) (250 1600))
R(l4 (-250 1200) (250 1600))
)
N(5
R(l8 (-510 -310) (220 220))
R(l8 (-220 180) (220 220))
R(l8 (-220 2180) (220 220))
R(l8 (-220 180) (220 220))
R(l12 (-290 -3530) (360 2840))
R(l12 (-360 -2800) (360 760))
R(l12 (-360 2040) (360 760))
R(l2 (-680 -855) (775 950))
R(l6 (-775 -3750) (775 950))
)
N(6 I(SUBSTRATE))
P(1)
P(2)
P(3)
P(4)
P(5)
P(6 I(SUBSTRATE))
D(1 D$PMOS
Y(0 2800)
E(L 0.25)
E(W 0.95)
E(AS 0.73625)
E(AD 0.73625)
E(PS 3.45)
E(PD 3.45)
T(S 5)
T(G 4)
T(D 2)
T(B 3)
)
D(2 D$NMOS
Y(0 0)
E(L 0.25)
E(W 0.95)
E(AS 0.73625)
E(AD 0.73625)
E(PS 3.45)
E(PD 3.45)
T(S 5)
T(G 4)
T(D 1)
T(B 6)
)
)
X(TOP
R((600 800) (8880 4600))
N(1
R(l4 (2920 2600) (2880 400))
R(l11 (-300 -300) (200 200))
R(l12 (-300 -300) (690 400))
)
N(2 I(A)
R(l4 (6600 2600) (2880 400))
R(l15 (-2380 -200) (0 0))
)
N(3 I(Q)
R(l12 (1810 2600) (690 400))
R(l16 (-400 -200) (0 0))
)
N(4 I(VDD)
R(l3 (4000 3400) (1600 2000))
R(l3 (-5000 -2000) (1000 2000))
R(l3 (6400 -2000) (1000 2000))
R(l8 (-8000 -900) (200 200))
R(l8 (-200 -600) (200 200))
R(l8 (7200 200) (200 200))
R(l8 (-200 -600) (200 200))
R(l12 (-7900 -350) (800 900))
R(l12 (6600 -900) (800 900))
R(l13 (-7900 -350) (200 200))
R(l13 (-200 -600) (200 200))
R(l13 (7200 200) (200 200))
R(l13 (-200 -600) (200 200))
R(l14 (-4400 -350) (1200 900))
R(l14 (-4800 -900) (1000 900))
R(l14 (6400 -900) (1000 900))
R(l17 (-4800 -450) (0 0))
)
N(5 I('SUBSTRATE,VSS')
R(l8 (1000 1700) (200 200))
R(l8 (-200 -600) (200 200))
R(l8 (7200 200) (200 200))
R(l8 (-200 -600) (200 200))
R(l12 (-7900 -350) (800 900))
R(l12 (6600 -900) (800 900))
R(l13 (-7900 -350) (200 200))
R(l13 (-200 -600) (200 200))
R(l13 (7200 200) (200 200))
R(l13 (-200 -600) (200 200))
R(l14 (-4400 -350) (1200 900))
R(l14 (-4800 -900) (1000 900))
R(l14 (6400 -900) (1000 900))
R(l17 (-4800 -550) (0 0))
)
P(2 I(A))
P(3 I(Q))
P(4 I(VDD))
P(5 I('SUBSTRATE,VSS'))
X(1 INV Y(3000 1600)
P(0 5)
P(1 4)
P(2 4)
P(3 1)
P(4 3)
P(5 5)
)
X(2 INV Y(6600 1600)
P(0 5)
P(1 4)
P(2 4)
P(3 2)
P(4 1)
P(5 5)
)
)

92
testdata/lvs/soft_connect2.lvs vendored Normal file
View File

@ -0,0 +1,92 @@
$lvs_test_source && source($lvs_test_source)
if $lvs_test_target_l2n
report_netlist($lvs_test_target_l2n)
else
report_netlist
end
writer = write_spice(true, false)
$lvs_test_target_cir && target_netlist($lvs_test_target_cir, writer, "Extracted by KLayout")
deep
# Drawing layers
nwell = input(1, 0)
active = input(2, 0)
nplus = input(2, 1)
pplus = input(2, 2)
poly = input(3, 0)
poly_lbl = input(3, 1)
diff_cont = input(4, 0)
poly_cont = input(5, 0)
metal1 = input(6, 0)
metal1_lbl = input(6, 1)
via1 = input(7, 0)
metal2 = input(8, 0)
metal2_lbl = input(8, 1)
# Bulk layer for terminal provisioning
bulk = polygon_layer
psd = nil
nsd = nil
# Computed layers
active_in_nwell = active & nwell
pactive = active_in_nwell & pplus
ntie = active_in_nwell & nplus
pgate = pactive & poly
psd = pactive - pgate
active_outside_nwell = active - nwell
nactive = active_outside_nwell & nplus
ptie = active_outside_nwell & pplus
ngate = nactive & poly
nsd = nactive - ngate
# Device extraction
# PMOS transistor device extraction
extract_devices(mos4("PMOS"), { "SD" => psd, "G" => pgate, "W" => nwell,
"tS" => psd, "tD" => psd, "tG" => poly })
# NMOS transistor device extraction
extract_devices(mos4("NMOS"), { "SD" => nsd, "G" => ngate, "W" => bulk,
"tS" => nsd, "tD" => nsd, "tG" => poly })
# Define connectivity for netlist extraction
# Inter-layer
soft_connect(diff_cont, psd)
soft_connect(diff_cont, nsd)
soft_connect(diff_cont, ptie)
soft_connect(diff_cont, ntie)
soft_connect(ntie, nwell)
soft_connect(poly_cont, poly)
connect(diff_cont, metal1)
connect(poly_cont, metal1)
connect(metal1, via1)
connect(via1, metal2)
# attach labels
connect(poly, poly_lbl)
connect(metal1, metal1_lbl)
connect(metal2, metal2_lbl)
# Global
connect_global(bulk, "SUBSTRATE")
soft_connect_global(ptie, "SUBSTRATE")
# Netlist section (NOTE: we only check log here)
netlist
netlist.simplify

21
testdata/lvs/soft_connect3.cir vendored Normal file
View File

@ -0,0 +1,21 @@
* Extracted by KLayout
.SUBCKT TOP A Q VDD SUBSTRATE|VSS
X$1 SUBSTRATE|VSS VDD VDD \$1 Q SUBSTRATE|VSS INV
X$2 SUBSTRATE|VSS VDD VDD A \$1 SUBSTRATE|VSS INV
.ENDS TOP
.SUBCKT INV \$1 \$2 \$3 \$4 \$5 SUBSTRATE
X$1 \$5 \$1 \$4 SUBSTRATE NTRANS
X$2 \$5 \$2 \$4 \$3 PTRANS
.ENDS INV
.SUBCKT PTRANS \$1 \$3 \$5 \$I3
M$1 \$3 \$5 \$1 \$I3 PMOS L=0.25U W=0.95U AS=0.73625P AD=0.73625P PS=3.45U
+ PD=3.45U
.ENDS PTRANS
.SUBCKT NTRANS \$1 \$3 \$5 SUBSTRATE
M$1 \$3 \$5 \$1 SUBSTRATE NMOS L=0.25U W=0.95U AS=0.73625P AD=0.73625P PS=3.45U
+ PD=3.45U
.ENDS NTRANS

BIN
testdata/lvs/soft_connect3.gds vendored Normal file

Binary file not shown.

257
testdata/lvs/soft_connect3.l2n vendored Normal file
View File

@ -0,0 +1,257 @@
#%l2n-klayout
W(TOP)
U(0.001)
L(l3 '1/0')
L(l4 '3/0')
L(l15 '3/1')
L(l8 '4/0')
L(l11 '5/0')
L(l12 '6/0')
L(l16 '6/1')
L(l13 '7/0')
L(l14 '8/0')
L(l17 '8/1')
L(l7)
L(l10)
L(l2)
L(l9)
L(l6)
C(l3 l3 l10)
C(l4 l4 l15 l11)
C(l15 l4 l15)
C(l8 l8 l12 l10 l2 l9 l6)
CS(l8 l10 l2 l9 l6)
C(l11 l4 l11 l12)
CS(l11 l4)
C(l12 l8 l11 l12 l16 l13)
C(l16 l12 l16)
C(l13 l12 l13 l14)
C(l14 l13 l14 l17)
C(l17 l14 l17)
C(l7 l7)
C(l10 l3 l8 l10)
CS(l10 l3)
C(l2 l8 l2)
C(l9 l8 l9)
C(l6 l8 l6)
G(l7 SUBSTRATE)
G(l9 SUBSTRATE)
GS(l9 SUBSTRATE)
H(W B('Net with incomplete wiring (soft-connected partial nets)') C(TOP) X('soft-connection-check'))
H(B('\tPartial net #1: TOP - VDD') C(TOP) Q('(0.6,3.95;0.6,4.85;4.5,4.85;4.5,3.95)'))
H(B('\tPartial net #2: TOP - $I4') C(TOP) Q('(5.1,3.95;5.1,4.85;9,4.85;9,3.95)'))
H(W B('Net with incomplete wiring (soft-connected partial nets)') C(TOP) X('soft-connection-check'))
H(B('\tPartial net #1: TOP - VSS') C(TOP) Q('(0.6,1.15;0.6,2.05;4.5,2.05;4.5,1.15)'))
H(B('\tPartial net #2: TOP - $I1') C(TOP) Q('(5.1,1.15;5.1,2.05;9,2.05;9,1.15)'))
K(PMOS MOS4)
K(NMOS MOS4)
D(D$PMOS PMOS
T(S
R(l2 (-900 -475) (775 950))
)
T(G
R(l4 (-125 -475) (250 950))
)
T(D
R(l2 (125 -475) (775 950))
)
T(B
R(l3 (-125 -475) (250 950))
)
)
D(D$NMOS NMOS
T(S
R(l6 (-900 -475) (775 950))
)
T(G
R(l4 (-125 -475) (250 950))
)
T(D
R(l6 (125 -475) (775 950))
)
T(B
R(l7 (-125 -475) (250 950))
)
)
X(NTRANS
R((-1000 -800) (2000 1600))
N(1
R(l8 (-510 -310) (220 220))
R(l8 (-220 180) (220 220))
R(l12 (-290 -690) (360 760))
R(l6 (-680 -855) (775 950))
)
N(2
R(l8 (290 -310) (220 220))
R(l8 (-220 180) (220 220))
R(l12 (-290 -690) (360 760))
R(l6 (-455 -855) (775 950))
)
N(3
R(l4 (-125 -800) (250 1600))
)
N(4 I(SUBSTRATE))
P(1)
P(2)
P(3)
P(4 I(SUBSTRATE))
D(1 D$NMOS
Y(0 0)
E(L 0.25)
E(W 0.95)
E(AS 0.73625)
E(AD 0.73625)
E(PS 3.45)
E(PD 3.45)
T(S 1)
T(G 3)
T(D 2)
T(B 4)
)
)
X(PTRANS
R((-1000 -800) (2000 1600))
N(1
R(l8 (-510 -310) (220 220))
R(l8 (-220 180) (220 220))
R(l12 (-290 -690) (360 760))
R(l2 (-680 -855) (775 950))
)
N(2
R(l8 (290 -310) (220 220))
R(l8 (-220 180) (220 220))
R(l12 (-290 -690) (360 760))
R(l2 (-455 -855) (775 950))
)
N(3
R(l4 (-125 -800) (250 1600))
)
N(4)
P(1)
P(2)
P(3)
P(4)
D(1 D$PMOS
Y(0 0)
E(L 0.25)
E(W 0.95)
E(AS 0.73625)
E(AD 0.73625)
E(PS 3.45)
E(PD 3.45)
T(S 1)
T(G 3)
T(D 2)
T(B 4)
)
)
X(INV
R((-1500 -800) (3000 4600))
N(1
R(l13 (275 -325) (250 250))
R(l13 (-250 150) (250 250))
R(l14 (-2025 -775) (3000 900))
)
N(2
R(l13 (275 2475) (250 250))
R(l13 (-250 150) (250 250))
R(l14 (-2025 -775) (3000 900))
)
N(3
R(l3 (-1500 1800) (3000 2000))
)
N(4
R(l4 (-125 -250) (250 2500))
)
N(5
R(l12 (-580 -420) (360 2840))
)
N(6 I(SUBSTRATE))
P(1)
P(2)
P(3)
P(4)
P(5)
P(6 I(SUBSTRATE))
X(1 NTRANS Y(0 0)
P(0 5)
P(1 1)
P(2 4)
P(3 6)
)
X(2 PTRANS Y(0 2800)
P(0 5)
P(1 2)
P(2 4)
P(3 3)
)
)
X(TOP
R((600 800) (8880 4600))
N(1
R(l4 (2920 2600) (2880 400))
R(l11 (-300 -300) (200 200))
R(l12 (-300 -300) (690 400))
)
N(2 I(A)
R(l4 (6600 2600) (2880 400))
R(l15 (-2380 -200) (0 0))
)
N(3 I(Q)
R(l12 (1810 2600) (690 400))
R(l16 (-400 -200) (0 0))
)
N(4 I(VDD)
R(l3 (4000 3400) (1600 2000))
R(l3 (-5000 -2000) (1000 2000))
R(l3 (6400 -2000) (1000 2000))
R(l8 (-8000 -900) (200 200))
R(l8 (-200 -600) (200 200))
R(l8 (7200 200) (200 200))
R(l8 (-200 -600) (200 200))
R(l12 (-7900 -350) (800 900))
R(l12 (6600 -900) (800 900))
R(l13 (-7900 -350) (200 200))
R(l13 (-200 -600) (200 200))
R(l13 (7200 200) (200 200))
R(l13 (-200 -600) (200 200))
R(l14 (-8000 -350) (1000 900))
R(l14 (6400 -900) (1000 900))
R(l17 (-4800 -450) (0 0))
)
N(5 I('SUBSTRATE,VSS')
R(l8 (1000 1700) (200 200))
R(l8 (-200 -600) (200 200))
R(l8 (7200 200) (200 200))
R(l8 (-200 -600) (200 200))
R(l12 (-7900 -350) (800 900))
R(l12 (6600 -900) (800 900))
R(l13 (-7900 -350) (200 200))
R(l13 (-200 -600) (200 200))
R(l13 (7200 200) (200 200))
R(l13 (-200 -600) (200 200))
R(l14 (-8000 -350) (1000 900))
R(l14 (6400 -900) (1000 900))
R(l17 (-4800 -550) (0 0))
)
P(2 I(A))
P(3 I(Q))
P(4 I(VDD))
P(5 I('SUBSTRATE,VSS'))
X(1 INV Y(3000 1600)
P(0 5)
P(1 4)
P(2 4)
P(3 1)
P(4 3)
P(5 5)
)
X(2 INV Y(6600 1600)
P(0 5)
P(1 4)
P(2 4)
P(3 2)
P(4 1)
P(5 5)
)
)

92
testdata/lvs/soft_connect3.lvs vendored Normal file
View File

@ -0,0 +1,92 @@
$lvs_test_source && source($lvs_test_source)
if $lvs_test_target_l2n
report_netlist($lvs_test_target_l2n)
else
report_netlist
end
writer = write_spice(true, false)
$lvs_test_target_cir && target_netlist($lvs_test_target_cir, writer, "Extracted by KLayout")
deep
# Drawing layers
nwell = input(1, 0)
active = input(2, 0)
nplus = input(2, 1)
pplus = input(2, 2)
poly = input(3, 0)
poly_lbl = input(3, 1)
diff_cont = input(4, 0)
poly_cont = input(5, 0)
metal1 = input(6, 0)
metal1_lbl = input(6, 1)
via1 = input(7, 0)
metal2 = input(8, 0)
metal2_lbl = input(8, 1)
# Bulk layer for terminal provisioning
bulk = polygon_layer
psd = nil
nsd = nil
# Computed layers
active_in_nwell = active & nwell
pactive = active_in_nwell & pplus
ntie = active_in_nwell & nplus
pgate = pactive & poly
psd = pactive - pgate
active_outside_nwell = active - nwell
nactive = active_outside_nwell & nplus
ptie = active_outside_nwell & pplus
ngate = nactive & poly
nsd = nactive - ngate
# Device extraction
# PMOS transistor device extraction
extract_devices(mos4("PMOS"), { "SD" => psd, "G" => pgate, "W" => nwell,
"tS" => psd, "tD" => psd, "tG" => poly })
# NMOS transistor device extraction
extract_devices(mos4("NMOS"), { "SD" => nsd, "G" => ngate, "W" => bulk,
"tS" => nsd, "tD" => nsd, "tG" => poly })
# Define connectivity for netlist extraction
# Inter-layer
soft_connect(diff_cont, psd)
soft_connect(diff_cont, nsd)
soft_connect(diff_cont, ptie)
soft_connect(diff_cont, ntie)
soft_connect(ntie, nwell)
soft_connect(poly_cont, poly)
connect(diff_cont, metal1)
connect(poly_cont, metal1)
connect(metal1, via1)
connect(via1, metal2)
# attach labels
connect(poly, poly_lbl)
connect(metal1, metal1_lbl)
connect(metal2, metal2_lbl)
# Global
connect_global(bulk, "SUBSTRATE")
soft_connect_global(ptie, "SUBSTRATE")
# Netlist section (NOTE: we only check log here)
netlist
netlist.simplify

13
testdata/lvs/soft_connect4.cir vendored Normal file
View File

@ -0,0 +1,13 @@
* Extracted by KLayout
.SUBCKT TOP A Q SUBSTRATE
X$1 \$5 \$1 Q SUBSTRATE INV
X$2 \$5 A \$1 SUBSTRATE INV
.ENDS TOP
.SUBCKT INV \$2 \$4 \$5 SUBSTRATE
M$1 \$2 \$4 \$5 \$2 PMOS L=0.25U W=0.95U AS=0.73625P AD=0.73625P PS=3.45U
+ PD=3.45U
M$2 SUBSTRATE \$4 \$5 SUBSTRATE NMOS L=0.25U W=0.95U AS=0.73625P AD=0.73625P
+ PS=3.45U PD=3.45U
.ENDS INV

BIN
testdata/lvs/soft_connect4.gds vendored Normal file

Binary file not shown.

192
testdata/lvs/soft_connect4.l2n vendored Normal file
View File

@ -0,0 +1,192 @@
#%l2n-klayout
W(TOP)
U(0.001)
L(l3 '1/0')
L(l4 '3/0')
L(l15 '3/1')
L(l8 '4/0')
L(l11 '5/0')
L(l12 '6/0')
L(l16 '6/1')
L(l13 '7/0')
L(l14 '8/0')
L(l17)
L(l7)
L(l10)
L(l2)
L(l9)
L(l6)
C(l3 l3 l10)
C(l4 l4 l15 l11)
C(l15 l4 l15)
C(l8 l8 l12 l10 l2 l9 l6)
CS(l8 l10 l2 l9 l6)
C(l11 l4 l11 l12)
CS(l11 l4)
C(l12 l8 l11 l12 l16 l13)
C(l16 l12 l16)
C(l13 l12 l13 l14)
C(l14 l13 l14 l17)
C(l17 l14 l17)
C(l7 l7)
C(l10 l3 l8 l10)
CS(l10 l3)
C(l2 l8 l2)
C(l9 l8 l9)
C(l6 l8 l6)
G(l7 SUBSTRATE)
G(l9 SUBSTRATE)
GS(l9 SUBSTRATE)
H(W B('Net with incomplete wiring (soft-connected partial nets)') C(TOP) X('soft-connection-check'))
H(B('\tPartial net #1: TOP/INV[r0 3,1.6]:$1 - $2') C(TOP) Q('(1.5,3.95;1.5,4.85;5.3,4.85;5.3,3.95)'))
H(B('\tPartial net #2: TOP/INV[r0 7.7,1.6]:$2 - $2') C(TOP) Q('(6.2,3.95;6.2,4.85;10,4.85;10,3.95)'))
H(W B('Net with incomplete wiring (soft-connected partial nets)') C(TOP) X('soft-connection-check'))
H(B('\tPartial net #1: TOP/INV[r0 3,1.6]:$1 - $1') C(TOP) Q('(1.5,1.15;1.5,2.05;5.3,2.05;5.3,1.15)'))
H(B('\tPartial net #2: TOP/INV[r0 7.7,1.6]:$2 - $1') C(TOP) Q('(6.2,1.15;6.2,2.05;10,2.05;10,1.15)'))
K(PMOS MOS4)
K(NMOS MOS4)
D(D$PMOS PMOS
T(S
R(l2 (-900 -475) (775 950))
)
T(G
R(l4 (-125 -475) (250 950))
)
T(D
R(l2 (125 -475) (775 950))
)
T(B
R(l3 (-125 -475) (250 950))
)
)
D(D$NMOS NMOS
T(S
R(l6 (-900 -475) (775 950))
)
T(G
R(l4 (-125 -475) (250 950))
)
T(D
R(l6 (125 -475) (775 950))
)
T(B
R(l7 (-125 -475) (250 950))
)
)
X(INV
R((-1500 -800) (3800 4600))
N(1 I(SUBSTRATE)
R(l8 (1700 100) (200 200))
R(l8 (-200 -600) (200 200))
R(l8 (-1610 -210) (220 220))
R(l8 (-220 180) (220 220))
R(l12 (890 -760) (800 900))
R(l12 (-1980 -830) (360 760))
R(l13 (-305 -705) (250 250))
R(l13 (-250 150) (250 250))
R(l13 (1175 -225) (200 200))
R(l13 (-200 -600) (200 200))
R(l14 (-3400 -350) (3000 900))
R(l14 (-200 -900) (1000 900))
R(l6 (-2175 -925) (775 950))
)
N(2
R(l3 (-1500 1800) (3000 2000))
R(l3 (-200 -2000) (1000 2000))
R(l8 (-2010 -1310) (220 220))
R(l8 (-220 180) (220 220))
R(l8 (1190 -210) (200 200))
R(l8 (-200 -600) (200 200))
R(l12 (-1680 -280) (360 760))
R(l12 (820 -830) (800 900))
R(l13 (-1925 -775) (250 250))
R(l13 (-250 150) (250 250))
R(l13 (1175 -225) (200 200))
R(l13 (-200 -600) (200 200))
R(l14 (-3400 -350) (3000 900))
R(l14 (-200 -900) (1000 900))
R(l10 (-700 -950) (400 1000))
R(l2 (-1875 -975) (775 950))
)
N(3
R(l4 (-125 -250) (250 2500))
R(l4 (-250 -3050) (250 1600))
R(l4 (-250 1200) (250 1600))
)
N(4
R(l8 (-510 -310) (220 220))
R(l8 (-220 180) (220 220))
R(l8 (-220 2180) (220 220))
R(l8 (-220 180) (220 220))
R(l12 (-290 -3530) (360 2840))
R(l12 (-360 -2800) (360 760))
R(l12 (-360 2040) (360 760))
R(l2 (-680 -855) (775 950))
R(l6 (-775 -3750) (775 950))
)
P(2)
P(3)
P(4)
P(1 I(SUBSTRATE))
D(1 D$PMOS
Y(0 2800)
E(L 0.25)
E(W 0.95)
E(AS 0.73625)
E(AD 0.73625)
E(PS 3.45)
E(PD 3.45)
T(S 4)
T(G 3)
T(D 2)
T(B 2)
)
D(2 D$NMOS
Y(0 0)
E(L 0.25)
E(W 0.95)
E(AS 0.73625)
E(AD 0.73625)
E(PS 3.45)
E(PD 3.45)
T(S 4)
T(G 3)
T(D 1)
T(B 1)
)
)
X(TOP
R((1500 800) (9080 4600))
N(1
R(l4 (2920 2600) (3980 400))
R(l11 (-300 -300) (200 200))
R(l12 (-300 -300) (690 400))
)
N(2 I(A)
R(l4 (7700 2600) (2880 400))
R(l15 (-2380 -200) (0 0))
)
N(3 I(Q)
R(l12 (1810 2600) (690 400))
R(l16 (-400 -200) (0 0))
)
N(4
R(l3 (4000 3400) (2700 2000))
)
N(5 I(SUBSTRATE))
P(2 I(A))
P(3 I(Q))
P(5 I(SUBSTRATE))
X(1 INV Y(3000 1600)
P(0 4)
P(1 1)
P(2 3)
P(3 5)
)
X(2 INV Y(7700 1600)
P(0 4)
P(1 2)
P(2 1)
P(3 5)
)
)

93
testdata/lvs/soft_connect4.lvs vendored Normal file
View File

@ -0,0 +1,93 @@
$lvs_test_source && source($lvs_test_source)
if $lvs_test_target_l2n
report_netlist($lvs_test_target_l2n)
else
report_netlist
end
writer = write_spice(true, false)
$lvs_test_target_cir && target_netlist($lvs_test_target_cir, writer, "Extracted by KLayout")
deep
# Drawing layers
nwell = input(1, 0)
active = input(2, 0)
nplus = input(2, 1)
pplus = input(2, 2)
poly = input(3, 0)
poly_lbl = input(3, 1)
diff_cont = input(4, 0)
poly_cont = input(5, 0)
metal1 = input(6, 0)
metal1_lbl = input(6, 1)
via1 = input(7, 0)
metal2 = input(8, 0)
metal2_lbl = input(8, 1)
# Bulk layer for terminal provisioning
bulk = polygon_layer
psd = nil
nsd = nil
# Computed layers
active_in_nwell = active & nwell
pactive = active_in_nwell & pplus
ntie = active_in_nwell & nplus
pgate = pactive & poly
psd = pactive - pgate
active_outside_nwell = active - nwell
nactive = active_outside_nwell & nplus
ptie = active_outside_nwell & pplus
ngate = nactive & poly
nsd = nactive - ngate
# Device extraction
# PMOS transistor device extraction
extract_devices(mos4("PMOS"), { "SD" => psd, "G" => pgate, "W" => nwell,
"tS" => psd, "tD" => psd, "tG" => poly })
# NMOS transistor device extraction
extract_devices(mos4("NMOS"), { "SD" => nsd, "G" => ngate, "W" => bulk,
"tS" => nsd, "tD" => nsd, "tG" => poly })
# Define connectivity for netlist extraction
# Inter-layer
soft_connect(diff_cont, psd)
soft_connect(diff_cont, nsd)
soft_connect(diff_cont, ptie)
soft_connect(diff_cont, ntie)
soft_connect(ntie, nwell)
soft_connect(poly_cont, poly)
connect(diff_cont, metal1)
connect(poly_cont, metal1)
connect(metal1, via1)
connect(via1, metal2)
# attach labels
connect(poly, poly_lbl)
connect(metal1, metal1_lbl)
connect(metal2, metal2_lbl)
# Global
connect_global(bulk, "SUBSTRATE")
soft_connect_global(ptie, "SUBSTRATE")
# Netlist section (NOTE: we only check log here)
# for debugging: _make_soft_connection_diodes(true)
netlist
netlist.simplify

13
testdata/lvs/soft_connect5.cir vendored Normal file
View File

@ -0,0 +1,13 @@
* Extracted by KLayout
.SUBCKT TOP A Q VDD SUBSTRATE|VSS
X$1 SUBSTRATE|VSS VDD \$1 Q INV
X$2 SUBSTRATE|VSS VDD A \$1 INV
.ENDS TOP
.SUBCKT INV SUBSTRATE \$2 \$4 \$5
M$1 \$2 \$4 \$5 \$2 PMOS L=0.25U W=0.95U AS=0.73625P AD=0.73625P PS=3.45U
+ PD=3.45U
M$2 SUBSTRATE \$4 \$5 SUBSTRATE NMOS L=0.25U W=0.95U AS=0.73625P AD=0.73625P
+ PS=3.45U PD=3.45U
.ENDS INV

BIN
testdata/lvs/soft_connect5.gds vendored Normal file

Binary file not shown.

196
testdata/lvs/soft_connect5.l2n vendored Normal file
View File

@ -0,0 +1,196 @@
#%l2n-klayout
W(TOP)
U(0.001)
L(l3 '1/0')
L(l4 '3/0')
L(l15 '3/1')
L(l8 '4/0')
L(l11 '5/0')
L(l12 '6/0')
L(l16 '6/1')
L(l13 '7/0')
L(l14 '8/0')
L(l17 '8/1')
L(l7)
L(l10)
L(l2)
L(l9)
L(l6)
C(l3 l3 l10)
C(l4 l4 l15 l11)
C(l15 l4 l15)
C(l8 l8 l12 l10 l2 l9 l6)
CS(l8 l10 l2 l9 l6)
C(l11 l4 l11 l12)
CS(l11 l4)
C(l12 l8 l11 l12 l16 l13)
C(l16 l12 l16)
C(l13 l12 l13 l14)
C(l14 l13 l14 l17)
C(l17 l14 l17)
C(l7 l7)
C(l10 l3 l8 l10)
CS(l10 l3)
C(l2 l8 l2)
C(l9 l8 l9)
C(l6 l8 l6)
G(l7 SUBSTRATE)
G(l9 SUBSTRATE)
GS(l9 SUBSTRATE)
H(W B('Net with incomplete wiring (soft-connected partial nets)') C(TOP) X('soft-connection-check'))
H(B('\tPartial net #1: TOP - VDD') C(TOP) Q('(1.5,3.95;1.5,4.85;5.3,4.85;5.3,3.95)'))
H(B('\tPartial net #2: TOP - $I7') C(TOP) Q('(6.2,3.95;6.2,4.85;10,4.85;10,3.95)'))
H(W B('Net with incomplete wiring (soft-connected partial nets)') C(TOP) X('soft-connection-check'))
H(B('\tPartial net #1: TOP - VSS') C(TOP) Q('(1.5,1.15;1.5,2.05;5.3,2.05;5.3,1.15)'))
H(B('\tPartial net #2: TOP - $I6') C(TOP) Q('(6.2,1.15;6.2,2.05;10,2.05;10,1.15)'))
K(PMOS MOS4)
K(NMOS MOS4)
D(D$PMOS PMOS
T(S
R(l2 (-900 -475) (775 950))
)
T(G
R(l4 (-125 -475) (250 950))
)
T(D
R(l2 (125 -475) (775 950))
)
T(B
R(l3 (-125 -475) (250 950))
)
)
D(D$NMOS NMOS
T(S
R(l6 (-900 -475) (775 950))
)
T(G
R(l4 (-125 -475) (250 950))
)
T(D
R(l6 (125 -475) (775 950))
)
T(B
R(l7 (-125 -475) (250 950))
)
)
X(INV
R((-1500 -800) (3800 4600))
N(1 I(SUBSTRATE)
R(l8 (1700 100) (200 200))
R(l8 (-200 -600) (200 200))
R(l8 (-1610 -210) (220 220))
R(l8 (-220 180) (220 220))
R(l12 (890 -760) (800 900))
R(l12 (-1980 -830) (360 760))
R(l13 (-305 -705) (250 250))
R(l13 (-250 150) (250 250))
R(l13 (1175 -225) (200 200))
R(l13 (-200 -600) (200 200))
R(l14 (-3400 -350) (3000 900))
R(l14 (-200 -900) (1000 900))
R(l6 (-2175 -925) (775 950))
)
N(2
R(l3 (-1500 1800) (3000 2000))
R(l3 (-200 -2000) (1000 2000))
R(l8 (-2010 -1310) (220 220))
R(l8 (-220 180) (220 220))
R(l8 (1190 -210) (200 200))
R(l8 (-200 -600) (200 200))
R(l12 (-1680 -280) (360 760))
R(l12 (820 -830) (800 900))
R(l13 (-1925 -775) (250 250))
R(l13 (-250 150) (250 250))
R(l13 (1175 -225) (200 200))
R(l13 (-200 -600) (200 200))
R(l14 (-3400 -350) (3000 900))
R(l14 (-200 -900) (1000 900))
R(l10 (-700 -950) (400 1000))
R(l2 (-1875 -975) (775 950))
)
N(3
R(l4 (-125 -250) (250 2500))
R(l4 (-250 -3050) (250 1600))
R(l4 (-250 1200) (250 1600))
)
N(4
R(l8 (-510 -310) (220 220))
R(l8 (-220 180) (220 220))
R(l8 (-220 2180) (220 220))
R(l8 (-220 180) (220 220))
R(l12 (-290 -3530) (360 2840))
R(l12 (-360 -2800) (360 760))
R(l12 (-360 2040) (360 760))
R(l2 (-680 -855) (775 950))
R(l6 (-775 -3750) (775 950))
)
P(1 I(SUBSTRATE))
P(2)
P(3)
P(4)
D(1 D$PMOS
Y(0 2800)
E(L 0.25)
E(W 0.95)
E(AS 0.73625)
E(AD 0.73625)
E(PS 3.45)
E(PD 3.45)
T(S 4)
T(G 3)
T(D 2)
T(B 2)
)
D(2 D$NMOS
Y(0 0)
E(L 0.25)
E(W 0.95)
E(AS 0.73625)
E(AD 0.73625)
E(PS 3.45)
E(PD 3.45)
T(S 4)
T(G 3)
T(D 1)
T(B 1)
)
)
X(TOP
R((1500 800) (9080 4600))
N(1
R(l4 (2920 2600) (3980 400))
R(l11 (-300 -300) (200 200))
R(l12 (-300 -300) (690 400))
)
N(2 I(A)
R(l4 (7700 2600) (2880 400))
R(l15 (-2380 -200) (0 0))
)
N(3 I(Q)
R(l12 (1810 2600) (690 400))
R(l16 (-400 -200) (0 0))
)
N(4 I(VDD)
R(l3 (4000 3400) (2700 2000))
R(l17 (-2500 -1000) (0 0))
)
N(5 I('SUBSTRATE,VSS')
R(l17 (4200 1500) (0 0))
)
P(2 I(A))
P(3 I(Q))
P(4 I(VDD))
P(5 I('SUBSTRATE,VSS'))
X(1 INV Y(3000 1600)
P(0 5)
P(1 4)
P(2 1)
P(3 3)
)
X(2 INV Y(7700 1600)
P(0 5)
P(1 4)
P(2 2)
P(3 1)
)
)

93
testdata/lvs/soft_connect5.lvs vendored Normal file
View File

@ -0,0 +1,93 @@
$lvs_test_source && source($lvs_test_source)
if $lvs_test_target_l2n
report_netlist($lvs_test_target_l2n)
else
report_netlist
end
writer = write_spice(true, false)
$lvs_test_target_cir && target_netlist($lvs_test_target_cir, writer, "Extracted by KLayout")
deep
# Drawing layers
nwell = input(1, 0)
active = input(2, 0)
nplus = input(2, 1)
pplus = input(2, 2)
poly = input(3, 0)
poly_lbl = input(3, 1)
diff_cont = input(4, 0)
poly_cont = input(5, 0)
metal1 = input(6, 0)
metal1_lbl = input(6, 1)
via1 = input(7, 0)
metal2 = input(8, 0)
metal2_lbl = input(8, 1)
# Bulk layer for terminal provisioning
bulk = polygon_layer
psd = nil
nsd = nil
# Computed layers
active_in_nwell = active & nwell
pactive = active_in_nwell & pplus
ntie = active_in_nwell & nplus
pgate = pactive & poly
psd = pactive - pgate
active_outside_nwell = active - nwell
nactive = active_outside_nwell & nplus
ptie = active_outside_nwell & pplus
ngate = nactive & poly
nsd = nactive - ngate
# Device extraction
# PMOS transistor device extraction
extract_devices(mos4("PMOS"), { "SD" => psd, "G" => pgate, "W" => nwell,
"tS" => psd, "tD" => psd, "tG" => poly })
# NMOS transistor device extraction
extract_devices(mos4("NMOS"), { "SD" => nsd, "G" => ngate, "W" => bulk,
"tS" => nsd, "tD" => nsd, "tG" => poly })
# Define connectivity for netlist extraction
# Inter-layer
soft_connect(diff_cont, psd)
soft_connect(diff_cont, nsd)
soft_connect(diff_cont, ptie)
soft_connect(diff_cont, ntie)
soft_connect(ntie, nwell)
soft_connect(poly_cont, poly)
connect(diff_cont, metal1)
connect(poly_cont, metal1)
connect(metal1, via1)
connect(via1, metal2)
# attach labels
connect(poly, poly_lbl)
connect(metal1, metal1_lbl)
connect(metal2, metal2_lbl)
# Global
connect_global(bulk, "SUBSTRATE")
soft_connect_global(ptie, "SUBSTRATE")
# Netlist section (NOTE: we only check log here)
# for debugging: _make_soft_connection_diodes(true)
netlist
netlist.simplify

13
testdata/lvs/soft_connect6.cir vendored Normal file
View File

@ -0,0 +1,13 @@
* Extracted by KLayout
.SUBCKT TOP A Q VDD SUBSTRATE|VSS
X$1 Q SUBSTRATE|VSS VDD \$1 INV
X$2 \$1 SUBSTRATE|VSS VDD A INV
.ENDS TOP
.SUBCKT INV \$1 SUBSTRATE \$4 \$6
M$1 \$4 \$6 \$1 \$4 PMOS L=0.25U W=0.95U AS=1.02125P AD=0.73625P PS=4.05U
+ PD=3.45U
M$2 SUBSTRATE \$6 \$1 SUBSTRATE NMOS L=0.25U W=0.95U AS=0.73625P AD=0.73625P
+ PS=3.45U PD=3.45U
.ENDS INV

BIN
testdata/lvs/soft_connect6.gds vendored Normal file

Binary file not shown.

201
testdata/lvs/soft_connect6.l2n vendored Normal file
View File

@ -0,0 +1,201 @@
#%l2n-klayout
W(TOP)
U(0.001)
L(l3 '1/0')
L(l4 '3/0')
L(l15 '3/1')
L(l8 '4/0')
L(l11 '5/0')
L(l12 '6/0')
L(l16 '6/1')
L(l13 '7/0')
L(l14 '8/0')
L(l17 '8/1')
L(l7)
L(l10)
L(l2)
L(l9)
L(l6)
C(l3 l3 l10)
C(l4 l4 l15 l11)
C(l15 l4 l15)
C(l8 l8 l12 l10 l2 l9 l6)
CS(l8 l10 l2 l9 l6)
C(l11 l4 l11 l12)
CS(l11 l4)
C(l12 l8 l11 l12 l16 l13)
C(l16 l12 l16)
C(l13 l12 l13 l14)
C(l14 l13 l14 l17)
C(l17 l14 l17)
C(l7 l7)
C(l10 l3 l8 l10)
CS(l10 l3)
C(l2 l8 l2)
C(l9 l8 l9)
C(l6 l8 l6)
G(l7 SUBSTRATE)
G(l9 SUBSTRATE)
GS(l9 SUBSTRATE)
H(W B('Net with incomplete wiring (soft-connected partial nets)') C(TOP) X('soft-connection-check'))
H(B('\tPartial net #1: TOP - $2') C(TOP) Q('(6.54,2.6;6.54,4.78;6.9,4.78;6.9,2.6)'))
H(B('\tPartial net #2: TOP - $I3') C(TOP) Q('(7.12,1.18;7.12,4.78;7.48,4.78;7.48,1.18)'))
H(W B('Net with incomplete wiring (soft-connected partial nets)') C(TOP) X('soft-connection-check'))
H(B('\tPartial net #1: TOP - Q') C(TOP) Q('(1.81,1.18;1.81,4.78;2.78,4.78;2.78,1.18)'))
H(B('\tPartial net #2: TOP - $I2') C(TOP) Q('(1.84,4.02;1.84,4.78;2.2,4.78;2.2,4.02)'))
K(PMOS MOS4)
K(NMOS MOS4)
D(D$PMOS PMOS
T(S
R(l2 (-1200 -475) (1075 950))
)
T(G
R(l4 (-125 -475) (250 950))
)
T(D
R(l2 (125 -475) (775 950))
)
T(B
R(l3 (-125 -475) (250 950))
)
)
D(D$NMOS NMOS
T(S
R(l6 (-900 -475) (775 950))
)
T(G
R(l4 (-125 -475) (250 950))
)
T(D
R(l6 (125 -475) (775 950))
)
T(B
R(l7 (-125 -475) (250 950))
)
)
X(INV
R((-1500 -800) (3800 4600))
N(1
R(l8 (-1090 2490) (220 220))
R(l8 (-220 180) (220 220))
R(l8 (360 -3420) (220 220))
R(l8 (-220 180) (220 220))
R(l8 (-220 2180) (220 220))
R(l8 (-220 180) (220 220))
R(l12 (-870 -690) (360 760))
R(l12 (220 -3600) (360 2840))
R(l12 (-360 -2800) (360 760))
R(l12 (-360 2040) (360 760))
R(l2 (-980 -855) (1075 950))
R(l6 (-775 -3750) (775 950))
)
N(2 I(SUBSTRATE)
R(l8 (1700 100) (200 200))
R(l8 (-200 -600) (200 200))
R(l8 (-1610 -210) (220 220))
R(l8 (-220 180) (220 220))
R(l12 (890 -760) (800 900))
R(l12 (-1980 -830) (360 760))
R(l13 (-305 -705) (250 250))
R(l13 (-250 150) (250 250))
R(l13 (1175 -225) (200 200))
R(l13 (-200 -600) (200 200))
R(l14 (-3400 -350) (3000 900))
R(l14 (-200 -900) (1000 900))
R(l6 (-2175 -925) (775 950))
)
N(3
R(l3 (-1500 1800) (3000 2000))
R(l3 (-200 -2000) (1000 2000))
R(l8 (-2010 -1310) (220 220))
R(l8 (-220 180) (220 220))
R(l8 (1190 -210) (200 200))
R(l8 (-200 -600) (200 200))
R(l12 (-1680 -280) (360 760))
R(l12 (820 -830) (800 900))
R(l13 (-1925 -775) (250 250))
R(l13 (-250 150) (250 250))
R(l13 (1175 -225) (200 200))
R(l13 (-200 -600) (200 200))
R(l14 (-3400 -350) (3000 900))
R(l14 (-200 -900) (1000 900))
R(l10 (-700 -950) (400 1000))
R(l2 (-1875 -975) (775 950))
)
N(4
R(l4 (-125 -250) (250 2500))
R(l4 (-250 -3050) (250 1600))
R(l4 (-250 1200) (250 1600))
)
P(1)
P(2 I(SUBSTRATE))
P(3)
P(4)
D(1 D$PMOS
Y(0 2800)
E(L 0.25)
E(W 0.95)
E(AS 1.02125)
E(AD 0.73625)
E(PS 4.05)
E(PD 3.45)
T(S 1)
T(G 4)
T(D 3)
T(B 3)
)
D(2 D$NMOS
Y(0 0)
E(L 0.25)
E(W 0.95)
E(AS 0.73625)
E(AD 0.73625)
E(PS 3.45)
E(PD 3.45)
T(S 1)
T(G 4)
T(D 2)
T(B 2)
)
)
X(TOP
R((1500 800) (9080 4600))
N(1
R(l4 (2920 2600) (3980 400))
R(l11 (-300 -300) (200 200))
R(l12 (-260 -300) (360 1600))
)
N(2 I(A)
R(l4 (7700 2600) (2880 400))
R(l15 (-2380 -200) (0 0))
)
N(3 I(Q)
R(l12 (1810 2600) (690 400))
R(l16 (-400 -200) (0 0))
)
N(4 I(VDD)
R(l3 (4000 3400) (2700 2000))
R(l14 (-1500 -1450) (1200 900))
R(l17 (-2200 -450) (0 0))
)
N(5 I('SUBSTRATE,VSS')
R(l14 (5200 1150) (1200 900))
R(l17 (-2200 -550) (0 0))
)
P(2 I(A))
P(3 I(Q))
P(4 I(VDD))
P(5 I('SUBSTRATE,VSS'))
X(1 INV Y(3000 1600)
P(0 3)
P(1 5)
P(2 4)
P(3 1)
)
X(2 INV Y(7700 1600)
P(0 1)
P(1 5)
P(2 4)
P(3 2)
)
)

93
testdata/lvs/soft_connect6.lvs vendored Normal file
View File

@ -0,0 +1,93 @@
$lvs_test_source && source($lvs_test_source)
if $lvs_test_target_l2n
report_netlist($lvs_test_target_l2n)
else
report_netlist
end
writer = write_spice(true, false)
$lvs_test_target_cir && target_netlist($lvs_test_target_cir, writer, "Extracted by KLayout")
deep
# Drawing layers
nwell = input(1, 0)
active = input(2, 0)
nplus = input(2, 1)
pplus = input(2, 2)
poly = input(3, 0)
poly_lbl = input(3, 1)
diff_cont = input(4, 0)
poly_cont = input(5, 0)
metal1 = input(6, 0)
metal1_lbl = input(6, 1)
via1 = input(7, 0)
metal2 = input(8, 0)
metal2_lbl = input(8, 1)
# Bulk layer for terminal provisioning
bulk = polygon_layer
psd = nil
nsd = nil
# Computed layers
active_in_nwell = active & nwell
pactive = active_in_nwell & pplus
ntie = active_in_nwell & nplus
pgate = pactive & poly
psd = pactive - pgate
active_outside_nwell = active - nwell
nactive = active_outside_nwell & nplus
ptie = active_outside_nwell & pplus
ngate = nactive & poly
nsd = nactive - ngate
# Device extraction
# PMOS transistor device extraction
extract_devices(mos4("PMOS"), { "SD" => psd, "G" => pgate, "W" => nwell,
"tS" => psd, "tD" => psd, "tG" => poly })
# NMOS transistor device extraction
extract_devices(mos4("NMOS"), { "SD" => nsd, "G" => ngate, "W" => bulk,
"tS" => nsd, "tD" => nsd, "tG" => poly })
# Define connectivity for netlist extraction
# Inter-layer
soft_connect(diff_cont, psd)
soft_connect(diff_cont, nsd)
soft_connect(diff_cont, ptie)
soft_connect(diff_cont, ntie)
soft_connect(ntie, nwell)
soft_connect(poly_cont, poly)
connect(diff_cont, metal1)
connect(poly_cont, metal1)
connect(metal1, via1)
connect(via1, metal2)
# attach labels
connect(poly, poly_lbl)
connect(metal1, metal1_lbl)
connect(metal2, metal2_lbl)
# Global
connect_global(bulk, "SUBSTRATE")
soft_connect_global(ptie, "SUBSTRATE")
# Netlist section (NOTE: we only check log here)
# for debugging: _make_soft_connection_diodes(true)
netlist
netlist.simplify

111
testdata/lvs/vexriscv_soft.lvs vendored Normal file
View File

@ -0,0 +1,111 @@
source($lvs_test_source)
# will get pretty big:
# report_lvs($lvs_test_target_lvsdb, true)
target_netlist($lvs_test_target_cir, write_spice(true), "Extracted by KLayout")
schematic("vexriscv_schematic.cir.gz")
deep
# Drawing layers
nwell = input(1, 0)
pactive = input(4, 0)
nactive = input(3, 0)
ntie = input(5, 0)
ptie = input(6, 0)
poly = input(7, 0)
cont = input(10, 0)
metal1 = input(11, 0)
via1 = input(14, 0)
metal2 = input(16, 0)
via2 = input(18, 0)
metal3 = input(19, 0)
via3 = input(21, 0)
metal4 = input(22, 0)
via4 = input(25, 0)
metal5 = input(26, 0)
# Bulk layer for terminal provisioning
bulk = polygon_layer
# Computed layers
poly_cont = cont & poly
diff_cont = cont - poly
pgate = pactive & poly
psd = pactive - pgate
ngate = nactive & poly
nsd = nactive - ngate
# Device extraction
# PMOS transistor device extraction
extract_devices(mos4("PMOS"), { "SD" => psd, "G" => pgate, "W" => nwell,
"tS" => psd, "tD" => psd, "tG" => poly, "tW" => nwell })
# NMOS transistor device extraction
extract_devices(mos4("NMOS"), { "SD" => nsd, "G" => ngate, "W" => bulk,
"tS" => nsd, "tD" => nsd, "tG" => poly, "tW" => bulk })
# Define connectivity for netlist extraction
# Inter-layer
soft_connect(diff_cont, psd)
soft_connect(diff_cont, nsd)
soft_connect(poly_cont, poly)
connect(poly_cont, metal1)
connect(diff_cont, metal1)
soft_connect(diff_cont, ntie)
soft_connect(diff_cont, ptie)
soft_connect(ntie, nwell)
connect(metal1, via1)
connect(via1, metal2)
connect(metal2, via2)
connect(via2, metal3)
connect(metal3, via3)
connect(via3, metal4)
connect(metal4, via4)
connect(via4, metal5)
# Global
soft_connect_global(ptie, "BULK")
connect_global(bulk, "BULK")
# Implicit
connect_implicit("VDD")
connect_implicit("VSS")
# Compare section
same_device_classes("PMOS", "TP")
same_device_classes("NMOS", "TN")
# Ignore all caps from the schematic
same_device_classes(nil, "CAP")
# Increase the default complexity from 100 to 200
# This is required because the clock tree is incorrect and exhibits manifold ambiguities
# (the netlists are just samples, not necessarily functional).
# The algorithm needs enough freedom to follow all these branches and different variants.
max_branch_complexity(200)
schematic.combine_devices
netlist.combine_devices
align
if ! compare
raise "Netlists don't match"
else
puts "Congratulations! Netlists match."
end

View File

@ -0,0 +1,57 @@
# encoding: UTF-8
# KLayout Layout Viewer
# Copyright (C) 2006-2024 Matthias Koefferlein
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
if !$:.member?(File::dirname($0))
$:.push(File::dirname($0))
end
load("test_prologue.rb")
class DBHierNetworkProcessor_TestClass < TestBase
# Connectivity
def test_1_Connectivity
conn = RBA::Connectivity::new
assert_equal(conn.to_s, "")
conn.connect(1)
assert_equal(conn.to_s, "1:1")
conn.connect(0, 1)
assert_equal(conn.to_s, "0:1\n1:0,1")
conn.soft_connect(0, 2)
assert_equal(conn.to_s, "0:1,2-S\n1:0,1\n2:0+S")
gid1 = conn.connect_global(0, "GLOBAL1")
assert_equal(gid1, 0)
assert_equal(conn.global_net_name(gid1), "GLOBAL1")
assert_equal(conn.global_net_id("GLOBAL1"), 0)
assert_equal(conn.to_s, "0:1,2-S\nG0:0\n1:0,1\n2:0+S")
conn.soft_connect_global(1, "GLOBAL1")
assert_equal(conn.to_s, "0:1,2-S\nG0:0\n1:0,1\nG1:0-S\n2:0+S")
end
end
load("test_epilogue.rb")