mirror of https://github.com/KLayout/klayout.git
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:
parent
3ebdfa83f9
commit
eabf558186
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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 "
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
|
|
|
|||
|
|
@ -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 <<"CODE"
|
||||
def #{f}(*args)
|
||||
_netter.#{f}(*args)
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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")
|
||||
|
||||
|
|
@ -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
|
||||
Binary file not shown.
|
|
@ -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)
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue