netlist exaction: selective net joining with labels

Now, a glob pattern can be used to identify the labels
which implicitly join nets. Also, net joining now
only happens on top level.
This commit is contained in:
Matthias Koefferlein 2019-04-15 23:24:27 +02:00
parent 3ebdfa83f9
commit eabf558186
20 changed files with 316 additions and 30 deletions

View File

@ -30,7 +30,7 @@ namespace db
// Circuit class implementation
Circuit::Circuit ()
: mp_netlist (0),
: m_cell_index (0), mp_netlist (0),
m_device_by_id (this, &Circuit::begin_devices, &Circuit::end_devices),
m_subcircuit_by_id (this, &Circuit::begin_subcircuits, &Circuit::end_subcircuits),
m_net_by_cluster_id (this, &Circuit::begin_nets, &Circuit::end_nets),
@ -45,7 +45,7 @@ Circuit::Circuit ()
}
Circuit::Circuit (const Circuit &other)
: gsi::ObjectBase (other), tl::Object (other), mp_netlist (0),
: gsi::ObjectBase (other), tl::Object (other), m_cell_index (0), mp_netlist (0),
m_device_by_id (this, &Circuit::begin_devices, &Circuit::end_devices),
m_subcircuit_by_id (this, &Circuit::begin_subcircuits, &Circuit::end_subcircuits),
m_net_by_cluster_id (this, &Circuit::begin_nets, &Circuit::end_nets),
@ -80,6 +80,7 @@ Circuit &Circuit::operator= (const Circuit &other)
clear ();
m_name = other.m_name;
m_cell_index = other.m_cell_index;
m_pins = other.m_pins;
std::map<const Device *, Device *> device_table;

View File

@ -118,6 +118,7 @@ DeviceClass &DeviceClass::operator= (const DeviceClass &other)
m_parameter_definitions = other.m_parameter_definitions;
m_name = other.m_name;
m_description = other.m_description;
mp_pc_delegate.reset (const_cast<DeviceParameterCompareDelegate *> (other.mp_pc_delegate.get ()));
}
return *this;
}
@ -228,6 +229,9 @@ bool DeviceClass::less (const db::Device &a, const db::Device &b)
const std::vector<db::DeviceParameterDefinition> &pd = a.device_class ()->parameter_definitions ();
for (std::vector<db::DeviceParameterDefinition>::const_iterator p = pd.begin (); p != pd.end (); ++p) {
if (! p->is_primary ()) {
continue;
}
int cmp = compare_parameters (a.parameter_value (p->id ()), b.parameter_value (p->id ()), 0.0, relative_tolerance);
if (cmp != 0) {
return cmp < 0;
@ -255,6 +259,9 @@ bool DeviceClass::equal (const db::Device &a, const db::Device &b)
const std::vector<db::DeviceParameterDefinition> &pd = a.device_class ()->parameter_definitions ();
for (std::vector<db::DeviceParameterDefinition>::const_iterator p = pd.begin (); p != pd.end (); ++p) {
if (! p->is_primary ()) {
continue;
}
int cmp = compare_parameters (a.parameter_value (p->id ()), b.parameter_value (p->id ()), 0.0, relative_tolerance);
if (cmp != 0) {
return false;

View File

@ -124,7 +124,7 @@ public:
* @brief Creates an empty device parameter definition
*/
DeviceParameterDefinition ()
: m_name (), m_description (), m_default_value (0.0), m_id (0)
: m_name (), m_description (), m_default_value (0.0), m_id (0), m_is_primary (true)
{
// .. nothing yet ..
}
@ -132,8 +132,8 @@ public:
/**
* @brief Creates a device parameter definition with the given name and description
*/
DeviceParameterDefinition (const std::string &name, const std::string &description, double default_value = 0.0)
: m_name (name), m_description (description), m_default_value (default_value), m_id (0)
DeviceParameterDefinition (const std::string &name, const std::string &description, double default_value = 0.0, bool is_primary = true)
: m_name (name), m_description (description), m_default_value (default_value), m_id (0), m_is_primary (is_primary)
{
// .. nothing yet ..
}
@ -186,6 +186,25 @@ public:
m_default_value = d;
}
/**
* @brief Sets a value indicating whether the parameter is a primary parameter
*
* If this flag is set to true (the default), the parameter is considered a primary parameter.
* Only primary parameters are compared by default.
*/
void set_is_primary (bool p)
{
m_is_primary = p;
}
/**
* @brief Gets a value indicating whether the parameter is a primary parameter
*/
bool is_primary () const
{
return m_is_primary;
}
/**
* @brief Gets the parameter ID
*/
@ -200,6 +219,7 @@ private:
std::string m_name, m_description;
double m_default_value;
size_t m_id;
bool m_is_primary;
void set_id (size_t id)
{

View File

@ -1769,7 +1769,7 @@ hier_clusters<T>::do_build (cell_clusters_box_converter<T> &cbc, const db::Layou
tl::RelativeProgress progress (tl::to_string (tr ("Computing local clusters")), called.size (), 1);
for (std::set<db::cell_index_type>::const_iterator c = called.begin (); c != called.end (); ++c) {
build_local_cluster (layout, layout.cell (*c), shape_flags, conn, attr_equivalence);
build_local_cluster (layout, layout.cell (*c), shape_flags, conn, *c == cell.cell_index () ? attr_equivalence : 0);
++progress;
}
}

View File

@ -245,7 +245,7 @@ size_t LayoutToNetlist::global_net_id (const std::string &name)
return m_conn.global_net_id (name);
}
void LayoutToNetlist::extract_netlist (bool join_nets_by_label)
void LayoutToNetlist::extract_netlist (const std::string &joined_net_names)
{
if (m_netlist_extracted) {
throw tl::Exception (tl::to_string (tr ("The netlist has already been extracted")));
@ -255,7 +255,7 @@ void LayoutToNetlist::extract_netlist (bool join_nets_by_label)
}
db::NetlistExtractor netex;
netex.extract_nets (dss (), m_layout_index, m_conn, *mp_netlist, m_net_clusters, join_nets_by_label);
netex.extract_nets (dss (), m_layout_index, m_conn, *mp_netlist, m_net_clusters, joined_net_names);
m_netlist_extracted = true;
}

View File

@ -296,7 +296,7 @@ public:
* @brief Runs the netlist extraction
* See the class description for more details.
*/
void extract_netlist (bool join_nets_by_label = true);
void extract_netlist (const std::string &joined_net_names = std::string ());
/**
* @brief Marks the netlist as extracted

View File

@ -214,10 +214,10 @@ DeviceClassMOS3Transistor::DeviceClassMOS3Transistor ()
add_parameter_definition (db::DeviceParameterDefinition ("L", "Gate length (micrometer)", 0.0));
add_parameter_definition (db::DeviceParameterDefinition ("W", "Gate width (micrometer)", 0.0));
add_parameter_definition (db::DeviceParameterDefinition ("AS", "Source area (square micrometer)", 0.0));
add_parameter_definition (db::DeviceParameterDefinition ("AD", "Drain area (square micrometer)", 0.0));
add_parameter_definition (db::DeviceParameterDefinition ("PS", "Source perimeter (micrometer)", 0.0));
add_parameter_definition (db::DeviceParameterDefinition ("PD", "Drain perimeter (micrometer)", 0.0));
add_parameter_definition (db::DeviceParameterDefinition ("AS", "Source area (square micrometer)", 0.0, false));
add_parameter_definition (db::DeviceParameterDefinition ("AD", "Drain area (square micrometer)", 0.0, false));
add_parameter_definition (db::DeviceParameterDefinition ("PS", "Source perimeter (micrometer)", 0.0, false));
add_parameter_definition (db::DeviceParameterDefinition ("PD", "Drain perimeter (micrometer)", 0.0, false));
}
bool DeviceClassMOS3Transistor::combine_devices (Device *a, Device *b) const

View File

@ -23,6 +23,7 @@
#include "dbNetlistExtractor.h"
#include "dbDeepShapeStore.h"
#include "dbNetlistDeviceExtractor.h"
#include "tlGlobPattern.h"
namespace db
{
@ -34,15 +35,18 @@ NetlistExtractor::NetlistExtractor ()
}
static void
build_net_name_equivalence (const db::Layout *layout, db::property_names_id_type net_name_id, tl::equivalence_clusters<unsigned int> &eq)
build_net_name_equivalence (const db::Layout *layout, db::property_names_id_type net_name_id, const std::string &joined_net_names, tl::equivalence_clusters<unsigned int> &eq)
{
std::map<std::string, std::set<unsigned int> > prop_by_name;
tl::GlobPattern jn_pattern (joined_net_names);
for (db::PropertiesRepository::iterator i = layout->properties_repository ().begin (); i != layout->properties_repository ().end (); ++i) {
for (db::PropertiesRepository::properties_set::const_iterator p = i->second.begin (); p != i->second.end (); ++p) {
if (p->first == net_name_id) {
std::string nn = p->second.to_string ();
prop_by_name [nn].insert (i->first);
if (jn_pattern.match (nn)) {
prop_by_name [nn].insert (i->first);
}
}
}
}
@ -58,7 +62,7 @@ build_net_name_equivalence (const db::Layout *layout, db::property_names_id_type
}
void
NetlistExtractor::extract_nets (const db::DeepShapeStore &dss, unsigned int layout_index, const db::Connectivity &conn, db::Netlist &nl, hier_clusters_type &clusters, bool join_nets_by_label)
NetlistExtractor::extract_nets (const db::DeepShapeStore &dss, unsigned int layout_index, const db::Connectivity &conn, db::Netlist &nl, hier_clusters_type &clusters, const std::string &joined_net_names)
{
mp_clusters = &clusters;
mp_layout = &dss.const_layout (layout_index);
@ -77,8 +81,8 @@ NetlistExtractor::extract_nets (const db::DeepShapeStore &dss, unsigned int layo
// the big part: actually extract the nets
tl::equivalence_clusters<unsigned int> net_name_equivalence;
if (m_text_annot_name_id.first && join_nets_by_label) {
build_net_name_equivalence (mp_layout, m_text_annot_name_id.second, net_name_equivalence);
if (m_text_annot_name_id.first && ! joined_net_names.empty ()) {
build_net_name_equivalence (mp_layout, m_text_annot_name_id.second, joined_net_names, net_name_equivalence);
}
mp_clusters->build (*mp_layout, *mp_cell, db::ShapeIterator::Polygons, conn, &net_name_equivalence);

View File

@ -82,7 +82,7 @@ public:
* @brief Extract the nets
* See the class description for more details.
*/
void extract_nets (const db::DeepShapeStore &dss, unsigned int layout_index, const db::Connectivity &conn, db::Netlist &nl, hier_clusters_type &clusters, bool join_nets_by_label = true);
void extract_nets (const db::DeepShapeStore &dss, unsigned int layout_index, const db::Connectivity &conn, db::Netlist &nl, hier_clusters_type &clusters, const std::string &joined_net_names = std::string ());
private:
hier_clusters_type *mp_clusters;

View File

@ -288,10 +288,23 @@ Class<db::LayoutToNetlist> decl_dbLayoutToNetlist ("db", "LayoutToNetlist",
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."
) +
gsi::method ("extract_netlist", &db::LayoutToNetlist::extract_netlist, gsi::arg ("join_nets_by_label", true),
gsi::method ("extract_netlist", &db::LayoutToNetlist::extract_netlist, gsi::arg ("join_net_names", std::string ()),
"@brief Runs the netlist extraction\n"
"If join_nets_by_label is true, nets on the same hierarchy level carrying the same label will be connected "
"implicitly even if there is no physical connection.\n"
"'join_net_names' is a glob expression for labels. Nets on top level carrying the same label which matches this glob "
"expression will be connected implicitly even if there is no physical connection. This feature is useful to simulate a connection "
"which will be made later when integrating the component.\n"
"\n"
"Valid glob expressions are:\n"
"@ul\n"
"@li \"\" no implicit connections.@/li\n"
"@li \"*\" to make all labels candidates for implicit connections.@/li\n"
"@li \"VDD\" to make all 'VDD'' nets candidates for implicit connections.@/li\n"
"@li \"VDD\" to make all 'VDD'+suffix nets candidates for implicit connections.@/li\n"
"@li \"{VDD,VSS}\" to all VDD and VSS nets candidates for implicit connections.@/li\n"
"@/ul\n"
"\n"
"Label matching is case sensitive.\n"
"\n"
"See the class description for more details.\n"
) +
gsi::method_ext ("internal_layout", &l2n_internal_layout,

View File

@ -462,6 +462,15 @@ Class<db::DeviceParameterDefinition> decl_dbDeviceParameterDefinition ("db", "De
"@brief Sets the default value of the parameter.\n"
"The default value is used to initialize parameters of \\Device objects."
) +
gsi::method ("is_primary?", &db::DeviceParameterDefinition::is_primary,
"@brief Gets a value indicating whether the parameter is a primary parameter\n"
"See \\is_primary= for details about this predicate."
) +
gsi::method ("is_primary=", &db::DeviceParameterDefinition::set_is_primary, gsi::arg ("primary"),
"@brief Sets a value indicating whether the parameter is a primary parameter\n"
"If this flag is set to true (the default), the parameter is considered a primary parameter.\n"
"Only primary parameters are compared by default.\n"
) +
gsi::method ("id", &db::DeviceParameterDefinition::id,
"@brief Gets the ID of the parameter.\n"
"The ID of the parameter is used in some places to refer to a specific parameter (e.g. in "

View File

@ -228,6 +228,17 @@ TEST(0_EqualDeviceParameters)
EXPECT_EQ (dc.less (d1, d2), false);
EXPECT_EQ (dc.less (d2, d1), false);
// AD, AS, PD and PS aren't a primary parameter, so we don't compare it.
d2.set_parameter_value (db::DeviceClassMOS3Transistor::param_id_AD, 1.0);
d2.set_parameter_value (db::DeviceClassMOS3Transistor::param_id_PS, 1.0);
d2.set_parameter_value (db::DeviceClassMOS3Transistor::param_id_AD, 1.0);
d2.set_parameter_value (db::DeviceClassMOS3Transistor::param_id_PS, 1.0);
EXPECT_EQ (dc.equal (d1, d2), true);
EXPECT_EQ (dc.equal (d2, d1), true);
EXPECT_EQ (dc.less (d1, d2), false);
EXPECT_EQ (dc.less (d2, d1), false);
d2.set_parameter_value (db::DeviceClassMOS3Transistor::param_id_L, 41.0);
EXPECT_EQ (dc.equal (d1, d2), false);
@ -1837,10 +1848,10 @@ TEST(15_EmptySubCircuitTest)
"end_circuit TRANS TRANS MATCH\n"
"begin_circuit INV2 INV2\n"
"match_nets $5 $5\n"
"match_nets OUT OUT\n"
"match_nets $2 $2\n"
"match_nets IN IN\n"
"match_nets $4 $4\n"
"match_nets OUT OUT\n"
"match_pins IN IN\n"
"match_pins $1 $1\n"
"match_pins OUT OUT\n"
@ -1912,10 +1923,10 @@ TEST(15_EmptySubCircuitWithoutPinNames)
"end_circuit TRANS TRANS MATCH\n"
"begin_circuit INV2 INV2\n"
"match_nets $5 $5\n"
"match_nets OUT OUT\n"
"match_nets $2 $2\n"
"match_nets IN IN\n"
"match_nets $4 $4\n"
"match_nets OUT OUT\n"
"match_pins IN IN\n"
"match_pins $1 $1\n"
"match_pins OUT OUT\n"
@ -1988,8 +1999,8 @@ TEST(16_UniqueSubCircuitMatching)
"match_nets VDD VDD\n"
"match_nets OUT OUT\n"
"match_nets $3 $3\n"
"match_nets $1 $1\n"
"match_nets IN IN\n"
"match_nets $1 $1\n"
"match_nets VSS VSS\n"
"match_nets BULK BULK\n"
"match_pins $0 $0\n"

View File

@ -483,7 +483,7 @@ TEST(2_DeviceAndNetExtractionFlat)
// extract the nets
// don't use "join_nets_by_label" because the flattened texts will spoil everything
net_ex.extract_nets (dss, 0, conn, nl, cl, false);
net_ex.extract_nets (dss, 0, conn, nl, cl);
// debug layers produced for nets
// 202/0 -> Active
@ -716,7 +716,17 @@ TEST(3_DeviceAndNetExtractionWithImplicitConnections)
// extract the nets
net_ex.extract_nets (dss, 0, conn, nl, cl);
db::Netlist nl2 = nl;
net_ex.extract_nets (dss, 0, conn, nl2, cl, "{VDDZ,VSSZ,NEXT,FB}");
EXPECT_EQ (all_net_names_unique (nl2), true);
nl2 = nl;
net_ex.extract_nets (dss, 0, conn, nl2, cl, "{VDDZ,VSSZ,NEXT}");
EXPECT_EQ (all_net_names_unique (nl2), false);
net_ex.extract_nets (dss, 0, conn, nl, cl, "*");
EXPECT_EQ (all_net_names_unique (nl), true);

View File

@ -259,11 +259,13 @@ TEST(1_DeviceTerminalDefinition)
dc.clear_terminal_definitions ();
EXPECT_EQ (dc.terminal_definitions ().empty (), true);
db::DeviceParameterDefinition ppd ("P1", "Parameter 1", 1.0);
db::DeviceParameterDefinition ppd ("P1", "Parameter 1", 1.0, false);
dc.add_parameter_definition (ppd);
EXPECT_EQ (ppd.is_primary (), false);
db::DeviceParameterDefinition ppd2 ("P2", "Parameter 2");
dc.add_parameter_definition (ppd2);
EXPECT_EQ (ppd.is_primary (), true);
EXPECT_EQ (pd2string (dc.parameter_definitions ()[0]), "P1(Parameter 1)=1 #0");
EXPECT_EQ (pd2string (dc.parameter_definitions ()[1]), "P2(Parameter 2)=0 #1");

View File

@ -3804,9 +3804,36 @@ CODE
@connections = []
@global_connections = []
@layers = {}
@join_nets = ""
modified
end
# %DRC%
# @name join_nets
# @brief Specifies a search pattern for labels which create implicit net connections
# @synopsis join_nets(label_pattern)
# Use this method to supply a glob pattern for labels which create implicit net connections
# on the top level circuit. This feature is useful to connect identically labelled nets
# while a component isn't integrated yet. If the component is integrated, net may be connected
# on a higher hierarchy level - e.g. by a power mesh. Inside the component this net consists
# of individual islands. To properly perform netlist extraction and comparison, these islands
# need to be connected even though there isn't a physical connection. "join_nets" can
# achive this if these islands are labelled with the same text on the top level of the
# component.
#
# Glob pattern are used which resemble shell file pattern: "*" is for all labels, "VDD"
# for all "VDD" labels (pattern act case sensitive). "VDD*" is for all labels beginning
# with "VDD" (still different labels will be connected to different nets!). "{VDD,VSS}"
# is either "VDD" or "VSS".
#
# The search pattern is applied on the next net extraction. The search pattern is cleared
# on "clear_connections".
def join_nets(arg)
@join_nets = arg
modified
end
# %DRC%
# @brief Performs an antenna check
# @name antenna_check
@ -3957,7 +3984,7 @@ CODE
@global_connections.each { |l,n| @l2n.connect_global(@layers[l], n) }
# run extraction in a timed environment
@engine._cmd(@l2n, :extract_netlist)
@engine._cmd(@l2n, :extract_netlist, @join_nets)
@l2n
end
@ -4922,6 +4949,12 @@ CODE
# @synopsis clear_connections
# See \Netter#clear_connections for a description of that function
# %DRC%
# @name join_nets
# @brief Specifies a label pattern for implicit net connections
# @synopsis join_nets(label_pattern)
# See \Netter#join_nets for a description of that function
# %DRC%
# @name antenna_check
# @brief Performs an antenna check
@ -4940,7 +4973,7 @@ CODE
# @synopsis extract_devices(extractor, layer_hash)
# See \Netter#extract_devices for a description of that function
%w(connect connect_global clear_connections antenna_check l2n_data extract_devices).each do |f|
%w(connect connect_global clear_connections join_nets antenna_check l2n_data extract_devices).each do |f|
eval &lt;&lt;"CODE"
def #{f}(*args)
_netter.#{f}(*args)

View File

@ -352,11 +352,13 @@ static void compare_netlists (tl::TestBase *_this, const std::string &cir, const
db::NetlistSpiceReader reader;
{
tl::info << "Output: " << cir;
tl::InputStream is (cir);
reader.read (is, nl);
}
{
tl::info << "Golden: " << cir_au;
tl::InputStream is (cir_au);
reader.read (is, nl_au);
}
@ -495,3 +497,39 @@ TEST(11_CustomDevices)
CHECKPOINT ();
compare_netlists (_this, output_simplified, au_simplified);
}
TEST(12_NetlistJoinLabels)
{
std::string rs = tl::testsrc ();
rs += "/testdata/drc/drcSimpleTests_12.drc";
std::string input = tl::testsrc ();
input += "/testdata/drc/implicit_nets.gds";
std::string au = tl::testsrc ();
au += "/testdata/drc/drcSimpleTests_au12a.cir";
std::string output = this->tmp_file ("tmp.cir");
{
// Set some variables
lym::Macro config;
config.set_text (tl::sprintf (
"$drc_test_source = '%s'\n"
"$drc_test_target = '%s'\n"
"$drc_test_target_simplified = nil\n"
, input, output)
);
config.set_interpreter (lym::Macro::Ruby);
EXPECT_EQ (config.run (), 0);
}
lym::Macro drc;
drc.load_from (rs);
EXPECT_EQ (drc.run (), 0);
// verify
CHECKPOINT ();
compare_netlists (_this, output, au);
}

66
testdata/drc/drcSimpleTests_12.drc vendored Normal file
View File

@ -0,0 +1,66 @@
# Flat extraction
source($drc_test_source)
deep
# Drawing layers
nwell = input(1, 0)
active = input(2, 0)
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)
# Computed layers
pactive = active & nwell
pgate = active & poly
psd = active - pgate
nactive = active - nwell
ngate = nactive & poly
nsd = nactive - ngate
# PMOS transistor device extraction
pmos_ex = RBA::DeviceExtractorMOS3Transistor::new("PMOS")
extract_devices(pmos_ex, { "SD" => psd, "G" => pgate, "P" => poly })
# NMOS transistor device extraction
nmos_ex = RBA::DeviceExtractorMOS3Transistor::new("NMOS")
extract_devices(nmos_ex, { "SD" => nsd, "G" => ngate, "P" => poly })
# Define connectivity for netlist extraction
# Inter-layer
connect(psd, diff_cont)
connect(nsd, diff_cont)
connect(poly, poly_cont)
connect(poly_cont, metal1)
connect(diff_cont, metal1)
connect(metal1, via1)
connect(via1, metal2)
connect(poly, poly_lbl)
connect(metal1, metal1_lbl)
connect(metal2, metal2_lbl)
# Actually performs the extraction
join_nets("{VDDZ,VSSZ,NEXT,FB}")
netlist = l2n_data.netlist
# Writes the netlist
writer = RBA::NetlistSpiceWriter::new
netlist.write($drc_test_target, writer, "RINGO netlist")

68
testdata/drc/drcSimpleTests_au12a.cir vendored Normal file
View File

@ -0,0 +1,68 @@
* RINGO netlist
* cell RINGO
.SUBCKT RINGO
* net 1 FB
* net 2 OSC
* net 3 NEXT
* net 4 VSSZ,VSS
* net 5 VDDZ,VDD
* cell instance $1 r180 *1 -0.24,9.18
X$1 16 1 2 4 5 INV2
* cell instance $2 r0 *1 0,0
X$2 1 14 15 4 5 INV2
* cell instance $3 r180 *1 10.32,9.18
X$3 3 9 19 4 5 INV2
* cell instance $4 r0 *1 10.56,0
X$4 20 10 3 4 5 INV2
* cell instance $5 r180 *1 7.68,9.18
X$5 19 8 18 4 5 INV2
* cell instance $6 r180 *1 5.04,9.18
X$6 18 7 17 4 5 INV2
* cell instance $7 r180 *1 2.4,9.18
X$7 17 6 16 4 5 INV2
* cell instance $8 r0 *1 2.64,0
X$8 15 13 22 4 5 INV2
* cell instance $9 r0 *1 5.28,0
X$9 22 12 21 4 5 INV2
* cell instance $10 r0 *1 7.92,0
X$10 21 11 20 4 5 INV2
.ENDS RINGO
* cell INV2
* pin IN
* pin
* pin OUT
* pin
* pin
.SUBCKT INV2 1 2 3 4 5
* net 1 IN
* net 3 OUT
* cell instance $1 r0 *1 -0.4,0
X$1 2 4 1 TRANS
* cell instance $2 r0 *1 -0.4,2.8
X$2 2 5 1 TRANS
* cell instance $3 m0 *1 0.4,2.8
X$3 5 3 2 TRANS
* cell instance $4 m0 *1 0.4,0
X$4 4 3 2 TRANS
* device instance $1 -0.4,0 PMOS
M$1 2 1 4 2 PMOS L=0.25U W=0.95U AS=0.49875P AD=0.26125P PS=2.95U PD=1.5U
* device instance $2 0.4,0 PMOS
M$2 4 2 3 4 PMOS L=0.25U W=0.95U AS=0.26125P AD=0.49875P PS=1.5U PD=2.95U
* device instance $3 -0.4,2.8 PMOS
M$3 2 1 5 2 PMOS L=0.25U W=0.95U AS=0.49875P AD=0.26125P PS=2.95U PD=1.5U
* device instance $4 0.4,2.8 PMOS
M$4 5 2 3 5 PMOS L=0.25U W=0.95U AS=0.26125P AD=0.49875P PS=1.5U PD=2.95U
* device instance $5 -0.4,0 NMOS
M$5 2 1 4 2 NMOS L=0.25U W=0.95U AS=0.49875P AD=0.26125P PS=2.95U PD=1.5U
* device instance $6 0.4,0 NMOS
M$6 4 2 3 4 NMOS L=0.25U W=0.95U AS=0.26125P AD=0.49875P PS=1.5U PD=2.95U
.ENDS INV2
* cell TRANS
* pin
* pin
* pin
.SUBCKT TRANS 1 2 3
.ENDS TRANS

BIN
testdata/drc/implicit_nets.gds vendored Normal file

Binary file not shown.

View File

@ -442,6 +442,10 @@ class DBNetlist_TestClass < TestBase
assert_equal(pd.default_value, 2.0)
pd.default_value = 1.0
assert_equal(pd.default_value, 1.0)
pd.is_primary = false
assert_equal(pd.is_primary?, false)
pd.is_primary = true
assert_equal(pd.is_primary?, true)
dc.add_parameter(pd)