Merge pull request #266 from KLayout/dvb

Dvb
This commit is contained in:
Matthias Köfferlein 2019-07-14 18:56:59 +02:00 committed by GitHub
commit 1a25b19ab1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
469 changed files with 65321 additions and 7517 deletions

BIN
samples/lvs/ringo.gds Normal file

Binary file not shown.

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

@ -0,0 +1,27 @@
.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

303
samples/lvs/tech.lyp Normal file
View File

@ -0,0 +1,303 @@
<?xml version="1.0" encoding="utf-8"?>
<layer-properties>
<properties>
<frame-color>#c0c0c0</frame-color>
<fill-color>#c0c0c0</fill-color>
<frame-brightness>0</frame-brightness>
<fill-brightness>0</fill-brightness>
<dither-pattern>I9</dither-pattern>
<line-style>I2</line-style>
<valid>true</valid>
<visible>true</visible>
<transparent>false</transparent>
<width>2</width>
<marked>false</marked>
<xfill>false</xfill>
<animation>0</animation>
<name>1 - NWELL</name>
<source>1/0@1</source>
</properties>
<properties>
<frame-color>#c0c0c0</frame-color>
<fill-color>#c0c0c0</fill-color>
<frame-brightness>48</frame-brightness>
<fill-brightness>48</fill-brightness>
<dither-pattern>C0</dither-pattern>
<line-style>I4</line-style>
<valid>true</valid>
<visible>true</visible>
<transparent>false</transparent>
<width>1</width>
<marked>false</marked>
<xfill>false</xfill>
<animation>0</animation>
<name>3 - PPLUS</name>
<source>3/0@1</source>
</properties>
<properties>
<frame-color>#c0c0c0</frame-color>
<fill-color>#c0c0c0</fill-color>
<frame-brightness>48</frame-brightness>
<fill-brightness>48</fill-brightness>
<dither-pattern>C1</dither-pattern>
<line-style>I4</line-style>
<valid>true</valid>
<visible>true</visible>
<transparent>false</transparent>
<width>1</width>
<marked>false</marked>
<xfill>false</xfill>
<animation>0</animation>
<name>4 - NPLUS</name>
<source>4/0@1</source>
</properties>
<properties>
<frame-color>#ff0000</frame-color>
<fill-color>#ff9d9d</fill-color>
<frame-brightness>-64</frame-brightness>
<fill-brightness>-64</fill-brightness>
<dither-pattern>I3</dither-pattern>
<line-style>I2</line-style>
<valid>true</valid>
<visible>true</visible>
<transparent>false</transparent>
<width>2</width>
<marked>false</marked>
<xfill>false</xfill>
<animation>0</animation>
<name>2 - DIFF</name>
<source>2/0@1</source>
</properties>
<properties>
<frame-color>#91ff00</frame-color>
<fill-color>#91ff00</fill-color>
<frame-brightness>-48</frame-brightness>
<fill-brightness>-48</fill-brightness>
<dither-pattern>I8</dither-pattern>
<line-style/>
<valid>true</valid>
<visible>true</visible>
<transparent>false</transparent>
<width>2</width>
<marked>false</marked>
<xfill>false</xfill>
<animation>0</animation>
<name>5 - POLY</name>
<source>5/0@1</source>
</properties>
<properties>
<frame-color>#008000</frame-color>
<fill-color>#808000</fill-color>
<frame-brightness>0</frame-brightness>
<fill-brightness>0</fill-brightness>
<dither-pattern>I3</dither-pattern>
<line-style>I4</line-style>
<valid>true</valid>
<visible>true</visible>
<transparent>false</transparent>
<width>1</width>
<marked>false</marked>
<xfill>false</xfill>
<animation>0</animation>
<name>6 - THICKOX</name>
<source>THICKOX 6/0@1</source>
</properties>
<properties>
<frame-color>#805000</frame-color>
<fill-color>#805000</fill-color>
<frame-brightness>0</frame-brightness>
<fill-brightness>0</fill-brightness>
<dither-pattern>I3</dither-pattern>
<line-style>I4</line-style>
<valid>true</valid>
<visible>true</visible>
<transparent>false</transparent>
<width>1</width>
<marked>false</marked>
<xfill>false</xfill>
<animation>0</animation>
<name>7 - POLYRES</name>
<source>'7 - SALBL' 7/0@1</source>
</properties>
<properties>
<frame-color>#ff00ff</frame-color>
<fill-color>#ffffff</fill-color>
<frame-brightness>0</frame-brightness>
<fill-brightness>0</fill-brightness>
<dither-pattern>I0</dither-pattern>
<line-style/>
<valid>true</valid>
<visible>true</visible>
<transparent>false</transparent>
<width>2</width>
<marked>false</marked>
<xfill>true</xfill>
<animation>0</animation>
<name>8 - CONTACT</name>
<source>8/0@1</source>
</properties>
<properties>
<frame-color>#8000ff</frame-color>
<fill-color>#9580ff</fill-color>
<frame-brightness>0</frame-brightness>
<fill-brightness>0</fill-brightness>
<dither-pattern>I4</dither-pattern>
<line-style/>
<valid>true</valid>
<visible>true</visible>
<transparent>false</transparent>
<width>2</width>
<marked>false</marked>
<xfill>false</xfill>
<animation>0</animation>
<name>9 - METAL1</name>
<source>9/0@1</source>
</properties>
<properties>
<frame-color>#e872ff</frame-color>
<fill-color>#e872ff</fill-color>
<frame-brightness>-48</frame-brightness>
<fill-brightness>-48</fill-brightness>
<dither-pattern>I1</dither-pattern>
<line-style/>
<valid>true</valid>
<visible>true</visible>
<transparent>false</transparent>
<width>2</width>
<marked>false</marked>
<xfill>true</xfill>
<animation>0</animation>
<name>10 - VIA</name>
<source>10/0@1</source>
</properties>
<properties>
<frame-color>#0000ff</frame-color>
<fill-color>#8086ff</fill-color>
<frame-brightness>0</frame-brightness>
<fill-brightness>0</fill-brightness>
<dither-pattern>I8</dither-pattern>
<line-style/>
<valid>true</valid>
<visible>true</visible>
<transparent>false</transparent>
<width>2</width>
<marked>false</marked>
<xfill>false</xfill>
<animation>0</animation>
<name>11 - METAL2</name>
<source>11/0@1</source>
</properties>
<properties>
<frame-color>#808080</frame-color>
<fill-color>#c0c0c0</fill-color>
<frame-brightness>0</frame-brightness>
<fill-brightness>128</fill-brightness>
<dither-pattern>I43</dither-pattern>
<line-style/>
<valid>true</valid>
<visible>true</visible>
<transparent>true</transparent>
<width>1</width>
<marked>false</marked>
<xfill>false</xfill>
<animation>0</animation>
<name>12 - PAD</name>
<source>12/0@1</source>
</properties>
<properties>
<frame-color>#c0c0c0</frame-color>
<fill-color>#c0c0c0</fill-color>
<frame-brightness>0</frame-brightness>
<fill-brightness>0</fill-brightness>
<dither-pattern>I1</dither-pattern>
<line-style>I4</line-style>
<valid>true</valid>
<visible>true</visible>
<transparent>false</transparent>
<width>1</width>
<marked>false</marked>
<xfill>false</xfill>
<animation>0</animation>
<name>13 - BORDER</name>
<source>13/0@1</source>
</properties>
<properties>
<frame-color>#606060</frame-color>
<fill-color>#606060</fill-color>
<frame-brightness>0</frame-brightness>
<fill-brightness>0</fill-brightness>
<dither-pattern>I1</dither-pattern>
<line-style>I2</line-style>
<valid>true</valid>
<visible>true</visible>
<transparent>false</transparent>
<width>1</width>
<marked>false</marked>
<xfill>true</xfill>
<animation>0</animation>
<name>1000 - MARKING 1</name>
<source>1000/0@1</source>
</properties>
<properties>
<frame-color>#000000</frame-color>
<fill-color>#000000</fill-color>
<frame-brightness>0</frame-brightness>
<fill-brightness>0</fill-brightness>
<dither-pattern>I1</dither-pattern>
<line-style>I2</line-style>
<valid>true</valid>
<visible>true</visible>
<transparent>false</transparent>
<width>1</width>
<marked>false</marked>
<xfill>false</xfill>
<animation>0</animation>
<name>1001 - MARKING 2</name>
<source>1001/0@1</source>
</properties>
<name/>
<custom-dither-pattern>
<pattern>
<line>................</line>
<line>...*............</line>
<line>...*............</line>
<line>...*............</line>
<line>*******.........</line>
<line>...*............</line>
<line>...*............</line>
<line>...*............</line>
<line>................</line>
<line>...........*....</line>
<line>...........*....</line>
<line>...........*....</line>
<line>........*******.</line>
<line>...........*....</line>
<line>...........*....</line>
<line>...........*....</line>
</pattern>
<order>1</order>
<name>custom plus</name>
</custom-dither-pattern>
<custom-dither-pattern>
<pattern>
<line>................</line>
<line>................</line>
<line>................</line>
<line>................</line>
<line>*******.........</line>
<line>................</line>
<line>................</line>
<line>................</line>
<line>................</line>
<line>................</line>
<line>................</line>
<line>................</line>
<line>........*******.</line>
<line>................</line>
<line>................</line>
<line>................</line>
</pattern>
<order>2</order>
<name/>
</custom-dither-pattern>
</layer-properties>

View File

@ -2,15 +2,21 @@
$script_call = $0 + " " + ARGV.join(" ")
$key="%DRC%"
$infile="src/drc/drc/built-in-macros/drc.lym"
$loc = "about/drc_ref"
$indir="src/drc/drc/built-in-macros"
$loc = "about"
$outfiles="src/lay/lay/doc"
$title="DRC Reference"
def create_ref(s)
if s =~ /(.*)#(.*)/
"<a href=\"/" + $loc + "_" + $1.downcase + ".xml#" + $2 + "\">#{s}</a>"
def create_ref(mod, s)
if s =~ /(.*)::(.*)#(.*)/
"<a href=\"/" + $loc + "/" + $1.downcase + "_ref_" + $2.downcase + ".xml#" + $3 + "\">#{s}</a>"
elsif s =~ /(.*)::(.*)/
"<a href=\"/" + $loc + "/" + $1.downcase + "_ref_" + $2.downcase + ".xml\">#{s}</a>"
elsif s =~ /(.*)#(.*)/
if $2 != ""
"<a href=\"/" + $loc + "/" + mod.downcase + "_ref_" + $1.downcase + ".xml#" + $2 + "\">#{s}</a>"
else
"<a href=\"/" + $loc + "/" + mod.downcase + "_ref_" + $1.downcase + ".xml\">#{$1}</a>"
end
else
"<a href=\"#" + s + "\">#{s}</a>"
end
@ -20,11 +26,11 @@ def create_class_doc_ref(s)
"<class_doc href=\"" + s + "\">#{s}</class_doc>"
end
def escape(s)
def escape(mod, s)
s.gsub("&", "&amp;").
gsub("<", "&lt;").
gsub(">", "&gt;").
gsub(/\\([\w#]+)/) { create_ref($1) }.
gsub(/\\([\w:#]+)/) { create_ref(mod, $1) }.
gsub(/RBA::([\w#]+)/) { create_class_doc_ref($1) }
end
@ -38,12 +44,14 @@ class DocItem
attr_accessor :synopsis
attr_accessor :name
attr_accessor :doc
attr_accessor :mod
def initialize(block)
def initialize(mod, block)
@paragraphs = []
para = nil
self.synopsis = []
self.mod = mod
in_code = false
block.each do |b|
@ -91,7 +99,7 @@ class DocItem
i > 0 && doc += "</p><p>\n"
p.each do |pp|
doc += escape(pp).
doc += escape(self.mod, pp).
gsub(/\\@/, "&at;").
gsub(/\s*@code\s*/, "<pre>").
gsub(/\s*@\/code\s*/, "</pre>").
@ -113,13 +121,13 @@ end
class Scope < DocItem
def initialize(block)
super(block)
def initialize(mod, block)
super(mod, block)
@items = {}
end
def add_doc_item(block)
item = DocItem::new(block)
def add_doc_item(mod, block)
item = DocItem::new(mod, block)
@items[item.name] = item
end
@ -137,8 +145,8 @@ class Scope < DocItem
HEAD
doc += "<doc>\n"
doc += "<title>" + escape(self.brief) + "</title>\n"
doc += "<keyword name=\"" + escape(self.name) + "\"/>\n"
doc += "<title>" + escape(self.mod, self.brief) + "</title>\n"
doc += "<keyword name=\"" + escape(self.mod, self.name) + "\"/>\n"
doc += super_produce_doc
@ -151,14 +159,14 @@ HEAD
item.name || raise("Missing @name for item #{item_key}")
item.brief || raise("Missing @brief for item #{item_key}")
doc += "<h2>\"" + escape(item.name) + "\" - " + escape(item.brief) + "</h2>\n"
doc += "<keyword name=\"" + escape(item.name) + "\"/>\n"
doc += "<a name=\"" + escape(item.name) + "\"/>"
doc += "<h2>\"" + escape(self.mod, item.name) + "\" - " + escape(self.mod, item.brief) + "</h2>\n"
doc += "<keyword name=\"" + escape(self.mod, item.name) + "\"/>\n"
doc += "<a name=\"" + escape(self.mod, item.name) + "\"/>"
if ! item.synopsis.empty?
doc += "<p>Usage:</p>\n"
doc += "<ul>\n"
item.synopsis.each do |s|
doc += "<li><tt>" + escape(s) + "</tt></li>\n"
doc += "<li><tt>" + escape(self.mod, s) + "</tt></li>\n"
end
doc += "</ul>\n"
end
@ -177,17 +185,22 @@ end
class Collector
def initialize(mod, title)
@mod = mod
@title = title
end
def add_block(block)
if block.find { |l| l =~ /^@scope/ }
# is a scope block
@scopes ||= {}
@current_scope = Scope::new(block)
@current_scope = Scope::new(@mod, block)
@scopes[@current_scope.name] = @current_scope
else
@current_scope && @current_scope.add_doc_item(block)
@current_scope && @current_scope.add_doc_item(@mod, block)
end
end
@ -196,7 +209,7 @@ class Collector
@scopes.keys.sort.each do |k|
suffix = k.downcase
outfile = $outfiles + "/" + $loc + "_" + suffix + ".xml"
outfile = $outfiles + "/" + $loc + "/" + @mod + "_ref_" + suffix + ".xml"
File.open(outfile, "w") do |file|
file.write(@scopes[k].produce_doc)
puts "---> #{outfile} written."
@ -207,7 +220,7 @@ class Collector
def produce_index
outfile = $outfiles + "/" + $loc + ".xml"
outfile = $outfiles + "/" + $loc + "/" + @mod + "_ref.xml"
File.open(outfile, "w") do |file|
doc = <<HEAD
@ -221,14 +234,14 @@ HEAD
doc += "<doc>\n"
doc += "<title>#{escape($title)}</title>\n"
doc += "<keyword name=\"#{escape($title)}\"/>\n"
doc += "<title>#{escape(@mod, @title)}</title>\n"
doc += "<keyword name=\"#{escape(@mod, @title)}\"/>\n"
doc += "<topics>\n"
@scopes.keys.sort.each do |k|
suffix = k.downcase
doc += "<topic href=\"/#{$loc}_#{suffix}.xml\"/>\n"
doc += "<topic href=\"/#{$loc}/#{@mod}_ref_#{suffix}.xml\"/>\n"
end
doc += "</topics>\n"
@ -244,36 +257,68 @@ HEAD
end
collector = Collector::new
collectors = {
"DRC" => Collector::new("drc", "DRC Reference"),
"LVS" => Collector::new("lvs", "LVS Reference")
}
File.open($infile, "r") do |file|
Dir.entries($indir).each do |p|
block = nil
line = 0
if p !~ /\.rb$/
next
end
file.each_line do |l|
infile = File.join($indir, p)
puts "Extracting doc from #{infile} .."
line += 1
File.open(infile, "r") do |file|
begin
l = unescape(l)
if l =~ /^\s*#\s*#{$key}/
block = []
elsif l =~ /^\s*#\s*(.*)\s*$/
block && block.push($1)
elsif l =~ /^\s*$/
block && collector.add_block(block)
block = nil
block = []
collector = nil
line = 0
file.each_line do |l|
line += 1
begin
l = unescape(l)
if ! collector
collectors.each do |k,c|
if l =~ /^\s*#\s*%#{k}%/
collector = c
l = nil
block = []
break
end
end
end
if l
if l =~ /^\s*#\s*(.*)\s*$/
collector && block.push($1)
elsif l =~ /^\s*$/
collector && collector.add_block(block)
collector = nil
end
end
rescue => ex
puts "ERROR in line #{line}:\n" + ex.to_s
puts ex.backtrace # @@@
exit 1
end
rescue => ex
puts "ERROR in line #{line}:\n" + ex.to_s
exit 1
end
end
end
collector.produce_doc
collector.produce_index
collectors.each do |k,collector|
collector.produce_doc
collector.produce_index
end

View File

@ -89,7 +89,7 @@
<string>...</string>
</property>
<property name="icon">
<iconset resource="../lay/layResources.qrc">
<iconset resource="../../lay/lay/layResources.qrc">
<normaloff>:/up.png</normaloff>:/up.png</iconset>
</property>
</widget>
@ -103,7 +103,7 @@
<string>...</string>
</property>
<property name="icon">
<iconset resource="../lay/layResources.qrc">
<iconset resource="../../lay/lay/layResources.qrc">
<normaloff>:/add.png</normaloff>:/add.png</iconset>
</property>
</widget>
@ -117,7 +117,7 @@
<string>...</string>
</property>
<property name="icon">
<iconset resource="../lay/layResources.qrc">
<iconset resource="../../lay/lay/layResources.qrc">
<normaloff>:/del.png</normaloff>:/del.png</iconset>
</property>
</widget>
@ -131,7 +131,7 @@
<string>...</string>
</property>
<property name="icon">
<iconset resource="../lay/layResources.qrc">
<iconset resource="../../lay/lay/layResources.qrc">
<normaloff>:/down.png</normaloff>:/down.png</iconset>
</property>
</widget>
@ -866,7 +866,7 @@
<tabstop>t_snap_cbx</tabstop>
</tabstops>
<resources>
<include location="../lay/layResources.qrc"/>
<include location="../../lay/lay/layResources.qrc"/>
</resources>
<connections/>
</ui>

View File

@ -176,7 +176,14 @@ SOURCES = \
dbNetlistCompare.cc \
dbNetlistReader.cc \
dbNetlistSpiceReader.cc \
gsiDeclDbNetlistCompare.cc
gsiDeclDbNetlistCompare.cc \
dbNetlistCrossReference.cc \
dbLayoutVsSchematicWriter.cc \
dbLayoutVsSchematicReader.cc \
dbLayoutVsSchematicFormatDefs.cc \
dbLayoutVsSchematic.cc \
gsiDeclDbNetlistCrossReference.cc \
gsiDeclDbLayoutVsSchematic.cc
HEADERS = \
dbArray.h \
@ -317,7 +324,12 @@ HEADERS = \
gsiDeclDbHelpers.h \
dbNetlistCompare.h \
dbNetlistReader.h \
dbNetlistSpiceReader.h
dbNetlistSpiceReader.h \
dbNetlistCrossReference.h \
dbLayoutVsSchematicWriter.h \
dbLayoutVsSchematicReader.h \
dbLayoutVsSchematicFormatDefs.h \
dbLayoutVsSchematic.h
!equals(HAVE_QT, "0") {

View File

@ -295,7 +295,7 @@ CellMapping::create_from_names (const db::Layout &layout_a, db::cell_index_type
}
std::vector<db::cell_index_type>
CellMapping::create_missing_mapping (db::Layout &layout_a, db::cell_index_type /*cell_index_a*/, const db::Layout &layout_b, db::cell_index_type cell_index_b, const std::set<db::cell_index_type> *exclude_cells)
CellMapping::create_missing_mapping (db::Layout &layout_a, db::cell_index_type /*cell_index_a*/, const db::Layout &layout_b, db::cell_index_type cell_index_b, const std::set<db::cell_index_type> *exclude_cells, const std::set<db::cell_index_type> *include_cells)
{
std::vector<db::cell_index_type> new_cells;
std::vector<db::cell_index_type> new_cells_b;
@ -305,7 +305,9 @@ CellMapping::create_missing_mapping (db::Layout &layout_a, db::cell_index_type /
called_b.insert (cell_index_b);
for (std::set<db::cell_index_type>::const_iterator b = called_b.begin (); b != called_b.end (); ++b) {
if (m_b2a_mapping.find (*b) == m_b2a_mapping.end () && (! exclude_cells || exclude_cells->find (*b) == exclude_cells->end ())) {
if (m_b2a_mapping.find (*b) == m_b2a_mapping.end ()
&& (! exclude_cells || exclude_cells->find (*b) == exclude_cells->end ())
&& (! include_cells || include_cells->find (*b) != include_cells->end ())) {
db::cell_index_type new_cell = layout_a.add_cell (layout_b.cell_name (*b));
new_cells.push_back (new_cell);
new_cells_b.push_back (*b);

View File

@ -190,9 +190,12 @@ public:
*
* If given, "exclude_cells" can specify a list of cells not to map.
*
* If given, "include_cells" can specify a list of cells which are included in the
* cell tree to create. Cells not in the "include_cells" list are ignored.
*
* The returned vector lists the new cells.
*/
std::vector<db::cell_index_type> create_missing_mapping (db::Layout &layout_a, db::cell_index_type cell_index_a, const db::Layout &layout_b, db::cell_index_type cell_index_b, const std::set<db::cell_index_type> *exclude_cells = 0);
std::vector<db::cell_index_type> create_missing_mapping (db::Layout &layout_a, db::cell_index_type cell_index_a, const db::Layout &layout_b, db::cell_index_type cell_index_b, const std::set<db::cell_index_type> *exclude_cells = 0, const std::set<db::cell_index_type> *include_cells = 0);
private:
void extract_unique (std::map <db::cell_index_type, std::vector<db::cell_index_type> >::const_iterator cand,

View File

@ -22,6 +22,9 @@
#include "dbCircuit.h"
#include "dbNetlist.h"
#include "dbLayout.h"
#include <set>
namespace db
{
@ -30,7 +33,7 @@ namespace db
// Circuit class implementation
Circuit::Circuit ()
: m_cell_index (0), mp_netlist (0),
: m_dont_purge (false), 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),
@ -44,8 +47,25 @@ Circuit::Circuit ()
m_subcircuits.changed ().add (this, &Circuit::subcircuits_changed);
}
Circuit::Circuit (const db::Layout &layout, db::cell_index_type ci)
: m_name (layout.cell_name (ci)), m_dont_purge (false), m_cell_index (ci), 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),
m_device_by_name (this, &Circuit::begin_devices, &Circuit::end_devices),
m_subcircuit_by_name (this, &Circuit::begin_subcircuits, &Circuit::end_subcircuits),
m_net_by_name (this, &Circuit::begin_nets, &Circuit::end_nets),
m_index (0)
{
m_devices.changed ().add (this, &Circuit::devices_changed);
m_nets.changed ().add (this, &Circuit::nets_changed);
m_subcircuits.changed ().add (this, &Circuit::subcircuits_changed);
set_boundary (db::DPolygon (db::CplxTrans (layout.dbu ()) * layout.cell (ci).bbox ()));
}
Circuit::Circuit (const Circuit &other)
: gsi::ObjectBase (other), tl::Object (other), m_cell_index (0), mp_netlist (0),
: gsi::ObjectBase (other), tl::Object (other), m_dont_purge (false), 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 +100,8 @@ Circuit &Circuit::operator= (const Circuit &other)
clear ();
m_name = other.m_name;
m_boundary = other.m_boundary;
m_dont_purge = other.m_dont_purge;
m_cell_index = other.m_cell_index;
m_pins = other.m_pins;
@ -142,6 +164,13 @@ const Pin *Circuit::pin_by_id (size_t id) const
}
}
void Circuit::rename_pin (size_t id, const std::string &name)
{
if (id < m_pins.size ()) {
m_pins [id].set_name (name);
}
}
const Pin *Circuit::pin_by_name (const std::string &name) const
{
for (Circuit::const_pin_iterator p = begin_pins (); p != end_pins (); ++p) {
@ -181,6 +210,7 @@ void Circuit::clear ()
m_devices.clear ();
m_nets.clear ();
m_subcircuits.clear ();
m_boundary.clear ();
}
void Circuit::set_name (const std::string &name)
@ -191,6 +221,16 @@ void Circuit::set_name (const std::string &name)
}
}
void Circuit::set_boundary (const db::DPolygon &boundary)
{
m_boundary = boundary;
}
void Circuit::set_dont_purge (bool dp)
{
m_dont_purge = dp;
}
void Circuit::set_cell_index (const db::cell_index_type ci)
{
m_cell_index = ci;
@ -358,6 +398,7 @@ void Circuit::flatten_subcircuit (SubCircuit *subcircuit)
if (! d->name ().empty ()) {
device->set_name (subcircuit->expanded_name () + "." + d->name ());
}
device->set_trans (subcircuit->trans () * device->trans ());
add_device (device);
const std::vector<db::DeviceTerminalDefinition> &td = d->device_class ()->terminal_definitions ();
@ -382,6 +423,7 @@ void Circuit::flatten_subcircuit (SubCircuit *subcircuit)
if (! new_subcircuit->name ().empty ()) {
new_subcircuit->set_name (subcircuit->expanded_name () + "." + new_subcircuit->name ());
}
new_subcircuit->set_trans (subcircuit->trans () * new_subcircuit->trans ());
add_subcircuit (new_subcircuit);
const db::Circuit *cr = sc->circuit_ref ();
@ -422,11 +464,7 @@ void Circuit::translate_device_classes (const std::map<const DeviceClass *, Devi
void Circuit::translate_device_abstracts (const std::map<const DeviceAbstract *, DeviceAbstract *> &map)
{
for (device_iterator i = m_devices.begin (); i != m_devices.end (); ++i) {
if (i->device_abstract ()) {
std::map<const DeviceAbstract *, DeviceAbstract *>::const_iterator m = map.find (i->device_abstract ());
tl_assert (m != map.end ());
i->set_device_abstract (m->second);
}
i->translate_device_abstracts (map);
}
}
@ -438,6 +476,35 @@ void Circuit::set_pin_ref_for_pin (size_t pin_id, Net::pin_iterator iter)
m_pin_refs [pin_id] = iter;
}
void Circuit::blank ()
{
tl_assert (netlist () != 0);
std::set<db::Circuit *> cs;
for (subcircuit_iterator i = m_subcircuits.begin (); i != m_subcircuits.end (); ++i) {
cs.insert (i->circuit_ref ());
}
// weak pointers are good because deleting a subcircuit might delete others ahead in
// this list:
std::list<tl::weak_ptr<db::Circuit> > called_circuits;
for (std::set<db::Circuit *>::const_iterator c = cs.begin (); c != cs.end (); ++c) {
called_circuits.push_back (*c);
}
m_nets.clear ();
m_subcircuits.clear ();
m_devices.clear ();
for (std::list<tl::weak_ptr<db::Circuit> >::iterator c = called_circuits.begin (); c != called_circuits.end (); ++c) {
if (c->get () && ! (*c)->has_refs ()) {
netlist ()->purge_circuit (c->get ());
}
}
set_dont_purge (true);
}
const Net *Circuit::net_for_pin (size_t pin_id) const
{
if (pin_id < m_pin_refs.size ()) {
@ -534,6 +601,7 @@ bool Circuit::combine_parallel_devices (const db::DeviceClass &cls)
for (size_t i = 0; i < cl.size () - 1; ++i) {
for (size_t j = i + 1; j < cl.size (); ) {
if (cls.combine_devices (cl [i], cl [j])) {
cl [i]->join_device (cl [j]);
check_device_before_remove (this, cl [j]); // sanity check
delete cl [j];
cl.erase (cl.begin () + j);
@ -623,6 +691,7 @@ bool Circuit::combine_serial_devices(const db::DeviceClass &cls)
// found a combination candidate
if (cls.combine_devices (dd.first, dd.second)) {
dd.first->join_device (dd.second);
check_device_before_remove (this, dd.second); // sanity check
delete dd.second;
any = true;

View File

@ -30,6 +30,7 @@
#include "dbPin.h"
#include "dbSubCircuit.h"
#include "dbNetlistUtils.h"
#include "dbPolygon.h"
#include "tlObject.h"
#include "tlObjectCollection.h"
@ -40,6 +41,43 @@ namespace db
{
class Netlist;
class Layout;
/**
* @brief An iterator wrapper for the child and parent circuit iterator
*/
template <class Iter, class Value>
struct DB_PUBLIC_TEMPLATE dereferencing_iterator
: public Iter
{
public:
typedef Value *pointer;
typedef Value &reference;
typedef typename Iter::difference_type difference_type;
dereferencing_iterator () { }
dereferencing_iterator (const dereferencing_iterator &d) : Iter (d) { }
dereferencing_iterator (const Iter &d) : Iter (d) { }
dereferencing_iterator &operator= (const dereferencing_iterator &d)
{
Iter::operator= (d);
return *this;
}
dereferencing_iterator operator+ (difference_type offset) const
{
return dereferencing_iterator (Iter::operator+ (offset));
}
dereferencing_iterator &operator+= (difference_type offset)
{
Iter::operator+= (offset);
return *this;
}
pointer operator-> () const { return Iter::operator* (); }
reference operator* () const { return *Iter::operator* (); }
};
/**
* @brief A circuit
@ -65,10 +103,10 @@ public:
typedef subcircuit_list::iterator subcircuit_iterator;
typedef tl::weak_collection<SubCircuit>::const_iterator const_refs_iterator;
typedef tl::weak_collection<SubCircuit>::iterator refs_iterator;
typedef tl::vector<Circuit *>::const_iterator child_circuit_iterator;
typedef tl::vector<const Circuit *>::const_iterator const_child_circuit_iterator;
typedef tl::vector<Circuit *>::const_iterator parent_circuit_iterator;
typedef tl::vector<const Circuit *>::const_iterator const_parent_circuit_iterator;
typedef dereferencing_iterator<tl::vector<Circuit *>::const_iterator, Circuit> child_circuit_iterator;
typedef dereferencing_iterator<tl::vector<const Circuit *>::const_iterator, const Circuit> const_child_circuit_iterator;
typedef dereferencing_iterator<tl::vector<Circuit *>::const_iterator, Circuit> parent_circuit_iterator;
typedef dereferencing_iterator<tl::vector<const Circuit *>::const_iterator, const Circuit> const_parent_circuit_iterator;
/**
* @brief Constructor
@ -77,6 +115,13 @@ public:
*/
Circuit ();
/**
* @brief Constructor
*
* Creates a circuit corresponding to a layout cell
*/
Circuit (const db::Layout &layout, db::cell_index_type ci);
/**
* @brief Copy constructor
*/
@ -128,6 +173,33 @@ public:
return m_name;
}
/**
* @brief Sets the boundary
*/
void set_boundary (const db::DPolygon &boundary);
/**
* @brief Gets the boundary
*/
const db::DPolygon &boundary () const
{
return m_boundary;
}
/**
* @brief Sets or resets the "don't purge" flag
* This flag will prevent "purge" from deleting this circuit. It is set by "blank".
*/
void set_dont_purge (bool dp);
/**
* @brief Gets or resets the "don't purge" flag
*/
bool dont_purge () const
{
return m_dont_purge;
}
/**
* @brief The index of the circuit in the netlist
* CAUTION: this attribute is used for internal purposes and may not be valid always.
@ -179,6 +251,23 @@ public:
return m_refs.begin ();
}
/**
* @brief Gets the references to this circuit (end, const version)
* This iterator will deliver all subcircuits referencing this circuit
*/
const_refs_iterator end_refs () const
{
return m_refs.end ();
}
/**
* @brief Returns a value indicating whether the circuit has references
*/
bool has_refs () const
{
return begin_refs () != end_refs ();
}
/**
* @brief Gets the child circuits iterator (begin)
* The child circuits are the circuits referenced by all subcircuits
@ -225,15 +314,6 @@ public:
*/
const_parent_circuit_iterator end_parents () const;
/**
* @brief Gets the references to this circuit (end, const version)
* This iterator will deliver all subcircuits referencing this circuit
*/
const_refs_iterator end_refs () const
{
return m_refs.end ();
}
/**
* @brief Clears the pins
*/
@ -309,6 +389,14 @@ public:
*/
void remove_net (Net *net);
/**
* @brief Gets the number of nets
*/
size_t net_count () const
{
return m_nets.size ();
}
/**
* @brief Begin iterator for the nets of the circuit (non-const version)
*/
@ -393,6 +481,14 @@ public:
*/
void remove_device (Device *device);
/**
* @brief Gets the number of devices
*/
size_t device_count () const
{
return m_devices.size ();
}
/**
* @brief Gets the device from a given ID (const version)
*
@ -477,6 +573,14 @@ public:
*/
void remove_subcircuit (SubCircuit *subcircuit);
/**
* @brief Gets the number of subcircuits
*/
size_t subcircuit_count () const
{
return m_subcircuits.size ();
}
/**
* @brief Gets the subcircuit from a given ID (const version)
*
@ -574,6 +678,11 @@ public:
*/
void connect_pin (size_t pin_id, Net *net);
/**
* @brief Renames the pin with the given ID
*/
void rename_pin (size_t pin_id, const std::string &name);
/**
* @brief Purge unused nets
*
@ -598,6 +707,16 @@ public:
*/
void flatten_subcircuit (SubCircuit *subcircuit);
/**
* @brief Blanks out the circuit
*
* This will remove all innards of the circuit (nets, devices, subcircuits)
* and circuits which will itself are not longer be called after this.
* This operation will eventually leave a blackbox model of the circuit
* containing only pins.
*/
void blank ();
private:
friend class Netlist;
friend class Net;
@ -605,6 +724,8 @@ private:
friend class Device;
std::string m_name;
db::DPolygon m_boundary;
bool m_dont_purge;
db::cell_index_type m_cell_index;
net_list m_nets;
pin_list m_pins;

View File

@ -23,7 +23,6 @@
#include "dbDeepRegion.h"
#include "dbDeepShapeStore.h"
#include "dbEmptyRegion.h"
#include "dbEmptyEdgePairs.h"
#include "dbRegion.h"
#include "dbRegionUtils.h"
@ -462,10 +461,13 @@ DeepRegion::and_with (const Region &other) const
{
const DeepRegion *other_deep = dynamic_cast <const DeepRegion *> (other.delegate ());
if (empty () || other.empty ()) {
if (empty ()) {
// Nothing to do
return new EmptyRegion ();
return clone ();
} else if (other.empty ()) {
return other.delegate ()->clone ();
} else if (! other_deep) {
@ -483,14 +485,8 @@ DeepRegion::not_with (const Region &other) const
{
const DeepRegion *other_deep = dynamic_cast <const DeepRegion *> (other.delegate ());
if (empty ()) {
if (empty () || other.empty ()) {
// Nothing to do
return new EmptyRegion ();
} else if (other.empty ()) {
// Nothing to do
return clone ();
} else if (! other_deep) {
@ -1231,6 +1227,12 @@ DeepRegion::merged (bool min_coherence, unsigned int min_wc) const
RegionDelegate *
DeepRegion::sized (coord_type d, unsigned int mode) const
{
if (empty ()) {
// Nothing to do - NOTE: don't return EmptyRegion because we want to
// maintain "deepness"
return clone ();
}
ensure_merged_polygons_valid ();
db::Layout &layout = m_merged_polygons.layout ();
@ -1299,6 +1301,12 @@ struct XYAnisotropyAndMagnificationReducer
RegionDelegate *
DeepRegion::sized (coord_type dx, coord_type dy, unsigned int mode) const
{
if (empty ()) {
// Nothing to do - NOTE: don't return EmptyRegion because we want to
// maintain "deepness"
return clone ();
}
if (dx == dy) {
return sized (dx, mode);
}

View File

@ -222,19 +222,11 @@ DeepLayer::check_dss () const
struct DeepShapeStore::LayoutHolder
{
LayoutHolder (const db::ICplxTrans &trans)
: refs (0), layout (false), builder (&layout, trans), m_empty_layer (std::numeric_limits<unsigned int>::max ())
: refs (0), layout (false), builder (&layout, trans)
{
// .. nothing yet ..
}
unsigned int empty_layer () const
{
if (m_empty_layer == std::numeric_limits<unsigned int>::max ()) {
const_cast<LayoutHolder *> (this)->make_empty_layer ();
}
return m_empty_layer;
}
void add_layer_ref (unsigned int layer)
{
layer_refs [layer] += 1;
@ -255,15 +247,6 @@ struct DeepShapeStore::LayoutHolder
db::Layout layout;
db::HierarchyBuilder builder;
std::map<unsigned int, int> layer_refs;
private:
unsigned int m_empty_layer;
void make_empty_layer ()
{
m_empty_layer = layout.insert_layer ();
layer_refs [m_empty_layer] += 1; // the empty layer is not deleted
}
};
// ----------------------------------------------------------------------------------
@ -514,6 +497,18 @@ void DeepShapeStore::make_layout (unsigned int layout_index, const db::Recursive
m_layout_map[std::make_pair (si, trans)] = layout_index;
}
static unsigned int init_layer (db::Layout &layout, const db::RecursiveShapeIterator &si)
{
unsigned int layer_index = layout.insert_layer ();
if (si.layout () && si.layer () < si.layout ()->layers ()) {
// try to preserve the layer properties
layout.set_properties (layer_index, si.layout ()->get_properties (si.layer ()));
}
return layer_index;
}
DeepLayer DeepShapeStore::create_polygon_layer (const db::RecursiveShapeIterator &si, double max_area_ratio, size_t max_vertex_count, const db::ICplxTrans &trans)
{
if (max_area_ratio == 0.0) {
@ -528,7 +523,7 @@ DeepLayer DeepShapeStore::create_polygon_layer (const db::RecursiveShapeIterator
db::Layout &layout = m_layouts[layout_index]->layout;
db::HierarchyBuilder &builder = m_layouts[layout_index]->builder;
unsigned int layer_index = layout.insert_layer ();
unsigned int layer_index = init_layer (layout, si);
builder.set_target_layer (layer_index);
// The chain of operators for producing clipped and reduced polygon references
@ -540,6 +535,7 @@ DeepLayer DeepShapeStore::create_polygon_layer (const db::RecursiveShapeIterator
try {
tl::SelfTimer timer (tl::verbosity () >= 41, tl::to_string (tr ("Building working hierarchy")));
db::LayoutLocker ll (&layout, true /*no update*/);
builder.set_shape_receiver (&clip);
db::RecursiveShapeIterator (si).push (& builder);
@ -553,17 +549,6 @@ DeepLayer DeepShapeStore::create_polygon_layer (const db::RecursiveShapeIterator
return DeepLayer (this, layout_index, layer_index);
}
DeepLayer DeepShapeStore::empty_layer (unsigned int layout_index) const
{
return DeepLayer (const_cast<DeepShapeStore *> (this), layout_index, m_layouts[layout_index]->empty_layer ());
}
DeepLayer DeepShapeStore::empty_layer () const
{
require_singular ();
return empty_layer (0);
}
DeepLayer DeepShapeStore::create_custom_layer (const db::RecursiveShapeIterator &si, HierarchyBuilderShapeReceiver *pipe, const db::ICplxTrans &trans)
{
unsigned int layout_index = layout_for_iter (si, trans);
@ -571,13 +556,14 @@ DeepLayer DeepShapeStore::create_custom_layer (const db::RecursiveShapeIterator
db::Layout &layout = m_layouts[layout_index]->layout;
db::HierarchyBuilder &builder = m_layouts[layout_index]->builder;
unsigned int layer_index = layout.insert_layer ();
unsigned int layer_index = init_layer (layout, si);
builder.set_target_layer (layer_index);
// Build the working hierarchy from the recursive shape iterator
try {
tl::SelfTimer timer (tl::verbosity () >= 41, tl::to_string (tr ("Building working hierarchy")));
db::LayoutLocker ll (&layout, true /*no update*/);
builder.set_shape_receiver (pipe);
db::RecursiveShapeIterator (si).push (& builder);
@ -624,7 +610,7 @@ DeepLayer DeepShapeStore::create_edge_layer (const db::RecursiveShapeIterator &s
db::Layout &layout = m_layouts[layout_index]->layout;
db::HierarchyBuilder &builder = m_layouts[layout_index]->builder;
unsigned int layer_index = layout.insert_layer ();
unsigned int layer_index = init_layer (layout, si);
builder.set_target_layer (layer_index);
// The chain of operators for producing edges
@ -634,6 +620,7 @@ DeepLayer DeepShapeStore::create_edge_layer (const db::RecursiveShapeIterator &s
try {
tl::SelfTimer timer (tl::verbosity () >= 41, tl::to_string (tr ("Building working hierarchy")));
db::LayoutLocker ll (&layout, true /*no update*/);
builder.set_shape_receiver (&refs);
db::RecursiveShapeIterator (si).push (& builder);
@ -654,7 +641,7 @@ DeepLayer DeepShapeStore::create_edge_pair_layer (const db::RecursiveShapeIterat
db::Layout &layout = m_layouts[layout_index]->layout;
db::HierarchyBuilder &builder = m_layouts[layout_index]->builder;
unsigned int layer_index = layout.insert_layer ();
unsigned int layer_index = init_layer (layout, si);
builder.set_target_layer (layer_index);
// The chain of operators for producing the edge pairs
@ -664,6 +651,7 @@ DeepLayer DeepShapeStore::create_edge_pair_layer (const db::RecursiveShapeIterat
try {
tl::SelfTimer timer (tl::verbosity () >= 41, tl::to_string (tr ("Building working hierarchy")));
db::LayoutLocker ll (&layout, true /*no update*/);
builder.set_shape_receiver (&refs);
db::RecursiveShapeIterator (si).push (& builder);
@ -697,7 +685,7 @@ DeepShapeStore::issue_variants (unsigned int layout_index, const std::map<db::ce
}
const db::CellMapping &
DeepShapeStore::cell_mapping_to_original (unsigned int layout_index, db::Layout *into_layout, db::cell_index_type into_cell, const std::set<db::cell_index_type> *excluded_cells)
DeepShapeStore::cell_mapping_to_original (unsigned int layout_index, db::Layout *into_layout, db::cell_index_type into_cell, const std::set<db::cell_index_type> *excluded_cells, const std::set<db::cell_index_type> *included_cells)
{
const db::Layout *source_layout = &m_layouts [layout_index]->layout;
if (source_layout->begin_top_down () == source_layout->end_top_cells ()) {
@ -758,7 +746,7 @@ DeepShapeStore::cell_mapping_to_original (unsigned int layout_index, db::Layout
// Add new cells for the variants and (possible) devices which are cells added during the device
// extraction process
cm->second.create_missing_mapping (*into_layout, into_cell, *source_layout, source_top, excluded_cells);
cm->second.create_missing_mapping (*into_layout, into_cell, *source_layout, source_top, excluded_cells, included_cells);
}

View File

@ -343,19 +343,6 @@ public:
*/
DeepLayer create_copy (const DeepLayer &source, HierarchyBuilderShapeReceiver *pipe);
/**
* @brief Gets the empty working layer
*
* This method will deliver an empty layer for the given layout index. CAUTION: don't modify this layer as it may
* be reused.
*/
DeepLayer empty_layer (unsigned int layout_index) const;
/**
* @brief Gets the empty working layer for the singular layout
*/
DeepLayer empty_layer () const;
/**
* @brief Inserts the deep layer's shapes into some target layout
*/
@ -375,9 +362,11 @@ public:
* "layout_index" is the layout to return to it's original. "into_layout" is the original layout, "into_cell"
* the original cell.
*
* "excluded_cells" - if not 0 - will exclude the given cells (and flatten them).
* "excluded_cells" - if not 0 - will exclude the given cells
*
* "included_cells" - if not 0 - will only include the given cells in the cell mapping
*/
const db::CellMapping &cell_mapping_to_original (unsigned int layout_index, db::Layout *into_layout, db::cell_index_type into_cell, const std::set<db::cell_index_type> *excluded_cells = 0);
const db::CellMapping &cell_mapping_to_original (unsigned int layout_index, db::Layout *into_layout, db::cell_index_type into_cell, const std::set<db::cell_index_type> *excluded_cells = 0, const std::set<db::cell_index_type> *included_cells = 0);
/**
* @brief Create cell variants from the given variant collector

View File

@ -67,7 +67,7 @@ Device &Device::operator= (const Device &other)
{
if (this != &other) {
m_name = other.m_name;
m_position = other.m_position;
m_trans = other.m_trans;
m_parameters = other.m_parameters;
mp_device_class = other.mp_device_class;
mp_device_abstract = other.mp_device_abstract;
@ -97,9 +97,9 @@ void Device::set_name (const std::string &n)
}
}
void Device::set_position (const db::DPoint &pt)
void Device::set_trans (const db::DCplxTrans &tr)
{
m_position = pt;
m_trans = tr;
}
void Device::set_terminal_ref_for_terminal (size_t terminal_id, Net::terminal_iterator iter)
@ -187,4 +187,103 @@ void Device::set_parameter_value (const std::string &name, double v)
}
}
void Device::add_others_terminals (unsigned int this_terminal, db::Device *other, unsigned int other_terminal)
{
std::vector<DeviceReconnectedTerminal> &terminals = m_reconnected_terminals [this_terminal];
std::map<unsigned int, std::vector<DeviceReconnectedTerminal> >::const_iterator ot = other->m_reconnected_terminals.find (other_terminal);
if (ot == other->m_reconnected_terminals.end ()) {
terminals.push_back (DeviceReconnectedTerminal (other_abstracts ().size () + 1, other_terminal));
} else {
size_t n = terminals.size ();
terminals.insert (terminals.end (), ot->second.begin (), ot->second.end ());
while (n < terminals.size ()) {
terminals [n].device_index += other_abstracts ().size () + 1;
++n;
}
}
}
void Device::init_terminal_routes ()
{
if (! device_class ()) {
return;
}
size_t n = device_class ()->terminal_definitions ().size ();
for (size_t i = 0; i < n; ++i) {
m_reconnected_terminals [i].push_back (DeviceReconnectedTerminal (0, i));
}
}
void Device::join_terminals (unsigned int this_terminal, db::Device *other, unsigned int other_terminal)
{
if (m_reconnected_terminals.empty ()) {
init_terminal_routes ();
}
other->connect_terminal (other_terminal, 0);
add_others_terminals (this_terminal, other, other_terminal);
}
void Device::reroute_terminal (unsigned int this_terminal, db::Device *other, unsigned int from_other_terminal, unsigned int other_terminal)
{
// TODO: the internal connection is not represented currently ...
if (m_reconnected_terminals.empty ()) {
init_terminal_routes ();
}
if (! m_reconnected_terminals.empty ()) {
m_reconnected_terminals.erase (this_terminal);
}
add_others_terminals (this_terminal, other, other_terminal);
connect_terminal (this_terminal, other->net_for_terminal (other_terminal));
other->connect_terminal (from_other_terminal, 0);
other->connect_terminal (other_terminal, 0);
}
void Device::join_device (db::Device *other)
{
db::DCplxTrans d = trans ().inverted () * other->trans ();
m_other_abstracts.reserve (m_other_abstracts.size () + 1 + other->m_other_abstracts.size ());
m_other_abstracts.push_back (db::DeviceAbstractRef (other->device_abstract (), d));
for (std::vector<db::DeviceAbstractRef>::const_iterator a = other->m_other_abstracts.begin (); a != other->m_other_abstracts.end (); ++a) {
m_other_abstracts.push_back (*a);
m_other_abstracts.back ().trans = d * m_other_abstracts.back ().trans;
}
}
static db::DeviceAbstract *map_da (const std::map<const DeviceAbstract *, DeviceAbstract *> &map, const db::DeviceAbstract *da)
{
if (! da) {
return 0;
} else {
std::map<const DeviceAbstract *, DeviceAbstract *>::const_iterator m = map.find (da);
tl_assert (m != map.end ());
return m->second;
}
}
void Device::translate_device_abstracts (const std::map<const DeviceAbstract *, DeviceAbstract *> &map)
{
set_device_abstract (map_da (map, device_abstract ()));
for (std::vector<db::DeviceAbstractRef>::iterator a = m_other_abstracts.begin (); a != m_other_abstracts.end (); ++a) {
a->device_abstract = map_da (map, a->device_abstract);
}
}
}

View File

@ -26,6 +26,8 @@
#include "dbCommon.h"
#include "dbNet.h"
#include "dbPoint.h"
#include "dbVector.h"
#include "dbTrans.h"
#include "tlObject.h"
@ -38,6 +40,51 @@ class Circuit;
class DeviceClass;
class DeviceAbstract;
/**
* @brief A structure describing a terminal reference into another device abstract
*/
struct DeviceReconnectedTerminal
{
DeviceReconnectedTerminal (size_t _device_index, unsigned int _other_terminal_id)
: device_index (_device_index), other_terminal_id (_other_terminal_id)
{
// .. nothing yet ..
}
DeviceReconnectedTerminal ()
: device_index (0), other_terminal_id (0)
{
// .. nothing yet ..
}
size_t device_index;
unsigned int other_terminal_id;
};
/**
* @brief A structure describing a reference to another device abstract
*
* This structure is used within Device to reference more than the standard
* device abstract.
*/
struct DeviceAbstractRef
{
DeviceAbstractRef (const db::DeviceAbstract *_device_abstract, const db::DCplxTrans &_trans)
: device_abstract (_device_abstract), trans (_trans)
{
// .. nothing yet ..
}
DeviceAbstractRef ()
: device_abstract (0), trans ()
{
// .. nothing yet ..
}
const db::DeviceAbstract *device_abstract;
db::DCplxTrans trans;
};
/**
* @brief An actual device
*
@ -166,19 +213,19 @@ public:
/**
* @brief Sets the device position
* The device position should be the center of the recognition shape or something similar.
* The device position should be the center and orientation of the recognition shape or something similar.
* Giving the device a position allows combining multiple devices with the same
* relative geometry into a single cell.
* The position has to be given in micrometer units.
* The transformation has to be given in micrometer units.
*/
void set_position (const db::DPoint &pos);
void set_trans (const db::DCplxTrans &tr);
/**
* @brief Gets the device position
*/
const db::DPoint &position () const
const db::DCplxTrans &trans () const
{
return m_position;
return m_trans;
}
/**
@ -228,6 +275,80 @@ public:
*/
void set_parameter_value (const std::string &name, double v);
/**
* @brief Used for device combination: join terminals with other device
*/
void join_terminals (unsigned int this_terminal, db::Device *other, unsigned int other_terminal);
/**
* @brief Used for device combination: reroute terminal to other device
*
* This will disconnect "this_terminal" from the device and make a connection to
* "other_terminal" of the "other" device instead.
*
* An internal connection between "this_terminal" and "from_other_terminal" is
* implied.
*/
void reroute_terminal (unsigned int this_terminal, db::Device *other, unsigned int from_other_terminal, unsigned int other_terminal);
/**
* @brief Gets the set of other terminal references
*
* This method will return 0 if the device isn't a combined device or the given terminal
* is not connector to a different abstract.
*
* The returned vector (if any) is a complete list of terminals connected to the given
* logical device terminal.
*/
const std::vector<DeviceReconnectedTerminal> *reconnected_terminals_for (unsigned int this_terminal) const
{
std::map<unsigned int, std::vector<DeviceReconnectedTerminal> >::const_iterator t = m_reconnected_terminals.find (this_terminal);
if (t != m_reconnected_terminals.end ()) {
return & t->second;
} else {
return 0;
}
}
/**
* @brief Gets the map of reconnected terminals
*/
const std::map<unsigned int, std::vector<DeviceReconnectedTerminal> > &reconnected_terminals () const
{
return m_reconnected_terminals;
}
/**
* @brief Gets the map of reconnected terminals (non-const version)
*
* NOTE: don't use this method to modify this container! It's provided for persistence implementation only.
*/
std::map<unsigned int, std::vector<DeviceReconnectedTerminal> > &reconnected_terminals ()
{
return m_reconnected_terminals;
}
/**
* @brief Gets the set of other device abstracts
*
* This list does not include the intrinsic original abstract of the device.
* This vector is non-empty if this device is a combined one.
*/
const std::vector<DeviceAbstractRef> &other_abstracts () const
{
return m_other_abstracts;
}
/**
* @brief Gets the set of other device abstracts (non-const version)
*
* NOTE: don't use this method to modify this container! It's provided for persistence implementation only.
*/
std::vector<DeviceAbstractRef> &other_abstracts ()
{
return m_other_abstracts;
}
private:
friend class Circuit;
friend class Net;
@ -235,11 +356,23 @@ private:
DeviceClass *mp_device_class;
DeviceAbstract *mp_device_abstract;
std::string m_name;
db::DPoint m_position;
db::DCplxTrans m_trans;
std::vector<Net::terminal_iterator> m_terminal_refs;
std::vector<double> m_parameters;
size_t m_id;
Circuit *mp_circuit;
std::vector<DeviceAbstractRef> m_other_abstracts;
std::map<unsigned int, std::vector<DeviceReconnectedTerminal> > m_reconnected_terminals;
/**
* @brief Translates the device abstracts
*/
void translate_device_abstracts (const std::map<const DeviceAbstract *, DeviceAbstract *> &map);
/**
* @brief Joins this device with another
*/
void join_device (db::Device *other);
/**
* @brief Sets the terminal reference for a specific terminal
@ -258,6 +391,9 @@ private:
* @brief Sets the circuit
*/
void set_circuit (Circuit *circuit);
void add_others_terminals (unsigned int this_terminal, db::Device *other, unsigned int other_terminal);
void init_terminal_routes ();
};
}

View File

@ -22,6 +22,7 @@
#include "dbDeviceClass.h"
#include "dbDevice.h"
#include "tlClassRegistry.h"
namespace db
{
@ -273,4 +274,29 @@ bool DeviceClass::equal (const db::Device &a, const db::Device &b)
}
}
// --------------------------------------------------------------------------------
// DeviceClassTemplateBase class implementation
DeviceClassTemplateBase *
DeviceClassTemplateBase::template_by_name (const std::string &name)
{
for (tl::Registrar<db::DeviceClassTemplateBase>::iterator i = tl::Registrar<db::DeviceClassTemplateBase>::begin (); i != tl::Registrar<db::DeviceClassTemplateBase>::end (); ++i) {
if (i->name () == name) {
return i.operator-> ();
}
}
return 0;
}
DeviceClassTemplateBase *
DeviceClassTemplateBase::is_a (const db::DeviceClass *dc)
{
for (tl::Registrar<db::DeviceClassTemplateBase>::iterator i = tl::Registrar<db::DeviceClassTemplateBase>::begin (); i != tl::Registrar<db::DeviceClassTemplateBase>::end (); ++i) {
if (i->is_of (dc)) {
return i.operator-> ();
}
}
return 0;
}
}

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_is_primary (true)
: m_name (), m_description (), m_default_value (0.0), m_id (0), m_is_primary (true), m_si_scaling (1.0)
{
// .. 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, bool is_primary = true)
: m_name (name), m_description (description), m_default_value (default_value), m_id (0), m_is_primary (is_primary)
DeviceParameterDefinition (const std::string &name, const std::string &description, double default_value = 0.0, bool is_primary = true, double si_scaling = 1.0)
: m_name (name), m_description (description), m_default_value (default_value), m_id (0), m_is_primary (is_primary), m_si_scaling (si_scaling)
{
// .. nothing yet ..
}
@ -178,6 +178,17 @@ public:
return m_default_value;
}
/**
* @brief Gets the SI unit scaling factor
*
* Some parameters are given in micrometers for example. This
* scaling factor gives the translation to SI units (1e-6 for micrometers).
*/
double si_scaling () const
{
return m_si_scaling;
}
/**
* @brief Sets the parameter description
*/
@ -220,6 +231,7 @@ private:
double m_default_value;
size_t m_id;
bool m_is_primary;
double m_si_scaling;
void set_id (size_t id)
{
@ -536,6 +548,74 @@ private:
}
};
/**
* @brief A device class template
*
* This is a registered class which provides a device class template.
* The built-in classes serve as templates and registering a template
* allows regenerating the class from an abstract description (template
* name).
*
* NOTE: device classes derived from one of the built-in classes
* cannot be distinguished from pure built-in classes. Entirely
* customized classes are treated as "non-template based" (i.e.
* "is_a" returns 0).
*/
class DB_PUBLIC DeviceClassTemplateBase
{
public:
DeviceClassTemplateBase (const std::string &name)
: m_name (name)
{
// .. nothing yet ..
}
virtual ~DeviceClassTemplateBase () { }
const std::string &name () const
{
return m_name;
}
virtual bool is_of (const db::DeviceClass *) const = 0;
virtual DeviceClass *create () const = 0;
static DeviceClassTemplateBase *template_by_name (const std::string &name);
static DeviceClassTemplateBase *is_a (const db::DeviceClass *dc);
private:
std::string m_name;
DeviceClassTemplateBase (const DeviceClassTemplateBase &);
DeviceClassTemplateBase &operator= (const DeviceClassTemplateBase &);
};
template <class T>
class DB_PUBLIC_TEMPLATE device_class_template
: public DeviceClassTemplateBase
{
public:
device_class_template (const std::string &name)
: DeviceClassTemplateBase (name)
{
// .. nothing yet ..
}
virtual bool is_of (const db::DeviceClass *dc) const
{
return dynamic_cast<const T *> (dc) != 0;
}
virtual DeviceClass *create () const
{
return new T ();
}
private:
device_class_template (const device_class_template &);
device_class_template &operator= (const device_class_template &);
};
}
#endif

View File

@ -145,6 +145,12 @@ Connectivity::global_net_id (const std::string &gn)
return id;
}
size_t
Connectivity::global_nets () const
{
return m_global_net_names.size ();
}
Connectivity::layer_iterator
Connectivity::begin_layers () const
{
@ -1270,6 +1276,11 @@ private:
for (db::CellInstArray::iterator ii2 = i2.begin_touching (ib1.transformed (t2i), mp_layout); ! ii2.at_end (); ++ii2) {
db::ICplxTrans tt2 = t2 * i2.complex_trans (*ii2);
if (i1.cell_index () == i2.cell_index () && tt1 == tt2) {
// skip interactions between identical instances (duplicate instance removal)
continue;
}
box_type ib2 = bb2.transformed (tt2);
box_type common12 = ib1 & ib2 & common;
@ -1653,6 +1664,8 @@ hier_clusters<T>::make_path (const db::Layout &layout, const db::Cell &cell, siz
connected_clusters<T> &child_cc = clusters_per_cell (p->inst_cell_index ());
if (child_cc.is_root (id)) {
std::set<std::pair<db::cell_index_type, ClusterInstance> > seen; // to avoid duplicate connections
const db::Cell &child_cell = layout.cell (p->inst_cell_index ());
for (db::Cell::parent_inst_iterator pi = child_cell.begin_parent_insts (); ! pi.at_end (); ++pi) {
@ -1662,7 +1675,7 @@ hier_clusters<T>::make_path (const db::Layout &layout, const db::Cell &cell, siz
for (db::CellInstArray::iterator pii = child_inst.begin (); ! pii.at_end (); ++pii) {
ClusterInstance ci2 (id, child_inst.cell_index (), child_inst.complex_trans (*pii), child_inst.prop_id ());
if (cell.cell_index () != pi->parent_cell_index () || ci != ci2) {
if ((cell.cell_index () != pi->parent_cell_index () || ci != ci2) && seen.find (std::make_pair (pi->parent_cell_index (), ci2)) == seen.end ()) {
size_t id_dummy;
@ -1676,6 +1689,7 @@ hier_clusters<T>::make_path (const db::Layout &layout, const db::Cell &cell, siz
}
parent_cc.add_connection (id_dummy, ci2);
seen.insert (std::make_pair (pi->parent_cell_index (), ci2));
}
@ -1709,6 +1723,8 @@ hier_clusters<T>::make_path (const db::Layout &layout, const db::Cell &cell, siz
connected_clusters<T> &child_cc = clusters_per_cell (p->inst_cell_index ());
if (child_cc.is_root (id)) {
std::set<std::pair<db::cell_index_type, ClusterInstance> > seen; // to avoid duplicate connections
const db::Cell &child_cell = layout.cell (p->inst_cell_index ());
for (db::Cell::parent_inst_iterator pi = child_cell.begin_parent_insts (); ! pi.at_end (); ++pi) {
@ -1717,22 +1733,27 @@ hier_clusters<T>::make_path (const db::Layout &layout, const db::Cell &cell, siz
connected_clusters<T> &parent_cc = clusters_per_cell (pi->parent_cell_index ());
for (db::CellInstArray::iterator pii = child_inst.begin (); ! pii.at_end (); ++pii) {
size_t id_dummy;
const typename db::local_cluster<T>::global_nets &gn = child_cc.cluster_by_id (id).get_global_nets ();
if (gn.empty ()) {
id_dummy = parent_cc.insert_dummy ();
} else {
local_cluster<T> *lc = parent_cc.insert ();
lc->set_global_nets (gn);
id_dummy = lc->id ();
}
ClusterInstance ci2 (id, child_inst.cell_index (), child_inst.complex_trans (*pii), child_inst.prop_id ());
parent_cc.add_connection (id_dummy, ci2);
if (seen.find (std::make_pair (pi->parent_cell_index (), ci2)) == seen.end ()) {
size_t id_dummy;
const typename db::local_cluster<T>::global_nets &gn = child_cc.cluster_by_id (id).get_global_nets ();
if (gn.empty ()) {
id_dummy = parent_cc.insert_dummy ();
} else {
local_cluster<T> *lc = parent_cc.insert ();
lc->set_global_nets (gn);
id_dummy = lc->id ();
}
parent_cc.add_connection (id_dummy, ci2);
seen.insert (std::make_pair (pi->parent_cell_index (), ci2));
if (pci == pi->parent_cell_index () && ci == ci2) {
id_new = id_dummy;
}
if (pci == pi->parent_cell_index () && ci == ci2) {
id_new = id_dummy;
}
}
@ -1821,7 +1842,7 @@ hier_clusters<T>::build_local_cluster (const db::Layout &layout, const db::Cell
tl::SelfTimer timer (tl::verbosity () > m_base_verbosity + 20, msg);
connected_clusters<T> &local = m_per_cell_clusters [cell.cell_index ()];
local.build_clusters (cell, shape_flags, conn, attr_equivalence, tl::verbosity () >= m_base_verbosity + 30);
local.build_clusters (cell, shape_flags, conn, attr_equivalence, true);
}
template <class T>
@ -1950,8 +1971,7 @@ hier_clusters<T>::build_hier_connections (cell_clusters_box_converter<T> &cbc, c
static std::string desc = tl::to_string (tr ("Instance to instance treatment"));
tl::SelfTimer timer (tl::verbosity () > m_base_verbosity + 30, desc);
bool report_progress = tl::verbosity () >= m_base_verbosity + 30;
db::box_scanner<db::Instance, unsigned int> bs (report_progress, desc);
db::box_scanner<db::Instance, unsigned int> bs (true, desc);
for (std::vector<db::Instance>::const_iterator inst = inst_storage.begin (); inst != inst_storage.end (); ++inst) {
bs.insert (inst.operator-> (), 0);
@ -1969,8 +1989,7 @@ hier_clusters<T>::build_hier_connections (cell_clusters_box_converter<T> &cbc, c
static std::string desc = tl::to_string (tr ("Local to instance treatment"));
tl::SelfTimer timer (tl::verbosity () > m_base_verbosity + 30, desc);
bool report_progress = tl::verbosity () >= m_base_verbosity + 30;
db::box_scanner2<db::local_cluster<T>, unsigned int, db::Instance, unsigned int> bs2 (report_progress, desc);
db::box_scanner2<db::local_cluster<T>, unsigned int, db::Instance, unsigned int> bs2 (true, desc);
for (typename connected_clusters<T>::const_iterator c = local.begin (); c != local.end (); ++c) {
@ -2025,8 +2044,9 @@ hier_clusters<T>::build_hier_connections (cell_clusters_box_converter<T> &cbc, c
// insert the global nets from here
for (typename db::connected_clusters<T>::const_iterator cl = local.begin (); cl != local.end (); ++cl) {
if (! cl->get_global_nets ().empty ()) {
global_net_clusters.add (cl->get_global_nets (), db::ClusterInstance (cl->id ()));
const typename db::local_cluster<T>::global_nets &gn = cl->get_global_nets ();
if (! gn.empty ()) {
global_net_clusters.add (gn, db::ClusterInstance (cl->id ()));
}
}
@ -2153,6 +2173,10 @@ template <class T>
recursive_cluster_shape_iterator<T>::recursive_cluster_shape_iterator (const hier_clusters<T> &hc, unsigned int layer, db::cell_index_type ci, typename local_cluster<T>::id_type id)
: mp_hc (&hc), m_layer (layer), m_id (id)
{
if (id == 0) {
return;
}
down (ci, id, db::ICplxTrans ());
while (m_shape_iter.at_end () && ! m_conn_iter_stack.empty ()) {

View File

@ -131,6 +131,11 @@ public:
*/
size_t global_net_id (const std::string &gn);
/**
* @brief Gets the number of global nets (it's also the max ID + 1)
*/
size_t global_nets () const;
/**
* @brief Begin iterator for the layers involved
*/

View File

@ -1562,6 +1562,17 @@ public:
}
}
/**
* @brief Cancel the "in changes" state (see "start_changes")
* This version does not force an update
*/
void end_changes_no_update ()
{
if (m_invalid > 0) {
--m_invalid;
}
}
/**
* @brief Tell if the layout object is under construction
*
@ -1763,8 +1774,8 @@ mem_stat (MemStatistics *stat, MemStatistics::purpose_t purpose, int cat, const
class DB_PUBLIC LayoutLocker
{
public:
explicit LayoutLocker (db::Layout *layout = 0)
: mp_layout (layout)
explicit LayoutLocker (db::Layout *layout = 0, bool no_update = false)
: mp_layout (layout), m_no_update (no_update)
{
if (mp_layout) {
mp_layout->start_changes ();
@ -1773,13 +1784,11 @@ public:
~LayoutLocker ()
{
if (mp_layout) {
mp_layout->end_changes ();
}
set (0, false);
}
LayoutLocker (const LayoutLocker &other)
: mp_layout (other.mp_layout)
: mp_layout (other.mp_layout), m_no_update (other.m_no_update)
{
if (mp_layout) {
mp_layout->start_changes ();
@ -1792,20 +1801,30 @@ public:
return *this;
}
if (mp_layout) {
mp_layout->end_changes ();
}
mp_layout = other.mp_layout;
if (mp_layout) {
mp_layout->start_changes ();
}
set (other.mp_layout, other.m_no_update);
return *this;
}
private:
db::Layout *mp_layout;
bool m_no_update;
void set (db::Layout *layout, bool no_update)
{
if (mp_layout) {
if (m_no_update) {
mp_layout->end_changes_no_update ();
} else {
mp_layout->end_changes ();
}
}
mp_layout = layout;
m_no_update = no_update;
if (mp_layout) {
mp_layout->start_changes ();
}
}
};
}

View File

@ -26,6 +26,11 @@
#include "dbDeepRegion.h"
#include "dbShapeRepository.h"
#include "dbCellMapping.h"
#include "dbLayoutToNetlistWriter.h"
#include "dbLayoutToNetlistReader.h"
#include "dbLayoutVsSchematic.h"
#include "dbLayoutToNetlistFormatDefs.h"
#include "dbLayoutVsSchematicFormatDefs.h"
namespace db
{
@ -87,6 +92,14 @@ LayoutToNetlist::~LayoutToNetlist ()
m_net_clusters.clear ();
}
void LayoutToNetlist::keep_dss ()
{
if (mp_dss.get () && ! mp_internal_dss.get ()) {
mp_dss->keep ();
mp_internal_dss.reset (mp_dss.get ());
}
}
void LayoutToNetlist::init ()
{
dss ().set_text_enlargement (1);
@ -155,7 +168,9 @@ db::Region *LayoutToNetlist::make_text_layer (unsigned int layer_index, const st
si.shape_flags (db::ShapeIterator::Texts);
std::auto_ptr <db::Region> region (new db::Region (si, dss ()));
register_layer (*region, n);
if (! n.empty ()) {
register_layer (*region, n);
}
return region.release ();
}
@ -166,7 +181,9 @@ db::Region *LayoutToNetlist::make_polygon_layer (unsigned int layer_index, const
si.shape_flags (db::ShapeIterator::Paths | db::ShapeIterator::Polygons | db::ShapeIterator::Boxes);
std::auto_ptr <db::Region> region (new db::Region (si, dss ()));
register_layer (*region, n);
if (! n.empty ()) {
register_layer (*region, n);
}
return region.release ();
}
@ -188,7 +205,7 @@ void LayoutToNetlist::connect (const db::Region &l)
}
if (! is_persisted (l)) {
throw (tl::Exception (tl::to_string (tr ("Only named layers can be used in intra-layer connectivity for netlist extraction"))));
register_layer (l, make_new_name ());
}
// we need to keep a reference, so we can safely delete the region
@ -204,10 +221,10 @@ void LayoutToNetlist::connect (const db::Region &a, const db::Region &b)
throw tl::Exception (tl::to_string (tr ("The netlist has already been extracted")));
}
if (! is_persisted (a)) {
throw (tl::Exception (tl::to_string (tr ("Only named layers can be used in inter-layer connectivity (first layer) for netlist extraction"))));
register_layer (a, make_new_name ());
}
if (! is_persisted (b)) {
throw (tl::Exception (tl::to_string (tr ("Only named layers can be used in inter-layer connectivity (second layer) for netlist extraction"))));
register_layer (b, make_new_name ());
}
// we need to keep a reference, so we can safely delete the region
@ -225,7 +242,7 @@ size_t LayoutToNetlist::connect_global (const db::Region &l, const std::string &
throw tl::Exception (tl::to_string (tr ("The netlist has already been extracted")));
}
if (! is_persisted (l)) {
throw (tl::Exception (tl::to_string (tr ("Only named layers can be used in global connectivity for netlist extraction"))));
register_layer (l, make_new_name ());
}
// we need to keep a reference, so we can safely delete the region
@ -320,11 +337,7 @@ void LayoutToNetlist::register_layer (const db::Region &region, const std::strin
db::DeepRegion *delegate = dynamic_cast<db::DeepRegion *> (region.delegate());
if (! delegate) {
if (region.empty ()) {
dl = dss ().empty_layer (m_layout_index);
} else {
dl = dss ().create_from_flat (region, true);
}
dl = dss ().create_from_flat (region, true);
} else {
@ -343,6 +356,29 @@ void LayoutToNetlist::register_layer (const db::Region &region, const std::strin
m_name_of_layer [dl.layer ()] = n;
}
std::string LayoutToNetlist::make_new_name (const std::string &stem)
{
int m = std::numeric_limits<int>::max () / 2 + 1;
int n = m;
std::string name;
while (m > 0) {
m /= 2;
name = stem;
name += std::string ("$");
name += tl::to_string (n - m);
if (m_named_regions.find (name) == m_named_regions.end ()) {
n -= m;
}
}
return name;
}
std::string LayoutToNetlist::name (const db::Region &region) const
{
std::map<unsigned int, std::string>::const_iterator n = m_name_of_layer.find (layer_of (region));
@ -396,9 +432,6 @@ db::DeepLayer LayoutToNetlist::deep_layer_of (const db::Region &region) const
std::pair<bool, db::DeepLayer> lff = dss ().layer_for_flat (region);
if (lff.first) {
return lff.second;
} else if (region.empty ()) {
// provide a substitute empty layer for empty
return dss ().empty_layer (m_layout_index);
} else {
throw (tl::Exception (tl::to_string (tr ("Non-hierarchical layers cannot be used in netlist extraction"))));
}
@ -413,7 +446,7 @@ unsigned int LayoutToNetlist::layer_of (const db::Region &region) const
return deep_layer_of (region).layer ();
}
db::CellMapping LayoutToNetlist::cell_mapping_into (db::Layout &layout, db::Cell &cell, bool with_device_cells)
db::CellMapping LayoutToNetlist::make_cell_mapping_into (db::Layout &layout, db::Cell &cell, const std::vector<const db::Net *> *nets, bool with_device_cells)
{
std::set<db::cell_index_type> device_cells;
if (! with_device_cells && mp_netlist.get ()) {
@ -422,7 +455,31 @@ db::CellMapping LayoutToNetlist::cell_mapping_into (db::Layout &layout, db::Cell
}
}
return dss ().cell_mapping_to_original (m_layout_index, &layout, cell.cell_index (), &device_cells);
std::set<db::cell_index_type> net_cells;
if (nets) {
// Compute the "included cell" list for cell_mapping_to_original: these are all cells which
// are required to represent the net hierarchically.
for (std::vector<const db::Net *>::const_iterator n = nets->begin (); n != nets->end (); ++n) {
const db::Net *net = *n;
db::cell_index_type net_cell = net->circuit ()->cell_index ();
if (net_cells.find (net_cell) == net_cells.end ()) {
net_cells.insert (net_cell);
internal_layout()->cell (net_cell).collect_caller_cells (net_cells);
}
}
}
return dss ().cell_mapping_to_original (m_layout_index, &layout, cell.cell_index (), &device_cells, nets ? &net_cells : 0);
}
db::CellMapping LayoutToNetlist::cell_mapping_into (db::Layout &layout, db::Cell &cell, const std::vector<const db::Net *> &nets, bool with_device_cells)
{
return make_cell_mapping_into (layout, cell, &nets, with_device_cells);
}
db::CellMapping LayoutToNetlist::cell_mapping_into (db::Layout &layout, db::Cell &cell, bool with_device_cells)
{
return make_cell_mapping_into (layout, cell, 0, with_device_cells);
}
db::CellMapping LayoutToNetlist::const_cell_mapping_into (const db::Layout &layout, const db::Cell &cell)
@ -436,6 +493,36 @@ db::CellMapping LayoutToNetlist::const_cell_mapping_into (const db::Layout &layo
return cm;
}
std::map<unsigned int, const db::Region *>
LayoutToNetlist::create_layermap (db::Layout &target_layout, int ln) const
{
std::map<unsigned int, const db::Region *> lm;
if (! internal_layout ()) {
return lm;
}
const db::Layout &source_layout = *internal_layout ();
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) {
layers_to_copy.insert (*layer);
}
for (std::set<unsigned int>::const_iterator l = layers_to_copy.begin (); l != layers_to_copy.end (); ++l) {
const db::LayerProperties &lp = source_layout.get_properties (*l);
unsigned int tl;
if (! lp.is_null ()) {
tl = target_layout.insert_layer (lp);
} else {
tl = target_layout.insert_layer (db::LayerProperties (ln++, 0, name (*l)));
}
lm.insert (std::make_pair (tl, const_cast<LayoutToNetlist *> (this)->layer_by_index (*l)));
}
return lm;
}
db::Netlist *LayoutToNetlist::netlist () const
{
return mp_netlist.get ();
@ -455,13 +542,13 @@ namespace
}
template <class Tr>
static bool deliver_shape (const db::PolygonRef &, StopOnFirst, const Tr &)
static bool deliver_shape (const db::PolygonRef &, StopOnFirst, const Tr &, db::properties_id_type)
{
return false;
}
template <class Tr>
static bool deliver_shape (const db::PolygonRef &pr, db::Region &region, const Tr &tr)
static bool deliver_shape (const db::PolygonRef &pr, db::Region &region, const Tr &tr, db::properties_id_type /*propid*/)
{
if (pr.obj ().is_box ()) {
region.insert (pr.obj ().box ().transformed (pr.trans ()).transformed (tr));
@ -472,76 +559,102 @@ static bool deliver_shape (const db::PolygonRef &pr, db::Region &region, const T
}
template <class Tr>
static bool deliver_shape (const db::PolygonRef &pr, db::Shapes &shapes, const Tr &tr)
static bool deliver_shape (const db::PolygonRef &pr, db::Shapes &shapes, const Tr &tr, db::properties_id_type propid)
{
if (pr.obj ().is_box ()) {
shapes.insert (pr.obj ().box ().transformed (pr.trans ()).transformed (tr));
if (propid) {
shapes.insert (db::BoxWithProperties (pr.obj ().box ().transformed (pr.trans ()).transformed (tr), propid));
} else {
shapes.insert (pr.obj ().box ().transformed (pr.trans ()).transformed (tr));
}
} else {
db::Layout *layout = shapes.layout ();
if (layout) {
shapes.insert (db::PolygonRef (pr.obj ().transformed (pr.trans ()).transformed (tr), layout->shape_repository ()));
db::PolygonRef polygon_ref (pr.obj ().transformed (pr.trans ()).transformed (tr), layout->shape_repository ());
if (propid) {
shapes.insert (db::PolygonRefWithProperties (polygon_ref, propid));
} else {
shapes.insert (polygon_ref);
}
} else {
shapes.insert (pr.obj ().transformed (pr.trans ()).transformed (tr));
db::Polygon polygon (pr.obj ().transformed (pr.trans ()).transformed (tr));
if (propid) {
shapes.insert (db::PolygonWithProperties (polygon, propid));
} else {
shapes.insert (polygon);
}
}
}
return true;
}
template <class To>
static bool deliver_shapes_of_net_recursive (const db::Netlist * /*nl*/, const db::hier_clusters<db::PolygonRef> &clusters, db::cell_index_type ci, size_t cid, unsigned int layer_id, const db::ICplxTrans &tr, To &to)
template <class To, class Shape>
static bool deliver_shapes_of_net_recursive (const db::Netlist * /*nl*/, const db::hier_clusters<Shape> &clusters, db::cell_index_type ci, size_t cid, unsigned int layer_id, const db::ICplxTrans &tr, To &to, db::properties_id_type propid)
{
// deliver the net shapes
for (db::recursive_cluster_shape_iterator<db::PolygonRef> rci (clusters, layer_id, ci, cid); !rci.at_end (); ++rci) {
if (! deliver_shape (*rci, to, tr * rci.trans ())) {
for (db::recursive_cluster_shape_iterator<Shape> rci (clusters, layer_id, ci, cid); !rci.at_end (); ++rci) {
if (! deliver_shape (*rci, to, tr * rci.trans (), propid)) {
return false;
}
}
return true;
}
template <class To>
static bool deliver_shapes_of_net_nonrecursive (const db::Netlist *nl, const db::hier_clusters<db::PolygonRef> &clusters, db::cell_index_type ci, size_t cid, unsigned int layer_id, const db::ICplxTrans &tr, To &to)
template <class To, class Shape>
static bool deliver_shapes_of_net (bool recursive, const db::Netlist *nl, const db::hier_clusters<Shape> &clusters, db::cell_index_type ci, size_t cid, const std::map<unsigned int, To *> &lmap, const db::ICplxTrans &tr, db::properties_id_type propid)
{
// NOTE: this scheme will deliver the shapes from the cell, including (!)
// subcells that are purged
// shortcut
if (lmap.empty ()) {
return true;
}
db::cell_index_type prev_ci = ci;
const db::connected_clusters<Shape> &cc = clusters.clusters_per_cell (ci);
const db::local_cluster<Shape> &lc = cc.cluster_by_id (cid);
// deliver the net shapes
for (db::recursive_cluster_shape_iterator<db::PolygonRef> rci (clusters, layer_id, ci, cid); !rci.at_end (); ) {
db::cell_index_type cci = rci.cell_index ();
if (cci != prev_ci && cci != ci && (! nl || nl->circuit_by_cell_index (cci) || nl->device_abstract_by_cell_index (cci))) {
rci.skip_cell ();
} else {
if (! deliver_shape (*rci, to, tr * rci.trans ())) {
for (typename std::map<unsigned int, To *>::const_iterator l = lmap.begin (); l != lmap.end (); ++l) {
for (typename db::local_cluster<Shape>::shape_iterator s = lc.begin (l->first); ! s.at_end (); ++s) {
if (! deliver_shape (*s, *l->second, tr, propid)) {
return false;
}
prev_ci = cci;
++rci;
}
}
const typename db::connected_clusters<Shape>::connections_type &conn = cc.connections_for_cluster (cid);
for (typename db::connected_clusters<Shape>::connections_type::const_iterator c = conn.begin (); c != conn.end (); ) {
db::cell_index_type cci = c->inst_cell_index ();
if (! recursive && (! nl || nl->circuit_by_cell_index (cci) || nl->device_abstract_by_cell_index (cci))) {
// skip this cell in non-recursive mode (and all following instances of the same cell too)
typename db::connected_clusters<Shape>::connections_type::const_iterator cc = c;
while (++cc != conn.end ()) {
if (cc->inst_cell_index () != cci) {
break;
}
}
c = cc;
continue;
}
if (! deliver_shapes_of_net (recursive, nl, clusters, cci, c->id (), lmap, tr * c->inst_trans (), propid)) {
return false;
}
++c;
}
return true;
}
void LayoutToNetlist::shapes_of_net (const db::Net &net, const db::Region &of_layer, bool recursive, db::Shapes &to) const
void LayoutToNetlist::shapes_of_net (const db::Net &net, const db::Region &of_layer, bool recursive, db::Shapes &to, db::properties_id_type propid) const
{
unsigned int lid = layer_of (of_layer);
const db::Circuit *circuit = net.circuit ();
tl_assert (circuit != 0);
if (! recursive) {
deliver_shapes_of_net_nonrecursive (mp_netlist.get (), m_net_clusters, circuit->cell_index (), net.cluster_id (), lid, db::ICplxTrans (), to);
} else {
deliver_shapes_of_net_recursive (mp_netlist.get (), m_net_clusters, circuit->cell_index (), net.cluster_id (), lid, db::ICplxTrans (), to);
}
std::map<unsigned int, db::Shapes *> lmap;
lmap [lid] = &to;
deliver_shapes_of_net (recursive, mp_netlist.get (), m_net_clusters, circuit->cell_index (), net.cluster_id (), lmap, db::ICplxTrans (), propid);
}
db::Region *LayoutToNetlist::shapes_of_net (const db::Net &net, const db::Region &of_layer, bool recursive) const
@ -551,27 +664,25 @@ db::Region *LayoutToNetlist::shapes_of_net (const db::Net &net, const db::Region
tl_assert (circuit != 0);
std::auto_ptr<db::Region> res (new db::Region ());
std::map<unsigned int, db::Region *> lmap;
lmap [lid] = res.get ();
if (! recursive) {
deliver_shapes_of_net_nonrecursive (mp_netlist.get (), m_net_clusters, circuit->cell_index (), net.cluster_id (), lid, db::ICplxTrans (), *res);
} else {
deliver_shapes_of_net_recursive (mp_netlist.get (), m_net_clusters, circuit->cell_index (), net.cluster_id (), lid, db::ICplxTrans (), *res);
}
deliver_shapes_of_net (recursive, mp_netlist.get (), m_net_clusters, circuit->cell_index (), net.cluster_id (), lmap, db::ICplxTrans (), 0);
return res.release ();
}
void
LayoutToNetlist::build_net_rec (const db::Net &net, db::Layout &target, db::Cell &target_cell, const std::map<unsigned int, const db::Region *> &lmap, const char *net_cell_name_prefix, const char *cell_name_prefix, const char *device_cell_name_prefix, std::map<std::pair<db::cell_index_type, size_t>, db::cell_index_type> &cmap, const db::ICplxTrans &tr) const
LayoutToNetlist::build_net_rec (const db::Net &net, db::Layout &target, db::Cell &target_cell, const std::map<unsigned int, const db::Region *> &lmap, const char *net_cell_name_prefix, db::properties_id_type netname_propid, BuildNetHierarchyMode hier_mode, const char *cell_name_prefix, const char *device_cell_name_prefix, cell_reuse_table_type &reuse_table, const db::ICplxTrans &tr) const
{
const db::Circuit *circuit = net.circuit ();
tl_assert (circuit != 0);
build_net_rec (circuit->cell_index (), net.cluster_id (), target, target_cell, lmap, &net, net_cell_name_prefix, cell_name_prefix, device_cell_name_prefix, cmap, tr);
build_net_rec (circuit->cell_index (), net.cluster_id (), target, target_cell, lmap, &net, net_cell_name_prefix, netname_propid, hier_mode, cell_name_prefix, device_cell_name_prefix, reuse_table, tr);
}
void
LayoutToNetlist::build_net_rec (db::cell_index_type ci, size_t cid, db::Layout &target, db::Cell &tc, const std::map<unsigned int, const db::Region *> &lmap, const db::Net *net, const char *net_cell_name_prefix, const char *circuit_cell_name_prefix, const char *device_cell_name_prefix, std::map<std::pair<db::cell_index_type, size_t>, db::cell_index_type> &cmap, const db::ICplxTrans &tr) const
LayoutToNetlist::build_net_rec (db::cell_index_type ci, size_t cid, db::Layout &target, db::Cell &tc, const std::map<unsigned int, const db::Region *> &lmap, const db::Net *net, const char *net_cell_name_prefix, db::properties_id_type netname_propid, BuildNetHierarchyMode hier_mode, const char *circuit_cell_name_prefix, const char *device_cell_name_prefix, cell_reuse_table_type &reuse_table, const db::ICplxTrans &tr) const
{
db::Cell *target_cell = &tc;
@ -582,14 +693,15 @@ LayoutToNetlist::build_net_rec (db::cell_index_type ci, size_t cid, db::Layout &
bool any_connections = circuit_cell_name_prefix && ! ccl.connections_for_cluster (cid).empty ();
if (! any_connections) {
bool consider_cell = any_connections;
for (std::map<unsigned int, const db::Region *>::const_iterator l = lmap.begin (); l != lmap.end () && !consider_cell; ++l) {
StopOnFirst sof;
std::map<unsigned int, StopOnFirst *> sof_lmap;
for (std::map<unsigned int, const db::Region *>::const_iterator l = lmap.begin (); l != lmap.end (); ++l) {
if (l->second) {
StopOnFirst sof;
consider_cell = !deliver_shapes_of_net_nonrecursive (mp_netlist.get (), m_net_clusters, ci, cid, layer_of (*l->second), tr, sof);
sof_lmap.insert (std::make_pair (layer_of (*l->second), &sof));
}
}
bool consider_cell = ! deliver_shapes_of_net (hier_mode == BNH_Flatten, mp_netlist.get (), m_net_clusters, ci, cid, sof_lmap, tr, 0);
if (! consider_cell) {
// shortcut if cell is empty -> no net cell will be produced
return;
@ -604,13 +716,16 @@ LayoutToNetlist::build_net_rec (db::cell_index_type ci, size_t cid, db::Layout &
}
std::map<unsigned int, db::Shapes *> target_lmap;
for (std::map<unsigned int, const db::Region *>::const_iterator l = lmap.begin (); l != lmap.end (); ++l) {
if (l->second) {
deliver_shapes_of_net_nonrecursive (mp_netlist.get (), m_net_clusters, ci, cid, layer_of (*l->second), tr, target_cell->shapes (l->first));
target_lmap.insert (std::make_pair (layer_of (*l->second), &target_cell->shapes (l->first)));
}
}
if (! circuit_cell_name_prefix && ! device_cell_name_prefix) {
deliver_shapes_of_net (hier_mode == BNH_Flatten, mp_netlist.get (), m_net_clusters, ci, cid, target_lmap, tr, netname_propid);
if (hier_mode != BNH_SubcircuitCells && ! device_cell_name_prefix) {
return;
}
@ -627,8 +742,10 @@ LayoutToNetlist::build_net_rec (db::cell_index_type ci, size_t cid, db::Layout &
db::cell_index_type subci = c->inst_cell_index ();
size_t subcid = c->id ();
std::map<std::pair<db::cell_index_type, size_t>, db::cell_index_type>::const_iterator cm = cmap.find (std::make_pair (subci, subcid));
if (cm == cmap.end ()) {
CellReuseTableKey cmap_key (subci, netname_propid, subcid);
cell_reuse_table_type::const_iterator cm = reuse_table.find (cmap_key);
if (cm == reuse_table.end ()) {
const char *name_prefix = 0;
if (mp_netlist->device_abstract_by_cell_index (subci)) {
@ -642,12 +759,12 @@ LayoutToNetlist::build_net_rec (db::cell_index_type ci, size_t cid, db::Layout &
std::string cell_name = internal_layout ()->cell_name (subci);
db::cell_index_type target_ci = target.add_cell ((std::string (name_prefix) + cell_name).c_str ());
cm = cmap.insert (std::make_pair (std::make_pair (subci, subcid), target_ci)).first;
cm = reuse_table.insert (std::make_pair (cmap_key, target_ci)).first;
build_net_rec (subci, subcid, target, target.cell (target_ci), lmap, 0, 0, circuit_cell_name_prefix, device_cell_name_prefix, cmap, tr_mag);
build_net_rec (subci, subcid, target, target.cell (target_ci), lmap, 0, 0, netname_propid, hier_mode, circuit_cell_name_prefix, device_cell_name_prefix, reuse_table, tr_mag);
} else {
cm = cmap.insert (std::make_pair (std::make_pair (subci, subcid), std::numeric_limits<db::cell_index_type>::max ())).first;
cm = reuse_table.insert (std::make_pair (cmap_key, std::numeric_limits<db::cell_index_type>::max ())).first;
}
}
@ -661,57 +778,113 @@ LayoutToNetlist::build_net_rec (db::cell_index_type ci, size_t cid, db::Layout &
}
}
void
LayoutToNetlist::build_net (const db::Net &net, db::Layout &target, db::Cell &target_cell, const std::map<unsigned int, const db::Region *> &lmap, const char *cell_name_prefix, const char *device_cell_name_prefix) const
db::properties_id_type
LayoutToNetlist::make_netname_propid (db::Layout &ly, const tl::Variant &netname_prop, const db::Net &net) const
{
if (! m_netlist_extracted) {
throw tl::Exception (tl::to_string (tr ("The netlist has not been extracted yet")));
if (! netname_prop.is_nil ()) {
db::property_names_id_type name_propnameid = ly.properties_repository ().prop_name_id (netname_prop);
db::PropertiesRepository::properties_set propset;
propset.insert (std::make_pair (name_propnameid, tl::Variant (net.expanded_name ())));
return ly.properties_repository ().properties_id (propset);
} else {
return 0;
}
std::map<std::pair<db::cell_index_type, size_t>, db::cell_index_type> cell_map;
double mag = internal_layout ()->dbu () / target.dbu ();
build_net_rec (net, target, target_cell, lmap, 0, cell_name_prefix, device_cell_name_prefix, cell_map, db::ICplxTrans (mag));
}
void
LayoutToNetlist::build_all_nets (const db::CellMapping &cmap, db::Layout &target, const std::map<unsigned int, const db::Region *> &lmap, const char *net_cell_name_prefix, const char *circuit_cell_name_prefix, const char *device_cell_name_prefix) const
LayoutToNetlist::build_net (const db::Net &net, db::Layout &target, db::Cell &target_cell, const std::map<unsigned int, const db::Region *> &lmap, const tl::Variant &netname_prop, BuildNetHierarchyMode hier_mode, const char *cell_name_prefix, const char *device_cell_name_prefix) const
{
if (! m_netlist_extracted) {
throw tl::Exception (tl::to_string (tr ("The netlist has not been extracted yet")));
}
std::map<std::pair<db::cell_index_type, size_t>, db::cell_index_type> cell_map;
cell_reuse_table_type cell_reuse_table;
double mag = internal_layout ()->dbu () / target.dbu ();
db::properties_id_type netname_propid = make_netname_propid (target, netname_prop, net);
build_net_rec (net, target, target_cell, lmap, 0, netname_propid, hier_mode, cell_name_prefix, device_cell_name_prefix, cell_reuse_table, db::ICplxTrans (mag));
}
void
LayoutToNetlist::build_all_nets (const db::CellMapping &cmap, db::Layout &target, const std::map<unsigned int, const db::Region *> &lmap, const char *net_cell_name_prefix, const tl::Variant &netname_prop, BuildNetHierarchyMode hier_mode, const char *circuit_cell_name_prefix, const char *device_cell_name_prefix) const
{
build_nets (0, cmap, target, lmap, net_cell_name_prefix, netname_prop, hier_mode, circuit_cell_name_prefix, device_cell_name_prefix);
}
void
LayoutToNetlist::build_net_rec (const db::Net &net, db::Layout &target, db::cell_index_type circuit_cell, const db::CellMapping &cmap, const std::map<unsigned int, const db::Region *> &lmap, const char *net_cell_name_prefix, db::properties_id_type netname_propid, BuildNetHierarchyMode hier_mode, const char *cell_name_prefix, const char *device_cell_name_prefix, cell_reuse_table_type &reuse_table, const ICplxTrans &tr) const
{
if (! cmap.has_mapping (circuit_cell)) {
const db::Cell &cc = internal_layout ()->cell (circuit_cell);
for (db::Cell::parent_inst_iterator p = cc.begin_parent_insts (); ! p.at_end (); ++p) {
db::CellInstArray ci = p->child_inst ().cell_inst ();
for (db::CellInstArray::iterator ia = ci.begin (); ! ia.at_end(); ++ia) {
db::ICplxTrans tr_parent = ci.complex_trans (*ia) * tr;
build_net_rec (net, target, p->parent_cell_index (), cmap, lmap, net_cell_name_prefix, netname_propid, hier_mode, cell_name_prefix, device_cell_name_prefix, reuse_table, tr_parent);
}
}
} else {
double mag = internal_layout ()->dbu () / target.dbu ();
db::cell_index_type target_ci = cmap.cell_mapping (circuit_cell);
build_net_rec (net, target, target.cell (target_ci), lmap, net_cell_name_prefix, netname_propid, hier_mode, cell_name_prefix, device_cell_name_prefix, reuse_table, db::ICplxTrans (mag) * tr);
}
}
void
LayoutToNetlist::build_nets (const std::vector<const db::Net *> *nets, const db::CellMapping &cmap, db::Layout &target, const std::map<unsigned int, const db::Region *> &lmap, const char *net_cell_name_prefix, const tl::Variant &netname_prop, BuildNetHierarchyMode hier_mode, const char *circuit_cell_name_prefix, const char *device_cell_name_prefix) const
{
if (! m_netlist_extracted) {
throw tl::Exception (tl::to_string (tr ("The netlist has not been extracted yet")));
}
std::set<const db::Net *> net_set;
if (nets) {
net_set.insert (nets->begin (), nets->end ());
}
cell_reuse_table_type cell_reuse_table;
const db::Netlist *netlist = mp_netlist.get ();
for (db::Netlist::const_circuit_iterator c = netlist->begin_circuits (); c != netlist->end_circuits (); ++c) {
if (! cmap.has_mapping (c->cell_index ())) {
continue;
}
bool is_top_circuit = c->begin_parents () == c->end_parents ();
db::cell_index_type target_ci = cmap.cell_mapping (c->cell_index ());
for (db::Circuit::const_net_iterator n = c->begin_nets (); n != c->end_nets (); ++n) {
// exlude local nets in recursive mode
if (circuit_cell_name_prefix && ! is_top_circuit && n->pin_count () > 0) {
// exlude local nets in recursive mode except if they are explicitly selected
if (! nets && hier_mode != BNH_Disconnected && ! is_top_circuit && n->pin_count () > 0) {
continue;
}
build_net_rec (*n, target, target.cell (target_ci), lmap, net_cell_name_prefix, circuit_cell_name_prefix, device_cell_name_prefix, cell_map, db::ICplxTrans (mag));
if (! nets || net_set.find (n.operator-> ()) != net_set.end ()) {
db::properties_id_type netname_propid = make_netname_propid (target, netname_prop, *n);
build_net_rec (*n, target, c->cell_index (), cmap, lmap, net_cell_name_prefix, netname_propid, hier_mode, circuit_cell_name_prefix, device_cell_name_prefix, cell_reuse_table, db::ICplxTrans ());
}
}
if (circuit_cell_name_prefix) {
if (hier_mode != BNH_Disconnected && ! nets) {
// with recursive nets we skip nets in subcircuits which are connected upwards. This means, nets will
// With recursive nets we skip nets in subcircuits which are connected upwards. This means, nets will
// get lost if there is no connection to this pin from the outside. Hence we need to deliver nets from
// subcircuits as part of the circuit which calls the subcircuit - but NOT in a subcircuit cell, because
// this will just apply to nets from certain instances. But the net cell name will be formed as "subcircuit:net"
//
// In explicit selection mode we don't care about this as nets are explicitly taken or not.
const db::Circuit &circuit = *c;
for (db::Circuit::const_subcircuit_iterator sc = circuit.begin_subcircuits (); sc != circuit.end_subcircuits (); ++sc) {
@ -725,13 +898,15 @@ LayoutToNetlist::build_all_nets (const db::CellMapping &cmap, db::Layout &target
if (n) {
double dbu = target.dbu ();
db::ICplxTrans tr = db::ICplxTrans (mag) * (db::CplxTrans (dbu).inverted () * subcircuit.trans () * db::CplxTrans (dbu));
db::ICplxTrans tr = db::CplxTrans (dbu).inverted () * subcircuit.trans () * db::CplxTrans (dbu);
db::properties_id_type netname_propid = make_netname_propid (target, netname_prop, *n);
if (net_cell_name_prefix) {
std::string ncn = std::string (net_cell_name_prefix) + subcircuit.expanded_name () + ":";
build_net_rec (*n, target, target.cell (target_ci), lmap, ncn.c_str (), circuit_cell_name_prefix, device_cell_name_prefix, cell_map, tr);
build_net_rec (*n, target, c->cell_index (), cmap, lmap, ncn.c_str (), netname_propid, hier_mode, circuit_cell_name_prefix, device_cell_name_prefix, cell_reuse_table, tr);
} else {
build_net_rec (*n, target, target.cell (target_ci), lmap, net_cell_name_prefix, circuit_cell_name_prefix, device_cell_name_prefix, cell_map, tr);
build_net_rec (*n, target, c->cell_index (), cmap, lmap, net_cell_name_prefix, netname_propid, hier_mode, circuit_cell_name_prefix, device_cell_name_prefix, cell_reuse_table, tr);
}
}
@ -893,8 +1068,8 @@ db::Region LayoutToNetlist::antenna_check (const db::Region &gate, const db::Reg
db::Region rgate, rmetal;
deliver_shapes_of_net_recursive (0, m_net_clusters, *cid, *c, layer_of (gate), db::ICplxTrans (), rgate);
deliver_shapes_of_net_recursive (0, m_net_clusters, *cid, *c, layer_of (metal), db::ICplxTrans (), rmetal);
deliver_shapes_of_net_recursive (0, m_net_clusters, *cid, *c, layer_of (gate), db::ICplxTrans (), rgate, 0);
deliver_shapes_of_net_recursive (0, m_net_clusters, *cid, *c, layer_of (metal), db::ICplxTrans (), rmetal, 0);
double agate = rgate.area () * dbu * dbu;
double ametal = rmetal.area () * dbu * dbu;
@ -905,7 +1080,7 @@ db::Region LayoutToNetlist::antenna_check (const db::Region &gate, const db::Reg
for (std::vector<std::pair<const db::Region *, double> >::const_iterator d = diodes.begin (); d != diodes.end () && ! skip; ++d) {
db::Region rdiode;
deliver_shapes_of_net_recursive (0, m_net_clusters, *cid, *c, layer_of (*d->first), db::ICplxTrans (), rdiode);
deliver_shapes_of_net_recursive (0, m_net_clusters, *cid, *c, layer_of (*d->first), db::ICplxTrans (), rdiode, 0);
if (fabs (d->second) < db::epsilon) {
if (rdiode.area () > 0) {
@ -939,4 +1114,46 @@ db::Region LayoutToNetlist::antenna_check (const db::Region &gate, const db::Reg
return db::Region (new db::DeepRegion (dl));
}
void db::LayoutToNetlist::save (const std::string &path, bool short_format)
{
tl::OutputStream stream (path);
db::LayoutToNetlistStandardWriter writer (stream, short_format);
set_filename (path);
writer.write (this);
}
void db::LayoutToNetlist::load (const std::string &path)
{
tl::InputStream stream (path);
db::LayoutToNetlistStandardReader reader (stream);
set_filename (path);
set_name (stream.filename ());
reader.read (this);
}
db::LayoutToNetlist *db::LayoutToNetlist::create_from_file (const std::string &path)
{
std::auto_ptr<db::LayoutToNetlist> db;
// TODO: generic concept to detect format
std::string first_line;
{
tl::InputStream stream (path);
tl::TextInputStream text_stream (stream);
first_line = text_stream.get_line ();
}
if (first_line.find (db::lvs_std_format::keys<false>::lvs_magic_string) == 0) {
db::LayoutVsSchematic *lvs_db = new db::LayoutVsSchematic ();
db.reset (lvs_db);
lvs_db->load (path);
} else {
db.reset (new db::LayoutToNetlist ());
db->load (path);
}
return db.release ();
}
}

View File

@ -20,8 +20,8 @@
*/
#ifndef _HDR_dbLayout2Netlist
#define _HDR_dbLayout2Netlist
#ifndef _HDR_dbLayoutToNetlist
#define _HDR_dbLayoutToNetlist
#include "dbCommon.h"
#include "dbCellMapping.h"
@ -114,6 +114,78 @@ public:
*/
~LayoutToNetlist ();
/**
* @brief Makes the extractor take over ownership over the DSS when it was created with an external DSS
*/
void keep_dss ();
/**
* @brief Gets the database description
*/
const std::string &description () const
{
return m_description;
}
/**
* @brief Sets the database description
*/
void set_description (const std::string &description)
{
m_description = description;
}
/**
* @brief Gets the original file
*
* The original file describes what original file the netlist database
* was derived from.
*/
const std::string &original_file () const
{
return m_original_file;
}
/**
* @brief Sets the database original file
*/
void set_original_file (const std::string &original_file)
{
m_original_file = original_file;
}
/**
* @brief Gets the database name
*/
const std::string &name () const
{
return m_name;
}
/**
* @brief Sets the database name
*/
void set_name (const std::string &name)
{
m_name = name;
}
/**
* @brief Gets the file name
*/
const std::string &filename () const
{
return m_filename;
}
/**
* @brief Sets the file name
*/
void set_filename (const std::string &filename)
{
m_filename = filename;
}
/**
* @brief Sets the number of threads to use for operations which support multiple threads
*/
@ -154,8 +226,7 @@ public:
* (see below) enhances readability of backannotated information
* if layers are involved. Use this method to attach a name to a region
* derived by boolean operations for example.
* Named regions are persisted inside the LayoutToNetlist object. Only
* named regions can be put into "connect".
* Named regions are persisted inside the LayoutToNetlist object.
*/
void register_layer (const db::Region &region, const std::string &name);
@ -264,21 +335,18 @@ public:
* a derived layer. Certain limitations apply. It's safe to use
* boolean operations for deriving layers. Other operations are applicable as long as they are
* capable of delivering hierarchical layers.
* Regions put into "connect" need to be named.
*/
void connect (const db::Region &l);
/**
* @brief Defines an inter-layer connection for the given layers.
* The conditions mentioned with intra-layer "connect" apply for this method too.
* Regions put into "connect" need to be named.
*/
void connect (const db::Region &a, const db::Region &b);
/**
* @brief Connects the given layer with a global net with the given name
* Returns the global net ID
* Regions put into "connect" need to be named.
*/
size_t connect_global (const db::Region &l, const std::string &gn);
@ -372,13 +440,31 @@ public:
*/
db::CellMapping cell_mapping_into (db::Layout &layout, db::Cell &cell, bool with_device_cells = false);
/**
* @brief Creates a cell mapping for copying shapes from the internal layout to the given target layout for a given list of nets
* This version will only create cells which are required to represent the given nets.
* If 'with_device_cells' is true, cells will be produced for devices. These are cells not corresponding to circuits, so they are disabled normally.
* Use this option, if you want to access device terminal shapes per device.
* CAUTION: This function may create new cells in "layout".
*/
db::CellMapping cell_mapping_into (db::Layout &layout, db::Cell &cell, const std::vector<const db::Net *> &nets, bool with_device_cells = false);
/**
* @brief Creates a cell mapping for copying shapes from the internal layout to the given target layout.
* This version will not create new cells in the target layout.
* If the required cells do not exist there yet, flatting will happen.
* If the required cells do not exist there yet, flattening will happen.
*/
db::CellMapping const_cell_mapping_into (const db::Layout &layout, const db::Cell &cell);
/**
* @brief Creates a layer mapping for build_nets etc.
* This method will create new layers inside the target layout corresponding to the
* original layers as kept inside the LayoutToNetlist database.
* It will return a layer mapping table suitable for use with build_all_nets, build_nets etc.
* Layers without original layer information will be given layer numbers ln, ln+1 etc.
*/
std::map<unsigned int, const db::Region *> create_layermap (db::Layout &target_layout, int ln) const;
/**
* @brief gets the netlist extracted (0 if no extraction happened yet)
*/
@ -428,32 +514,64 @@ public:
*
* This methods returns a new'd Region. It's the responsibility of the caller
* to delete this object.
*
* propid is an optional properties ID which is attached to the shapes if not 0.
*/
void shapes_of_net (const db::Net &net, const db::Region &of_layer, bool recursive, db::Shapes &to) const;
void shapes_of_net (const db::Net &net, const db::Region &of_layer, bool recursive, db::Shapes &to, properties_id_type propid = 0) const;
/**
* @brief An enum describing the way the net hierarchy is mapped
*/
enum BuildNetHierarchyMode
{
/**
* @brief Flatten the net
* Collects all shapes of a net and puts that into the net cell or circuit cell
*/
BNH_Flatten = 0,
/**
* @brief Build a net hierarchy adding cells for each subcircuit on the net
* Uses the circuit_cell_prefix to build the subcircuit cell names
*/
BNH_SubcircuitCells = 1,
/**
* @brief No hierarchy
* Just output the shapes of the net belonging to the circuit cell.
* Connections are not indicated!
*/
BNH_Disconnected = 2
};
/**
* @brief Builds a net representation in the given layout and cell
*
* This method has two modes: recursive and top-level mode. In recursive mode,
* it will create a proper hierarchy below the given target cell to hold all subcircuits the
* net connects to. It will copy the net's parts from this subcircuits into these cells.
* This method puts the shapes of a net into the given target cell using a variety of options
* to represent the net name and the hierarchy of the net.
*
* In top-level mode, only the shapes from the net inside it's circuit are copied to
* the given target cell. No other cells are created.
* If the netname_prop name is not nil, a property with the given name is created and assigned
* the net name.
*
* Recursive mode is picked when a cell name prefix is given. The new cells will be
* named like cell_name_prefix + circuit name.
* Net hierarchy is covered in three ways:
* * No connection indicated (hier_mode == BNH_Disconnected: the net shapes are simply put into their
* respective circuits. The connections are not indicated.
* * Subnet hierarchy (hier_mode == BNH_SubcircuitCells): for each root net, a full hierarchy is built
* to accommodate the subnets (see build_net in recursive mode).
* * Flat (hier_mode == BNH_Flatten): each net is flattened and put into the circuit it
* belongs to.
*
* If a device cell name prefix is given, cells will be produced for each device abstract
* using a name like device_cell_name_prefix + device name.
* using a name like device_cell_name_prefix + device name. Otherwise the device shapes are
* treated as part of the net.
*
* @param target The target layout
* @param target_cell The target cell
* @param lmap Target layer indexes (keys) and net regions (values)
* @param hier_mode See description of this method
* @param netname_prop An (optional) property name to which to attach the net name
* @param cell_name_prefix Chooses recursive mode if non-null
* @param device_cell_name_prefix See above
*/
void build_net (const db::Net &net, db::Layout &target, db::Cell &target_cell, const std::map<unsigned int, const db::Region *> &lmap, const char *cell_name_prefix, const char *device_cell_name_prefix) const;
void build_net (const db::Net &net, db::Layout &target, db::Cell &target_cell, const std::map<unsigned int, const db::Region *> &lmap, const tl::Variant &netname_prop, BuildNetHierarchyMode hier_mode, const char *cell_name_prefix, const char *device_cell_name_prefix) const;
/**
* @brief Builds a full hierarchical representation of the nets
@ -462,28 +580,42 @@ public:
* object to determine the target cell (create them with "cell_mapping_into" or "const_cell_mapping_into").
* If no mapping is requested, the specific circuit it skipped.
*
* The method has two net annotation modes:
* * No annotation (net_cell_name_prefix == 0): the shapes will be put into the target cell simply
* The method has three net annotation modes:
* * No annotation (net_cell_name_prefix == 0 and netname_prop == nil): the shapes will be put
* into the target cell simply
* * Net name property (net_cell_name_prefix == 0 and netname_prop != nil): the shapes will be
* annotated with a property named with netname_prop and containing the net name string.
* * Individual subcells per net (net_cell_name_prefix != 0): for each net, a subcell is created
* and the net shapes will be put there (name of the subcell = net_cell_name_prefix + net name).
* (this mode can be combined with netname_prop too).
*
* In addition, net hierarchy is covered in two ways:
* * No connection indicated (circuit_cell_name_prefix == 0: the net shapes are simply put into their
* In addition, net hierarchy is covered in three ways:
* * No connection indicated (hier_mode == BNH_Disconnected: the net shapes are simply put into their
* respective circuits. The connections are not indicated.
* * Subnet hierarchy (circuit_cell_name_prefix != 0): for each root net, a full hierarchy is built
* * Subnet hierarchy (hier_mode == BNH_SubcircuitCells): for each root net, a full hierarchy is built
* to accommodate the subnets (see build_net in recursive mode).
* * Flat (hier_mode == BNH_Flatten): each net is flattened and put into the circuit it
* belongs to.
*
* If a device cell name prefix is given, cells will be produced for each device abstract
* using a name like device_cell_name_prefix + device name.
* using a name like device_cell_name_prefix + device name. Otherwise the device shapes are
* treated as part of the net.
*
* @param cmap The mapping of internal layout to target layout for the circuit mapping
* @param target The target layout
* @param lmap Target layer indexes (keys) and net regions (values)
* @param hier_mode See description of this method
* @param netname_prop An (optional) property name to which to attach the net name
* @param circuit_cell_name_prefix See method description
* @param net_cell_name_prefix See method description
* @param device_cell_name_prefix See above
*/
void build_all_nets (const db::CellMapping &cmap, db::Layout &target, const std::map<unsigned int, const db::Region *> &lmap, const char *net_cell_name_prefix, const char *circuit_cell_name_prefix, const char *device_cell_name_prefix) const;
void build_all_nets (const db::CellMapping &cmap, db::Layout &target, const std::map<unsigned int, const db::Region *> &lmap, const char *net_cell_name_prefix, const tl::Variant &netname_prop, BuildNetHierarchyMode hier_mode, const char *circuit_cell_name_prefix, const char *device_cell_name_prefix) const;
/**
* @brief Like build_all_nets, but with the ability to select some nets
*/
void build_nets (const std::vector<const Net *> *nets, const db::CellMapping &cmap, db::Layout &target, const std::map<unsigned int, const db::Region *> &lmap, const char *net_cell_name_prefix, const tl::Variant &netname_prop, BuildNetHierarchyMode hier_mode, const char *circuit_cell_name_prefix, const char *device_cell_name_prefix) const;
/**
* @brief Finds the net by probing a specific location on the given layer
@ -531,11 +663,42 @@ public:
*/
db::Region antenna_check (const db::Region &gate, const db::Region &metal, double ratio, const std::vector<std::pair<const db::Region *, double> > &diodes = std::vector<std::pair<const db::Region *, double> > ());
/**
* @brief Saves the database to the given path
*
* Currently, the internal format will be used. If "short_format" is true, the short version
* of the format is used.
*
* This is a convenience method. The low-level functionality is the LayoutToNetlistWriter.
*/
void save (const std::string &path, bool short_format);
/**
* @brief Loads the database from the given path
*
* This is a convenience method. The low-level functionality is the LayoutToNetlistReader.
*/
void load (const std::string &path);
/**
* @brief Creates a LayoutToNetlist object from a file
*
* This method analyses the file and will create a LayoutToNetlist object
* or one of a derived class (specifically LayoutVsSchematic).
*
* The returned object is new'd one and must be deleted by the caller.
*/
static db::LayoutToNetlist *create_from_file (const std::string &path);
private:
// no copying
LayoutToNetlist (const db::LayoutToNetlist &other);
LayoutToNetlist &operator= (const db::LayoutToNetlist &other);
std::string m_description;
std::string m_name;
std::string m_original_file;
std::string m_filename;
db::RecursiveShapeIterator m_iter;
std::auto_ptr<db::DeepShapeStore> mp_internal_dss;
tl::weak_ptr<db::DeepShapeStore> mp_dss;
@ -550,12 +713,45 @@ private:
bool m_is_flat;
db::DeepLayer m_dummy_layer;
struct CellReuseTableKey
{
CellReuseTableKey (db::cell_index_type _cell_index, db::properties_id_type _netname_propid, size_t _cluster_id)
: cell_index (_cell_index), netname_propid (_netname_propid), cluster_id (_cluster_id)
{
// .. nothing yet ..
}
bool operator< (const CellReuseTableKey &other) const
{
if (cell_index != other.cell_index) {
return cell_index < other.cell_index;
}
if (netname_propid != other.netname_propid) {
return netname_propid < other.netname_propid;
}
if (cluster_id != other.cluster_id) {
return cluster_id < other.cluster_id;
}
return false;
}
db::cell_index_type cell_index;
db::properties_id_type netname_propid;
size_t cluster_id;
};
typedef std::map<CellReuseTableKey, db::cell_index_type> cell_reuse_table_type;
void init ();
size_t search_net (const db::ICplxTrans &trans, const db::Cell *cell, const db::local_cluster<db::PolygonRef> &test_cluster, std::vector<db::InstElement> &rev_inst_path);
void build_net_rec (const db::Net &net, db::Layout &target, db::Cell &target_cell, const std::map<unsigned int, const db::Region *> &lmap, const char *net_cell_name_prefix, const char *cell_name_prefix, const char *device_cell_name_prefix, std::map<std::pair<db::cell_index_type, size_t>, db::cell_index_type> &cmap, const ICplxTrans &tr) const;
void build_net_rec (db::cell_index_type ci, size_t cid, db::Layout &target, db::Cell &target_cell, const std::map<unsigned int, const db::Region *> &lmap, const Net *net, const char *net_cell_name_prefix, const char *cell_name_prefix, const char *device_cell_name_prefix, std::map<std::pair<db::cell_index_type, size_t>, db::cell_index_type> &cmap, const ICplxTrans &tr) const;
void build_net_rec (const db::Net &net, db::Layout &target, cell_index_type circuit_cell, const db::CellMapping &cmap, const std::map<unsigned int, const db::Region *> &lmap, const char *net_cell_name_prefix, db::properties_id_type netname_propid, BuildNetHierarchyMode hier_mode, const char *cell_name_prefix, const char *device_cell_name_prefix, cell_reuse_table_type &reuse_table, const ICplxTrans &tr) const;
void build_net_rec (const db::Net &net, db::Layout &target, db::Cell &target_cell, const std::map<unsigned int, const db::Region *> &lmap, const char *net_cell_name_prefix, db::properties_id_type netname_propid, BuildNetHierarchyMode hier_mode, const char *cell_name_prefix, const char *device_cell_name_prefix, cell_reuse_table_type &reuse_table, const ICplxTrans &tr) const;
void build_net_rec (db::cell_index_type ci, size_t cid, db::Layout &target, db::Cell &target_cell, const std::map<unsigned int, const db::Region *> &lmap, const Net *net, const char *net_cell_name_prefix, db::properties_id_type netname_propid, BuildNetHierarchyMode hier_mode, const char *cell_name_prefix, const char *device_cell_name_prefix, cell_reuse_table_type &reuse_table, const ICplxTrans &tr) const;
db::DeepLayer deep_layer_of (const db::Region &region) const;
void ensure_layout () const;
std::string make_new_name (const std::string &stem = std::string ());
db::properties_id_type make_netname_propid (db::Layout &ly, const tl::Variant &netname_prop, const db::Net &net) const;
db::CellMapping make_cell_mapping_into (db::Layout &layout, db::Cell &cell, const std::vector<const db::Net *> *nets, bool with_device_cells);
};
}

View File

@ -27,49 +27,57 @@ namespace db
namespace l2n_std_format
{
template<> DB_PUBLIC const std::string keys<false>::version_key ("version");
template<> DB_PUBLIC const std::string keys<false>::description_key ("description");
template<> DB_PUBLIC const std::string keys<false>::top_key ("top");
template<> DB_PUBLIC const std::string keys<false>::unit_key ("unit");
template<> DB_PUBLIC const std::string keys<false>::layer_key ("layer");
template<> DB_PUBLIC const std::string keys<false>::connect_key ("connect");
template<> DB_PUBLIC const std::string keys<false>::global_key ("global");
template<> DB_PUBLIC const std::string keys<false>::circuit_key ("circuit");
template<> DB_PUBLIC const std::string keys<false>::net_key ("net");
template<> DB_PUBLIC const std::string keys<false>::name_key ("name");
template<> DB_PUBLIC const std::string keys<false>::device_key ("device");
template<> DB_PUBLIC const std::string keys<false>::polygon_key ("polygon");
template<> DB_PUBLIC const std::string keys<false>::rect_key ("rect");
template<> DB_PUBLIC const std::string keys<false>::terminal_key ("terminal");
template<> DB_PUBLIC const std::string keys<false>::abstract_key ("abstract");
template<> DB_PUBLIC const std::string keys<false>::param_key ("param");
template<> DB_PUBLIC const std::string keys<false>::location_key ("location");
template<> DB_PUBLIC const std::string keys<false>::rotation_key ("rotation");
template<> DB_PUBLIC const std::string keys<false>::mirror_key ("mirror");
template<> DB_PUBLIC const std::string keys<false>::scale_key ("scale");
template<> DB_PUBLIC const std::string keys<false>::pin_key ("pin");
const char *l2n_magic_string_cstr = "#%l2n-klayout";
template<> DB_PUBLIC const std::string keys<true>::version_key ("V");
template<> DB_PUBLIC const std::string keys<true>::description_key ("B");
template<> DB_PUBLIC const std::string keys<true>::top_key ("W");
template<> DB_PUBLIC const std::string keys<true>::unit_key ("U");
template<> DB_PUBLIC const std::string keys<true>::layer_key ("L");
template<> DB_PUBLIC const std::string keys<true>::connect_key ("C");
template<> DB_PUBLIC const std::string keys<true>::global_key ("G");
template<> DB_PUBLIC const std::string keys<true>::circuit_key ("X");
template<> DB_PUBLIC const std::string keys<true>::net_key ("N");
template<> DB_PUBLIC const std::string keys<true>::name_key ("I");
template<> DB_PUBLIC const std::string keys<true>::device_key ("D");
template<> DB_PUBLIC const std::string keys<true>::polygon_key ("Q");
template<> DB_PUBLIC const std::string keys<true>::rect_key ("R");
template<> DB_PUBLIC const std::string keys<true>::terminal_key ("T");
template<> DB_PUBLIC const std::string keys<true>::abstract_key ("A");
template<> DB_PUBLIC const std::string keys<true>::param_key ("E");
template<> DB_PUBLIC const std::string keys<true>::location_key ("Y");
template<> DB_PUBLIC const std::string keys<true>::rotation_key ("O");
template<> DB_PUBLIC const std::string keys<true>::mirror_key ("M");
template<> DB_PUBLIC const std::string keys<true>::scale_key ("S");
template<> DB_PUBLIC const std::string keys<true>::pin_key ("P");
DB_PUBLIC std::string LongKeys::l2n_magic_string (l2n_magic_string_cstr);
DB_PUBLIC std::string ShortKeys::l2n_magic_string (l2n_magic_string_cstr);
DB_PUBLIC std::string LongKeys::version_key ("version");
DB_PUBLIC std::string LongKeys::description_key ("description");
DB_PUBLIC std::string LongKeys::top_key ("top");
DB_PUBLIC std::string LongKeys::unit_key ("unit");
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::global_key ("global");
DB_PUBLIC std::string LongKeys::circuit_key ("circuit");
DB_PUBLIC std::string LongKeys::net_key ("net");
DB_PUBLIC std::string LongKeys::name_key ("name");
DB_PUBLIC std::string LongKeys::device_key ("device");
DB_PUBLIC std::string LongKeys::polygon_key ("polygon");
DB_PUBLIC std::string LongKeys::rect_key ("rect");
DB_PUBLIC std::string LongKeys::terminal_key ("terminal");
DB_PUBLIC std::string LongKeys::abstract_key ("abstract");
DB_PUBLIC std::string LongKeys::param_key ("param");
DB_PUBLIC std::string LongKeys::location_key ("location");
DB_PUBLIC std::string LongKeys::rotation_key ("rotation");
DB_PUBLIC std::string LongKeys::mirror_key ("mirror");
DB_PUBLIC std::string LongKeys::scale_key ("scale");
DB_PUBLIC std::string LongKeys::pin_key ("pin");
// A, B, C, D, E, G, I, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y
DB_PUBLIC std::string ShortKeys::version_key ("V");
DB_PUBLIC std::string ShortKeys::description_key ("B");
DB_PUBLIC std::string ShortKeys::top_key ("W");
DB_PUBLIC std::string ShortKeys::unit_key ("U");
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::global_key ("G");
DB_PUBLIC std::string ShortKeys::circuit_key ("X");
DB_PUBLIC std::string ShortKeys::net_key ("N");
DB_PUBLIC std::string ShortKeys::name_key ("I");
DB_PUBLIC std::string ShortKeys::device_key ("D");
DB_PUBLIC std::string ShortKeys::polygon_key ("Q");
DB_PUBLIC std::string ShortKeys::rect_key ("R");
DB_PUBLIC std::string ShortKeys::terminal_key ("T");
DB_PUBLIC std::string ShortKeys::abstract_key ("A");
DB_PUBLIC std::string ShortKeys::param_key ("E");
DB_PUBLIC std::string ShortKeys::location_key ("Y");
DB_PUBLIC std::string ShortKeys::rotation_key ("O");
DB_PUBLIC std::string ShortKeys::mirror_key ("M");
DB_PUBLIC std::string ShortKeys::scale_key ("S");
DB_PUBLIC std::string ShortKeys::pin_key ("P");
}
}

View File

@ -52,35 +52,69 @@ namespace db
* description(<text>) - an arbitrary description text [short key: B]
* unit(<unit>) - specifies the database unit [short key: U]
* top(<circuit>) - specifies the name of the top circuit [short key: W]
* layer(<name>) - define a layer [short key: L]
* layer(<name> <source-spec>?) - define a layer [short key: L]
* connect(<layer1> <name> ...) - connects layer1 with the following layers [short key: C]
* global(<layer> <net-name> ...)
* - connects the shapes of the layer with the given global
* nets [short key: G]
* circuit(<name> [circuit-def]) - circuit (cell) [short key: X]
* class(<name> <template>) - a device class definition (template: RES,CAP,...) [short key: K]
* device(<name> <class> [device-abstract-def])
* - device abstract [short key: D]
*
* [circuit-def]:
*
* net(<id> [net-name]? [geometry-def]*)
* [boundary-def]
*
* net(<id> [name]? [geometry-def]*)
* - net geometry [short key: N]
* A net declaration shall be there also if no geometry
* is present. The ID is a numerical shortcut for the net.
* pin(<name> <net-id>) - outgoing pin connection [short key: P]
* device(<name> <abstract> [device-def])
* pin(<net-id> [name]?) - outgoing pin connection [short key: P]
* Statement order specifies pin order.
* device(<id> <abstract-or-class> [name]? [combined-device]* [terminal-route]* [device-def])
* - device with connections [short key: D]
* circuit(<name> [circuit-def]) - subcircuit with connections [short key: X]
* circuit(<id> [name]? [subcircuit-def])
* - subcircuit with connections [short key: X]
*
* [net-name]:
* name(<net-name>) - specify net name [short key:
* [boundary-def]:
*
* polygon([coord] ...) - defines a polygon [short key: Q]
* "*" for <x> or <y> means take previous
* rect([coord] [coord]) - defines a rectangle [short key: R]
* coordinates are bottom/left and top/right
*
* [combined-device]:
*
* device(<abstract> [trans-def])
* - specifies an additional device component
* (for combined devices) with abstract <abstract>
* and offset dx, dy.
*
* [terminal-route]:
*
* connect(<device-index> <outer-terminal-name> <inner-terminal-name>)
* - connects the outer terminal with the terminal
* of the device component with <device-index>:
* 0 is the basic device, 1 the first combined
* device etc.
*
* [name]:
*
* name(<name>) - specify net name [short key: I]
*
* [geometry-def]:
*
* polygon(<layer> <x> <y> ...) - defines a polygon [short key: Q]
* polygon(<layer> [coord] ...) - defines a polygon [short key: Q]
* "*" for <x> or <y> means take previous
* rect(<layer> <left> <bottom> <right> <top>)
* - defines a rectangle [short key: R]
* rect(<layer> [coord] [coord]) - defines a rectangle [short key: R]
* coordinates are bottom/left and top/right
*
* [coord]
*
* <x> <y> - absolute coordinates
* (<x> <y>) - relative coordinates (reference is reset to 0,0
* for each net or terminal in device abstract)
*
* [device-abstract-def]:
*
@ -89,7 +123,7 @@ namespace db
*
* [device-def]:
*
* location(<x> <y>) - location of the device [short key Y]
* [trans-def] - location of the device [short key Y]
* must be before terminal
* param(<name> <value>) - defines a parameter [short key E]
* terminal(<terminal-name> <net-id>)
@ -98,44 +132,91 @@ namespace db
*
* [subcircuit-def]:
*
* location(<x> <y>) - location of the subcircuit [short key Y]
* [trans-def] - location of the subcircuit [short key Y]
* pin(<pin-id> <net-id>) - specifies connection of the pin with a net [short key: P]
*
* [trans-def]:
*
* location(<x> <y>) - location of the instance [short key Y]
* rotation(<angle>) - rotation angle (in degree, default is 0) [short key O]
* mirror - if specified, the instance is mirrored before rotation [short key M]
* scale(<mag>) - magnification (default is 1) [short key S]
* pin(<pin-name> <net-id>) - specifies connection of the pin with a net [short key: P]
*/
namespace l2n_std_format
{
template <bool Short>
struct DB_PUBLIC keys
struct DB_PUBLIC ShortKeys
{
static const std::string version_key;
static const std::string description_key;
static const std::string top_key;
static const std::string unit_key;
static const std::string layer_key;
static const std::string connect_key;
static const std::string global_key;
static const std::string circuit_key;
static const std::string net_key;
static const std::string name_key;
static const std::string device_key;
static const std::string subcircuit_key;
static const std::string polygon_key;
static const std::string rect_key;
static const std::string terminal_key;
static const std::string abstract_key;
static const std::string param_key;
static const std::string location_key;
static const std::string rotation_key;
static const std::string mirror_key;
static const std::string scale_key;
static const std::string pin_key;
static const std::string indent1;
static const std::string indent2;
static std::string l2n_magic_string;
inline static bool is_short () { return Short; }
static std::string version_key;
static std::string description_key;
static std::string top_key;
static std::string unit_key;
static std::string layer_key;
static std::string class_key;
static std::string connect_key;
static std::string global_key;
static std::string circuit_key;
static std::string net_key;
static std::string name_key;
static std::string device_key;
static std::string subcircuit_key;
static std::string polygon_key;
static std::string rect_key;
static std::string terminal_key;
static std::string abstract_key;
static std::string param_key;
static std::string location_key;
static std::string rotation_key;
static std::string mirror_key;
static std::string scale_key;
static std::string pin_key;
static std::string indent1;
static std::string indent2;
};
struct DB_PUBLIC LongKeys
{
static std::string l2n_magic_string;
static std::string version_key;
static std::string description_key;
static std::string top_key;
static std::string unit_key;
static std::string layer_key;
static std::string class_key;
static std::string connect_key;
static std::string global_key;
static std::string circuit_key;
static std::string net_key;
static std::string name_key;
static std::string device_key;
static std::string subcircuit_key;
static std::string polygon_key;
static std::string rect_key;
static std::string terminal_key;
static std::string abstract_key;
static std::string param_key;
static std::string location_key;
static std::string rotation_key;
static std::string mirror_key;
static std::string scale_key;
static std::string pin_key;
static std::string indent1;
static std::string indent2;
};
template <bool Short> struct DB_PUBLIC keys;
template <> struct DB_PUBLIC keys<true> : public ShortKeys
{
inline static bool is_short () { return true; }
};
template <> struct DB_PUBLIC keys<false> : public LongKeys
{
inline static bool is_short () { return false; }
};
}

File diff suppressed because it is too large Load Diff

View File

@ -26,13 +26,30 @@
#include "dbCommon.h"
#include "dbPolygon.h"
#include "dbCell.h"
#include "dbLayoutToNetlist.h"
#include "tlStream.h"
#include "tlProgress.h"
namespace db {
class LayoutToNetlistStandardReader;
namespace l2n_std_reader {
class Layers;
class Brace;
class Brace
{
public:
Brace (db::LayoutToNetlistStandardReader *reader);
operator bool ();
void done ();
private:
db::LayoutToNetlistStandardReader *mp_reader;
bool m_checked;
bool m_has_brace;
};
}
class LayoutToNetlist;
@ -52,7 +69,13 @@ public:
LayoutToNetlistReaderBase () { }
virtual ~LayoutToNetlistReaderBase () { }
virtual void read (db::LayoutToNetlist *l2n) = 0;
void read (db::LayoutToNetlist *l2n)
{
do_read (l2n);
}
private:
virtual void do_read (db::LayoutToNetlist *l2n) = 0;
};
/**
@ -62,14 +85,35 @@ class DB_PUBLIC LayoutToNetlistStandardReader
: public LayoutToNetlistReaderBase
{
public:
struct ObjectMap
{
std::map<unsigned int, db::Net *> id2net;
std::map<unsigned int, db::Device *> id2device;
std::map<unsigned int, db::SubCircuit *> id2subcircuit;
};
LayoutToNetlistStandardReader (tl::InputStream &stream);
void read (db::LayoutToNetlist *l2n);
void do_read (db::LayoutToNetlist *l2n);
private:
protected:
friend class l2n_std_reader::Brace;
typedef l2n_std_reader::Brace Brace;
typedef l2n_std_reader::Layers Layers;
void read_netlist (Netlist *netlist, db::LayoutToNetlist *l2n, bool nested = false, std::map<const db::Circuit *, ObjectMap> *map_per_circuit = 0);
static size_t terminal_id (const db::DeviceClass *device_class, const std::string &tname);
static std::pair<db::DeviceAbstract *, const db::DeviceClass *> device_model_by_name (db::Netlist *netlist, const std::string &dmname);
const std::string &path () const
{
return m_path;
}
tl::TextInputStream &stream ()
{
return m_stream;
}
struct Connections
{
@ -80,13 +124,6 @@ private:
size_t from_cluster, to_cluster;
};
tl::TextInputStream m_stream;
std::string m_path;
std::string m_line;
tl::Extractor m_ex;
void do_read (db::LayoutToNetlist *l2n);
bool test (const std::string &token);
void expect (const std::string &token);
void read_word_or_quoted(std::string &s);
@ -96,12 +133,26 @@ private:
bool at_end ();
void skip ();
void read_net (db::LayoutToNetlist *l2n, db::Circuit *circuit, std::map<unsigned int, db::Net *> &id2net);
void read_pin (db::LayoutToNetlist *l2n, db::Circuit *circuit, std::map<unsigned int, db::Net *> &id2net);
db::CellInstArray read_device (db::LayoutToNetlist *l2n, db::Circuit *circuit, std::list<Connections> &refs, std::map<unsigned int, db::Net *> &id2net);
db::CellInstArray read_subcircuit (db::LayoutToNetlist *l2n, db::Circuit *circuit, std::list<Connections> &refs, std::map<unsigned int, db::Net *> &id2net);
void read_net (Netlist *netlist, db::LayoutToNetlist *l2n, db::Circuit *circuit, ObjectMap &map);
void read_pin (Netlist *netlist, db::LayoutToNetlist *l2n, db::Circuit *circuit, ObjectMap &map);
void read_device (Netlist *netlist, db::LayoutToNetlist *l2n, db::Circuit *circuit, ObjectMap &map, std::map<db::CellInstArray, std::list<Connections> > &connections);
void read_subcircuit (Netlist *netlist, db::LayoutToNetlist *l2n, db::Circuit *circuit, ObjectMap &map, std::map<db::CellInstArray, std::list<Connections> > &connections);
bool read_trans_part (db::DCplxTrans &tr);
void read_abstract_terminal (db::LayoutToNetlist *l2n, db::DeviceAbstract *dm, db::DeviceClass *dc);
std::pair<unsigned int, db::PolygonRef> read_geometry (db::LayoutToNetlist *l2n);
db::Polygon read_polygon ();
db::Box read_rect ();
void read_geometries (Brace &br, db::LayoutToNetlist *l2n, db::local_cluster<db::PolygonRef> &lc, db::Cell &cell);
db::Point read_point ();
private:
tl::TextInputStream m_stream;
std::string m_path;
std::string m_line;
double m_dbu;
tl::Extractor m_ex;
db::Point m_ref;
tl::AbsoluteProgress m_progress;
};
}

View File

@ -23,44 +23,45 @@
#include "dbLayoutToNetlistWriter.h"
#include "dbLayoutToNetlist.h"
#include "dbLayoutToNetlistFormatDefs.h"
#include "dbPolygonTools.h"
namespace db
{
namespace l2n_std_format
// -------------------------------------------------------------------------------------------
// LayoutToNetlistWriterBase implementation
LayoutToNetlistWriterBase::LayoutToNetlistWriterBase ()
{
// .. nothing yet ..
}
LayoutToNetlistWriterBase::~LayoutToNetlistWriterBase ()
{
// .. nothing yet ..
}
void LayoutToNetlistWriterBase::write (const db::LayoutToNetlist *l2n)
{
do_write (l2n);
}
// -------------------------------------------------------------------------------------------
// std_writer_impl<Keys> implementation
template <class Keys>
class std_writer_impl
namespace l2n_std_format
{
public:
std_writer_impl (tl::OutputStream &stream);
void write (const db::LayoutToNetlist *l2n);
private:
tl::OutputStream *mp_stream;
void write (const db::LayoutToNetlist *l2n, const db::Circuit &circuit);
void write (const db::LayoutToNetlist *l2n, const db::Net &net, unsigned int id);
void write (const db::LayoutToNetlist *l2n, const db::SubCircuit &subcircuit, std::map<const Net *, unsigned int> &net2id);
void write (const db::LayoutToNetlist *l2n, const db::Device &device, std::map<const Net *, unsigned int> &net2id);
void write (const db::LayoutToNetlist *l2n, const db::DeviceAbstract &device_abstract);
void write (const db::PolygonRef *s, const db::ICplxTrans &tr, const std::string &lname);
};
static const std::string endl ("\n");
static const std::string indent1 (" ");
static const std::string indent2 (" ");
template <class Keys>
std_writer_impl<Keys>::std_writer_impl (tl::OutputStream &stream)
: mp_stream (&stream)
std_writer_impl<Keys>::std_writer_impl (tl::OutputStream &stream, double dbu, const std::string &progress_description)
: mp_stream (&stream), m_dbu (dbu),
m_progress (progress_description.empty () ? tl::to_string (tr ("Writing L2N database")) : progress_description, 10000)
{
// .. nothing yet ..
m_progress.set_format (tl::to_string (tr ("%.0f MB")));
m_progress.set_unit (1024 * 1024);
}
static std::string name_for_layer (const db::LayoutToNetlist *l2n, unsigned int l)
@ -74,143 +75,272 @@ static std::string name_for_layer (const db::LayoutToNetlist *l2n, unsigned int
template <class Keys>
void std_writer_impl<Keys>::write (const db::LayoutToNetlist *l2n)
{
write (l2n->netlist (), l2n, false, 0);
}
template <class Keys>
void std_writer_impl<Keys>::write (const db::Netlist *nl, const db::LayoutToNetlist *l2n, bool nested, std::map<const db::Circuit *, std::map<const db::Net *, unsigned int> > *net2id_per_circuit)
{
bool any = false;
const int version = 0;
const db::Layout *ly = l2n->internal_layout ();
const db::Netlist *nl = l2n->netlist ();
const db::Layout *ly = l2n ? l2n->internal_layout () : 0;
const std::string indent (nested ? indent1 : "");
*mp_stream << "#%l2n-klayout" << endl;
if (! Keys::is_short ()) {
*mp_stream << endl << "# General section" << endl << endl;
if (! nested) {
*mp_stream << Keys::l2n_magic_string << endl;
}
if (version > 0) {
*mp_stream << Keys::version_key << "(" << version << ")" << endl;
*mp_stream << indent << Keys::version_key << "(" << version << ")" << endl;
}
*mp_stream << Keys::top_key << "(" << tl::to_word_or_quoted_string (ly->cell_name (l2n->internal_top_cell ()->cell_index ())) << ")" << endl;
*mp_stream << Keys::unit_key << "(" << ly->dbu () << ")" << endl;
if (! Keys::is_short ()) {
*mp_stream << endl << "# Layer section" << endl;
*mp_stream << "# This section lists the mask layers (drawing or derived) and their connections." << endl;
if (ly) {
*mp_stream << indent << Keys::top_key << "(" << tl::to_word_or_quoted_string (ly->cell_name (l2n->internal_top_cell ()->cell_index ())) << ")" << endl;
*mp_stream << indent << Keys::unit_key << "(" << m_dbu << ")" << endl;
}
if (! Keys::is_short ()) {
*mp_stream << endl << "# Mask layers" << endl;
}
for (db::Connectivity::layer_iterator l = l2n->connectivity ().begin_layers (); l != l2n->connectivity ().end_layers (); ++l) {
*mp_stream << Keys::layer_key << "(" << name_for_layer (l2n, *l) << ")" << endl;
}
if (l2n) {
if (! Keys::is_short ()) {
*mp_stream << endl << "# Mask layer connectivity" << endl;
}
for (db::Connectivity::layer_iterator l = l2n->connectivity ().begin_layers (); l != l2n->connectivity ().end_layers (); ++l) {
db::Connectivity::layer_iterator ce = l2n->connectivity ().end_connected (*l);
db::Connectivity::layer_iterator cb = l2n->connectivity ().begin_connected (*l);
if (cb != ce) {
*mp_stream << Keys::connect_key << "(" << name_for_layer (l2n, *l);
for (db::Connectivity::layer_iterator c = l2n->connectivity ().begin_connected (*l); c != ce; ++c) {
*mp_stream << " " << name_for_layer (l2n, *c);
}
*mp_stream << ")" << endl;
if (! Keys::is_short ()) {
*mp_stream << endl << indent << "# Layer section" << endl;
*mp_stream << indent << "# This section lists the mask layers (drawing or derived) and their connections." << endl;
}
}
if (! Keys::is_short ()) {
*mp_stream << endl << indent << "# Mask layers" << endl;
}
for (db::Connectivity::layer_iterator l = l2n->connectivity ().begin_layers (); l != l2n->connectivity ().end_layers (); ++l) {
*mp_stream << indent << Keys::layer_key << "(" << name_for_layer (l2n, *l);
db::LayerProperties lp = ly->get_properties (*l);
if (! lp.is_null ()) {
*mp_stream << " " << tl::to_word_or_quoted_string (lp.to_string ());
}
*mp_stream << ")" << endl;
m_progress.set (mp_stream->pos ());
}
any = false;
for (db::Connectivity::layer_iterator l = l2n->connectivity ().begin_layers (); l != l2n->connectivity ().end_layers (); ++l) {
if (! Keys::is_short ()) {
*mp_stream << endl << indent << "# Mask layer connectivity" << endl;
}
for (db::Connectivity::layer_iterator l = l2n->connectivity ().begin_layers (); l != l2n->connectivity ().end_layers (); ++l) {
db::Connectivity::global_nets_iterator ge = l2n->connectivity ().end_global_connections (*l);
db::Connectivity::global_nets_iterator gb = l2n->connectivity ().begin_global_connections (*l);
if (gb != ge) {
if (! any) {
if (! Keys::is_short ()) {
*mp_stream << endl << "# Global nets and connectivity" << endl;
db::Connectivity::layer_iterator ce = l2n->connectivity ().end_connected (*l);
db::Connectivity::layer_iterator cb = l2n->connectivity ().begin_connected (*l);
if (cb != ce) {
*mp_stream << indent << Keys::connect_key << "(" << name_for_layer (l2n, *l);
for (db::Connectivity::layer_iterator c = l2n->connectivity ().begin_connected (*l); c != ce; ++c) {
*mp_stream << " " << name_for_layer (l2n, *c);
}
any = true;
*mp_stream << ")" << endl;
m_progress.set (mp_stream->pos ());
}
*mp_stream << Keys::global_key << "(" << name_for_layer (l2n, *l);
for (db::Connectivity::global_nets_iterator g = gb; g != ge; ++g) {
*mp_stream << " " << tl::to_word_or_quoted_string (l2n->connectivity ().global_net_name (*g));
}
*mp_stream << ")" << endl;
}
any = false;
for (db::Connectivity::layer_iterator l = l2n->connectivity ().begin_layers (); l != l2n->connectivity ().end_layers (); ++l) {
db::Connectivity::global_nets_iterator ge = l2n->connectivity ().end_global_connections (*l);
db::Connectivity::global_nets_iterator gb = l2n->connectivity ().begin_global_connections (*l);
if (gb != ge) {
if (! any) {
if (! Keys::is_short ()) {
*mp_stream << endl << indent << "# Global nets and connectivity" << endl;
}
any = true;
}
*mp_stream << indent << Keys::global_key << "(" << name_for_layer (l2n, *l);
for (db::Connectivity::global_nets_iterator g = gb; g != ge; ++g) {
*mp_stream << " " << tl::to_word_or_quoted_string (l2n->connectivity ().global_net_name (*g));
}
*mp_stream << ")" << endl;
m_progress.set (mp_stream->pos ());
}
}
}
if (nl->begin_device_classes () != nl->end_device_classes () && ! Keys::is_short ()) {
*mp_stream << endl << indent << "# Device class section" << endl;
for (db::Netlist::const_device_class_iterator c = nl->begin_device_classes (); c != nl->end_device_classes (); ++c) {
db::DeviceClassTemplateBase *temp = db::DeviceClassTemplateBase::is_a (c.operator-> ());
if (temp) {
*mp_stream << indent << Keys::class_key << "(" << tl::to_word_or_quoted_string (c->name ()) << " " << tl::to_word_or_quoted_string (temp->name ()) << ")" << endl;
m_progress.set (mp_stream->pos ());
}
}
}
if (nl->begin_device_abstracts () != nl->end_device_abstracts () && ! Keys::is_short ()) {
*mp_stream << endl << "# Device abstracts section" << endl;
*mp_stream << "# Device abstracts list the pin shapes of the devices." << endl;
*mp_stream << endl << indent << "# Device abstracts section" << endl;
*mp_stream << indent << "# Device abstracts list the pin shapes of the devices." << endl;
}
for (db::Netlist::const_abstract_model_iterator m = nl->begin_device_abstracts (); m != nl->end_device_abstracts (); ++m) {
if (m->device_class ()) {
*mp_stream << Keys::device_key << "(" << tl::to_word_or_quoted_string (m->name ()) << " " << tl::to_word_or_quoted_string (m->device_class ()->name ()) << endl;
write (l2n, *m);
*mp_stream << ")" << endl;
*mp_stream << indent << Keys::device_key << "(" << tl::to_word_or_quoted_string (m->name ()) << " " << tl::to_word_or_quoted_string (m->device_class ()->name ()) << endl;
write (l2n, *m, indent);
*mp_stream << indent << ")" << endl;
m_progress.set (mp_stream->pos ());
}
}
if (! Keys::is_short ()) {
*mp_stream << endl << "# Circuit section" << endl;
*mp_stream << "# Circuits are the hierarchical building blocks of the netlist." << endl;
*mp_stream << endl << indent << "# Circuit section" << endl;
*mp_stream << indent << "# Circuits are the hierarchical building blocks of the netlist." << endl;
}
for (db::Netlist::const_bottom_up_circuit_iterator i = nl->begin_bottom_up (); i != nl->end_bottom_up (); ++i) {
const db::Circuit *x = *i;
*mp_stream << Keys::circuit_key << "(" << tl::to_word_or_quoted_string (x->name ()) << endl;
write (l2n, *x);
*mp_stream << ")" << endl;
const db::Circuit *x = i.operator-> ();
*mp_stream << indent << Keys::circuit_key << "(" << tl::to_word_or_quoted_string (x->name ()) << endl;
write (nl, l2n, *x, indent, net2id_per_circuit);
*mp_stream << indent << ")" << endl;
m_progress.set (mp_stream->pos ());
}
}
void write_point (tl::OutputStream &stream, const db::Point &pt, db::Point &ref, bool relative)
{
if (relative) {
stream << "(";
stream << pt.x () - ref.x ();
stream << " ";
stream << pt.y () - ref.y ();
stream << ")";
} else {
if (pt.x () == 0 || pt.x () != ref.x ()) {
stream << pt.x ();
} else {
stream << "*";
}
if (pt.y () == 0 || pt.y () != ref.y ()) {
stream << pt.y ();
} else {
stream << "*";
}
}
ref = pt;
}
template <class T, class Tr>
void write_points (tl::OutputStream &stream, const T &poly, const Tr &tr, db::Point &ref, bool relative)
{
for (typename T::polygon_contour_iterator c = poly.begin_hull (); c != poly.end_hull (); ++c) {
typename T::point_type pt = tr * *c;
stream << " ";
write_point (stream, pt, ref, relative);
}
}
template <class Keys>
void std_writer_impl<Keys>::write (const db::LayoutToNetlist *l2n, const db::Circuit &circuit)
void std_writer_impl<Keys>::write (const db::Netlist *netlist, const db::LayoutToNetlist *l2n, const db::Circuit &circuit, const std::string &indent, std::map<const db::Circuit *, std::map<const db::Net *, unsigned int> > *net2id_per_circuit)
{
std::map<const db::Net *, unsigned int> net2id;
if (circuit.boundary ().vertices () > 0) {
if (! Keys::is_short ()) {
*mp_stream << endl << indent << indent1 << "# Circuit boundary" << endl;
}
reset_geometry_ref ();
db::Polygon poly = circuit.boundary ().transformed (db::VCplxTrans (1.0 / m_dbu));
if (poly.is_box ()) {
db::Box box = poly.box ();
*mp_stream << indent << indent1 << Keys::rect_key << "(";
write_point (*mp_stream, box.p1 (), m_ref, true);
*mp_stream << " ";
write_point (*mp_stream, box.p2 (), m_ref, true);
*mp_stream << ")" << endl;
} else {
*mp_stream << indent << indent1 << Keys::polygon_key << "(";
if (poly.holes () > 0) {
db::SimplePolygon sp = db::polygon_to_simple_polygon (poly);
write_points (*mp_stream, sp, db::UnitTrans (), m_ref, true);
} else {
write_points (*mp_stream, poly, db::UnitTrans (), m_ref, true);
}
*mp_stream << ")" << endl;
}
}
std::map<const db::Net *, unsigned int> net2id_local;
std::map<const db::Net *, unsigned int> *net2id = &net2id_local;
if (net2id_per_circuit) {
net2id = &(*net2id_per_circuit) [&circuit];
}
unsigned int id = 0;
for (db::Circuit::const_net_iterator n = circuit.begin_nets (); n != circuit.end_nets (); ++n) {
net2id->insert (std::make_pair (n.operator-> (), ++id));
}
if (circuit.begin_nets () != circuit.end_nets ()) {
if (! Keys::is_short ()) {
*mp_stream << endl << indent1 << "# Nets with their geometries" << endl;
if (l2n) {
*mp_stream << endl << indent << indent1 << "# Nets with their geometries" << endl;
} else {
*mp_stream << endl << indent << indent1 << "# Nets" << endl;
}
}
for (db::Circuit::const_net_iterator n = circuit.begin_nets (); n != circuit.end_nets (); ++n) {
net2id.insert (std::make_pair (n.operator-> (), ++id));
write (l2n, *n, id);
write (netlist, l2n, *n, (*net2id) [n.operator-> ()], indent);
m_progress.set (mp_stream->pos ());
}
}
if (circuit.begin_pins () != circuit.end_pins ()) {
if (! Keys::is_short ()) {
*mp_stream << endl << indent1 << "# Outgoing pins and their connections to nets" << endl;
*mp_stream << endl << indent << indent1 << "# Outgoing pins and their connections to nets" << endl;
}
for (db::Circuit::const_pin_iterator p = circuit.begin_pins (); p != circuit.end_pins (); ++p) {
*mp_stream << indent << indent1 << Keys::pin_key << "(";
const db::Net *net = circuit.net_for_pin (p->id ());
if (net) {
*mp_stream << indent1 << Keys::pin_key << "(" << tl::to_word_or_quoted_string (p->expanded_name ()) << " " << net2id [net] << ")" << endl;
*mp_stream << (*net2id) [net];
}
if (! p->name ().empty ()) {
if (net) {
*mp_stream << " ";
}
*mp_stream << Keys::name_key << "(" << tl::to_word_or_quoted_string (p->name ()) << ")";
}
*mp_stream << ")" << endl;
m_progress.set (mp_stream->pos ());
}
}
if (circuit.begin_devices () != circuit.end_devices ()) {
if (! Keys::is_short ()) {
*mp_stream << endl << indent1 << "# Devices and their connections" << endl;
*mp_stream << endl << indent << indent1 << "# Devices and their connections" << endl;
}
for (db::Circuit::const_device_iterator d = circuit.begin_devices (); d != circuit.end_devices (); ++d) {
write (l2n, *d, net2id);
write (l2n, *d, *net2id, indent);
m_progress.set (mp_stream->pos ());
}
}
if (circuit.begin_subcircuits () != circuit.end_subcircuits ()) {
if (! Keys::is_short ()) {
*mp_stream << endl << indent1 << "# Subcircuits and their connections" << endl;
*mp_stream << endl << indent << indent1 << "# Subcircuits and their connections" << endl;
}
for (db::Circuit::const_subcircuit_iterator x = circuit.begin_subcircuits (); x != circuit.end_subcircuits (); ++x) {
write (l2n, *x, net2id);
write (l2n, *x, *net2id, indent);
m_progress.set (mp_stream->pos ());
}
}
@ -219,39 +349,14 @@ void std_writer_impl<Keys>::write (const db::LayoutToNetlist *l2n, const db::Cir
}
}
template <class T, class Tr>
void write_points (tl::OutputStream &stream, const T &poly, const Tr &tr)
template <class Keys>
void std_writer_impl<Keys>::reset_geometry_ref ()
{
db::Coord x = 0, y = 0;
bool first = true;
for (typename T::polygon_contour_iterator c = poly.begin_hull (); c != poly.end_hull (); ++c) {
typename T::point_type pt = tr * *c;
stream << " ";
if (first || pt.x () != x) {
stream << pt.x ();
} else {
stream << "*";
}
stream << " ";
if (first || pt.y () != y) {
stream << pt.y ();
} else {
stream << "*";
}
first = false;
x = pt.x (); y = pt.y ();
}
m_ref = db::Point ();
}
template <class Keys>
void std_writer_impl<Keys>::write (const db::PolygonRef *s, const db::ICplxTrans &tr, const std::string &lname)
void std_writer_impl<Keys>::write (const db::PolygonRef *s, const db::ICplxTrans &tr, const std::string &lname, bool relative)
{
db::ICplxTrans t = tr * db::ICplxTrans (s->trans ());
@ -260,18 +365,20 @@ void std_writer_impl<Keys>::write (const db::PolygonRef *s, const db::ICplxTrans
db::Box box = t * poly.box ();
*mp_stream << Keys::rect_key << "(" << lname;
*mp_stream << " " << box.left () << " " << box.bottom ();
*mp_stream << " " << box.right () << " " << box.top ();
*mp_stream << " ";
write_point (*mp_stream, box.p1 (), m_ref, relative);
*mp_stream << " ";
write_point (*mp_stream, box.p2 (), m_ref, relative);
*mp_stream << ")";
} else {
*mp_stream << Keys::polygon_key << "(" << lname;
if (poly.holes () > 0) {
db::SimplePolygon sp (poly);
write_points (*mp_stream, sp, t);
db::SimplePolygon sp = db::polygon_to_simple_polygon (poly);
write_points (*mp_stream, sp, t, m_ref, relative);
} else {
write_points (*mp_stream, poly, t);
write_points (*mp_stream, poly, t, m_ref, relative);
}
*mp_stream << ")";
@ -279,52 +386,55 @@ void std_writer_impl<Keys>::write (const db::PolygonRef *s, const db::ICplxTrans
}
template <class Keys>
void std_writer_impl<Keys>::write (const db::LayoutToNetlist *l2n, const db::Net &net, unsigned int id)
void std_writer_impl<Keys>::write (const db::Netlist *netlist, const db::LayoutToNetlist *l2n, const db::Net &net, unsigned int id, const std::string &indent)
{
if (! l2n->netlist ()) {
throw tl::Exception (tl::to_string (tr ("Can't write annotated netlist before extraction has been done")));
}
const db::hier_clusters<db::PolygonRef> &clusters = l2n->net_clusters ();
const db::Circuit *circuit = net.circuit ();
const db::Connectivity &conn = l2n->connectivity ();
bool any = false;
for (db::Connectivity::layer_iterator l = conn.begin_layers (); l != conn.end_layers (); ++l) {
if (l2n) {
db::cell_index_type cci = circuit->cell_index ();
db::cell_index_type prev_ci = cci;
reset_geometry_ref ();
for (db::recursive_cluster_shape_iterator<db::PolygonRef> si (clusters, *l, cci, net.cluster_id ()); ! si.at_end (); ) {
for (db::Connectivity::layer_iterator l = conn.begin_layers (); l != conn.end_layers (); ++l) {
// NOTE: we don't recursive into circuits which will later be output. However, as circuits may
// vanish in "purge" but the clusters will still be there we need to recursive into clusters from
// unknown cells.
db::cell_index_type ci = si.cell_index ();
if (ci != prev_ci && ci != cci && (l2n->netlist ()->circuit_by_cell_index (ci) || l2n->netlist ()->device_abstract_by_cell_index (ci))) {
db::cell_index_type cci = circuit->cell_index ();
db::cell_index_type prev_ci = cci;
si.skip_cell ();
for (db::recursive_cluster_shape_iterator<db::PolygonRef> si (clusters, *l, cci, net.cluster_id ()); ! si.at_end (); ) {
} else {
// NOTE: we don't recursive into circuits which will later be output. However, as circuits may
// vanish in "purge" but the clusters will still be there we need to recursive into clusters from
// unknown cells.
db::cell_index_type ci = si.cell_index ();
if (ci != prev_ci && ci != cci && (netlist->circuit_by_cell_index (ci) || netlist->device_abstract_by_cell_index (ci))) {
if (! any) {
*mp_stream << indent1 << Keys::net_key << "(" << id;
if (! net.name ().empty ()) {
*mp_stream << " " << Keys::name_key << "(" << tl::to_word_or_quoted_string (net.name ()) << ")";
si.skip_cell ();
} else {
if (! any) {
*mp_stream << indent << indent1 << Keys::net_key << "(" << id;
if (! net.name ().empty ()) {
*mp_stream << " " << Keys::name_key << "(" << tl::to_word_or_quoted_string (net.name ()) << ")";
}
*mp_stream << endl;
any = true;
}
*mp_stream << indent << indent2;
write (si.operator-> (), si.trans (), name_for_layer (l2n, *l), true);
*mp_stream << endl;
any = true;
m_progress.set (mp_stream->pos ());
prev_ci = ci;
++si;
}
*mp_stream << indent2;
write (si.operator-> (), si.trans (), name_for_layer (l2n, *l));
*mp_stream << endl;
prev_ci = ci;
++si;
}
}
@ -332,10 +442,10 @@ void std_writer_impl<Keys>::write (const db::LayoutToNetlist *l2n, const db::Net
}
if (any) {
*mp_stream << indent1 << ")" << endl;
*mp_stream << indent << indent1 << ")" << endl;
} else {
*mp_stream << indent1 << Keys::net_key << "(" << id;
*mp_stream << indent << indent1 << Keys::net_key << "(" << id;
if (! net.name ().empty ()) {
*mp_stream << " " << Keys::name_key << "(" << tl::to_word_or_quoted_string (net.name ()) << ")";
}
@ -345,25 +455,19 @@ void std_writer_impl<Keys>::write (const db::LayoutToNetlist *l2n, const db::Net
}
template <class Keys>
void std_writer_impl<Keys>::write (const db::LayoutToNetlist *l2n, const db::SubCircuit &subcircuit, std::map<const Net *, unsigned int> &net2id)
void std_writer_impl<Keys>::write (const db::LayoutToNetlist *l2n, const db::SubCircuit &subcircuit, std::map<const db::Net *, unsigned int> &net2id, const std::string &indent)
{
const db::Layout *ly = l2n->internal_layout ();
double dbu = ly->dbu ();
*mp_stream << indent1 << Keys::circuit_key << "(" << tl::to_word_or_quoted_string (subcircuit.expanded_name ());
*mp_stream << indent << indent1 << Keys::circuit_key << "(" << tl::to_string (subcircuit.id ());
*mp_stream << " " << tl::to_word_or_quoted_string (subcircuit.circuit_ref ()->name ());
const db::DCplxTrans &tr = subcircuit.trans ();
if (tr.is_mag ()) {
*mp_stream << " " << Keys::scale_key << "(" << tr.mag () << ")";
if (! subcircuit.name ().empty ()) {
*mp_stream << " " << Keys::name_key << "(" << tl::to_word_or_quoted_string (subcircuit.name ()) << ")";
}
if (tr.is_mirror ()) {
*mp_stream << " " << Keys::mirror_key;
if (l2n) {
*mp_stream << " ";
write (subcircuit.trans ());
}
if (fabs (tr.angle ()) > 1e-6) {
*mp_stream << " " << Keys::rotation_key << "(" << tr.angle () << ")";
}
*mp_stream << " " << Keys::location_key << "(" << tr.disp ().x () / dbu << " " << tr.disp ().y () / dbu << ")";
// each pin in one line for more than a few pins
bool separate_lines = (subcircuit.circuit_ref ()->pin_count () > 1);
@ -376,26 +480,27 @@ void std_writer_impl<Keys>::write (const db::LayoutToNetlist *l2n, const db::Sub
const db::Net *net = subcircuit.net_for_pin (p->id ());
if (net) {
if (separate_lines) {
*mp_stream << indent2;
*mp_stream << indent << indent2;
} else {
*mp_stream << " ";
}
*mp_stream << Keys::pin_key << "(" << tl::to_word_or_quoted_string (p->expanded_name ()) << " " << net2id [net] << ")";
*mp_stream << Keys::pin_key << "(" << tl::to_string (p->id ()) << " " << net2id [net] << ")";
if (separate_lines) {
*mp_stream << endl;
}
m_progress.set (mp_stream->pos ());
}
}
if (separate_lines) {
*mp_stream << indent1;
*mp_stream << indent << indent1;
}
*mp_stream << ")" << endl;
}
template <class Keys>
void std_writer_impl<Keys>::write (const db::LayoutToNetlist *l2n, const db::DeviceAbstract &device_abstract)
void std_writer_impl<Keys>::write (const db::LayoutToNetlist *l2n, const db::DeviceAbstract &device_abstract, const std::string &indent)
{
const std::vector<db::DeviceTerminalDefinition> &td = device_abstract.device_class ()->terminal_definitions ();
@ -404,55 +509,131 @@ void std_writer_impl<Keys>::write (const db::LayoutToNetlist *l2n, const db::Dev
for (std::vector<db::DeviceTerminalDefinition>::const_iterator t = td.begin (); t != td.end (); ++t) {
*mp_stream << indent1 << Keys::terminal_key << "(" << t->name () << endl;
*mp_stream << indent << indent1 << Keys::terminal_key << "(" << t->name () << endl;
reset_geometry_ref ();
for (db::Connectivity::layer_iterator l = conn.begin_layers (); l != conn.end_layers (); ++l) {
const db::local_cluster<db::PolygonRef> &lc = clusters.clusters_per_cell (device_abstract.cell_index ()).cluster_by_id (device_abstract.cluster_id_for_terminal (t->id ()));
size_t cid = device_abstract.cluster_id_for_terminal (t->id ());
if (cid == 0) {
// no geometry
continue;
}
const db::local_cluster<db::PolygonRef> &lc = clusters.clusters_per_cell (device_abstract.cell_index ()).cluster_by_id (cid);
for (db::local_cluster<db::PolygonRef>::shape_iterator s = lc.begin (*l); ! s.at_end (); ++s) {
*mp_stream << indent2;
write (s.operator-> (), db::ICplxTrans (), name_for_layer (l2n, *l));
*mp_stream << indent << indent2;
write (s.operator-> (), db::ICplxTrans (), name_for_layer (l2n, *l), true);
*mp_stream << endl;
m_progress.set (mp_stream->pos ());
}
}
*mp_stream << indent1 << ")" << endl;
*mp_stream << indent << indent1 << ")" << endl;
m_progress.set (mp_stream->pos ());
}
}
template <class Keys>
void std_writer_impl<Keys>::write (const db::LayoutToNetlist *l2n, const db::Device &device, std::map<const Net *, unsigned int> &net2id)
void std_writer_impl<Keys>::write (const db::DCplxTrans &tr)
{
const db::Layout *ly = l2n->internal_layout ();
double dbu = ly->dbu ();
bool first = true;
*mp_stream << indent1 << Keys::device_key << "(" << tl::to_word_or_quoted_string (device.expanded_name ());
tl_assert (device.device_abstract () != 0);
*mp_stream << " " << tl::to_word_or_quoted_string (device.device_abstract ()->name ()) << endl;
*mp_stream << indent2 << Keys::location_key << "(" << device.position ().x () / dbu << " " << device.position ().y () / dbu << ")" << endl;
const std::vector<DeviceParameterDefinition> &pd = device.device_class ()->parameter_definitions ();
for (std::vector<DeviceParameterDefinition>::const_iterator i = pd.begin (); i != pd.end (); ++i) {
*mp_stream << indent2 << Keys::param_key << "(" << tl::to_word_or_quoted_string (i->name ()) << " " << device.parameter_value (i->id ()) << ")" << endl;
if (tr.is_mag ()) {
*mp_stream << Keys::scale_key << "(" << tr.mag () << ")";
first = false;
}
if (tr.is_mirror ()) {
if (! first) {
*mp_stream << " ";
}
*mp_stream << Keys::mirror_key;
first = false;
}
if (fabs (tr.angle ()) > 1e-6) {
if (! first) {
*mp_stream << " ";
}
*mp_stream << Keys::rotation_key << "(" << tr.angle () << ")";
first = false;
}
if (! first) {
*mp_stream << " ";
}
*mp_stream << Keys::location_key << "(" << floor (0.5 + tr.disp ().x () / m_dbu) << " " << floor (0.5 + tr.disp ().y () / m_dbu) << ")";
}
template <class Keys>
void std_writer_impl<Keys>::write (const db::LayoutToNetlist * /*l2n*/, const db::Device &device, std::map<const Net *, unsigned int> &net2id, const std::string &indent)
{
tl_assert (device.device_class () != 0);
const std::vector<DeviceTerminalDefinition> &td = device.device_class ()->terminal_definitions ();
const std::vector<DeviceParameterDefinition> &pd = device.device_class ()->parameter_definitions ();
*mp_stream << indent << indent1 << Keys::device_key << "(" << tl::to_string (device.id ());
if (device.device_abstract ()) {
*mp_stream << " " << tl::to_word_or_quoted_string (device.device_abstract ()->name ()) << endl;
const std::vector<db::DeviceAbstractRef> &other_abstracts = device.other_abstracts ();
for (std::vector<db::DeviceAbstractRef>::const_iterator a = other_abstracts.begin (); a != other_abstracts.end (); ++a) {
*mp_stream << indent << indent2 << Keys::device_key << "(" << tl::to_word_or_quoted_string (a->device_abstract->name ()) << " ";
write (a->trans);
*mp_stream << ")" << endl;
}
const std::map<unsigned int, std::vector<db::DeviceReconnectedTerminal> > &reconnected_terminals = device.reconnected_terminals ();
for (std::map<unsigned int, std::vector<db::DeviceReconnectedTerminal> >::const_iterator t = reconnected_terminals.begin (); t != reconnected_terminals.end (); ++t) {
for (std::vector<db::DeviceReconnectedTerminal>::const_iterator c = t->second.begin (); c != t->second.end (); ++c) {
*mp_stream << indent << indent2 << Keys::connect_key << "(" << c->device_index << " " << tl::to_word_or_quoted_string (td [t->first].name ()) << " " << tl::to_word_or_quoted_string (td [c->other_terminal_id].name ()) << ")" << endl;
}
}
*mp_stream << indent << indent2;
write (device.trans ());
*mp_stream << endl;
} else {
*mp_stream << " " << tl::to_word_or_quoted_string (device.device_class ()->name ()) << endl;
}
if (! device.name ().empty ()) {
*mp_stream << indent << indent2 << Keys::name_key << "(" << tl::to_word_or_quoted_string (device.name ()) << ")" << endl;
}
for (std::vector<DeviceParameterDefinition>::const_iterator i = pd.begin (); i != pd.end (); ++i) {
*mp_stream << indent << indent2 << Keys::param_key << "(" << tl::to_word_or_quoted_string (i->name ()) << " " << tl::sprintf ("%.12g", device.parameter_value (i->id ())) << ")" << endl;
}
for (std::vector<DeviceTerminalDefinition>::const_iterator i = td.begin (); i != td.end (); ++i) {
const db::Net *net = device.net_for_terminal (i->id ());
if (net) {
*mp_stream << indent2 << Keys::terminal_key << "(" << tl::to_word_or_quoted_string (i->name ()) << " " << net2id [net] << ")" << endl;
*mp_stream << indent << indent2 << Keys::terminal_key << "(" << tl::to_word_or_quoted_string (i->name ()) << " " << net2id [net] << ")" << endl;
} else {
*mp_stream << indent << indent2 << Keys::terminal_key << "(" << tl::to_word_or_quoted_string (i->name ()) << ")" << endl;
}
}
*mp_stream << indent1 << ")" << endl;
*mp_stream << indent << indent1 << ")" << endl;
}
// explicit instantiation
template class std_writer_impl<l2n_std_format::keys<false> >;
template class std_writer_impl<l2n_std_format::keys<true> >;
}
// -------------------------------------------------------------------------------------------
@ -464,13 +645,22 @@ LayoutToNetlistStandardWriter::LayoutToNetlistStandardWriter (tl::OutputStream &
// .. nothing yet ..
}
void LayoutToNetlistStandardWriter::write (const db::LayoutToNetlist *l2n)
void LayoutToNetlistStandardWriter::do_write (const db::LayoutToNetlist *l2n)
{
if (! l2n->netlist ()) {
throw tl::Exception (tl::to_string (tr ("Can't write annotated netlist before the netlist has been created")));
}
if (! l2n->internal_layout ()) {
throw tl::Exception (tl::to_string (tr ("Can't write annotated netlist before the layout has been loaded")));
}
double dbu = l2n->internal_layout ()->dbu ();
if (m_short_version) {
l2n_std_format::std_writer_impl<l2n_std_format::keys<true> > writer (*mp_stream);
l2n_std_format::std_writer_impl<l2n_std_format::keys<true> > writer (*mp_stream, dbu);
writer.write (l2n);
} else {
l2n_std_format::std_writer_impl<l2n_std_format::keys<false> > writer (*mp_stream);
l2n_std_format::std_writer_impl<l2n_std_format::keys<false> > writer (*mp_stream, dbu);
writer.write (l2n);
}
}

View File

@ -24,11 +24,59 @@
#define HDR_dbLayoutToNetlistWriter
#include "dbCommon.h"
#include "dbPoint.h"
#include "dbTrans.h"
#include "dbPolygon.h"
#include "tlStream.h"
#include "tlProgress.h"
namespace db
{
class Circuit;
class SubCircuit;
class Device;
class DeviceAbstract;
class Net;
class Netlist;
class LayoutToNetlist;
namespace l2n_std_format
{
template <class Keys>
class std_writer_impl
{
public:
std_writer_impl (tl::OutputStream &stream, double dbu, const std::string &progress_description = std::string ());
void write (const db::LayoutToNetlist *l2n);
protected:
void write (const db::Netlist *netlist, const db::LayoutToNetlist *l2n, bool nested, std::map<const db::Circuit *, std::map<const db::Net *, unsigned int> > *net2id_per_circuit);
void write (const db::Netlist *netlist, const db::LayoutToNetlist *l2n, const db::Circuit &circuit, const std::string &indent, std::map<const db::Circuit *, std::map<const db::Net *, unsigned int> > *net2id_per_circuit);
void write (const db::Netlist *netlist, const db::LayoutToNetlist *l2n, const db::Net &net, unsigned int id, const std::string &indent);
void write (const db::LayoutToNetlist *l2n, const db::SubCircuit &subcircuit, std::map<const Net *, unsigned int> &net2id, const std::string &indent);
void write (const db::LayoutToNetlist *l2n, const db::Device &device, std::map<const Net *, unsigned int> &net2id, const std::string &indent);
void write (const db::LayoutToNetlist *l2n, const db::DeviceAbstract &device_abstract, const std::string &indent);
void write (const db::PolygonRef *s, const db::ICplxTrans &tr, const std::string &lname, bool relative);
void write (const db::DCplxTrans &trans);
void reset_geometry_ref ();
tl::OutputStream &stream ()
{
return *mp_stream;
}
private:
tl::OutputStream *mp_stream;
db::Point m_ref;
double m_dbu;
tl::AbsoluteProgress m_progress;
};
}
class LayoutToNetlist;
/**
@ -37,10 +85,16 @@ class LayoutToNetlist;
class DB_PUBLIC LayoutToNetlistWriterBase
{
public:
LayoutToNetlistWriterBase () { }
virtual ~LayoutToNetlistWriterBase () { }
LayoutToNetlistWriterBase ();
virtual ~LayoutToNetlistWriterBase ();
virtual void write (const db::LayoutToNetlist *l2n) = 0;
void write (const db::LayoutToNetlist *l2n);
protected:
virtual void do_write (const db::LayoutToNetlist *l2n) = 0;
private:
std::string m_filename;
};
/**
@ -52,7 +106,8 @@ class DB_PUBLIC LayoutToNetlistStandardWriter
public:
LayoutToNetlistStandardWriter (tl::OutputStream &stream, bool short_version);
void write (const db::LayoutToNetlist *l2n);
protected:
void do_write (const db::LayoutToNetlist *l2n);
private:
tl::OutputStream *mp_stream;

View File

@ -409,8 +409,8 @@ find_layout_context (const db::Layout &layout, db::cell_index_type from, db::cel
// ------------------------------------------------------------
// Implementation of ContextCache
ContextCache::ContextCache (const db::Layout &layout)
: mp_layout (&layout)
ContextCache::ContextCache (const db::Layout *layout)
: mp_layout (layout)
{
// .. nothing yet ..
}
@ -418,6 +418,11 @@ ContextCache::ContextCache (const db::Layout &layout)
const std::pair<bool, db::ICplxTrans> &
ContextCache::find_layout_context (db::cell_index_type from, db::cell_index_type to)
{
if (! mp_layout) {
static std::pair<bool, db::ICplxTrans> nothing (false, db::ICplxTrans ());
return nothing;
}
std::map<std::pair<db::cell_index_type, db::cell_index_type>, std::pair<bool, db::ICplxTrans> >::iterator c = m_cache.find (std::make_pair (from, to));
if (c == m_cache.end ()) {
c = m_cache.insert (std::make_pair (std::make_pair (from, to), std::make_pair (false, db::ICplxTrans ()))).first;

View File

@ -211,7 +211,7 @@ public:
/**
* @brief Creates an object of ContextCache associated with the given layout
*/
ContextCache (const db::Layout &layout);
ContextCache (const db::Layout *layout);
/**
* @brief Find the context for the given cell combination

View File

@ -0,0 +1,106 @@
/*
KLayout Layout Viewer
Copyright (C) 2006-2019 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 "dbLayoutVsSchematic.h"
#include "dbLayoutVsSchematicWriter.h"
#include "dbLayoutVsSchematicReader.h"
namespace db
{
LayoutVsSchematic::LayoutVsSchematic (const db::RecursiveShapeIterator &iter)
: LayoutToNetlist (iter)
{
// .. nothing yet ..
}
LayoutVsSchematic::LayoutVsSchematic (db::DeepShapeStore *dss, unsigned int layout_index)
: LayoutToNetlist (dss, layout_index)
{
// .. nothing yet ..
}
LayoutVsSchematic::LayoutVsSchematic (const std::string &topcell_name, double dbu)
: LayoutToNetlist (topcell_name, dbu)
{
// .. nothing yet ..
}
LayoutVsSchematic::LayoutVsSchematic ()
: LayoutToNetlist ()
{
// .. nothing yet ..
}
LayoutVsSchematic::~LayoutVsSchematic ()
{
// .. nothing yet ..
}
void LayoutVsSchematic::set_reference_netlist (db::Netlist *ref_netlist)
{
mp_reference_netlist.reset (ref_netlist);
mp_cross_ref.reset (0);
}
bool LayoutVsSchematic::compare_netlists (db::NetlistComparer *compare)
{
if (! netlist ()) {
throw tl::Exception (tl::to_string (tr ("The netlist has not been extracted yet")));
}
if (! reference_netlist ()) {
throw tl::Exception (tl::to_string (tr ("The reference netlist has not been set yet")));
}
return compare->compare (netlist (), reference_netlist (), make_cross_ref ());
}
db::NetlistCrossReference *LayoutVsSchematic::make_cross_ref ()
{
if (! mp_cross_ref.get ()) {
mp_cross_ref.reset (new db::NetlistCrossReference ());
}
return mp_cross_ref.get ();
}
void db::LayoutVsSchematic::save (const std::string &path, bool short_format)
{
tl::OutputStream stream (path);
db::LayoutVsSchematicStandardWriter writer (stream, short_format);
set_filename (path);
writer.write (this);
}
void db::LayoutVsSchematic::load (const std::string &path)
{
tl::InputStream stream (path);
db::LayoutVsSchematicStandardReader reader (stream);
set_filename (path);
set_name (stream.filename ());
reader.read (this);
}
}

View File

@ -0,0 +1,187 @@
/*
KLayout Layout Viewer
Copyright (C) 2006-2019 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_dbLayoutVsSchematic
#define _HDR_dbLayoutVsSchematic
#include "dbCommon.h"
#include "dbLayoutToNetlist.h"
#include "dbNetlistCompare.h"
#include "dbNetlistCrossReference.h"
namespace db
{
/**
* @brief An extension of the LayoutToNetlist framework towards comparision vs. schematic
*
* This aggregate holds the following entities in addition to the ones provided by
* the LayoutToNetlist entity:
*
* * A reference netlist
* * A cross-reference object
*
* The cross-reference object connects the extracted netlist with the reference netlist.
*
* In addition to the steps required to create a LayoutToNetlist object, the following
* has to be provided:
*
* * A reference netlist has to be loaded using "set_reference_netlist"
* * Netlist comparison has to be performed using the NetlistCompare object provided.
* This will establish the cross-reference between the two netlists.
*/
class DB_PUBLIC LayoutVsSchematic
: public db::LayoutToNetlist
{
public:
/**
* @brief The constructor
*
* See the LayoutToNetlist for details.
*/
LayoutVsSchematic (const db::RecursiveShapeIterator &iter);
/**
* @brief Alternative constructor using an external deep shape storage
*
* See the LayoutToNetlist for details.
*/
LayoutVsSchematic (db::DeepShapeStore *dss, unsigned int layout_index = 0);
/**
* @brief Alternative constructor for flat mode
*
* See the LayoutToNetlist for details.
*/
LayoutVsSchematic (const std::string &topcell_name, double dbu);
/**
* @brief The default constructor
*/
LayoutVsSchematic ();
/**
* @brief The destructor
*/
~LayoutVsSchematic ();
/**
* @brief Sets the reference netlist
*
* This will establish the reference netlist for the comparison.
* The LayoutVsSchematic object will take ownership over the netlist
* object.
*
* Setting the reference netlist will reset the cross-reference
* object.
*/
void set_reference_netlist (db::Netlist *ref_netlist);
/**
* @brief Gets the reference netlist
*/
const db::Netlist *reference_netlist () const
{
return mp_reference_netlist.get ();
}
/**
* @brief Gets the reference netlist (non-const version)
*/
db::Netlist *reference_netlist ()
{
return mp_reference_netlist.get ();
}
/**
* @brief Performs the comparison
*/
bool compare_netlists(NetlistComparer *compare);
/**
* @brief Gets the cross-reference object
*
* This reference is 0 if the netlist compare has not been performed yet.
*/
const db::NetlistCrossReference *cross_ref () const
{
return mp_cross_ref.get ();
}
/**
* @brief Gets the cross-reference object (non-const version)
*
* This reference is 0 if the netlist compare has not been performed yet.
*/
db::NetlistCrossReference *cross_ref ()
{
return mp_cross_ref.get ();
}
/**
* @brief Creates the cross-reference object if it isn't created yet
*
* This method is provided for special purposes such as the reader.
*/
db::NetlistCrossReference *make_cross_ref ();
/**
* @brief Saves the database to the given path
*
* Currently, the internal format will be used. If "short_format" is true, the short version
* of the format is used.
*
* This is a convenience method. The low-level functionality is the LayoutVsSchematicWriter.
*/
void save (const std::string &path, bool short_format);
/**
* @brief Loads the database from the given path
*
* This is a convenience method. The low-level functionality is the LayoutVsSchematicReader.
*/
void load (const std::string &path);
private:
// no copying
LayoutVsSchematic (const db::LayoutVsSchematic &other);
LayoutVsSchematic &operator= (const db::LayoutVsSchematic &other);
tl::shared_ptr<db::Netlist> mp_reference_netlist;
tl::shared_ptr<db::NetlistCrossReference> mp_cross_ref;
};
}
namespace tl
{
template<> struct type_traits<db::LayoutVsSchematic> : public tl::type_traits<void>
{
// mark "NetlistDeviceExtractor" as not having a default ctor and no copy ctor
typedef tl::false_tag has_copy_constructor;
typedef tl::false_tag has_default_constructor;
};
}
#endif

View File

@ -0,0 +1,55 @@
/*
KLayout Layout Viewer
Copyright (C) 2006-2019 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 "dbLayoutVsSchematicFormatDefs.h"
namespace db
{
namespace lvs_std_format
{
const char *lvs_magic_string_cstr = "#%lvsdb-klayout";
DB_PUBLIC std::string ShortKeys::lvs_magic_string (lvs_magic_string_cstr);
DB_PUBLIC std::string LongKeys::lvs_magic_string (lvs_magic_string_cstr);
DB_PUBLIC std::string LongKeys::reference_key ("reference");
DB_PUBLIC std::string LongKeys::layout_key ("layout");
DB_PUBLIC std::string LongKeys::xref_key ("xref");
DB_PUBLIC std::string LongKeys::mismatch_key ("mismatch");
DB_PUBLIC std::string LongKeys::match_key ("match");
DB_PUBLIC std::string LongKeys::nomatch_key ("nomatch");
DB_PUBLIC std::string LongKeys::warning_key ("warning");
DB_PUBLIC std::string LongKeys::skipped_key ("skipped");
DB_PUBLIC std::string ShortKeys::reference_key ("H");
DB_PUBLIC std::string ShortKeys::layout_key ("J");
DB_PUBLIC std::string ShortKeys::xref_key ("Z");
DB_PUBLIC std::string ShortKeys::mismatch_key ("0");
DB_PUBLIC std::string ShortKeys::match_key ("1");
DB_PUBLIC std::string ShortKeys::nomatch_key ("X");
DB_PUBLIC std::string ShortKeys::warning_key ("W");
DB_PUBLIC std::string ShortKeys::skipped_key ("S");
}
}

View File

@ -0,0 +1,171 @@
/*
KLayout Layout Viewer
Copyright (C) 2006-2019 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_dbLayoutVsSchematicFormatDefs
#define HDR_dbLayoutVsSchematicFormatDefs
#include "dbCommon.h"
#include "dbLayoutToNetlistFormatDefs.h"
#include <string>
namespace db
{
/**
* This is the internal persistency format for LayoutVsSchematic
*
* It's intentionally *not* XML to keep the overhead low.
*
* Comments are introduced by hash: # ...
* Names are words (alphanumerical plus "$", "_", ".") or enclosed in single or double quotes.
* Escape character is backslash.
* Separator is either , or whitespace. Keywords and names are case sensitive.
* Short keys are provided for compacter representation. Short keys can be
* non-alpha (e.g. "*") or empty.
* Single-valued attributes can be given without brackets.
* All dimensions are in units of database unit.
* The file follows the declaration-before-use principle
* (circuits before subcircuits, nets before use ...)
*
* Global statements:
*
* #%lvsdb-klayout - header line identifies format
* version(<number>) - file format version [short key: V]
* description(<text>) - an arbitrary description text [short key: B]
* layout([layout]) - layout part [short key: J]
* reference([reference-def]*) - reference netlist part [short key: H]
* xref([xref-def]*) - cross-reference part [short key: Z]
*
* [layout]:
*
* ... - the LayoutToNetlist dump without version and description
*
* [reference-def]:
*
* circuit(<name> [netlist-circuit-def]*)
* - circuit [short key: X]
* [netlist-circuit-def]:
*
* net(<id> [net-name]?) - a net declaration [short key: N]
* pin(<name> <net-id>) - outgoing pin connection [short key: P]
* device(<name> [device-def]*) - device with connections [short key: D]
* circuit(<name> [subcircuit-def]*)
* - subcircuit with connections [short key: X]
*
* [net-name]:
*
* name(<net-name>) - specify net name [short key: I]
*
* [device-def]:
*
* terminal(<terminal-name> <net-id>)
* - specifies connection of the terminal with
* a net [short key: T]
*
* [subcircuit-def]:
*
* pin(<pin-name> <net-id>) - specifies connection of the pin with a net [short key: P]
*
* [xref-def]:
*
* circuit([non] [non] [status]? [circuit-xrefs])
* - circuit pair [short key: X]
*
* [circuit-xrefs]:
*
* xref([pair]*)
*
* [pair]
*
* pin([ion] [ion] [status]?) - a pin pair [short key: P]
* device([ion] [ion] [status]?) - a device pair [short key: D]
* circuit([ion] [ion] [status]?) - a subcircuit pair [short key: X]
* net([ion] [ion] [status]?) - a net pair [short key: N]
*
* [non]
*
* <name> | ()
*
* [ion]
*
* <id> | ()
*
* [status]
*
* mismatch | - [short key: 0]
* match | - [short key: 1]
* nomatch | - [short key: X]
* warning | - [short key: W]
* skipped - [short key: S]
*/
namespace lvs_std_format
{
struct DB_PUBLIC ShortKeys
{
static std::string lvs_magic_string;
static std::string reference_key;
static std::string layout_key;
static std::string xref_key;
static std::string mismatch_key;
static std::string match_key;
static std::string nomatch_key;
static std::string warning_key;
static std::string skipped_key;
};
struct DB_PUBLIC LongKeys
{
static std::string lvs_magic_string;
static std::string reference_key;
static std::string layout_key;
static std::string xref_key;
static std::string mismatch_key;
static std::string match_key;
static std::string nomatch_key;
static std::string warning_key;
static std::string skipped_key;
};
template <bool Short> struct DB_PUBLIC keys;
template <> struct DB_PUBLIC keys<true> : public l2n_std_format::keys<true>, public ShortKeys
{
typedef l2n_std_format::keys<true> l2n_keys;
inline static bool is_short () { return true; }
};
template <> struct DB_PUBLIC keys<false> : public l2n_std_format::keys<false>, public LongKeys
{
typedef l2n_std_format::keys<false> l2n_keys;
inline static bool is_short () { return false; }
};
}
}
#endif

View File

@ -0,0 +1,383 @@
/*
KLayout Layout Viewer
Copyright (C) 2006-2019 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 "dbLayoutVsSchematicReader.h"
#include "dbLayoutVsSchematicFormatDefs.h"
namespace db
{
typedef lvs_std_format::keys<true> skeys;
typedef lvs_std_format::keys<false> lkeys;
// -------------------------------------------------------------------------------------------------
// LayoutVsSchematicStandardReader implementation
LayoutVsSchematicStandardReader::LayoutVsSchematicStandardReader (tl::InputStream &stream)
: LayoutToNetlistStandardReader (stream)
{
// .. nothing yet ..
}
void LayoutVsSchematicStandardReader::do_read_lvs (db::LayoutVsSchematic *l2n)
{
try {
read_netlist (l2n);
} catch (tl::Exception &ex) {
throw tl::Exception (tl::sprintf (tl::to_string (tr ("%s in line: %d of %s")), ex.msg (), stream ().line_number (), path ()));
}
}
void LayoutVsSchematicStandardReader::read_netlist (db::LayoutVsSchematic *lvs)
{
int version = 0;
std::string description;
m_map_per_circuit_a.clear ();
m_map_per_circuit_b.clear ();
tl_assert (lvs->internal_layout ());
lvs->internal_layout ()->dbu (1.0); // mainly for testing
if (lvs->internal_layout ()->cells () == 0) {
lvs->internal_layout ()->add_cell ("TOP");
}
tl_assert (lvs->internal_top_cell () != 0);
lvs->make_netlist ();
while (! at_end ()) {
if (test (skeys::version_key) || test (lkeys::version_key)) {
Brace br (this);
version = read_int ();
br.done ();
} else if (test (skeys::description_key) || test (lkeys::description_key)) {
Brace br (this);
read_word_or_quoted (description);
br.done ();
} else if (test (skeys::layout_key) || test (lkeys::layout_key)) {
Brace br (this);
LayoutToNetlistStandardReader::read_netlist (0, lvs, true /*nested*/, &m_map_per_circuit_a);
br.done ();
} else if (test (skeys::reference_key) || test (lkeys::reference_key)) {
Brace br (this);
std::auto_ptr<db::Netlist> netlist (new db::Netlist ());
LayoutToNetlistStandardReader::read_netlist (netlist.get (), 0, true /*nested*/, &m_map_per_circuit_b);
lvs->set_reference_netlist (netlist.release ());
br.done ();
} else if (test (skeys::xref_key) || test (lkeys::xref_key)) {
if (! lvs->reference_netlist () || ! lvs->netlist ()) {
throw tl::Exception (tl::to_string (tr ("xref section before reference or layout netlist")));
}
db::NetlistCrossReference *xref = lvs->make_cross_ref ();
xref->gen_begin_netlist (lvs->netlist (), lvs->reference_netlist ());
read_xref (xref);
xref->gen_end_netlist (lvs->netlist (), lvs->reference_netlist ());
} else if (at_end ()) {
throw tl::Exception (tl::to_string (tr ("Unexpected end of file")));
} else {
throw tl::Exception (tl::to_string (tr ("Invalid keyword")));
}
}
}
bool LayoutVsSchematicStandardReader::read_status (db::NetlistCrossReference::Status &status)
{
if (test (skeys::match_key) || test (lkeys::match_key)) {
status = db::NetlistCrossReference::Match;
return true;
} else if (test (skeys::nomatch_key) || test (lkeys::nomatch_key)) {
status = db::NetlistCrossReference::NoMatch;
return true;
} else if (test (skeys::mismatch_key) || test (lkeys::mismatch_key)) {
status = db::NetlistCrossReference::Mismatch;
return true;
} else if (test (skeys::warning_key) || test (lkeys::warning_key)) {
status = db::NetlistCrossReference::MatchWithWarning;
return true;
} else if (test (skeys::skipped_key) || test (lkeys::skipped_key)) {
status = db::NetlistCrossReference::Skipped;
return true;
} else {
return false;
}
}
void LayoutVsSchematicStandardReader::read_xrefs_for_circuits (db::NetlistCrossReference *xref, const db::Circuit *circuit_a, const db::Circuit *circuit_b)
{
Brace br (this);
while (br) {
if (test (skeys::net_key) || test (lkeys::net_key)) {
read_net_pair (xref, circuit_a, circuit_b);
} else if (test (skeys::pin_key) || test (lkeys::pin_key)) {
read_pin_pair (xref, circuit_a, circuit_b);
} else if (test (skeys::device_key) || test (lkeys::device_key)) {
read_device_pair (xref, circuit_a, circuit_b);
} else if (test (skeys::circuit_key) || test (lkeys::circuit_key)) {
read_subcircuit_pair (xref, circuit_a, circuit_b);
} else if (at_end ()) {
throw tl::Exception (tl::to_string (tr ("Unexpected end of file inside circuit definition (net, pin, device or circuit expected)")));
} else {
throw tl::Exception (tl::to_string (tr ("Invalid keyword inside circuit definition (net, pin, device or circuit expected)")));
}
}
br.done ();
}
void LayoutVsSchematicStandardReader::read_xref (db::NetlistCrossReference *xref)
{
Brace br (this);
while (br) {
if (test (skeys::circuit_key) || test (lkeys::circuit_key)) {
Brace br (this);
std::pair<std::string, bool> non_a, non_b;
non_a = read_non ();
non_b = read_non ();
const db::Circuit *circuit_a = 0;
if (non_a.second) {
circuit_a = xref->netlist_a ()->circuit_by_name (non_a.first);
if (! circuit_a) {
throw tl::Exception (tl::to_string (tr ("Not a valid circuit name: ")) + non_a.first);
}
}
const db::Circuit *circuit_b = 0;
if (non_b.second) {
circuit_b = xref->netlist_b ()->circuit_by_name (non_b.first);
if (! circuit_b) {
throw tl::Exception (tl::to_string (tr ("Not a valid circuit name: ")) + non_b.first);
}
}
xref->gen_begin_circuit (circuit_a, circuit_b);
db::NetlistCrossReference::Status status = db::NetlistCrossReference::None;
while (br) {
if (read_status (status)) {
// continue
} else if (test (skeys::xref_key) || test (lkeys::xref_key)) {
read_xrefs_for_circuits (xref, circuit_a, circuit_b);
} else if (at_end ()) {
throw tl::Exception (tl::to_string (tr ("Unexpected end of file inside circuit definition (status keyword of xrefs expected)")));
} else {
throw tl::Exception (tl::to_string (tr ("Invalid keyword inside circuit definition (status keyword of xrefs expected)")));
}
}
xref->gen_end_circuit (circuit_a, circuit_b, status);
br.done ();
}
}
br.done ();
}
std::pair<std::string, bool> LayoutVsSchematicStandardReader::read_non ()
{
if (test ("(")) {
expect (")");
return std::make_pair (std::string (), false);
} else {
std::string s;
read_word_or_quoted (s);
return std::make_pair (s, true);
}
}
std::pair<unsigned int, bool> LayoutVsSchematicStandardReader::read_ion ()
{
if (test ("(")) {
expect (")");
return std::make_pair (0, false);
} else {
return std::make_pair ((unsigned int) read_int (), true);
}
}
static const db::Net *net_by_numerical_id (const db::Circuit *circuit, const std::pair<unsigned int, bool> &ion, std::map<const db::Circuit *, db::LayoutToNetlistStandardReader::ObjectMap> &map_per_circuit)
{
if (ion.second && circuit) {
std::map<const db::Circuit *, db::LayoutToNetlistStandardReader::ObjectMap>::const_iterator i = map_per_circuit.find (circuit);
if (i != map_per_circuit.end ()) {
std::map<unsigned int, Net *>::const_iterator j = i->second.id2net.find (ion.first);
if (j != i->second.id2net.end ()) {
return j->second;
}
}
throw tl::Exception (tl::to_string (tr ("Not a valid net ID: ")) + tl::to_string (ion.first));
}
return 0;
}
static const db::Device *device_by_numerical_id (const db::Circuit *circuit, const std::pair<unsigned int, bool> &ion, std::map<const db::Circuit *, db::LayoutToNetlistStandardReader::ObjectMap> &map_per_circuit)
{
if (ion.second && circuit) {
std::map<const db::Circuit *, db::LayoutToNetlistStandardReader::ObjectMap>::const_iterator i = map_per_circuit.find (circuit);
if (i != map_per_circuit.end ()) {
std::map<unsigned int, Device *>::const_iterator j = i->second.id2device.find (ion.first);
if (j != i->second.id2device.end ()) {
return j->second;
}
}
throw tl::Exception (tl::to_string (tr ("Not a valid device ID: ")) + tl::to_string (ion.first));
}
return 0;
}
static const db::SubCircuit *subcircuit_by_numerical_id (const db::Circuit *circuit, const std::pair<unsigned int, bool> &ion, std::map<const db::Circuit *, db::LayoutToNetlistStandardReader::ObjectMap> &map_per_circuit)
{
if (ion.second && circuit) {
std::map<const db::Circuit *, db::LayoutToNetlistStandardReader::ObjectMap>::const_iterator i = map_per_circuit.find (circuit);
if (i != map_per_circuit.end ()) {
std::map<unsigned int, SubCircuit *>::const_iterator j = i->second.id2subcircuit.find (ion.first);
if (j != i->second.id2subcircuit.end ()) {
return j->second;
}
}
throw tl::Exception (tl::to_string (tr ("Not a subcircuit device ID: ")) + tl::to_string (ion.first));
}
return 0;
}
static const db::Pin *pin_by_numerical_id (const db::Circuit *circuit, const std::pair<unsigned int, bool> &ion)
{
if (ion.second && circuit) {
const db::Pin *pin = circuit->pin_by_id (ion.first);
if (! pin) {
throw tl::Exception (tl::to_string (tr ("Not a valid pin ID: ")) + tl::to_string (ion.first));
}
return pin;
} else {
return 0;
}
}
void LayoutVsSchematicStandardReader::read_net_pair (db::NetlistCrossReference *xref, const db::Circuit *circuit_a, const db::Circuit *circuit_b)
{
Brace br (this);
std::pair<unsigned int, bool> ion_a, ion_b;
ion_a = read_ion ();
ion_b = read_ion ();
db::NetlistCrossReference::Status status = db::NetlistCrossReference::None;
read_status (status);
br.done ();
xref->gen_nets (net_by_numerical_id (circuit_a, ion_a, m_map_per_circuit_a), net_by_numerical_id (circuit_b, ion_b, m_map_per_circuit_b), status);
}
void LayoutVsSchematicStandardReader::read_pin_pair (db::NetlistCrossReference *xref, const db::Circuit *circuit_a, const db::Circuit *circuit_b)
{
Brace br (this);
std::pair<unsigned int, bool> ion_a, ion_b;
ion_a = read_ion ();
ion_b = read_ion ();
db::NetlistCrossReference::Status status = db::NetlistCrossReference::None;
read_status (status);
br.done ();
xref->gen_pins (pin_by_numerical_id (circuit_a, ion_a), pin_by_numerical_id (circuit_b, ion_b), status);
}
void LayoutVsSchematicStandardReader::read_device_pair (db::NetlistCrossReference *xref, const db::Circuit *circuit_a, const db::Circuit *circuit_b)
{
Brace br (this);
std::pair<unsigned int, bool> ion_a, ion_b;
ion_a = read_ion ();
ion_b = read_ion ();
db::NetlistCrossReference::Status status = db::NetlistCrossReference::None;
read_status (status);
br.done ();
xref->gen_devices (device_by_numerical_id (circuit_a, ion_a, m_map_per_circuit_a), device_by_numerical_id (circuit_b, ion_b, m_map_per_circuit_b), status);
}
void LayoutVsSchematicStandardReader::read_subcircuit_pair (db::NetlistCrossReference *xref, const db::Circuit *circuit_a, const db::Circuit *circuit_b)
{
Brace br (this);
std::pair<unsigned int, bool> ion_a, ion_b;
ion_a = read_ion ();
ion_b = read_ion ();
db::NetlistCrossReference::Status status = db::NetlistCrossReference::None;
read_status (status);
br.done ();
xref->gen_subcircuits (subcircuit_by_numerical_id (circuit_a, ion_a, m_map_per_circuit_a), subcircuit_by_numerical_id (circuit_b, ion_b, m_map_per_circuit_b), status);
}
}

View File

@ -0,0 +1,96 @@
/*
KLayout Layout Viewer
Copyright (C) 2006-2019 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_dbLayoutVsSchematicReader
#define HDR_dbLayoutVsSchematicReader
#include "dbCommon.h"
#include "dbPolygon.h"
#include "dbCell.h"
#include "dbLayoutVsSchematic.h"
#include "dbLayoutToNetlistReader.h"
#include "tlStream.h"
namespace db {
class LayoutVsSchematic;
class Circuit;
class Cell;
class DeviceAbstract;
class DeviceClass;
class Net;
class Region;
/**
* @brief The base class for a LayoutVsSchematic writer
*/
class DB_PUBLIC LayoutVsSchematicReaderBase
{
public:
LayoutVsSchematicReaderBase () { }
virtual ~LayoutVsSchematicReaderBase () { }
void read (db::LayoutVsSchematic *lvs)
{
do_read_lvs (lvs);
}
protected:
virtual void do_read_lvs (db::LayoutVsSchematic *lvs) = 0;
};
/**
* @brief The standard writer
*/
class DB_PUBLIC LayoutVsSchematicStandardReader
: public LayoutVsSchematicReaderBase, protected LayoutToNetlistStandardReader
{
public:
LayoutVsSchematicStandardReader (tl::InputStream &stream);
void read (db::LayoutVsSchematic *lvs)
{
do_read_lvs (lvs);
}
virtual void do_read_lvs (db::LayoutVsSchematic *lvs);
private:
void read_netlist (db::LayoutVsSchematic *lvs);
bool read_status (db::NetlistCrossReference::Status &status);
void read_xref (db::NetlistCrossReference *xref);
void read_xrefs_for_circuits (db::NetlistCrossReference *xref, const db::Circuit *circuit_a, const db::Circuit *circuit_b);
void read_net_pair (db::NetlistCrossReference *xref, const db::Circuit *circuit_a, const db::Circuit *circuit_b);
void read_pin_pair (db::NetlistCrossReference *xref, const db::Circuit *circuit_a, const db::Circuit *circuit_b);
void read_device_pair (db::NetlistCrossReference *xref, const db::Circuit *circuit_a, const db::Circuit *circuit_b);
void read_subcircuit_pair (db::NetlistCrossReference *xref, const db::Circuit *circuit_a, const db::Circuit *circuit_b);
std::pair<std::string, bool> read_non ();
std::pair<unsigned int, bool> read_ion ();
std::map<const db::Circuit *, LayoutToNetlistStandardReader::ObjectMap> m_map_per_circuit_a, m_map_per_circuit_b;
};
}
#endif

View File

@ -0,0 +1,241 @@
/*
KLayout Layout Viewer
Copyright (C) 2006-2019 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 "dbLayoutToNetlistWriter.h"
#include "dbLayoutVsSchematicWriter.h"
#include "dbLayoutVsSchematic.h"
#include "dbLayoutVsSchematicFormatDefs.h"
namespace db
{
// -------------------------------------------------------------------------------------------
// LayoutVsSchematicWriterBase implementation
LayoutVsSchematicWriterBase::LayoutVsSchematicWriterBase ()
{
// .. nothing yet ..
}
LayoutVsSchematicWriterBase::~LayoutVsSchematicWriterBase ()
{
// .. nothing yet ..
}
void LayoutVsSchematicWriterBase::write (const db::LayoutVsSchematic *lvs)
{
do_write_lvs (lvs);
}
// -------------------------------------------------------------------------------------------
namespace lvs_std_format
{
// -------------------------------------------------------------------------------------------
// std_writer_impl<Keys> implementation
template <class Keys>
class std_writer_impl
: public l2n_std_format::std_writer_impl<typename Keys::l2n_keys>
{
public:
std_writer_impl (tl::OutputStream &stream, double dbu, const std::string &progress_description = std::string ());
void write (const db::LayoutVsSchematic *l2n);
private:
tl::OutputStream &stream ()
{
return l2n_std_format::std_writer_impl<typename Keys::l2n_keys>::stream ();
}
std::string status_to_s (const db::NetlistCrossReference::Status status);
void write (const db::NetlistCrossReference *xref);
std::map<const db::Circuit *, std::map<const db::Net *, unsigned int> > m_net2id_per_circuit_a, m_net2id_per_circuit_b;
};
static const std::string endl ("\n");
static const std::string indent1 (" ");
static const std::string indent2 (" ");
template <class Keys>
std_writer_impl<Keys>::std_writer_impl (tl::OutputStream &stream, double dbu, const std::string &progress_description)
: l2n_std_format::std_writer_impl<typename Keys::l2n_keys> (stream, dbu, progress_description.empty () ? tl::to_string (tr ("Writing LVS database")) : progress_description)
{
// .. nothing yet ..
}
template <class Keys>
void std_writer_impl<Keys>::write (const db::LayoutVsSchematic *lvs)
{
const int version = 0;
stream () << Keys::lvs_magic_string << endl;
if (version > 0) {
stream () << Keys::version_key << "(" << version << ")" << endl;
}
if (lvs->netlist ()) {
if (! Keys::is_short ()) {
stream () << endl << "# Layout" << endl;
}
stream () << Keys::layout_key << "(" << endl;
l2n_std_format::std_writer_impl<typename Keys::l2n_keys>::write (lvs->netlist (), lvs, true, &m_net2id_per_circuit_a);
stream () << ")" << endl;
}
if (lvs->reference_netlist ()) {
if (! Keys::is_short ()) {
stream () << endl << "# Reference netlist" << endl;
}
stream () << Keys::reference_key << "(" << endl;
l2n_std_format::std_writer_impl<typename Keys::l2n_keys>::write (lvs->reference_netlist (), 0, true, &m_net2id_per_circuit_b);
stream () << ")" << endl;
}
if (lvs->cross_ref ()) {
if (! Keys::is_short ()) {
stream () << endl << "# Cross reference" << endl;
}
stream () << Keys::xref_key << "(" << endl;
write (lvs->cross_ref ());
stream () << ")" << endl;
}
}
template <class Obj>
std::string name_to_s (const Obj *obj)
{
if (obj) {
return tl::to_word_or_quoted_string (obj->name ());
} else {
return "()";
}
}
template <class Obj>
std::string ion_to_s (const Obj *obj)
{
if (obj) {
return tl::to_string (obj->id ());
} else {
return "()";
}
}
std::string net_id_to_s (const db::Net *net, const std::map<const db::Net *, unsigned int> &net2id)
{
if (net) {
std::map<const db::Net *, unsigned int>::const_iterator i = net2id.find (net);
tl_assert (i != net2id.end ());
return tl::to_string (i->second);
} else {
return "()";
}
}
template <class Keys>
std::string std_writer_impl<Keys>::status_to_s (const db::NetlistCrossReference::Status status)
{
if (status == db::NetlistCrossReference::Match) {
return " " + Keys::match_key;
} else if (status == db::NetlistCrossReference::NoMatch) {
return " " + Keys::nomatch_key;
} else if (status == db::NetlistCrossReference::Mismatch) {
return " " + Keys::mismatch_key;
} else if (status == db::NetlistCrossReference::MatchWithWarning) {
return " " + Keys::warning_key;
} else if (status == db::NetlistCrossReference::Skipped) {
return " " + Keys::skipped_key;
} else {
return std::string ();
}
}
template <class Keys>
void std_writer_impl<Keys>::write (const db::NetlistCrossReference *xref)
{
for (db::NetlistCrossReference::circuits_iterator c = xref->begin_circuits (); c != xref->end_circuits (); ++c) {
const db::NetlistCrossReference::PerCircuitData *pcd = xref->per_circuit_data_for (*c);
tl_assert (pcd != 0);
stream () << indent1 << Keys::circuit_key << "(" << name_to_s (c->first) << " " << name_to_s (c->second) << status_to_s (pcd->status) << endl;
stream () << indent2 << Keys::xref_key << "(" << endl;
for (db::NetlistCrossReference::PerCircuitData::net_pairs_const_iterator n = pcd->nets.begin (); n != pcd->nets.end (); ++n) {
stream () << indent1 << indent2 << Keys::net_key << "(" << net_id_to_s (n->pair.first, m_net2id_per_circuit_a [c->first]) << " " << net_id_to_s (n->pair.second, m_net2id_per_circuit_b [c->second]) << status_to_s (n->status) << ")" << endl;
}
for (db::NetlistCrossReference::PerCircuitData::pin_pairs_const_iterator n = pcd->pins.begin (); n != pcd->pins.end (); ++n) {
stream () << indent1 << indent2 << Keys::pin_key << "(" << ion_to_s (n->pair.first) << " " << ion_to_s (n->pair.second) << status_to_s (n->status) << ")" << endl;
}
for (db::NetlistCrossReference::PerCircuitData::device_pairs_const_iterator n = pcd->devices.begin (); n != pcd->devices.end (); ++n) {
stream () << indent1 << indent2 << Keys::device_key << "(" << ion_to_s (n->pair.first) << " " << ion_to_s (n->pair.second) << status_to_s (n->status) << ")" << endl;
}
for (db::NetlistCrossReference::PerCircuitData::subcircuit_pairs_const_iterator n = pcd->subcircuits.begin (); n != pcd->subcircuits.end (); ++n) {
stream () << indent1 << indent2 << Keys::circuit_key << "(" << ion_to_s (n->pair.first) << " " << ion_to_s (n->pair.second) << status_to_s (n->status) << ")" << endl;
}
stream () << indent2 << ")" << endl;
stream () << indent1 << ")" << endl;
}
}
}
// -------------------------------------------------------------------------------------------
// LayoutVsSchematicStandardWriter implementation
LayoutVsSchematicStandardWriter::LayoutVsSchematicStandardWriter (tl::OutputStream &stream, bool short_version)
: mp_stream (&stream), m_short_version (short_version)
{
// .. nothing yet ..
}
void LayoutVsSchematicStandardWriter::do_write_lvs (const db::LayoutVsSchematic *lvs)
{
if (! lvs->netlist ()) {
throw tl::Exception (tl::to_string (tr ("Can't write LVS DB before the netlist has been created")));
}
if (! lvs->internal_layout ()) {
throw tl::Exception (tl::to_string (tr ("Can't write LVS DB before the layout has been loaded")));
}
double dbu = lvs->internal_layout ()->dbu ();
if (m_short_version) {
lvs_std_format::std_writer_impl<lvs_std_format::keys<true> > writer (*mp_stream, dbu);
writer.write (lvs);
} else {
lvs_std_format::std_writer_impl<lvs_std_format::keys<false> > writer (*mp_stream, dbu);
writer.write (lvs);
}
}
}

View File

@ -0,0 +1,75 @@
/*
KLayout Layout Viewer
Copyright (C) 2006-2019 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_dbLayoutVsSchematicWriter
#define HDR_dbLayoutVsSchematicWriter
#include "dbCommon.h"
#include "dbLayoutToNetlistWriter.h"
#include "tlStream.h"
namespace db
{
class Circuit;
class Net;
class LayoutVsSchematic;
class NetlistCrossReference;
/**
* @brief The base class for a LayoutVsSchematic writer
*/
class DB_PUBLIC LayoutVsSchematicWriterBase
{
public:
LayoutVsSchematicWriterBase ();
virtual ~LayoutVsSchematicWriterBase ();
void write (const db::LayoutVsSchematic *lvs);
protected:
virtual void do_write_lvs (const db::LayoutVsSchematic *lvs) = 0;
private:
std::string m_filename;
};
/**
* @brief The standard writer
*/
class DB_PUBLIC LayoutVsSchematicStandardWriter
: public LayoutVsSchematicWriterBase
{
public:
LayoutVsSchematicStandardWriter (tl::OutputStream &stream, bool short_version);
protected:
void do_write_lvs (const db::LayoutVsSchematic *lvs);
private:
tl::OutputStream *mp_stream;
bool m_short_version;
};
}
#endif

View File

@ -467,6 +467,15 @@ public:
return m_cluster_id;
}
/**
* @brief Provided for API compatibility with the other objects
* This ID is not well-formed like for other objects.
*/
size_t id () const
{
return m_cluster_id;
}
/**
* @brief Adds a pin to this net
*/

View File

@ -233,6 +233,10 @@ void Netlist::validate_topology ()
throw tl::Exception (tl::to_string (tr ("Recursive hierarchy detected in netlist")));
}
// doing this reverse will mean we preserve bottom-up order. This is useful for
// netlists where subcircuits have to be defined before they are used.
std::reverse (m_top_down_circuits.begin () + n_top_down_circuits, m_top_down_circuits.end ());
}
// Determine the number of top cells
@ -369,6 +373,12 @@ void Netlist::remove_circuit (Circuit *circuit)
m_circuits.erase (circuit);
}
void Netlist::purge_circuit (Circuit *circuit)
{
circuit->blank ();
remove_circuit (circuit);
}
void Netlist::flatten_circuit (Circuit *circuit)
{
tl_assert (circuit != 0);
@ -431,7 +441,7 @@ void Netlist::make_top_level_pins ()
size_t ntop = top_circuit_count ();
for (top_down_circuit_iterator c = begin_top_down (); c != end_top_down () && ntop > 0; ++c, --ntop) {
Circuit *circuit = *c;
Circuit *circuit = c.operator-> ();
if (circuit->pin_count () == 0) {
@ -456,10 +466,10 @@ void Netlist::purge ()
for (bottom_up_circuit_iterator c = begin_bottom_up (); c != end_bottom_up (); ++c) {
Circuit *circuit = *c;
Circuit *circuit = c.operator-> ();
circuit->purge_nets ();
if (circuit->begin_nets () == circuit->end_nets ()) {
if (circuit->begin_nets () == circuit->end_nets () && ! circuit->dont_purge ()) {
// No nets left: delete the subcircuits that refer to us and finally delete the circuit
while (circuit->begin_refs () != circuit->end_refs ()) {
@ -479,6 +489,14 @@ void Netlist::combine_devices ()
}
}
void Netlist::simplify ()
{
make_top_level_pins ();
purge ();
combine_devices ();
purge_nets ();
}
static std::string net2string (const db::Net *net)
{
return net ? tl::to_word_or_quoted_string (net->expanded_name ()) : "(null)";
@ -542,7 +560,7 @@ std::string Netlist::to_string () const
if (p != pd.begin ()) {
ps += ",";
}
ps += p->name () + "=" + tl::to_string (d->parameter_value (p->id ()));
ps += p->name () + "=" + tl::sprintf ("%.12g", d->parameter_value (p->id ()));
}
res += std::string (" device ") + tl::to_word_or_quoted_string (d->device_class ()->name ()) + " " + device2string (*d) + " (" + ts + ") (" + ps + ");\n";
}
@ -551,13 +569,17 @@ std::string Netlist::to_string () const
std::string ps;
const db::SubCircuit &subcircuit = *sc;
const db::Circuit *circuit = sc->circuit_ref ();
for (db::Circuit::const_pin_iterator p = circuit->begin_pins (); p != circuit->end_pins (); ++p) {
if (p != circuit->begin_pins ()) {
ps += ",";
if (circuit) {
for (db::Circuit::const_pin_iterator p = circuit->begin_pins (); p != circuit->end_pins (); ++p) {
if (p != circuit->begin_pins ()) {
ps += ",";
}
ps += pin2string (*p) + "=" + net2string (subcircuit.net_for_pin (p->id ()));
}
ps += pin2string (*p) + "=" + net2string (subcircuit.net_for_pin (p->id ()));
res += std::string (" subcircuit ") + tl::to_word_or_quoted_string (circuit->name ()) + " " + subcircuit2string (*sc) + " (" + ps + ");\n";
} else {
res += std::string (" subcircuit (null);\n");
}
res += std::string (" subcircuit ") + tl::to_word_or_quoted_string (circuit->name ()) + " " + subcircuit2string (*sc) + " (" + ps + ");\n";
}
res += std::string ("end;\n");

View File

@ -55,10 +55,10 @@ public:
typedef tl::shared_collection<DeviceAbstract> device_abstract_list;
typedef device_abstract_list::const_iterator const_abstract_model_iterator;
typedef device_abstract_list::iterator device_abstract_iterator;
typedef tl::vector<Circuit *>::const_iterator top_down_circuit_iterator;
typedef tl::vector<const Circuit *>::const_iterator const_top_down_circuit_iterator;
typedef tl::vector<Circuit *>::const_reverse_iterator bottom_up_circuit_iterator;
typedef tl::vector<const Circuit *>::const_reverse_iterator const_bottom_up_circuit_iterator;
typedef dereferencing_iterator<tl::vector<Circuit *>::iterator, Circuit> top_down_circuit_iterator;
typedef dereferencing_iterator<tl::vector<const Circuit *>::const_iterator, const Circuit> const_top_down_circuit_iterator;
typedef dereferencing_iterator<tl::vector<Circuit *>::reverse_iterator, Circuit> bottom_up_circuit_iterator;
typedef dereferencing_iterator<tl::vector<const Circuit *>::const_reverse_iterator, const Circuit> const_bottom_up_circuit_iterator;
/**
* @brief Constructor
@ -138,6 +138,22 @@ public:
*/
void remove_circuit (Circuit *circuit);
/**
* @brief Purges a circuit from the netlist
* In contrast to "delete", this method will
* also remove subcircuits unless called
* otherwise.
*/
void purge_circuit (Circuit *circuit);
/**
* @brief Gets the number of circuits
*/
size_t circuit_count () const
{
return m_circuits.size ();
}
/**
* @brief Flattens the given circuit
* All subcircuit references are replaced by the content of this circuit.
@ -441,9 +457,17 @@ public:
*
* This method will purge all nets which return "floating". Circuits which don't have any
* nets (or only floating ones) and removed. Their subcircuits are disconnected.
*
* This method respects the circuit's "dont_purge" flag and will not purge them if this
* flag is set.
*/
void purge ();
/**
* @brief Convenience method: simplify (purge, combine devices, ...)
*/
void simplify ();
/**
* @brief Combine devices
*

File diff suppressed because it is too large Load Diff

View File

@ -99,6 +99,9 @@ public:
/**
* @brief Net a or b doesn't match
* "a" is null if there is no match for b and vice versa.
* In some cases, a mismatch is reported with two nets given. This means,
* nets are known not to match. Still the compare algorithm will proceed as
* if these nets were equivalent to derive further matches.
*/
virtual void net_mismatch (const db::Net * /*a*/, const db::Net * /*b*/) { }
@ -155,7 +158,7 @@ public:
/**
* @brief Constructor
*/
NetlistComparer (NetlistCompareLogger *logger);
NetlistComparer (NetlistCompareLogger *logger = 0);
/**
* @brief Mark two nets as identical
@ -258,12 +261,17 @@ public:
*/
bool compare (const db::Netlist *a, const db::Netlist *b) const;
/**
* @brief Actually compares the two netlists using the given logger
*/
bool compare (const db::Netlist *a, const db::Netlist *b, db::NetlistCompareLogger *logger) const;
protected:
bool compare_circuits (const db::Circuit *c1, const db::Circuit *c2, db::DeviceCategorizer &device_categorizer, db::CircuitCategorizer &circuit_categorizer, db::CircuitPinMapper &circuit_pin_mapper, const std::vector<std::pair<const Net *, const Net *> > &net_identity, bool &pin_mismatch, std::map<const db::Circuit *, CircuitMapper> &c12_circuit_and_pin_mapping, std::map<const db::Circuit *, CircuitMapper> &c22_circuit_and_pin_mapping) const;
bool all_subcircuits_verified (const db::Circuit *c, const std::set<const db::Circuit *> &verified_circuits) const;
static void derive_pin_equivalence (const db::Circuit *ca, const db::Circuit *cb, CircuitPinMapper *circuit_pin_mapper);
NetlistCompareLogger *mp_logger;
mutable NetlistCompareLogger *mp_logger;
std::map<std::pair<const db::Circuit *, const db::Circuit *>, std::vector<std::pair<const Net *, const Net *> > > m_same_nets;
std::auto_ptr<CircuitPinMapper> mp_circuit_pin_mapper;
std::auto_ptr<DeviceCategorizer> mp_device_categorizer;

View File

@ -0,0 +1,558 @@
/*
KLayout Layout Viewer
Copyright (C) 2006-2019 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 "dbNetlistCrossReference.h"
namespace db
{
NetlistCrossReference::NetlistCrossReference ()
{
// .. nothing yet ..
}
NetlistCrossReference::~NetlistCrossReference ()
{
// .. nothing yet ..
}
const NetlistCrossReference::PerCircuitData *
NetlistCrossReference::per_circuit_data_for (const std::pair<const db::Circuit *, const db::Circuit *> &circuits) const
{
std::map<const db::Circuit *, PerCircuitData *>::const_iterator i;
if (circuits.first) {
i = m_data_refs.find (circuits.first);
if (i != m_data_refs.end ()) {
return i->second;
}
}
if (circuits.second) {
i = m_data_refs.find (circuits.second);
if (i != m_data_refs.end ()) {
return i->second;
}
}
return 0;
}
const db::Circuit *
NetlistCrossReference::other_circuit_for (const db::Circuit *circuit) const
{
std::map<const db::Circuit *, const db::Circuit *>::const_iterator i = m_other_circuit.find (circuit);
if (i != m_other_circuit.end ()) {
return i->second;
} else {
return 0;
}
}
const db::Net *
NetlistCrossReference::other_net_for (const db::Net *net) const
{
std::map<const db::Net *, const db::Net *>::const_iterator i = m_other_net.find (net);
if (i != m_other_net.end ()) {
return i->second;
} else {
return 0;
}
}
const NetlistCrossReference::PerNetData *
NetlistCrossReference::per_net_data_for (const std::pair<const db::Net *, const db::Net *> &nets) const
{
std::map<std::pair<const db::Net *, const db::Net *>, PerNetData>::iterator i = m_per_net_data.find (nets);
if (i == m_per_net_data.end ()) {
i = m_per_net_data.insert (std::make_pair (nets, PerNetData ())).first;
build_per_net_info (nets, i->second);
}
return &i->second;
}
void
NetlistCrossReference::clear ()
{
mp_netlist_a.reset (0);
mp_netlist_b.reset (0);
m_circuits.clear ();
m_per_circuit_data.clear ();
m_data_refs.clear ();
m_per_net_data.clear ();
m_other_circuit.clear ();
m_other_net.clear ();
m_other_device.clear ();
m_other_pin.clear ();
m_other_subcircuit.clear ();
m_current_circuits.first = 0;
m_current_circuits.second = 0;
mp_per_circuit_data = 0;
}
void
NetlistCrossReference::gen_begin_netlist (const db::Netlist *a, const db::Netlist *b)
{
mp_netlist_a.reset (const_cast <db::Netlist *> (a));
mp_netlist_b.reset (const_cast <db::Netlist *> (b));
m_current_circuits = std::make_pair ((const db::Circuit *)0, (const db::Circuit *)0);
}
namespace {
static int string_value_compare (const std::string &a, const std::string &b)
{
return a == b ? 0 : (a < b ? -1 : 1);
}
template <class Obj>
struct by_name_value_compare
{
int operator() (const Obj &a, const Obj &b) const
{
return string_value_compare (a.name (), b.name ());
}
};
template <class Obj>
struct by_expanded_name_value_compare
{
int operator() (const Obj &a, const Obj &b) const
{
return string_value_compare (a.expanded_name (), b.expanded_name ());
}
};
template <class Obj>
struct net_object_compare;
template <>
struct net_object_compare<db::NetTerminalRef>
{
int operator() (const db::NetTerminalRef &a, const db::NetTerminalRef &b) const
{
int ct = by_expanded_name_value_compare<db::Device> () (*a.device (), *b.device ());
if (ct == 0) {
return (a.terminal_id () != b.terminal_id () ? (a.terminal_id () < b.terminal_id () ? -1 : 1) : 0);
} else {
return ct;
}
}
};
template <>
struct net_object_compare<db::NetSubcircuitPinRef>
{
int operator() (const db::NetSubcircuitPinRef &a, const db::NetSubcircuitPinRef &b) const
{
int ct = by_expanded_name_value_compare<db::SubCircuit> () (*a.subcircuit (), *b.subcircuit ());
if (ct == 0) {
return by_expanded_name_value_compare<db::Pin> () (*a.pin (), *b.pin ());
} else {
return ct;
}
}
};
template <>
struct net_object_compare<db::NetPinRef>
{
int operator() (const db::NetPinRef &a, const db::NetPinRef &b) const
{
return by_expanded_name_value_compare<db::Pin> () (*a.pin (), *b.pin ());
}
};
template <class Obj, class ValueCompare>
struct two_pointer_compare
{
int operator() (const Obj *a, const Obj *b) const
{
if ((a == 0) != (b == 0)) {
return (a == 0) > (b == 0) ? -1 : 1;
}
if (a != 0) {
return ValueCompare () (*a, *b);
} else {
return 0;
}
}
};
template <class Obj, class ValueCompare>
struct two_pair_compare
{
bool operator() (const std::pair<const Obj *, const Obj *> &a, const std::pair<const Obj *, const Obj *> &b)
{
int ct = two_pointer_compare<Obj, ValueCompare> () (a.first, b.first);
if (ct != 0) {
return ct < 0;
}
return two_pointer_compare<Obj, ValueCompare> () (a.second, b.second) < 0;
}
};
template <class PairData, class ValueCompare>
struct pair_data_compare
{
bool operator () (const PairData &a, const PairData &b) const
{
return two_pair_compare<typename PairData::object_type, ValueCompare> () (a.pair, b.pair);
}
};
struct CircuitsCompareByName
: public two_pair_compare<db::Circuit, by_name_value_compare<db::Circuit> >
{
// .. nothing yet ..
};
struct SortNetTerminals
: public two_pair_compare<db::NetTerminalRef, net_object_compare<db::NetTerminalRef> >
{
// .. nothing yet ..
};
struct SortNetPins
: public two_pair_compare<db::NetPinRef, net_object_compare<db::NetPinRef> >
{
// .. nothing yet ..
};
struct SortNetSubCircuitPins
: public two_pair_compare<db::NetSubcircuitPinRef, net_object_compare<db::NetSubcircuitPinRef> >
{
// .. nothing yet ..
};
}
void
NetlistCrossReference::gen_end_netlist (const db::Netlist *, const db::Netlist *)
{
std::sort (m_circuits.begin (), m_circuits.end (), CircuitsCompareByName ());
}
void
NetlistCrossReference::establish_pair (const db::Circuit *a, const db::Circuit *b)
{
m_circuits.push_back (std::make_pair (a, b));
m_per_circuit_data.push_back (PerCircuitData ());
mp_per_circuit_data = & m_per_circuit_data.back ();
m_data_refs [a] = mp_per_circuit_data;
m_data_refs [b] = mp_per_circuit_data;
if (a) {
m_other_circuit [a] = b;
}
if (b) {
m_other_circuit [b] = a;
}
}
void
NetlistCrossReference::establish_pair (const db::Net *a, const db::Net *b, Status status)
{
mp_per_circuit_data->nets.push_back (NetPairData (a, b, status));
if (a) {
m_other_net [a] = b;
}
if (b) {
m_other_net [b] = a;
}
}
void
NetlistCrossReference::establish_pair (const db::Device *a, const db::Device *b, Status status)
{
mp_per_circuit_data->devices.push_back (DevicePairData (a, b, status));
if (a) {
m_other_device [a] = b;
}
if (b) {
m_other_device [b] = a;
}
}
void
NetlistCrossReference::establish_pair (const db::Pin *a, const db::Pin *b, Status status)
{
mp_per_circuit_data->pins.push_back (PinPairData (a, b, status));
if (a) {
m_other_pin [a] = b;
}
if (b) {
m_other_pin [b] = a;
}
}
void
NetlistCrossReference::establish_pair (const db::SubCircuit *a, const db::SubCircuit *b, Status status)
{
mp_per_circuit_data->subcircuits.push_back (SubCircuitPairData (a, b, status));
if (a) {
m_other_subcircuit [a] = b;
}
if (b) {
m_other_subcircuit [b] = a;
}
}
void
NetlistCrossReference::gen_begin_circuit (const db::Circuit *a, const db::Circuit *b)
{
m_current_circuits = std::pair<const db::Circuit *, const db::Circuit *> (a, b);
establish_pair (a, b);
}
void
NetlistCrossReference::gen_end_circuit (const db::Circuit *, const db::Circuit *, Status status)
{
mp_per_circuit_data->status = status;
std::stable_sort (mp_per_circuit_data->devices.begin (), mp_per_circuit_data->devices.end (), pair_data_compare<DevicePairData, by_name_value_compare<db::Device> > ());
std::stable_sort (mp_per_circuit_data->pins.begin (), mp_per_circuit_data->pins.end (), pair_data_compare<PinPairData, by_name_value_compare<db::Pin> > ());
std::stable_sort (mp_per_circuit_data->subcircuits.begin (), mp_per_circuit_data->subcircuits.end (), pair_data_compare<SubCircuitPairData, by_name_value_compare<db::SubCircuit> > ());
std::stable_sort (mp_per_circuit_data->nets.begin (), mp_per_circuit_data->nets.end (), pair_data_compare<NetPairData, by_name_value_compare<db::Net> > ());
m_current_circuits = std::make_pair((const db::Circuit *)0, (const db::Circuit *)0);
mp_per_circuit_data = 0;
}
void
NetlistCrossReference::gen_nets (const db::Net *a, const db::Net *b, Status status)
{
establish_pair (a, b, status);
}
void
NetlistCrossReference::gen_devices (const db::Device *a, const db::Device *b, Status status)
{
establish_pair (a, b, status);
}
void
NetlistCrossReference::gen_pins (const db::Pin *a, const db::Pin *b, Status status)
{
establish_pair (a, b, status);
}
void
NetlistCrossReference::gen_subcircuits (const db::SubCircuit *a, const db::SubCircuit *b, Status status)
{
establish_pair (a, b, status);
}
static void init_data_from_single (const db::Net *net, NetlistCrossReference::PerNetData &data, bool first)
{
data.pins.reserve (net->pin_count ());
for (db::Net::const_pin_iterator i = net->begin_pins (); i != net->end_pins (); ++i) {
if (! first) {
data.pins.push_back (std::make_pair ((const db::NetPinRef *) 0, i.operator-> ()));
} else {
data.pins.push_back (std::make_pair (i.operator-> (), (const db::NetPinRef *) 0));
}
}
data.subcircuit_pins.reserve (net->subcircuit_pin_count ());
for (db::Net::const_subcircuit_pin_iterator i = net->begin_subcircuit_pins (); i != net->end_subcircuit_pins (); ++i) {
if (! first) {
data.subcircuit_pins.push_back (std::make_pair ((const db::NetSubcircuitPinRef *) 0, i.operator-> ()));
} else {
data.subcircuit_pins.push_back (std::make_pair (i.operator-> (), (const db::NetSubcircuitPinRef *) 0));
}
}
data.terminals.reserve (net->terminal_count ());
for (db::Net::const_terminal_iterator i = net->begin_terminals (); i != net->end_terminals (); ++i) {
if (! first) {
data.terminals.push_back (std::make_pair ((const db::NetTerminalRef *) 0, i.operator-> ()));
} else {
data.terminals.push_back (std::make_pair (i.operator-> (), (const db::NetTerminalRef *) 0));
}
}
}
void
NetlistCrossReference::build_terminal_refs (const std::pair<const db::Net *, const db::Net *> &nets, PerNetData &data) const
{
std::map<std::pair<const db::Device *, size_t>, const db::NetTerminalRef *> d2t_a, d2t_b;
for (db::Net::const_terminal_iterator i = nets.first->begin_terminals (); i != nets.first->end_terminals (); ++i) {
d2t_a.insert (std::make_pair (std::make_pair (i->device (), i->terminal_id ()), i.operator-> ()));
}
for (db::Net::const_terminal_iterator i = nets.second->begin_terminals (); i != nets.second->end_terminals (); ++i) {
d2t_b.insert (std::make_pair (std::make_pair (i->device (), i->terminal_id ()), i.operator-> ()));
}
for (std::map<std::pair<const db::Device *, size_t>, const db::NetTerminalRef *>::const_iterator a = d2t_a.begin (); a != d2t_a.end (); ++a) {
const db::Device *da = a->first.first;
const db::NetTerminalRef *pb = 0;
std::map<const db::Device *, const db::Device *>::const_iterator idb = m_other_device.find (da);
if (idb != m_other_device.end () && idb->second) {
const db::Device *db = idb->second;
// we have a device pair - now we need to match the terminals: we do so on the basis
// of normalized terminal ID's
size_t atid = da->device_class ()->normalize_terminal_id (a->first.second);
const std::vector<db::DeviceTerminalDefinition> &termdefs_b = db->device_class ()->terminal_definitions ();
for (std::vector<db::DeviceTerminalDefinition>::const_iterator t = termdefs_b.begin (); t != termdefs_b.end (); ++t) {
if (atid == db->device_class ()->normalize_terminal_id (t->id ())) {
std::map<std::pair<const db::Device *, size_t>, const db::NetTerminalRef *>::iterator b = d2t_b.find (std::make_pair (db, t->id ()));
if (b != d2t_b.end ()) {
pb = b->second;
// remove the entry so we won't find it again
d2t_b.erase (b);
break;
}
}
}
}
data.terminals.push_back (std::make_pair (a->second, pb));
}
for (std::map<std::pair<const db::Device *, size_t>, const db::NetTerminalRef *>::const_iterator b = d2t_b.begin (); b != d2t_b.end (); ++b) {
data.terminals.push_back (std::make_pair ((const db::NetTerminalRef *) 0, b->second));
}
std::stable_sort (data.terminals.begin (), data.terminals.end (), SortNetTerminals ());
}
void
NetlistCrossReference::build_pin_refs (const std::pair<const db::Net *, const db::Net *> &nets, PerNetData &data) const
{
std::map<const db::Pin *, const db::NetPinRef *> p2r_a, p2r_b;
for (db::Net::const_pin_iterator i = nets.first->begin_pins (); i != nets.first->end_pins (); ++i) {
p2r_a.insert (std::make_pair (i->pin (), i.operator-> ()));
}
for (db::Net::const_pin_iterator i = nets.second->begin_pins (); i != nets.second->end_pins (); ++i) {
p2r_b.insert (std::make_pair (i->pin (), i.operator-> ()));
}
for (std::map<const Pin *, const db::NetPinRef *>::const_iterator a = p2r_a.begin (); a != p2r_a.end (); ++a) {
const db::Pin *pa = a->first;
const db::NetPinRef *prb = 0;
std::map<const db::Pin *, const db::Pin *>::const_iterator ipb = m_other_pin.find (pa);
if (ipb != m_other_pin.end () && ipb->second) {
const db::Pin *pb = ipb->second;
std::map<const Pin *, const db::NetPinRef *>::iterator b = p2r_b.find (pb);
if (b != p2r_b.end ()) {
prb = b->second;
// remove the entry so we won't find it again
p2r_b.erase (b);
}
}
data.pins.push_back (std::make_pair (a->second, prb));
}
for (std::map<const Pin *, const db::NetPinRef *>::const_iterator b = p2r_b.begin (); b != p2r_b.end (); ++b) {
data.pins.push_back (std::make_pair ((const db::NetPinRef *) 0, b->second));
}
std::stable_sort (data.pins.begin (), data.pins.end (), SortNetPins ());
}
void
NetlistCrossReference::build_subcircuit_pin_refs (const std::pair<const db::Net *, const db::Net *> &nets, PerNetData &data) const
{
std::map<std::pair<const db::SubCircuit *, size_t>, const db::NetSubcircuitPinRef *> s2t_a, s2t_b;
for (db::Net::const_subcircuit_pin_iterator i = nets.first->begin_subcircuit_pins (); i != nets.first->end_subcircuit_pins (); ++i) {
s2t_a.insert (std::make_pair (std::make_pair (i->subcircuit (), i->pin_id ()), i.operator-> ()));
}
for (db::Net::const_subcircuit_pin_iterator i = nets.second->begin_subcircuit_pins (); i != nets.second->end_subcircuit_pins (); ++i) {
s2t_b.insert (std::make_pair (std::make_pair (i->subcircuit (), i->pin_id ()), i.operator-> ()));
}
for (std::map<std::pair<const db::SubCircuit *, size_t>, const db::NetSubcircuitPinRef *>::const_iterator a = s2t_a.begin (); a != s2t_a.end (); ++a) {
const db::SubCircuit *sa = a->first.first;
const db::NetSubcircuitPinRef *pb = 0;
std::map<const db::SubCircuit *, const db::SubCircuit *>::const_iterator isb = m_other_subcircuit.find (sa);
if (isb != m_other_subcircuit.end () && isb->second) {
const db::SubCircuit *sb = isb->second;
// we have a subcircuit pair - now we need to match the pins: we do so on the basis
// pin matching
const db::Pin *pa = sa->circuit_ref ()->pin_by_id (a->first.second);
std::map<const db::Pin *, const db::Pin *>::const_iterator ipb = m_other_pin.find (pa);
if (ipb != m_other_pin.end ()) {
std::map<std::pair<const db::SubCircuit *, size_t>, const db::NetSubcircuitPinRef *>::iterator b = s2t_b.find (std::make_pair (sb, ipb->second->id ()));
if (b != s2t_b.end ()) {
pb = b->second;
// remove the entry so we won't find it again
s2t_b.erase (b);
}
}
}
data.subcircuit_pins.push_back (std::make_pair (a->second, pb));
}
for (std::map<std::pair<const db::SubCircuit *, size_t>, const db::NetSubcircuitPinRef *>::const_iterator b = s2t_b.begin (); b != s2t_b.end (); ++b) {
data.subcircuit_pins.push_back (std::make_pair ((const db::NetSubcircuitPinRef *) 0, b->second));
}
std::stable_sort (data.subcircuit_pins.begin (), data.subcircuit_pins.end (), SortNetSubCircuitPins ());
}
void
NetlistCrossReference::build_per_net_info (const std::pair<const db::Net *, const db::Net *> &nets, PerNetData &data) const
{
if (! nets.second) {
init_data_from_single (nets.first, data, true);
} else if (! nets.first) {
init_data_from_single (nets.second, data, false);
} else if (nets.first) {
build_terminal_refs (nets, data);
build_pin_refs (nets, data);
build_subcircuit_pin_refs (nets, data);
}
}
}

View File

@ -0,0 +1,299 @@
/*
KLayout Layout Viewer
Copyright (C) 2006-2019 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_dbNetlistCrossReference
#define HDR_dbNetlistCrossReference
#include "dbCommon.h"
#include "dbNetlistCompare.h"
#include "tlObject.h"
#include <vector>
#include <map>
namespace db
{
/**
* @brief The NetlistCrossReference class
*
* This class stores the results of a netlist compare in a form which is compatible
* with the netlist cross-reference model. The intention of this class is twofold:
* persisting the results of a netlist compare and display in the netlist browser.
*/
class DB_PUBLIC NetlistCrossReference
: public db::NetlistCompareLogger, public tl::Object
{
public:
NetlistCrossReference ();
~NetlistCrossReference ();
enum Status {
None = 0,
Match, // objects are paired and match
NoMatch, // objects are paired, but don't match
Skipped, // objects are skipped
MatchWithWarning, // objects are paired and match, but with warning (i.e. ambiguous nets)
Mismatch // objects are not paired
};
struct NetPairData
{
typedef db::Net object_type;
NetPairData (const db::Net *a, const db::Net *b, Status s) : pair (a, b), status (s) { }
NetPairData () : pair ((const db::Net *)0, (const db::Net *)0), status (None) { }
std::pair<const db::Net *, const db::Net *> pair;
Status status;
};
struct DevicePairData
{
typedef db::Device object_type;
DevicePairData (const db::Device *a, const db::Device *b, Status s) : pair (a, b), status (s) { }
DevicePairData () : pair ((const db::Device *)0, (const db::Device *)0), status (None) { }
std::pair<const db::Device *, const db::Device *> pair;
Status status;
};
struct PinPairData
{
typedef db::Pin object_type;
PinPairData (const db::Pin *a, const db::Pin *b, Status s) : pair (a, b), status (s) { }
PinPairData () : pair ((const db::Pin *)0, (const db::Pin *)0), status (None) { }
std::pair<const db::Pin *, const db::Pin *> pair;
Status status;
};
struct SubCircuitPairData
{
typedef db::SubCircuit object_type;
SubCircuitPairData (const db::SubCircuit *a, const db::SubCircuit *b, Status s) : pair (a, b), status (s) { }
SubCircuitPairData () : pair ((const db::SubCircuit *)0, (const db::SubCircuit *)0), status (None) { }
std::pair<const db::SubCircuit *, const db::SubCircuit *> pair;
Status status;
};
struct PerCircuitData
{
PerCircuitData () : status (None) { }
typedef std::vector<NetPairData> net_pairs_type;
typedef net_pairs_type::const_iterator net_pairs_const_iterator;
typedef std::vector<DevicePairData> device_pairs_type;
typedef device_pairs_type::const_iterator device_pairs_const_iterator;
typedef std::vector<PinPairData> pin_pairs_type;
typedef pin_pairs_type::const_iterator pin_pairs_const_iterator;
typedef std::vector<SubCircuitPairData> subcircuit_pairs_type;
typedef subcircuit_pairs_type::const_iterator subcircuit_pairs_const_iterator;
Status status;
net_pairs_type nets;
device_pairs_type devices;
pin_pairs_type pins;
subcircuit_pairs_type subcircuits;
};
struct PerNetData
{
typedef std::vector<std::pair<const db::NetTerminalRef *, const db::NetTerminalRef *> > terminal_pairs_type;
typedef terminal_pairs_type::const_iterator terminal_pairs_const_iterator;
typedef std::vector<std::pair<const db::NetPinRef *, const db::NetPinRef *> > pin_pairs_type;
typedef pin_pairs_type::const_iterator pin_pairs_const_iterator;
typedef std::vector<std::pair<const db::NetSubcircuitPinRef *, const db::NetSubcircuitPinRef *> > subcircuit_pin_pairs_type;
typedef subcircuit_pin_pairs_type::const_iterator subcircuit_pin_pairs_const_iterator;
std::vector<std::pair<const db::NetTerminalRef *, const db::NetTerminalRef *> > terminals;
std::vector<std::pair<const db::NetPinRef *, const db::NetPinRef *> > pins;
std::vector<std::pair<const db::NetSubcircuitPinRef *, const db::NetSubcircuitPinRef *> > subcircuit_pins;
};
// Generic events - thew NetlistCompareLogger events are mapped to these
void gen_begin_netlist (const db::Netlist *a, const db::Netlist *b);
void gen_end_netlist (const db::Netlist *a, const db::Netlist *b);
void gen_begin_circuit (const db::Circuit *a, const db::Circuit *b);
void gen_end_circuit (const db::Circuit *a, const db::Circuit *b, Status status);
void gen_nets (const db::Net *a, const db::Net *b, Status status);
void gen_devices (const db::Device *a, const db::Device *b, Status status);
void gen_pins (const db::Pin *a, const db::Pin *b, Status status);
void gen_subcircuits (const db::SubCircuit *a, const db::SubCircuit *b, Status status);
// db::NetlistCompareLogger interface
virtual void begin_netlist (const db::Netlist *a, const db::Netlist *b)
{
gen_begin_netlist (a, b);
}
virtual void end_netlist (const db::Netlist *a, const db::Netlist *b)
{
gen_end_netlist (a, b);
}
virtual void begin_circuit (const db::Circuit *a, const db::Circuit *b)
{
gen_begin_circuit (a, b);
}
virtual void end_circuit (const db::Circuit *a, const db::Circuit *b, bool matching)
{
gen_end_circuit (a, b, matching ? Match : NoMatch);
}
virtual void circuit_skipped (const db::Circuit *a, const db::Circuit *b)
{
gen_begin_circuit (a, b);
gen_end_circuit (a, b, Skipped);
}
virtual void circuit_mismatch (const db::Circuit *a, const db::Circuit *b)
{
gen_begin_circuit (a, b);
gen_end_circuit (a, b, Mismatch);
}
virtual void match_nets (const db::Net *a, const db::Net *b)
{
gen_nets (a, b, Match);
}
virtual void match_ambiguous_nets (const db::Net *a, const db::Net *b)
{
gen_nets (a, b, MatchWithWarning);
}
virtual void net_mismatch (const db::Net *a, const db::Net *b)
{
gen_nets (a, b, Mismatch);
}
virtual void match_devices (const db::Device *a, const db::Device *b)
{
gen_devices (a, b, Match);
}
virtual void match_devices_with_different_parameters (const db::Device *a, const db::Device *b)
{
gen_devices (a, b, MatchWithWarning);
}
virtual void match_devices_with_different_device_classes (const db::Device *a, const db::Device *b)
{
gen_devices (a, b, MatchWithWarning);
}
virtual void device_mismatch (const db::Device *a, const db::Device *b)
{
gen_devices (a, b, Mismatch);
}
virtual void match_pins (const db::Pin *a, const db::Pin *b)
{
gen_pins (a, b, Match);
}
virtual void pin_mismatch (const db::Pin *a, const db::Pin *b)
{
gen_pins (a, b, Mismatch);
}
virtual void match_subcircuits (const db::SubCircuit *a, const db::SubCircuit *b)
{
gen_subcircuits (a, b, Match);
}
virtual void subcircuit_mismatch (const db::SubCircuit *a, const db::SubCircuit *b)
{
gen_subcircuits (a, b, Mismatch);
}
void clear ();
size_t circuit_count () const
{
return m_circuits.size ();
}
const PerCircuitData *per_circuit_data_for (const std::pair<const db::Circuit *, const db::Circuit *> &circuits) const;
typedef std::vector<std::pair<const db::Circuit *, const db::Circuit *> >::const_iterator circuits_iterator;
circuits_iterator begin_circuits () const
{
return m_circuits.begin ();
}
circuits_iterator end_circuits () const
{
return m_circuits.end ();
}
const db::Circuit *other_circuit_for (const db::Circuit *circuit) const;
const db::Net *other_net_for (const db::Net *net) const;
const PerNetData *per_net_data_for (const std::pair<const db::Net *, const db::Net *> &nets) const;
const db::Netlist *netlist_a () const
{
return mp_netlist_a.get ();
}
const db::Netlist *netlist_b () const
{
return mp_netlist_b.get ();
}
private:
tl::weak_ptr<db::Netlist> mp_netlist_a, mp_netlist_b;
std::vector<std::pair<const db::Circuit *, const db::Circuit *> > m_circuits;
std::list<PerCircuitData> m_per_circuit_data;
std::map<const db::Circuit *, PerCircuitData *> m_data_refs;
mutable std::map<std::pair<const db::Net *, const db::Net *>, PerNetData> m_per_net_data;
std::map<const db::Circuit *, const db::Circuit *> m_other_circuit;
std::map<const db::Net *, const db::Net *> m_other_net;
std::map<const db::Device *, const db::Device *> m_other_device;
std::map<const db::Pin *, const db::Pin *> m_other_pin;
std::map<const db::SubCircuit *, const db::SubCircuit *> m_other_subcircuit;
std::pair<const db::Circuit *, const db::Circuit *> m_current_circuits;
PerCircuitData *mp_per_circuit_data;
void establish_pair (const db::Circuit *a, const db::Circuit *b);
void establish_pair (const db::Net *a, const db::Net *b, Status status);
void establish_pair (const db::Device *a, const db::Device *b, Status status);
void establish_pair (const db::Pin *a, const db::Pin *b, Status status);
void establish_pair (const db::SubCircuit *a, const db::SubCircuit *b, Status status);
void build_per_net_info (const std::pair<const db::Net *, const db::Net *> &nets, PerNetData &data) const;
void build_subcircuit_pin_refs (const std::pair<const db::Net *, const db::Net *> &nets, PerNetData &data) const;
void build_pin_refs (const std::pair<const db::Net *, const db::Net *> &nets, PerNetData &data) const;
void build_terminal_refs (const std::pair<const db::Net *, const db::Net *> &nets, PerNetData &data) const;
};
}
#endif

View File

@ -21,10 +21,21 @@
*/
#include "dbNetlistDeviceClasses.h"
#include "tlClassRegistry.h"
namespace db
{
// ------------------------------------------------------------------------------------
// The built-in device class templates
static tl::RegisteredClass<db::DeviceClassTemplateBase> dct_cap (new db::device_class_template<db::DeviceClassCapacitor> ("CAP"));
static tl::RegisteredClass<db::DeviceClassTemplateBase> dct_res (new db::device_class_template<db::DeviceClassResistor> ("RES"));
static tl::RegisteredClass<db::DeviceClassTemplateBase> dct_ind (new db::device_class_template<db::DeviceClassInductor> ("IND"));
static tl::RegisteredClass<db::DeviceClassTemplateBase> dct_diode (new db::device_class_template<db::DeviceClassDiode> ("DIODE"));
static tl::RegisteredClass<db::DeviceClassTemplateBase> dct_mos3 (new db::device_class_template<db::DeviceClassMOS3Transistor> ("MOS3"));
static tl::RegisteredClass<db::DeviceClassTemplateBase> dct_mos4 (new db::device_class_template<db::DeviceClassMOS4Transistor> ("MOS4"));
// ------------------------------------------------------------------------------------
// DeviceClassTwoTerminalDevice implementation
@ -35,30 +46,46 @@ bool DeviceClassTwoTerminalDevice::combine_devices (Device *a, Device *b) const
db::Net *nb1 = b->net_for_terminal (0);
db::Net *nb2 = b->net_for_terminal (1);
bool res = true;
if ((na1 == nb1 && na2 == nb2) || (na1 == nb2 && na2 == nb1)) {
parallel (a, b);
if (na1 == nb1 && na2 == nb2) {
a->join_terminals (0, b, 0);
a->join_terminals (1, b, 1);
} else {
a->join_terminals (0, b, 1);
a->join_terminals (1, b, 0);
}
return true;
} else if ((na2 == nb1 || na2 == nb2) && na2->is_internal ()) {
// serial a(B) to b(A or B)
serial (a, b);
a->connect_terminal (1, (na2 == nb1 ? nb2 : nb1));
if (na2 == nb1) {
a->reroute_terminal (1, b, 0, 1);
} else {
a->reroute_terminal (1, b, 1, 0);
}
return true;
} else if ((na1 == nb1 || na1 == nb2) && na1->is_internal ()) {
// serial a(A) to b(A or B)
serial (a, b);
a->connect_terminal (0, (na1 == nb1 ? nb2 : nb1));
}
if (na1 == nb1) {
a->reroute_terminal (0, b, 0, 1);
} else {
a->reroute_terminal (0, b, 1, 0);
}
if (res) {
b->connect_terminal (0, 0);
b->connect_terminal (1, 0);
return true;
} else {
return false;
}
@ -69,6 +96,10 @@ bool DeviceClassTwoTerminalDevice::combine_devices (Device *a, Device *b) const
// DeviceClassResistor implementation
DB_PUBLIC size_t DeviceClassResistor::param_id_R = 0;
DB_PUBLIC size_t DeviceClassResistor::param_id_L = 1;
DB_PUBLIC size_t DeviceClassResistor::param_id_W = 2;
DB_PUBLIC size_t DeviceClassResistor::param_id_A = 3;
DB_PUBLIC size_t DeviceClassResistor::param_id_P = 4;
DB_PUBLIC size_t DeviceClassResistor::terminal_id_A = 0;
DB_PUBLIC size_t DeviceClassResistor::terminal_id_B = 1;
@ -79,6 +110,10 @@ DeviceClassResistor::DeviceClassResistor ()
add_terminal_definition (db::DeviceTerminalDefinition ("B", "Terminal B"));
add_parameter_definition (db::DeviceParameterDefinition ("R", "Resistance (Ohm)", 0.0));
add_parameter_definition (db::DeviceParameterDefinition ("L", "Length (micrometer)", 0.0, false, 1e-6));
add_parameter_definition (db::DeviceParameterDefinition ("W", "Width (micrometer)", 0.0, false, 1e-6));
add_parameter_definition (db::DeviceParameterDefinition ("A", "Area (square micrometer)", 0.0, false, 1e-12));
add_parameter_definition (db::DeviceParameterDefinition ("P", "Perimeter (micrometer)", 0.0, false, 1e-6));
}
void DeviceClassResistor::parallel (Device *a, Device *b) const
@ -86,6 +121,32 @@ void DeviceClassResistor::parallel (Device *a, Device *b) const
double va = a->parameter_value (0);
double vb = b->parameter_value (0);
a->set_parameter_value (0, va + vb < 1e-10 ? 0.0 : va * vb / (va + vb));
// parallel width is sum of both, length is the one that gives the same value of resistance
// R = 1/(1/R1 + 1/R2)
// R = L/(W1+W2)
// R1 = L1/W1
// R2 = L2/W2
// -> L = (L1*L2*(W1+W2))/(L2*W1+L1*W2))
double l1 = a->parameter_value (1);
double w1 = a->parameter_value (2);
double l2 = b->parameter_value (1);
double w2 = b->parameter_value (2);
double dnom = (l2 * w1 + l1 * w2);
if (fabs (dnom) > 1e-15) {
a->set_parameter_value (1, (l1 * l2 * (w1 + w2)) / dnom);
}
a->set_parameter_value (2, w1 + w2);
// TODO: does this implementation make sense? (area)
double aa = a->parameter_value (3);
double ab = b->parameter_value (3);
a->set_parameter_value (3, aa + ab);
// TODO: does this implementation make sense? (perimeter)
double pa = a->parameter_value (4);
double pb = b->parameter_value (4);
a->set_parameter_value (4, pa + pb);
}
void DeviceClassResistor::serial (Device *a, Device *b) const
@ -93,12 +154,63 @@ void DeviceClassResistor::serial (Device *a, Device *b) const
double va = a->parameter_value (0);
double vb = b->parameter_value (0);
a->set_parameter_value (0, va + vb);
// parallel length is sum of both, width is the one that gives the same value of resistance
// assuming same sheet rho
// R = R1+R2
// R = (L1+L2)/W
// R1 = L1/W1
// R2 = L2/W2
// -> W = ((L1+L2)*W1*W2)/(W1*L2+W2*L1)
double l1 = a->parameter_value (1);
double w1 = a->parameter_value (2);
double l2 = b->parameter_value (1);
double w2 = b->parameter_value (2);
a->set_parameter_value (1, l1 + l2);
double dnom = (l2 * w1 + l1 * w2);
if (fabs (dnom) > 1e-15) {
a->set_parameter_value (2, (w1 * w2 * (l1 + l2)) / dnom);
}
double aa = a->parameter_value (3);
double ab = b->parameter_value (3);
a->set_parameter_value (3, aa + ab);
double pa = a->parameter_value (4);
double pb = b->parameter_value (4);
a->set_parameter_value (4, pa + pb);
}
// ------------------------------------------------------------------------------------
// DeviceClassResistorWithBulk implementation
DB_PUBLIC size_t DeviceClassResistorWithBulk::terminal_id_W = 2;
DeviceClassResistorWithBulk::DeviceClassResistorWithBulk ()
: DeviceClassResistor ()
{
add_terminal_definition (db::DeviceTerminalDefinition ("W", "Terminal W (well, bulk)"));
}
bool DeviceClassResistorWithBulk::combine_devices (Device *a, Device *b) const
{
db::Net *nab = a->net_for_terminal (2);
db::Net *nbb = b->net_for_terminal (2);
if (nab == nbb && DeviceClassResistor::combine_devices (a, b)) {
a->join_terminals (2, b, 2);
return true;
} else {
return false;
}
}
// ------------------------------------------------------------------------------------
// DeviceClassCapacitor implementation
DB_PUBLIC size_t DeviceClassCapacitor::param_id_C = 0;
DB_PUBLIC size_t DeviceClassCapacitor::param_id_A = 1;
DB_PUBLIC size_t DeviceClassCapacitor::param_id_P = 2;
DB_PUBLIC size_t DeviceClassCapacitor::terminal_id_A = 0;
DB_PUBLIC size_t DeviceClassCapacitor::terminal_id_B = 1;
@ -109,6 +221,8 @@ DeviceClassCapacitor::DeviceClassCapacitor ()
add_terminal_definition (db::DeviceTerminalDefinition ("B", "Terminal B"));
add_parameter_definition (db::DeviceParameterDefinition ("C", "Capacitance (Farad)", 0.0));
add_parameter_definition (db::DeviceParameterDefinition ("A", "Area (square micrometer)", 0.0, false, 1e-12));
add_parameter_definition (db::DeviceParameterDefinition ("P", "Perimeter (micrometer)", 0.0, false, 1e-6));
}
void DeviceClassCapacitor::serial (Device *a, Device *b) const
@ -116,6 +230,16 @@ void DeviceClassCapacitor::serial (Device *a, Device *b) const
double va = a->parameter_value (0);
double vb = b->parameter_value (0);
a->set_parameter_value (0, va + vb < 1e-10 ? 0.0 : va * vb / (va + vb));
// TODO: does this implementation make sense?
double aa = a->parameter_value (1);
double ab = b->parameter_value (1);
a->set_parameter_value (1, aa + ab);
// TODO: does this implementation make sense?
double pa = a->parameter_value (2);
double pb = b->parameter_value (2);
a->set_parameter_value (2, pa + pb);
}
void DeviceClassCapacitor::parallel (Device *a, Device *b) const
@ -123,6 +247,38 @@ void DeviceClassCapacitor::parallel (Device *a, Device *b) const
double va = a->parameter_value (0);
double vb = b->parameter_value (0);
a->set_parameter_value (0, va + vb);
double aa = a->parameter_value (1);
double ab = b->parameter_value (1);
a->set_parameter_value (1, aa + ab);
double pa = a->parameter_value (2);
double pb = b->parameter_value (2);
a->set_parameter_value (2, pa + pb);
}
// ------------------------------------------------------------------------------------
// DeviceClassCapacitorWithBulk implementation
DB_PUBLIC size_t DeviceClassCapacitorWithBulk::terminal_id_W = 2;
DeviceClassCapacitorWithBulk::DeviceClassCapacitorWithBulk ()
: DeviceClassCapacitor ()
{
add_terminal_definition (db::DeviceTerminalDefinition ("W", "Terminal W (well, bulk)"));
}
bool DeviceClassCapacitorWithBulk::combine_devices (Device *a, Device *b) const
{
db::Net *nab = a->net_for_terminal (2);
db::Net *nbb = b->net_for_terminal (2);
if (nab == nbb && DeviceClassCapacitor::combine_devices (a, b)) {
a->join_terminals (2, b, 2);
return true;
} else {
return false;
}
}
// ------------------------------------------------------------------------------------
@ -159,6 +315,7 @@ void DeviceClassInductor::serial (Device *a, Device *b) const
// DeviceClassInductor implementation
DB_PUBLIC size_t DeviceClassDiode::param_id_A = 0;
DB_PUBLIC size_t DeviceClassDiode::param_id_P = 1;
DB_PUBLIC size_t DeviceClassDiode::terminal_id_A = 0;
DB_PUBLIC size_t DeviceClassDiode::terminal_id_C = 1;
@ -168,7 +325,8 @@ DeviceClassDiode::DeviceClassDiode ()
add_terminal_definition (db::DeviceTerminalDefinition ("A", "Anode"));
add_terminal_definition (db::DeviceTerminalDefinition ("C", "Cathode"));
add_parameter_definition (db::DeviceParameterDefinition ("A", "Area (square micrometer)", 0.0));
add_parameter_definition (db::DeviceParameterDefinition ("A", "Area (square micrometer)", 0.0, false, 1e-12));
add_parameter_definition (db::DeviceParameterDefinition ("P", "Perimeter (micrometer)", 0.0, false, 1e-6));
}
bool DeviceClassDiode::combine_devices (Device *a, Device *b) const
@ -182,8 +340,10 @@ bool DeviceClassDiode::combine_devices (Device *a, Device *b) const
if (na1 == nb1 && na2 == nb2) {
a->set_parameter_value (0, a->parameter_value (0) + b->parameter_value (0));
b->connect_terminal (0, 0);
b->connect_terminal (1, 0);
a->set_parameter_value (1, a->parameter_value (1) + b->parameter_value (1));
a->join_terminals (0, b, 0);
a->join_terminals (1, b, 1);
return true;
@ -212,12 +372,12 @@ DeviceClassMOS3Transistor::DeviceClassMOS3Transistor ()
add_terminal_definition (db::DeviceTerminalDefinition ("G", "Gate"));
add_terminal_definition (db::DeviceTerminalDefinition ("D", "Drain"));
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, 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));
add_parameter_definition (db::DeviceParameterDefinition ("L", "Gate length (micrometer)", 0.0, true, 1e-6));
add_parameter_definition (db::DeviceParameterDefinition ("W", "Gate width (micrometer)", 0.0, true, 1e-6));
add_parameter_definition (db::DeviceParameterDefinition ("AS", "Source area (square micrometer)", 0.0, false, 1e-12));
add_parameter_definition (db::DeviceParameterDefinition ("AD", "Drain area (square micrometer)", 0.0, false, 1e-12));
add_parameter_definition (db::DeviceParameterDefinition ("PS", "Source perimeter (micrometer)", 0.0, false, 1e-6));
add_parameter_definition (db::DeviceParameterDefinition ("PD", "Drain perimeter (micrometer)", 0.0, false, 1e-6));
}
bool DeviceClassMOS3Transistor::combine_devices (Device *a, Device *b) const
@ -237,9 +397,15 @@ bool DeviceClassMOS3Transistor::combine_devices (Device *a, Device *b) const
combine_parameters (a, b);
b->connect_terminal (0, 0);
b->connect_terminal (1, 0);
b->connect_terminal (2, 0);
if (nas == nbs && nad == nbd) {
a->join_terminals (0, b, 0);
a->join_terminals (2, b, 2);
} else {
a->join_terminals (0, b, 2);
a->join_terminals (2, b, 0);
}
a->join_terminals (1, b, 1);
return true;
@ -288,10 +454,16 @@ bool DeviceClassMOS4Transistor::combine_devices (Device *a, Device *b) const
combine_parameters (a, b);
b->connect_terminal (0, 0);
b->connect_terminal (1, 0);
b->connect_terminal (2, 0);
b->connect_terminal (3, 0);
if (nas == nbs && nad == nbd) {
a->join_terminals (0, b, 0);
a->join_terminals (2, b, 2);
} else {
a->join_terminals (0, b, 2);
a->join_terminals (2, b, 0);
}
a->join_terminals (1, b, 1);
a->join_terminals (3, b, 3);
return true;
@ -302,4 +474,105 @@ bool DeviceClassMOS4Transistor::combine_devices (Device *a, Device *b) const
return false;
}
// ------------------------------------------------------------------------------------
// DeviceClassBJT3Transistor implementation
DB_PUBLIC size_t DeviceClassBJT3Transistor::param_id_AE = 0;
DB_PUBLIC size_t DeviceClassBJT3Transistor::param_id_PE = 1;
DB_PUBLIC size_t DeviceClassBJT3Transistor::param_id_AB = 2;
DB_PUBLIC size_t DeviceClassBJT3Transistor::param_id_PB = 3;
DB_PUBLIC size_t DeviceClassBJT3Transistor::param_id_AC = 4;
DB_PUBLIC size_t DeviceClassBJT3Transistor::param_id_PC = 5;
DB_PUBLIC size_t DeviceClassBJT3Transistor::param_id_NE = 6;
DB_PUBLIC size_t DeviceClassBJT3Transistor::terminal_id_C = 0;
DB_PUBLIC size_t DeviceClassBJT3Transistor::terminal_id_B = 1;
DB_PUBLIC size_t DeviceClassBJT3Transistor::terminal_id_E = 2;
DeviceClassBJT3Transistor::DeviceClassBJT3Transistor ()
{
add_terminal_definition (db::DeviceTerminalDefinition ("C", "Collector"));
add_terminal_definition (db::DeviceTerminalDefinition ("B", "Base"));
add_terminal_definition (db::DeviceTerminalDefinition ("E", "Emitter"));
// NOTE: the emitter area and the emitter count are the primary parameters
add_parameter_definition (db::DeviceParameterDefinition ("AE", "Emitter area (square micrometer)", 0.0, true, 1e-12));
add_parameter_definition (db::DeviceParameterDefinition ("PE", "Emitter perimeter (micrometer)", 0.0, false, 1e-6));
add_parameter_definition (db::DeviceParameterDefinition ("AB", "Base area (square micrometer)", 0.0, false, 1e-12));
add_parameter_definition (db::DeviceParameterDefinition ("PB", "Base perimeter (micrometer)", 0.0, false, 1e-6));
add_parameter_definition (db::DeviceParameterDefinition ("AC", "Collector area (square micrometer)", 0.0, false, 1e-12));
add_parameter_definition (db::DeviceParameterDefinition ("PC", "Collector perimeter (micrometer)", 0.0, false, 1e-6));
add_parameter_definition (db::DeviceParameterDefinition ("NE", "Emitter count", 1.0, true));
}
bool DeviceClassBJT3Transistor::combine_devices (Device *a, Device *b) const
{
const db::Net *nac = a->net_for_terminal (0);
const db::Net *nab = a->net_for_terminal (1);
const db::Net *nae = a->net_for_terminal (2);
const db::Net *nbc = b->net_for_terminal (0);
const db::Net *nbb = b->net_for_terminal (1);
const db::Net *nbe = b->net_for_terminal (2);
// parallel transistors can be combined into one
if (nac == nbc && nae == nbe && nab == nbb) {
combine_parameters (a, b);
a->join_terminals (0, b, 0);
a->join_terminals (1, b, 1);
a->join_terminals (2, b, 2);
return true;
}
return false;
}
void DeviceClassBJT3Transistor::combine_parameters (Device *a, Device *b) const
{
a->set_parameter_value (param_id_AE, a->parameter_value (param_id_AE) + b->parameter_value (param_id_AE));
a->set_parameter_value (param_id_PE, a->parameter_value (param_id_PE) + b->parameter_value (param_id_PE));
a->set_parameter_value (param_id_NE, a->parameter_value (param_id_NE) + b->parameter_value (param_id_NE));
}
// ------------------------------------------------------------------------------------
// DeviceClassBJT4Transistor implementation
DB_PUBLIC size_t DeviceClassBJT4Transistor::terminal_id_S = 3;
DeviceClassBJT4Transistor::DeviceClassBJT4Transistor ()
{
add_terminal_definition (db::DeviceTerminalDefinition ("S", "Substrate"));
}
bool DeviceClassBJT4Transistor::combine_devices (Device *a, Device *b) const
{
const db::Net *nac = a->net_for_terminal (0);
const db::Net *nab = a->net_for_terminal (1);
const db::Net *nae = a->net_for_terminal (2);
const db::Net *nas = a->net_for_terminal (3);
const db::Net *nbc = b->net_for_terminal (0);
const db::Net *nbb = b->net_for_terminal (1);
const db::Net *nbe = b->net_for_terminal (2);
const db::Net *nbs = b->net_for_terminal (3);
// parallel transistors can be combined into one
if (nac == nbc && nae == nbe && nab == nbb && nas == nbs) {
combine_parameters (a, b);
a->join_terminals (0, b, 0);
a->join_terminals (1, b, 1);
a->join_terminals (2, b, 2);
a->join_terminals (3, b, 3);
return true;
}
return false;
}
}

View File

@ -61,6 +61,10 @@ public:
}
static size_t param_id_R;
static size_t param_id_L;
static size_t param_id_W;
static size_t param_id_A;
static size_t param_id_P;
static size_t terminal_id_A;
static size_t terminal_id_B;
@ -74,6 +78,27 @@ public:
}
};
/**
* @brief A resistor device class with a bulk terminal (well, bulk)
* In addition to DeviceClassResistor, this class defines a third terminal
* ("W") for the bulk or well connection.
*/
class DB_PUBLIC DeviceClassResistorWithBulk
: public db::DeviceClassResistor
{
public:
DeviceClassResistorWithBulk ();
virtual db::DeviceClass *clone () const
{
return new DeviceClassResistorWithBulk (*this);
}
static size_t terminal_id_W;
virtual bool combine_devices (Device *a, Device *b) const;
};
/**
* @brief A basic capacitor device class
* A capacitor defines a single parameter, "C", which is the capacitance in Farad.
@ -91,6 +116,8 @@ public:
}
static size_t param_id_C;
static size_t param_id_A;
static size_t param_id_P;
static size_t terminal_id_A;
static size_t terminal_id_B;
@ -98,12 +125,33 @@ public:
virtual void parallel (Device *a, Device *b) const;
virtual void serial (Device *a, Device *b) const;
virtual size_t normalize_terminal_id (size_t) const
virtual size_t normalize_terminal_id (size_t id) const
{
return terminal_id_A;
return id == terminal_id_B ? terminal_id_A : id;
}
};
/**
* @brief A capacitor device class with a bulk terminal (well, bulk)
* In addition to DeviceClassCapacitor, this class defines a third terminal
* ("W") for the bulk or well connection.
*/
class DB_PUBLIC DeviceClassCapacitorWithBulk
: public db::DeviceClassCapacitor
{
public:
DeviceClassCapacitorWithBulk ();
virtual db::DeviceClass *clone () const
{
return new DeviceClassCapacitorWithBulk (*this);
}
static size_t terminal_id_W;
virtual bool combine_devices (Device *a, Device *b) const;
};
/**
* @brief A basic inductor device class
* An inductor defines a single parameter, "L", which is the inductance in Henry.
@ -128,9 +176,9 @@ public:
virtual void parallel (Device *a, Device *b) const;
virtual void serial (Device *a, Device *b) const;
virtual size_t normalize_terminal_id (size_t) const
virtual size_t normalize_terminal_id (size_t id) const
{
return terminal_id_A;
return id == terminal_id_B ? terminal_id_A : id;
}
};
@ -147,6 +195,7 @@ public:
DeviceClassDiode ();
static size_t param_id_A;
static size_t param_id_P;
static size_t terminal_id_A;
static size_t terminal_id_C;
@ -226,6 +275,63 @@ public:
virtual bool combine_devices (Device *a, Device *b) const;
};
/**
* @brief A bipolar transistor device class with three terminals
* A MOSFET defines two parameters: "AE" for the emitter area in square micrometers and "PE" for the emitter perimeter
* in micrometers.
* The bipolar transistor defines three terminals, "C", "B" and "E" for collector, base and emitter.
*/
class DB_PUBLIC DeviceClassBJT3Transistor
: public db::DeviceClass
{
public:
DeviceClassBJT3Transistor ();
static size_t param_id_AE;
static size_t param_id_PE;
static size_t param_id_AB;
static size_t param_id_PB;
static size_t param_id_AC;
static size_t param_id_PC;
static size_t param_id_NE;
static size_t terminal_id_C;
static size_t terminal_id_B;
static size_t terminal_id_E;
virtual db::DeviceClass *clone () const
{
return new DeviceClassBJT3Transistor (*this);
}
virtual bool combine_devices (Device *a, Device *b) const;
virtual bool supports_parallel_combination () const { return true; }
protected:
void combine_parameters (Device *a, Device *b) const;
};
/**
* @brief A basic bipolar transistor class with four terminals
* The four-terminal BJT behaves identical to the three-terminal one but adds one more
* terminal for the substrate.
*/
class DB_PUBLIC DeviceClassBJT4Transistor
: public DeviceClassBJT3Transistor
{
public:
DeviceClassBJT4Transistor ();
static size_t terminal_id_S;
virtual db::DeviceClass *clone () const
{
return new DeviceClassBJT4Transistor (*this);
}
virtual bool combine_devices (Device *a, Device *b) const;
};
}
#endif

View File

@ -132,9 +132,31 @@ void NetlistDeviceExtractor::extract (db::DeepShapeStore &dss, unsigned int layo
for (layer_definitions::const_iterator ld = begin_layer_definitions (); ld != end_layer_definitions (); ++ld) {
input_layers::const_iterator l = layer_map.find (ld->name);
size_t ld_index = ld->index;
input_layers::const_iterator l = layer_map.find (m_layer_definitions [ld_index].name);
while (l == layer_map.end () && m_layer_definitions [ld_index].fallback_index < m_layer_definitions.size ()) {
// try fallback layer
ld_index = m_layer_definitions [ld_index].fallback_index;
l = layer_map.find (m_layer_definitions [ld_index].name);
}
if (l == layer_map.end ()) {
throw tl::Exception (tl::to_string (tr ("Missing input layer for device extraction: ")) + ld->name);
// gets the layer names for the error message
std::string layer_names = m_layer_definitions [ld_index].name;
ld_index = ld->index;
l = layer_map.find (m_layer_definitions [ld_index].name);
while (l == layer_map.end () && m_layer_definitions [ld_index].fallback_index < m_layer_definitions.size ()) {
ld_index = m_layer_definitions [ld_index].fallback_index;
std::string ln = m_layer_definitions [ld_index].name;
layer_names += "/";
layer_names += ln;
l = layer_map.find (ln);
}
// TODO: maybe use empty layers for optional ones?
throw tl::Exception (tl::sprintf (tl::to_string (tr ("Missing input layer for device extraction (device %s): %s")), name (), layer_names));
}
tl_assert (l->second != 0);
@ -145,9 +167,6 @@ void NetlistDeviceExtractor::extract (db::DeepShapeStore &dss, unsigned int layo
if (alias.first) {
// use deep layer alias for a given flat one (if found)
layers.push_back (alias.second.layer ());
} else if (l->second->empty ()) {
// provide a substitute empty layer
layers.push_back (dss.empty_layer (layout_index).layer ());
} else {
throw tl::Exception (tl::sprintf (tl::to_string (tr ("Invalid region passed to input layer '%s' for device extraction (device %s): must be of deep region kind")), ld->name, name ()));
}
@ -260,9 +279,7 @@ void NetlistDeviceExtractor::extract_without_initialize (db::Layout &layout, db:
} else {
// create a new circuit for this cell
mp_circuit = new db::Circuit ();
mp_circuit->set_cell_index (*ci);
mp_circuit->set_name (layout.cell_name (*ci));
mp_circuit = new db::Circuit (layout, *ci);
m_netlist->add_circuit (mp_circuit);
}
@ -339,8 +356,8 @@ void NetlistDeviceExtractor::push_new_devices (const db::Vector &disp_cache)
db::Device *device = d->second.first;
db::Vector disp = dbu_inv * device->position () - db::Point ();
device->set_position (device->position () + dbu * disp_cache);
db::Vector disp = dbu_inv * device->trans ().disp ();
device->set_trans (db::DCplxTrans (device->trans ().disp () + dbu * disp_cache));
DeviceCellKey key;
@ -426,11 +443,11 @@ void NetlistDeviceExtractor::push_cached_devices (const tl::vector<db::Device *>
for (std::vector<db::Device *>::const_iterator d = cached_devices.begin (); d != cached_devices.end (); ++d) {
db::Device *cached_device = *d;
db::Vector disp = dbu_inv * cached_device->position () - disp_cache - db::Point ();
db::Vector disp = dbu_inv * cached_device->trans ().disp () - disp_cache;
db::Device *device = new db::Device (*cached_device);
mp_circuit->add_device (device);
device->set_position (device->position () + dbu * (new_disp - disp_cache));
device->set_trans (db::DCplxTrans (device->trans ().disp () + dbu * (new_disp - disp_cache)));
// Build a property set for the device ID
ps.clear ();
@ -476,9 +493,16 @@ void NetlistDeviceExtractor::register_device_class (DeviceClass *device_class)
m_netlist->add_device_class (device_class);
}
void NetlistDeviceExtractor::define_layer (const std::string &name, const std::string &description)
const db::NetlistDeviceExtractorLayerDefinition &NetlistDeviceExtractor::define_layer (const std::string &name, const std::string &description)
{
m_layer_definitions.push_back (db::NetlistDeviceExtractorLayerDefinition (name, description, m_layer_definitions.size ()));
m_layer_definitions.push_back (db::NetlistDeviceExtractorLayerDefinition (name, description, m_layer_definitions.size (), std::numeric_limits<size_t>::max ()));
return m_layer_definitions.back ();
}
const db::NetlistDeviceExtractorLayerDefinition &NetlistDeviceExtractor::define_layer (const std::string &name, size_t fallback, const std::string &description)
{
m_layer_definitions.push_back (db::NetlistDeviceExtractorLayerDefinition (name, description, m_layer_definitions.size (), fallback));
return m_layer_definitions.back ();
}
Device *NetlistDeviceExtractor::create_device ()
@ -493,6 +517,21 @@ Device *NetlistDeviceExtractor::create_device ()
return device;
}
void NetlistDeviceExtractor::define_terminal (Device *device, size_t terminal_id, size_t geometry_index, const db::Region &region)
{
tl_assert (mp_layout != 0);
tl_assert (geometry_index < m_layers.size ());
unsigned int layer_index = m_layers [geometry_index];
std::pair<db::Device *, geometry_per_terminal_type> &dd = m_new_devices[device->id ()];
dd.first = device;
std::vector<db::PolygonRef> &geo = dd.second[terminal_id][layer_index];
for (db::Region::const_iterator p = region.begin_merged (); !p.at_end (); ++p) {
geo.push_back (db::PolygonRef (*p, mp_layout->shape_repository ()));
}
}
void NetlistDeviceExtractor::define_terminal (Device *device, size_t terminal_id, size_t geometry_index, const db::Polygon &polygon)
{
tl_assert (mp_layout != 0);

View File

@ -162,8 +162,8 @@ public:
// .. nothing yet ..
}
NetlistDeviceExtractorLayerDefinition (const std::string &_name, const std::string &_description, size_t _index)
: name (_name), description (_description), index (_index)
NetlistDeviceExtractorLayerDefinition (const std::string &_name, const std::string &_description, size_t _index, size_t _fallback_index)
: name (_name), description (_description), index (_index), fallback_index (_fallback_index)
{
// .. nothing yet ..
}
@ -182,6 +182,12 @@ public:
* @brief The index of the layer
*/
size_t index;
/**
* @brief The index of the fallback layer
* This is the layer to be used when this layer isn't specified for input or (more important) output
*/
size_t fallback_index;
};
/**
@ -366,7 +372,14 @@ public:
* the device layers. The actual geometries are later available to "extract_devices"
* in the order the layers are defined.
*/
void define_layer (const std::string &name, const std::string &description = std::string ());
const db::NetlistDeviceExtractorLayerDefinition &define_layer (const std::string &name, const std::string &description = std::string ());
/**
* @brief Defines a layer with a fallback layer
* Like "define_layer" without fallback layer, but will fall back to the given layer
* (by index) if this layer isn't specified for input or terminal markup.
*/
const db::NetlistDeviceExtractorLayerDefinition &define_layer (const std::string &name, size_t fallback, const std::string &description = std::string ());
/**
* @brief Creates a device
@ -375,6 +388,11 @@ public:
*/
Device *create_device ();
/**
* @brief Defines a device terminal in the layout (a region)
*/
void define_terminal (Device *device, size_t terminal_id, size_t layer_index, const db::Region &region);
/**
* @brief Defines a device terminal in the layout (a polygon)
*/

View File

@ -38,9 +38,15 @@ NetlistDeviceExtractorMOS3Transistor::NetlistDeviceExtractorMOS3Transistor (cons
void NetlistDeviceExtractorMOS3Transistor::setup ()
{
define_layer ("SD", "Source/drain diffusion");
define_layer ("G", "Gate");
define_layer ("P", "Poly");
define_layer ("SD", "Source/drain diffusion"); // #0
define_layer ("G", "Gate input"); // #1
// for backward compatibility
define_layer ("P", 1, "Gate terminal output"); // #2 -> G
// terminal output
define_layer ("tG", 2, "Gate terminal output"); // #3 -> P -> G
define_layer ("tS", 0, "Source terminal output (default is SD)"); // #4
define_layer ("tD", 0, "Drain terminal output (default is SD)"); // #5
register_device_class (new db::DeviceClassMOS3Transistor ());
}
@ -66,8 +72,14 @@ db::Connectivity NetlistDeviceExtractorMOS3Transistor::get_connectivity (const d
void NetlistDeviceExtractorMOS3Transistor::extract_devices (const std::vector<db::Region> &layer_geometry)
{
const db::Region &rdiff = layer_geometry [0];
const db::Region &rgates = layer_geometry [1];
unsigned int diff_geometry_index = 0;
unsigned int gate_geometry_index = 1;
unsigned int gate_terminal_geometry_index = 3;
unsigned int source_terminal_geometry_index = 4;
unsigned int drain_terminal_geometry_index = 5;
const db::Region &rdiff = layer_geometry [diff_geometry_index];
const db::Region &rgates = layer_geometry [gate_geometry_index];
for (db::Region::const_iterator p = rgates.begin_merged (); !p.at_end (); ++p) {
@ -81,11 +93,8 @@ void NetlistDeviceExtractorMOS3Transistor::extract_devices (const std::vector<db
error (tl::to_string (tr ("Gate shape touches no diffusion - ignored")), *p);
} else {
unsigned int terminal_geometry_index = 0;
unsigned int gate_geometry_index = 2;
if (rdiff2gate.size () != 2) {
error (tl::sprintf (tl::to_string (tr ("Expected two polygons on diff interacting one gate shape (found %d) - gate shape ignored")), int (rdiff2gate.size ())), *p);
error (tl::sprintf (tl::to_string (tr ("Expected two polygons on diff interacting with one gate shape (found %d) - gate shape ignored")), int (rdiff2gate.size ())), *p);
continue;
}
@ -101,7 +110,7 @@ void NetlistDeviceExtractorMOS3Transistor::extract_devices (const std::vector<db
db::Device *device = create_device ();
device->set_position (db::CplxTrans (dbu ()) * p->box ().center ());
device->set_trans (db::DCplxTrans ((p->box ().center () - db::Point ()) * dbu ()));
device->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_W, dbu () * edges.length () * 0.5);
device->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_L, dbu () * (p->perimeter () - edges.length ()) * 0.5);
@ -117,11 +126,12 @@ void NetlistDeviceExtractorMOS3Transistor::extract_devices (const std::vector<db
device->set_parameter_value (diff_index == 0 ? db::DeviceClassMOS3Transistor::param_id_AS : db::DeviceClassMOS3Transistor::param_id_AD, dbu () * dbu () * d->area () / double (n));
device->set_parameter_value (diff_index == 0 ? db::DeviceClassMOS3Transistor::param_id_PS : db::DeviceClassMOS3Transistor::param_id_PD, dbu () * d->perimeter () / double (n));
define_terminal (device, diff_index == 0 ? db::DeviceClassMOS3Transistor::terminal_id_S : db::DeviceClassMOS3Transistor::terminal_id_D, terminal_geometry_index, *d);
unsigned int sd_index = diff_index == 0 ? source_terminal_geometry_index : drain_terminal_geometry_index;
define_terminal (device, diff_index == 0 ? db::DeviceClassMOS3Transistor::terminal_id_S : db::DeviceClassMOS3Transistor::terminal_id_D, sd_index, *d);
}
define_terminal (device, db::DeviceClassMOS3Transistor::terminal_id_G, gate_geometry_index, *p);
define_terminal (device, db::DeviceClassMOS3Transistor::terminal_id_G, gate_terminal_geometry_index, *p);
// allow derived classes to modify the device
modify_device (*p, layer_geometry, device);
@ -145,18 +155,495 @@ NetlistDeviceExtractorMOS4Transistor::NetlistDeviceExtractorMOS4Transistor (cons
void NetlistDeviceExtractorMOS4Transistor::setup ()
{
define_layer ("SD", "Source/drain diffusion");
define_layer ("G", "Gate");
define_layer ("P", "Poly");
define_layer ("W", "Well");
define_layer ("SD", "Source/drain diffusion"); // #0
define_layer ("G", "Gate input"); // #1
// for backward compatibility
define_layer ("P", 1, "Gate terminal output"); // #2 -> G
// terminal output
define_layer ("tG", 2, "Gate terminal output"); // #3 -> P -> G
define_layer ("tS", 0, "Source terminal output (default is SD)"); // #4
define_layer ("tD", 0, "Drain terminal output (default is SD)"); // #5
// for backward compatibility
define_layer ("W", "Well (bulk) terminal output"); // #6
define_layer ("tB", 6, "Well (bulk) terminal output"); // #7 -> W
register_device_class (new db::DeviceClassMOS4Transistor ());
}
void NetlistDeviceExtractorMOS4Transistor::modify_device (const db::Polygon &rgate, const std::vector<db::Region> & /*layer_geometry*/, db::Device *device)
{
unsigned int well_geometry_index = 3;
define_terminal (device, db::DeviceClassMOS4Transistor::terminal_id_B, well_geometry_index, rgate);
unsigned int bulk_terminal_geometry_index = 7;
define_terminal (device, db::DeviceClassMOS4Transistor::terminal_id_B, bulk_terminal_geometry_index, rgate);
}
// ---------------------------------------------------------------------------------
// NetlistDeviceExtractorResistor implementation
NetlistDeviceExtractorResistor::NetlistDeviceExtractorResistor (const std::string &name, double sheet_rho)
: db::NetlistDeviceExtractor (name), m_sheet_rho (sheet_rho)
{
// .. nothing yet ..
}
void NetlistDeviceExtractorResistor::setup ()
{
define_layer ("R", "Resistor"); // #0
define_layer ("C", "Contacts"); // #1
define_layer ("tA", 1, "A terminal output"); // #2 -> C
define_layer ("tB", 1, "B terminal output"); // #3 -> C
register_device_class (new db::DeviceClassResistor ());
}
db::Connectivity NetlistDeviceExtractorResistor::get_connectivity (const db::Layout & /*layout*/, const std::vector<unsigned int> &layers) const
{
tl_assert (layers.size () >= 2);
unsigned int res = layers [0];
unsigned int contact = layers [1];
// The layer definition is res, contact
db::Connectivity conn;
// collect all connected resistor shapes
conn.connect (res, res);
// connect res with contact for the contact shapes
conn.connect (res, contact);
return conn;
}
void NetlistDeviceExtractorResistor::extract_devices (const std::vector<db::Region> &layer_geometry)
{
size_t res_geometry_index = 0;
size_t contacts_geometry_index = 1;
size_t a_terminal_geometry_index = 2;
size_t b_terminal_geometry_index = 3;
const db::Region &res = layer_geometry [res_geometry_index];
const db::Region &contact = layer_geometry [contacts_geometry_index];
db::Region res_merged (res);
res_merged.set_base_verbosity (res.base_verbosity ());
db::Region contact_wo_res (contact);
contact_wo_res.set_base_verbosity (contact.base_verbosity ());
contact_wo_res -= res;
for (db::Region::const_iterator p = res_merged.begin_merged (); !p.at_end (); ++p) {
db::Region rres (*p);
db::Region contacts_per_res = contact_wo_res.selected_interacting (rres);
if (contacts_per_res.size () != 2) {
error (tl::sprintf (tl::to_string (tr ("Expected two polygons on contacts interacting with one resistor shape (found %d) - resistor shape ignored")), int (contacts_per_res.size ())), *p);
continue;
}
db::Device *device = create_device ();
device->set_trans (db::DCplxTrans ((p->box ().center () - db::Point ()) * dbu ()));
// TODO: this is a very rough approximation for the general case - it assumes a "good" geometry
db::Edges eparallel = rres.edges ();
eparallel -= contacts_per_res.edges ();
db::Edges eperp = rres.edges ();
eperp &= contacts_per_res.edges ();
db::Coord length = eparallel.length ();
db::Coord width = eperp.length ();
if (width < 1) {
error (tl::to_string (tr ("Invalid contact geometry - resistor shape ignored")), *p);
continue;
}
device->set_parameter_value (db::DeviceClassResistor::param_id_R, m_sheet_rho * double (length) / double (width));
device->set_parameter_value (db::DeviceClassResistor::param_id_L, dbu () * length);
device->set_parameter_value (db::DeviceClassResistor::param_id_W, dbu () * width);
device->set_parameter_value (db::DeviceClassResistor::param_id_A, dbu () * dbu () * p->area ());
device->set_parameter_value (db::DeviceClassResistor::param_id_P, dbu () * p->perimeter ());
int cont_index = 0;
for (db::Region::const_iterator d = contacts_per_res.begin (); !d.at_end () && cont_index < 2; ++d, ++cont_index) {
size_t terminal_geometry_index = cont_index == 0 ? a_terminal_geometry_index : b_terminal_geometry_index;
define_terminal (device, cont_index == 0 ? db::DeviceClassResistor::terminal_id_A : db::DeviceClassResistor::terminal_id_B, terminal_geometry_index, *d);
}
// allow derived classes to modify the device
modify_device (*p, layer_geometry, device);
// output the device for debugging
device_out (device, rres, contacts_per_res);
}
}
// ---------------------------------------------------------------------------------
// NetlistDeviceExtractorResistorWithBulk implementation
NetlistDeviceExtractorResistorWithBulk::NetlistDeviceExtractorResistorWithBulk (const std::string &name, double sheet_rho)
: NetlistDeviceExtractorResistor (name, sheet_rho)
{
// .. nothing yet ..
}
void NetlistDeviceExtractorResistorWithBulk::setup ()
{
define_layer ("R", "Resistor"); // #0
define_layer ("C", "Contacts"); // #1
define_layer ("tA", 1, "A terminal output"); // #2 -> C
define_layer ("tB", 1, "B terminal output"); // #3 -> C
define_layer ("W", "Well/Bulk"); // #4
define_layer ("tW", 4, "W terminal output"); // #5 -> W
register_device_class (new db::DeviceClassResistorWithBulk ());
}
void NetlistDeviceExtractorResistorWithBulk::modify_device (const db::Polygon &res, const std::vector<db::Region> & /*layer_geometry*/, db::Device *device)
{
unsigned int bulk_terminal_geometry_index = 5;
define_terminal (device, db::DeviceClassResistorWithBulk::terminal_id_W, bulk_terminal_geometry_index, res);
}
// ---------------------------------------------------------------------------------
// NetlistDeviceExtractorCapacitor implementation
NetlistDeviceExtractorCapacitor::NetlistDeviceExtractorCapacitor (const std::string &name, double area_cap)
: db::NetlistDeviceExtractor (name), m_area_cap (area_cap)
{
// .. nothing yet ..
}
void NetlistDeviceExtractorCapacitor::setup ()
{
define_layer ("P1", "Plate 1"); // #0
define_layer ("P2", "Plate 2"); // #1
define_layer ("tA", 0, "A terminal output"); // #2 -> P1
define_layer ("tB", 1, "B terminal output"); // #3 -> P2
register_device_class (new db::DeviceClassCapacitor ());
}
db::Connectivity NetlistDeviceExtractorCapacitor::get_connectivity (const db::Layout & /*layout*/, const std::vector<unsigned int> &layers) const
{
tl_assert (layers.size () >= 2);
unsigned int plate1 = layers [0];
unsigned int plate2 = layers [1];
// The layer definition is plate1, plate2
db::Connectivity conn;
// collect all connected plate 1 shapes
conn.connect (plate1, plate1);
// collect all connected plate 1 shapes
conn.connect (plate2, plate2);
// connect the plates (NOTE that this is a logical, not a physical connection)
conn.connect (plate1, plate2);
return conn;
}
void NetlistDeviceExtractorCapacitor::extract_devices (const std::vector<db::Region> &layer_geometry)
{
size_t plate1_geometry_index = 0;
size_t plate2_geometry_index = 1;
size_t a_terminal_geometry_index = 2;
size_t b_terminal_geometry_index = 3;
const db::Region &plate1 = layer_geometry [plate1_geometry_index];
const db::Region &plate2 = layer_geometry [plate2_geometry_index];
db::Region overlap (plate1);
overlap.set_base_verbosity (plate1.base_verbosity ());
overlap &= plate2;
for (db::Region::const_iterator p = overlap.begin_merged (); !p.at_end (); ++p) {
db::Device *device = create_device ();
device->set_trans (db::DCplxTrans ((p->box ().center () - db::Point ()) * dbu ()));
double area = p->area () * dbu () * dbu ();
device->set_parameter_value (db::DeviceClassCapacitor::param_id_C, m_area_cap * area);
device->set_parameter_value (db::DeviceClassCapacitor::param_id_A, area);
device->set_parameter_value (db::DeviceClassCapacitor::param_id_P, dbu () * p->perimeter ());
define_terminal (device, db::DeviceClassCapacitor::terminal_id_A, a_terminal_geometry_index, *p);
define_terminal (device, db::DeviceClassCapacitor::terminal_id_B, b_terminal_geometry_index, *p);
// allow derived classes to modify the device
modify_device (*p, layer_geometry, device);
// output the device for debugging
device_out (device, *p);
}
}
// ---------------------------------------------------------------------------------
// NetlistDeviceExtractorCapacitorWithBulk implementation
NetlistDeviceExtractorCapacitorWithBulk::NetlistDeviceExtractorCapacitorWithBulk (const std::string &name, double area_cap)
: NetlistDeviceExtractorCapacitor (name, area_cap)
{
// .. nothing yet ..
}
void NetlistDeviceExtractorCapacitorWithBulk::setup ()
{
define_layer ("P1", "Plate 1"); // #0
define_layer ("P2", "Plate 2"); // #1
define_layer ("tA", 0, "A terminal output"); // #2 -> P1
define_layer ("tB", 1, "B terminal output"); // #3 -> P2
define_layer ("W", "Well/Bulk"); // #4
define_layer ("tW", 4, "W terminal output"); // #5 -> W
register_device_class (new db::DeviceClassCapacitorWithBulk ());
}
void NetlistDeviceExtractorCapacitorWithBulk::modify_device (const db::Polygon &cap, const std::vector<db::Region> & /*layer_geometry*/, db::Device *device)
{
unsigned int bulk_terminal_geometry_index = 5;
define_terminal (device, db::DeviceClassCapacitorWithBulk::terminal_id_W, bulk_terminal_geometry_index, cap);
}
// ---------------------------------------------------------------------------------
// NetlistDeviceExtractorBJT3Transistor implementation
NetlistDeviceExtractorBJT3Transistor::NetlistDeviceExtractorBJT3Transistor (const std::string &name)
: db::NetlistDeviceExtractor (name)
{
// .. nothing yet ..
}
void NetlistDeviceExtractorBJT3Transistor::setup ()
{
define_layer ("C", "Collector"); // #0
define_layer ("B", "Base"); // #1
define_layer ("E", "Emitter"); // #2
// terminal output
define_layer ("tC", 0, "Collector terminal output"); // #3 -> C
define_layer ("tB", 1, "Base terminal output"); // #4 -> B
define_layer ("tE", 2, "Emitter terminal output"); // #5 -> E
register_device_class (new db::DeviceClassBJT3Transistor ());
}
db::Connectivity NetlistDeviceExtractorBJT3Transistor::get_connectivity (const db::Layout & /*layout*/, const std::vector<unsigned int> &layers) const
{
tl_assert (layers.size () >= 3);
unsigned int collector = layers [0];
unsigned int base = layers [1];
unsigned int emitter = layers [2];
db::Connectivity conn;
// collect all connected base shapes. Join polygons.
conn.connect (base, base);
// collect all collector and emitter shapes connected with base
conn.connect (base, collector);
conn.connect (base, emitter);
return conn;
}
void NetlistDeviceExtractorBJT3Transistor::extract_devices (const std::vector<db::Region> &layer_geometry)
{
unsigned int collector_geometry_index = 0;
unsigned int base_geometry_index = 1;
unsigned int emitter_geometry_index = 2;
unsigned int collector_terminal_geometry_index = 3;
unsigned int base_terminal_geometry_index = 4;
unsigned int emitter_terminal_geometry_index = 5;
const db::Region &rbases = layer_geometry [base_geometry_index];
const db::Region &rcollectors = layer_geometry [collector_geometry_index];
const db::Region &remitters = layer_geometry [emitter_geometry_index];
for (db::Region::const_iterator p = rbases.begin_merged (); !p.at_end (); ++p) {
db::Region rbase (*p);
rbase.set_base_verbosity (rbases.base_verbosity ());
db::Region remitter2base = rbase & remitters;
if (remitter2base.empty ()) {
error (tl::to_string (tr ("Base shape without emitters - ignored")), *p);
} else {
// collectors inside base
db::Region rcollector2base = rbase & rcollectors;
db::Region rcollector;
if (rcollector2base.empty ()) {
// collector is bulk (vertical)
rcollector2base = rbase;
rcollector = rbase;
} else if ((rbase - rcollector2base).empty ()) {
// vertical transistor: collector entirely covers base -> collector terminal is collector outside base
rcollector = rcollector2base - rbase;
} else {
// lateral transistor: base is reduced by collector area
rcollector = rcollector2base;
rbase -= rcollector2base;
}
// this is what is the true base contact
rbase -= remitter2base;
// emitter wins over collector for the collector contact
rcollector -= remitter2base;
double ab = dbu () * dbu () * p->area ();
double pb = dbu () * p->perimeter ();
double ac = dbu () * dbu () * rcollector2base.area ();
double pc = dbu () * rcollector2base.perimeter ();
for (db::Region::const_iterator pe = remitter2base.begin_merged (); !pe.at_end (); ++pe) {
db::Device *device = create_device ();
device->set_trans (db::DCplxTrans ((pe->box ().center () - db::Point ()) * dbu ()));
device->set_parameter_value (db::DeviceClassBJT3Transistor::param_id_NE, 1.0);
device->set_parameter_value (db::DeviceClassBJT3Transistor::param_id_AE, dbu () * dbu () * pe->area ());
device->set_parameter_value (db::DeviceClassBJT3Transistor::param_id_PE, dbu () * pe->perimeter ());
device->set_parameter_value (db::DeviceClassBJT3Transistor::param_id_AB, ab);
device->set_parameter_value (db::DeviceClassBJT3Transistor::param_id_PB, pb);
device->set_parameter_value (db::DeviceClassBJT3Transistor::param_id_AC, ac);
device->set_parameter_value (db::DeviceClassBJT3Transistor::param_id_PC, pc);
define_terminal (device, db::DeviceClassBJT3Transistor::terminal_id_C, collector_terminal_geometry_index, rcollector);
define_terminal (device, db::DeviceClassBJT3Transistor::terminal_id_B, base_terminal_geometry_index, rbase);
define_terminal (device, db::DeviceClassBJT3Transistor::terminal_id_E, emitter_terminal_geometry_index, *pe);
// allow derived classes to modify the device
modify_device (*p, layer_geometry, device);
// output the device for debugging
device_out (device, rcollector, rbase, *pe);
}
}
}
}
// ---------------------------------------------------------------------------------
// NetlistDeviceExtractorBJT4Transistor implementation
NetlistDeviceExtractorBJT4Transistor::NetlistDeviceExtractorBJT4Transistor (const std::string &name)
: NetlistDeviceExtractorBJT3Transistor (name)
{
// .. nothing yet ..
}
void NetlistDeviceExtractorBJT4Transistor::setup ()
{
define_layer ("C", "Collector"); // #0
define_layer ("B", "Base"); // #1
define_layer ("E", "Emitter"); // #2
// terminal output
define_layer ("tC", 0, "Collector terminal output"); // #3 -> C
define_layer ("tB", 1, "Base terminal output"); // #4 -> B
define_layer ("tE", 2, "Emitter terminal output"); // #5 -> E
// for convenience and consistency with MOS4
define_layer ("S", "Substrate (bulk) terminal output"); // #6
define_layer ("tS", 6, "Substrate (bulk) terminal output"); // #7 -> S
register_device_class (new db::DeviceClassBJT4Transistor ());
}
void NetlistDeviceExtractorBJT4Transistor::modify_device (const db::Polygon &emitter, const std::vector<db::Region> & /*layer_geometry*/, db::Device *device)
{
unsigned int substrate_terminal_geometry_index = 7;
define_terminal (device, db::DeviceClassBJT4Transistor::terminal_id_S, substrate_terminal_geometry_index, emitter);
}
// ---------------------------------------------------------------------------------
// NetlistDeviceExtractorDiode implementation
NetlistDeviceExtractorDiode::NetlistDeviceExtractorDiode (const std::string &name)
: db::NetlistDeviceExtractor (name)
{
// .. nothing yet ..
}
void NetlistDeviceExtractorDiode::setup ()
{
define_layer ("P", "P region"); // #0
define_layer ("N", "N region"); // #1
define_layer ("tA", 0, "A terminal output"); // #2 -> P
define_layer ("tC", 1, "C terminal output"); // #3 -> N
register_device_class (new db::DeviceClassDiode ());
}
db::Connectivity NetlistDeviceExtractorDiode::get_connectivity (const db::Layout & /*layout*/, const std::vector<unsigned int> &layers) const
{
tl_assert (layers.size () >= 2);
unsigned int pregion = layers [0];
unsigned int nregion = layers [1];
// The layer definition is plate1, plate2
db::Connectivity conn;
// collect all connected plate 1 shapes
conn.connect (pregion, pregion);
// collect all connected plate 1 shapes
conn.connect (nregion, nregion);
// connect the plates (NOTE that this is a logical, not a physical connection)
conn.connect (pregion, nregion);
return conn;
}
void NetlistDeviceExtractorDiode::extract_devices (const std::vector<db::Region> &layer_geometry)
{
size_t pregion_geometry_index = 0;
size_t nregion_geometry_index = 1;
size_t a_terminal_geometry_index = 2;
size_t c_terminal_geometry_index = 3;
const db::Region &pregion = layer_geometry [pregion_geometry_index];
const db::Region &nregion = layer_geometry [nregion_geometry_index];
db::Region overlap (pregion);
overlap.set_base_verbosity (pregion.base_verbosity ());
overlap &= nregion;
for (db::Region::const_iterator p = overlap.begin_merged (); !p.at_end (); ++p) {
db::Device *device = create_device ();
device->set_trans (db::DCplxTrans ((p->box ().center () - db::Point ()) * dbu ()));
double area = p->area () * dbu () * dbu ();
device->set_parameter_value (db::DeviceClassDiode::param_id_A, area);
device->set_parameter_value (db::DeviceClassDiode::param_id_P, dbu () * p->perimeter ());
define_terminal (device, db::DeviceClassDiode::terminal_id_A, a_terminal_geometry_index, *p);
define_terminal (device, db::DeviceClassDiode::terminal_id_C, c_terminal_geometry_index, *p);
// allow derived classes to modify the device
modify_device (*p, layer_geometry, device);
// output the device for debugging
device_out (device, *p);
}
}
}

View File

@ -95,6 +95,253 @@ private:
virtual void modify_device (const db::Polygon &rgate, const std::vector<db::Region> &layer_geometry, db::Device *device);
};
/**
* @brief A device extractor for a two-terminal resistor
*
* This class supplies the generic extractor for an resistor
* The resistor is defined by a "wire" with two connectors on
* each side.
*
* The resistance is computed from the width (W) and length (L) of the
* wire by R = L / W * sheet_rho.
*
* The device class produced by this extractor is DeviceClassResistor.
* The extractor extracts the three parameters of this class: R, A and P.
* A is the area of the wire and P is the perimeter.
*
* The layers are R for the "wire" and "C" for the two contacts and the
* end of the wire. "tA" and "tB" are the layers on which the A and B
* terminals are produced.
*/
class DB_PUBLIC NetlistDeviceExtractorResistor
: public db::NetlistDeviceExtractor
{
public:
NetlistDeviceExtractorResistor (const std::string &name, double sheet_rho);
virtual void setup ();
virtual db::Connectivity get_connectivity (const db::Layout &layout, const std::vector<unsigned int> &layers) const;
virtual void extract_devices (const std::vector<db::Region> &layer_geometry);
protected:
/**
* @brief A callback when the device is produced
* This callback is provided as a debugging port
*/
virtual void device_out (const db::Device * /*device*/, const db::Region & /*res*/, const db::Region & /*contacts*/)
{
// .. no specific implementation ..
}
/**
* @brief Allow derived classes to modify the device
*/
virtual void modify_device (const db::Polygon & /*res*/, const std::vector<db::Region> & /*layer_geometry*/, db::Device * /*device*/)
{
// .. no specific implementation ..
}
private:
double m_sheet_rho;
};
/**
* @brief A device extractor for a two-terminal resistor with a bulk terminal
*
* Extracts a resistor like NetlistDeviceExtractorResistor, but adds one more terminal
* for the bulk or well the resistor is embedded in.
*/
class DB_PUBLIC NetlistDeviceExtractorResistorWithBulk
: public db::NetlistDeviceExtractorResistor
{
public:
NetlistDeviceExtractorResistorWithBulk (const std::string &name, double sheet_rho);
virtual void setup ();
virtual void modify_device (const db::Polygon &res, const std::vector<db::Region> & /*layer_geometry*/, db::Device *device);
};
/**
* @brief A device extractor for a planar capacitor
*
* This class supplies the generic extractor for a planar capacitor.
* The capacitor is defined by two layers whose overlap area forms
* the capacitor.
*
* The resistance is computed from the area (A) of the overlapping region
* by C = A * area_cap.
*
* The device class produced by this extractor is DeviceClassCapacitor.
* The extractor extracts the three parameters of this class: C, A and P.
* A is the area of the overlap area and P is the perimeter.
*
* The layers are P1 and P2 for the plates. tA and tB are layers where
* the terminals for A and B are produced respectively.
*/
class DB_PUBLIC NetlistDeviceExtractorCapacitor
: public db::NetlistDeviceExtractor
{
public:
NetlistDeviceExtractorCapacitor (const std::string &name, double area_cap);
virtual void setup ();
virtual db::Connectivity get_connectivity (const db::Layout &layout, const std::vector<unsigned int> &layers) const;
virtual void extract_devices (const std::vector<db::Region> &layer_geometry);
protected:
/**
* @brief A callback when the device is produced
* This callback is provided as a debugging port
*/
virtual void device_out (const db::Device * /*device*/, const db::Polygon & /*cap_area*/)
{
// .. no specific implementation ..
}
/**
* @brief Allow derived classes to modify the device
*/
virtual void modify_device (const db::Polygon & /*cap_area*/, const std::vector<db::Region> & /*layer_geometry*/, db::Device * /*device*/)
{
// .. no specific implementation ..
}
private:
double m_area_cap;
};
/**
* @brief A device extractor for a two-terminal capacitor with a bulk terminal
*
* Extracts a resistor like NetlistDeviceExtractorCapacitor, but adds one more terminal
* for the bulk or well the capacitor is embedded in.
*/
class DB_PUBLIC NetlistDeviceExtractorCapacitorWithBulk
: public db::NetlistDeviceExtractorCapacitor
{
public:
NetlistDeviceExtractorCapacitorWithBulk (const std::string &name, double cap_area);
virtual void setup ();
virtual void modify_device (const db::Polygon &cap, const std::vector<db::Region> & /*layer_geometry*/, db::Device *device);
};
/**
* @brief A device extractor for a bipolar transistor
*
* This class supplies the generic extractor for a bipolar transistor device.
* Extraction of vertical and lateral transistors is supported through a generic geometry model:
*
* The basic area is the base area. A marker shape must be provided for this area.
* The emitter of the transistor is defined by emitter layer shapes inside the base area.
* Multiple emitter shapes can be present. In this case, multiple transistor devices sharing the
* same base and collector are generated.
*
* Finally, a collector layer can be given. If non-empty, the parts inside the base region will define
* the collector terminals. If empty, the collector is formed by the substrate. In this case, the base
* region will be output to the 'tC' terminal output layer. This layer then needs to be connected to a global net
* to form the net connection.
*
* The device class produced by this extractor is \\DeviceClassBJT3Transistor.
* The extractor extracts the two parameters of this class: AE and PE.
*
* The device recognition layer names are 'C' (collector), 'B' (base) and 'E' (emitter).
* The terminal output layer names are 'tC' (collector), 'tB' (base) and 'tE' (emitter).
*/
class DB_PUBLIC NetlistDeviceExtractorBJT3Transistor
: public db::NetlistDeviceExtractor
{
public:
NetlistDeviceExtractorBJT3Transistor (const std::string &name);
virtual void setup ();
virtual db::Connectivity get_connectivity (const db::Layout &layout, const std::vector<unsigned int> &layers) const;
virtual void extract_devices (const std::vector<db::Region> &layer_geometry);
protected:
/**
* @brief A callback when the device is produced
* This callback is provided as a debugging port
*/
virtual void device_out (const db::Device * /*device*/, const db::Region & /*collector*/, const db::Region & /*base*/, const db::Polygon & /*emitter*/)
{
// .. no specific implementation ..
}
/**
* @brief Allow derived classes to modify the device
*/
virtual void modify_device (const db::Polygon & /*emitter*/, const std::vector<db::Region> & /*layer_geometry*/, db::Device * /*device*/)
{
// .. no specific implementation ..
}
};
/**
* @brief A device extractor for a four-terminal BJT transistor
*
* This class is like the BJT3Transistor extractor, but requires a forth
* input layer (Substrate). This layer will be used to output the substrate terminal.
*
* The device class produced by this extractor is DeviceClassBJT4Transistor.
*/
class DB_PUBLIC NetlistDeviceExtractorBJT4Transistor
: public NetlistDeviceExtractorBJT3Transistor
{
public:
NetlistDeviceExtractorBJT4Transistor (const std::string &name);
virtual void setup ();
private:
virtual void modify_device (const db::Polygon &emitter, const std::vector<db::Region> &layer_geometry, db::Device *device);
};
/**
* @brief A device extractor for a planar diode
*
* This class supplies the generic extractor for a planar diode.
* The diode is defined by two layers whose overlap area forms
* the diode. The p-type layer forms the anode, the n-type layer
* the cathode.
*
* The device class produced by this extractor is DeviceClassDiode.
* The extractor extracts the two parameters of this class: A and P.
* A is the area of the overlap area and P is the perimeter.
*
* The layers are "P" and "N" for the p and n region respectively.
* The terminal output layers are "tA" and "tC" for anode and
* cathode respectively.
*/
class DB_PUBLIC NetlistDeviceExtractorDiode
: public db::NetlistDeviceExtractor
{
public:
NetlistDeviceExtractorDiode (const std::string &name);
virtual void setup ();
virtual db::Connectivity get_connectivity (const db::Layout &layout, const std::vector<unsigned int> &layers) const;
virtual void extract_devices (const std::vector<db::Region> &layer_geometry);
protected:
/**
* @brief A callback when the device is produced
* This callback is provided as a debugging port
*/
virtual void device_out (const db::Device * /*device*/, const db::Polygon & /*diode_area*/)
{
// .. no specific implementation ..
}
/**
* @brief Allow derived classes to modify the device
*/
virtual void modify_device (const db::Polygon & /*diode_area*/, const std::vector<db::Region> & /*layer_geometry*/, db::Device * /*device*/)
{
// .. no specific implementation ..
}
};
}
namespace tl
@ -112,6 +359,48 @@ template<> struct type_traits<db::NetlistDeviceExtractorMOS4Transistor> : public
typedef tl::false_tag has_default_constructor;
};
template<> struct type_traits<db::NetlistDeviceExtractorCapacitor> : public tl::type_traits<void>
{
typedef tl::false_tag has_copy_constructor;
typedef tl::false_tag has_default_constructor;
};
template<> struct type_traits<db::NetlistDeviceExtractorCapacitorWithBulk> : public tl::type_traits<void>
{
typedef tl::false_tag has_copy_constructor;
typedef tl::false_tag has_default_constructor;
};
template<> struct type_traits<db::NetlistDeviceExtractorResistor> : public tl::type_traits<void>
{
typedef tl::false_tag has_copy_constructor;
typedef tl::false_tag has_default_constructor;
};
template<> struct type_traits<db::NetlistDeviceExtractorResistorWithBulk> : public tl::type_traits<void>
{
typedef tl::false_tag has_copy_constructor;
typedef tl::false_tag has_default_constructor;
};
template<> struct type_traits<db::NetlistDeviceExtractorBJT3Transistor> : public tl::type_traits<void>
{
typedef tl::false_tag has_copy_constructor;
typedef tl::false_tag has_default_constructor;
};
template<> struct type_traits<db::NetlistDeviceExtractorBJT4Transistor> : public tl::type_traits<void>
{
typedef tl::false_tag has_copy_constructor;
typedef tl::false_tag has_default_constructor;
};
template<> struct type_traits<db::NetlistDeviceExtractorDiode> : public tl::type_traits<void>
{
typedef tl::false_tag has_copy_constructor;
typedef tl::false_tag has_default_constructor;
};
}
#endif

View File

@ -105,6 +105,7 @@ NetlistExtractor::extract_nets (const db::DeepShapeStore &dss, unsigned int layo
db::DeviceAbstract *dm = nl.device_abstract_by_cell_index (*cid);
if (dm) {
// This is a device abstract cell:
// make the terminal to cluster ID connections for the device abstract from the device cells
make_device_abstract_connections (dm, clusters);
continue;
@ -116,10 +117,8 @@ NetlistExtractor::extract_nets (const db::DeepShapeStore &dss, unsigned int layo
std::map<db::cell_index_type, db::Circuit *>::const_iterator k = circuits.find (*cid);
if (k == circuits.end ()) {
circuit = new db::Circuit ();
circuit = new db::Circuit (*mp_layout, *cid);
nl.add_circuit (circuit);
circuit->set_name (mp_layout->cell_name (*cid));
circuit->set_cell_index (*cid);
circuits.insert (std::make_pair (*cid, circuit));
} else {
circuit = k->second;
@ -142,11 +141,6 @@ NetlistExtractor::extract_nets (const db::DeepShapeStore &dss, unsigned int layo
net->set_cluster_id (*c);
circuit->add_net (net);
const db::local_cluster<db::PolygonRef>::global_nets &gn = lc.get_global_nets ();
for (db::local_cluster<db::PolygonRef>::global_nets::const_iterator g = gn.begin (); g != gn.end (); ++g) {
assign_net_name (conn.global_net_name (*g), net);
}
// make subcircuit connections (also make the subcircuits if required) from the connections of the clusters
make_and_connect_subcircuits (circuit, clusters, *c, net, subcircuits, circuits, pins_per_cluster_per_cell);
@ -154,7 +148,18 @@ NetlistExtractor::extract_nets (const db::DeepShapeStore &dss, unsigned int layo
connect_devices (circuit, clusters, *c, net);
// collect labels to net names
collect_labels (clusters, *c, net);
std::set<std::string> net_names;
collect_labels (clusters, *c, net_names);
// add the global names as second priority
if (net_names.empty ()) {
const db::local_cluster<db::PolygonRef>::global_nets &gn = lc.get_global_nets ();
for (db::local_cluster<db::PolygonRef>::global_nets::const_iterator g = gn.begin (); g != gn.end (); ++g) {
net_names.insert (conn.global_net_name (*g));
}
}
assign_net_names (net, net_names);
if (! clusters.is_root (*c)) {
// a non-root cluster makes a pin
@ -167,6 +172,22 @@ NetlistExtractor::extract_nets (const db::DeepShapeStore &dss, unsigned int layo
}
}
void
NetlistExtractor::assign_net_names (db::Net *net, const std::set<std::string> &net_names)
{
std::string nn;
for (std::set<std::string>::const_iterator n = net_names.begin (); n != net_names.end (); ++n) {
if (! n->empty ()) {
if (! nn.empty ()) {
nn += ",";
}
nn += *n;
}
}
net->set_name (nn);
}
void
NetlistExtractor::make_device_abstract_connections (db::DeviceAbstract *dm, const connected_clusters_type &clusters)
{
@ -190,11 +211,19 @@ NetlistExtractor::make_device_abstract_connections (db::DeviceAbstract *dm, cons
}
}
// check whether all connections have been made
const std::vector<db::DeviceTerminalDefinition> &td = dm->device_class ()->terminal_definitions ();
for (std::vector<db::DeviceTerminalDefinition>::const_iterator t = td.begin (); t != td.end (); ++t) {
if (! dm->cluster_id_for_terminal (t->id ())) {
throw tl::Exception (tl::sprintf (tl::to_string (tr ("Terminal '%s' of a device of class '%s' isn't connected - maybe the terminal annotation layer of this device type isn't part of the connectivity?")), t->name (), dm->device_class ()->name ()));
}
}
}
void NetlistExtractor::collect_labels (const connected_clusters_type &clusters,
size_t cid,
db::Net *net)
std::set<std::string> &net_names)
{
// collect the properties - we know that the cluster attributes are property ID's because the
// cluster processor converts shape property IDs to attributes
@ -206,7 +235,7 @@ void NetlistExtractor::collect_labels (const connected_clusters_type &clusters,
for (db::PropertiesRepository::properties_set::const_iterator j = ps.begin (); j != ps.end (); ++j) {
if (m_text_annot_name_id.first && j->first == m_text_annot_name_id.second) {
assign_net_name (j->second.to_string (), net);
net_names.insert (j->second.to_string ());
}
}
@ -348,15 +377,4 @@ size_t NetlistExtractor::make_pin (db::Circuit *circuit, db::Net *net)
return pin_id;
}
void NetlistExtractor::assign_net_name (const std::string &n, db::Net *net)
{
std::string nn = n;
if (! nn.empty ()) {
if (! net->name ().empty ()) {
nn = net->name () + "," + nn;
}
net->set_name (nn);
}
}
}

View File

@ -27,6 +27,8 @@
#include "dbHierNetworkProcessor.h"
#include <map>
#include <set>
#include <string>
namespace db
{
@ -92,9 +94,9 @@ private:
std::pair<bool, db::property_names_id_type> m_device_annot_name_id;
std::pair<bool, db::property_names_id_type> m_terminal_annot_name_id;
void assign_net_name (const std::string &n, db::Net *net);
bool instance_is_device (db::properties_id_type prop_id) const;
db::Device *device_from_instance (db::properties_id_type prop_id, db::Circuit *circuit) const;
void assign_net_names (db::Net *net, const std::set<std::string> &net_names);
/**
* @brief Make a pin connection from clusters
@ -151,7 +153,7 @@ private:
*/
void collect_labels (const connected_clusters_type &clusters,
size_t cid,
db::Net *net);
std::set<std::string> &net_names);
/**
* @brief Makes the terminal to cluster ID connections of the device abstract

View File

@ -26,6 +26,7 @@
#include "tlStream.h"
#include "tlLog.h"
#include "tlString.h"
#include <sstream>
#include <cctype>
@ -33,14 +34,156 @@
namespace db
{
static const char *allowed_name_chars = "_.:,!+$/&\\#[]|";
// ------------------------------------------------------------------------------------------------------
NetlistSpiceReader::NetlistSpiceReader ()
: mp_netlist (0), mp_stream (0)
NetlistSpiceReaderDelegate::NetlistSpiceReaderDelegate ()
{
// .. nothing yet ..
}
NetlistSpiceReaderDelegate::~NetlistSpiceReaderDelegate ()
{
// .. nothing yet ..
}
void NetlistSpiceReaderDelegate::start (db::Netlist * /*netlist*/)
{
// .. nothing yet ..
}
void NetlistSpiceReaderDelegate::finish (db::Netlist * /*netlist*/)
{
// .. nothing yet ..
}
bool NetlistSpiceReaderDelegate::wants_subcircuit (const std::string & /*circuit_name*/)
{
return false;
}
void NetlistSpiceReaderDelegate::error (const std::string &msg)
{
throw tl::Exception (msg);
}
template <class Cls>
static db::DeviceClass *make_device_class (db::Circuit *circuit, const std::string &name)
{
if (! circuit || ! circuit->netlist ()) {
return 0;
}
db::DeviceClass *cls = circuit->netlist ()->device_class_by_name (name);
if (! cls) {
cls = new Cls ();
cls->set_name (name);
circuit->netlist ()->add_device_class (cls);
}
return cls;
}
bool NetlistSpiceReaderDelegate::element (db::Circuit *circuit, const std::string &element, const std::string &name, const std::string &model, double value, const std::vector<db::Net *> &nets, const std::map<std::string, double> &params)
{
std::string cn = model;
db::DeviceClass *cls = circuit->netlist ()->device_class_by_name (cn);
if (cls) {
// use given class
} else if (element == "R") {
if (cn.empty ()) {
cn = "RES";
}
cls = make_device_class<db::DeviceClassResistor> (circuit, cn);
} else if (element == "L") {
if (cn.empty ()) {
cn = "IND";
}
cls = make_device_class<db::DeviceClassInductor> (circuit, cn);
} else if (element == "C") {
if (cn.empty ()) {
cn = "CAP";
}
cls = make_device_class<db::DeviceClassCapacitor> (circuit, cn);
} else if (element == "D") {
if (cn.empty ()) {
cn = "DIODE";
}
cls = make_device_class<db::DeviceClassDiode> (circuit, cn);
} else if (element == "Q") {
if (nets.size () == 3) {
if (cn.empty ()) {
cn = "BJT3";
}
cls = make_device_class<db::DeviceClassBJT3Transistor> (circuit, cn);
} else if (nets.size () == 4) {
if (cn.empty ()) {
cn = "BJT4";
}
cls = make_device_class<db::DeviceClassBJT4Transistor> (circuit, cn);
} else {
error (tl::to_string (tr ("'Q' element needs to have 3 or 4 terminals")));
}
} else if (element == "M") {
if (nets.size () == 4) {
if (cn.empty ()) {
cn = "MOS4";
}
cls = make_device_class<db::DeviceClassMOS4Transistor> (circuit, cn);
} else {
error (tl::to_string (tr ("'M' element needs to have 4 terminals")));
}
} else {
error (tl::sprintf (tl::to_string (tr ("Not a known element type: '%s'")), element));
}
const std::vector<db::DeviceTerminalDefinition> &td = cls->terminal_definitions ();
if (td.size () != nets.size ()) {
error (tl::sprintf (tl::to_string (tr ("Wrong number of terminals: class '%s' expects %d, but %d are given")), cn, int (td.size ()), int (nets.size ())));
}
db::Device *device = new db::Device (cls, name);
circuit->add_device (device);
for (std::vector<db::DeviceTerminalDefinition>::const_iterator t = td.begin (); t != td.end (); ++t) {
device->connect_terminal (t->id (), nets [t - td.begin ()]);
}
size_t defp = std::numeric_limits<size_t>::max ();
if (dynamic_cast<db::DeviceClassCapacitor *> (cls)) {
defp = db::DeviceClassCapacitor::param_id_C;
} else if (dynamic_cast<db::DeviceClassResistor *> (cls)) {
defp = db::DeviceClassResistor::param_id_R;
} else if (dynamic_cast<db::DeviceClassInductor *> (cls)) {
defp = db::DeviceClassInductor::param_id_L;
}
const std::vector<db::DeviceParameterDefinition> &pd = cls->parameter_definitions ();
for (std::vector<db::DeviceParameterDefinition>::const_iterator i = pd.begin (); i != pd.end (); ++i) {
std::map<std::string, double>::const_iterator v = params.find (i->name ());
if (v != params.end ()) {
device->set_parameter_value (i->id (), v->second / i->si_scaling ());
} else if (i->id () == defp) {
device->set_parameter_value (i->id (), value / i->si_scaling ());
}
}
return true;
}
// ------------------------------------------------------------------------------------------------------
static const char *allowed_name_chars = "_.:,!+$/&\\#[]|<>";
NetlistSpiceReader::NetlistSpiceReader (NetlistSpiceReaderDelegate *delegate)
: mp_netlist (0), mp_stream (0), mp_delegate (delegate)
{
static NetlistSpiceReaderDelegate std_delegate;
if (! delegate) {
mp_delegate.reset (&std_delegate);
}
}
NetlistSpiceReader::~NetlistSpiceReader ()
{
// .. nothing yet ..
@ -55,10 +198,13 @@ void NetlistSpiceReader::read (tl::InputStream &stream, db::Netlist &netlist)
try {
mp_delegate->start (&netlist);
while (! at_end ()) {
read_element ();
read_card ();
}
mp_delegate->finish (&netlist);
finish ();
} catch (tl::Exception &ex) {
@ -83,6 +229,11 @@ void NetlistSpiceReader::finish ()
pop_stream ();
}
// purge nets with single connections (this way unconnected pins can be realized)
if (mp_netlist) {
mp_netlist->purge_nets ();
}
mp_stream.reset (0);
mp_netlist = 0;
mp_circuit = 0;
@ -141,8 +292,7 @@ std::string NetlistSpiceReader::get_line ()
tl::Extractor ex (l.c_str ());
if (ex.test_without_case (".include")) {
std::string path;
ex.read_word_or_quoted (path, allowed_name_chars);
std::string path = read_name_with_case (ex);
push_stream (path);
@ -162,7 +312,19 @@ void NetlistSpiceReader::unget_line (const std::string &l)
m_stored_line = l;
}
bool NetlistSpiceReader::read_element ()
bool NetlistSpiceReader::subcircuit_captured (const std::string &nc_name)
{
std::map<std::string, bool>::const_iterator c = m_captured.find (nc_name);
if (c != m_captured.end ()) {
return c->second;
} else {
bool cap = mp_delegate->wants_subcircuit (nc_name);
m_captured.insert (std::make_pair (nc_name, cap));
return cap;
}
}
bool NetlistSpiceReader::read_card ()
{
std::string l = get_line ();
if (l.empty ()) {
@ -171,9 +333,8 @@ bool NetlistSpiceReader::read_element ()
tl::Extractor ex (l.c_str ());
const char *res_device_class_name = "RES";
const char *cap_device_class_name = "CAP";
const char *ind_device_class_name = "IND";
ex.skip ();
char next_char = toupper (*ex);
if (ex.test_without_case (".")) {
@ -184,7 +345,12 @@ bool NetlistSpiceReader::read_element ()
} else if (ex.test_without_case ("subckt")) {
read_circuit (ex);
std::string nc = read_name (ex);
if (subcircuit_captured (nc)) {
skip_circuit (ex);
} else {
read_circuit (ex, nc);
}
} else if (ex.test_without_case ("ends")) {
@ -203,59 +369,24 @@ bool NetlistSpiceReader::read_element ()
}
} else if (ex.test_without_case ("r")) {
} else if (isalpha (next_char)) {
db::DeviceClass *dev_cls = mp_netlist->device_class_by_name (res_device_class_name);
if (! dev_cls) {
dev_cls = new db::DeviceClassResistor ();
dev_cls->set_name (res_device_class_name);
mp_netlist->add_device_class (dev_cls);
++ex;
std::string name = read_name (ex);
ensure_circuit ();
std::string es;
es.push_back (next_char);
if (! read_element (ex, es, name)) {
warn (tl::sprintf (tl::to_string (tr ("Element type '%c' ignored")), next_char));
}
ensure_circuit ();
read_device (dev_cls, db::DeviceClassResistor::param_id_R, ex);
} else if (ex.test_without_case ("c")) {
db::DeviceClass *dev_cls = mp_netlist->device_class_by_name (cap_device_class_name);
if (! dev_cls) {
dev_cls = new db::DeviceClassCapacitor ();
dev_cls->set_name (cap_device_class_name);
mp_netlist->add_device_class (dev_cls);
}
ensure_circuit ();
read_device (dev_cls, db::DeviceClassCapacitor::param_id_C, ex);
} else if (ex.test_without_case ("l")) {
db::DeviceClass *dev_cls = mp_netlist->device_class_by_name (ind_device_class_name);
if (! dev_cls) {
dev_cls = new db::DeviceClassInductor ();
dev_cls->set_name (ind_device_class_name);
mp_netlist->add_device_class (dev_cls);
}
ensure_circuit ();
read_device (dev_cls, db::DeviceClassInductor::param_id_L, ex);
} else if (ex.test_without_case ("m")) {
ensure_circuit ();
read_mos4_device (ex);
} else if (ex.test_without_case ("x")) {
ensure_circuit ();
read_subcircuit (ex);
ex.expect_end ();
} else {
char c = *ex.skip ();
if (c) {
warn (tl::sprintf (tl::to_string (tr ("Element type '%c' ignored")), c));
}
warn (tl::to_string (tr ("Line ignored")));
}
return false;
@ -393,88 +524,264 @@ db::Net *NetlistSpiceReader::make_net (const std::string &name)
return net;
}
void NetlistSpiceReader::read_subcircuit (tl::Extractor &ex)
void NetlistSpiceReader::read_pin_and_parameters (tl::Extractor &ex, std::vector<std::string> &nn, std::map<std::string, double> &pv)
{
std::string sc_name;
ex.read_word_or_quoted (sc_name, allowed_name_chars);
std::vector<std::string> nn;
std::map<std::string, double> pv;
bool in_params = false;
while (! ex.at_end ()) {
std::string n;
ex.read_word_or_quoted (n, allowed_name_chars);
if (ex.test_without_case ("params:")) {
in_params = true;
if (ex.test ("=")) {
// a parameter
pv.insert (std::make_pair (tl::to_upper_case (n), read_value (ex)));
} else {
nn.push_back (n);
std::string n = read_name (ex);
if (ex.test ("=")) {
// a parameter
pv.insert (std::make_pair (n, read_value (ex)));
} else {
if (in_params) {
error (tl::to_string (tr ("Missing '=' in parameter assignment")));
}
nn.push_back (n);
}
}
}
}
inline static int hex_num (char c)
{
if (c >= '0' && c <= '9') {
return (int (c - '0'));
} else if (c >= 'a' && c <= 'f') {
return (int (c - 'f') + 10);
} else {
return -1;
}
}
std::string NetlistSpiceReader::read_name_with_case (tl::Extractor &ex)
{
std::string n;
ex.read_word_or_quoted (n, allowed_name_chars);
std::string nn;
nn.reserve (n.size ());
const char *cp = n.c_str ();
while (*cp) {
if (*cp == '\\' && cp[1]) {
if (tolower (cp[1]) == 'x') {
cp += 2;
char c = 0;
for (int i = 0; i < 2 && *cp; ++i) {
int n = hex_num (*cp);
if (n >= 0) {
++cp;
c = c * 16 + char (n);
} else {
break;
}
}
nn += c;
} else {
++cp;
nn += *cp++;
}
} else {
nn += *cp++;
}
}
if (nn.empty ()) {
error (tl::to_string (tr ("No circuit name given for subcircuit call")));
}
if (! pv.empty ()) {
warn (tl::to_string (tr ("Circuit parameters are not allowed currently")));
return nn;
}
std::string NetlistSpiceReader::read_name (tl::Extractor &ex)
{
// TODO: allow configuring Spice reader as case sensitive?
// this is easy to do: just avoid to_upper here:
#if 1
return tl::to_upper_case (read_name_with_case (ex));
#else
return read_name_with_case (ex);
#endif
}
bool NetlistSpiceReader::read_element (tl::Extractor &ex, const std::string &element, const std::string &name)
{
// generic parse
std::vector<std::string> nn;
std::map<std::string, double> pv;
std::string model;
double value = 0.0;
// interpret the parameters according to the code
if (element == "X") {
// subcircuit call:
// Xname n1 n2 ... nn circuit [params]
read_pin_and_parameters (ex, nn, pv);
if (nn.empty ()) {
error (tl::to_string (tr ("No circuit name given for subcircuit call")));
}
model = nn.back ();
nn.pop_back ();
} else if (element == "R" || element == "C" || element == "L") {
// resistor, cap, inductor: two-terminal devices with a value
// Rname n1 n2 value
// Rname n1 n2 value model [params]
// Rname n1 n2 model [params]
// (same for C, L instead of R)
while (! ex.at_end () && nn.size () < 2) {
nn.push_back (read_name (ex));
}
if (nn.size () != 2) {
error (tl::to_string (tr ("Two-terminal device needs two nets")));
}
tl::Extractor ve (ex);
double vv = 0.0;
if (ve.try_read (vv) || ve.test ("(")) {
value = read_value (ex);
}
while (! ex.at_end ()) {
std::string n = read_name (ex);
if (ex.test ("=")) {
pv [n] = read_value (ex);
} else if (! model.empty ()) {
error (tl::sprintf (tl::to_string (tr ("Too many arguments for two-terminal device (additional argumen is '%s')")), n));
} else {
model = n;
}
}
} else {
// others: n-terminal devices with a model (last node)
while (! ex.at_end ()) {
std::string n = read_name (ex);
if (ex.test ("=")) {
pv [n] = read_value (ex);
} else {
nn.push_back (n);
}
}
if (nn.empty ()) {
error (tl::sprintf (tl::to_string (tr ("No model name given for element '%s'")), element));
}
model = nn.back ();
nn.pop_back ();
if (element == "M") {
if (nn.size () != 4) {
error (tl::to_string (tr ("'M' element must have four nodes")));
}
} else if (element == "Q") {
if (nn.size () != 3 && nn.size () != 4) {
error (tl::to_string (tr ("'Q' element must have three or four nodes")));
}
} else if (element == "D") {
if (nn.size () != 2) {
error (tl::to_string (tr ("'D' element must have two nodes")));
}
}
// TODO: other devices?
}
std::string nc = nn.back ();
nn.pop_back ();
std::vector<db::Net *> nets;
for (std::vector<std::string>::const_iterator i = nn.begin (); i != nn.end (); ++i) {
nets.push_back (make_net (*i));
}
if (nn.empty ()) {
if (element == "X" && ! subcircuit_captured (model)) {
if (! pv.empty ()) {
warn (tl::to_string (tr ("Circuit parameters are not allowed currently")));
}
read_subcircuit (name, model, nets);
return true;
} else {
return mp_delegate->element (mp_circuit, element, name, model, value, nets, pv);
}
}
void NetlistSpiceReader::read_subcircuit (const std::string &sc_name, const std::string &nc_name, const std::vector<db::Net *> &nets)
{
if (nets.empty ()) {
error (tl::to_string (tr ("A circuit call needs at least one net")));
}
db::Circuit *cc = mp_netlist->circuit_by_name (nc);
db::Circuit *cc = mp_netlist->circuit_by_name (nc_name);
if (! cc) {
cc = new db::Circuit ();
mp_netlist->add_circuit (cc);
cc->set_name (nc);
for (std::vector<std::string>::const_iterator i = nn.begin (); i != nn.end (); ++i) {
cc->set_name (nc_name);
for (std::vector<db::Net *>::const_iterator i = nets.begin (); i != nets.end (); ++i) {
cc->add_pin (std::string ());
}
} else {
if (cc->pin_count () != nn.size ()) {
error (tl::sprintf (tl::to_string (tr ("Pin count mismatch between circuit definition and circuit call: %d expected, got %d")), int (cc->pin_count ()), int (nn.size ())));
if (cc->pin_count () != nets.size ()) {
error (tl::sprintf (tl::to_string (tr ("Pin count mismatch between circuit definition and circuit call: %d expected, got %d")), int (cc->pin_count ()), int (nets.size ())));
}
}
db::SubCircuit *sc = new db::SubCircuit (cc, sc_name);
mp_circuit->add_subcircuit (sc);
for (std::vector<std::string>::const_iterator i = nn.begin (); i != nn.end (); ++i) {
db::Net *net = make_net (*i);
sc->connect_pin (i - nn.begin (), net);
for (std::vector<db::Net *>::const_iterator i = nets.begin (); i != nets.end (); ++i) {
sc->connect_pin (i - nets.begin (), *i);
}
ex.expect_end ();
}
void NetlistSpiceReader::read_circuit (tl::Extractor &ex)
void NetlistSpiceReader::skip_circuit (tl::Extractor & /*ex*/)
{
std::string nc;
ex.read_word_or_quoted (nc, allowed_name_chars);
while (! at_end ()) {
std::vector<std::string> nn;
std::map<std::string, double> pv;
std::string l = get_line ();
tl::Extractor ex (l.c_str ());
if (ex.test_without_case (".")) {
while (! ex.at_end ()) {
// control statement
if (ex.test_without_case ("subckt")) {
skip_circuit (ex);
} else if (ex.test_without_case ("ends")) {
break;
}
std::string n;
ex.read_word_or_quoted (n, allowed_name_chars);
if (ex.test ("=")) {
// a parameter
pv.insert (std::make_pair (tl::to_upper_case (n), read_value (ex)));
} else {
nn.push_back (n);
}
}
}
void NetlistSpiceReader::read_circuit (tl::Extractor &ex, const std::string &nc)
{
std::vector<std::string> nn;
std::map<std::string, double> pv;
read_pin_and_parameters (ex, nn, pv);
if (! pv.empty ()) {
warn (tl::to_string (tr ("Circuit parameters are not allowed currently")));
@ -501,11 +808,15 @@ void NetlistSpiceReader::read_circuit (tl::Extractor &ex)
for (std::vector<std::string>::const_iterator i = nn.begin (); i != nn.end (); ++i) {
db::Net *net = make_net (*i);
// use the net name to name the pin (otherwise SPICE pins are always unnamed)
if (! i->empty ()) {
mp_circuit->rename_pin (i - nn.begin (), net->name ());
}
mp_circuit->connect_pin (i - nn.begin (), net);
}
while (! at_end ()) {
if (read_element ()) {
if (read_card ()) {
break;
}
}
@ -516,102 +827,4 @@ void NetlistSpiceReader::read_circuit (tl::Extractor &ex)
ex.expect_end ();
}
void NetlistSpiceReader::read_device (db::DeviceClass *dev_cls, size_t param_id, tl::Extractor &ex)
{
std::string dn;
ex.read_word_or_quoted (dn, allowed_name_chars);
std::vector<std::string> nn;
while (! ex.at_end () && nn.size () < 2) {
nn.push_back (std::string ());
ex.read_word_or_quoted (nn.back (), allowed_name_chars);
}
if (nn.size () != 2) {
error (tl::to_string (tr ("Two-terminal device needs two nets")));
}
double v = read_value (ex);
db::Device *dev = new db::Device (dev_cls, dn);
mp_circuit->add_device (dev);
for (std::vector<std::string>::const_iterator i = nn.begin (); i != nn.end (); ++i) {
db::Net *net = make_net (*i);
dev->connect_terminal (i - nn.begin (), net);
}
dev->set_parameter_value (param_id, v);
ex.expect_end ();
}
void NetlistSpiceReader::read_mos4_device (tl::Extractor &ex)
{
std::string dn;
ex.read_word_or_quoted (dn, allowed_name_chars);
std::vector<std::string> nn;
std::map<std::string, double> pv;
while (! ex.at_end ()) {
std::string n;
ex.read_word_or_quoted (n, allowed_name_chars);
if (ex.test ("=")) {
// a parameter
pv.insert (std::make_pair (tl::to_upper_case (n), read_value (ex)));
} else {
nn.push_back (n);
}
}
if (nn.empty ()) {
error (tl::to_string (tr ("No model name given for MOS transistor element")));
}
std::string mn = nn.back ();
nn.pop_back ();
if (nn.size () != 4) {
error (tl::to_string (tr ("A MOS transistor needs four nets")));
}
db::DeviceClass *dev_cls = mp_netlist->device_class_by_name (mn);
if (! dev_cls) {
dev_cls = new db::DeviceClassMOS4Transistor ();
dev_cls->set_name (mn);
mp_netlist->add_device_class (dev_cls);
}
db::Device *dev = new db::Device (dev_cls, dn);
mp_circuit->add_device (dev);
for (std::vector<std::string>::const_iterator i = nn.begin (); i != nn.end (); ++i) {
db::Net *net = make_net (*i);
dev->connect_terminal (i - nn.begin (), net);
}
const std::vector<db::DeviceParameterDefinition> &pd = dev_cls->parameter_definitions ();
for (std::vector<db::DeviceParameterDefinition>::const_iterator i = pd.begin (); i != pd.end (); ++i) {
std::map<std::string, double>::const_iterator v = pv.find (i->name ());
if (v != pv.end ()) {
// by conventions, dimensions are in micrometer
if (i->id () == db::DeviceClassMOS4Transistor::param_id_AD || i->id () == db::DeviceClassMOS4Transistor::param_id_AS) {
dev->set_parameter_value (i->id (), v->second * 1e12);
} else if (i->id () == db::DeviceClassMOS4Transistor::param_id_W
|| i->id () == db::DeviceClassMOS4Transistor::param_id_L
|| i->id () == db::DeviceClassMOS4Transistor::param_id_PD
|| i->id () == db::DeviceClassMOS4Transistor::param_id_PS) {
dev->set_parameter_value (i->id (), v->second * 1e6);
}
}
}
ex.expect_end ();
}
}

View File

@ -37,6 +37,75 @@ class Netlist;
class Net;
class Circuit;
class DeviceClass;
class Device;
/**
* @brief A specialized exception class to handle netlist reader delegate errors
*/
class DB_PUBLIC NetlistSpiceReaderDelegateError
: public tl::Exception
{
public:
NetlistSpiceReaderDelegateError (const std::string &msg)
: tl::Exception (msg)
{ }
};
/**
* @brief A delegate to handle various forms of devices and translates them
*
* The reader delegate can be configured to recieve subcircuit elements too.
* In this case, parameters are allowed.
* For receiving subcircuit elements, the delegate needs to indicate
* this by returning true upon "wants_subcircuit".
*/
class DB_PUBLIC NetlistSpiceReaderDelegate
: public tl::Object
{
public:
NetlistSpiceReaderDelegate ();
virtual ~NetlistSpiceReaderDelegate ();
/**
* @brief Called when the netlist reading starts
*/
virtual void start (db::Netlist *netlist);
/**
* @brief Called when the netlist reading ends
*/
virtual void finish (db::Netlist *netlist);
/**
* @brief Returns true, if the delegate wants subcircuit elements with this name
*
* The name is always upper case.
*/
virtual bool wants_subcircuit (const std::string &circuit_name);
/**
* @brief Makes a device from an element line
*
* @param circuit The circuit that is currently read.
* @param element The upper-case element code ("M", "R", ...).
* @param name The element's name.
* @param model The upper-case model name (may be empty).
* @param value The default value (e.g. registance for resistors) and may be zero.
* @param nets The nets given in the element line.
* @param parameters The parameters of the element statement (parameter names are upper case).
*
* The default implementation will create corresponding devices for
* some known elements using the Spice writer's parameter conventions.
*
* This method returns true, if the element was read.
*/
virtual bool element (db::Circuit *circuit, const std::string &element, const std::string &name, const std::string &model, double value, const std::vector<db::Net *> &nets, const std::map<std::string, double> &params);
/**
* @brief Produces an error with the given message
*/
virtual void error (const std::string &msg);
};
/**
* @brief A SPICE format reader for netlists
@ -45,7 +114,7 @@ class DB_PUBLIC NetlistSpiceReader
: public NetlistReader
{
public:
NetlistSpiceReader ();
NetlistSpiceReader (NetlistSpiceReaderDelegate *delegate = 0);
virtual ~NetlistSpiceReader ();
virtual void read (tl::InputStream &stream, db::Netlist &netlist);
@ -54,19 +123,24 @@ private:
db::Netlist *mp_netlist;
db::Circuit *mp_circuit;
std::auto_ptr<tl::TextInputStream> mp_stream;
tl::weak_ptr<NetlistSpiceReaderDelegate> mp_delegate;
std::vector<std::pair<tl::InputStream *, tl::TextInputStream *> > m_streams;
std::auto_ptr<std::map<std::string, db::Net *> > mp_nets_by_name;
std::string m_stored_line;
std::map<std::string, bool> m_captured;
void push_stream (const std::string &path);
void pop_stream ();
bool at_end ();
void read_subcircuit (tl::Extractor &ex);
void read_circuit (tl::Extractor &ex);
void read_device (db::DeviceClass *dev_cls, size_t param_id, tl::Extractor &ex);
void read_mos4_device (tl::Extractor &ex);
bool read_element ();
void read_pin_and_parameters (tl::Extractor &ex, std::vector<std::string> &nn, std::map<std::string, double> &pv);
bool read_element (tl::Extractor &ex, const std::string &element, const std::string &name);
void read_subcircuit (const std::string &sc_name, const std::string &nc_name, const std::vector<db::Net *> &nets);
void read_circuit (tl::Extractor &ex, const std::string &name);
void skip_circuit (tl::Extractor &ex);
bool read_card ();
double read_value (tl::Extractor &ex);
std::string read_name_with_case (tl::Extractor &ex);
std::string read_name (tl::Extractor &ex);
double read_atomic_value (tl::Extractor &ex);
double read_dot_expr (tl::Extractor &ex);
double read_bar_expr (tl::Extractor &ex);
@ -77,6 +151,7 @@ private:
void finish ();
db::Net *make_net (const std::string &name);
void ensure_circuit ();
bool subcircuit_captured (const std::string &nc_name);
};
}

View File

@ -25,13 +25,16 @@
#include "dbNetlistDeviceClasses.h"
#include "tlStream.h"
#include "tlUniqueName.h"
#include <sstream>
#include <set>
namespace db
{
static const char *allowed_name_chars = "_.:,!+$/&\\#[]";
static const char *allowed_name_chars = "_.:,!+$/&\\#[]<>";
static const char *not_connect_prefix = "nc_";
// --------------------------------------------------------------------------------
@ -94,6 +97,8 @@ void NetlistSpiceWriterDelegate::write_device (const db::Device &dev) const
const db::DeviceClassDiode *diode = dynamic_cast<const db::DeviceClassDiode *> (dc);
const db::DeviceClassMOS3Transistor *mos3 = dynamic_cast<const db::DeviceClassMOS3Transistor *> (dc);
const db::DeviceClassMOS4Transistor *mos4 = dynamic_cast<const db::DeviceClassMOS4Transistor *> (dc);
const db::DeviceClassBJT3Transistor *bjt3 = dynamic_cast<const db::DeviceClassBJT3Transistor *> (dc);
const db::DeviceClassBJT3Transistor *bjt4 = dynamic_cast<const db::DeviceClassBJT4Transistor *> (dc);
std::ostringstream os;
@ -127,9 +132,10 @@ void NetlistSpiceWriterDelegate::write_device (const db::Device &dev) const
os << format_name (dev.expanded_name ());
os << format_terminals (dev);
// Use "D" + device class name for the model
os << " D";
// Use device class name for the model
os << " ";
os << format_name (dev.device_class ()->name ());
os << format_params (dev);
} else if (mos3 || mos4) {
@ -146,13 +152,18 @@ void NetlistSpiceWriterDelegate::write_device (const db::Device &dev) const
// Use device class name for the model
os << " ";
os << format_name (dev.device_class ()->name ());
os << format_params (dev);
os << " L=" << tl::sprintf ("%.12gU", dev.parameter_value (db::DeviceClassMOS3Transistor::param_id_L));
os << " W=" << tl::sprintf ("%.12gU", dev.parameter_value (db::DeviceClassMOS3Transistor::param_id_W));
os << " AS=" << tl::sprintf ("%.12gP", dev.parameter_value (db::DeviceClassMOS3Transistor::param_id_AS));
os << " AD=" << tl::sprintf ("%.12gP", dev.parameter_value (db::DeviceClassMOS3Transistor::param_id_AD));
os << " PS=" << tl::sprintf ("%.12gU", dev.parameter_value (db::DeviceClassMOS3Transistor::param_id_PS));
os << " PD=" << tl::sprintf ("%.12gU", dev.parameter_value (db::DeviceClassMOS3Transistor::param_id_PD));
} else if (bjt3 || bjt4) {
os << "Q";
os << format_name (dev.expanded_name ());
os << format_terminals (dev);
// Use device class name for the model
os << " ";
os << format_name (dev.device_class ()->name ());
os << format_params (dev);
} else {
@ -181,13 +192,24 @@ std::string NetlistSpiceWriterDelegate::format_terminals (const db::Device &dev)
return os.str ();
}
std::string NetlistSpiceWriterDelegate::format_params (const db::Device &dev) const
std::string NetlistSpiceWriterDelegate::format_params (const db::Device &dev, size_t without_id) const
{
std::ostringstream os;
const std::vector<db::DeviceParameterDefinition> &pd = dev.device_class ()->parameter_definitions ();
for (std::vector<db::DeviceParameterDefinition>::const_iterator i = pd.begin (); i != pd.end (); ++i) {
os << " " << i->name () << "=" << tl::to_string (dev.parameter_value (i->id ()));
if (i->id () != without_id) {
double sis = i->si_scaling ();
os << " " << i->name () << "=";
// for compatibility
if (fabs (sis * 1e6 - 1.0) < 1e-10) {
os << tl::to_string (dev.parameter_value (i->id ())) << "U";
} else if (fabs (sis * 1e12 - 1.0) < 1e-10) {
os << tl::to_string (dev.parameter_value (i->id ())) << "P";
} else {
os << tl::to_string (dev.parameter_value (i->id ()) * sis);
}
}
}
return os.str ();
@ -196,7 +218,7 @@ std::string NetlistSpiceWriterDelegate::format_params (const db::Device &dev) co
// --------------------------------------------------------------------------------
NetlistSpiceWriter::NetlistSpiceWriter (NetlistSpiceWriterDelegate *delegate)
: mp_netlist (0), mp_stream (0), mp_delegate (delegate), m_use_net_names (false)
: mp_netlist (0), mp_stream (0), mp_delegate (delegate), m_next_net_id (0), m_use_net_names (false), m_with_comments (true)
{
static NetlistSpiceWriterDelegate std_delegate;
if (! delegate) {
@ -214,6 +236,11 @@ void NetlistSpiceWriter::set_use_net_names (bool use_net_names)
m_use_net_names = use_net_names;
}
void NetlistSpiceWriter::set_with_comments (bool with_comments)
{
m_with_comments = with_comments;
}
void NetlistSpiceWriter::write (tl::OutputStream &stream, const db::Netlist &netlist, const std::string &description)
{
mp_stream = &stream;
@ -244,7 +271,7 @@ std::string NetlistSpiceWriter::net_to_string (const db::Net *net) const
if (! net) {
return "0";
return std::string (not_connect_prefix) + tl::to_string (++m_next_net_id);
} else {
@ -252,7 +279,10 @@ std::string NetlistSpiceWriter::net_to_string (const db::Net *net) const
// It does not like: , ;
// We translate , to | for the net separator
std::string n = net->expanded_name ();
std::map<const db::Net *, std::string>::const_iterator ni = m_net_to_spice_name.find (net);
tl_assert (ni != m_net_to_spice_name.end ());
const std::string &n = ni->second;
std::string nn;
nn.reserve (n.size () + 1);
if (!isalnum (*n.c_str ())) {
@ -276,8 +306,7 @@ std::string NetlistSpiceWriter::net_to_string (const db::Net *net) const
std::map<const db::Net *, size_t>::const_iterator n = m_net_to_spice_id.find (net);
if (! net || n == m_net_to_spice_id.end ()) {
// TODO: this should assert or similar
return "0";
return tl::to_string (++m_next_net_id);
} else {
return tl::to_string (n->second);
}
@ -360,13 +389,41 @@ void NetlistSpiceWriter::do_write (const std::string &description)
for (db::Netlist::const_top_down_circuit_iterator c = mp_netlist->begin_top_down (); c != mp_netlist->end_top_down (); ++c) {
const db::Circuit &circuit = **c;
const db::Circuit &circuit = *c;
// assign internal node numbers to the nets
m_net_to_spice_id.clear ();
size_t nid = 0;
for (db::Circuit::const_net_iterator n = circuit.begin_nets (); n != circuit.end_nets (); ++n) {
m_net_to_spice_id.insert (std::make_pair (n.operator-> (), ++nid));
m_net_to_spice_name.clear ();
m_next_net_id = 0;
if (! m_use_net_names) {
for (db::Circuit::const_net_iterator n = circuit.begin_nets (); n != circuit.end_nets (); ++n) {
m_net_to_spice_id.insert (std::make_pair (n.operator-> (), ++m_next_net_id));
}
} else {
// create unique names for those nets with a name
std::set<std::string> names;
for (db::Circuit::const_net_iterator n = circuit.begin_nets (); n != circuit.end_nets (); ++n) {
std::string nn = tl::unique_name (n->expanded_name (), names);
names.insert (nn);
m_net_to_spice_name.insert (std::make_pair (n.operator-> (), nn));
}
// determine the next net id for non-connected nets such that there is no clash with
// existing names
size_t prefix_len = strlen (not_connect_prefix);
for (std::set<std::string>::const_iterator n = names.begin (); n != names.end (); ++n) {
if (n->find (not_connect_prefix) == 0 && n->size () > prefix_len) {
size_t num = 0;
tl::from_string (n->c_str () + prefix_len, num);
m_next_net_id = std::max (m_next_net_id, num);
}
}
}
write_circuit_header (circuit);
@ -377,9 +434,10 @@ void NetlistSpiceWriter::do_write (const std::string &description)
for (db::Circuit::const_device_iterator i = circuit.begin_devices (); i != circuit.end_devices (); ++i) {
// TODO: make this configurable?
std::string comment = "device instance " + i->expanded_name () + " " + i->position ().to_string () + " " + i->device_class ()->name ();
emit_comment (comment);
if (m_with_comments) {
std::string comment = "device instance " + i->expanded_name () + " " + i->trans ().to_string () + " " + i->device_class ()->name ();
emit_comment (comment);
}
mp_delegate->write_device (*i);
@ -392,9 +450,10 @@ void NetlistSpiceWriter::do_write (const std::string &description)
void NetlistSpiceWriter::write_subcircuit_call (const db::SubCircuit &subcircuit) const
{
// TODO: make this configurable?
std::string comment = "cell instance " + subcircuit.expanded_name() + " " + subcircuit.trans ().to_string ();
emit_comment (comment);
if (m_with_comments) {
std::string comment = "cell instance " + subcircuit.expanded_name() + " " + subcircuit.trans ().to_string ();
emit_comment (comment);
}
std::ostringstream os;
os << "X";
@ -415,9 +474,11 @@ void NetlistSpiceWriter::write_circuit_header (const db::Circuit &circuit) const
{
emit_line ("");
emit_comment ("cell " + circuit.name ());
for (db::Circuit::const_pin_iterator p = circuit.begin_pins (); p != circuit.end_pins (); ++p) {
emit_comment ("pin " + p->name ());
if (m_with_comments) {
emit_comment ("cell " + circuit.name ());
for (db::Circuit::const_pin_iterator p = circuit.begin_pins (); p != circuit.end_pins (); ++p) {
emit_comment ("pin " + p->name ());
}
}
std::ostringstream os;
@ -432,7 +493,7 @@ void NetlistSpiceWriter::write_circuit_header (const db::Circuit &circuit) const
emit_line (os.str ());
if (! m_use_net_names) {
if (! m_use_net_names && m_with_comments) {
for (db::Circuit::const_net_iterator n = circuit.begin_nets (); n != circuit.end_nets (); ++n) {
if (! n->name ().empty ()) {
emit_comment ("net " + net_to_string (n.operator-> ()) + " " + n->name ());

View File

@ -29,6 +29,7 @@
#include <string>
#include <map>
#include <limits>
namespace db
{
@ -62,7 +63,7 @@ public:
void emit_comment (const std::string &comment) const;
std::string format_name (const std::string &s) const;
std::string format_terminals (const db::Device &dev) const;
std::string format_params (const db::Device &dev) const;
std::string format_params (const db::Device &dev, size_t without_id = std::numeric_limits<size_t>::max ()) const;
private:
friend class NetlistSpiceWriter;
@ -92,6 +93,12 @@ public:
return m_use_net_names;
}
void set_with_comments (bool f);
bool with_comments () const
{
return m_with_comments;
}
private:
friend class NetlistSpiceWriterDelegate;
@ -99,7 +106,10 @@ private:
tl::OutputStream *mp_stream;
tl::weak_ptr<NetlistSpiceWriterDelegate> mp_delegate;
std::map<const db::Net *, size_t> m_net_to_spice_id;
std::map<const db::Net *, std::string> m_net_to_spice_name;
mutable size_t m_next_net_id;
bool m_use_net_names;
bool m_with_comments;
void do_write (const std::string &description);

View File

@ -80,6 +80,11 @@ private:
{
m_id = id;
}
void set_name (const std::string &name)
{
m_name = name;
}
};
}

View File

@ -232,6 +232,13 @@ Region::Region (const RecursiveShapeIterator &si, DeepShapeStore &dss, const db:
mp_delegate = new DeepRegion (si, dss, trans, merged_semantics, area_ratio, max_vertex_count);
}
Region::Region (DeepShapeStore &dss)
{
tl_assert (dss.is_singular ());
unsigned int layout_index = 0; // singular layout index
mp_delegate = new db::DeepRegion (db::DeepLayer (&dss, layout_index, dss.layout (layout_index).insert_layer ()));
}
const db::RecursiveShapeIterator &
Region::iter () const
{

View File

@ -345,6 +345,12 @@ public:
*/
explicit Region (const RecursiveShapeIterator &si, DeepShapeStore &dss, const db::ICplxTrans &trans, bool merged_semantics = true, double area_ratio = 3.0, size_t max_vertex_count = 16);
/**
* @brief Creates a new empty layer inside the dss
* This method requires the DSS to be singular.
*/
explicit Region (DeepShapeStore &dss);
/**
* @brief Gets the underlying delegate object
*/

View File

@ -21,9 +21,8 @@
*/
#include "gsiDecl.h"
#include "gsiEnums.h"
#include "dbLayoutToNetlist.h"
#include "dbLayoutToNetlistWriter.h"
#include "dbLayoutToNetlistReader.h"
#include "tlStream.h"
#include "tlVariant.h"
@ -67,33 +66,27 @@ static db::Cell *l2n_internal_top_cell (db::LayoutToNetlist *l2n)
return const_cast<db::Cell *> (l2n->internal_top_cell ());
}
static void build_net (const db::LayoutToNetlist *l2n, const db::Net &net, db::Layout &target, db::Cell &target_cell, const std::map<unsigned int, const db::Region *> &lmap, const tl::Variant &circuit_cell_name_prefix, const tl::Variant &device_cell_name_prefix)
static void build_net (const db::LayoutToNetlist *l2n, const db::Net &net, db::Layout &target, db::Cell &target_cell, const std::map<unsigned int, const db::Region *> &lmap, const tl::Variant &netname_prop, db::LayoutToNetlist::BuildNetHierarchyMode hier_mode, const tl::Variant &circuit_cell_name_prefix, const tl::Variant &device_cell_name_prefix)
{
std::string p = circuit_cell_name_prefix.to_string ();
std::string dp = device_cell_name_prefix.to_string ();
l2n->build_net (net, target, target_cell, lmap, circuit_cell_name_prefix.is_nil () ? 0 : p.c_str (), device_cell_name_prefix.is_nil () ? 0 : dp.c_str ());
l2n->build_net (net, target, target_cell, lmap, netname_prop, hier_mode, circuit_cell_name_prefix.is_nil () ? 0 : p.c_str (), device_cell_name_prefix.is_nil () ? 0 : dp.c_str ());
}
static void build_all_nets (const db::LayoutToNetlist *l2n, const db::CellMapping &cmap, db::Layout &target, const std::map<unsigned int, const db::Region *> &lmap, const tl::Variant &net_cell_name_prefix, const tl::Variant &circuit_cell_name_prefix, const tl::Variant &device_cell_name_prefix)
static void build_all_nets (const db::LayoutToNetlist *l2n, const db::CellMapping &cmap, db::Layout &target, const std::map<unsigned int, const db::Region *> &lmap, const tl::Variant &net_cell_name_prefix, const tl::Variant &netname_prop, db::LayoutToNetlist::BuildNetHierarchyMode hier_mode, const tl::Variant &circuit_cell_name_prefix, const tl::Variant &device_cell_name_prefix)
{
std::string cp = circuit_cell_name_prefix.to_string ();
std::string np = net_cell_name_prefix.to_string ();
std::string dp = device_cell_name_prefix.to_string ();
l2n->build_all_nets (cmap, target, lmap, net_cell_name_prefix.is_nil () ? 0 : np.c_str (), circuit_cell_name_prefix.is_nil () ? 0 : cp.c_str (), device_cell_name_prefix.is_nil () ? 0 : dp.c_str ());
l2n->build_all_nets (cmap, target, lmap, net_cell_name_prefix.is_nil () ? 0 : np.c_str (), netname_prop, hier_mode, circuit_cell_name_prefix.is_nil () ? 0 : cp.c_str (), device_cell_name_prefix.is_nil () ? 0 : dp.c_str ());
}
static void write_l2n (const db::LayoutToNetlist *l2n, const std::string &path, bool short_format)
static void build_nets (const db::LayoutToNetlist *l2n, const std::vector<const db::Net *> &nets, const db::CellMapping &cmap, db::Layout &target, const std::map<unsigned int, const db::Region *> &lmap, const tl::Variant &net_cell_name_prefix, const tl::Variant &netname_prop, db::LayoutToNetlist::BuildNetHierarchyMode hier_mode, const tl::Variant &circuit_cell_name_prefix, const tl::Variant &device_cell_name_prefix)
{
tl::OutputStream stream (path);
db::LayoutToNetlistStandardWriter writer (stream, short_format);
writer.write (l2n);
}
static void read_l2n (db::LayoutToNetlist *l2n, const std::string &path)
{
tl::InputStream stream (path);
db::LayoutToNetlistStandardReader reader (stream);
reader.read (l2n);
std::string cp = circuit_cell_name_prefix.to_string ();
std::string np = net_cell_name_prefix.to_string ();
std::string dp = device_cell_name_prefix.to_string ();
l2n->build_nets (&nets, cmap, target, lmap, net_cell_name_prefix.is_nil () ? 0 : np.c_str (), netname_prop, hier_mode, circuit_cell_name_prefix.is_nil () ? 0 : cp.c_str (), device_cell_name_prefix.is_nil () ? 0 : dp.c_str ());
}
static std::vector<std::string> l2n_layer_names (const db::LayoutToNetlist *l2n)
@ -154,7 +147,9 @@ Class<db::LayoutToNetlist> decl_dbLayoutToNetlist ("db", "LayoutToNetlist",
"add layers (regions) inside the 'dss' object.\n"
"\n"
"The make_... methods will not create new layers as there is no particular place "
"defined where to create the layers."
"defined where to create the layers.\n"
"\n"
"The extractor will not take ownership of the dss object unless you call \\keep_dss."
) +
gsi::constructor ("new", &make_l2n_from_existing_dss_with_layout, gsi::arg ("dss"), gsi::arg ("layout_index"),
"@brief Creates a new extractor object reusing an existing \\DeepShapeStore object\n"
@ -174,6 +169,12 @@ Class<db::LayoutToNetlist> decl_dbLayoutToNetlist ("db", "LayoutToNetlist",
"The database unit is mandatory because the physical parameter extraction "
"for devices requires this unit for translation of layout to physical dimensions.\n"
) +
gsi::method ("dss", (db::DeepShapeStore &(db::LayoutToNetlist::*) ()) &db::LayoutToNetlist::dss,
"@brief Gets a reference to the internal DSS object.\n"
) +
gsi::method ("keep_dss", &db::LayoutToNetlist::keep_dss,
"@brief Resumes ownership over the DSS object if created with an external one.\n"
) +
gsi::method ("threads=", &db::LayoutToNetlist::set_threads, gsi::arg ("n"),
"@brief Sets the number of threads to use for operations which support multiple threads\n"
) +
@ -197,11 +198,34 @@ Class<db::LayoutToNetlist> decl_dbLayoutToNetlist ("db", "LayoutToNetlist",
gsi::method ("max_vertex_count", &db::LayoutToNetlist::max_vertex_count,
"See \\max_vertex_count= for details about this attribute."
) +
gsi::method ("name", (std::string (db::LayoutToNetlist::*) (const db::Region &region) const) &db::LayoutToNetlist::name, gsi::arg ("l"),
"@brief Get the name of the given layer\n"
gsi::method ("name", (const std::string &(db::LayoutToNetlist::*) () const) &db::LayoutToNetlist::name,
"@brief Gets the name of the database\n"
) +
gsi::method ("name", (std::string (db::LayoutToNetlist::*) (unsigned int) const) &db::LayoutToNetlist::name, gsi::arg ("l"),
"@brief Get the name of the given layer (by index)\n"
gsi::method ("name=", &db::LayoutToNetlist::set_name,
"@brief Sets the name of the database\n"
) +
gsi::method ("description", (const std::string &(db::LayoutToNetlist::*) () const) &db::LayoutToNetlist::name,
"@brief Gets the description of the database\n"
) +
gsi::method ("description=", &db::LayoutToNetlist::set_name,
"@brief Sets the description of the database\n"
) +
gsi::method ("filename", &db::LayoutToNetlist::filename,
"@brief Gets the file name of the database\n"
"The filename is the name under which the database is stored or empty if it is not associated with a file."
) +
gsi::method ("original_file", &db::LayoutToNetlist::original_file,
"@brief Gets the original file name of the database\n"
"The original filename is the layout file from which the netlist DB was created."
) +
gsi::method ("original_file=", &db::LayoutToNetlist::set_original_file,
"@brief Sets the original file name of the database\n"
) +
gsi::method ("layer_name", (std::string (db::LayoutToNetlist::*) (const db::Region &region) const) &db::LayoutToNetlist::name, gsi::arg ("l"),
"@brief Gets the name of the given layer\n"
) +
gsi::method ("layer_name", (std::string (db::LayoutToNetlist::*) (unsigned int) const) &db::LayoutToNetlist::name, gsi::arg ("l"),
"@brief Gets the name of the given layer (by index)\n"
) +
gsi::method ("register", (void (db::LayoutToNetlist::*) (const db::Region &region, const std::string &)) &db::LayoutToNetlist::register_layer, gsi::arg ("l"), gsi::arg ("n"),
"@brief Names the given layer\n"
@ -210,8 +234,9 @@ Class<db::LayoutToNetlist> decl_dbLayoutToNetlist ("db", "LayoutToNetlist",
"\n"
"Naming a layer allows the system to indicate the layer in various contexts, i.e. "
"when writing the data to a file. Named layers are also persisted inside the LayoutToNetlist object. "
"They are not discarded when the Region object is destroyed. Only named layers can be put into "
"\\connect.\n"
"They are not discarded when the Region object is destroyed.\n"
"\n"
"If required, the system will assign a name automatically."
) +
gsi::method_ext ("layer_names", &l2n_layer_names,
"@brief Returns a list of names of the layer kept inside the LayoutToNetlist object."
@ -237,7 +262,7 @@ Class<db::LayoutToNetlist> decl_dbLayoutToNetlist ("db", "LayoutToNetlist",
"The name is optional. If given, the layer will already be named accordingly (see \\register).\n"
) +
gsi::factory ("make_layer", (db::Region *(db::LayoutToNetlist::*) (unsigned int, const std::string &)) &db::LayoutToNetlist::make_layer, gsi::arg ("layer_index"), gsi::arg ("name", std::string ()),
"@brief Creates a new hierarchical region representing an original layer\n"
"@brief Creates a new hierarchical region reprfesenting an original layer\n"
"'layer_index' is the layer index of the desired layer in the original layout.\n"
"This variant produces polygons and takes texts for net name annotation.\n"
"A variant not taking texts is \\make_polygon_layer. A Variant only taking\n"
@ -322,11 +347,21 @@ Class<db::LayoutToNetlist> decl_dbLayoutToNetlist ("db", "LayoutToNetlist",
"This method is required to derive the internal layer index - for example for\n"
"investigating the cluster tree.\n"
) +
gsi::method ("cell_mapping_into", &db::LayoutToNetlist::cell_mapping_into, gsi::arg ("layout"), gsi::arg ("cell"), gsi::arg ("with_device_cells", false),
gsi::method ("cell_mapping_into", (db::CellMapping (db::LayoutToNetlist::*) (db::Layout &, db::Cell &, bool)) &db::LayoutToNetlist::cell_mapping_into, gsi::arg ("layout"), gsi::arg ("cell"), gsi::arg ("with_device_cells", false),
"@brief Creates a cell mapping for copying shapes from the internal layout to the given target layout.\n"
"If 'with_device_cells' is true, cells will be produced for devices. These are cells not corresponding to circuits, so they are disabled normally.\n"
"Use this option, if you want to access device terminal shapes per device.\n"
"CAUTION: this function may create new cells in 'layout'.\n"
"\n"
"CAUTION: this function may create new cells in 'layout'. Use \\const_cell_mapping_into if you want to use the target layout's hierarchy and not modify it.\n"
) +
gsi::method ("cell_mapping_into", (db::CellMapping (db::LayoutToNetlist::*) (db::Layout &, db::Cell &, const std::vector<const db::Net *> &, bool)) &db::LayoutToNetlist::cell_mapping_into, gsi::arg ("layout"), gsi::arg ("cell"), gsi::arg ("nets"), gsi::arg ("with_device_cells", false),
"@brief Creates a cell mapping for copying shapes from the internal layout to the given target layout.\n"
"This version will only create cells which are required to represent the nets from the 'nets' argument.\n"
"\n"
"If 'with_device_cells' is true, cells will be produced for devices. These are cells not corresponding to circuits, so they are disabled normally.\n"
"Use this option, if you want to access device terminal shapes per device.\n"
"\n"
"CAUTION: this function may create new cells in 'layout'. Use \\const_cell_mapping_into if you want to use the target layout's hierarchy and not modify it.\n"
) +
gsi::method ("const_cell_mapping_into", &db::LayoutToNetlist::const_cell_mapping_into, gsi::arg ("layout"), gsi::arg ("cell"),
"@brief Creates a cell mapping for copying shapes from the internal layout to the given target layout.\n"
@ -341,66 +376,86 @@ Class<db::LayoutToNetlist> decl_dbLayoutToNetlist ("db", "LayoutToNetlist",
"If 'recursive'' is true, the returned region will contain the shapes of\n"
"all subcircuits too.\n"
) +
gsi::method ("shapes_of_net", (void (db::LayoutToNetlist::*) (const db::Net &, const db::Region &, bool, db::Shapes &) const) &db::LayoutToNetlist::shapes_of_net, gsi::arg ("net"), gsi::arg ("of_layer"), gsi::arg ("recursive"), gsi::arg ("to"),
gsi::method ("shapes_of_net", (void (db::LayoutToNetlist::*) (const db::Net &, const db::Region &, bool, db::Shapes &, db::properties_id_type) const) &db::LayoutToNetlist::shapes_of_net, gsi::arg ("net"), gsi::arg ("of_layer"), gsi::arg ("recursive"), gsi::arg ("to"), gsi::arg ("propid", db::properties_id_type (0), "0"),
"@brief Sends all shapes of a specific net and layer to the given Shapes container.\n"
"If 'recursive'' is true, the returned region will contain the shapes of\n"
"all subcircuits too.\n"
"\"prop_id\" is an optional properties ID. If given, this property set will be attached to the shapes."
) +
gsi::method_ext ("build_net", &build_net, gsi::arg ("net"), gsi::arg ("target"), gsi::arg ("target_cell"), gsi::arg ("lmap"), gsi::arg ("circuit_cell_name_prefix", tl::Variant (), "nil"), gsi::arg ("device_cell_name_prefix", tl::Variant (), "nil"),
gsi::method_ext ("build_net", &build_net, gsi::arg ("net"), gsi::arg ("target"), gsi::arg ("target_cell"), gsi::arg ("lmap"), gsi::arg ("netname_prop", tl::Variant (), "nil"), gsi::arg ("hier_mode", db::LayoutToNetlist::BNH_Flatten, "BNH_Flatten"), gsi::arg ("circuit_cell_name_prefix", tl::Variant (), "nil"), gsi::arg ("device_cell_name_prefix", tl::Variant (), "nil"),
"@brief Builds a net representation in the given layout and cell\n"
"\n"
"This method has two modes: recursive and top-level mode. In recursive mode,\n"
"it will create a proper hierarchy below the given target cell to hold all subcircuits the\n"
"net connects to. It will copy the net's parts from this subcircuits into these cells.\n"
"This method puts the shapes of a net into the given target cell using a variety of options\n"
"to represent the net name and the hierarchy of the net.\n"
"\n"
"In top-level mode, only the shapes from the net inside it's circuit are copied to\n"
"the given target cell. No other cells are created.\n"
"If the netname_prop name is not nil, a property with the given name is created and assigned\n"
"the net name.\n"
"\n"
"Recursive mode is picked when a circuit cell name prefix is given. The new cells will be\n"
"named like circuit_cell_name_prefix + circuit name.\n"
"\n"
"If a device cell name prefix is given, device shapes will be output on device cells named\n"
"like device_cell_name_prefix + device name.\n"
"Net hierarchy is covered in three ways:\n"
"@ul\n"
" @li No connection indicated (hier_mode == \\BNH_Disconnected: the net shapes are simply put into their\n"
" respective circuits. The connections are not indicated. @/li\n"
" @li Subnet hierarchy (hier_mode == \\BNH_SubcircuitCells): for each root net, a full hierarchy is built\n"
" to accommodate the subnets (see build_net in recursive mode). @/li\n"
" @li Flat (hier_mode == \\BNH_Flatten): each net is flattened and put into the circuit it\n"
" belongs to. @/li\n"
"@/ul\n"
"If a device cell name prefix is given, cells will be produced for each device abstract\n"
"using a name like device_cell_name_prefix + device name. Otherwise the device shapes are\n"
"treated as part of the net.\n"
"\n"
"@param target The target layout\n"
"@param target_cell The target cell\n"
"@param lmap Target layer indexes (keys) and net regions (values)\n"
"@param circuit_cell_name_prefix Chooses recursive mode if non-nil\n"
"@param device_cell_name_prefix If given, devices will be output as separate cells\n"
"@param hier_mode See description of this method\n"
"@param netname_prop An (optional) property name to which to attach the net name\n"
"@param cell_name_prefix Chooses recursive mode if non-null\n"
"@param device_cell_name_prefix See above\n"
) +
gsi::method_ext ("build_all_nets", &build_all_nets, gsi::arg ("cmap"), gsi::arg ("target"), gsi::arg ("lmap"), gsi::arg ("net_cell_name_prefix", tl::Variant (), "nil"), gsi::arg ("circuit_cell_name_prefix", tl::Variant (), "nil"), gsi::arg ("device_cell_name_prefix", tl::Variant (), "nil"),
gsi::method_ext ("build_all_nets", &build_all_nets, gsi::arg ("cmap"), gsi::arg ("target"), gsi::arg ("lmap"), gsi::arg ("net_cell_name_prefix", tl::Variant (), "nil"), gsi::arg ("netname_prop", tl::Variant (), "nil"), gsi::arg ("hier_mode", db::LayoutToNetlist::BNH_Flatten, "BNH_Flatten"), gsi::arg ("circuit_cell_name_prefix", tl::Variant (), "nil"), gsi::arg ("device_cell_name_prefix", tl::Variant (), "nil"),
"@brief Builds a full hierarchical representation of the nets\n"
"\n"
"This method copies all nets into cells corresponding to the circuits. It uses the cmap\n"
"object to determine the target cell (create them with \\cell_mapping_into or \\const_cell_mapping_into.\n"
"If no mapping is requested, the specific circuit it skipped.\n"
"\n"
"The method has two net annotation modes:\n"
"This method copies all nets into cells corresponding to the circuits. It uses the 'cmap'\n"
"object to determine the target cell (create it with \"cell_mapping_into\" or \"const_cell_mapping_into\").\n"
"If no mapping is provided for a specific circuit cell, the nets are copied into the next mapped parent as "
"many times as the circuit cell appears there (circuit flattening).\n"
"\n"
"The method has three net annotation modes:\n"
"@ul\n"
"@li 'No annotation'' (net_cell_name_prefix == 0): the shapes will be put into the target cell simply @/li\n"
"@li Individual subcells per net (net_cell_name_prefix != 0): for each net, a subcell is created\n"
" and the net shapes will be put there (name of the subcell = net_cell_name_prefix + net name). @/li\n"
" @li No annotation (net_cell_name_prefix == nil and netname_prop == nil): the shapes will be put\n"
" into the target cell simply. @/li\n"
" @li Net name property (net_cell_name_prefix == nil and netname_prop != nil): the shapes will be\n"
" annotated with a property named with netname_prop and containing the net name string. @/li\n"
" @li Individual subcells per net (net_cell_name_prefix != 0): for each net, a subcell is created\n"
" and the net shapes will be put there (name of the subcell = net_cell_name_prefix + net name).\n"
" (this mode can be combined with netname_prop too). @/li\n"
"@/ul\n"
"\n"
"In addition, net hierarchy is covered in two ways:\n"
"\n"
"In addition, net hierarchy is covered in three ways:\n"
"@ul\n"
"@li No connection indicated (circuit_cell_name_prefix == 0: the net shapes are simply put into their\n"
" respective circuits. The connections are not indicated. @/li\n"
"@li Subnet hierarchy (circuit_cell_name_prefix != 0): for each root net, a full hierarchy is built\n"
" to accommodate the subnets (see build_net in recursive mode). @/li\n"
" @li No connection indicated (hier_mode == \\BNH_Disconnected: the net shapes are simply put into their\n"
" respective circuits. The connections are not indicated. @/li\n"
" @li Subnet hierarchy (hier_mode == \\BNH_SubcircuitCells): for each root net, a full hierarchy is built\n"
" to accommodate the subnets (see build_net in recursive mode). @/li\n"
" @li Flat (hier_mode == \\BNH_Flatten): each net is flattened and put into the circuit it\n"
" belongs to. @/li\n"
"@/ul\n"
"\n"
"If a device name prefix is given, device shapes will be output on device cells named\n"
"like device_name_prefix + device name.\n"
"If a device cell name prefix is given, cells will be produced for each device abstract\n"
"using a name like device_cell_name_prefix + device name. Otherwise the device shapes are\n"
"treated as part of the net.\n"
"\n"
"@param cmap The mapping of internal layout to target layout for the circuit mapping\n"
"@param target The target layout\n"
"@param lmap Target layer indexes (keys) and net regions (values)\n"
"@param net_cell_name_prefix See method description\n"
"@param hier_mode See description of this method\n"
"@param netname_prop An (optional) property name to which to attach the net name\n"
"@param circuit_cell_name_prefix See method description\n"
"@param device_cell_name_prefix If given, devices will be output as separate cells\n"
"@param net_cell_name_prefix See method description\n"
"@param device_cell_name_prefix See above\n"
) +
gsi::method_ext ("build_nets", &build_nets, gsi::arg ("nets"), gsi::arg ("cmap"), gsi::arg ("target"), gsi::arg ("lmap"), gsi::arg ("net_cell_name_prefix", tl::Variant (), "nil"), gsi::arg ("netname_prop", tl::Variant (), "nil"), gsi::arg ("hier_mode", db::LayoutToNetlist::BNH_Flatten, "BNH_Flatten"), gsi::arg ("circuit_cell_name_prefix", tl::Variant (), "nil"), gsi::arg ("device_cell_name_prefix", tl::Variant (), "nil"),
"@brief Like \\build_all_nets, but with the ability to select some nets."
) +
gsi::method ("probe_net", (db::Net *(db::LayoutToNetlist::*) (const db::Region &, const db::DPoint &)) &db::LayoutToNetlist::probe_net, gsi::arg ("of_layer"), gsi::arg ("point"),
"@brief Finds the net by probing a specific location on the given layer\n"
@ -425,11 +480,11 @@ Class<db::LayoutToNetlist> decl_dbLayoutToNetlist ("db", "LayoutToNetlist",
"This variant accepts a database-unit location. The location is given in the\n"
"coordinate space of the initial cell.\n"
) +
gsi::method_ext ("write", &write_l2n, gsi::arg ("path"), gsi::arg ("short_format", false),
gsi::method ("write|write_l2n", &db::LayoutToNetlist::save, gsi::arg ("path"), gsi::arg ("short_format", false),
"@brief Writes the extracted netlist to a file.\n"
"This method employs the native format of KLayout.\n"
) +
gsi::method_ext ("read", &read_l2n, gsi::arg ("path"),
gsi::method ("read|read_l2n", &db::LayoutToNetlist::load, gsi::arg ("path"),
"@brief Reads the extracted netlist from the file.\n"
"This method employs the native format of KLayout.\n"
) +
@ -523,4 +578,21 @@ Class<db::LayoutToNetlist> decl_dbLayoutToNetlist ("db", "LayoutToNetlist",
"This class has been introduced in version 0.26."
);
gsi::EnumIn<db::LayoutToNetlist, db::LayoutToNetlist::BuildNetHierarchyMode> decl_dbLayoutToNetlist_BuildNetHierarchyMode ("db", "BuildNetHierarchyMode",
gsi::enum_const ("BNH_Flatten", db::LayoutToNetlist::BNH_Flatten,
"@brief This constant tells \\build_net and \\build_all_nets to flatten the nets (used for the \"hier_mode\" parameter)."
) +
gsi::enum_const ("BNH_Disconnected", db::LayoutToNetlist::BNH_Disconnected,
"@brief This constant tells \\build_net and \\build_all_nets to produce local nets without connections to subcircuits (used for the \"hier_mode\" parameter)."
) +
gsi::enum_const ("BNH_SubcircuitCells", db::LayoutToNetlist::BNH_SubcircuitCells,
"@brief This constant tells \\build_net and \\build_all_nets to produce a hierarchy of subcircuit cells per net (used for the \"hier_mode\" parameter)."
),
"@brief This class represents the LayoutToNetlist::BuildNetHierarchyMode enum\n"
"This enum is used for \\LayoutToNetlist#build_all_nets and \\LayoutToNetlist#build_net."
);
// Inject the NetlistCrossReference::Status declarations into NetlistCrossReference:
gsi::ClassExt<db::LayoutToNetlist> inject_dbLayoutToNetlist_BuildNetHierarchyMode_in_parent (decl_dbLayoutToNetlist_BuildNetHierarchyMode.defs ());
}

View File

@ -0,0 +1,157 @@
/*
KLayout Layout Viewer
Copyright (C) 2006-2019 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 "gsiDecl.h"
#include "dbLayoutVsSchematic.h"
#include "tlStream.h"
#include "tlVariant.h"
namespace gsi
{
extern Class<db::LayoutToNetlist> decl_dbLayoutToNetlist;
static db::LayoutVsSchematic *make_lvs (const db::RecursiveShapeIterator &iter)
{
return new db::LayoutVsSchematic (iter);
}
static db::LayoutVsSchematic *make_lvs_default ()
{
return new db::LayoutVsSchematic ();
}
static db::LayoutVsSchematic *make_lvs_from_existing_dss_with_layout (db::DeepShapeStore *dss, unsigned int layout_index)
{
return new db::LayoutVsSchematic (dss, layout_index);
}
static db::LayoutVsSchematic *make_lvs_from_existing_dss (db::DeepShapeStore *dss)
{
return new db::LayoutVsSchematic (dss);
}
static db::LayoutVsSchematic *make_lvs_flat (const std::string &topcell_name, double dbu)
{
return new db::LayoutVsSchematic (topcell_name, dbu);
}
static void save_l2n (db::LayoutVsSchematic *lvs, const std::string &path, bool short_format)
{
lvs->db::LayoutToNetlist::save (path, short_format);
}
static void load_l2n (db::LayoutVsSchematic *lvs, const std::string &path)
{
lvs->db::LayoutToNetlist::load (path);
}
Class<db::LayoutVsSchematic> decl_dbLayoutVsSchematic (decl_dbLayoutToNetlist, "db", "LayoutVsSchematic",
gsi::constructor ("new", &make_lvs, gsi::arg ("iter"),
"@brief Creates a new LVS object with the extractor connected to an original layout\n"
"This constructor will attach the extractor of the LVS object to an original layout through the "
"shape iterator.\n"
) +
gsi::constructor ("new", &make_lvs_default,
"@brief Creates a new LVS object\n"
"The main objective for this constructor is to create an object suitable for reading and writing LVS database files.\n"
) +
gsi::constructor ("new", &make_lvs_from_existing_dss, gsi::arg ("dss"),
"@brief Creates a new LVS object with the extractor object reusing an existing \\DeepShapeStore object\n"
"See the corresponding constructor of the \\LayoutToNetlist object for more details."
) +
gsi::constructor ("new", &make_lvs_from_existing_dss_with_layout, gsi::arg ("dss"), gsi::arg ("layout_index"),
"@brief Creates a new LVS object with the extractor object reusing an existing \\DeepShapeStore object\n"
"See the corresponding constructor of the \\LayoutToNetlist object for more details."
) +
gsi::constructor ("new", &make_lvs_flat, gsi::arg ("topcell_name"), gsi::arg ("dbu"),
"@brief Creates a new LVS object with the extractor object taking a flat DSS\n"
"See the corresponding constructor of the \\LayoutToNetlist object for more details."
) +
gsi::method ("reference=", &db::LayoutVsSchematic::set_reference_netlist, gsi::arg ("reference_netlist"),
"@brief Sets the reference netlist.\n"
"This will set the reference netlist used inside \\compare as the second netlist to compare against "
"the layout-extracted netlist.\n"
"\n"
"The LVS object will take ownership over the netlist - i.e. if it goes out of scope, the "
"reference netlist is deleted.\n"
) +
gsi::method ("reference", (db::Netlist *(db::LayoutVsSchematic::*) ()) &db::LayoutVsSchematic::reference_netlist,
"@brief Gets the reference netlist.\n"
) +
gsi::method ("compare", &db::LayoutVsSchematic::compare_netlists, gsi::arg ("comparer"),
"@brief Compare the layout-extracted netlist against the reference netlist using the given netlist comparer.\n"
) +
gsi::method ("xref", (db::NetlistCrossReference *(db::LayoutVsSchematic::*) ()) &db::LayoutVsSchematic::cross_ref,
"@brief Gets the cross-reference object\n"
"The cross-reference object is created while comparing the layout-extracted netlist against the "
"reference netlist - i.e. during \\compare. Before \\compare is called, this object is nil.\n"
"It holds the results of the comparison - a cross-reference between the nets and other objects "
"in the match case and a listing of non-matching nets and other objects for the non-matching cases."
"\n"
"See \\NetlistCrossReference for more details.\n"
) +
gsi::method_ext ("write_l2n", &save_l2n, gsi::arg ("path"), gsi::arg ("short_format", false),
"@brief Writes the \\LayoutToNetlist part of the object to a file.\n"
"This method employs the native format of KLayout.\n"
) +
gsi::method_ext ("read_l2n", &load_l2n, gsi::arg ("path"),
"@brief Reads the \\LayoutToNetlist part of the object from a file.\n"
"This method employs the native format of KLayout.\n"
) +
gsi::method ("write", &db::LayoutVsSchematic::save, gsi::arg ("path"), gsi::arg ("short_format", false),
"@brief Writes the LVS object to a file.\n"
"This method employs the native format of KLayout.\n"
) +
gsi::method ("read", &db::LayoutVsSchematic::load, gsi::arg ("path"),
"@brief Reads the LVS object from the file.\n"
"This method employs the native format of KLayout.\n"
),
"@brief A generic framework for doing LVS (layout vs. schematic)\n"
"\n"
"This class extends the concept of the netlist extraction from a layout to LVS verification. "
"It does so by adding these concepts to the \\LayoutToNetlist class:\n"
"\n"
"@ul\n"
"@li A reference netlist. This will be the netlist against which the layout-derived netlist is "
"compared against. See \\reference and \\reference=.\n"
"@/li\n"
"@li A compare step. During the compare the layout-derived netlist and the reference netlists "
"are compared. The compare results are captured in the cross-reference object. "
"See \\compare and \\NetlistComparer for the comparer object.\n"
"@/li\n"
"@li A cross-reference. This object (of class \\NetlistCrossReference) will keep the relations "
"between the objects of the two netlists. It also lists the differences between the netlists. "
"See \\xref about how to access this object."
"@/li\n"
"@/ul\n"
"\n"
"The LVS object can be persisted to and from a file in a specific format, so it is sometimes "
"referred to as the \"LVS database\".\n"
"\n"
"LVS objects can be attached to layout views with \\LayoutView#add_lvsdb so they become available in the "
"netlist database browser.\n"
"\n"
"This class has been introduced in version 0.26."
);
}

View File

@ -29,6 +29,7 @@
#include "tlException.h"
#include "tlInternational.h"
#include "tlStream.h"
#include "tlGlobPattern.h"
namespace gsi
{
@ -70,6 +71,160 @@ static void device_disconnect_terminal_by_name (db::Device *device, const std::s
device_connect_terminal_by_name (device, terminal_name, 0);
}
static size_t get_device_index (const db::DeviceReconnectedTerminal *obj)
{
return obj->device_index;
}
static void set_device_index (db::DeviceReconnectedTerminal *obj, size_t device_index)
{
obj->device_index = device_index;
}
static size_t get_other_terminal_id (const db::DeviceReconnectedTerminal *obj)
{
return obj->other_terminal_id;
}
static void set_other_terminal_id (db::DeviceReconnectedTerminal *obj, size_t other_terminal_id)
{
obj->other_terminal_id = other_terminal_id;
}
Class<db::DeviceReconnectedTerminal> decl_dbDeviceReconnectedTerminal ("db", "DeviceReconnectedTerminal",
gsi::method_ext ("device_index=", &set_device_index, gsi::arg ("device_index"),
"@brief The device abstract index setter.\n"
"See the class description for details."
) +
gsi::method_ext ("device_index", &get_device_index,
"@brief The device abstract index getter.\n"
"See the class description for details."
) +
gsi::method_ext ("other_terminal_id=", &set_other_terminal_id, gsi::arg ("other_terminal_id"),
"@brief The setter for the abstract's connected terminal.\n"
"See the class description for details."
) +
gsi::method_ext ("other_terminal_id", &get_other_terminal_id,
"@brief The getter for the abstract's connected terminal.\n"
"See the class description for details."
),
"@brief Describes a terminal rerouting in combined devices.\n"
"Combined devices are implemented as a generalization of the device abstract concept in \\Device. For "
"combined devices, multiple \\DeviceAbstract references are present. To support different combination schemes, "
"device-to-abstract routing is supported. Parallel combinations will route all outer terminals to corresponding "
"terminals of all device abstracts (because of terminal swapping these may be different ones).\n"
"\n"
"This object describes one route to an abstract's terminal. The device index is 0 for the main device abstract and "
"1 for the first combined device abstract.\n"
"\n"
"This class has been introduced in version 0.26.\n"
);
static const db::DeviceAbstract *get_device_abstract (const db::DeviceAbstractRef *obj)
{
return obj->device_abstract;
}
static void set_device_abstract (db::DeviceAbstractRef *obj, const db::DeviceAbstract *device_abstract)
{
obj->device_abstract = device_abstract;
}
static db::DCplxTrans get_trans (const db::DeviceAbstractRef *obj)
{
return obj->trans;
}
static void set_trans (db::DeviceAbstractRef *obj, const db::DCplxTrans &trans)
{
obj->trans = trans;
}
Class<db::DeviceAbstractRef> decl_dbDeviceAbstractRef ("db", "DeviceAbstractRef",
gsi::method_ext ("device_abstract=", &set_device_abstract, gsi::arg ("device_abstract"),
"@brief The setter for the device abstract reference.\n"
"See the class description for details."
) +
gsi::method_ext ("device_abstract", &get_device_abstract,
"@brief The getter for the device abstract reference.\n"
"See the class description for details."
) +
gsi::method_ext ("trans=", &set_trans, gsi::arg ("tr"),
"@brief The setter for the relative transformation of the instance.\n"
"See the class description for details."
) +
gsi::method_ext ("trans", &get_trans,
"@brief The getter for the relative transformation of the instance.\n"
"See the class description for details."
),
"@brief Describes an additional device abstract reference for combined devices.\n"
"Combined devices are implemented as a generalization of the device abstract concept in \\Device. For "
"combined devices, multiple \\DeviceAbstract references are present. This class describes such an "
"additional reference. A reference is a pointer to an abstract plus a transformation by which the abstract "
"is transformed geometrically as compared to the first (initial) abstract.\n"
"\n"
"This class has been introduced in version 0.26.\n"
);
static bool is_combined_device (const db::Device *device)
{
return ! device->reconnected_terminals ().empty ();
}
static std::vector<db::DeviceReconnectedTerminal>::const_iterator begin_reconnected_terminals_for (const db::Device *device, size_t terminal_id)
{
static std::vector<db::DeviceReconnectedTerminal> empty;
const std::vector<db::DeviceReconnectedTerminal> *ti = device->reconnected_terminals_for (terminal_id);
if (! ti) {
return empty.begin ();
} else {
return ti->begin ();
}
}
static std::vector<db::DeviceReconnectedTerminal>::const_iterator end_reconnected_terminals_for (const db::Device *device, size_t terminal_id)
{
static std::vector<db::DeviceReconnectedTerminal> empty;
const std::vector<db::DeviceReconnectedTerminal> *ti = device->reconnected_terminals_for (terminal_id);
if (! ti) {
return empty.end ();
} else {
return ti->end ();
}
}
static void clear_reconnected_terminals (db::Device *device)
{
device->reconnected_terminals ().clear ();
}
static void add_reconnected_terminals (db::Device *device, size_t outer_terminal, const db::DeviceReconnectedTerminal &t)
{
device->reconnected_terminals () [outer_terminal].push_back (t);
}
static std::vector<db::DeviceAbstractRef>::const_iterator begin_other_abstracts (const db::Device *device)
{
return device->other_abstracts ().begin ();
}
static std::vector<db::DeviceAbstractRef>::const_iterator end_other_abstracts (const db::Device *device)
{
return device->other_abstracts ().end ();
}
static void clear_other_abstracts (db::Device *device)
{
device->other_abstracts ().clear ();
}
static void add_other_abstracts (db::Device *device, const db::DeviceAbstractRef &ref)
{
device->other_abstracts ().push_back (ref);
}
Class<db::Device> decl_dbDevice ("db", "Device",
gsi::method ("device_class", &db::Device::device_class,
"@brief Gets the device class the device belongs to.\n"
@ -78,7 +233,40 @@ Class<db::Device> decl_dbDevice ("db", "Device",
"@brief Gets the device abstract for this device instance.\n"
"See \\DeviceAbstract for more details.\n"
) +
gsi::method ("circuit", (db::Circuit *(db::Device::*) ()) &db::Device::circuit,
gsi::method ("device_abstract=", &db::Device::set_device_abstract,
"@hide\n"
"Provided for test purposes mainly. Be careful with pointers!"
) +
gsi::method_ext ("is_combined_device?", &is_combined_device,
"@brief Returns true, if the device is a combined device.\n"
"Combined devices feature multiple device abstracts and device-to-abstract terminal connections.\n"
"See \\each_reconnected_terminal and \\each_combined_abstract for more details.\n"
) +
gsi::iterator_ext ("each_reconnected_terminal_for", &begin_reconnected_terminals_for, &end_reconnected_terminals_for, gsi::arg ("terminal_id"),
"@brief Iterates over the reconnected terminal specifications for a given outer terminal.\n"
"This feature applies to combined devices. This iterator will deliver all device-to-abstract terminal reroutings.\n"
) +
gsi::method_ext ("clear_reconnected_terminals", &clear_reconnected_terminals,
"@hide\n"
"Provided for test purposes mainly."
) +
gsi::method_ext ("add_reconnected_terminal_for", &add_reconnected_terminals, gsi::arg ("outer_terminal"), gsi::arg ("descriptor"),
"@hide\n"
"Provided for test purposes mainly."
) +
gsi::iterator_ext ("each_combined_abstract", &begin_other_abstracts, &end_other_abstracts,
"@brief Iterates over the combined device specifications.\n"
"This feature applies to combined devices. This iterator will deliver all device abstracts present in addition to the default device abstract.\n"
) +
gsi::method_ext ("clear_combined_abstracts", &clear_other_abstracts,
"@hide\n"
"Provided for test purposes mainly."
) +
gsi::method_ext ("add_combined_abstract", &add_other_abstracts, gsi::arg ("ref"),
"@hide\n"
"Provided for test purposes mainly."
) +
gsi::method ("circuit", (const db::Circuit *(db::Device::*) () const) &db::Device::circuit,
"@brief Gets the circuit the device lives in."
) +
gsi::method ("id", &db::Device::id,
@ -99,7 +287,7 @@ Class<db::Device> decl_dbDevice ("db", "Device",
"@brief Gets the expanded name of the device.\n"
"The expanded name takes the name of the device. If the name is empty, the numeric ID will be used to build a name. "
) +
gsi::method ("net_for_terminal", (db::Net *(db::Device::*) (size_t)) &db::Device::net_for_terminal, gsi::arg ("terminal_id"),
gsi::method ("net_for_terminal", (const db::Net *(db::Device::*) (size_t) const) &db::Device::net_for_terminal, gsi::arg ("terminal_id"),
"@brief Gets the net connected to the specified terminal.\n"
"If the terminal is not connected, nil is returned for the net."
) +
@ -152,7 +340,7 @@ Class<db::Device> decl_dbDevice ("db", "Device",
);
Class<db::DeviceAbstract> decl_dbDeviceAbstract ("db", "DeviceAbstract",
gsi::method ("netlist", (db::Netlist *(db::DeviceAbstract::*) ()) &db::DeviceAbstract::netlist,
gsi::method ("netlist", (const db::Netlist *(db::DeviceAbstract::*) () const) &db::DeviceAbstract::netlist,
"@brief Gets the netlist the device abstract lives in."
) +
gsi::method ("device_class", &db::DeviceAbstract::device_class,
@ -201,10 +389,10 @@ static void subcircuit_disconnect_pin1 (db::SubCircuit *subcircuit, const db::Pi
}
Class<db::SubCircuit> decl_dbSubCircuit ("db", "SubCircuit",
gsi::method ("circuit_ref", (db::Circuit *(db::SubCircuit::*) ()) &db::SubCircuit::circuit_ref,
gsi::method ("circuit_ref", (const db::Circuit *(db::SubCircuit::*) () const) &db::SubCircuit::circuit_ref,
"@brief Gets the circuit referenced by the subcircuit.\n"
) +
gsi::method ("circuit", (db::Circuit *(db::SubCircuit::*) ()) &db::SubCircuit::circuit,
gsi::method ("circuit", (const db::Circuit *(db::SubCircuit::*) () const) &db::SubCircuit::circuit,
"@brief Gets the circuit the subcircuit lives in.\n"
"This is NOT the circuit which is referenced. For getting the circuit that the subcircuit references, use \\circuit_ref."
) +
@ -226,7 +414,7 @@ Class<db::SubCircuit> decl_dbSubCircuit ("db", "SubCircuit",
"@brief Gets the expanded name of the subcircuit.\n"
"The expanded name takes the name of the subcircuit. If the name is empty, the numeric ID will be used to build a name. "
) +
gsi::method ("net_for_pin", (db::Net *(db::SubCircuit::*) (size_t)) &db::SubCircuit::net_for_pin, gsi::arg ("pin_id"),
gsi::method ("net_for_pin", (const db::Net *(db::SubCircuit::*) (size_t) const) &db::SubCircuit::net_for_pin, gsi::arg ("pin_id"),
"@brief Gets the net connected to the specified pin of the subcircuit.\n"
"If the pin is not connected, nil is returned for the net."
) +
@ -263,17 +451,17 @@ Class<db::NetTerminalRef> decl_dbNetTerminalRef ("db", "NetTerminalRef",
gsi::method ("terminal_id", &db::NetTerminalRef::terminal_id,
"@brief Gets the ID of the terminal of the device the connection is made to."
) +
gsi::method ("device", (db::Device *(db::NetTerminalRef::*) ()) &db::NetTerminalRef::device,
gsi::method ("device", (const db::Device *(db::NetTerminalRef::*) () const) &db::NetTerminalRef::device,
"@brief Gets the device reference.\n"
"Gets the device object that this connection is made to."
) +
gsi::method ("net", (db::Net *(db::NetTerminalRef::*) ()) &db::NetTerminalRef::net,
gsi::method ("net", (const db::Net *(db::NetTerminalRef::*) () const) &db::NetTerminalRef::net,
"@brief Gets the net this terminal reference is attached to"
) +
gsi::method ("device_class", (db::DeviceClass *(db::NetTerminalRef::*) ()) &db::NetTerminalRef::device_class,
gsi::method ("device_class", (const db::DeviceClass *(db::NetTerminalRef::*) () const) &db::NetTerminalRef::device_class,
"@brief Gets the class of the device which is addressed."
) +
gsi::method ("terminal_def", (db::DeviceTerminalDefinition *(db::NetTerminalRef::*) ()) &db::NetTerminalRef::terminal_def,
gsi::method ("terminal_def", (const db::DeviceTerminalDefinition *(db::NetTerminalRef::*) () const) &db::NetTerminalRef::terminal_def,
"@brief Gets the terminal definition of the terminal that is connected"
),
"@brief A connection to a terminal of a device.\n"
@ -289,7 +477,7 @@ Class<db::NetPinRef> decl_dbNetPinRef ("db", "NetPinRef",
gsi::method ("pin", &db::NetPinRef::pin,
"@brief Gets the \\Pin object of the pin the connection is made to."
) +
gsi::method ("net", (db::Net *(db::NetPinRef::*) ()) &db::NetPinRef::net,
gsi::method ("net", (const db::Net *(db::NetPinRef::*) () const) &db::NetPinRef::net,
"@brief Gets the net this pin reference is attached to"
),
"@brief A connection to an outgoing pin of the circuit.\n"
@ -305,12 +493,12 @@ Class<db::NetSubcircuitPinRef> decl_dbNetSubcircuitPinRef ("db", "NetSubcircuitP
gsi::method ("pin", &db::NetSubcircuitPinRef::pin,
"@brief Gets the \\Pin object of the pin the connection is made to."
) +
gsi::method ("subcircuit", (db::SubCircuit *(db::NetSubcircuitPinRef::*) ()) &db::NetSubcircuitPinRef::subcircuit,
gsi::method ("subcircuit", (const db::SubCircuit *(db::NetSubcircuitPinRef::*) () const) &db::NetSubcircuitPinRef::subcircuit,
"@brief Gets the subcircuit reference.\n"
"This attribute indicates the subcircuit the net attaches to. The "
"subcircuit lives in the same circuit than the net. "
) +
gsi::method ("net", (db::Net *(db::NetSubcircuitPinRef::*) ()) &db::NetSubcircuitPinRef::net,
gsi::method ("net", (const db::Net *(db::NetSubcircuitPinRef::*) () const) &db::NetSubcircuitPinRef::net,
"@brief Gets the net this pin reference is attached to"
),
"@brief A connection to a pin of a subcircuit.\n"
@ -353,17 +541,17 @@ Class<db::Net> decl_dbNet ("db", "Net",
"@brief Gets the cluster ID of the net.\n"
"See \\cluster_id= for details about the cluster ID."
) +
gsi::iterator ("each_pin", (db::Net::pin_iterator (db::Net::*) ()) &db::Net::begin_pins, (db::Net::pin_iterator (db::Net::*) ()) &db::Net::end_pins,
gsi::iterator ("each_pin", gsi::return_reference (), (db::Net::const_pin_iterator (db::Net::*) () const) &db::Net::begin_pins, (db::Net::const_pin_iterator (db::Net::*) () const) &db::Net::end_pins,
"@brief Iterates over all outgoing pins the net connects.\n"
"Pin connections are described by \\NetPinRef objects. Pin connections "
"are connections to outgoing pins of the circuit the net lives in."
) +
gsi::iterator ("each_subcircuit_pin", (db::Net::subcircuit_pin_iterator (db::Net::*) ()) &db::Net::begin_subcircuit_pins, (db::Net::subcircuit_pin_iterator (db::Net::*) ()) &db::Net::end_subcircuit_pins,
gsi::iterator ("each_subcircuit_pin", gsi::return_reference (), (db::Net::const_subcircuit_pin_iterator (db::Net::*) () const) &db::Net::begin_subcircuit_pins, (db::Net::const_subcircuit_pin_iterator (db::Net::*) () const) &db::Net::end_subcircuit_pins,
"@brief Iterates over all subcircuit pins the net connects.\n"
"Subcircuit pin connections are described by \\NetSubcircuitPinRef objects. These are "
"connections to specific pins of subcircuits."
) +
gsi::iterator ("each_terminal", (db::Net::terminal_iterator (db::Net::*) ()) &db::Net::begin_terminals, (db::Net::terminal_iterator (db::Net::*) ()) &db::Net::end_terminals,
gsi::iterator ("each_terminal", gsi::return_reference (), (db::Net::const_terminal_iterator (db::Net::*) () const) &db::Net::begin_terminals, (db::Net::const_terminal_iterator (db::Net::*) () const) &db::Net::end_terminals,
"@brief Iterates over all terminals the net connects.\n"
"Terminals connect devices. Terminal connections are described by \\NetTerminalRef "
"objects."
@ -434,14 +622,19 @@ Class<db::DeviceTerminalDefinition> decl_dbDeviceTerminalDefinition ("db", "Devi
"This class has been added in version 0.26."
);
static db::DeviceParameterDefinition *new_parameter_definition (const std::string &name, const std::string &description, double default_value)
static db::DeviceParameterDefinition *new_parameter_definition (const std::string &name, const std::string &description, double default_value, bool is_primary, double si_scaling)
{
return new db::DeviceParameterDefinition (name, description, default_value);
return new db::DeviceParameterDefinition (name, description, default_value, is_primary, si_scaling);
}
Class<db::DeviceParameterDefinition> decl_dbDeviceParameterDefinition ("db", "DeviceParameterDefinition",
gsi::constructor ("new", &gsi::new_parameter_definition, gsi::arg ("name"), gsi::arg ("description", std::string ()), gsi::arg ("default_value", 0.0),
gsi::constructor ("new", &gsi::new_parameter_definition, gsi::arg ("name"), gsi::arg ("description", std::string ()), gsi::arg ("default_value", 0.0), gsi::arg ("is_primary", true), gsi::arg ("si_scaling", 1.0),
"@brief Creates a new parameter definition."
"@param name The name of the parameter\n"
"@param description The human-readable description\n"
"@param default_value The initial value\n"
"@param is_primary True, if the parameter is a primary parameter (see \\is_primary=)\n"
"@param si_scaling The scaling factor to SI units\n"
) +
gsi::method ("name", &db::DeviceParameterDefinition::name,
"@brief Gets the name of the parameter."
@ -471,6 +664,10 @@ Class<db::DeviceParameterDefinition> decl_dbDeviceParameterDefinition ("db", "De
"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 ("si_scaling", &db::DeviceParameterDefinition::si_scaling,
"@brief Gets the scaling factor to SI units.\n"
"For parameters in micrometers for example, this factor will be 1e-6."
) +
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 "
@ -845,7 +1042,7 @@ static db::SubCircuit *create_subcircuit1 (db::Circuit *c, db::Circuit *cc, cons
return sc;
}
static db::Net *circuit_net_for_pin (db::Circuit *c, const db::Pin *pin)
static const db::Net *circuit_net_for_pin (const db::Circuit *c, const db::Pin *pin)
{
return pin ? c->net_for_pin (pin->id ()) : 0;
}
@ -884,6 +1081,10 @@ Class<db::Circuit> decl_dbCircuit ("db", "Circuit",
"@brief Iterates over the parent circuits of this circuit\n"
"Child circuits are the ones that are referencing this circuit via subcircuits."
) +
gsi::method ("has_refs", &db::Circuit::has_refs,
"@brief Returns a value indicating whether the circuit has references\n"
"A circuit has references if there is at least one subcircuit referring to it."
) +
gsi::iterator ("each_ref", (db::Circuit::refs_iterator (db::Circuit::*) ()) &db::Circuit::begin_refs, (db::Circuit::refs_iterator (db::Circuit::*) ()) &db::Circuit::end_refs,
"@brief Iterates over the subcircuit objects referencing this circuit\n"
) +
@ -973,6 +1174,14 @@ Class<db::Circuit> decl_dbCircuit ("db", "Circuit",
gsi::iterator ("each_subcircuit", (db::Circuit::subcircuit_iterator (db::Circuit::*) ()) &db::Circuit::begin_subcircuits, (db::Circuit::subcircuit_iterator (db::Circuit::*) ()) &db::Circuit::end_subcircuits,
"@brief Iterates over the subcircuits of the circuit"
) +
gsi::method ("blank", &db::Circuit::blank,
"@brief Blanks out the circuit\n"
"This method will remove all the innards of the circuit and just leave the pins. "
"The pins won't be connected to inside nets anymore, but the circuit can still be "
"called by subcircuit references. "
"This method will eventually create a 'circuit abstract' (or black box). It will "
"set the \\dont_purge flag to mark this circuit as 'intentionally empty'."
) +
gsi::method ("netlist", (db::Netlist *(db::Circuit::*) ()) &db::Circuit::netlist,
"@brief Gets the netlist object the circuit lives in"
) +
@ -982,6 +1191,20 @@ Class<db::Circuit> decl_dbCircuit ("db", "Circuit",
gsi::method ("name", &db::Circuit::name,
"@brief Gets the name of the circuit"
) +
gsi::method ("boundary=", &db::Circuit::set_boundary, gsi::arg ("boundary"),
"@brief Sets the boundary of the circuit"
) +
gsi::method ("boundary", &db::Circuit::boundary,
"@brief Gets the boundary of the circuit"
) +
gsi::method ("dont_purge", &db::Circuit::dont_purge,
"@brief Gets a value indicating whether the circuit can be purged on \\Netlist#purge.\n"
) +
gsi::method ("dont_purge=", &db::Circuit::set_dont_purge, gsi::arg ("f"),
"@brief Sets a value indicating whether the circuit can be purged on \\Netlist#purge.\n"
"If this attribute is set to true, \\Netlist#purge will never delete this circuit.\n"
"This flag therefore marks this circuit as 'precious'."
) +
gsi::method ("cell_index=", &db::Circuit::set_cell_index, gsi::arg ("cell_index"),
"@brief Sets the cell index\n"
"The cell index relates a circuit with a cell from a layout. It's intended to "
@ -1088,6 +1311,40 @@ static void read_netlist (db::Netlist *nl, const std::string &file, db::NetlistR
reader->read (os, *nl);
}
static void flatten_circuit_by_name (db::Netlist *nl, const std::string &name_pattern)
{
std::list<tl::weak_ptr<db::Circuit> > circuits_to_flatten;
tl::GlobPattern pat (name_pattern);
for (db::Netlist::circuit_iterator c = nl->begin_circuits (); c != nl->end_circuits (); ++c) {
if (pat.match (c->name ())) {
circuits_to_flatten.push_back (c.operator-> ());
}
}
for (std::list<tl::weak_ptr<db::Circuit> >::iterator c = circuits_to_flatten.begin (); c != circuits_to_flatten.end (); ++c) {
if (c->get ()) {
nl->flatten_circuit (c->get ());
}
}
}
static void blank_circuit_by_name (db::Netlist *nl, const std::string &name_pattern)
{
std::list<tl::weak_ptr<db::Circuit> > circuits_to_blank;
tl::GlobPattern pat (name_pattern);
for (db::Netlist::circuit_iterator c = nl->begin_circuits (); c != nl->end_circuits (); ++c) {
if (pat.match (c->name ())) {
circuits_to_blank.push_back (c.operator-> ());
}
}
for (std::list<tl::weak_ptr<db::Circuit> >::iterator c = circuits_to_blank.begin (); c != circuits_to_blank.end (); ++c) {
if (c->get ()) {
(*c)->blank ();
}
}
}
Class<db::Netlist> decl_dbNetlist ("db", "Netlist",
gsi::method_ext ("add", &gsi::add_circuit, gsi::arg ("circuit"),
"@brief Adds the circuit to the netlist\n"
@ -1096,13 +1353,37 @@ Class<db::Netlist> decl_dbNetlist ("db", "Netlist",
) +
gsi::method ("remove", &db::Netlist::remove_circuit, gsi::arg ("circuit"),
"@brief Removes the given circuit object from the netlist\n"
"After the object has been removed, it becomes invalid and cannot be used further."
"After the circuit has been removed, the object becomes invalid and cannot be used further. "
"A circuit with references (see \\has_refs) should not be removed as the "
"subcircuits calling it would afterwards point to nothing."
) +
gsi::method ("purge_circuit", &db::Netlist::purge_circuit, gsi::arg ("circuit"),
"@brief Removes the given circuit object and all child circuits which are not used otherwise from the netlist\n"
"After the circuit has been removed, the object becomes invalid and cannot be used further. "
"A circuit with references (see \\has_refs) should not be removed as the "
"subcircuits calling it would afterwards point to nothing."
) +
gsi::method ("flatten_circuit", &db::Netlist::flatten_circuit, gsi::arg ("circuit"),
"@brief Flattens a subcircuit\n"
"This method will substitute all instances (subcircuits) of the given circuit by it's "
"contents. After this, the circuit is removed."
) +
gsi::method_ext ("flatten_circuit", &flatten_circuit_by_name, gsi::arg ("pattern"),
"@brief Flattens circuits matching a certain pattern\n"
"This method will substitute all instances (subcircuits) of all circuits with names matching the given name pattern. "
"The name pattern is a glob expression. For example, 'flatten_circuit(\"np*\")' will flatten all circuits with names "
"starting with 'np'."
) +
gsi::method_ext ("blank_circuit", &blank_circuit_by_name, gsi::arg ("pattern"),
"@brief Blanks circuits matching a certain pattern\n"
"This method will erase everything from inside the circuits matching the given pattern. It will only leave pins which are "
"not connected to any net. Hence, this method forms 'abstract' or black-box circuits which can be instantiated through "
"subcircuits like the former ones, but are empty shells.\n"
"The name pattern is a glob expression. For example, 'flatten_circuit(\"np*\")' will blank out all circuits with names "
"starting with 'np'.\n"
"\n"
"For more details see \\Circuit#blank which is the corresponding method on the actual object."
) +
gsi::method ("circuit_by_cell_index", (db::Circuit *(db::Netlist::*) (db::cell_index_type)) &db::Netlist::circuit_by_cell_index, gsi::arg ("cell_index"),
"@brief Gets the circuit object for a given cell index.\n"
"If the cell index is not valid or no circuit is registered with this index, nil is returned."
@ -1172,13 +1453,19 @@ Class<db::Netlist> decl_dbNetlist ("db", "Netlist",
gsi::method ("purge", &db::Netlist::purge,
"@brief Purge unused nets, circuits and subcircuits.\n"
"This method will purge all nets which return \\floating == true. Circuits which don't have any "
"nets (or only floating ones) and removed. Their subcircuits are disconnected."
"nets (or only floating ones) and removed. Their subcircuits are disconnected.\n"
"This method respects the \\Circuit#dont_purge attribute and will never delete circuits "
"with this flag set."
) +
gsi::method ("purge_nets", &db::Netlist::purge_nets,
"@brief Purges floating nets.\n"
"Floating nets can be created as effect of reconnections of devices or pins. "
"This method will eliminate all nets that make less than two connections."
) +
gsi::method ("simplify", &db::Netlist::simplify,
"@brief Convience method that combines the simplification.\n"
"This method is a convenience method that runs \\make_top_level_pins, \\purge, \\combine_devices and \\purge_nets."
) +
gsi::method_ext ("read", &read_netlist, gsi::arg ("file"), gsi::arg ("reader"),
"@brief Writes the netlist to the given file using the given reader object to parse the file\n"
"See \\NetlistSpiceReader for an example for a parser. "
@ -1264,26 +1551,31 @@ public:
db::NetlistSpiceWriterDelegate::write_device (dev);
}
void org_write_header () const
{
db::NetlistSpiceWriterDelegate::write_header ();
}
gsi::Callback cb_write_header;
gsi::Callback cb_write_device_intro;
gsi::Callback cb_write_device;
};
Class<NetlistSpiceWriterDelegateImpl> db_NetlistSpiceWriterDelegate ("db", "NetlistSpiceWriterDelegate",
gsi::method ("write_header", &NetlistSpiceWriterDelegateImpl::org_write_header, "@hide") +
gsi::callback ("write_header", &NetlistSpiceWriterDelegateImpl::write_header, &NetlistSpiceWriterDelegateImpl::cb_write_header,
"@brief Writes the text at the beginning of the SPICE netlist\n"
"Reimplement this method to insert your own text at the beginning of the file"
) +
gsi::method ("write_device_intro", &NetlistSpiceWriterDelegateImpl::org_write_device_intro, "@hide") +
gsi::callback ("write_device_intro", &NetlistSpiceWriterDelegateImpl::reimpl_write_device_intro, &NetlistSpiceWriterDelegateImpl::cb_write_device_intro, gsi::arg ("device_class"),
"@brief Inserts a text for the given device class\n"
"Reimplement this method to insert your own text at the beginning of the file for the given device class"
) +
gsi::method ("write_device", &NetlistSpiceWriterDelegateImpl::org_write_device, gsi::arg ("device"), "@hide") +
gsi::callback ("write_device", &NetlistSpiceWriterDelegateImpl::reimpl_write_device, &NetlistSpiceWriterDelegateImpl::cb_write_device, gsi::arg ("device"),
"@brief Inserts a text for the given device\n"
"Reimplement this method to write the given device in the desired way"
) +
gsi::method ("write_device", &NetlistSpiceWriterDelegateImpl::org_write_device, gsi::arg ("device"),
"@brief Calls the default implementation of the \\write_device method.\n"
"Reimplement this method to write the given device in the desired way. "
"The default implementation will utilize the device class information to write native SPICE "
"elements for the devices."
) +
@ -1357,8 +1649,15 @@ Class<db::NetlistSpiceWriter> db_NetlistSpiceWriter (db_NetlistWriter, "db", "Ne
"@brief Sets a value indicating whether to use net names (true) or net numbers (false).\n"
"The default is to use net numbers."
) +
gsi::method ("use_net_names", &db::NetlistSpiceWriter::use_net_names,
gsi::method ("use_net_names?", &db::NetlistSpiceWriter::use_net_names,
"@brief Gets a value indicating whether to use net names (true) or net numbers (false).\n"
) +
gsi::method ("with_comments=", &db::NetlistSpiceWriter::set_with_comments, gsi::arg ("f"),
"@brief Sets a value indicating whether to embed comments for position etc. (true) or not (false).\n"
"The default is to embed comments."
) +
gsi::method ("with_comments?", &db::NetlistSpiceWriter::with_comments,
"@brief Gets a value indicating whether to embed comments for position etc. (true) or not (false).\n"
),
"@brief Implements a netlist writer for the SPICE format.\n"
"Provide a delegate for customizing the way devices are written.\n"
@ -1384,7 +1683,7 @@ Class<db::NetlistSpiceWriter> db_NetlistSpiceWriter (db_NetlistWriter, "db", "Ne
"@ul\n"
"@li A global header (\\NetlistSpiceWriterDelegate#write_header): this method is called to print the part right after the headline @/li\n"
"@li A per-device class header (\\NetlistSpiceWriterDelegate#write_device_intro): this method is called for every device class and may print device-class specific headers (e.g. model definitions) @/li\n"
"@li Per-device output: this method (\\NetlistSpiceWriterDelegate#write_device): this method is called for every device and may print the device statement(s) in a specific way.\n"
"@li Per-device output: this method (\\NetlistSpiceWriterDelegate#write_device): this method is called for every device and may print the device statement(s) in a specific way. @/li\n"
"@/ul\n"
"\n"
"The delegate must use \\NetlistSpiceWriterDelegate#emit_line to print a line, \\NetlistSpiceWriterDelegate#emit_comment to print a comment etc.\n"
@ -1405,10 +1704,13 @@ Class<db::NetlistSpiceWriter> db_NetlistSpiceWriter (db_NetlistWriter, "db", "Ne
"\n"
" def write_device(dev)\n"
" if dev.device_class.name != \"MYDEVICE\"\n"
" emit_comment(\"Terminal #1: \" + net_to_string(dev.net_for_terminal(0)))\n"
" emit_comment(\"Terminal #2: \" + net_to_string(dev.net_for_terminal(1)))\n"
" super(dev)\n"
" emit_comment(\"After device \" + dev.expanded_name)\n"
" emit_comment(\"Terminal #1: \" + net_to_string(dev.net_for_terminal(0)))\n"
" emit_comment(\"Terminal #2: \" + net_to_string(dev.net_for_terminal(1)))\n"
" super(dev)\n"
" emit_comment(\"After device \" + dev.expanded_name)\n"
" else\n"
" super(dev)\n"
" end\n"
" end\n"
"\n"
"end\n"
@ -1426,14 +1728,212 @@ Class<db::NetlistReader> db_NetlistReader ("db", "NetlistReader",
"@hide\n"
);
/**
* @brief A SPICE reader delegate base class for reimplementation
*/
class NetlistSpiceReaderDelegateImpl
: public db::NetlistSpiceReaderDelegate, public gsi::ObjectBase
{
public:
NetlistSpiceReaderDelegateImpl ()
: db::NetlistSpiceReaderDelegate ()
{
// .. nothing yet ..
}
virtual void error (const std::string &msg)
{
// doing this avoids passing exceptions through script code which spoils the message
// (the exception will be decorated with a stack trace). TODO: a better solution was
// to define a specific exception type for "raw exception".
m_error = msg;
db::NetlistSpiceReaderDelegate::error (msg);
}
virtual void start (db::Netlist *netlist)
{
try {
m_error.clear ();
if (cb_start.can_issue ()) {
cb_start.issue<db::NetlistSpiceReaderDelegate, db::Netlist *> (&db::NetlistSpiceReaderDelegate::start, netlist);
} else {
db::NetlistSpiceReaderDelegate::start (netlist);
}
} catch (tl::Exception &) {
if (! m_error.empty ()) {
db::NetlistSpiceReaderDelegate::error (m_error);
} else {
throw;
}
}
}
virtual void finish (db::Netlist *netlist)
{
try {
m_error.clear ();
if (cb_finish.can_issue ()) {
cb_finish.issue<db::NetlistSpiceReaderDelegate, db::Netlist *> (&db::NetlistSpiceReaderDelegate::finish, netlist);
} else {
db::NetlistSpiceReaderDelegate::finish (netlist);
}
} catch (tl::Exception &) {
if (! m_error.empty ()) {
db::NetlistSpiceReaderDelegate::error (m_error);
} else {
throw;
}
}
}
virtual bool wants_subcircuit (const std::string &circuit_name)
{
try {
m_error.clear ();
if (cb_wants_subcircuit.can_issue ()) {
return cb_wants_subcircuit.issue<db::NetlistSpiceReaderDelegate, bool, const std::string &> (&db::NetlistSpiceReaderDelegate::wants_subcircuit, circuit_name);
} else {
return db::NetlistSpiceReaderDelegate::wants_subcircuit (circuit_name);
}
} catch (tl::Exception &) {
if (! m_error.empty ()) {
db::NetlistSpiceReaderDelegate::error (m_error);
} else {
throw;
}
return false;
}
}
virtual bool element (db::Circuit *circuit, const std::string &element, const std::string &name, const std::string &model, double value, const std::vector<db::Net *> &nets, const std::map<std::string, double> &params)
{
try {
m_error.clear ();
if (cb_element.can_issue ()) {
return cb_element.issue<db::NetlistSpiceReaderDelegate, bool, db::Circuit *, const std::string &, const std::string &, const std::string &, double, const std::vector<db::Net *> &, const std::map<std::string, double> &> (&db::NetlistSpiceReaderDelegate::element, circuit, element, name, model, value, nets, params);
} else {
return db::NetlistSpiceReaderDelegate::element (circuit, element, name, model, value, nets, params);
}
} catch (tl::Exception &) {
if (! m_error.empty ()) {
db::NetlistSpiceReaderDelegate::error (m_error);
} else {
throw;
}
return false;
}
}
gsi::Callback cb_start;
gsi::Callback cb_finish;
gsi::Callback cb_wants_subcircuit;
gsi::Callback cb_element;
private:
std::string m_error;
};
static void start_fb (db::NetlistSpiceReaderDelegate *delegate, db::Netlist *netlist)
{
delegate->db::NetlistSpiceReaderDelegate::start (netlist);
}
static void finish_fb (db::NetlistSpiceReaderDelegate *delegate, db::Netlist *netlist)
{
delegate->db::NetlistSpiceReaderDelegate::finish (netlist);
}
static bool wants_subcircuit_fb (db::NetlistSpiceReaderDelegate *delegate, const std::string &model)
{
return delegate->db::NetlistSpiceReaderDelegate::wants_subcircuit (model);
}
static bool element_fb (db::NetlistSpiceReaderDelegate *delegate, db::Circuit *circuit, const std::string &element, const std::string &name, const std::string &model, double value, const std::vector<db::Net *> &nets, const std::map<std::string, double> &params)
{
return delegate->db::NetlistSpiceReaderDelegate::element (circuit, element, name, model, value, nets, params);
}
Class<NetlistSpiceReaderDelegateImpl> db_NetlistSpiceReaderDelegate ("db", "NetlistSpiceReaderDelegate",
gsi::method_ext ("start", &start_fb, "@hide") +
gsi::method_ext ("finish", &finish_fb, "@hide") +
gsi::method_ext ("wants_subcircuit", &wants_subcircuit_fb, "@hide") +
gsi::method_ext ("element", &element_fb, "@hide") +
gsi::callback ("start", &NetlistSpiceReaderDelegateImpl::start, &NetlistSpiceReaderDelegateImpl::cb_start, gsi::arg ("netlist"),
"@brief This method is called when the reader starts reading a netlist\n"
) +
gsi::callback ("finish", &NetlistSpiceReaderDelegateImpl::finish, &NetlistSpiceReaderDelegateImpl::cb_finish, gsi::arg ("netlist"),
"@brief This method is called when the reader is done reading a netlist successfully\n"
) +
gsi::callback ("wants_subcircuit", &NetlistSpiceReaderDelegateImpl::wants_subcircuit, &NetlistSpiceReaderDelegateImpl::cb_wants_subcircuit, gsi::arg ("circuit_name"),
"@brief Returns true, if the delegate wants subcircuit elements with this name\n"
"The name is always upper case.\n"
) +
gsi::callback ("element", &NetlistSpiceReaderDelegateImpl::element, &NetlistSpiceReaderDelegateImpl::cb_element,
gsi::arg ("circuit"), gsi::arg ("element"), gsi::arg ("name"), gsi::arg ("model"), gsi::arg ("value"), gsi::arg ("nets"), gsi::arg ("parameters"),
"@brief Makes a device from an element line\n"
"@param circuit The circuit that is currently read.\n"
"@param element The upper-case element code (\"M\", \"R\", ...).\n"
"@param name The element's name.\n"
"@param model The upper-case model name (may be empty).\n"
"@param value The default value (e.g. registance for resistors) and may be zero.\n"
"@param nets The nets given in the element line.\n"
"@param parameters The parameters of the element statement (parameter names are upper case).\n"
"\n"
"The default implementation will create corresponding devices for\n"
"some known elements using the Spice writer's parameter conventions.\n"
"\n"
"The method must return true, if the element was was understood and false otherwise.\n"
) +
gsi::method ("error", &NetlistSpiceReaderDelegateImpl::error, gsi::arg ("msg"),
"@brief Issues an error with the given message.\n"
"Use this method to generate an error."
),
"@brief Provides a delegate for the SPICE reader for translating device statements\n"
"Supply a customized class to provide a specialized reading scheme for devices. "
"You need a customized class if you want to implement device reading from model subcircuits or to "
"translate device parameters.\n"
"\n"
"See \\NetlistSpiceReader for more details.\n"
"\n"
"This class has been introduced in version 0.26."
);
namespace {
class NetlistSpiceReaderWithOwnership
: public db::NetlistSpiceReader
{
public:
NetlistSpiceReaderWithOwnership (NetlistSpiceReaderDelegateImpl *delegate)
: db::NetlistSpiceReader (delegate), m_ownership (delegate)
{
if (delegate) {
delegate->keep ();
}
}
private:
tl::shared_ptr<NetlistSpiceReaderDelegateImpl> m_ownership;
};
}
db::NetlistSpiceReader *new_spice_reader ()
{
return new db::NetlistSpiceReader ();
}
db::NetlistSpiceReader *new_spice_reader2 (NetlistSpiceReaderDelegateImpl *delegate)
{
return new NetlistSpiceReaderWithOwnership (delegate);
}
Class<db::NetlistSpiceReader> db_NetlistSpiceReader (db_NetlistReader, "db", "NetlistSpiceReader",
gsi::constructor ("new", &new_spice_reader,
"@brief Creates a new reader.\n"
) +
gsi::constructor ("new", &new_spice_reader2, gsi::arg ("delegate"),
"@brief Creates a new reader with a delegate.\n"
),
"@brief Implements a netlist Reader for the SPICE format.\n"
"Use the SPICE reader like this:\n"
@ -1444,8 +1944,65 @@ Class<db::NetlistSpiceReader> db_NetlistSpiceReader (db_NetlistReader, "db", "Ne
"netlist.read(path, reader)\n"
"@/code\n"
"\n"
"The translation of SPICE elements can be tailored by providing a \\NetlistSpiceReaderDelegate class. "
"This allows translating of device parameters and mapping of some subcircuits to devices.\n"
"\n"
"The following example is a delegate that turns subcircuits called HVNMOS and HVPMOS into "
"MOS4 devices with the parameters scaled by 1.5:\n"
"\n"
"@code\n"
"class MyDelegate < RBA::NetlistSpiceReaderDelegate\n"
"\n"
" # says we want to catch these subcircuits as devices\n"
" def wants_subcircuit(name)\n"
" name == \"HVNMOS\" || name == \"HVPMOS\"\n"
" end\n"
"\n"
" # translate the element\n"
" def element(circuit, el, name, model, value, nets, params)\n"
"\n"
" if el != \"X\"\n"
" # all other elements are left to the standard implementation\n"
" return super\n"
" end\n"
"\n"
" if nets.size != 4\n"
" error(\"Subcircuit #{model} needs four nodes\")\n"
" end\n"
"\n"
" # provide a device class\n"
" cls = circuit.netlist.device_class_by_name(model)\n"
" if ! cls\n"
" cls = RBA::DeviceClassMOS4Transistor::new\n"
" cls.name = model\n"
" circuit.netlist.add(cls)\n"
" end\n"
"\n"
" # create a device\n"
" device = circuit.create_device(cls, name)\n"
"\n"
" # and configure the device\n"
" [ \"S\", \"G\", \"D\", \"B\" ].each_with_index do |t,index|\n"
" device.connect_terminal(t, nets[index])\n"
" end\n"
" params.each do |p,value|\n"
" device.set_parameter(p, value * 1.5)\n"
" end\n"
"\n"
" end\n"
"\n"
"end\n"
"\n"
"# usage:\n"
"\n"
"mydelegate = MyDelegate::new\n"
"reader = RBA::NetlistSpiceReader::new(mydelegate)\n"
"\n"
"nl = RBA::Netlist::new\n"
"nl.read(input_file, reader)\n"
"@/code\n"
"\n"
"This class has been introduced in version 0.26."
);
}

View File

@ -315,7 +315,13 @@ public:
namespace gsi
{
Class<GenericNetlistCompareLogger> decl_GenericNetlistCompareLogger ("db", "GenericNetlistCompareLogger",
Class<db::NetlistCompareLogger> decl_dbNetlistCompareLogger ("db", "NetlistCompareLogger",
gsi::Methods (),
"@brief A base class for netlist comparer event receivers\n"
"See \\GenericNetlistCompareLogger for custom implementations of such receivers."
);
Class<GenericNetlistCompareLogger> decl_GenericNetlistCompareLogger (decl_dbNetlistCompareLogger, "db", "GenericNetlistCompareLogger",
gsi::callback ("begin_netlist", &GenericNetlistCompareLogger::begin_netlist, &GenericNetlistCompareLogger::cb_begin_netlist, gsi::arg ("a"), gsi::arg ("b"),
"@brief This function is called at the beginning of the compare process.\n"
"This method is called once when the compare run begins.\n"
@ -374,6 +380,10 @@ Class<GenericNetlistCompareLogger> decl_GenericNetlistCompareLogger ("db", "Gene
"@brief This function is called when a net can't be paired.\n"
"This method will be called, if a net cannot be identified as identical with another net. The corresponding argument "
"will identify the net and source netlist. The other argument will be nil.\n"
"\n"
"In some cases, a mismatch is reported with two nets given. This means,\n"
"nets are known not to match. Still the compare algorithm will proceed as\n"
"if these nets were equivalent to derive further matches.\n"
) +
gsi::callback ("match_devices", &GenericNetlistCompareLogger::match_devices, &GenericNetlistCompareLogger::cb_match_devices, gsi::arg ("a"), gsi::arg ("b"),
"@brief This function is called when two devices are identified.\n"
@ -425,7 +435,7 @@ Class<GenericNetlistCompareLogger> decl_GenericNetlistCompareLogger ("db", "Gene
static db::NetlistComparer *make_comparer0 ()
{
return new db::NetlistComparer (0);
return new db::NetlistComparer ();
}
static db::NetlistComparer *make_comparer1 (GenericNetlistCompareLogger *logger)
@ -505,11 +515,17 @@ Class<db::NetlistComparer> decl_dbNetlistComparer ("db", "NetlistComparer",
"@brief Gets the maximum branch complexity\n"
"See \\max_branch_complexity= for details."
) +
gsi::method ("compare", &db::NetlistComparer::compare, gsi::arg ("netlist_a"), gsi::arg ("netlist_b"),
gsi::method ("compare", (bool (db::NetlistComparer::*) (const db::Netlist *, const db::Netlist *) const) &db::NetlistComparer::compare, gsi::arg ("netlist_a"), gsi::arg ("netlist_b"),
"@brief Compares two netlists.\n"
"This method will perform the actual netlist compare. It will return true if both netlists are identical. "
"If the comparer has been configured with \\same_nets or similar methods, the objects given there must "
"be located inside 'circuit_a' and 'circuit_b' respectively."
) +
gsi::method ("compare", (bool (db::NetlistComparer::*) (const db::Netlist *, const db::Netlist *, db::NetlistCompareLogger *) const) &db::NetlistComparer::compare, gsi::arg ("netlist_a"), gsi::arg ("netlist_b"), gsi::arg ("logger"),
"@brief Compares two netlists.\n"
"This method will perform the actual netlist compare using the given logger. It will return true if both netlists are identical. "
"If the comparer has been configured with \\same_nets or similar methods, the objects given there must "
"be located inside 'circuit_a' and 'circuit_b' respectively."
),
"@brief Compares two netlists\n"
"This class performs a comparison of two netlists.\n"

View File

@ -0,0 +1,482 @@
/*
KLayout Layout Viewer
Copyright (C) 2006-2019 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 "gsiDecl.h"
#include "gsiEnums.h"
#include "dbNetlistCrossReference.h"
namespace gsi
{
namespace
{
struct CircuitPairData
{
typedef db::Circuit object_type;
CircuitPairData (const db::Circuit *a, const db::Circuit *b, db::NetlistCrossReference::Status s) : pair (a, b), status (s) { }
CircuitPairData () : pair ((const db::Circuit *)0, (const db::Circuit *)0), status (db::NetlistCrossReference::None) { }
std::pair<const db::Circuit *, const db::Circuit *> pair;
db::NetlistCrossReference::Status status;
};
template <class PairData>
static const typename PairData::object_type *first (const PairData *data)
{
return data->pair.first;
}
template <class PairData>
static const typename PairData::object_type *second (const PairData *data)
{
return data->pair.second;
}
template <class PairData>
static db::NetlistCrossReference::Status status (const PairData *data)
{
return data->status;
}
template <class PairData>
class PairDataClass
: public ChildClass<db::NetlistCrossReference, PairData>
{
public:
PairDataClass (const std::string &module, const std::string &name, const std::string &doc)
: gsi::ChildClass<db::NetlistCrossReference, PairData> (module, name,
gsi::method_ext ("first", &first<PairData>,
"@brief Gets the first object of the relation pair.\n"
"The first object is usually the one obtained from the layout-derived netlist. "
"This member can be nil if the pair is describing a non-matching reference object. "
"In this case, the \\second member is the reference object for which no match was found."
) +
gsi::method_ext ("second", &second<PairData>,
"@brief Gets the second object of the relation pair.\n"
"The first object is usually the one obtained from the reference netlist. "
"This member can be nil if the pair is describing a non-matching layout object. "
"In this case, the \\first member is the layout-derived object for which no match was found."
) +
gsi::method_ext ("status", &status<PairData>,
"@brief Gets the status of the relation.\n"
"This enum described the match status of the relation pair. "
),
doc +
"\n"
"Upon successful match, the \\first and \\second members are the matching objects and \\status is 'Match'."
"\n"
"This object is also used to describe non-matches or match errors. In this case, \\first or \\second may be nil and "
"\\status further describes the case."
)
{ }
};
template <class Obj>
static const Obj *first_of_pair (const std::pair<const Obj *, const Obj *> *pair)
{
return pair->first;
}
template <class Obj>
static const Obj *second_of_pair (const std::pair<const Obj *, const Obj *> *pair)
{
return pair->second;
}
template <class Obj>
class NetObjectPairClass
: public ChildClass<db::NetlistCrossReference, std::pair<const Obj *, const Obj *> >
{
public:
NetObjectPairClass (const std::string &module, const std::string &name, const std::string &doc)
: gsi::ChildClass<db::NetlistCrossReference, std::pair<const Obj *, const Obj *> > (module, name,
gsi::method_ext ("first", &first_of_pair<Obj>,
"@brief Gets the first object of the relation pair.\n"
"The first object is usually the one obtained from the layout-derived netlist. "
"This member can be nil if the pair is describing a non-matching reference object. "
"In this case, the \\second member is the reference object for which no match was found."
) +
gsi::method_ext ("second", &second_of_pair<Obj>,
"@brief Gets the second object of the relation pair.\n"
"The first object is usually the one obtained from the reference netlist. "
"This member can be nil if the pair is describing a non-matching layout object. "
"In this case, the \\first member is the layout-derived object for which no match was found."
),
doc +
"\n"
"Upon successful match, the \\first and \\second members are the matching net objects."
"Otherwise, either \\first or \\second is nil and the other member is the object for "
"which no match was found."
)
{ }
};
}
PairDataClass<db::NetlistCrossReference::NetPairData> decl_dbNetlistCrossReference_NetPairData ("db", "NetPairData",
"@brief A net match entry.\n"
"This object is used to describe the relationship of two nets in a netlist match.\n"
);
PairDataClass<db::NetlistCrossReference::DevicePairData> decl_dbNetlistCrossReference_DevicePairData ("db", "DevicePairData",
"@brief A device match entry.\n"
"This object is used to describe the relationship of two devices in a netlist match.\n"
);
PairDataClass<db::NetlistCrossReference::PinPairData> decl_dbNetlistCrossReference_PinPairData ("db", "PinPairData",
"@brief A pin match entry.\n"
"This object is used to describe the relationship of two circuit pins in a netlist match.\n"
);
PairDataClass<db::NetlistCrossReference::SubCircuitPairData> decl_dbNetlistCrossReference_SubCircuitPairData ("db", "SubCircuitPairData",
"@brief A subcircuit match entry.\n"
"This object is used to describe the relationship of two subcircuits in a netlist match.\n"
);
PairDataClass<CircuitPairData> decl_dbNetlistCrossReference_CircuitPairData ("db", "CircuitPairData",
"@brief A circuit match entry.\n"
"This object is used to describe the relationship of two circuits in a netlist match.\n"
);
NetObjectPairClass<db::NetTerminalRef> decl_dbNetlistCrossReference_NetTerminalRefPair ("db", "NetTerminalRefPair",
"@brief A match entry for a net terminal pair.\n"
"This object is used to describe the matching terminal pairs or non-matching terminals on a net.\n"
);
NetObjectPairClass<db::NetPinRef> decl_dbNetlistCrossReference_NetPinRefPair ("db", "NetPinRefPair",
"@brief A match entry for a net pin pair.\n"
"This object is used to describe the matching pin pairs or non-matching pins on a net.\n"
);
NetObjectPairClass<db::NetSubcircuitPinRef> decl_dbNetlistCrossReference_NetSubcircuitPinRefPair ("db", "NetSubcircuitPinRefPair",
"@brief A match entry for a net subcircuit pin pair.\n"
"This object is used to describe the matching subcircuit pin pairs or non-matching subcircuit pins on a net.\n"
);
extern Class<db::NetlistCompareLogger> decl_dbNetlistCompareLogger;
namespace {
class CircuitPairIterator
{
public:
// makes STL happy:
typedef std::forward_iterator_tag iterator_category;
typedef CircuitPairData value_type;
typedef size_t difference_type;
typedef const CircuitPairData *pointer;
typedef const CircuitPairData &reference;
CircuitPairIterator (db::NetlistCrossReference *xref)
: m_xref (xref), m_iter (xref->begin_circuits ()), m_end_iter (xref->end_circuits ())
{ }
bool at_end () const
{
return m_xref.get () == 0 || m_iter == m_end_iter;
}
CircuitPairIterator &operator++ ()
{
++m_iter;
return *this;
}
pointer operator-> () const
{
m_data.pair = *m_iter;
const db::NetlistCrossReference::PerCircuitData *data = m_xref->per_circuit_data_for (*m_iter);
tl_assert (data != 0);
m_data.status = data->status;
return &m_data;
}
reference operator* () const
{
return *operator-> ();
}
tl::weak_ptr<db::NetlistCrossReference> m_xref;
mutable CircuitPairData m_data;
db::NetlistCrossReference::circuits_iterator m_iter, m_end_iter;
};
template <class PairData, class PairDataIter>
class pair_data_iterator
{
public:
// makes STL happy:
typedef std::forward_iterator_tag iterator_category;
typedef PairData value_type;
typedef size_t difference_type;
typedef const PairData *pointer;
typedef const PairData &reference;
pair_data_iterator ()
: m_xref (), m_iter (), m_end_iter ()
{ }
pair_data_iterator (db::NetlistCrossReference *xref, const PairDataIter &iter, const PairDataIter &end_iter)
: m_xref (xref), m_iter (iter), m_end_iter (end_iter)
{ }
bool at_end () const
{
return m_xref.get () == 0 || m_iter == m_end_iter;
}
pair_data_iterator &operator++ ()
{
++m_iter;
return *this;
}
pointer operator-> () const
{
return m_iter.operator-> ();
}
reference operator* () const
{
return *operator-> ();
}
tl::weak_ptr<db::NetlistCrossReference> m_xref;
PairDataIter m_iter, m_end_iter;
};
}
static CircuitPairIterator each_circuit_pair (db::NetlistCrossReference *xref)
{
tl_assert (xref->netlist_a () != 0 && xref->netlist_b () != 0);
return CircuitPairIterator (xref);
}
static pair_data_iterator<db::NetlistCrossReference::NetPairData, db::NetlistCrossReference::PerCircuitData::net_pairs_const_iterator> each_net_pair (db::NetlistCrossReference *xref, const CircuitPairData &circuit_pair)
{
tl_assert (xref->netlist_a () != 0 && xref->netlist_b () != 0);
typedef pair_data_iterator<db::NetlistCrossReference::NetPairData, db::NetlistCrossReference::PerCircuitData::net_pairs_const_iterator> iter_type;
const db::NetlistCrossReference::PerCircuitData *data = xref->per_circuit_data_for (circuit_pair.pair);
if (! data) {
return iter_type ();
} else {
return iter_type (xref, data->nets.begin (), data->nets.end ());
}
}
static pair_data_iterator<db::NetlistCrossReference::DevicePairData, db::NetlistCrossReference::PerCircuitData::device_pairs_const_iterator> each_device_pair (db::NetlistCrossReference *xref, const CircuitPairData &circuit_pair)
{
tl_assert (xref->netlist_a () != 0 && xref->netlist_b () != 0);
typedef pair_data_iterator<db::NetlistCrossReference::DevicePairData, db::NetlistCrossReference::PerCircuitData::device_pairs_const_iterator> iter_type;
const db::NetlistCrossReference::PerCircuitData *data = xref->per_circuit_data_for (circuit_pair.pair);
if (! data) {
return iter_type ();
} else {
return iter_type (xref, data->devices.begin (), data->devices.end ());
}
}
static pair_data_iterator<db::NetlistCrossReference::PinPairData, db::NetlistCrossReference::PerCircuitData::pin_pairs_const_iterator> each_pin_pair (db::NetlistCrossReference *xref, const CircuitPairData &circuit_pair)
{
tl_assert (xref->netlist_a () != 0 && xref->netlist_b () != 0);
typedef pair_data_iterator<db::NetlistCrossReference::PinPairData, db::NetlistCrossReference::PerCircuitData::pin_pairs_const_iterator> iter_type;
const db::NetlistCrossReference::PerCircuitData *data = xref->per_circuit_data_for (circuit_pair.pair);
if (! data) {
return iter_type ();
} else {
return iter_type (xref, data->pins.begin (), data->pins.end ());
}
}
static pair_data_iterator<db::NetlistCrossReference::SubCircuitPairData, db::NetlistCrossReference::PerCircuitData::subcircuit_pairs_const_iterator> each_subcircuit_pair (db::NetlistCrossReference *xref, const CircuitPairData &circuit_pair)
{
tl_assert (xref->netlist_a () != 0 && xref->netlist_b () != 0);
typedef pair_data_iterator<db::NetlistCrossReference::SubCircuitPairData, db::NetlistCrossReference::PerCircuitData::subcircuit_pairs_const_iterator> iter_type;
const db::NetlistCrossReference::PerCircuitData *data = xref->per_circuit_data_for (circuit_pair.pair);
if (! data) {
return iter_type ();
} else {
return iter_type (xref, data->subcircuits.begin (), data->subcircuits.end ());
}
}
static pair_data_iterator<std::pair<const db::NetTerminalRef *, const db::NetTerminalRef *>, db::NetlistCrossReference::PerNetData::terminal_pairs_const_iterator> each_net_terminal_pair (db::NetlistCrossReference *xref, const db::NetlistCrossReference::NetPairData &net_pair)
{
tl_assert (xref->netlist_a () != 0 && xref->netlist_b () != 0);
typedef pair_data_iterator<std::pair<const db::NetTerminalRef *, const db::NetTerminalRef *>, db::NetlistCrossReference::PerNetData::terminal_pairs_const_iterator> iter_type;
const db::NetlistCrossReference::PerNetData *data = xref->per_net_data_for (net_pair.pair);
if (! data) {
return iter_type ();
} else {
return iter_type (xref, data->terminals.begin (), data->terminals.end ());
}
}
static pair_data_iterator<std::pair<const db::NetPinRef *, const db::NetPinRef *>, db::NetlistCrossReference::PerNetData::pin_pairs_const_iterator> each_net_pin_pair (db::NetlistCrossReference *xref, const db::NetlistCrossReference::NetPairData &net_pair)
{
tl_assert (xref->netlist_a () != 0 && xref->netlist_b () != 0);
typedef pair_data_iterator<std::pair<const db::NetPinRef *, const db::NetPinRef *>, db::NetlistCrossReference::PerNetData::pin_pairs_const_iterator> iter_type;
const db::NetlistCrossReference::PerNetData *data = xref->per_net_data_for (net_pair.pair);
if (! data) {
return iter_type ();
} else {
return iter_type (xref, data->pins.begin (), data->pins.end ());
}
}
static pair_data_iterator<std::pair<const db::NetSubcircuitPinRef *, const db::NetSubcircuitPinRef *>, db::NetlistCrossReference::PerNetData::subcircuit_pin_pairs_const_iterator> each_net_subcircuit_pin_pair (db::NetlistCrossReference *xref, const db::NetlistCrossReference::NetPairData &net_pair)
{
tl_assert (xref->netlist_a () != 0 && xref->netlist_b () != 0);
typedef pair_data_iterator<std::pair<const db::NetSubcircuitPinRef *, const db::NetSubcircuitPinRef *>, db::NetlistCrossReference::PerNetData::subcircuit_pin_pairs_const_iterator> iter_type;
const db::NetlistCrossReference::PerNetData *data = xref->per_net_data_for (net_pair.pair);
if (! data) {
return iter_type ();
} else {
return iter_type (xref, data->subcircuit_pins.begin (), data->subcircuit_pins.end ());
}
}
Class<db::NetlistCrossReference> decl_dbNetlistCrossReference (decl_dbNetlistCompareLogger, "db", "NetlistCrossReference",
gsi::iterator_ext ("each_circuit_pair", &each_circuit_pair,
"@brief Delivers the circuit pairs and their status.\n"
"See the class description for details."
) +
gsi::iterator_ext ("each_net_pair", &each_net_pair, gsi::arg ("circuit_pair"),
"@brief Delivers the net pairs and their status for the given circuit pair.\n"
"See the class description for details."
) +
gsi::iterator_ext ("each_device_pair", &each_device_pair, gsi::arg ("circuit_pair"),
"@brief Delivers the device pairs and their status for the given circuit pair.\n"
"See the class description for details."
) +
gsi::iterator_ext ("each_pin_pair", &each_pin_pair, gsi::arg ("circuit_pair"),
"@brief Delivers the pin pairs and their status for the given circuit pair.\n"
"See the class description for details."
) +
gsi::iterator_ext ("each_subcircuit_pair", &each_subcircuit_pair, gsi::arg ("circuit_pair"),
"@brief Delivers the subcircuit pairs and their status for the given circuit pair.\n"
"See the class description for details."
) +
gsi::iterator_ext ("each_net_terminal_pair", &each_net_terminal_pair, gsi::arg ("net_pair"),
"@brief Delivers the device terminal pairs for the given net pair.\n"
"For the net pair, lists the device terminal pairs identified on this net."
) +
gsi::iterator_ext ("each_net_pin_pair", &each_net_pin_pair, gsi::arg ("net_pair"),
"@brief Delivers the pin pairs for the given net pair.\n"
"For the net pair, lists the pin pairs identified on this net."
) +
gsi::iterator_ext ("each_net_subcircuit_pin_pair", &each_net_subcircuit_pin_pair, gsi::arg ("net_pair"),
"@brief Delivers the subcircuit pin pairs for the given net pair.\n"
"For the net pair, lists the subcircuit pin pairs identified on this net."
) +
gsi::method ("other_net_for", &db::NetlistCrossReference::other_net_for, gsi::arg ("net"),
"@brief Gets the matching other net for a given primary net.\n"
"The return value will be nil if no match is found. "
"Otherwise it is the 'b' net for nets from the 'a' netlist and vice versa."
) +
gsi::method ("clear", &db::NetlistCrossReference::clear,
"@hide\n"
) +
gsi::method ("circuit_count", &db::NetlistCrossReference::circuit_count,
"@brief Gets the number of circuit pairs in the cross-reference object."
) +
gsi::method ("netlist_a", &db::NetlistCrossReference::netlist_a,
"@brief Gets the first netlist which participated in the compare.\n"
"This member may be nil, if the respective netlist is no longer valid. "
"In this case, the netlist cross-reference object cannot be used."
) +
gsi::method ("netlist_b", &db::NetlistCrossReference::netlist_b,
"@brief Gets the second netlist which participated in the compare.\n"
"This member may be nil, if the respective netlist is no longer valid."
"In this case, the netlist cross-reference object cannot be used."
),
"@brief Represents the identity mapping between the objects of two netlists.\n"
"\n"
"The NetlistCrossReference object is a container for the results of a netlist comparison. "
"It implemented the \\NetlistCompareLogger interface, hence can be used as output for "
"a netlist compare operation (\\NetlistComparer#compare). It's purpose is to store the "
"results of the compare. It is used in this sense inside the \\LayoutVsSchematic framework.\n"
"\n"
"The basic idea of the cross reference object is pairing: the netlist comparer will try "
"to identify matching items and store them as pairs inside the cross reference object. "
"If no match is found, a single-sided pair is generated: one item is nil in this case.\n"
"Beside the items, a status is kept which gives more details about success or failure of the "
"match operation.\n"
"\n"
"Item pairing happens on different levels, reflecting the hierarchy of the netlists. "
"On the top level there are circuits. Inside circuits nets, devices, subcircuits and pins "
"are paired. Nets further contribute their connected items through terminals (for devices), "
"pins (outgoing) and subcircuit pins.\n"
"\n"
"This class has been introduced in version 0.26."
);
gsi::EnumIn<db::NetlistCrossReference, db::NetlistCrossReference::Status> decl_dbNetlistCrossReference_Status ("db", "Status",
gsi::enum_const ("None", db::NetlistCrossReference::None,
"@brief Enum constant NetlistCrossReference::None\n"
"No specific status is implied if this code is present."
) +
gsi::enum_const ("Match", db::NetlistCrossReference::Match,
"@brief Enum constant NetlistCrossReference::Match\n"
"An exact match exists if this code is present.\n"
) +
gsi::enum_const ("NoMatch", db::NetlistCrossReference::NoMatch,
"@brief Enum constant NetlistCrossReference::NoMatch\n"
"If this code is present, no match could be found.\n"
"There is also 'Mismatch' which means there is a candidate, but exact "
"identity could not be confirmed."
) +
gsi::enum_const ("Skipped", db::NetlistCrossReference::Skipped,
"@brief Enum constant NetlistCrossReference::Skipped\n"
"On circuits this code means that a match has not been attempted because "
"subcircuits of this circuits were not matched. As circuit matching happens "
"bottom-up, all subcircuits must match at least with respect to their pins "
"to allow any parent circuit to be matched."
) +
gsi::enum_const ("MatchWithWarning", db::NetlistCrossReference::MatchWithWarning,
"@brief Enum constant NetlistCrossReference::MatchWithWarning\n"
"If this code is present, a match was found but a warning is issued. For nets, this "
"means that the choice is ambiguous and one, unspecific candidate has been chosen. "
"For devices, this means a device match was established, but parameters or the device class "
"are not matching exactly."
) +
gsi::enum_const ("Mismatch", db::NetlistCrossReference::Mismatch,
"@brief Enum constant NetlistCrossReference::Mismatch\n"
"This code means there is a match candidate, but exact identity could not be confirmed."
),
"@brief This class represents the NetlistCrossReference::Status enum"
);
// Inject the NetlistCrossReference::Status declarations into NetlistCrossReference:
gsi::ClassExt<db::NetlistCrossReference> inject_NetlistCrossReference_Status_in_parent (decl_dbNetlistCrossReference_Status.defs ());
}

View File

@ -37,12 +37,39 @@ Class<db::DeviceClassResistor> decl_dbDeviceClassResistor (decl_dbDeviceClass, "
) +
gsi::constant ("PARAM_R", db::DeviceClassResistor::param_id_R,
"@brief A constant giving the parameter ID for parameter R"
) +
gsi::constant ("PARAM_L", db::DeviceClassResistor::param_id_L,
"@brief A constant giving the parameter ID for parameter L"
) +
gsi::constant ("PARAM_W", db::DeviceClassResistor::param_id_W,
"@brief A constant giving the parameter ID for parameter W"
) +
gsi::constant ("PARAM_A", db::DeviceClassResistor::param_id_A,
"@brief A constant giving the parameter ID for parameter A"
) +
gsi::constant ("PARAM_P", db::DeviceClassResistor::param_id_P,
"@brief A constant giving the parameter ID for parameter P"
),
"@brief A device class for a resistor.\n"
"This class can be used to describe resistors. Resistors are defined by their combination behavior and "
"This class describes a resistor. Resistors are defined by their combination behavior and "
"the basic parameter 'R' which is the resistance in Ohm.\n"
"\n"
"A resistor has two terminals, A and B.\n"
"The parameters of a resistor are R (the value in Ohms), L and W (length and width in micrometers) and "
"A and P (area and perimeter in square micrometers and micrometers respectively).\n"
"\n"
"This class has been introduced in version 0.26."
);
Class<db::DeviceClassResistorWithBulk> decl_dbDeviceClassResistorWithBulk (decl_dbDeviceClassResistor, "db", "DeviceClassResistorWithBulk",
gsi::constant ("TERMINAL_W", db::DeviceClassResistorWithBulk::terminal_id_W,
"@brief A constant giving the terminal ID for terminal W (well, bulk)"
),
"@brief A device class for a resistor with a bulk terminal (substrate, well).\n"
"This class is similar to \\DeviceClassResistor, but provides an additional terminal (BULK) for the "
"well or substrate the resistor is embedded in.\n"
"\n"
"The additional terminal is 'W' for the well/substrate terminal.\n"
"\n"
"This class has been introduced in version 0.26."
);
@ -56,12 +83,33 @@ Class<db::DeviceClassCapacitor> decl_dbDeviceClassCapacitor (decl_dbDeviceClass,
) +
gsi::constant ("PARAM_C", db::DeviceClassCapacitor::param_id_C,
"@brief A constant giving the parameter ID for parameter C"
) +
gsi::constant ("PARAM_A", db::DeviceClassCapacitor::param_id_A,
"@brief A constant giving the parameter ID for parameter A"
) +
gsi::constant ("PARAM_P", db::DeviceClassCapacitor::param_id_P,
"@brief A constant giving the parameter ID for parameter P"
),
"@brief A device class for a capacitor.\n"
"This class can be used to describe capacitors. Capacitors are defined by their combination behavior and "
"This describes a capacitor. Capacitors are defined by their combination behavior and "
"the basic parameter 'C' which is the capacitance in Farad.\n"
"\n"
"A capacitor has two terminals, A and B.\n"
"The parameters of a capacitor are C (the value in Farad) and "
"A and P (area and perimeter in square micrometers and micrometers respectively).\n"
"\n"
"This class has been introduced in version 0.26."
);
Class<db::DeviceClassCapacitorWithBulk> decl_dbDeviceClassCapacitorWithBulk (decl_dbDeviceClassCapacitor, "db", "DeviceClassCapacitorWithBulk",
gsi::constant ("TERMINAL_W", db::DeviceClassCapacitorWithBulk::terminal_id_W,
"@brief A constant giving the terminal ID for terminal W (well, bulk)"
),
"@brief A device class for a capacitor with a bulk terminal (substrate, well).\n"
"This class is similar to \\DeviceClassCapacitor, but provides an additional terminal (BULK) for the "
"well or substrate the capacitor is embedded in.\n"
"\n"
"The additional terminal is 'W' for the well/substrate terminal.\n"
"\n"
"This class has been introduced in version 0.26."
);
@ -77,7 +125,7 @@ Class<db::DeviceClassInductor> decl_dbDeviceClassInductor (decl_dbDeviceClass, "
"@brief A constant giving the parameter ID for parameter L"
),
"@brief A device class for an inductor.\n"
"This class can be used to describe inductors. Inductors are defined by their combination behavior and "
"This class describes an inductor. Inductors are defined by their combination behavior and "
"the basic parameter 'L' which is the inductance in Henry.\n"
"\n"
"An inductor has two terminals, A and B.\n"
@ -94,14 +142,73 @@ Class<db::DeviceClassDiode> decl_dbDeviceClassDiode (decl_dbDeviceClass, "db", "
) +
gsi::constant ("PARAM_A", db::DeviceClassDiode::param_id_A,
"@brief A constant giving the parameter ID for parameter A"
) +
gsi::constant ("PARAM_P", db::DeviceClassDiode::param_id_P,
"@brief A constant giving the parameter ID for parameter P"
),
"@brief A device class for a diode.\n"
"This class can be used to describe diodes. Diodes are defined by their combination behavior and "
"the basic parameter 'A' which is their area in square micrometers.\n"
"This class descibes a diode.\n"
"A diode has two terminals, A (anode) and C (cathode).\n"
"It has two parameters: The diode area in square micrometers (A) and the diode area perimeter in micrometers (P).\n"
"\n"
"Diodes only combine when parallel and in the same direction. In this case, their areas are added."
"Diodes only combine when parallel and in the same direction. In this case, their areas and perimeters are added."
"\n"
"An inductor has two terminals, A (anode) and C (cathode).\n"
"This class has been introduced in version 0.26."
);
Class<db::DeviceClassBJT3Transistor> decl_dbDeviceClassBJT3Transistor (decl_dbDeviceClass, "db", "DeviceClassBJT3Transistor",
gsi::constant ("TERMINAL_C", db::DeviceClassBJT3Transistor::terminal_id_C,
"@brief A constant giving the terminal ID for terminal C (collector)"
) +
gsi::constant ("TERMINAL_B", db::DeviceClassBJT3Transistor::terminal_id_B,
"@brief A constant giving the terminal ID for terminal B (base)"
) +
gsi::constant ("TERMINAL_E", db::DeviceClassBJT3Transistor::terminal_id_E,
"@brief A constant giving the terminal ID for terminal E (emitter)"
) +
gsi::constant ("PARAM_AE", db::DeviceClassBJT3Transistor::param_id_AE,
"@brief A constant giving the parameter ID for parameter AE (emitter area)"
) +
gsi::constant ("PARAM_PE", db::DeviceClassBJT3Transistor::param_id_PE,
"@brief A constant giving the parameter ID for parameter PE (emitter perimeter)"
) +
gsi::constant ("PARAM_AB", db::DeviceClassBJT3Transistor::param_id_AB,
"@brief A constant giving the parameter ID for parameter AB (base area)"
) +
gsi::constant ("PARAM_PB", db::DeviceClassBJT3Transistor::param_id_PB,
"@brief A constant giving the parameter ID for parameter PB (base perimeter)"
) +
gsi::constant ("PARAM_AC", db::DeviceClassBJT3Transistor::param_id_AC,
"@brief A constant giving the parameter ID for parameter AC (collector area)"
) +
gsi::constant ("PARAM_PC", db::DeviceClassBJT3Transistor::param_id_PC,
"@brief A constant giving the parameter ID for parameter PC (collector perimeter)"
) +
gsi::constant ("PARAM_NE", db::DeviceClassBJT3Transistor::param_id_NE,
"@brief A constant giving the parameter ID for parameter NE (emitter count)"
),
"@brief A device class for a bipolar transistor.\n"
"This class describes a bipolar transistor. Bipolar transistors have tree terminals: the collector (C), the base (B) and the emitter (E).\n"
"Multi-emitter transistors are resolved in individual devices."
"\n"
"The parameters are AE, AB and AC for the emitter, base and collector areas in square micrometers and "
"PE, PB and PC for the emitter, base and collector perimeters in micrometers.\n"
"In addition, the emitter count (NE) is given. The emitter count is 1 always for a transistor extracted initially. "
"Upon combination of devices, the emitter counts are added, thus forming multi-emitter devices.\n"
"\n"
"This class has been introduced in version 0.26."
);
Class<db::DeviceClassBJT4Transistor> decl_dbDeviceClassBJT4Transistor (decl_dbDeviceClassBJT3Transistor, "db", "DeviceClassBJT4Transistor",
gsi::constant ("TERMINAL_S", db::DeviceClassBJT4Transistor::terminal_id_S,
"@brief A constant giving the terminal ID for terminal S"
),
"@brief A device class for a 4-terminal bipolar transistor.\n"
"This class describes a bipolar transistor with a substrate terminal. "
"A device class for a bipolar transistor without a substrate terminal is \\DeviceClassBJT3Transistor. "
"\n"
"The additional terminal is 'S' for the substrate terminal.\n"
"BJT4 transistors combine in parallel if both substrate terminals are connected to the same net.\n"
"\n"
"This class has been introduced in version 0.26."
);
@ -135,7 +242,7 @@ Class<db::DeviceClassMOS3Transistor> decl_dbDeviceClassMOS3Transistor (decl_dbDe
"@brief A constant giving the parameter ID for parameter PD"
),
"@brief A device class for a 3-terminal MOS transistor.\n"
"This class can be used to describe MOS transistors without a bulk terminal. "
"This class describes a MOS transistor without a bulk terminal. "
"A device class for a MOS transistor with a bulk terminal is \\DeviceClassMOS4Transistor. "
"MOS transistors are defined by their combination behavior and the basic parameters.\n"
"\n"
@ -151,50 +258,17 @@ Class<db::DeviceClassMOS3Transistor> decl_dbDeviceClassMOS3Transistor (decl_dbDe
"This class has been introduced in version 0.26."
);
Class<db::DeviceClassMOS4Transistor> decl_dbDeviceClassMOS4Transistor (decl_dbDeviceClass, "db", "DeviceClassMOS4Transistor",
gsi::constant ("TERMINAL_S", db::DeviceClassMOS4Transistor::terminal_id_S,
"@brief A constant giving the terminal ID for terminal S"
) +
gsi::constant ("TERMINAL_D", db::DeviceClassMOS4Transistor::terminal_id_D,
"@brief A constant giving the terminal ID for terminal D"
) +
gsi::constant ("TERMINAL_G", db::DeviceClassMOS4Transistor::terminal_id_G,
"@brief A constant giving the terminal ID for terminal G"
) +
Class<db::DeviceClassMOS4Transistor> decl_dbDeviceClassMOS4Transistor (decl_dbDeviceClassMOS3Transistor, "db", "DeviceClassMOS4Transistor",
gsi::constant ("TERMINAL_B", db::DeviceClassMOS4Transistor::terminal_id_B,
"@brief A constant giving the terminal ID for terminal B"
) +
gsi::constant ("PARAM_L", db::DeviceClassMOS4Transistor::param_id_L,
"@brief A constant giving the parameter ID for parameter L"
) +
gsi::constant ("PARAM_W", db::DeviceClassMOS4Transistor::param_id_W,
"@brief A constant giving the parameter ID for parameter W"
) +
gsi::constant ("PARAM_AS", db::DeviceClassMOS4Transistor::param_id_AS,
"@brief A constant giving the parameter ID for parameter AS"
) +
gsi::constant ("PARAM_AD", db::DeviceClassMOS4Transistor::param_id_AD,
"@brief A constant giving the parameter ID for parameter AD"
) +
gsi::constant ("PARAM_PS", db::DeviceClassMOS4Transistor::param_id_PS,
"@brief A constant giving the parameter ID for parameter PS"
) +
gsi::constant ("PARAM_PD", db::DeviceClassMOS4Transistor::param_id_PD,
"@brief A constant giving the parameter ID for parameter PD"
),
"@brief A device class for a 4-terminal MOS transistor.\n"
"This class can be used to describe MOS transistors with a bulk terminal. "
"This class describes a MOS transistor with a bulk terminal. "
"A device class for a MOS transistor without a bulk terminal is \\DeviceClassMOS3Transistor. "
"MOS transistors are defined by their combination behavior and the basic parameters.\n"
"\n"
"The parameters are L, W, AS, AD, PS and PD for the gate length and width in micrometers, source and drain area "
"in square micrometers and the source and drain perimeter in micrometers.\n"
"\n"
"The terminals are S, G, D and B for source, gate, drain and bulk.\n"
"\n"
"MOS transistors combine in parallel mode, when both gate lengths are identical and "
"their gates and bulk terminals are connected (source and drain can be swapped). In this case, their widths and source and drain "
"areas are added.\n"
"The additional terminal is 'B' for the bulk terminal.\n"
"MOS4 transistors combine in parallel if both bulk terminals are connected to the same net.\n"
"\n"
"This class has been introduced in version 0.26."
);

View File

@ -183,6 +183,11 @@ static size_t ld_index (const db::NetlistDeviceExtractorLayerDefinition *ld)
return ld->index;
}
static size_t ld_fallback_index (const db::NetlistDeviceExtractorLayerDefinition *ld)
{
return ld->fallback_index;
}
Class<db::NetlistDeviceExtractorLayerDefinition> decl_dbNetlistDeviceExtractorLayerDefinition ("db", "NetlistDeviceExtractorLayerDefinition",
gsi::method_ext ("name", &ld_name,
"@brief Gets the name of the layer.\n"
@ -192,6 +197,10 @@ Class<db::NetlistDeviceExtractorLayerDefinition> decl_dbNetlistDeviceExtractorLa
) +
gsi::method_ext ("index", &ld_index,
"@brief Gets the index of the layer.\n"
) +
gsi::method_ext ("fallback_index", &ld_fallback_index,
"@brief Gets the index of the fallback layer.\n"
"This is the index of the layer to be used when this layer isn't specified for input or (more important) output.\n"
),
"@brief Describes a layer used in the device extraction\n"
"This read-only structure is used to describe a layer in the device extraction.\n"
@ -267,13 +276,23 @@ Class<GenericDeviceExtractor> decl_GenericDeviceExtractor (decl_dbNetlistDeviceE
"This method shall be used inside the implementation of \\setup to register\n"
"the device classes.\n"
) +
gsi::method ("define_layer", &GenericDeviceExtractor::define_layer, gsi::arg ("name"), gsi::arg ("description"),
gsi::method ("define_layer", (const db::NetlistDeviceExtractorLayerDefinition &(GenericDeviceExtractor::*) (const std::string &name, const std::string &)) &GenericDeviceExtractor::define_layer, gsi::arg ("name"), gsi::arg ("description"),
"@brief Defines a layer.\n"
"@return The layer descriptor object created for this layer (use 'index' to get the layer's index)\n"
"Each call will define one more layer for the device extraction.\n"
"This method shall be used inside the implementation of \\setup to define\n"
"the device layers. The actual geometries are later available to \\extract_devices\n"
"in the order the layers are defined.\n"
) +
gsi::method ("define_opt_layer", (const db::NetlistDeviceExtractorLayerDefinition &(GenericDeviceExtractor::*) (const std::string &name, const std::string &)) &GenericDeviceExtractor::define_layer, gsi::arg ("name"), gsi::arg ("description"),
"@brief Defines a layer with a fallback layer.\n"
"@return The layer descriptor object created for this layer (use 'index' to get the layer's index)\n"
"As \\define_layer, this method allows specification of device extraction layer. In addition to \\define_layout, it features "
"a fallback layer. If in the device extraction statement, the primary layer is not given, "
"the fallback layer will be used. Hence, this layer is optional. The fallback layer is given by it's "
"index and must be defined before the layer using the fallback layer is defined. "
"For the index, 0 is the first layer defined, 1 the second and so forth."
) +
gsi::method ("create_device", &GenericDeviceExtractor::create_device,
"@brief Creates a device.\n"
"The device object returned can be configured by the caller, e.g. set parameters.\n"
@ -392,7 +411,10 @@ Class<db::NetlistDeviceExtractorMOS3Transistor> decl_NetlistDeviceExtractorMOS3T
"conductive layer.\n"
"\n"
"The device class produced by this extractor is \\DeviceClassMOS3Transistor.\n"
"The extractor extracts the four parameters of this class: L, W, AS and AD.\n"
"The extractor extracts the six parameters of this class: L, W, AS, AD, PS and PD.\n"
"\n"
"The device recognition layer names are 'SD' (source/drain) and 'G' (gate).\n"
"The terminal output layer names are 'tS' (source), 'tG' (gate) and 'tD' (drain).\n"
"\n"
"The diffusion area is distributed on the number of gates connecting to\n"
"the particular source or drain area.\n"
@ -415,22 +437,231 @@ Class<db::NetlistDeviceExtractorMOS4Transistor> decl_NetlistDeviceExtractorMOS4T
"@brief A device extractor for a four-terminal MOS transistor\n"
"\n"
"This class supplies the generic extractor for a MOS device.\n"
"The device is defined by two basic input layers: the diffusion area\n"
"(source and drain) and the gate area. It requires a third layer\n"
"(poly) to put the gate terminals on and a forth layer to put the bulk\n"
"terminal an. The separation between poly\n"
"and allows separating the device recognition layer (gate) from the\n"
"conductive layer.\n"
"It is based on the \\DeviceExtractorMOS3Transistor class with the extension of a bulk terminal "
"and corresponding bulk terminal output (annotation) layer.\n"
"\n"
"The bulk terminal layer can be an empty layer representing the substrate.\n"
"In this use mode the bulk terminal shapes will be produced there. This\n"
"layer then needs to be connected to a global net to establish the net.\n"
"The bulk terminal layer ('tB') can be an empty layer representing the substrate.\n"
"In this use mode the bulk terminal shapes will be produced on the 'tB' layer. This\n"
"layer then needs to be connected to a global net to establish the net connection.\n"
"\n"
"The device class produced by this extractor is \\DeviceClassMOS4Transistor.\n"
"The extractor extracts the four parameters of this class: L, W, AS and AD.\n"
"The "
"This class is a closed one and methods cannot be reimplemented. To reimplement "
"specific methods, see \\DeviceExtractor.\n"
"\n"
"The diffusion area is distributed on the number of gates connecting to\n"
"the particular source or drain area.\n"
"This class has been introduced in version 0.26."
);
db::NetlistDeviceExtractorResistor *make_res_extractor (const std::string &name, double sheet_rho)
{
return new db::NetlistDeviceExtractorResistor (name, sheet_rho);
}
Class<db::NetlistDeviceExtractorResistor> decl_NetlistDeviceExtractorResistor (decl_dbNetlistDeviceExtractor, "db", "DeviceExtractorResistor",
gsi::constructor ("new", &make_res_extractor, gsi::arg ("name"), gsi::arg ("sheet_rho"),
"@brief Creates a new device extractor with the given name."
),
"@brief A device extractor for a two-terminal resistor\n"
"\n"
"This class supplies the generic extractor for a resistor device.\n"
"The device is defined by two geometry layers: the resistor 'wire' and "
"two contacts per wire. The contacts should be attached to the ends "
"of the wire. The wire length and width is computed from the "
"edge lengths between the contacts and along the contacts respectively.\n"
"\n"
"This simple computation is precise only when the resistor shape is "
"a rectangle.\n"
"\n"
"Using the given sheet resistance, the resistance value is computed by "
"'R = L / W * sheet_rho'.\n"
"\n"
"The device class produced by this extractor is \\DeviceClassResistor.\n"
"The extractor extracts the three parameters of this class: R, A and P.\n"
"\n"
"The device recognition layer names are 'R' (resistor) and 'C' (contacts).\n"
"The terminal output layer names are 'tA' (terminal A) and 'tB' (terminal B).\n"
"\n"
"This class is a closed one and methods cannot be reimplemented. To reimplement "
"specific methods, see \\DeviceExtractor.\n"
"\n"
"This class has been introduced in version 0.26."
);
db::NetlistDeviceExtractorResistorWithBulk *make_res_with_bulk_extractor (const std::string &name, double sheet_rho)
{
return new db::NetlistDeviceExtractorResistorWithBulk (name, sheet_rho);
}
Class<db::NetlistDeviceExtractorResistorWithBulk> decl_NetlistDeviceExtractorResistorWithBulk (decl_dbNetlistDeviceExtractor, "db", "DeviceExtractorResistorWithBulk",
gsi::constructor ("new", &make_res_with_bulk_extractor, gsi::arg ("name"), gsi::arg ("sheet_rho"),
"@brief Creates a new device extractor with the given name."
),
"@brief A device extractor for a resistor with a bulk terminal\n"
"\n"
"This class supplies the generic extractor for a resistor device including a bulk terminal.\n"
"The device is defined the same way than devices are defined for \\DeviceExtractorResistor.\n"
"\n"
"In addition, a bulk terminal layer must be provided.\n"
"The bulk terminal layer can be an empty layer representing the substrate.\n"
"In this use mode the bulk terminal shapes will be produced on the 'tW' layer. This\n"
"layer then needs to be connected to a global net to establish the net connection.\n"
"\n"
"The device class produced by this extractor is \\DeviceClassResistorWithBulk.\n"
"The extractor extracts the three parameters of this class: R, A and P.\n"
"\n"
"The device recognition layer names are 'R' (resistor), 'C' (contacts) and 'W' (well, bulk).\n"
"The terminal output layer names are 'tA' (terminal A), 'tB' (terminal B) and 'tW' (well, bulk).\n"
"\n"
"This class is a closed one and methods cannot be reimplemented. To reimplement "
"specific methods, see \\DeviceExtractor.\n"
"\n"
"This class has been introduced in version 0.26."
);
db::NetlistDeviceExtractorCapacitor *make_cap_extractor (const std::string &name, double area_cap)
{
return new db::NetlistDeviceExtractorCapacitor (name, area_cap);
}
Class<db::NetlistDeviceExtractorCapacitor> decl_NetlistDeviceExtractorCapacitor (decl_dbNetlistDeviceExtractor, "db", "DeviceExtractorCapacitor",
gsi::constructor ("new", &make_cap_extractor, gsi::arg ("name"), gsi::arg ("area_cap"),
"@brief Creates a new device extractor with the given name."
),
"@brief A device extractor for a two-terminal capacitor\n"
"\n"
"This class supplies the generic extractor for a capacitor device.\n"
"The device is defined by two geometry layers forming the 'plates' of the capacitor.\n"
"The capacitance is computed from the overlapping area of the plates "
"using 'C = A * area_cap' (area_cap is the capacitance per square micrometer area).\n"
"\n"
"The device class produced by this extractor is \\DeviceClassCapacitor.\n"
"The extractor extracts the three parameters of this class: C, A and P.\n"
"\n"
"The device recognition layer names are 'P1' (plate 1) and 'P2' (plate 2).\n"
"The terminal output layer names are 'tA' (terminal A) and 'tB' (terminal B).\n"
"\n"
"This class is a closed one and methods cannot be reimplemented. To reimplement "
"specific methods, see \\DeviceExtractor.\n"
"\n"
"This class has been introduced in version 0.26."
);
db::NetlistDeviceExtractorCapacitorWithBulk *make_cap_with_bulk_extractor (const std::string &name, double area_cap)
{
return new db::NetlistDeviceExtractorCapacitorWithBulk (name, area_cap);
}
Class<db::NetlistDeviceExtractorCapacitorWithBulk> decl_NetlistDeviceExtractorCapacitorWithBulk (decl_dbNetlistDeviceExtractor, "db", "DeviceExtractorCapacitorWithBulk",
gsi::constructor ("new", &make_cap_with_bulk_extractor, gsi::arg ("name"), gsi::arg ("sheet_rho"),
"@brief Creates a new device extractor with the given name."
),
"@brief A device extractor for a capacitor with a bulk terminal\n"
"\n"
"This class supplies the generic extractor for a capacitor device including a bulk terminal.\n"
"The device is defined the same way than devices are defined for \\DeviceExtractorCapacitor.\n"
"\n"
"In addition, a bulk terminal layer must be provided.\n"
"The bulk terminal layer can be an empty layer representing the substrate.\n"
"In this use mode the bulk terminal shapes will be produced on the 'tW' layer. This\n"
"layer then needs to be connected to a global net to establish the net connection.\n"
"\n"
"The device class produced by this extractor is \\DeviceClassCapacitorWithBulk.\n"
"The extractor extracts the three parameters of this class: C, A and P.\n"
"\n"
"The device recognition layer names are 'P1' (plate 1), 'P2' (plate 2) and 'W' (well, bulk).\n"
"The terminal output layer names are 'tA' (terminal A), 'tB' (terminal B) and 'tW' (well, bulk).\n"
"\n"
"This class is a closed one and methods cannot be reimplemented. To reimplement "
"specific methods, see \\DeviceExtractor.\n"
"\n"
"This class has been introduced in version 0.26."
);
db::NetlistDeviceExtractorBJT3Transistor *make_bjt3_extractor (const std::string &name)
{
return new db::NetlistDeviceExtractorBJT3Transistor (name);
}
Class<db::NetlistDeviceExtractorBJT3Transistor> decl_dbNetlistDeviceExtractorBJT3Transistor (decl_dbNetlistDeviceExtractor, "db", "DeviceExtractorBJT3Transistor",
gsi::constructor ("new", &make_bjt3_extractor, gsi::arg ("name"),
"@brief Creates a new device extractor with the given name."
),
"@brief A device extractor for a bipolar transistor (BJT)\n"
"\n"
"This class supplies the generic extractor for a bipolar transistor device.\n"
"\n"
"Extraction of vertical and lateral transistors is supported through a generic geometry model: "
"The basic area is the base area. A marker shape must be provided for this area. "
"The emitter of the transistor is defined by emitter layer shapes inside the base area. "
"Multiple emitter shapes can be present. In this case, multiple transistor devices sharing the "
"same base and collector are generated.\n"
"Finally, a collector layer can be given. If non-empty, the parts inside the base region will define "
"the collector terminals. If empty, the collector is formed by the substrate. In this case, the base "
"region will be output to the 'tC' terminal output layer. This layer then needs to be connected to a global net "
"to form the net connection.\n"
"\n"
"The device class produced by this extractor is \\DeviceClassBJT3Transistor.\n"
"The extractor extracts the two parameters of this class: AE and PE.\n"
"\n"
"The device recognition layer names are 'C' (collector), 'B' (base) and 'E' (emitter).\n"
"The terminal output layer names are 'tC' (collector), 'tB' (base) and 'tE' (emitter).\n"
"\n"
"This class is a closed one and methods cannot be reimplemented. To reimplement "
"specific methods, see \\DeviceExtractor.\n"
"\n"
"This class has been introduced in version 0.26."
);
db::NetlistDeviceExtractorBJT4Transistor *make_bjt4_extractor (const std::string &name)
{
return new db::NetlistDeviceExtractorBJT4Transistor (name);
}
Class<db::NetlistDeviceExtractorBJT4Transistor> decl_NetlistDeviceExtractorBJT4Transistor (decl_dbNetlistDeviceExtractorBJT3Transistor, "db", "DeviceExtractorBJT4Transistor",
gsi::constructor ("new", &make_bjt4_extractor, gsi::arg ("name"),
"@brief Creates a new device extractor with the given name."
),
"@brief A device extractor for a four-terminal bipolar transistor (BJT)\n"
"\n"
"This class supplies the generic extractor for a bipolar transistor device.\n"
"It is based on the \\DeviceExtractorBJT3Transistor class with the extension of a substrate terminal "
"and corresponding substrate terminal output (annotation) layer.\n"
"\n"
"The bulk terminal layer ('tS') can be an empty layer representing the wafer substrate.\n"
"In this use mode the substrate terminal shapes will be produced on the 'tS' layer. This\n"
"layer then needs to be connected to a global net to establish the net connection.\n"
"\n"
"The device class produced by this extractor is \\DeviceClassBJT4Transistor.\n"
"The "
"This class is a closed one and methods cannot be reimplemented. To reimplement "
"specific methods, see \\DeviceExtractor.\n"
"\n"
"This class has been introduced in version 0.26."
);
db::NetlistDeviceExtractorDiode *make_diode_extractor (const std::string &name)
{
return new db::NetlistDeviceExtractorDiode (name);
}
Class<db::NetlistDeviceExtractorDiode> decl_NetlistDeviceExtractorDiode (decl_dbNetlistDeviceExtractor, "db", "DeviceExtractorDiode",
gsi::constructor ("new", &make_diode_extractor, gsi::arg ("name"),
"@brief Creates a new device extractor with the given name."
),
"@brief A device extractor for a planar diode\n"
"\n"
"This class supplies the generic extractor for a planar diode.\n"
"The diode is defined by two layers whose overlap area forms\n"
"the diode. The p-type layer forms the anode, the n-type layer\n"
"the cathode.\n"
"\n"
"The device class produced by this extractor is DeviceClassDiode.\n"
"The extractor extracts the two parameters of this class: A and P.\n"
"A is the area of the overlap area and P is the perimeter.\n"
"\n"
"The layers are \"P\" and \"N\" for the p and n region respectively.\n"
"The terminal output layers are \"tA\" and \"tC\" for anode and \n"
"cathode respectively.\n"
"\n"
"This class is a closed one and methods cannot be reimplemented. To reimplement "
"specific methods, see \\DeviceExtractor.\n"

View File

@ -75,6 +75,16 @@ struct simple_polygon_defs
return c->hull ().size ();
}
static bool is_rectilinear (C *c)
{
return c->hull ().is_rectilinear ();
}
static bool is_empty (C *c)
{
return c->hull ().size () == 0;
}
static C *from_string (const char *s)
{
tl::Extractor ex (s);
@ -260,39 +270,32 @@ struct simple_polygon_defs
"\n"
"The 'raw' argument has been added in version 0.24.\n"
) +
constructor ("new", &new_b,
constructor ("new", &new_b, gsi::arg ("box"),
"@brief Constructor converting a box to a polygon\n"
"\n"
"@args box\n"
"\n"
"@param box The box to convert to a polygon\n"
) +
constructor ("ellipse", &ellipse,
constructor ("ellipse", &ellipse, gsi::arg ("box"), gsi::arg ("n"),
"@brief Creates a simple polygon appoximating an ellipse\n"
"\n"
"@args box, n\n"
"\n"
"@param box The bounding box of the ellipse\n"
"@param n The number of points that will be used to approximate the ellipse\n"
"\n"
"This method has been introduced in version 0.23.\n"
) +
method ("<", &C::less,
"@brief Less operator\n"
"@args p\n"
method ("<", &C::less, gsi::arg ("p"),
"@brief Returns a value indicating whether self is less than p\n"
"@param p The object to compare against\n"
"This operator is provided to establish some, not necessarily a certain sorting order\n"
"\n"
"This method has been introduced in version 0.25."
) +
method ("==", &C::equal,
"@brief Equality test\n"
"@args p\n"
method ("==", &C::equal, gsi::arg ("p"),
"@brief Returns a value indicating whether self is equal to p\n"
"@param p The object to compare against\n"
) +
method ("!=", &C::not_equal,
"@brief Inequality test\n"
"@args p\n"
method ("!=", &C::not_equal, gsi::arg ("p"),
"@brief Returns a value indicating whether self is not equal to p\n"
"@param p The object to compare against\n"
) +
method_ext ("hash", &hash_value,
@ -301,16 +304,15 @@ struct simple_polygon_defs
"\n"
"This method has been introduced in version 0.25.\n"
) +
method_ext ("points=", &set_points1,
"@brief Set the points of the simple polygon\n"
method_ext ("points=", &set_points1, gsi::arg ("pts"),
"@brief Sets the points of the simple polygon\n"
"\n"
"@args pts\n"
"@param pts An array of points to assign to the simple polygon\n"
"\n"
"See the constructor description for details about raw mode.\n"
) +
method_ext ("set_points", &set_points, gsi::arg ("pts"), gsi::arg ("raw", false),
"@brief Set the points of the simple polygon\n"
"@brief Sets the points of the simple polygon\n"
"\n"
"@param pts An array of points to assign to the simple polygon\n"
"@param raw If true, the points are taken as they are\n"
@ -319,31 +321,34 @@ struct simple_polygon_defs
"\n"
"This method has been added in version 0.24.\n"
) +
method_ext ("point", &point,
"@brief Get a specific point of the contour"
"@args p\n"
method_ext ("point", &point, gsi::arg ("p"),
"@brief Gets a specific point of the contour"
"@param p The index of the point to get\n"
"If the index of the point is not a valid index, a default value is returned.\n"
"This method was introduced in version 0.18.\n"
) +
method_ext ("num_points", &num_points,
"@brief Get the number of points"
"@brief Gets the number of points"
) +
iterator ("each_point", &C::begin_hull, &C::end_hull,
"@brief Iterate over the points that make up the simple polygon"
"@brief Iterates over the points that make up the simple polygon"
) +
iterator ("each_edge", &C::begin_edge,
"@brief Iterate over the edges that make up the simple polygon"
"@brief Iterates over the edges that make up the simple polygon"
) +
method_ext ("inside?", &inside,
"@brief Test, if the given point is inside the polygon\n"
"@args p\n"
method_ext ("is_empty?", &is_empty,
"@brief Returns a value indicating whether the polygon is empty\n"
) +
method_ext ("is_rectilinear?", &is_rectilinear,
"@brief Returns a value indicating whether the polygon is rectilinear\n"
) +
method_ext ("inside?", &inside, gsi::arg ("p"),
"@brief Gets a value indicating whether the given point is inside the polygon\n"
"If the given point is inside or on the edge the polygon, true is returned. "
"This tests works well only if the polygon is not self-overlapping and oriented clockwise. "
) +
method_ext ("compress", &compress,
"@brief Compress the simple polygon.\n"
"@args remove_reflected\n"
method_ext ("compress", &compress, gsi::arg ("remove_reflected"),
"@brief Compressed the simple polygon.\n"
"\n"
"This method removes redundant points from the polygon, such as points being on a line formed by two other points.\n"
"If remove_reflected is true, points are also removed if the two adjacent edges form a spike.\n"
@ -353,7 +358,7 @@ struct simple_polygon_defs
"This method was introduced in version 0.18.\n"
) +
method ("is_box?", &C::is_box,
"@brief Returns true, if the polygon is a simple box.\n"
"@brief Returns a value indicating whether the polygon is a simple box.\n"
"\n"
"A polygon is a box if it is identical to it's bounding box.\n"
"\n"
@ -361,17 +366,14 @@ struct simple_polygon_defs
"\n"
"This method was introduced in version 0.23.\n"
) +
method_ext ("*", &scale,
"@brief Scaling by some factor\n"
"\n"
"@args f\n"
method_ext ("*", &scale, gsi::arg ("f"),
"@brief Scales the polygon by some factor\n"
"\n"
"Returns the scaled object. All coordinates are multiplied with the given factor and if "
"necessary rounded."
) +
method ("move", &C::move,
method ("move", &C::move, gsi::arg ("p"),
"@brief Moves the simple polygon.\n"
"@args p\n"
"\n"
"Moves the simple polygon by the given offset and returns the \n"
"moved simple polygon. The polygon is overwritten.\n"
@ -380,9 +382,8 @@ struct simple_polygon_defs
"\n"
"@return The moved simple polygon.\n"
) +
method_ext ("move", &move_xy,
method_ext ("move", &move_xy, gsi::arg ("x"), gsi::arg ("y"),
"@brief Moves the polygon.\n"
"@args x,y\n"
"\n"
"Moves the polygon by the given offset and returns the \n"
"moved polygon. The polygon is overwritten.\n"
@ -392,9 +393,8 @@ struct simple_polygon_defs
"\n"
"@return The moved polygon (self).\n"
) +
method ("moved", &C::moved,
method ("moved", &C::moved, gsi::arg ("p"),
"@brief Returns the moved simple polygon\n"
"@args p\n"
"\n"
"Moves the simple polygon by the given offset and returns the \n"
"moved simple polygon. The polygon is not modified.\n"
@ -403,9 +403,8 @@ struct simple_polygon_defs
"\n"
"@return The moved simple polygon.\n"
) +
method_ext ("moved", &moved_xy,
method_ext ("moved", &moved_xy, gsi::arg ("x"), gsi::arg ("y"),
"@brief Returns the moved polygon (does not modify self)\n"
"@args x,y\n"
"\n"
"Moves the polygon by the given offset and returns the \n"
"moved polygon. The polygon is not modified.\n"
@ -417,9 +416,8 @@ struct simple_polygon_defs
"\n"
"This method has been introduced in version 0.23.\n"
) +
method_ext ("transform", &transform,
method_ext ("transform", &transform, gsi::arg ("t"),
"@brief Transforms the simple polygon (in-place)\n"
"@args t\n"
"\n"
"Transforms the simple polygon with the given transformation.\n"
"Modifies self and returns self. An out-of-place version which does not modify self is \\transformed.\n"
@ -428,9 +426,8 @@ struct simple_polygon_defs
"\n"
"This method has been introduced in version 0.24.\n"
) +
method_ext ("transformed", &transformed,
method_ext ("transformed", &transformed, gsi::arg ("t"),
"@brief Transforms the simple polygon.\n"
"@args t\n"
"\n"
"Transforms the simple polygon with the given transformation.\n"
"Does not modify the simple polygon but returns the transformed polygon.\n"
@ -439,9 +436,8 @@ struct simple_polygon_defs
"\n"
"@return The transformed simple polygon.\n"
) +
method_ext ("transformed|#transformed_cplx", &transformed_cplx,
method_ext ("transformed|#transformed_cplx", &transformed_cplx, gsi::arg ("t"),
"@brief Transforms the simple polygon.\n"
"@args t\n"
"\n"
"Transforms the simple polygon with the given complex transformation.\n"
"Does not modify the simple polygon but returns the transformed polygon.\n"
@ -453,19 +449,17 @@ struct simple_polygon_defs
"With version 0.25, the original 'transformed_cplx' method is deprecated and "
"'transformed' takes both simple and complex transformations."
) +
constructor ("from_s", &from_string,
constructor ("from_s", &from_string, gsi::arg ("s"),
"@brief Creates an object from a string\n"
"@args s\n"
"Creates the object from a string representation (as returned by \\to_s)\n"
"\n"
"This method has been added in version 0.23.\n"
) +
method ("to_s", (std::string (C::*) () const) &C::to_string,
"@brief Convert to a string\n"
"@brief Returns a string representing the polygon\n"
) +
method_ext ("round_corners", &round_corners,
"@brief Round the corners of the polygon\n"
"@args rinner, router, n\n"
method_ext ("round_corners", &round_corners, gsi::arg ("rinner"), gsi::arg ("router"), gsi::arg ("n"),
"@brief Rounds the corners of the polygon\n"
"\n"
"Replaces the corners of the polygon with circle segments.\n"
"\n"
@ -516,15 +510,15 @@ struct simple_polygon_defs
"This method has been introduced in version 0.25.3."
) +
method_ext ("area", &area,
"@brief The area of the polygon\n"
"@brief Gets the area of the polygon\n"
"The area is correct only if the polygon is not self-overlapping and the polygon is oriented clockwise."
) +
method ("perimeter", &C::perimeter,
"@brief The perimeter of the polygon\n"
"@brief Gets the perimeter of the polygon\n"
"The perimeter is sum of the lengths of all edges making up the polygon."
) +
method ("bbox", &C::box,
"@brief Return the bounding box of the simple polygon"
"@brief Returns the bounding box of the simple polygon"
) +
method_ext ("touches?", &touches_box, gsi::arg ("box"),
"@brief Returns true, if the polygon touches the given box.\n"
@ -625,57 +619,48 @@ Class<db::SimplePolygon> decl_SimplePolygon ("db", "SimplePolygon",
"\n"
"This method has been introduced in version 0.25."
) +
method_ext ("minkowsky_sum", &sp_minkowsky_sum_pe,
"@brief Compute the Minkowsky sum of a polygon and an edge\n"
"@args a, b, resolve_holes\n"
method_ext ("minkowsky_sum", &sp_minkowsky_sum_pe, gsi::arg ("e"), gsi::arg ("resolve_holes"),
"@brief Computes the Minkowsky sum of a polygon and an edge\n"
"\n"
"@param a The first argument.\n"
"@param b The second argument.\n"
"@param e The edge.\n"
"@param resolve_holes If true, the output polygon will not contain holes, but holes are resolved by joining the holes with the hull.\n"
"\n"
"@return The new polygon representing the Minkowsky sum of a and b.\n"
"@return The new polygon representing the Minkowsky sum of self and e.\n"
"\n"
"This method was introduced in version 0.22.\n"
) +
method_ext ("minkowsky_sum", &sp_minkowsky_sum_pp,
"@brief Compute the Minkowsky sum of a polygon and a polygon\n"
"@args a, b, resolve_holes\n"
method_ext ("minkowsky_sum", &sp_minkowsky_sum_pp, gsi::arg ("p"), gsi::arg ("resolve_holes"),
"@brief Computes the Minkowsky sum of a polygon and a polygon\n"
"\n"
"@param a The first argument.\n"
"@param b The second argument.\n"
"@param p The other polygon.\n"
"@param resolve_holes If true, the output polygon will not contain holes, but holes are resolved by joining the holes with the hull.\n"
"\n"
"@return The new polygon representing the Minkowsky sum of a and b.\n"
"@return The new polygon representing the Minkowsky sum of self and p.\n"
"\n"
"This method was introduced in version 0.22.\n"
) +
method_ext ("minkowsky_sum", &sp_minkowsky_sum_pb,
"@brief Compute the Minkowsky sum of a polygon and a box\n"
"@args a, b, resolve_holes\n"
method_ext ("minkowsky_sum", &sp_minkowsky_sum_pb, gsi::arg ("b"), gsi::arg ("resolve_holes"),
"@brief Computes the Minkowsky sum of a polygon and a box\n"
"\n"
"@param a The first argument.\n"
"@param b The second argument.\n"
"@param b The box.\n"
"@param resolve_holes If true, the output polygon will not contain holes, but holes are resolved by joining the holes with the hull.\n"
"\n"
"@return The new polygon representing the Minkowsky sum of a and b.\n"
"@return The new polygon representing the Minkowsky sum of self and b.\n"
"\n"
"This method was introduced in version 0.22.\n"
) +
method_ext ("minkowsky_sum", &sp_minkowsky_sum_pc,
"@brief Compute the Minkowsky sum of a polygon and a contour of points (a trace)\n"
"@args a, b, resolve_holes\n"
method_ext ("minkowsky_sum", &sp_minkowsky_sum_pc, gsi::arg ("c"), gsi::arg ("resolve_holes"),
"@brief Computes the Minkowsky sum of a polygon and a contour of points (a trace)\n"
"\n"
"@param a The first argument.\n"
"@param b The second argument (a series of points forming the trace).\n"
"@param c The contour (a series of points forming the trace).\n"
"@param resolve_holes If true, the output polygon will not contain holes, but holes are resolved by joining the holes with the hull.\n"
"\n"
"@return The new polygon representing the Minkowsky sum of a and b.\n"
"@return The new polygon representing the Minkowsky sum of self and c.\n"
"\n"
"This method was introduced in version 0.22.\n"
) +
method_ext ("transform", &transform_icplx_sp,
method_ext ("transform", &transform_icplx_sp, gsi::arg ("t"),
"@brief Transforms the simple polygon with a complex transformation (in-place)\n"
"@args t\n"
"\n"
"Transforms the simple polygon with the given complex transformation.\n"
"Modifies self and returns self. An out-of-place version which does not modify self is \\transformed.\n"
@ -684,9 +669,8 @@ Class<db::SimplePolygon> decl_SimplePolygon ("db", "SimplePolygon",
"\n"
"This method has been introduced in version 0.24.\n"
) +
method_ext ("transformed", &transformed_icplx_sp,
"@brief Transform the simple polygon.\n"
"@args t\n"
method_ext ("transformed", &transformed_icplx_sp, gsi::arg ("t"),
"@brief Transforms the simple polygon.\n"
"\n"
"Transforms the simple polygon with the given complex transformation.\n"
"Does not modify the simple polygon but returns the transformed polygon.\n"
@ -747,9 +731,8 @@ Class<db::DSimplePolygon> decl_DSimplePolygon ("db", "DSimplePolygon",
"\n"
"This method has been introduced in version 0.25."
) +
method_ext ("transform", &transform_cplx_sp,
method_ext ("transform", &transform_cplx_sp, gsi::arg ("t"),
"@brief Transforms the simple polygon with a complex transformation (in-place)\n"
"@args t\n"
"\n"
"Transforms the simple polygon with the given complex transformation.\n"
"Modifies self and returns self. An out-of-place version which does not modify self is \\transformed.\n"
@ -758,11 +741,9 @@ Class<db::DSimplePolygon> decl_DSimplePolygon ("db", "DSimplePolygon",
"\n"
"This method has been introduced in version 0.24.\n"
) +
method_ext ("transformed", &transformed_vplx_sp,
method_ext ("transformed", &transformed_vplx_sp, gsi::arg ("t"),
"@brief Transforms the polygon with the given complex transformation\n"
"\n"
"@args t\n"
"\n"
"@param t The magnifying transformation to apply\n"
"@return The transformed polygon (in this case an integer coordinate polygon)\n"
"\n"
@ -873,6 +854,16 @@ struct polygon_defs
return c->vertices ();
}
static bool is_rectilinear (C *c)
{
return c->is_rectilinear ();
}
static bool is_empty (C *c)
{
return c->vertices () == 0;
}
static point_type point_hull (C *c, size_t p)
{
if (c->hull ().size () > p) {
@ -1089,71 +1080,67 @@ struct polygon_defs
{
return
constructor ("new", &new_v,
"@brief Default constructor: creates an empty (invalid) polygon"
"@brief Creates an empty (invalid) polygon"
) +
constructor ("new", &p_from_sp,
"@brief Constructor from a simple polygon\n"
"@args sp\n"
constructor ("new", &p_from_sp, gsi::arg ("sp"),
"@brief Creates a polygon from a simple polygon\n"
"@param sp The simple polygon that is converted into the polygon\n"
"This method was introduced in version 0.22.\n"
) +
constructor ("new", &new_p, gsi::arg ("pts"), gsi::arg ("raw", false),
"@brief Constructor given the points of the polygon hull\n"
"@brief Creates a polygon from a point array for the hull\n"
"\n"
"@param pts The points forming the polygon hull\n"
"@param raw If true, the point list won't be modified (see \\assign_hull)\n"
"\n"
"The 'raw' argument was added in version 0.24.\n"
) +
constructor ("new", &new_b,
"@brief Constructor converting a box to a polygon\n"
"\n"
"@args box\n"
constructor ("new", &new_b, gsi::arg ("box"),
"@brief Creates a polygon from a box\n"
"\n"
"@param box The box to convert to a polygon\n"
) +
constructor ("ellipse", &ellipse,
constructor ("ellipse", &ellipse, gsi::arg ("box"), gsi::arg ("n"),
"@brief Creates a simple polygon appoximating an ellipse\n"
"\n"
"@args box, n\n"
"\n"
"@param box The bounding box of the ellipse\n"
"@param n The number of points that will be used to approximate the ellipse\n"
"\n"
"This method has been introduced in version 0.23.\n"
) +
method ("<", &C::less,
"@brief Less operator\n"
"@args p\n"
method ("<", &C::less, gsi::arg ("p"),
"@brief Returns a value indicating whether self is less than p\n"
"@param p The object to compare against\n"
"This operator is provided to establish some, not necessarily a certain sorting order\n"
) +
method ("==", &C::equal,
"@brief Equality test\n"
"@args p\n"
method ("==", &C::equal, gsi::arg ("p"),
"@brief Returns a value indicating whether the polygons are equal\n"
"@param p The object to compare against\n"
) +
method ("!=", &C::not_equal,
"@brief Inequality test\n"
"@args p\n"
method ("!=", &C::not_equal, gsi::arg ("p"),
"@brief Returns a value indicating whether the polygons are not equal\n"
"@param p The object to compare against\n"
) +
method_ext ("is_empty?", &is_empty,
"@brief Returns a value indicating whether the polygon is empty\n"
) +
method_ext ("is_rectilinear?", &is_rectilinear,
"@brief Returns a value indicating whether the polygon is rectilinear\n"
) +
method_ext ("hash", &hash_value,
"@brief Computes a hash value\n"
"Returns a hash value for the given polygon. This method enables polygons as hash keys.\n"
"\n"
"This method has been introduced in version 0.25.\n"
) +
method_ext ("hull=", &set_hull1,
"@brief Set the points of the hull of polygon\n"
"@args p\n"
method_ext ("hull=", &set_hull1, gsi::arg ("p"),
"@brief Sets the points of the hull of polygon\n"
"@param p An array of points to assign to the polygon's hull"
"\n"
"The 'assign_hull' variant is provided in analogy to 'assign_hole'.\n"
) +
method_ext ("assign_hull", &set_hull, gsi::arg ("p"), gsi::arg ("raw", false),
"@brief Set the points of the hull of polygon\n"
"@args p\n"
"@brief Sets the points of the hull of polygon\n"
"@param p An array of points to assign to the polygon's hull\n"
"@param raw If true, the points won't be compressed\n"
"\n"
@ -1171,7 +1158,7 @@ struct polygon_defs
"The 'raw' argument was added in version 0.24.\n"
) +
method_ext ("assign_hole", &set_hole, gsi::arg ("n"), gsi::arg ("p"), gsi::arg ("raw", false),
"@brief Set the points of the given hole of the polygon\n"
"@brief Sets the points of the given hole of the polygon\n"
"@param n The index of the hole to which the points should be assigned\n"
"@param p An array of points to assign to the polygon's hole\n"
"@param raw If true, the points won't be compressed (see \\assign_hull)\n"
@ -1180,66 +1167,59 @@ struct polygon_defs
"This method was introduced in version 0.18.\n"
"The 'raw' argument was added in version 0.24.\n"
) +
method_ext ("assign_hole", &set_hole_box,
"@brief Set the box as the given hole of the polygon\n"
"@args n,b\n"
method_ext ("assign_hole", &set_hole_box, gsi::arg ("n"), gsi::arg ("b"),
"@brief Sets the box as the given hole of the polygon\n"
"@param n The index of the hole to which the points should be assigned\n"
"@param b The box to assign to the polygon's hole\n"
"If the hole index is not valid, this method does nothing.\n"
"This method was introduced in version 0.23.\n"
) +
method_ext ("num_points", &num_points,
"@brief Get the total number of points (hull plus holes)\n"
"@brief Gets the total number of points (hull plus holes)\n"
"This method was introduced in version 0.18.\n"
) +
method_ext ("point_hull", &point_hull,
"@brief Get a specific point of the hull\n"
"@args p\n"
method_ext ("point_hull", &point_hull, gsi::arg ("p"),
"@brief Gets a specific point of the hull\n"
"@param p The index of the point to get\n"
"If the index of the point is not a valid index, a default value is returned.\n"
"This method was introduced in version 0.18.\n"
) +
method_ext ("point_hole", &point_hole,
"@brief Get a specific point of a hole\n"
"@args n,p\n"
method_ext ("point_hole", &point_hole, gsi::arg ("n"), gsi::arg ("p"),
"@brief Gets a specific point of a hole\n"
"@param n The index of the hole to which the points should be assigned\n"
"@param p The index of the point to get\n"
"If the index of the point or of the hole is not valid, a default value is returned.\n"
"This method was introduced in version 0.18.\n"
) +
method_ext ("num_points_hull", &num_points_hull,
"@brief Get the number of points of the hull\n"
"@brief Gets the number of points of the hull\n"
) +
method_ext ("num_points_hole", &num_points_hole,
"@brief Get the number of points of the given hole\n"
"@args n\n"
method_ext ("num_points_hole", &num_points_hole, gsi::arg ("n"),
"@brief Gets the number of points of the given hole\n"
"The argument gives the index of the hole of which the number of points "
"are requested. The index must be less than the number of holes (see \\holes). "
) +
method_ext ("insert_hole", &insert_hole, gsi::arg ("p"), gsi::arg ("raw", false),
"@brief Insert a hole with the given points\n"
"@brief Inserts a hole with the given points\n"
"@param p An array of points to insert as a new hole\n"
"@param raw If true, the points won't be compressed (see \\assign_hull)\n"
"\n"
"The 'raw' argument was added in version 0.24.\n"
) +
method_ext ("insert_hole", &insert_hole_box,
"@brief Insert a hole from the given box\n"
"@args b\n"
method_ext ("insert_hole", &insert_hole_box, gsi::arg ("b"),
"@brief Inserts a hole from the given box\n"
"@param b The box to insert as a new hole\n"
"This method was introduced in version 0.23.\n"
) +
iterator ("each_point_hull", &C::begin_hull, &C::end_hull,
"@brief Iterate over the points that make up the hull"
"@brief Iterates over the points that make up the hull"
) +
iterator ("each_point_hole", &C::begin_hole, &C::end_hole,
"@brief Iterate over the points that make up the nth hole\n"
"@args n\n"
iterator ("each_point_hole", &C::begin_hole, &C::end_hole, gsi::arg ("n"),
"@brief Iterates over the points that make up the nth hole\n"
"The hole number must be less than the number of holes (see \\holes)"
) +
method_ext ("size", &size_xy,
"@brief Sizing (biasing)\n"
"@args dx, dy, mode\n"
method_ext ("size", &size_xy, gsi::arg ("dx"), gsi::arg ("dy"), gsi::arg ("mode"),
"@brief Sizes the polygon (biasing)\n"
"\n"
"Shifts the contour outwards (dx,dy>0) or inwards (dx,dy<0).\n"
"dx is the sizing in x-direction and dy is the sizing in y-direction. The sign of dx and dy should be identical.\n"
@ -1261,9 +1241,8 @@ struct polygon_defs
"result = ep.simple_merge_p2p([ poly ], false, false, 1)\n"
"@/code\n"
) +
method_ext ("size", &size_dm,
"@brief Sizing (biasing)\n"
"@args d, mode\n"
method_ext ("size", &size_dm, gsi::arg ("d"), gsi::arg ("mode"),
"@brief Sizes the polygon (biasing)\n"
"\n"
"Shifts the contour outwards (d>0) or inwards (d<0).\n"
"This method is equivalent to\n"
@ -1275,9 +1254,8 @@ struct polygon_defs
"\n"
"This method has been introduced in version 0.23.\n"
) +
method_ext ("sized", &sized_xy,
"@brief Sizing (biasing) without modifying self\n"
"@args dx, dy, mode\n"
method_ext ("sized", &sized_xy, gsi::arg ("dx"), gsi::arg ("dy"), gsi::arg ("mode"),
"@brief Sizes the polygon (biasing) without modifying self\n"
"\n"
"This method applies sizing to the polygon but does not modify self. Instead a sized copy "
"is returned.\n"
@ -1285,9 +1263,8 @@ struct polygon_defs
"\n"
"This method has been introduced in version 0.23.\n"
) +
method_ext ("sized", &sized_dm,
"@brief Sizing (biasing) without modifying self\n"
"@args d, mode\n"
method_ext ("sized", &sized_dm, gsi::arg ("d"), gsi::arg ("mode"),
"@brief Sizes the polygon (biasing) without modifying self\n"
"\n"
"Shifts the contour outwards (d>0) or inwards (d<0).\n"
"This method is equivalent to\n"
@ -1297,9 +1274,8 @@ struct polygon_defs
"\n"
"See \\size and \\sized for a detailed description.\n"
) +
method_ext ("sized", &sized_d,
"@brief Sizing (biasing)\n"
"@args d\n"
method_ext ("sized", &sized_d, gsi::arg ("d"),
"@brief Sizes the polygon (biasing)\n"
"\n"
"@brief Sizing (biasing) without modifying self\n"
"This method is equivalent to\n"
@ -1309,9 +1285,8 @@ struct polygon_defs
"\n"
"See \\size and \\sized for a detailed description.\n"
) +
method_ext ("size", &size_d,
"@brief Sizing (biasing)\n"
"@args d\n"
method_ext ("size", &size_d, gsi::arg ("d"),
"@brief Sizes the polygon (biasing)\n"
"\n"
"Shifts the contour outwards (d>0) or inwards (d<0).\n"
"This method is equivalent to\n"
@ -1325,15 +1300,14 @@ struct polygon_defs
"@brief Returns the number of holes"
) +
iterator ("each_edge", (typename C::polygon_edge_iterator (C::*)() const) (&C::begin_edge),
"@brief Iterate over the edges that make up the polygon\n"
"@brief Iterates over the edges that make up the polygon\n"
"\n"
"This iterator will deliver all edges, including those of the holes. "
"Hole edges are oriented counterclockwise while hull edges are oriented clockwise.\n"
) +
iterator ("each_edge", (typename C::polygon_edge_iterator (C::*)(unsigned int) const) (&C::begin_edge),
"@brief Iterate over the edges of one contour of the polygon\n"
iterator ("each_edge", (typename C::polygon_edge_iterator (C::*)(unsigned int) const) (&C::begin_edge), gsi::arg ("contour"),
"@brief Iterates over the edges of one contour of the polygon\n"
"\n"
"@args contour\n"
"@param contour The contour number (0 for hull, 1 for first hole ...)\n"
"\n"
"This iterator will deliver all edges of the contour specified by the contour parameter. "
@ -1342,15 +1316,13 @@ struct polygon_defs
"\n"
"This method was introduced in version 0.24."
) +
method_ext ("inside?", &inside,
"@brief Test, if the given point is inside the polygon\n"
"@args p\n"
method_ext ("inside?", &inside, gsi::arg ("p"),
"@brief Tests, if the given point is inside the polygon\n"
"If the given point is inside or on the edge of the polygon, true is returned. "
"This tests works well only if the polygon is not self-overlapping and oriented clockwise. "
) +
method_ext ("compress", &compress,
"@brief Compress the polygon.\n"
"@args remove_reflected\n"
method_ext ("compress", &compress, gsi::arg ("remove_reflected"),
"@brief Compresses the polygon.\n"
"\n"
"This method removes redundant points from the polygon, such as points being on a line formed by two other points.\n"
"If remove_reflected is true, points are also removed if the two adjacent edges form a spike.\n"
@ -1368,17 +1340,14 @@ struct polygon_defs
"\n"
"This method was introduced in version 0.23.\n"
) +
method_ext ("*", &scale,
"@brief Scaling by some factor\n"
"\n"
"@args f\n"
method_ext ("*", &scale, gsi::arg ("f"),
"@brief Scales the polygon by some factor\n"
"\n"
"Returns the scaled object. All coordinates are multiplied with the given factor and if "
"necessary rounded."
) +
method ("move", &C::move,
method ("move", &C::move, gsi::arg ("p"),
"@brief Moves the polygon.\n"
"@args p\n"
"\n"
"Moves the polygon by the given offset and returns the \n"
"moved polygon. The polygon is overwritten.\n"
@ -1389,9 +1358,8 @@ struct polygon_defs
"\n"
"This method has been introduced in version 0.23.\n"
) +
method_ext ("move", &move_xy,
method_ext ("move", &move_xy, gsi::arg ("x"), gsi::arg ("y"),
"@brief Moves the polygon.\n"
"@args x,y\n"
"\n"
"Moves the polygon by the given offset and returns the \n"
"moved polygon. The polygon is overwritten.\n"
@ -1401,9 +1369,8 @@ struct polygon_defs
"\n"
"@return The moved polygon (self).\n"
) +
method ("moved", &C::moved,
method ("moved", &C::moved, gsi::arg ("p"),
"@brief Returns the moved polygon (does not modify self)\n"
"@args p\n"
"\n"
"Moves the polygon by the given offset and returns the \n"
"moved polygon. The polygon is not modified.\n"
@ -1414,9 +1381,8 @@ struct polygon_defs
"\n"
"This method has been introduced in version 0.23.\n"
) +
method_ext ("moved", &moved_xy,
method_ext ("moved", &moved_xy, gsi::arg ("x"), gsi::arg ("y"),
"@brief Returns the moved polygon (does not modify self)\n"
"@args x,y\n"
"\n"
"Moves the polygon by the given offset and returns the \n"
"moved polygon. The polygon is not modified.\n"
@ -1428,9 +1394,8 @@ struct polygon_defs
"\n"
"This method has been introduced in version 0.23.\n"
) +
method_ext ("transform", &transform,
method_ext ("transform", &transform, gsi::arg ("t"),
"@brief Transforms the polygon (in-place)\n"
"@args t\n"
"\n"
"Transforms the polygon with the given transformation.\n"
"Modifies self and returns self. An out-of-place version which does not modify self is \\transformed.\n"
@ -1439,9 +1404,8 @@ struct polygon_defs
"\n"
"This method has been introduced in version 0.24.\n"
) +
method_ext ("transformed", &transformed,
method_ext ("transformed", &transformed, gsi::arg ("t"),
"@brief Transforms the polygon\n"
"@args t\n"
"\n"
"Transforms the polygon with the given transformation.\n"
"Does not modify the polygon but returns the transformed polygon.\n"
@ -1450,9 +1414,8 @@ struct polygon_defs
"\n"
"@return The transformed polygon.\n"
) +
method_ext ("transformed|#transformed_cplx", &transformed_cplx,
method_ext ("transformed|#transformed_cplx", &transformed_cplx, gsi::arg ("t"),
"@brief Transforms the polygon with a complex transformation\n"
"@args t\n"
"\n"
"Transforms the polygon with the given complex transformation.\n"
"Does not modify the polygon but returns the transformed polygon.\n"
@ -1464,19 +1427,17 @@ struct polygon_defs
"With version 0.25, the original 'transformed_cplx' method is deprecated and "
"'transformed' takes both simple and complex transformations."
) +
constructor ("from_s", &from_string,
"@brief Creates an object from a string\n"
"@args s\n"
constructor ("from_s", &from_string, gsi::arg ("s"),
"@brief Creates a polygon from a string\n"
"Creates the object from a string representation (as returned by \\to_s)\n"
"\n"
"This method has been added in version 0.23.\n"
) +
method ("to_s", (std::string (C::*) () const) &C::to_string,
"@brief Convert to a string\n"
"@brief Returns a string representing the polygon\n"
) +
method_ext ("round_corners", &round_corners,
method_ext ("round_corners", &round_corners, gsi::arg ("rinner"), gsi::arg ("router"), gsi::arg ("n"),
"@brief Rounds the corners of the polygon\n"
"@args rinner, router, n\n"
"\n"
"Replaces the corners of the polygon with circle segments.\n"
"\n"
@ -1527,18 +1488,18 @@ struct polygon_defs
"This method has been introduced in version 0.25.3."
) +
method_ext ("area", &area,
"@brief The area of the polygon\n"
"@brief Gets the area of the polygon\n"
"The area is correct only if the polygon is not self-overlapping and the polygon is oriented clockwise."
"Orientation is ensured automatically in most cases.\n"
) +
method ("perimeter", &C::perimeter,
"@brief The perimeter of the polygon\n"
"@brief Gets the perimeter of the polygon\n"
"The perimeter is sum of the lengths of all edges making up the polygon.\n"
"\n"
"This method has been introduce in version 0.23.\n"
) +
method ("bbox", &C::box,
"@brief Return the bounding box of the polygon\n"
"@brief Returns the bounding box of the polygon\n"
"The bounding box is the box enclosing all points of the polygon.\n"
) +
method_ext ("touches?", &touches_box, gsi::arg ("box"),
@ -1788,9 +1749,8 @@ Class<db::Polygon> decl_Polygon ("db", "Polygon",
"\n"
"This method was introduced in version 0.22.\n"
) +
method_ext ("smooth", &smooth,
method_ext ("smooth", &smooth, gsi::arg ("d"),
"@brief Smoothes a polygon\n"
"@args d\n"
"\n"
"Remove vertices that deviate by more than the distance d from the average contour.\n"
"The value d is basically the roughness which is removed.\n"
@ -1801,9 +1761,8 @@ Class<db::Polygon> decl_Polygon ("db", "Polygon",
"\n"
"This method was introduced in version 0.23.\n"
) +
method_ext ("minkowsky_sum", &minkowsky_sum_pe,
method_ext ("minkowsky_sum", &minkowsky_sum_pe, gsi::arg ("e"), gsi::arg ("resolve_holes"),
"@brief Computes the Minkowsky sum of the polygon and an edge\n"
"@args e, resolve_holes\n"
"\n"
"@param e The edge.\n"
"@param resolve_holes If true, the output polygon will not contain holes, but holes are resolved by joining the holes with the hull.\n"
@ -1816,9 +1775,8 @@ Class<db::Polygon> decl_Polygon ("db", "Polygon",
"\n"
"This method was introduced in version 0.22.\n"
) +
method_ext ("minkowsky_sum", &minkowsky_sum_pp,
method_ext ("minkowsky_sum", &minkowsky_sum_pp, gsi::arg ("b"), gsi::arg ("resolve_holes"),
"@brief Computes the Minkowsky sum of the polygon and a polygon\n"
"@args p, resolve_holes\n"
"\n"
"@param p The first argument.\n"
"@param resolve_holes If true, the output polygon will not contain holes, but holes are resolved by joining the holes with the hull.\n"
@ -1827,9 +1785,8 @@ Class<db::Polygon> decl_Polygon ("db", "Polygon",
"\n"
"This method was introduced in version 0.22.\n"
) +
method_ext ("minkowsky_sum", &minkowsky_sum_pb,
method_ext ("minkowsky_sum", &minkowsky_sum_pb, gsi::arg ("b"), gsi::arg ("resolve_holes"),
"@brief Computes the Minkowsky sum of the polygon and a box\n"
"@args b, resolve_holes\n"
"\n"
"@param b The box.\n"
"@param resolve_holes If true, the output polygon will not contain holes, but holes are resolved by joining the holes with the hull.\n"
@ -1838,9 +1795,8 @@ Class<db::Polygon> decl_Polygon ("db", "Polygon",
"\n"
"This method was introduced in version 0.22.\n"
) +
method_ext ("minkowsky_sum", &minkowsky_sum_pc,
method_ext ("minkowsky_sum", &minkowsky_sum_pc, gsi::arg ("b"), gsi::arg ("resolve_holes"),
"@brief Computes the Minkowsky sum of the polygon and a contour of points (a trace)\n"
"@args b, resolve_holes\n"
"\n"
"@param b The contour (a series of points forming the trace).\n"
"@param resolve_holes If true, the output polygon will not contain holes, but holes are resolved by joining the holes with the hull.\n"
@ -1849,9 +1805,8 @@ Class<db::Polygon> decl_Polygon ("db", "Polygon",
"\n"
"This method was introduced in version 0.22.\n"
) +
method_ext ("transform", &transform_icplx_dp,
method_ext ("transform", &transform_icplx_dp, gsi::arg ("t"),
"@brief Transforms the polygon with a complex transformation (in-place)\n"
"@args t\n"
"\n"
"Transforms the polygon with the given complex transformation.\n"
"This version modifies self and will return self as the modified polygon. An out-of-place version "
@ -1861,9 +1816,8 @@ Class<db::Polygon> decl_Polygon ("db", "Polygon",
"\n"
"This method was introduced in version 0.24.\n"
) +
method_ext ("transformed", &transformed_icplx_dp,
method_ext ("transformed", &transformed_icplx_dp, gsi::arg ("t"),
"@brief Transforms the polygon with a complex transformation\n"
"@args t\n"
"\n"
"Transforms the polygon with the given complex transformation.\n"
"Does not modify the polygon but returns the transformed polygon.\n"

View File

@ -1095,3 +1095,27 @@ TEST(BasicHierarchyVariantsNot2)
run_test_bool (_this, "hlp14.oas", TMNot, 101);
}
TEST(RedundantHierarchyAnd1)
{
// Redundant hierarchy, NOT
run_test_bool2 (_this, "hlp15.oas", TMAnd, 100);
}
TEST(RedundantHierarchyNot1)
{
// Redundant hierarchy, NOT
run_test_bool2 (_this, "hlp15.oas", TMNot, 101);
}
TEST(RedundantHierarchyAnd2)
{
// Redundant hierarchy, NOT
run_test_bool2 (_this, "hlp16.gds", TMAnd, 100);
}
TEST(RedundantHierarchyNot2)
{
// Redundant hierarchy, NOT
run_test_bool2 (_this, "hlp16.gds", TMNot, 101);
}

View File

@ -81,12 +81,169 @@ TEST(1_ReaderBasic)
lmap [ly2.insert_layer (db::LayerProperties (7, 0)) ] = l2n.layer_by_name ("via1");
lmap [ly2.insert_layer (db::LayerProperties (8, 0)) ] = l2n.layer_by_name ("metal2");
l2n.build_all_nets (cm, ly2, lmap, "NET_", 0, "DEVICE_");
l2n.build_all_nets (cm, ly2, lmap, "NET_", tl::Variant (), db::LayoutToNetlist::BNH_Disconnected, 0, "DEVICE_");
std::string au = tl::testsrc ();
au = tl::combine_path (au, "testdata");
au = tl::combine_path (au, "algo");
au = tl::combine_path (au, "l2n_reader_au.gds");
au = tl::combine_path (au, "l2n_reader_au_1.gds");
db::compare_layouts (_this, ly2, au);
}
{
db::Layout ly2;
ly2.dbu (l2n.internal_layout ()->dbu ());
db::Cell &top2 = ly2.cell (ly2.add_cell ("TOP"));
std::map<unsigned int, const db::Region *> lmap;
lmap [ly2.insert_layer (db::LayerProperties (10, 0))] = l2n.layer_by_name ("psd");
lmap [ly2.insert_layer (db::LayerProperties (11, 0))] = l2n.layer_by_name ("nsd");
lmap [ly2.insert_layer (db::LayerProperties (3, 0)) ] = l2n.layer_by_name ("poly");
lmap [ly2.insert_layer (db::LayerProperties (4, 0)) ] = l2n.layer_by_name ("diff_cont");
lmap [ly2.insert_layer (db::LayerProperties (5, 0)) ] = l2n.layer_by_name ("poly_cont");
lmap [ly2.insert_layer (db::LayerProperties (6, 0)) ] = l2n.layer_by_name ("metal1");
lmap [ly2.insert_layer (db::LayerProperties (7, 0)) ] = l2n.layer_by_name ("via1");
lmap [ly2.insert_layer (db::LayerProperties (8, 0)) ] = l2n.layer_by_name ("metal2");
std::vector<const db::Net *> nets;
nets.push_back (l2n.netlist ()->circuit_by_name ("RINGO")->net_by_name ("VSS"));
nets.push_back (l2n.netlist ()->circuit_by_name ("RINGO")->net_by_name ("VDD"));
db::CellMapping cm = l2n.cell_mapping_into (ly2, top2, nets);
l2n.build_nets (&nets, cm, ly2, lmap, "NET_", tl::Variant (), db::LayoutToNetlist::BNH_Disconnected, 0, "DEVICE_");
std::string au = tl::testsrc ();
au = tl::combine_path (au, "testdata");
au = tl::combine_path (au, "algo");
au = tl::combine_path (au, "l2n_reader_au_1b.gds");
db::compare_layouts (_this, ly2, au);
}
{
db::Layout ly2;
ly2.dbu (l2n.internal_layout ()->dbu ());
db::Cell &top2 = ly2.cell (ly2.add_cell ("TOP"));
std::map<unsigned int, const db::Region *> lmap;
lmap [ly2.insert_layer (db::LayerProperties (10, 0))] = l2n.layer_by_name ("psd");
lmap [ly2.insert_layer (db::LayerProperties (11, 0))] = l2n.layer_by_name ("nsd");
lmap [ly2.insert_layer (db::LayerProperties (3, 0)) ] = l2n.layer_by_name ("poly");
lmap [ly2.insert_layer (db::LayerProperties (4, 0)) ] = l2n.layer_by_name ("diff_cont");
lmap [ly2.insert_layer (db::LayerProperties (5, 0)) ] = l2n.layer_by_name ("poly_cont");
lmap [ly2.insert_layer (db::LayerProperties (6, 0)) ] = l2n.layer_by_name ("metal1");
lmap [ly2.insert_layer (db::LayerProperties (7, 0)) ] = l2n.layer_by_name ("via1");
lmap [ly2.insert_layer (db::LayerProperties (8, 0)) ] = l2n.layer_by_name ("metal2");
std::vector<const db::Net *> nets;
nets.push_back (l2n.netlist ()->circuit_by_name ("RINGO")->net_by_name ("VSS"));
nets.push_back (l2n.netlist ()->circuit_by_name ("RINGO")->net_by_name ("VDD"));
db::CellMapping cm = l2n.cell_mapping_into (ly2, top2, nets);
l2n.build_nets (&nets, cm, ly2, lmap, "NET_", tl::Variant (), db::LayoutToNetlist::BNH_Flatten, 0, "DEVICE_");
std::string au = tl::testsrc ();
au = tl::combine_path (au, "testdata");
au = tl::combine_path (au, "algo");
au = tl::combine_path (au, "l2n_reader_au_1c.gds");
db::compare_layouts (_this, ly2, au);
}
{
db::Layout ly2;
ly2.dbu (l2n.internal_layout ()->dbu ());
db::Cell &top2 = ly2.cell (ly2.add_cell ("TOP"));
std::map<unsigned int, const db::Region *> lmap;
lmap [ly2.insert_layer (db::LayerProperties (10, 0))] = l2n.layer_by_name ("psd");
lmap [ly2.insert_layer (db::LayerProperties (11, 0))] = l2n.layer_by_name ("nsd");
lmap [ly2.insert_layer (db::LayerProperties (3, 0)) ] = l2n.layer_by_name ("poly");
lmap [ly2.insert_layer (db::LayerProperties (4, 0)) ] = l2n.layer_by_name ("diff_cont");
lmap [ly2.insert_layer (db::LayerProperties (5, 0)) ] = l2n.layer_by_name ("poly_cont");
lmap [ly2.insert_layer (db::LayerProperties (6, 0)) ] = l2n.layer_by_name ("metal1");
lmap [ly2.insert_layer (db::LayerProperties (7, 0)) ] = l2n.layer_by_name ("via1");
lmap [ly2.insert_layer (db::LayerProperties (8, 0)) ] = l2n.layer_by_name ("metal2");
std::vector<const db::Net *> nets;
nets.push_back (l2n.netlist ()->circuit_by_name ("RINGO")->net_by_name ("VSS"));
nets.push_back (l2n.netlist ()->circuit_by_name ("RINGO")->net_by_name ("VDD"));
db::CellMapping cm = l2n.cell_mapping_into (ly2, top2, nets);
l2n.build_nets (&nets, cm, ly2, lmap, "NET_", tl::Variant (), db::LayoutToNetlist::BNH_SubcircuitCells, "CIRCUIT_", "DEVICE_");
std::string au = tl::testsrc ();
au = tl::combine_path (au, "testdata");
au = tl::combine_path (au, "algo");
au = tl::combine_path (au, "l2n_reader_au_1d.gds");
db::compare_layouts (_this, ly2, au);
}
{
db::Layout ly2;
ly2.dbu (l2n.internal_layout ()->dbu ());
db::Cell &top2 = ly2.cell (ly2.add_cell ("TOP"));
std::map<unsigned int, const db::Region *> lmap;
lmap [ly2.insert_layer (db::LayerProperties (10, 0))] = l2n.layer_by_name ("psd");
lmap [ly2.insert_layer (db::LayerProperties (11, 0))] = l2n.layer_by_name ("nsd");
lmap [ly2.insert_layer (db::LayerProperties (3, 0)) ] = l2n.layer_by_name ("poly");
lmap [ly2.insert_layer (db::LayerProperties (4, 0)) ] = l2n.layer_by_name ("diff_cont");
lmap [ly2.insert_layer (db::LayerProperties (5, 0)) ] = l2n.layer_by_name ("poly_cont");
lmap [ly2.insert_layer (db::LayerProperties (6, 0)) ] = l2n.layer_by_name ("metal1");
lmap [ly2.insert_layer (db::LayerProperties (7, 0)) ] = l2n.layer_by_name ("via1");
lmap [ly2.insert_layer (db::LayerProperties (8, 0)) ] = l2n.layer_by_name ("metal2");
std::vector<const db::Net *> nets;
nets.push_back (l2n.netlist ()->circuit_by_name ("RINGO")->net_by_name ("VSS"));
nets.push_back (l2n.netlist ()->circuit_by_name ("RINGO")->net_by_name ("VDD"));
nets.push_back (l2n.netlist ()->circuit_by_name ("INV2")->net_by_name ("IN"));
db::CellMapping cm = l2n.cell_mapping_into (ly2, top2, nets);
l2n.build_nets (&nets, cm, ly2, lmap, "NET_", tl::Variant (), db::LayoutToNetlist::BNH_SubcircuitCells, "CIRCUIT_", 0);
std::string au = tl::testsrc ();
au = tl::combine_path (au, "testdata");
au = tl::combine_path (au, "algo");
au = tl::combine_path (au, "l2n_reader_au_1e.gds");
db::compare_layouts (_this, ly2, au);
}
{
db::Layout ly2;
ly2.dbu (l2n.internal_layout ()->dbu ());
db::Cell &top2 = ly2.cell (ly2.add_cell ("TOP"));
std::map<unsigned int, const db::Region *> lmap;
lmap [ly2.insert_layer (db::LayerProperties (10, 0))] = l2n.layer_by_name ("psd");
lmap [ly2.insert_layer (db::LayerProperties (11, 0))] = l2n.layer_by_name ("nsd");
lmap [ly2.insert_layer (db::LayerProperties (3, 0)) ] = l2n.layer_by_name ("poly");
lmap [ly2.insert_layer (db::LayerProperties (4, 0)) ] = l2n.layer_by_name ("diff_cont");
lmap [ly2.insert_layer (db::LayerProperties (5, 0)) ] = l2n.layer_by_name ("poly_cont");
lmap [ly2.insert_layer (db::LayerProperties (6, 0)) ] = l2n.layer_by_name ("metal1");
lmap [ly2.insert_layer (db::LayerProperties (7, 0)) ] = l2n.layer_by_name ("via1");
lmap [ly2.insert_layer (db::LayerProperties (8, 0)) ] = l2n.layer_by_name ("metal2");
std::vector<const db::Net *> nets;
nets.push_back (l2n.netlist ()->circuit_by_name ("RINGO")->net_by_name ("VSS"));
nets.push_back (l2n.netlist ()->circuit_by_name ("RINGO")->net_by_name ("VDD"));
nets.push_back (l2n.netlist ()->circuit_by_name ("INV2")->net_by_name ("IN"));
db::CellMapping cm = l2n.const_cell_mapping_into (ly2, top2);
l2n.build_nets (&nets, cm, ly2, lmap, "NET_", tl::Variant (), db::LayoutToNetlist::BNH_SubcircuitCells, "CIRCUIT_", "DEVICE_");
std::string au = tl::testsrc ();
au = tl::combine_path (au, "testdata");
au = tl::combine_path (au, "algo");
au = tl::combine_path (au, "l2n_reader_au_1f.gds");
db::compare_layouts (_this, ly2, au);
}
@ -177,7 +334,7 @@ TEST(2_ReaderWithGlobalNets)
lmap [ly2.insert_layer (db::LayerProperties (7, 0)) ] = l2n.layer_by_name ("via1");
lmap [ly2.insert_layer (db::LayerProperties (8, 0)) ] = l2n.layer_by_name ("metal2");
l2n.build_all_nets (cm, ly2, lmap, "NET_", "CIRCUIT_", "DEVICE_");
l2n.build_all_nets (cm, ly2, lmap, "NET_", tl::Variant (), db::LayoutToNetlist::BNH_SubcircuitCells, "CIRCUIT_", "DEVICE_");
std::string au = tl::testsrc ();
au = tl::combine_path (au, "testdata");
@ -188,3 +345,121 @@ TEST(2_ReaderWithGlobalNets)
}
}
TEST(3_ReaderAbsoluteCoordinates)
{
db::LayoutToNetlist l2n;
std::string in_path = tl::combine_path (tl::combine_path (tl::combine_path (tl::testsrc (), "testdata"), "algo"), "l2n_writer_au_2_abs.txt");
tl::InputStream is_in (in_path);
db::LayoutToNetlistStandardReader reader (is_in);
reader.read (&l2n);
// verify against the input
std::string path = tmp_file ("tmp_l2nreader_2.txt");
{
tl::OutputStream stream (path);
db::LayoutToNetlistStandardWriter writer (stream, false);
writer.write (&l2n);
}
std::string au_path = tl::combine_path (tl::combine_path (tl::combine_path (tl::testsrc (), "testdata"), "algo"), "l2n_writer_au_2.txt");
tl::InputStream is (path);
tl::InputStream is_au (au_path);
if (is.read_all () != is_au.read_all ()) {
_this->raise (tl::sprintf ("Compare failed - see\n actual: %s\n golden: %s",
tl::absolute_file_path (path),
tl::absolute_file_path (au_path)));
}
// test build_all_nets from read l2n
{
db::Layout ly2;
ly2.dbu (l2n.internal_layout ()->dbu ());
db::Cell &top2 = ly2.cell (ly2.add_cell ("TOP"));
db::CellMapping cm = l2n.cell_mapping_into (ly2, top2, true /*with device cells*/);
std::map<unsigned int, const db::Region *> lmap;
lmap [ly2.insert_layer (db::LayerProperties (10, 0))] = l2n.layer_by_name ("psd");
lmap [ly2.insert_layer (db::LayerProperties (11, 0))] = l2n.layer_by_name ("nsd");
lmap [ly2.insert_layer (db::LayerProperties (12, 0))] = l2n.layer_by_name ("rbulk");
lmap [ly2.insert_layer (db::LayerProperties (13, 0))] = l2n.layer_by_name ("ptie");
lmap [ly2.insert_layer (db::LayerProperties (14, 0))] = l2n.layer_by_name ("ntie");
lmap [ly2.insert_layer (db::LayerProperties (1, 0)) ] = l2n.layer_by_name ("nwell");
lmap [ly2.insert_layer (db::LayerProperties (3, 0)) ] = l2n.layer_by_name ("poly");
lmap [ly2.insert_layer (db::LayerProperties (4, 0)) ] = l2n.layer_by_name ("diff_cont");
lmap [ly2.insert_layer (db::LayerProperties (5, 0)) ] = l2n.layer_by_name ("poly_cont");
lmap [ly2.insert_layer (db::LayerProperties (6, 0)) ] = l2n.layer_by_name ("metal1");
lmap [ly2.insert_layer (db::LayerProperties (7, 0)) ] = l2n.layer_by_name ("via1");
lmap [ly2.insert_layer (db::LayerProperties (8, 0)) ] = l2n.layer_by_name ("metal2");
l2n.build_all_nets (cm, ly2, lmap, "NET_", tl::Variant (), db::LayoutToNetlist::BNH_SubcircuitCells, "CIRCUIT_", "DEVICE_");
std::string au = tl::testsrc ();
au = tl::combine_path (au, "testdata");
au = tl::combine_path (au, "algo");
au = tl::combine_path (au, "l2n_reader_au_2.gds");
db::compare_layouts (_this, ly2, au);
}
}
TEST(4_ReaderCombinedDevices)
{
db::LayoutToNetlist l2n;
// build from: testdata/algo/l2n_reader_4.gds
std::string in_path = tl::combine_path (tl::combine_path (tl::combine_path (tl::testsrc (), "testdata"), "algo"), "l2n_reader_4.l2n");
tl::InputStream is_in (in_path);
db::LayoutToNetlistStandardReader reader (is_in);
reader.read (&l2n);
// verify against the input
std::string path = tmp_file ("tmp_l2nreader_4.txt");
{
tl::OutputStream stream (path);
db::LayoutToNetlistStandardWriter writer (stream, false);
writer.write (&l2n);
}
std::string au_path = tl::combine_path (tl::combine_path (tl::combine_path (tl::testsrc (), "testdata"), "algo"), "l2n_reader_au_4.l2n");
tl::InputStream is (path);
tl::InputStream is_au (au_path);
if (is.read_all () != is_au.read_all ()) {
_this->raise (tl::sprintf ("Compare failed - see\n actual: %s\n golden: %s",
tl::absolute_file_path (path),
tl::absolute_file_path (au_path)));
}
// test build_all_nets from read l2n
{
db::Layout ly2;
ly2.dbu (l2n.internal_layout ()->dbu ());
db::Cell &top2 = ly2.cell (ly2.add_cell ("TOP"));
db::CellMapping cm = l2n.cell_mapping_into (ly2, top2, true /*with device cells*/);
std::map<unsigned int, const db::Region *> lmap = l2n.create_layermap (ly2, 1000);
l2n.build_all_nets (cm, ly2, lmap, "NET_", tl::Variant (), db::LayoutToNetlist::BNH_SubcircuitCells, "CIRCUIT_", "DEVICE_");
std::string au = tl::testsrc ();
au = tl::combine_path (au, "testdata");
au = tl::combine_path (au, "algo");
au = tl::combine_path (au, "l2n_reader_au_4.gds");
db::compare_layouts (_this, ly2, au);
}
}

View File

@ -397,7 +397,7 @@ TEST(1_BasicExtraction)
lmap [ly2.insert_layer (db::LayerProperties (7, 0)) ] = rvia1.get ();
lmap [ly2.insert_layer (db::LayerProperties (8, 0)) ] = rmetal2.get ();
l2n.build_all_nets (cm, ly2, lmap, 0, 0, 0);
l2n.build_all_nets (cm, ly2, lmap, 0, tl::Variant (), db::LayoutToNetlist::BNH_Disconnected, 0, 0);
std::string au = tl::testsrc ();
au = tl::combine_path (au, "testdata");
@ -424,7 +424,7 @@ TEST(1_BasicExtraction)
lmap [ly2.insert_layer (db::LayerProperties (7, 0)) ] = rvia1.get ();
lmap [ly2.insert_layer (db::LayerProperties (8, 0)) ] = rmetal2.get ();
l2n.build_all_nets (cm, ly2, lmap, "NET_", 0, 0);
l2n.build_all_nets (cm, ly2, lmap, "NET_", tl::Variant (), db::LayoutToNetlist::BNH_Disconnected, 0, 0);
std::string au = tl::testsrc ();
au = tl::combine_path (au, "testdata");
@ -451,7 +451,7 @@ TEST(1_BasicExtraction)
lmap [ly2.insert_layer (db::LayerProperties (7, 0)) ] = rvia1.get ();
lmap [ly2.insert_layer (db::LayerProperties (8, 0)) ] = rmetal2.get ();
l2n.build_all_nets (cm, ly2, lmap, 0, "CIRCUIT_", 0);
l2n.build_all_nets (cm, ly2, lmap, 0, tl::Variant (), db::LayoutToNetlist::BNH_SubcircuitCells, "CIRCUIT_", 0);
std::string au = tl::testsrc ();
au = tl::combine_path (au, "testdata");
@ -478,7 +478,61 @@ TEST(1_BasicExtraction)
lmap [ly2.insert_layer (db::LayerProperties (7, 0)) ] = rvia1.get ();
lmap [ly2.insert_layer (db::LayerProperties (8, 0)) ] = rmetal2.get ();
l2n.build_all_nets (cm, ly2, lmap, "NET_", "CIRCUIT_", "DEVICE_");
l2n.build_all_nets (cm, ly2, lmap, 0, tl::Variant (42), db::LayoutToNetlist::BNH_Flatten, 0, 0);
std::string au = tl::testsrc ();
au = tl::combine_path (au, "testdata");
au = tl::combine_path (au, "algo");
au = tl::combine_path (au, "device_extract_au1_rebuild_pf.gds");
db::compare_layouts (_this, ly2, au);
}
{
db::Layout ly2;
ly2.dbu (ly.dbu ());
db::Cell &top2 = ly2.cell (ly2.add_cell ("TOP"));
db::CellMapping cm = l2n.cell_mapping_into (ly2, top2, true /*with device cells*/);
std::map<unsigned int, const db::Region *> lmap;
lmap [ly2.insert_layer (db::LayerProperties (10, 0))] = &rpsd;
lmap [ly2.insert_layer (db::LayerProperties (11, 0))] = &rnsd;
lmap [ly2.insert_layer (db::LayerProperties (3, 0)) ] = rpoly.get ();
lmap [ly2.insert_layer (db::LayerProperties (4, 0)) ] = rdiff_cont.get ();
lmap [ly2.insert_layer (db::LayerProperties (5, 0)) ] = rpoly_cont.get ();
lmap [ly2.insert_layer (db::LayerProperties (6, 0)) ] = rmetal1.get ();
lmap [ly2.insert_layer (db::LayerProperties (7, 0)) ] = rvia1.get ();
lmap [ly2.insert_layer (db::LayerProperties (8, 0)) ] = rmetal2.get ();
l2n.build_all_nets (cm, ly2, lmap, 0, tl::Variant (42), db::LayoutToNetlist::BNH_SubcircuitCells, "CIRCUIT_", 0);
std::string au = tl::testsrc ();
au = tl::combine_path (au, "testdata");
au = tl::combine_path (au, "algo");
au = tl::combine_path (au, "device_extract_au1_rebuild_pr.gds");
db::compare_layouts (_this, ly2, au);
}
{
db::Layout ly2;
ly2.dbu (ly.dbu ());
db::Cell &top2 = ly2.cell (ly2.add_cell ("TOP"));
db::CellMapping cm = l2n.cell_mapping_into (ly2, top2, true /*with device cells*/);
std::map<unsigned int, const db::Region *> lmap;
lmap [ly2.insert_layer (db::LayerProperties (10, 0))] = &rpsd;
lmap [ly2.insert_layer (db::LayerProperties (11, 0))] = &rnsd;
lmap [ly2.insert_layer (db::LayerProperties (3, 0)) ] = rpoly.get ();
lmap [ly2.insert_layer (db::LayerProperties (4, 0)) ] = rdiff_cont.get ();
lmap [ly2.insert_layer (db::LayerProperties (5, 0)) ] = rpoly_cont.get ();
lmap [ly2.insert_layer (db::LayerProperties (6, 0)) ] = rmetal1.get ();
lmap [ly2.insert_layer (db::LayerProperties (7, 0)) ] = rvia1.get ();
lmap [ly2.insert_layer (db::LayerProperties (8, 0)) ] = rmetal2.get ();
l2n.build_all_nets (cm, ly2, lmap, "NET_", tl::Variant (), db::LayoutToNetlist::BNH_SubcircuitCells, "CIRCUIT_", "DEVICE_");
std::string au = tl::testsrc ();
au = tl::combine_path (au, "testdata");
@ -1016,7 +1070,7 @@ TEST(3_GlobalNetConnections)
EXPECT_EQ (qnet_name (l2n.probe_net (*rmetal2, db::DPoint (-2.0, 1.8))), "(null)");
EXPECT_EQ (qnet_name (l2n.probe_net (*rmetal1, db::DPoint (-1.5, 1.8))), "RINGO:FB");
EXPECT_EQ (qnet_name (l2n.probe_net (*rmetal1, db::DPoint (24.5, 1.8))), "RINGO:OSC");
EXPECT_EQ (qnet_name (l2n.probe_net (*rmetal1, db::DPoint (5.3, 0.0))), "RINGO:BULK,VSS");
EXPECT_EQ (qnet_name (l2n.probe_net (*rmetal1, db::DPoint (5.3, 0.0))), "RINGO:VSS");
EXPECT_EQ (qnet_name (l2n.probe_net (*rmetal1, db::DPoint (2.6, 1.0))), "RINGO:$I22");
EXPECT_EQ (qnet_name (l2n.probe_net (*rmetal1, db::DPoint (6.4, 1.0))), "INV2PAIR:$I4");
@ -1031,12 +1085,12 @@ TEST(3_GlobalNetConnections)
// compare netlist as string
CHECKPOINT ();
db::compare_netlist (_this, *l2n.netlist (),
"circuit RINGO (FB=FB,OSC=OSC,VDD=VDD,'BULK,VSS'='BULK,VSS');\n"
" subcircuit INV2PAIR $1 (BULK='BULK,VSS',$2=FB,$3=VDD,$4='BULK,VSS',$5=$I7,$6=OSC,$7=VDD);\n"
" subcircuit INV2PAIR $2 (BULK='BULK,VSS',$2=(null),$3=VDD,$4='BULK,VSS',$5=FB,$6=$I13,$7=VDD);\n"
" subcircuit INV2PAIR $3 (BULK='BULK,VSS',$2=(null),$3=VDD,$4='BULK,VSS',$5=$I13,$6=$I5,$7=VDD);\n"
" subcircuit INV2PAIR $4 (BULK='BULK,VSS',$2=(null),$3=VDD,$4='BULK,VSS',$5=$I5,$6=$I6,$7=VDD);\n"
" subcircuit INV2PAIR $5 (BULK='BULK,VSS',$2=(null),$3=VDD,$4='BULK,VSS',$5=$I6,$6=$I7,$7=VDD);\n"
"circuit RINGO (FB=FB,OSC=OSC,VDD=VDD,VSS=VSS);\n"
" subcircuit INV2PAIR $1 (BULK=VSS,$2=FB,$3=VDD,$4=VSS,$5=$I7,$6=OSC,$7=VDD);\n"
" subcircuit INV2PAIR $2 (BULK=VSS,$2=(null),$3=VDD,$4=VSS,$5=FB,$6=$I13,$7=VDD);\n"
" subcircuit INV2PAIR $3 (BULK=VSS,$2=(null),$3=VDD,$4=VSS,$5=$I13,$6=$I5,$7=VDD);\n"
" subcircuit INV2PAIR $4 (BULK=VSS,$2=(null),$3=VDD,$4=VSS,$5=$I5,$6=$I6,$7=VDD);\n"
" subcircuit INV2PAIR $5 (BULK=VSS,$2=(null),$3=VDD,$4=VSS,$5=$I6,$6=$I7,$7=VDD);\n"
"end;\n"
"circuit INV2PAIR (BULK=BULK,$2=$I8,$3=$I6,$4=$I5,$5=$I3,$6=$I2,$7=$I1);\n"
" subcircuit INV2 $1 ($1=$I1,IN=$I3,$3=(null),OUT=$I4,VSS=$I5,VDD=$I6,BULK=BULK);\n"
@ -1303,7 +1357,7 @@ TEST(4_GlobalNetDeviceExtraction)
EXPECT_EQ (qnet_name (l2n.probe_net (*rmetal2, db::DPoint (-2.0, 1.8))), "(null)");
EXPECT_EQ (qnet_name (l2n.probe_net (*rmetal1, db::DPoint (-1.5, 1.8))), "RINGO:FB");
EXPECT_EQ (qnet_name (l2n.probe_net (*rmetal1, db::DPoint (24.5, 1.8))), "RINGO:OSC");
EXPECT_EQ (qnet_name (l2n.probe_net (*rmetal1, db::DPoint (5.3, 0.0))), "RINGO:BULK,VSS");
EXPECT_EQ (qnet_name (l2n.probe_net (*rmetal1, db::DPoint (5.3, 0.0))), "RINGO:VSS");
EXPECT_EQ (qnet_name (l2n.probe_net (*rmetal1, db::DPoint (2.6, 1.0))), "RINGO:$I22");
EXPECT_EQ (qnet_name (l2n.probe_net (*rmetal1, db::DPoint (6.4, 1.0))), "INV2PAIR:$I4");
@ -1318,12 +1372,12 @@ TEST(4_GlobalNetDeviceExtraction)
// compare netlist as string
CHECKPOINT ();
db::compare_netlist (_this, *l2n.netlist (),
"circuit RINGO (FB=FB,OSC=OSC,VDD=VDD,'BULK,VSS'='BULK,VSS');\n"
" subcircuit INV2PAIR $1 (BULK='BULK,VSS',$2=FB,$3=VDD,$4='BULK,VSS',$5=$I7,$6=OSC,$7=VDD);\n"
" subcircuit INV2PAIR $2 (BULK='BULK,VSS',$2=(null),$3=VDD,$4='BULK,VSS',$5=FB,$6=$I13,$7=VDD);\n"
" subcircuit INV2PAIR $3 (BULK='BULK,VSS',$2=(null),$3=VDD,$4='BULK,VSS',$5=$I13,$6=$I5,$7=VDD);\n"
" subcircuit INV2PAIR $4 (BULK='BULK,VSS',$2=(null),$3=VDD,$4='BULK,VSS',$5=$I5,$6=$I6,$7=VDD);\n"
" subcircuit INV2PAIR $5 (BULK='BULK,VSS',$2=(null),$3=VDD,$4='BULK,VSS',$5=$I6,$6=$I7,$7=VDD);\n"
"circuit RINGO (FB=FB,OSC=OSC,VDD=VDD,VSS=VSS);\n"
" subcircuit INV2PAIR $1 (BULK=VSS,$2=FB,$3=VDD,$4=VSS,$5=$I7,$6=OSC,$7=VDD);\n"
" subcircuit INV2PAIR $2 (BULK=VSS,$2=(null),$3=VDD,$4=VSS,$5=FB,$6=$I13,$7=VDD);\n"
" subcircuit INV2PAIR $3 (BULK=VSS,$2=(null),$3=VDD,$4=VSS,$5=$I13,$6=$I5,$7=VDD);\n"
" subcircuit INV2PAIR $4 (BULK=VSS,$2=(null),$3=VDD,$4=VSS,$5=$I5,$6=$I6,$7=VDD);\n"
" subcircuit INV2PAIR $5 (BULK=VSS,$2=(null),$3=VDD,$4=VSS,$5=$I6,$6=$I7,$7=VDD);\n"
"end;\n"
"circuit INV2PAIR (BULK=BULK,$2=$I8,$3=$I6,$4=$I5,$5=$I3,$6=$I2,$7=$I1);\n"
" subcircuit INV2 $1 ($1=$I1,IN=$I3,$3=(null),OUT=$I4,VSS=$I5,VDD=$I6,BULK=BULK);\n"
@ -1590,7 +1644,7 @@ TEST(5_DeviceExtractionWithDeviceCombination)
EXPECT_EQ (qnet_name (l2n.probe_net (*rmetal2, db::DPoint (-2.0, 1.8))), "(null)");
EXPECT_EQ (qnet_name (l2n.probe_net (*rmetal1, db::DPoint (-1.5, 1.8))), "RINGO:FB");
EXPECT_EQ (qnet_name (l2n.probe_net (*rmetal1, db::DPoint (24.5, 1.8))), "RINGO:OSC");
EXPECT_EQ (qnet_name (l2n.probe_net (*rmetal1, db::DPoint (5.3, 0.0))), "RINGO:BULK,VSS");
EXPECT_EQ (qnet_name (l2n.probe_net (*rmetal1, db::DPoint (5.3, 0.0))), "RINGO:VSS");
// doesn't do anything here, but we test that this does not destroy anything:
l2n.netlist ()->combine_devices ();
@ -1602,12 +1656,12 @@ TEST(5_DeviceExtractionWithDeviceCombination)
// compare netlist as string
CHECKPOINT ();
db::compare_netlist (_this, *l2n.netlist (),
"circuit RINGO (FB=FB,OSC=OSC,VDD=VDD,'BULK,VSS'='BULK,VSS');\n"
" subcircuit INV2PAIR $1 (BULK='BULK,VSS',$2=VDD,$3='BULK,VSS',$4=FB,$5=$I7,$6=OSC,$7=VDD);\n"
" subcircuit INV2PAIR $2 (BULK='BULK,VSS',$2=VDD,$3='BULK,VSS',$4=(null),$5=FB,$6=$I13,$7=VDD);\n"
" subcircuit INV2PAIR $3 (BULK='BULK,VSS',$2=VDD,$3='BULK,VSS',$4=(null),$5=$I13,$6=$I5,$7=VDD);\n"
" subcircuit INV2PAIR $4 (BULK='BULK,VSS',$2=VDD,$3='BULK,VSS',$4=(null),$5=$I5,$6=$I6,$7=VDD);\n"
" subcircuit INV2PAIR $5 (BULK='BULK,VSS',$2=VDD,$3='BULK,VSS',$4=(null),$5=$I6,$6=$I7,$7=VDD);\n"
"circuit RINGO (FB=FB,OSC=OSC,VDD=VDD,VSS=VSS);\n"
" subcircuit INV2PAIR $1 (BULK=VSS,$2=VDD,$3=VSS,$4=FB,$5=$I7,$6=OSC,$7=VDD);\n"
" subcircuit INV2PAIR $2 (BULK=VSS,$2=VDD,$3=VSS,$4=(null),$5=FB,$6=$I13,$7=VDD);\n"
" subcircuit INV2PAIR $3 (BULK=VSS,$2=VDD,$3=VSS,$4=(null),$5=$I13,$6=$I5,$7=VDD);\n"
" subcircuit INV2PAIR $4 (BULK=VSS,$2=VDD,$3=VSS,$4=(null),$5=$I5,$6=$I6,$7=VDD);\n"
" subcircuit INV2PAIR $5 (BULK=VSS,$2=VDD,$3=VSS,$4=(null),$5=$I6,$6=$I7,$7=VDD);\n"
"end;\n"
"circuit INV2PAIR (BULK=BULK,$2=$I6,$3=$I5,$4=$I4,$5=$I3,$6=$I2,$7=$I1);\n"
" subcircuit INV2 $1 ($1=$I1,IN=$I3,OUT=$I4,VSS=$I5,VDD=$I6,BULK=BULK);\n"
@ -2499,3 +2553,205 @@ TEST(10_Antenna)
db::compare_layouts (_this, ly2, au);
}
TEST(11_DuplicateInstances)
{
db::Layout ly;
db::LayerMap lmap;
unsigned int nwell = define_layer (ly, lmap, 1);
unsigned int active = define_layer (ly, lmap, 2);
unsigned int poly = define_layer (ly, lmap, 3);
unsigned int poly_lbl = define_layer (ly, lmap, 3, 1);
unsigned int diff_cont = define_layer (ly, lmap, 4);
unsigned int poly_cont = define_layer (ly, lmap, 5);
unsigned int metal1 = define_layer (ly, lmap, 6);
unsigned int metal1_lbl = define_layer (ly, lmap, 6, 1);
unsigned int via1 = define_layer (ly, lmap, 7);
unsigned int metal2 = define_layer (ly, lmap, 8);
unsigned int metal2_lbl = define_layer (ly, lmap, 8, 1);
{
db::LoadLayoutOptions options;
options.get_options<db::CommonReaderOptions> ().layer_map = lmap;
options.get_options<db::CommonReaderOptions> ().create_other_layers = false;
std::string fn (tl::testsrc ());
fn = tl::combine_path (fn, "testdata");
fn = tl::combine_path (fn, "algo");
fn = tl::combine_path (fn, "device_extract_l1_dup_inst.gds");
tl::InputStream stream (fn);
db::Reader reader (stream);
reader.read (ly, options);
}
db::Cell &tc = ly.cell (*ly.begin_top_down ());
db::LayoutToNetlist l2n (db::RecursiveShapeIterator (ly, tc, std::set<unsigned int> ()));
std::auto_ptr<db::Region> rnwell (l2n.make_layer (nwell, "nwell"));
std::auto_ptr<db::Region> ractive (l2n.make_layer (active, "active"));
std::auto_ptr<db::Region> rpoly (l2n.make_polygon_layer (poly, "poly"));
std::auto_ptr<db::Region> rpoly_lbl (l2n.make_text_layer (poly_lbl, "poly_lbl"));
std::auto_ptr<db::Region> rdiff_cont (l2n.make_polygon_layer (diff_cont, "diff_cont"));
std::auto_ptr<db::Region> rpoly_cont (l2n.make_polygon_layer (poly_cont, "poly_cont"));
std::auto_ptr<db::Region> rmetal1 (l2n.make_polygon_layer (metal1, "metal1"));
std::auto_ptr<db::Region> rmetal1_lbl (l2n.make_text_layer (metal1_lbl, "metal1_lbl"));
std::auto_ptr<db::Region> rvia1 (l2n.make_polygon_layer (via1, "via1"));
std::auto_ptr<db::Region> rmetal2 (l2n.make_polygon_layer (metal2, "metal2"));
std::auto_ptr<db::Region> rmetal2_lbl (l2n.make_text_layer (metal2_lbl, "metal2_lbl"));
// derived regions
db::Region rpactive = *ractive & *rnwell;
db::Region rpgate = rpactive & *rpoly;
db::Region rpsd = rpactive - rpgate;
db::Region rnactive = *ractive - *rnwell;
db::Region rngate = rnactive & *rpoly;
db::Region rnsd = rnactive - rngate;
db::NetlistDeviceExtractorMOS3Transistor pmos_ex ("PMOS");
db::NetlistDeviceExtractorMOS3Transistor nmos_ex ("NMOS");
// device extraction
db::NetlistDeviceExtractor::input_layers dl;
dl["SD"] = &rpsd;
dl["G"] = &rpgate;
dl["P"] = rpoly.get (); // not needed for extraction but to return terminal shapes
l2n.extract_devices (pmos_ex, dl);
dl["SD"] = &rnsd;
dl["G"] = &rngate;
dl["P"] = rpoly.get (); // not needed for extraction but to return terminal shapes
l2n.extract_devices (nmos_ex, dl);
// return the computed layers into the original layout and write it for debugging purposes
// NOTE: this will include the device layers too
unsigned int lgate = ly.insert_layer (db::LayerProperties (10, 0)); // 10/0 -> Gate
unsigned int lsd = ly.insert_layer (db::LayerProperties (11, 0)); // 11/0 -> Source/Drain
unsigned int lpdiff = ly.insert_layer (db::LayerProperties (12, 0)); // 12/0 -> P Diffusion
unsigned int lndiff = ly.insert_layer (db::LayerProperties (13, 0)); // 13/0 -> N Diffusion
unsigned int lpoly = ly.insert_layer (db::LayerProperties (14, 0)); // 14/0 -> Poly with gate terminal
rpgate.insert_into (&ly, tc.cell_index (), lgate);
rngate.insert_into (&ly, tc.cell_index (), lgate);
rpsd.insert_into (&ly, tc.cell_index (), lsd);
rnsd.insert_into (&ly, tc.cell_index (), lsd);
rpsd.insert_into (&ly, tc.cell_index (), lpdiff);
rnsd.insert_into (&ly, tc.cell_index (), lndiff);
rpoly->insert_into (&ly, tc.cell_index (), lpoly);
// net extraction
l2n.register_layer (rpsd, "psd");
l2n.register_layer (rnsd, "nsd");
// Intra-layer
l2n.connect (rpsd);
l2n.connect (rnsd);
l2n.connect (*rpoly);
l2n.connect (*rdiff_cont);
l2n.connect (*rpoly_cont);
l2n.connect (*rmetal1);
l2n.connect (*rvia1);
l2n.connect (*rmetal2);
// Inter-layer
l2n.connect (rpsd, *rdiff_cont);
l2n.connect (rnsd, *rdiff_cont);
l2n.connect (*rpoly, *rpoly_cont);
l2n.connect (*rpoly_cont, *rmetal1);
l2n.connect (*rdiff_cont, *rmetal1);
l2n.connect (*rmetal1, *rvia1);
l2n.connect (*rvia1, *rmetal2);
l2n.connect (*rpoly, *rpoly_lbl); // attaches labels
l2n.connect (*rmetal1, *rmetal1_lbl); // attaches labels
l2n.connect (*rmetal2, *rmetal2_lbl); // attaches labels
// create some mess - we have to keep references to the layers to make them not disappear
rmetal1_lbl.reset (0);
rmetal2_lbl.reset (0);
rpoly_lbl.reset (0);
l2n.extract_netlist ();
// debug layers produced for nets
// 202/0 -> Active
// 203/0 -> Poly
// 204/0 -> Diffusion contacts
// 205/0 -> Poly contacts
// 206/0 -> Metal1
// 207/0 -> Via1
// 208/0 -> Metal2
// 210/0 -> N source/drain
// 211/0 -> P source/drain
std::map<const db::Region *, unsigned int> dump_map;
dump_map [&rpsd ] = ly.insert_layer (db::LayerProperties (210, 0));
dump_map [&rnsd ] = ly.insert_layer (db::LayerProperties (211, 0));
dump_map [rpoly.get () ] = ly.insert_layer (db::LayerProperties (203, 0));
dump_map [rdiff_cont.get ()] = ly.insert_layer (db::LayerProperties (204, 0));
dump_map [rpoly_cont.get ()] = ly.insert_layer (db::LayerProperties (205, 0));
dump_map [rmetal1.get () ] = ly.insert_layer (db::LayerProperties (206, 0));
dump_map [rvia1.get () ] = ly.insert_layer (db::LayerProperties (207, 0));
dump_map [rmetal2.get () ] = ly.insert_layer (db::LayerProperties (208, 0));
// write nets to layout
db::CellMapping cm = l2n.cell_mapping_into (ly, tc, true /*with device cells*/);
dump_nets_to_layout (l2n, ly, dump_map, cm);
dump_map.clear ();
dump_map [&rpsd ] = ly.insert_layer (db::LayerProperties (310, 0));
dump_map [&rnsd ] = ly.insert_layer (db::LayerProperties (311, 0));
dump_map [rpoly.get () ] = ly.insert_layer (db::LayerProperties (303, 0));
dump_map [rdiff_cont.get ()] = ly.insert_layer (db::LayerProperties (304, 0));
dump_map [rpoly_cont.get ()] = ly.insert_layer (db::LayerProperties (305, 0));
dump_map [rmetal1.get () ] = ly.insert_layer (db::LayerProperties (306, 0));
dump_map [rvia1.get () ] = ly.insert_layer (db::LayerProperties (307, 0));
dump_map [rmetal2.get () ] = ly.insert_layer (db::LayerProperties (308, 0));
dump_recursive_nets_to_layout (l2n, ly, dump_map, cm);
// compare the collected test data
std::string au = tl::testsrc ();
au = tl::combine_path (au, "testdata");
au = tl::combine_path (au, "algo");
au = tl::combine_path (au, "device_extract_au1_dup_inst_with_rec_nets.gds");
db::compare_layouts (_this, ly, au);
// compare netlist as string
CHECKPOINT ();
db::compare_netlist (_this, *l2n.netlist (),
"circuit RINGO ();\n"
" subcircuit INV2 $1 (IN=$I12,$2=FB,OUT=OSC,$4=VSS,$5=VDD);\n"
" subcircuit INV2 $2 (IN=FB,$2=$I25,OUT=$I1,$4=VSS,$5=VDD);\n"
" subcircuit INV2 $3 (IN=$I1,$2=$I26,OUT=$I2,$4=VSS,$5=VDD);\n"
" subcircuit INV2 $4 (IN=$I2,$2=$I27,OUT=$I3,$4=VSS,$5=VDD);\n"
" subcircuit INV2 $5 (IN=$I3,$2=$I28,OUT=$I4,$4=VSS,$5=VDD);\n"
" subcircuit BLOCK $6 ($1=$I29,$2=VDD,$3=VSS,$4=$I11,$5=$I5,$6=$I4,$7=$I12);\n"
" subcircuit INV2 $7 (IN=$I11,$2=$I29,OUT=$I5,$4=VSS,$5=VDD);\n"
"end;\n"
"circuit INV2 (IN=IN,$2=$2,OUT=OUT,$4=$4,$5=$5);\n"
" device PMOS $1 (S=$2,G=IN,D=$5) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n"
" device PMOS $2 (S=$5,G=$2,D=OUT) (L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95);\n"
" device NMOS $3 (S=$2,G=IN,D=$4) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n"
" device NMOS $4 (S=$4,G=$2,D=OUT) (L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95);\n"
" subcircuit TRANS $1 ($1=$2,$2=$4,$3=IN);\n"
" subcircuit TRANS $2 ($1=$2,$2=$5,$3=IN);\n"
" subcircuit TRANS $3 ($1=$5,$2=OUT,$3=$2);\n"
" subcircuit TRANS $4 ($1=$4,$2=OUT,$3=$2);\n"
"end;\n"
"circuit TRANS ($1=$1,$2=$2,$3=$3);\n"
"end;\n"
"circuit BLOCK ($1=$I21,$2=$I14,$3=$I10,$4=$I9,$5=$I6,$6=$I5,$7=$I3);\n"
" subcircuit INV2 $1 (IN=$I6,$2=$I22,OUT=$I1,$4=$I10,$5=$I14);\n"
" subcircuit INV2 $2 (IN=$I9,$2=$I21,OUT=$I6,$4=$I10,$5=$I14);\n"
" subcircuit INV2 $3 (IN=$I2,$2=$I20,OUT=$I3,$4=$I10,$5=$I14);\n"
" subcircuit INV2 $4 (IN=$I1,$2=$I19,OUT=$I2,$4=$I10,$5=$I14);\n"
" subcircuit INV2 $5 (IN=$I5,$2=$I18,OUT=$I9,$4=$I10,$5=$I14);\n"
"end;\n"
);
}

View File

@ -226,7 +226,7 @@ TEST(1_WriterBasic)
lmap [ly2.insert_layer (db::LayerProperties (7, 0)) ] = rvia1.get ();
lmap [ly2.insert_layer (db::LayerProperties (8, 0)) ] = rmetal2.get ();
l2n.build_all_nets (cm, ly2, lmap, "NET_", 0, "DEVICE_");
l2n.build_all_nets (cm, ly2, lmap, "NET_", tl::Variant (), db::LayoutToNetlist::BNH_Disconnected, 0, "DEVICE_");
std::string au = tl::testsrc ();
au = tl::combine_path (au, "testdata");
@ -452,7 +452,7 @@ TEST(2_WriterWithGlobalNets)
lmap [ly2.insert_layer (db::LayerProperties (7, 0)) ] = rvia1.get ();
lmap [ly2.insert_layer (db::LayerProperties (8, 0)) ] = rmetal2.get ();
l2n.build_all_nets (cm, ly2, lmap, "NET_", "CIRCUIT_", "DEVICE_");
l2n.build_all_nets (cm, ly2, lmap, "NET_", tl::Variant (), db::LayoutToNetlist::BNH_SubcircuitCells, "CIRCUIT_", "DEVICE_");
std::string au = tl::testsrc ();
au = tl::combine_path (au, "testdata");

View File

@ -0,0 +1,466 @@
/*
KLayout Layout Viewer
Copyright (C) 2006-2019 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 "dbNetlistDeviceExtractorClasses.h"
#include "dbLayoutVsSchematic.h"
#include "dbStream.h"
#include "dbDeepRegion.h"
#include "dbDeepShapeStore.h"
#include "dbReader.h"
#include "dbWriter.h"
#include "dbCommonReader.h"
#include "dbTestSupport.h"
#include "dbNetlistSpiceWriter.h" // to create debug files
#include "dbNetlistSpiceReader.h"
#include "dbNetlistCompare.h"
#include "tlUnitTest.h"
#include "tlString.h"
#include "tlFileUtils.h"
#include <memory>
#include <limits>
#if defined(_MSC_VER)
// different hash algorithm
# define AUFILE_SUFFIX ".2"
#else
# define AUFILE_SUFFIX ""
#endif
static unsigned int define_layer (db::Layout &ly, db::LayerMap &lmap, int gds_layer, int gds_datatype = 0)
{
unsigned int lid = ly.insert_layer (db::LayerProperties (gds_layer, gds_datatype));
lmap.map (ly.get_properties (lid), lid);
return lid;
}
static void compare_lvsdbs (tl::TestBase *_this, const std::string &path, const std::string &au_path)
{
tl::InputStream is (path);
tl::InputStream is_au (au_path);
std::string netlist = is.read_all ();
std::string netlist_au = is_au.read_all ();
if (netlist != netlist_au) {
_this->raise (tl::sprintf ("Compare failed - see\n actual: %s\n golden: %s",
tl::absolute_file_path (path),
tl::absolute_file_path (au_path)));
}
}
TEST(1_BasicFlow)
{
db::Layout ly;
db::LayerMap lmap;
unsigned int nwell = define_layer (ly, lmap, 1);
unsigned int active = define_layer (ly, lmap, 2);
unsigned int pplus = define_layer (ly, lmap, 10);
unsigned int nplus = define_layer (ly, lmap, 11);
unsigned int poly = define_layer (ly, lmap, 3);
unsigned int poly_lbl = define_layer (ly, lmap, 3, 1);
unsigned int diff_cont = define_layer (ly, lmap, 4);
unsigned int poly_cont = define_layer (ly, lmap, 5);
unsigned int metal1 = define_layer (ly, lmap, 6);
unsigned int metal1_lbl = define_layer (ly, lmap, 6, 1);
unsigned int via1 = define_layer (ly, lmap, 7);
unsigned int metal2 = define_layer (ly, lmap, 8);
unsigned int metal2_lbl = define_layer (ly, lmap, 8, 1);
{
db::LoadLayoutOptions options;
options.get_options<db::CommonReaderOptions> ().layer_map = lmap;
options.get_options<db::CommonReaderOptions> ().create_other_layers = false;
std::string fn (tl::testsrc ());
fn = tl::combine_path (fn, "testdata");
fn = tl::combine_path (fn, "algo");
fn = tl::combine_path (fn, "lvs_test_1.gds");
tl::InputStream stream (fn);
db::Reader reader (stream);
reader.read (ly, options);
}
db::Cell &tc = ly.cell (*ly.begin_top_down ());
db::LayoutVsSchematic lvs (db::RecursiveShapeIterator (ly, tc, std::set<unsigned int> ()));
std::auto_ptr<db::Region> rbulk (lvs.make_layer ("bulk"));
std::auto_ptr<db::Region> rnwell (lvs.make_layer (nwell, "nwell"));
std::auto_ptr<db::Region> ractive (lvs.make_layer (active, "active"));
std::auto_ptr<db::Region> rpplus (lvs.make_layer (pplus, "pplus"));
std::auto_ptr<db::Region> rnplus (lvs.make_layer (nplus, "nplus"));
std::auto_ptr<db::Region> rpoly (lvs.make_polygon_layer (poly, "poly"));
std::auto_ptr<db::Region> rpoly_lbl (lvs.make_text_layer (poly_lbl, "poly_lbl"));
std::auto_ptr<db::Region> rdiff_cont (lvs.make_polygon_layer (diff_cont, "diff_cont"));
std::auto_ptr<db::Region> rpoly_cont (lvs.make_polygon_layer (poly_cont, "poly_cont"));
std::auto_ptr<db::Region> rmetal1 (lvs.make_polygon_layer (metal1, "metal1"));
std::auto_ptr<db::Region> rmetal1_lbl (lvs.make_text_layer (metal1_lbl, "metal1_lbl"));
std::auto_ptr<db::Region> rvia1 (lvs.make_polygon_layer (via1, "via1"));
std::auto_ptr<db::Region> rmetal2 (lvs.make_polygon_layer (metal2, "metal2"));
std::auto_ptr<db::Region> rmetal2_lbl (lvs.make_text_layer (metal2_lbl, "metal2_lbl"));
// derived regions
db::Region ractive_in_nwell = *ractive & *rnwell;
db::Region rpactive = ractive_in_nwell & *rpplus;
db::Region rntie = ractive_in_nwell & *rnplus;
db::Region rpgate = rpactive & *rpoly;
db::Region rpsd = rpactive - rpgate;
db::Region ractive_outside_nwell = *ractive - *rnwell;
db::Region rnactive = ractive_outside_nwell & *rnplus;
db::Region rptie = ractive_outside_nwell & *rpplus;
db::Region rngate = rnactive & *rpoly;
db::Region rnsd = rnactive - rngate;
// return the computed layers into the original layout and write it for debugging purposes
unsigned int lgate = ly.insert_layer (db::LayerProperties (20, 0)); // 20/0 -> Gate
unsigned int lsd = ly.insert_layer (db::LayerProperties (21, 0)); // 21/0 -> Source/Drain
unsigned int lpdiff = ly.insert_layer (db::LayerProperties (22, 0)); // 22/0 -> P Diffusion
unsigned int lndiff = ly.insert_layer (db::LayerProperties (23, 0)); // 23/0 -> N Diffusion
unsigned int lptie = ly.insert_layer (db::LayerProperties (24, 0)); // 24/0 -> P Tie
unsigned int lntie = ly.insert_layer (db::LayerProperties (25, 0)); // 25/0 -> N Tie
rpgate.insert_into (&ly, tc.cell_index (), lgate);
rngate.insert_into (&ly, tc.cell_index (), lgate);
rpsd.insert_into (&ly, tc.cell_index (), lsd);
rnsd.insert_into (&ly, tc.cell_index (), lsd);
rpsd.insert_into (&ly, tc.cell_index (), lpdiff);
rnsd.insert_into (&ly, tc.cell_index (), lndiff);
rpsd.insert_into (&ly, tc.cell_index (), lptie);
rnsd.insert_into (&ly, tc.cell_index (), lntie);
db::NetlistDeviceExtractorMOS4Transistor pmos_ex ("PMOS");
db::NetlistDeviceExtractorMOS4Transistor nmos_ex ("NMOS");
// device extraction
db::NetlistDeviceExtractor::input_layers dl;
dl["SD"] = &rpsd;
dl["G"] = &rpgate;
dl["P"] = rpoly.get (); // not needed for extraction but to return terminal shapes
dl["W"] = rnwell.get ();
lvs.extract_devices (pmos_ex, dl);
dl["SD"] = &rnsd;
dl["G"] = &rngate;
dl["P"] = rpoly.get (); // not needed for extraction but to return terminal shapes
dl["W"] = rbulk.get ();
lvs.extract_devices (nmos_ex, dl);
// net extraction
lvs.register_layer (rpsd, "psd");
lvs.register_layer (rnsd, "nsd");
lvs.register_layer (rptie, "ptie");
lvs.register_layer (rntie, "ntie");
// Intra-layer
lvs.connect (rpsd);
lvs.connect (rnsd);
lvs.connect (*rnwell);
lvs.connect (*rpoly);
lvs.connect (*rdiff_cont);
lvs.connect (*rpoly_cont);
lvs.connect (*rmetal1);
lvs.connect (*rvia1);
lvs.connect (*rmetal2);
lvs.connect (rptie);
lvs.connect (rntie);
// Inter-layer
lvs.connect (rpsd, *rdiff_cont);
lvs.connect (rnsd, *rdiff_cont);
lvs.connect (*rpoly, *rpoly_cont);
lvs.connect (*rpoly_cont, *rmetal1);
lvs.connect (*rdiff_cont, *rmetal1);
lvs.connect (*rdiff_cont, rptie);
lvs.connect (*rdiff_cont, rntie);
lvs.connect (*rnwell, rntie);
lvs.connect (*rmetal1, *rvia1);
lvs.connect (*rvia1, *rmetal2);
lvs.connect (*rpoly, *rpoly_lbl); // attaches labels
lvs.connect (*rmetal1, *rmetal1_lbl); // attaches labels
lvs.connect (*rmetal2, *rmetal2_lbl); // attaches labels
// Global
lvs.connect_global (rptie, "BULK");
lvs.connect_global (*rbulk, "BULK");
// create some mess - we have to keep references to the layers to make them not disappear
rmetal1_lbl.reset (0);
rmetal2_lbl.reset (0);
rpoly_lbl.reset (0);
lvs.extract_netlist ();
// doesn't do anything here, but we test that this does not destroy anything:
lvs.netlist ()->combine_devices ();
// make pins for named nets of top-level circuits - this way they are not purged
lvs.netlist ()->make_top_level_pins ();
lvs.netlist ()->purge ();
// read the reference netlist
{
db::NetlistSpiceReader reader;
std::string fn (tl::testsrc ());
fn = tl::combine_path (fn, "testdata");
fn = tl::combine_path (fn, "algo");
fn = tl::combine_path (fn, "lvs_test_1.spi");
std::auto_ptr<db::Netlist> netlist (new db::Netlist ());
tl::InputStream stream (fn);
reader.read (stream, *netlist);
lvs.set_reference_netlist (netlist.release ());
}
// perform the compare
{
db::NetlistComparer comparer;
lvs.compare_netlists (&comparer);
}
// save and compare
std::string path = tmp_file ("tmp_lvstest1.lvsdb");
lvs.save (path, false);
std::string au_path = tl::combine_path (tl::combine_path (tl::combine_path (tl::testsrc (), "testdata"), "algo"), "lvs_test1_au.lvsdb" AUFILE_SUFFIX);
compare_lvsdbs (_this, path, au_path);
// load, save and compare
db::LayoutVsSchematic lvs2;
std::string path2 = tmp_file ("tmp_lvstest1b.lvsdb");
lvs2.load (path);
lvs2.save (path2, false);
std::string au_path2 = tl::combine_path (tl::combine_path (tl::combine_path (tl::testsrc (), "testdata"), "algo"), "lvs_test1b_au.lvsdb" AUFILE_SUFFIX);
compare_lvsdbs (_this, path2, au_path2);
}
TEST(2_FlowWithErrors)
{
db::Layout ly;
db::LayerMap lmap;
unsigned int nwell = define_layer (ly, lmap, 1);
unsigned int active = define_layer (ly, lmap, 2);
unsigned int pplus = define_layer (ly, lmap, 10);
unsigned int nplus = define_layer (ly, lmap, 11);
unsigned int poly = define_layer (ly, lmap, 3);
unsigned int poly_lbl = define_layer (ly, lmap, 3, 1);
unsigned int diff_cont = define_layer (ly, lmap, 4);
unsigned int poly_cont = define_layer (ly, lmap, 5);
unsigned int metal1 = define_layer (ly, lmap, 6);
unsigned int metal1_lbl = define_layer (ly, lmap, 6, 1);
unsigned int via1 = define_layer (ly, lmap, 7);
unsigned int metal2 = define_layer (ly, lmap, 8);
unsigned int metal2_lbl = define_layer (ly, lmap, 8, 1);
{
db::LoadLayoutOptions options;
options.get_options<db::CommonReaderOptions> ().layer_map = lmap;
options.get_options<db::CommonReaderOptions> ().create_other_layers = false;
std::string fn (tl::testsrc ());
fn = tl::combine_path (fn, "testdata");
fn = tl::combine_path (fn, "algo");
fn = tl::combine_path (fn, "lvs_test_1.gds");
tl::InputStream stream (fn);
db::Reader reader (stream);
reader.read (ly, options);
}
db::Cell &tc = ly.cell (*ly.begin_top_down ());
db::LayoutVsSchematic lvs (db::RecursiveShapeIterator (ly, tc, std::set<unsigned int> ()));
std::auto_ptr<db::Region> rbulk (lvs.make_layer ("bulk"));
std::auto_ptr<db::Region> rnwell (lvs.make_layer (nwell, "nwell"));
std::auto_ptr<db::Region> ractive (lvs.make_layer (active, "active"));
std::auto_ptr<db::Region> rpplus (lvs.make_layer (pplus, "pplus"));
std::auto_ptr<db::Region> rnplus (lvs.make_layer (nplus, "nplus"));
std::auto_ptr<db::Region> rpoly (lvs.make_polygon_layer (poly, "poly"));
std::auto_ptr<db::Region> rpoly_lbl (lvs.make_text_layer (poly_lbl, "poly_lbl"));
std::auto_ptr<db::Region> rdiff_cont (lvs.make_polygon_layer (diff_cont, "diff_cont"));
std::auto_ptr<db::Region> rpoly_cont (lvs.make_polygon_layer (poly_cont, "poly_cont"));
std::auto_ptr<db::Region> rmetal1 (lvs.make_polygon_layer (metal1, "metal1"));
std::auto_ptr<db::Region> rmetal1_lbl (lvs.make_text_layer (metal1_lbl, "metal1_lbl"));
std::auto_ptr<db::Region> rvia1 (lvs.make_polygon_layer (via1, "via1"));
std::auto_ptr<db::Region> rmetal2 (lvs.make_polygon_layer (metal2, "metal2"));
std::auto_ptr<db::Region> rmetal2_lbl (lvs.make_text_layer (metal2_lbl, "metal2_lbl"));
// derived regions
db::Region ractive_in_nwell = *ractive & *rnwell;
db::Region rpactive = ractive_in_nwell & *rpplus;
db::Region rntie = ractive_in_nwell & *rnplus;
db::Region rpgate = rpactive & *rpoly;
db::Region rpsd = rpactive - rpgate;
db::Region ractive_outside_nwell = *ractive - *rnwell;
db::Region rnactive = ractive_outside_nwell & *rnplus;
db::Region rptie = ractive_outside_nwell & *rpplus;
db::Region rngate = rnactive & *rpoly;
db::Region rnsd = rnactive - rngate;
// return the computed layers into the original layout and write it for debugging purposes
unsigned int lgate = ly.insert_layer (db::LayerProperties (20, 0)); // 20/0 -> Gate
unsigned int lsd = ly.insert_layer (db::LayerProperties (21, 0)); // 21/0 -> Source/Drain
unsigned int lpdiff = ly.insert_layer (db::LayerProperties (22, 0)); // 22/0 -> P Diffusion
unsigned int lndiff = ly.insert_layer (db::LayerProperties (23, 0)); // 23/0 -> N Diffusion
unsigned int lptie = ly.insert_layer (db::LayerProperties (24, 0)); // 24/0 -> P Tie
unsigned int lntie = ly.insert_layer (db::LayerProperties (25, 0)); // 25/0 -> N Tie
rpgate.insert_into (&ly, tc.cell_index (), lgate);
rngate.insert_into (&ly, tc.cell_index (), lgate);
rpsd.insert_into (&ly, tc.cell_index (), lsd);
rnsd.insert_into (&ly, tc.cell_index (), lsd);
rpsd.insert_into (&ly, tc.cell_index (), lpdiff);
rnsd.insert_into (&ly, tc.cell_index (), lndiff);
rpsd.insert_into (&ly, tc.cell_index (), lptie);
rnsd.insert_into (&ly, tc.cell_index (), lntie);
db::NetlistDeviceExtractorMOS4Transistor pmos_ex ("PMOS");
db::NetlistDeviceExtractorMOS4Transistor nmos_ex ("NMOS");
// device extraction
db::NetlistDeviceExtractor::input_layers dl;
dl["SD"] = &rpsd;
dl["G"] = &rpgate;
dl["P"] = rpoly.get (); // not needed for extraction but to return terminal shapes
dl["W"] = rnwell.get ();
lvs.extract_devices (pmos_ex, dl);
dl["SD"] = &rnsd;
dl["G"] = &rngate;
dl["P"] = rpoly.get (); // not needed for extraction but to return terminal shapes
dl["W"] = rbulk.get ();
lvs.extract_devices (nmos_ex, dl);
// net extraction
lvs.register_layer (rpsd, "psd");
lvs.register_layer (rnsd, "nsd");
lvs.register_layer (rptie, "ptie");
lvs.register_layer (rntie, "ntie");
// Intra-layer
lvs.connect (rpsd);
lvs.connect (rnsd);
lvs.connect (*rnwell);
lvs.connect (*rpoly);
lvs.connect (*rdiff_cont);
lvs.connect (*rpoly_cont);
lvs.connect (*rmetal1);
lvs.connect (*rvia1);
lvs.connect (*rmetal2);
lvs.connect (rptie);
lvs.connect (rntie);
// Inter-layer
lvs.connect (rpsd, *rdiff_cont);
lvs.connect (rnsd, *rdiff_cont);
lvs.connect (*rpoly, *rpoly_cont);
lvs.connect (*rpoly_cont, *rmetal1);
lvs.connect (*rdiff_cont, *rmetal1);
lvs.connect (*rdiff_cont, rptie);
lvs.connect (*rdiff_cont, rntie);
lvs.connect (*rnwell, rntie);
lvs.connect (*rmetal1, *rvia1);
lvs.connect (*rvia1, *rmetal2);
lvs.connect (*rpoly, *rpoly_lbl); // attaches labels
lvs.connect (*rmetal1, *rmetal1_lbl); // attaches labels
lvs.connect (*rmetal2, *rmetal2_lbl); // attaches labels
// Global
lvs.connect_global (rptie, "BULK");
lvs.connect_global (*rbulk, "BULK");
// create some mess - we have to keep references to the layers to make them not disappear
rmetal1_lbl.reset (0);
rmetal2_lbl.reset (0);
rpoly_lbl.reset (0);
lvs.extract_netlist ();
// doesn't do anything here, but we test that this does not destroy anything:
lvs.netlist ()->combine_devices ();
// make pins for named nets of top-level circuits - this way they are not purged
lvs.netlist ()->make_top_level_pins ();
lvs.netlist ()->purge ();
// read the reference netlist
{
db::NetlistSpiceReader reader;
std::string fn (tl::testsrc ());
fn = tl::combine_path (fn, "testdata");
fn = tl::combine_path (fn, "algo");
fn = tl::combine_path (fn, "lvs_test_2.spi");
std::auto_ptr<db::Netlist> netlist (new db::Netlist ());
tl::InputStream stream (fn);
reader.read (stream, *netlist);
lvs.set_reference_netlist (netlist.release ());
}
// perform the compare
{
db::NetlistComparer comparer;
lvs.compare_netlists (&comparer);
}
// save and compare
std::string path = tmp_file ("tmp_lvstest2.lvsdb");
lvs.save (path, false);
std::string au_path = tl::combine_path (tl::combine_path (tl::combine_path (tl::testsrc (), "testdata"), "algo"), "lvs_test2_au.lvsdb" AUFILE_SUFFIX);
compare_lvsdbs (_this, path, au_path);
// load, save and compare
db::LayoutVsSchematic lvs2;
std::string path2 = tmp_file ("tmp_lvstest2b.lvsdb");
lvs2.load (path);
lvs2.save (path2, false);
std::string au_path2 = tl::combine_path (tl::combine_path (tl::combine_path (tl::testsrc (), "testdata"), "algo"), "lvs_test2b_au.lvsdb" AUFILE_SUFFIX);
compare_lvsdbs (_this, path2, au_path2);
}

View File

@ -23,6 +23,7 @@
#include "tlUnitTest.h"
#include "dbNetlistDeviceClasses.h"
#include "dbNetlistCompare.h"
#include "dbNetlistCrossReference.h"
class NetlistCompareTestLogger
: public db::NetlistCompareLogger
@ -162,6 +163,114 @@ private:
}
};
std::string xref_status2s (db::NetlistCrossReference::Status status)
{
if (status == db::NetlistCrossReference::Match) {
return "Match";
} else if (status == db::NetlistCrossReference::Mismatch) {
return "Mismatch";
} else if (status == db::NetlistCrossReference::NoMatch) {
return "NoMatch";
} else if (status == db::NetlistCrossReference::MatchWithWarning) {
return "MatchWithWarning";
} else if (status == db::NetlistCrossReference::Skipped) {
return "Skipped";
} else {
return "None";
}
}
template <class Obj>
static std::string name_of (const Obj *obj)
{
return obj ? obj->name () : std::string ("(null)");
}
template <class Obj>
static std::string expanded_name_of (const Obj *obj)
{
return obj ? obj->expanded_name () : std::string ("(null)");
}
template <class PairData>
static
std::string dp2s (const PairData &dp)
{
return expanded_name_of (dp.pair.first) + ":" + expanded_name_of (dp.pair.second) + " [" + xref_status2s (dp.status) + "]";
}
static std::string ne2s (const db::NetTerminalRef *ref)
{
if (ref) {
return expanded_name_of (ref->device ()) + "[" + name_of (ref->terminal_def ()) + "]";
} else {
return "(null)";
}
}
static std::string ne2s (const db::NetSubcircuitPinRef *ref)
{
if (ref) {
return expanded_name_of (ref->subcircuit ()) + "[" + expanded_name_of (ref->pin ()) + "]";
} else {
return "(null)";
}
}
static std::string ne2s (const db::NetPinRef *ref)
{
if (ref) {
return expanded_name_of (ref->pin ());
} else {
return "(null)";
}
}
template <class Data>
static std::string nep2s (const std::pair<const Data *, const Data *> &p)
{
return ne2s (p.first) + ":" + ne2s (p.second);
}
std::string xref2s (const db::NetlistCrossReference &xref)
{
std::string s;
for (db::NetlistCrossReference::circuits_iterator c = xref.begin_circuits (); c != xref.end_circuits (); ++c) {
const db::NetlistCrossReference::PerCircuitData *pcd = xref.per_circuit_data_for (*c);
tl_assert (pcd != 0);
s += name_of (c->first) + ":" + name_of (c->second) + " [" + xref_status2s (pcd->status) + "]:\n";
for (db::NetlistCrossReference::PerCircuitData::pin_pairs_const_iterator i = pcd->pins.begin (); i != pcd->pins.end (); ++i) {
s += " pin " + dp2s (*i) + "\n";
}
for (db::NetlistCrossReference::PerCircuitData::net_pairs_const_iterator i = pcd->nets.begin (); i != pcd->nets.end (); ++i) {
s += " net " + dp2s (*i) + "\n";
const db::NetlistCrossReference::PerNetData *pnd = xref.per_net_data_for (i->pair);
for (db::NetlistCrossReference::PerNetData::terminal_pairs_const_iterator j = pnd->terminals.begin (); j != pnd->terminals.end (); ++j) {
s += " terminal " + nep2s (*j) + "\n";
}
for (db::NetlistCrossReference::PerNetData::pin_pairs_const_iterator j = pnd->pins.begin (); j != pnd->pins.end (); ++j) {
s += " pin " + nep2s (*j) + "\n";
}
for (db::NetlistCrossReference::PerNetData::subcircuit_pin_pairs_const_iterator j = pnd->subcircuit_pins.begin (); j != pnd->subcircuit_pins.end (); ++j) {
s += " subcircuit_pin " + nep2s (*j) + "\n";
}
}
for (db::NetlistCrossReference::PerCircuitData::device_pairs_const_iterator i = pcd->devices.begin (); i != pcd->devices.end (); ++i) {
s += " device " + dp2s (*i) + "\n";
}
for (db::NetlistCrossReference::PerCircuitData::subcircuit_pairs_const_iterator i = pcd->subcircuits.begin (); i != pcd->subcircuits.end (); ++i) {
s += " subcircuit " + dp2s (*i) + "\n";
}
}
return s;
}
static void prep_nl (db::Netlist &nl, const char *str)
{
db::DeviceClass *dc;
@ -358,6 +467,36 @@ TEST(1_SimpleInverter)
"end_circuit INV INV MATCH"
);
EXPECT_EQ (good, true);
db::NetlistCrossReference xref;
db::NetlistComparer comp_xref (&xref);
good = comp_xref.compare (&nl1, &nl2);
EXPECT_EQ (xref2s (xref),
"INV:INV [Match]:\n"
" pin $0:$1 [Match]\n"
" pin $1:$3 [Match]\n"
" pin $2:$0 [Match]\n"
" pin $3:$2 [Match]\n"
" net IN:IN [Match]\n"
" terminal $1[G]:$2[G]\n"
" terminal $2[G]:$1[G]\n"
" pin $0:$1\n"
" net OUT:OUT [Match]\n"
" terminal $1[D]:$2[D]\n"
" terminal $2[D]:$1[S]\n"
" pin $1:$3\n"
" net VDD:VDD [Match]\n"
" terminal $1[S]:$2[S]\n"
" pin $2:$0\n"
" net VSS:VSS [Match]\n"
" terminal $2[S]:$1[D]\n"
" pin $3:$2\n"
" device $2:$1 [Match]\n"
" device $1:$2 [Match]\n"
);
EXPECT_EQ (good, true);
}
TEST(1_SimpleInverterMatchedDeviceClasses)
@ -438,9 +577,9 @@ TEST(1_SimpleInverterSkippedDevices)
EXPECT_EQ (logger.text (),
"begin_circuit INV INV\n"
"match_nets VDD VDD\n"
"match_nets OUT OUT\n"
"net_mismatch OUT OUT\n"
"match_nets VSS VSS\n"
"match_nets IN IN\n"
"net_mismatch IN IN\n"
"match_pins $0 $1\n"
"match_pins $1 $3\n"
"match_pins $2 $0\n"
@ -453,6 +592,42 @@ TEST(1_SimpleInverterSkippedDevices)
);
EXPECT_EQ (good, false);
db::NetlistCrossReference xref;
db::NetlistComparer comp_xref (&xref);
good = comp_xref.compare (&nl1, &nl2);
EXPECT_EQ (xref2s (xref),
"INV:INV [NoMatch]:\n"
" pin $0:$1 [Match]\n"
" pin $1:$3 [Match]\n"
" pin $2:$0 [Match]\n"
" pin $3:$2 [Match]\n"
" net IN:IN [Mismatch]\n"
" terminal (null):$3[B]\n"
" terminal $1[G]:$4[G]\n"
" terminal $2[B]:$2[B]\n"
" terminal $3[G]:$1[G]\n"
" pin $0:$1\n"
" net OUT:OUT [Mismatch]\n"
" terminal (null):$3[A]\n"
" terminal $1[D]:$4[D]\n"
" terminal $2[A]:$2[A]\n"
" terminal $3[D]:$1[S]\n"
" pin $1:$3\n"
" net VDD:VDD [Match]\n"
" terminal $1[S]:$4[S]\n"
" pin $2:$0\n"
" net VSS:VSS [Match]\n"
" terminal $3[S]:$1[D]\n"
" pin $3:$2\n"
" device (null):$3 [Mismatch]\n"
" device $3:$1 [Match]\n"
" device $2:$2 [MatchWithWarning]\n"
" device $1:$4 [Match]\n"
);
EXPECT_EQ (good, false);
comp.exclude_caps (1e-11);
comp.exclude_resistors (900.0);
@ -474,6 +649,86 @@ TEST(1_SimpleInverterSkippedDevices)
"end_circuit INV INV MATCH"
);
EXPECT_EQ (good, true);
xref.clear ();
comp_xref.exclude_caps (1e-11);
comp_xref.exclude_resistors (900.0);
good = comp_xref.compare (&nl1, &nl2);
EXPECT_EQ (xref2s (xref),
"INV:INV [Match]:\n"
" pin $0:$1 [Match]\n"
" pin $1:$3 [Match]\n"
" pin $2:$0 [Match]\n"
" pin $3:$2 [Match]\n"
" net IN:IN [Match]\n"
" terminal (null):$2[B]\n"
" terminal (null):$3[B]\n"
" terminal $1[G]:$4[G]\n"
" terminal $2[B]:(null)\n"
" terminal $3[G]:$1[G]\n"
" pin $0:$1\n"
" net OUT:OUT [Match]\n"
" terminal (null):$2[A]\n"
" terminal (null):$3[A]\n"
" terminal $1[D]:$4[D]\n"
" terminal $2[A]:(null)\n"
" terminal $3[D]:$1[S]\n"
" pin $1:$3\n"
" net VDD:VDD [Match]\n"
" terminal $1[S]:$4[S]\n"
" pin $2:$0\n"
" net VSS:VSS [Match]\n"
" terminal $3[S]:$1[D]\n"
" pin $3:$2\n"
" device $3:$1 [Match]\n"
" device $1:$4 [Match]\n"
);
EXPECT_EQ (good, true);
xref.clear ();
comp_xref.exclude_caps (-1);
comp_xref.exclude_resistors (-1);
comp_xref.same_device_classes (0, nl2.device_class_by_name ("RES"));
comp_xref.same_device_classes (0, nl2.device_class_by_name ("CAP"));
comp_xref.same_device_classes (nl1.device_class_by_name ("RES"), 0);
comp_xref.same_device_classes (nl1.device_class_by_name ("CAP"), 0);
good = comp_xref.compare (&nl1, &nl2);
EXPECT_EQ (xref2s (xref),
"INV:INV [Match]:\n"
" pin $0:$1 [Match]\n"
" pin $1:$3 [Match]\n"
" pin $2:$0 [Match]\n"
" pin $3:$2 [Match]\n"
" net IN:IN [Match]\n"
" terminal (null):$2[B]\n"
" terminal (null):$3[B]\n"
" terminal $1[G]:$4[G]\n"
" terminal $2[B]:(null)\n"
" terminal $3[G]:$1[G]\n"
" pin $0:$1\n"
" net OUT:OUT [Match]\n"
" terminal (null):$2[A]\n"
" terminal (null):$3[A]\n"
" terminal $1[D]:$4[D]\n"
" terminal $2[A]:(null)\n"
" terminal $3[D]:$1[S]\n"
" pin $1:$3\n"
" net VDD:VDD [Match]\n"
" terminal $1[S]:$4[S]\n"
" pin $2:$0\n"
" net VSS:VSS [Match]\n"
" terminal $3[S]:$1[D]\n"
" pin $3:$2\n"
" device $3:$1 [Match]\n"
" device $1:$4 [Match]\n"
);
EXPECT_EQ (good, true);
}
TEST(2_SimpleInverterWithForcedNetAssignment)
@ -670,8 +925,8 @@ TEST(5_BufferTwoPathsDifferentParameters)
"begin_circuit BUF BUF\n"
"match_nets OUT OUT\n"
"match_nets INT $10\n"
"match_nets IN IN\n"
"match_nets INT2 $11\n"
"net_mismatch IN IN\n"
"net_mismatch INT2 $11\n"
"match_pins $0 $1\n"
"match_pins $1 $3\n"
"match_pins $2 $0\n"
@ -722,8 +977,8 @@ TEST(5_BufferTwoPathsDifferentParameters)
"begin_circuit BUF BUF\n"
"match_nets OUT OUT\n"
"match_nets INT $10\n"
"match_nets IN IN\n"
"match_nets INT2 $11\n"
"net_mismatch IN IN\n"
"net_mismatch INT2 $11\n"
"match_pins $0 $1\n"
"match_pins $1 $3\n"
"match_pins $2 $0\n"
@ -748,8 +1003,8 @@ TEST(5_BufferTwoPathsDifferentParameters)
"begin_circuit BUF BUF\n"
"match_nets OUT OUT\n"
"match_nets INT $10\n"
"match_nets IN IN\n"
"match_nets INT2 $11\n"
"net_mismatch IN IN\n"
"net_mismatch INT2 $11\n"
"match_pins $0 $1\n"
"match_pins $1 $3\n"
"match_pins $2 $0\n"
@ -828,8 +1083,8 @@ TEST(5_BufferTwoPathsDifferentParameters)
"begin_circuit BUF BUF\n"
"match_nets OUT OUT\n"
"match_nets INT $10\n"
"match_nets IN IN\n"
"match_nets INT2 $11\n"
"net_mismatch IN IN\n"
"net_mismatch INT2 $11\n"
"match_pins $0 $1\n"
"match_pins $1 $3\n"
"match_pins $2 $0\n"
@ -892,8 +1147,8 @@ TEST(5_BufferTwoPathsDifferentDeviceClasses)
"begin_circuit BUF BUF\n"
"match_nets INT $10\n"
"match_nets IN IN\n"
"match_nets OUT OUT\n"
"match_nets INT2 $11\n"
"net_mismatch OUT OUT\n"
"net_mismatch INT2 $11\n"
"match_pins $0 $1\n"
"match_pins $1 $3\n"
"match_pins $2 $0\n"
@ -955,10 +1210,10 @@ TEST(6_BufferTwoPathsAdditionalResistor)
EXPECT_EQ (logger.text (),
"begin_circuit BUF BUF\n"
"match_nets INT $10\n"
"net_mismatch INT $10\n"
"match_nets IN IN\n"
"match_nets OUT OUT\n"
"match_nets INT2 $11\n"
"net_mismatch INT2 $11\n"
"match_pins $0 $1\n"
"match_pins $1 $3\n"
"match_pins $2 $0\n"
@ -1017,11 +1272,11 @@ TEST(6_BufferTwoPathsAdditionalDevices)
EXPECT_EQ (logger.text (),
"begin_circuit BUF BUF\n"
"match_nets INT $11\n"
"match_nets VDD VDD\n"
"net_mismatch VDD VDD\n"
"match_nets IN IN\n"
"match_nets VSS VSS\n"
"match_nets OUT OUT\n"
"match_nets INT2 $10\n"
"net_mismatch VSS VSS\n"
"net_mismatch OUT OUT\n"
"net_mismatch INT2 $10\n"
"match_pins $0 $1\n"
"match_pins $1 $3\n"
"match_pins $2 $0\n"
@ -1110,8 +1365,8 @@ TEST(7_ResistorsParameterMismatch)
EXPECT_EQ (logger.text (),
"begin_circuit TRIANGLE TRIANGLE\n"
"match_nets P2 P2\n"
"match_nets P1 P1\n"
"match_nets P3 P3\n"
"net_mismatch P1 P1\n"
"net_mismatch P3 P3\n"
"match_pins $0 $0\n"
"match_pins $1 $1\n"
"match_pins $2 $2\n"
@ -1152,8 +1407,8 @@ TEST(7_ResistorsPlusOneDevice)
EXPECT_EQ (logger.text (),
"begin_circuit TRIANGLE TRIANGLE\n"
"match_nets P3 P3\n"
"match_nets P2 P2\n"
"match_nets P1 P1\n"
"net_mismatch P2 P2\n"
"net_mismatch P1 P1\n"
"match_pins $0 $0\n"
"match_pins $1 $1\n"
"match_pins $2 $2\n"
@ -1235,8 +1490,8 @@ TEST(8_DiodesDontMatchOnSwappedPins)
EXPECT_EQ (logger.text (),
"begin_circuit TRIANGLE TRIANGLE\n"
"match_nets P1 P3\n"
"match_nets P2 P1\n"
"match_nets P3 P2\n"
"net_mismatch P2 P1\n"
"net_mismatch P3 P2\n"
"match_pins $0 $2\n"
"match_pins $1 $0\n"
"match_pins $2 $1\n"
@ -1295,10 +1550,10 @@ TEST(10_SimpleSubCircuits)
"end_circuit INV INV MATCH\n"
"begin_circuit TOP TOP\n"
"match_nets OUT OUT\n"
"match_nets VDD VDD\n"
"match_nets IN IN\n"
"match_nets VSS VSS\n"
"match_nets INT INT\n"
"match_nets IN IN\n"
"match_nets VDD VDD\n"
"match_pins $0 $2\n"
"match_pins $1 $0\n"
"match_pins $2 $1\n"
@ -1362,10 +1617,10 @@ TEST(10_SimpleSubCircuitsMatchedNames)
"end_circuit INV INVB MATCH\n"
"begin_circuit TOP TOP\n"
"match_nets OUT OUT\n"
"match_nets VDD VDD\n"
"match_nets IN IN\n"
"match_nets VSS VSS\n"
"match_nets INT INT\n"
"match_nets IN IN\n"
"match_nets VDD VDD\n"
"match_pins $0 $2\n"
"match_pins $1 $0\n"
"match_pins $2 $1\n"
@ -1386,8 +1641,8 @@ TEST(11_MismatchingSubcircuits)
" device NMOS $2 (S=VSS,G=IN,D=OUT) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n"
"end;\n"
"circuit TOP ($0=IN,$1=OUT,$2=VDD,$3=VSS);\n"
" subcircuit INV $1 ($1=IN,$2=INT,$3=VDD,$4=VSS);\n"
" subcircuit INV $2 ($1=INT,$2=OUT,$3=VDD,$4=VSS);\n"
" subcircuit INV $1 ($0=IN,$1=INT,$2=VDD,$3=VSS);\n"
" subcircuit INV $2 ($0=INT,$1=OUT,$2=VDD,$3=VSS);\n"
"end;\n";
const char *nls2 =
@ -1397,8 +1652,8 @@ TEST(11_MismatchingSubcircuits)
" device PMOS $2 (S=IN,G=IN,D=OUT) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n"
"end;\n"
"circuit TOP ($0=OUT,$1=VDD,$2=IN,$3=VSS);\n"
" subcircuit INV $1 ($1=VDD,$2=INT,$3=VSS,$4=OUT);\n"
" subcircuit INV $2 ($1=VDD,$2=IN,$3=VSS,$4=INT);\n"
" subcircuit INV $1 ($0=VDD,$1=INT,$2=VSS,$3=OUT);\n"
" subcircuit INV $2 ($0=VDD,$1=IN,$2=VSS,$3=INT);\n"
"end;\n";
db::Netlist nl1, nl2;
@ -1413,9 +1668,9 @@ TEST(11_MismatchingSubcircuits)
EXPECT_EQ (logger.text (),
"begin_circuit INV INV\n"
"match_nets VSS VSS\n"
"match_nets OUT OUT\n"
"match_nets IN IN\n"
"match_nets VDD VDD\n"
"net_mismatch OUT OUT\n"
"net_mismatch IN IN\n"
"net_mismatch VDD VDD\n"
"match_pins $0 $1\n"
"match_pins $1 $3\n"
"match_pins $2 $0\n"
@ -1426,10 +1681,10 @@ TEST(11_MismatchingSubcircuits)
"end_circuit INV INV NOMATCH\n"
"begin_circuit TOP TOP\n"
"match_nets OUT OUT\n"
"match_nets VDD VDD\n"
"match_nets IN IN\n"
"match_nets VSS VSS\n"
"match_nets INT INT\n"
"match_nets IN IN\n"
"match_nets VDD VDD\n"
"match_pins $0 $2\n"
"match_pins $1 $0\n"
"match_pins $2 $1\n"
@ -1440,6 +1695,101 @@ TEST(11_MismatchingSubcircuits)
);
EXPECT_EQ (good, false);
db::NetlistCrossReference xref;
db::NetlistComparer comp_xref (&xref);
good = comp_xref.compare (&nl1, &nl2);
EXPECT_EQ (xref2s (xref),
"INV:INV [NoMatch]:\n"
" pin $0:$1 [Match]\n"
" pin $1:$3 [Match]\n"
" pin $2:$0 [Match]\n"
" pin $3:$2 [Match]\n"
" net IN:IN [Mismatch]\n"
" terminal (null):$2[S]\n"
" terminal (null):$2[G]\n"
" terminal $1[G]:(null)\n"
" terminal $2[G]:$1[G]\n"
" pin $0:$1\n"
" net OUT:OUT [Mismatch]\n"
" terminal (null):$2[D]\n"
" terminal $1[D]:(null)\n"
" terminal $2[D]:$1[S]\n"
" pin $1:$3\n"
" net VDD:VDD [Mismatch]\n"
" terminal $1[S]:(null)\n"
" pin $2:$0\n"
" net VSS:VSS [Match]\n"
" terminal $2[S]:$1[D]\n"
" pin $3:$2\n"
" device (null):$2 [Mismatch]\n"
" device $1:(null) [Mismatch]\n"
" device $2:$1 [Match]\n"
"TOP:TOP [Match]:\n"
" pin $0:$2 [Match]\n"
" pin $1:$0 [Match]\n"
" pin $2:$1 [Match]\n"
" pin $3:$3 [Match]\n"
" net IN:IN [Match]\n"
" pin $0:$2\n"
" subcircuit_pin $1[$0]:$2[$1]\n"
" net INT:INT [Match]\n"
" subcircuit_pin $1[$1]:$2[$3]\n"
" subcircuit_pin $2[$0]:$1[$1]\n"
" net OUT:OUT [Match]\n"
" pin $1:$0\n"
" subcircuit_pin $2[$1]:$1[$3]\n"
" net VDD:VDD [Match]\n"
" pin $2:$1\n"
" subcircuit_pin $1[$2]:$2[$0]\n"
" subcircuit_pin $2[$2]:$1[$0]\n"
" net VSS:VSS [Match]\n"
" pin $3:$3\n"
" subcircuit_pin $1[$3]:$2[$2]\n"
" subcircuit_pin $2[$3]:$1[$2]\n"
" subcircuit $2:$1 [Match]\n"
" subcircuit $1:$2 [Match]\n"
);
EXPECT_EQ (good, false);
xref.clear ();
// ignore the subcircuits to make them match
comp_xref.same_circuits (nl1.circuit_by_name ("INV"), 0);
comp_xref.same_circuits (0, nl2.circuit_by_name ("INV"));
good = comp_xref.compare (&nl1, &nl2);
// nets are now ambiguous
EXPECT_EQ (xref2s (xref),
"TOP:TOP [Match]:\n"
" pin $0:$0 [Match]\n"
" pin $1:$1 [Match]\n"
" pin $2:$2 [Match]\n"
" pin $3:$3 [Match]\n"
" net IN:OUT [MatchWithWarning]\n"
" pin $0:$0\n"
" subcircuit_pin (null):$1[$3]\n"
" subcircuit_pin $1[$0]:(null)\n"
" net OUT:VDD [MatchWithWarning]\n"
" pin $1:$1\n"
" subcircuit_pin (null):$1[$0]\n"
" subcircuit_pin (null):$2[$0]\n"
" subcircuit_pin $2[$1]:(null)\n"
" net VDD:IN [MatchWithWarning]\n"
" pin $2:$2\n"
" subcircuit_pin (null):$2[$1]\n"
" subcircuit_pin $1[$2]:(null)\n"
" subcircuit_pin $2[$2]:(null)\n"
" net VSS:VSS [MatchWithWarning]\n"
" pin $3:$3\n"
" subcircuit_pin (null):$1[$2]\n"
" subcircuit_pin (null):$2[$2]\n"
" subcircuit_pin $1[$3]:(null)\n"
" subcircuit_pin $2[$3]:(null)\n"
);
EXPECT_EQ (good, true);
}
TEST(12_MismatchingSubcircuitsDuplicates)
@ -1489,10 +1839,10 @@ TEST(12_MismatchingSubcircuitsDuplicates)
"end_circuit INV INV MATCH\n"
"begin_circuit TOP TOP\n"
"match_nets IN IN\n"
"match_nets VDD VDD\n"
"match_nets VSS VSS\n"
"match_nets INT INT\n"
"match_nets OUT OUT\n"
"net_mismatch INT INT\n"
"net_mismatch VSS VSS\n"
"net_mismatch VDD VDD\n"
"net_mismatch OUT OUT\n"
"match_pins $0 $1\n"
"match_pins $1 $0\n"
"match_pins $2 $2\n"
@ -1557,10 +1907,10 @@ TEST(13_MismatchingSubcircuitsAdditionalHierarchy)
"end_circuit INV INV MATCH\n"
"begin_circuit TOP TOP\n"
"match_nets OUT OUT\n"
"match_nets VDD VDD\n"
"match_nets IN IN\n"
"match_nets VSS VSS\n"
"match_nets INT INT\n"
"match_nets IN IN\n"
"match_nets VDD VDD\n"
"match_pins $0 $1\n"
"match_pins $1 $0\n"
"match_pins $2 $2\n"
@ -1629,11 +1979,11 @@ TEST(14_Subcircuit2Nand)
"end_circuit NAND NAND MATCH\n"
"begin_circuit TOP TOP\n"
"match_nets OUT OUT\n"
"match_nets VSS VSS\n"
"match_nets VDD VDD\n"
"match_nets INT INT\n"
"match_nets IN2 IN2\n"
"match_nets IN1 IN1\n"
"match_nets VDD VDD\n"
"match_nets VSS VSS\n"
"match_pins $0 $0\n"
"match_pins $1 $1\n"
"match_pins $2 $2\n"
@ -1703,11 +2053,11 @@ TEST(14_Subcircuit2NandMismatchNoSwap)
"end_circuit NAND NAND MATCH\n"
"begin_circuit TOP TOP\n"
"match_nets OUT OUT\n"
"match_nets IN1 INT\n"
"match_nets INT IN1\n"
"match_nets VDD VDD\n"
"match_nets VSS VSS\n"
"match_nets IN2 IN2\n"
"match_nets VDD VDD\n"
"net_mismatch INT IN1\n"
"net_mismatch IN1 INT\n"
"net_mismatch IN2 IN2\n"
"pin_mismatch $0 (null)\n"
"match_pins $1 $1\n"
"match_pins $2 $2\n"
@ -1715,12 +2065,87 @@ TEST(14_Subcircuit2NandMismatchNoSwap)
"match_pins $4 $4\n"
"pin_mismatch (null) $0\n"
"match_subcircuits $2 $1\n"
"subcircuit_mismatch (null) $2\n"
"subcircuit_mismatch $1 (null)\n"
"subcircuit_mismatch $1 $2\n"
"end_circuit TOP TOP NOMATCH"
);
EXPECT_EQ (good, false);
db::NetlistCrossReference xref;
db::NetlistComparer comp_xref (&xref);
good = comp_xref.compare (&nl1, &nl2);
EXPECT_EQ (xref2s (xref),
"NAND:NAND [Match]:\n"
" pin $0:$0 [Match]\n"
" pin $1:$1 [Match]\n"
" pin $2:$2 [Match]\n"
" pin $3:$3 [Match]\n"
" pin $4:$4 [Match]\n"
" net A:A [Match]\n"
" terminal $1[G]:$1[G]\n"
" terminal $3[G]:$3[G]\n"
" pin $0:$0\n"
" net B:B [Match]\n"
" terminal $2[G]:$2[G]\n"
" terminal $4[G]:$4[G]\n"
" pin $1:$1\n"
" net INT:INT [Match]\n"
" terminal $3[D]:$3[D]\n"
" terminal $4[S]:$4[S]\n"
" net OUT:OUT [Match]\n"
" terminal $1[D]:$1[D]\n"
" terminal $2[D]:$2[D]\n"
" terminal $4[D]:$4[D]\n"
" pin $2:$2\n"
" net VDD:VDD [Match]\n"
" terminal $1[S]:$1[S]\n"
" terminal $2[S]:$2[S]\n"
" pin $3:$3\n"
" net VSS:VSS [Match]\n"
" terminal $3[S]:$3[S]\n"
" pin $4:$4\n"
" device $1:$1 [Match]\n"
" device $2:$2 [Match]\n"
" device $3:$3 [Match]\n"
" device $4:$4 [Match]\n"
"TOP:TOP [NoMatch]:\n"
" pin (null):$0 [Mismatch]\n"
" pin $0:(null) [Mismatch]\n"
" pin $1:$1 [Match]\n"
" pin $2:$2 [Match]\n"
" pin $3:$3 [Match]\n"
" pin $4:$4 [Match]\n"
" net IN1:INT [Mismatch]\n"
" pin $0:(null)\n"
" subcircuit_pin (null):$2[$2]\n"
" subcircuit_pin $1[$0]:(null)\n"
" subcircuit_pin $2[$0]:$1[$0]\n"
" net IN2:IN2 [Mismatch]\n"
" pin $1:$1\n"
" subcircuit_pin (null):$2[$0]\n"
" subcircuit_pin $1[$1]:(null)\n"
" net INT:IN1 [Mismatch]\n"
" pin (null):$0\n"
" subcircuit_pin (null):$2[$1]\n"
" subcircuit_pin $1[$2]:(null)\n"
" subcircuit_pin $2[$1]:$1[$1]\n"
" net OUT:OUT [Match]\n"
" pin $2:$2\n"
" subcircuit_pin $2[$2]:$1[$2]\n"
" net VDD:VDD [Match]\n"
" pin $3:$3\n"
" subcircuit_pin $1[$3]:$2[$3]\n"
" subcircuit_pin $2[$3]:$1[$3]\n"
" net VSS:VSS [Match]\n"
" pin $4:$4\n"
" subcircuit_pin $1[$4]:$2[$4]\n"
" subcircuit_pin $2[$4]:$1[$4]\n"
" subcircuit $2:$1 [Match]\n"
" subcircuit $1:$2 [Mismatch]\n"
);
EXPECT_EQ (good, false);
}
TEST(14_Subcircuit2MatchWithSwap)
@ -1779,11 +2204,11 @@ TEST(14_Subcircuit2MatchWithSwap)
"end_circuit NAND NAND MATCH\n"
"begin_circuit TOP TOP\n"
"match_nets OUT OUT\n"
"match_nets VSS VSS\n"
"match_nets VDD VDD\n"
"match_nets INT INT\n"
"match_nets IN2 IN2\n"
"match_nets IN1 IN1\n"
"match_nets VDD VDD\n"
"match_nets VSS VSS\n"
"match_pins $0 $0\n"
"match_pins $1 $1\n"
"match_pins $2 $2\n"
@ -2017,14 +2442,14 @@ TEST(16_UniqueSubCircuitMatching)
"end_circuit INV2 INV2 MATCH\n"
"begin_circuit INV2PAIR INV2PAIR\n"
"match_nets $I2 $I2\n"
"match_nets $I1 $I1\n"
"match_nets BULK BULK\n"
"match_nets $I6 $I6\n"
"match_nets $I5 $I5\n"
"match_nets $I4 $I4\n"
"match_nets $I3 $I3\n"
"match_nets $I7 $I7\n"
"match_nets $I4 $I4\n"
"match_nets $I5 $I5\n"
"match_nets $I1 $I1\n"
"match_nets $I8 $I8\n"
"match_nets $I6 $I6\n"
"match_nets BULK BULK\n"
"match_pins BULK BULK\n"
"match_pins $1 $1\n"
"match_pins $2 $2\n"
@ -2037,17 +2462,17 @@ TEST(16_UniqueSubCircuitMatching)
"end_circuit INV2PAIR INV2PAIR MATCH\n"
"begin_circuit RINGO RINGO\n"
"match_nets OSC OSC\n"
"match_nets BULK,VSS BULK,VSS\n"
"match_nets FB FB\n"
"match_nets $I22 $I22\n"
"match_nets VDD VDD\n"
"match_nets FB FB\n"
"match_nets $I13 $I13\n"
"match_nets $I23 $I23\n"
"match_nets $I5 $I5\n"
"match_nets $I24 $I24\n"
"match_nets $I6 $I6\n"
"match_nets $I7 $I7\n"
"match_nets BULK,VSS BULK,VSS\n"
"match_nets $I25 $I25\n"
"match_nets $I24 $I24\n"
"match_nets $I23 $I23\n"
"match_nets $I22 $I22\n"
"match_subcircuits $1 $1\n"
"match_subcircuits $4 $2\n"
"match_subcircuits $3 $3\n"
@ -2125,28 +2550,28 @@ TEST(17_InherentlyAmbiguousDecoder)
"begin_circuit DECODER DECODER\n"
"match_nets VSS VSS\n"
"match_nets VDD VDD\n"
"match_ambiguous_nets A B\n"
"match_ambiguous_nets B A\n"
"match_nets NB NA\n"
"match_nets NA NB\n"
"match_nets NQ0 NQ0\n"
"match_nets NQ2 NQ1\n"
"match_nets NQ1 NQ2\n"
"match_ambiguous_nets NQ1 NQ1\n"
"match_ambiguous_nets NQ2 NQ2\n"
"match_nets NQ3 NQ3\n"
"match_pins $0 $0\n"
"match_pins $1 $1\n"
"match_ambiguous_nets NA NA\n"
"match_ambiguous_nets NB NB\n"
"match_nets B B\n"
"match_nets A A\n"
"match_pins $0 $1\n"
"match_pins $1 $0\n"
"match_pins $2 $2\n"
"match_pins $3 $4\n"
"match_pins $4 $3\n"
"match_pins $3 $3\n"
"match_pins $4 $4\n"
"match_pins $5 $5\n"
"match_pins $6 $6\n"
"match_pins $7 $7\n"
"match_subcircuits $2 $1\n"
"match_subcircuits $1 $2\n"
"match_subcircuits $5 $3\n"
"match_subcircuits $1 $1\n"
"match_subcircuits $2 $2\n"
"match_subcircuits $4 $3\n"
"match_subcircuits $6 $4\n"
"match_subcircuits $3 $5\n"
"match_subcircuits $4 $6\n"
"match_subcircuits $5 $6\n"
"end_circuit DECODER DECODER MATCH"
);
@ -2175,11 +2600,11 @@ TEST(17_InherentlyAmbiguousDecoder)
"match_devices $4 $4\n"
"end_circuit NAND NAND MATCH\n"
"begin_circuit DECODER DECODER\n"
"match_nets NB NB\n"
"match_nets B B\n"
"match_nets NA NA\n"
"match_nets VDD VDD\n"
"match_nets VSS VSS\n"
"match_nets VDD VDD\n"
"match_nets B B\n"
"match_nets NB NB\n"
"match_nets NA NA\n"
"match_nets NQ0 NQ0\n"
"match_nets NQ2 NQ2\n"
"match_nets NQ1 NQ1\n"
@ -2261,7 +2686,13 @@ TEST(18_ClockTree)
bool good = comp.compare (&nl1, &nl2);
EXPECT_EQ (logger.text (),
std::string txt = logger.text ();
// because L/R matching is ambiguous, we need to do this to
// establish reproducability on different platforms:
txt = tl::replaced (txt, "L", "X");
txt = tl::replaced (txt, "R", "X");
EXPECT_EQ (txt,
"begin_circuit INV INV\n"
"match_nets VDD VDD\n"
"match_nets OUT OUT\n"
@ -2274,41 +2705,41 @@ TEST(18_ClockTree)
"match_devices $1 $1\n"
"match_devices $2 $2\n"
"end_circuit INV INV MATCH\n"
"begin_circuit TREE TREE\n"
"begin_circuit TXEE TXEE\n"
"match_nets IN IN\n"
"match_nets S S\n"
"match_nets VDD VDD\n"
"match_nets VSS VSS\n"
"match_ambiguous_nets SL SR\n"
"match_ambiguous_nets SR SL\n"
"match_ambiguous_nets SLL SRL\n"
"match_ambiguous_nets SLR SRR\n"
"match_ambiguous_nets SLLL SRLL\n"
"match_ambiguous_nets SLLR SRLR\n"
"match_ambiguous_nets SLRL SRRL\n"
"match_ambiguous_nets SLRR SRRR\n"
"match_ambiguous_nets SRL SLL\n"
"match_ambiguous_nets SRR SLR\n"
"match_ambiguous_nets SRLL SLLR\n"
"match_ambiguous_nets SRLR SLLL\n"
"match_ambiguous_nets SRRL SLRR\n"
"match_ambiguous_nets SRRR SLRL\n"
"match_subcircuits TRRL TLRR\n"
"match_subcircuits TL TR\n"
"match_subcircuits TLRL TRRL\n"
"match_subcircuits TLLR TRLR\n"
"match_subcircuits TRR TLR\n"
"match_subcircuits TRL TLL\n"
"match_subcircuits TLRR TRRR\n"
"match_subcircuits TRLR TLLL\n"
"match_subcircuits TRRR TLRL\n"
"match_nets VDD VDD\n"
"match_nets S S\n"
"match_ambiguous_nets SX SX\n"
"match_ambiguous_nets SX SX\n"
"match_ambiguous_nets SXX SXX\n"
"match_ambiguous_nets SXX SXX\n"
"match_ambiguous_nets SXXX SXXX\n"
"match_ambiguous_nets SXXX SXXX\n"
"match_ambiguous_nets SXXX SXXX\n"
"match_ambiguous_nets SXXX SXXX\n"
"match_ambiguous_nets SXX SXX\n"
"match_ambiguous_nets SXX SXX\n"
"match_ambiguous_nets SXXX SXXX\n"
"match_ambiguous_nets SXXX SXXX\n"
"match_ambiguous_nets SXXX SXXX\n"
"match_ambiguous_nets SXXX SXXX\n"
"match_subcircuits TXXX TXXX\n"
"match_subcircuits TX TX\n"
"match_subcircuits TXXX TXXX\n"
"match_subcircuits TXXX TXXX\n"
"match_subcircuits TXX TXX\n"
"match_subcircuits TXX TXX\n"
"match_subcircuits TXXX TXXX\n"
"match_subcircuits TXXX TXXX\n"
"match_subcircuits TXXX TXXX\n"
"match_subcircuits T T\n"
"match_subcircuits TRLL TLLR\n"
"match_subcircuits TR TL\n"
"match_subcircuits TLR TRR\n"
"match_subcircuits TLLL TRLL\n"
"match_subcircuits TLL TRL\n"
"end_circuit TREE TREE MATCH"
"match_subcircuits TXXX TXXX\n"
"match_subcircuits TX TX\n"
"match_subcircuits TXX TXX\n"
"match_subcircuits TXXX TXXX\n"
"match_subcircuits TXX TXX\n"
"end_circuit TXEE TXEE MATCH"
);
EXPECT_EQ (good, true);
}

File diff suppressed because it is too large Load Diff

View File

@ -58,7 +58,7 @@ static unsigned int define_layer (db::Layout &ly, db::LayerMap &lmap, int gds_la
return lid;
}
static void dump_nets_to_layout (const db::Netlist &nl, const db::hier_clusters<db::PolygonRef> &clusters, db::Layout &ly, const std::map<unsigned int, unsigned int> &lmap, const db::CellMapping &cmap)
static void dump_nets_to_layout (const db::Netlist &nl, const db::hier_clusters<db::PolygonRef> &clusters, db::Layout &ly, const std::map<unsigned int, unsigned int> &lmap, const db::CellMapping &cmap, bool with_device_cells = false)
{
std::set<db::cell_index_type> device_cells_seen;
@ -75,7 +75,7 @@ static void dump_nets_to_layout (const db::Netlist &nl, const db::hier_clusters<
any_shapes = ! lc.begin (m->first).at_end ();
}
if (any_shapes) {
if (any_shapes || (with_device_cells && n->terminal_count() > 0)) {
std::string nn = "NET_" + c->name () + "_" + n->expanded_name ();
db::Cell &net_cell = ly.cell (ly.add_cell (nn.c_str ()));
@ -88,40 +88,63 @@ static void dump_nets_to_layout (const db::Netlist &nl, const db::hier_clusters<
}
}
if (with_device_cells) {
for (db::Net::const_terminal_iterator t = n->begin_terminals (); t != n->end_terminals (); ++t) {
const db::NetTerminalRef &tref = *t;
db::cell_index_type dci = tref.device ()->device_abstract ()->cell_index ();
db::DCplxTrans dtr = tref.device ()->trans ();
net_cell.insert (db::CellInstArray (db::CellInst (dci), db::CplxTrans (ly.dbu ()).inverted () * dtr * db::CplxTrans (ly.dbu ())));
for (std::vector<db::DeviceAbstractRef>::const_iterator a = tref.device ()->other_abstracts ().begin (); a != tref.device ()->other_abstracts ().end (); ++a) {
const db::DeviceAbstractRef &aref = *a;
dci = aref.device_abstract->cell_index ();
net_cell.insert (db::CellInstArray (db::CellInst (dci), db::CplxTrans (ly.dbu ()).inverted () * dtr * aref.trans * db::CplxTrans (ly.dbu ())));
}
}
}
}
}
for (db::Circuit::const_device_iterator d = c->begin_devices (); d != c->end_devices (); ++d) {
std::vector<db::cell_index_type> original_device_cells;
db::cell_index_type dci = d->device_abstract ()->cell_index ();
if (device_cells_seen.find (dci) != device_cells_seen.end ()) {
continue;
if (device_cells_seen.find (dci) == device_cells_seen.end ()) {
original_device_cells.push_back (dci);
device_cells_seen.insert (dci);
}
db::Cell &device_cell = ly.cell (cmap.cell_mapping (dci));
device_cells_seen.insert (dci);
std::string ps;
const std::vector<db::DeviceParameterDefinition> &pd = d->device_class ()->parameter_definitions ();
for (std::vector<db::DeviceParameterDefinition>::const_iterator i = pd.begin (); i != pd.end (); ++i) {
if (! ps.empty ()) {
ps += ",";
for (std::vector<db::DeviceAbstractRef>::const_iterator a = d->other_abstracts ().begin (); a != d->other_abstracts ().end (); ++a) {
db::cell_index_type dci = a->device_abstract->cell_index ();
if (device_cells_seen.find (dci) == device_cells_seen.end ()) {
original_device_cells.push_back (dci);
device_cells_seen.insert (dci);
}
ps += i->name () + "=" + tl::to_string (d->parameter_value (i->id ()));
}
const std::vector<db::DeviceTerminalDefinition> &td = d->device_class ()->terminal_definitions ();
for (std::vector<db::DeviceTerminalDefinition>::const_iterator t = td.begin (); t != td.end (); ++t) {
for (std::vector<db::cell_index_type>::const_iterator i = original_device_cells.begin (); i != original_device_cells.end (); ++i) {
const db::local_cluster<db::PolygonRef> &dc = clusters.clusters_per_cell (dci).cluster_by_id (d->device_abstract ()->cluster_id_for_terminal (t->id ()));
db::cell_index_type dci = *i;
db::Cell &device_cell = ly.cell (cmap.cell_mapping (dci));
for (std::map<unsigned int, unsigned int>::const_iterator m = lmap.begin (); m != lmap.end (); ++m) {
db::Shapes &target = device_cell.shapes (m->second);
for (db::local_cluster<db::PolygonRef>::shape_iterator s = dc.begin (m->first); !s.at_end (); ++s) {
target.insert (*s);
const std::vector<db::DeviceTerminalDefinition> &td = d->device_class ()->terminal_definitions ();
for (std::vector<db::DeviceTerminalDefinition>::const_iterator t = td.begin (); t != td.end (); ++t) {
const db::local_cluster<db::PolygonRef> &dc = clusters.clusters_per_cell (dci).cluster_by_id (d->device_abstract ()->cluster_id_for_terminal (t->id ()));
for (std::map<unsigned int, unsigned int>::const_iterator m = lmap.begin (); m != lmap.end (); ++m) {
db::Shapes &target = device_cell.shapes (m->second);
for (db::local_cluster<db::PolygonRef>::shape_iterator s = dc.begin (m->first); !s.at_end (); ++s) {
target.insert (*s);
}
}
}
}
@ -823,3 +846,885 @@ TEST(3_DeviceAndNetExtractionWithImplicitConnections)
db::compare_layouts (_this, ly, au);
}
TEST(4_ResAndCapExtraction)
{
db::Layout ly;
db::LayerMap lmap;
unsigned int nwell = define_layer (ly, lmap, 1);
unsigned int active = define_layer (ly, lmap, 2);
unsigned int poly = define_layer (ly, lmap, 3);
unsigned int poly_lbl = define_layer (ly, lmap, 3, 1);
unsigned int diff_cont = define_layer (ly, lmap, 4);
unsigned int poly_cont = define_layer (ly, lmap, 5);
unsigned int metal1 = define_layer (ly, lmap, 6);
unsigned int metal1_lbl = define_layer (ly, lmap, 6, 1);
unsigned int via1 = define_layer (ly, lmap, 7);
unsigned int metal2 = define_layer (ly, lmap, 8);
unsigned int metal2_lbl = define_layer (ly, lmap, 8, 1);
unsigned int cap = define_layer (ly, lmap, 10);
unsigned int res = 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::testsrc ());
fn = tl::combine_path (fn, "testdata");
fn = tl::combine_path (fn, "algo");
fn = tl::combine_path (fn, "devices_test.oas");
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 ractive (db::RecursiveShapeIterator (ly, tc, active), dss);
db::Region rpoly_all (db::RecursiveShapeIterator (ly, tc, poly), dss);
db::Region rpoly_lbl (db::RecursiveShapeIterator (ly, tc, poly_lbl), dss);
db::Region rdiff_cont (db::RecursiveShapeIterator (ly, tc, diff_cont), dss);
db::Region rpoly_cont (db::RecursiveShapeIterator (ly, tc, poly_cont), dss);
db::Region rmetal1 (db::RecursiveShapeIterator (ly, tc, metal1), dss);
db::Region rmetal1_lbl (db::RecursiveShapeIterator (ly, tc, metal1_lbl), dss);
db::Region rvia1 (db::RecursiveShapeIterator (ly, tc, via1), dss);
db::Region rmetal2 (db::RecursiveShapeIterator (ly, tc, metal2), dss);
db::Region rmetal2_lbl (db::RecursiveShapeIterator (ly, tc, metal2_lbl), dss);
db::Region rcap (db::RecursiveShapeIterator (ly, tc, cap), dss);
db::Region rres (db::RecursiveShapeIterator (ly, tc, res), dss);
// derived regions
db::Region rpoly = rpoly_all - rres;
db::Region rpoly_res = rpoly_all & rres;
db::Region rpactive = ractive & rnwell;
db::Region rpgate = rpactive & rpoly;
db::Region rpsd = rpactive - rpgate;
db::Region rnactive = ractive - rnwell;
db::Region rngate = rnactive & rpoly;
db::Region rnsd = rnactive - rngate;
db::Region rcap1 = rmetal1 & rcap;
db::Region rcap2 = rmetal2 & rcap;
// return the computed layers into the original layout and write it for debugging purposes
unsigned int lgate = ly.insert_layer (db::LayerProperties (10, 0)); // 10/0 -> Gate
unsigned int lsd = ly.insert_layer (db::LayerProperties (11, 0)); // 11/0 -> Source/Drain
unsigned int lpdiff = ly.insert_layer (db::LayerProperties (12, 0)); // 12/0 -> P Diffusion
unsigned int lndiff = ly.insert_layer (db::LayerProperties (13, 0)); // 13/0 -> N Diffusion
unsigned int lpoly_res = ly.insert_layer (db::LayerProperties (14, 0)); // 14/0 -> Resistor Poly
unsigned int lcap1 = ly.insert_layer (db::LayerProperties (15, 0)); // 15/0 -> Cap 1
unsigned int lcap2 = ly.insert_layer (db::LayerProperties (16, 0)); // 16/0 -> Cap 2
rpgate.insert_into (&ly, tc.cell_index (), lgate);
rngate.insert_into (&ly, tc.cell_index (), lgate);
rpsd.insert_into (&ly, tc.cell_index (), lsd);
rnsd.insert_into (&ly, tc.cell_index (), lsd);
rpsd.insert_into (&ly, tc.cell_index (), lpdiff);
rnsd.insert_into (&ly, tc.cell_index (), lndiff);
rpoly_res.insert_into (&ly, tc.cell_index (), lpoly_res);
rcap1.insert_into (&ly, tc.cell_index (), lcap1);
rcap2.insert_into (&ly, tc.cell_index (), lcap2);
// perform the extraction
db::Netlist nl;
db::hier_clusters<db::PolygonRef> cl;
db::NetlistDeviceExtractorMOS3Transistor pmos_ex ("PMOS");
db::NetlistDeviceExtractorMOS3Transistor nmos_ex ("NMOS");
db::NetlistDeviceExtractorResistor res_ex ("POLY_RES", 50.0);
db::NetlistDeviceExtractorCapacitor cap_ex ("MIM_CAP", 1.0e-15);
db::NetlistDeviceExtractor::input_layers dl;
dl["SD"] = &rpsd;
dl["G"] = &rpgate;
// terminal patches
dl["tG"] = &rpoly;
dl["tS"] = &rpsd;
dl["tD"] = &rpsd;
pmos_ex.extract (dss, 0, dl, nl, cl);
dl.clear ();
dl["SD"] = &rnsd;
dl["G"] = &rngate;
// terminal patches
dl["tG"] = &rpoly;
dl["tS"] = &rnsd;
dl["tD"] = &rnsd;
nmos_ex.extract (dss, 0, dl, nl, cl);
dl.clear ();
dl["R"] = &rpoly_res;
dl["C"] = &rpoly;
// terminal patches
dl["tA"] = &rpoly;
dl["tB"] = &rpoly;
res_ex.extract (dss, 0, dl, nl, cl);
dl.clear ();
dl["P1"] = &rcap1;
dl["P2"] = &rcap2;
// terminal patches
dl["tA"] = &rmetal1;
dl["tB"] = &rmetal2;
cap_ex.extract (dss, 0, dl, nl, cl);
// perform the net extraction
db::NetlistExtractor net_ex;
db::Connectivity conn;
// Intra-layer
conn.connect (rpsd);
conn.connect (rnsd);
conn.connect (rpoly);
conn.connect (rdiff_cont);
conn.connect (rpoly_cont);
conn.connect (rmetal1);
conn.connect (rvia1);
conn.connect (rmetal2);
// Inter-layer
conn.connect (rpsd, rdiff_cont);
conn.connect (rnsd, rdiff_cont);
conn.connect (rpoly, rpoly_cont);
conn.connect (rpoly_cont, rmetal1);
conn.connect (rdiff_cont, rmetal1);
conn.connect (rmetal1, rvia1);
conn.connect (rvia1, rmetal2);
conn.connect (rpoly, rpoly_lbl); // attaches labels
conn.connect (rmetal1, rmetal1_lbl); // attaches labels
conn.connect (rmetal2, rmetal2_lbl); // attaches labels
// extract the nets
net_ex.extract_nets (dss, 0, conn, nl, cl, "*");
// Flatten device circuits
std::vector<std::string> circuits_to_flatten;
circuits_to_flatten.push_back ("RES");
circuits_to_flatten.push_back ("RES_MEANDER");
circuits_to_flatten.push_back ("TRANS");
circuits_to_flatten.push_back ("TRANS2");
for (std::vector<std::string>::const_iterator i = circuits_to_flatten.begin (); i != circuits_to_flatten.end (); ++i) {
db::Circuit *c = nl.circuit_by_name (*i);
tl_assert (c != 0);
nl.flatten_circuit (c);
}
// cleanup + completion
nl.combine_devices ();
nl.make_top_level_pins ();
nl.purge ();
EXPECT_EQ (all_net_names_unique (nl), true);
// debug layers produced for nets
// 202/0 -> Active
// 203/0 -> Poly
// 204/0 -> Diffusion contacts
// 205/0 -> Poly contacts
// 206/0 -> Metal1
// 207/0 -> Via1
// 208/0 -> Metal2
// 210/0 -> N source/drain
// 211/0 -> P source/drain
std::map<unsigned int, unsigned int> dump_map;
dump_map [layer_of (rpsd) ] = ly.insert_layer (db::LayerProperties (210, 0));
dump_map [layer_of (rnsd) ] = ly.insert_layer (db::LayerProperties (211, 0));
dump_map [layer_of (rpoly) ] = ly.insert_layer (db::LayerProperties (203, 0));
dump_map [layer_of (rdiff_cont)] = ly.insert_layer (db::LayerProperties (204, 0));
dump_map [layer_of (rpoly_cont)] = 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 /*with device cells*/);
// compare netlist as string
CHECKPOINT ();
db::compare_netlist (_this, nl,
"circuit TOP (VSS=VSS,IN=IN,OUT=OUT,VDD=VDD);\n"
" device PMOS $1 (S=VDD,G=$4,D=OUT) (L=0.4,W=2.3,AS=1.38,AD=1.38,PS=5.8,PD=5.8);\n"
" device PMOS $2 (S=VDD,G=IN,D=$3) (L=0.4,W=2.3,AS=1.38,AD=1.38,PS=5.8,PD=5.8);\n"
" device NMOS $3 (S=VSS,G=$4,D=OUT) (L=0.4,W=4.6,AS=2.185,AD=2.185,PS=8.8,PD=8.8);\n"
" device MIM_CAP $5 (A=$4,B=VSS) (C=2.622e-14,A=26.22,P=29.8);\n"
" device POLY_RES $7 (A=$3,B=$4) (R=750,A=2.4,P=13.6);\n"
" device POLY_RES $9 (A=$4,B=VSS) (R=1825,A=5.84,P=30);\n"
" device NMOS $10 (S=VSS,G=IN,D=$3) (L=0.4,W=3.1,AS=1.86,AD=1.86,PS=7.4,PD=7.4);\n"
"end;\n"
);
// compare the collected test data
std::string au = tl::testsrc ();
au = tl::combine_path (au, "testdata");
au = tl::combine_path (au, "algo");
au = tl::combine_path (au, "device_extract_capres_nets.gds");
db::compare_layouts (_this, ly, au);
}
TEST(5_ResAndCapWithBulkExtraction)
{
db::Layout ly;
db::LayerMap lmap;
unsigned int nwell = define_layer (ly, lmap, 1);
unsigned int active = define_layer (ly, lmap, 2);
unsigned int poly = define_layer (ly, lmap, 3);
unsigned int poly_lbl = define_layer (ly, lmap, 3, 1);
unsigned int diff_cont = define_layer (ly, lmap, 4);
unsigned int poly_cont = define_layer (ly, lmap, 5);
unsigned int metal1 = define_layer (ly, lmap, 6);
unsigned int metal1_lbl = define_layer (ly, lmap, 6, 1);
unsigned int via1 = define_layer (ly, lmap, 7);
unsigned int metal2 = define_layer (ly, lmap, 8);
unsigned int metal2_lbl = define_layer (ly, lmap, 8, 1);
unsigned int cap = define_layer (ly, lmap, 10);
unsigned int res = 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::testsrc ());
fn = tl::combine_path (fn, "testdata");
fn = tl::combine_path (fn, "algo");
fn = tl::combine_path (fn, "devices_with_bulk_test.oas");
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 ractive (db::RecursiveShapeIterator (ly, tc, active), dss);
db::Region rpoly_all (db::RecursiveShapeIterator (ly, tc, poly), dss);
db::Region rpoly_lbl (db::RecursiveShapeIterator (ly, tc, poly_lbl), dss);
db::Region rdiff_cont (db::RecursiveShapeIterator (ly, tc, diff_cont), dss);
db::Region rpoly_cont (db::RecursiveShapeIterator (ly, tc, poly_cont), dss);
db::Region rmetal1 (db::RecursiveShapeIterator (ly, tc, metal1), dss);
db::Region rmetal1_lbl (db::RecursiveShapeIterator (ly, tc, metal1_lbl), dss);
db::Region rvia1 (db::RecursiveShapeIterator (ly, tc, via1), dss);
db::Region rmetal2 (db::RecursiveShapeIterator (ly, tc, metal2), dss);
db::Region rmetal2_lbl (db::RecursiveShapeIterator (ly, tc, metal2_lbl), dss);
db::Region rcap (db::RecursiveShapeIterator (ly, tc, cap), dss);
db::Region rres (db::RecursiveShapeIterator (ly, tc, res), dss);
db::Region rbulk (dss);
// derived regions
db::Region rpoly = rpoly_all - rres;
db::Region rpoly_res = rpoly_all & rres;
db::Region rpoly_res_nw = rpoly_res & rnwell;
db::Region rpoly_res_sub = rpoly_res - rnwell;
db::Region rpactive = ractive & rnwell;
db::Region rpgate = rpactive & rpoly;
db::Region rpsd = rpactive - rpgate;
db::Region rnactive = ractive - rnwell;
db::Region rngate = rnactive & rpoly;
db::Region rnsd = rnactive - rngate;
db::Region rcap1 = rmetal1 & rcap;
db::Region rcap2 = rmetal2 & rcap;
db::Region rcap1_nw = rcap1 & rnwell;
db::Region rcap2_nw = rcap2 & rnwell;
db::Region rcap1_sub = rcap1 - rnwell;
db::Region rcap2_sub = rcap2 - rnwell;
// return the computed layers into the original layout and write it for debugging purposes
unsigned int lgate = ly.insert_layer (db::LayerProperties (10, 0)); // 10/0 -> Gate
unsigned int lsd = ly.insert_layer (db::LayerProperties (11, 0)); // 11/0 -> Source/Drain
unsigned int lpdiff = ly.insert_layer (db::LayerProperties (12, 0)); // 12/0 -> P Diffusion
unsigned int lndiff = ly.insert_layer (db::LayerProperties (13, 0)); // 13/0 -> N Diffusion
unsigned int lpoly_res = ly.insert_layer (db::LayerProperties (14, 0)); // 14/0 -> Resistor Poly
unsigned int lcap1 = ly.insert_layer (db::LayerProperties (15, 0)); // 15/0 -> Cap 1
unsigned int lcap2 = ly.insert_layer (db::LayerProperties (16, 0)); // 16/0 -> Cap 2
rpgate.insert_into (&ly, tc.cell_index (), lgate);
rngate.insert_into (&ly, tc.cell_index (), lgate);
rpsd.insert_into (&ly, tc.cell_index (), lsd);
rnsd.insert_into (&ly, tc.cell_index (), lsd);
rpsd.insert_into (&ly, tc.cell_index (), lpdiff);
rnsd.insert_into (&ly, tc.cell_index (), lndiff);
rpoly_res.insert_into (&ly, tc.cell_index (), lpoly_res);
rcap1.insert_into (&ly, tc.cell_index (), lcap1);
rcap2.insert_into (&ly, tc.cell_index (), lcap2);
// perform the extraction
db::Netlist nl;
db::hier_clusters<db::PolygonRef> cl;
db::NetlistDeviceExtractorMOS4Transistor pmos_ex ("PMOS");
db::NetlistDeviceExtractorMOS4Transistor nmos_ex ("NMOS");
db::NetlistDeviceExtractorResistorWithBulk res_substrate_ex ("POLY_RES_SUBSTRATE", 50.0);
db::NetlistDeviceExtractorResistorWithBulk res_nwell_ex ("POLY_RES_NWELL", 50.0);
db::NetlistDeviceExtractorCapacitorWithBulk cap_substrate_ex ("MIM_CAP_SUBSTRATE", 1.0e-15);
db::NetlistDeviceExtractorCapacitorWithBulk cap_nwell_ex ("MIM_CAP_NWELL", 1.0e-15);
db::NetlistDeviceExtractor::input_layers dl;
dl["SD"] = &rpsd;
dl["G"] = &rpgate;
dl["W"] = &rnwell;
// terminal patches
dl["tG"] = &rpoly;
dl["tS"] = &rpsd;
dl["tD"] = &rpsd;
pmos_ex.extract (dss, 0, dl, nl, cl);
dl.clear ();
dl["SD"] = &rnsd;
dl["G"] = &rngate;
dl["W"] = &rbulk;
// terminal patches
dl["tG"] = &rpoly;
dl["tS"] = &rnsd;
dl["tD"] = &rnsd;
nmos_ex.extract (dss, 0, dl, nl, cl);
dl.clear ();
dl["R"] = &rpoly_res_sub;
dl["C"] = &rpoly;
dl["W"] = &rbulk;
// terminal patches
dl["tA"] = &rpoly;
dl["tB"] = &rpoly;
res_substrate_ex.extract (dss, 0, dl, nl, cl);
dl.clear ();
dl["R"] = &rpoly_res_nw;
dl["C"] = &rpoly;
dl["W"] = &rnwell;
// terminal patches
dl["tA"] = &rpoly;
dl["tB"] = &rpoly;
res_nwell_ex.extract (dss, 0, dl, nl, cl);
dl.clear ();
dl["P1"] = &rcap1_sub;
dl["P2"] = &rcap2_sub;
dl["W"] = &rbulk;
// terminal patches
dl["tA"] = &rmetal1;
dl["tB"] = &rmetal2;
cap_substrate_ex.extract (dss, 0, dl, nl, cl);
dl.clear ();
dl["P1"] = &rcap1_nw;
dl["P2"] = &rcap2_nw;
dl["W"] = &rnwell;
// terminal patches
dl["tA"] = &rmetal1;
dl["tB"] = &rmetal2;
cap_nwell_ex.extract (dss, 0, dl, nl, cl);
// perform the net extraction
db::NetlistExtractor net_ex;
db::Connectivity conn;
// Intra-layer
conn.connect (rpsd);
conn.connect (rnsd);
conn.connect (rpoly);
conn.connect (rdiff_cont);
conn.connect (rpoly_cont);
conn.connect (rmetal1);
conn.connect (rvia1);
conn.connect (rmetal2);
// Inter-layer
conn.connect (rpsd, rdiff_cont);
conn.connect (rnsd, rdiff_cont);
conn.connect (rpoly, rpoly_cont);
conn.connect (rpoly_cont, rmetal1);
conn.connect (rdiff_cont, rmetal1);
conn.connect (rmetal1, rvia1);
conn.connect (rvia1, rmetal2);
conn.connect (rpoly, rpoly_lbl); // attaches labels
conn.connect (rmetal1, rmetal1_lbl); // attaches labels
conn.connect (rmetal2, rmetal2_lbl); // attaches labels
// Global nets
conn.connect_global (rbulk, "BULK");
conn.connect_global (rnwell, "NWELL");
// extract the nets
net_ex.extract_nets (dss, 0, conn, nl, cl, "*");
// Flatten device circuits
std::vector<std::string> circuits_to_flatten;
circuits_to_flatten.push_back ("RES");
circuits_to_flatten.push_back ("RES_MEANDER");
circuits_to_flatten.push_back ("TRANS");
circuits_to_flatten.push_back ("TRANS2");
for (std::vector<std::string>::const_iterator i = circuits_to_flatten.begin (); i != circuits_to_flatten.end (); ++i) {
db::Circuit *c = nl.circuit_by_name (*i);
tl_assert (c != 0);
nl.flatten_circuit (c);
}
// cleanup + completion
nl.combine_devices ();
nl.make_top_level_pins ();
nl.purge ();
EXPECT_EQ (all_net_names_unique (nl), true);
// debug layers produced for nets
// 202/0 -> Active
// 203/0 -> Poly
// 204/0 -> Diffusion contacts
// 205/0 -> Poly contacts
// 206/0 -> Metal1
// 207/0 -> Via1
// 208/0 -> Metal2
// 210/0 -> N source/drain
// 211/0 -> P source/drain
std::map<unsigned int, unsigned int> dump_map;
dump_map [layer_of (rpsd) ] = ly.insert_layer (db::LayerProperties (210, 0));
dump_map [layer_of (rnsd) ] = ly.insert_layer (db::LayerProperties (211, 0));
dump_map [layer_of (rpoly) ] = ly.insert_layer (db::LayerProperties (203, 0));
dump_map [layer_of (rdiff_cont)] = ly.insert_layer (db::LayerProperties (204, 0));
dump_map [layer_of (rpoly_cont)] = 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 /*with device cells*/);
// compare netlist as string
CHECKPOINT ();
db::compare_netlist (_this, nl,
"circuit TOP (VSS=VSS,IN=IN,OUT=OUT,VDD=VDD,BULK=BULK,NWELL=NWELL);\n"
" device PMOS $1 (S=VDD,G=$4,D=OUT,B=NWELL) (L=0.4,W=2.3,AS=1.38,AD=1.38,PS=5.8,PD=5.8);\n"
" device PMOS $2 (S=VDD,G=IN,D=$3,B=NWELL) (L=0.4,W=2.3,AS=1.38,AD=1.38,PS=5.8,PD=5.8);\n"
" device NMOS $3 (S=VSS,G=$4,D=OUT,B=BULK) (L=0.4,W=4.6,AS=2.185,AD=2.185,PS=8.8,PD=8.8);\n"
" device MIM_CAP_SUBSTRATE $5 (A=$4,B=VSS,W=BULK) (C=1.334e-14,A=13.34,P=15);\n"
" device MIM_CAP_NWELL $6 (A=$4,B=VSS,W=NWELL) (C=1.288e-14,A=12.88,P=14.8);\n"
" device POLY_RES_NWELL $7 (A=$3,B=$4,W=NWELL) (R=750,A=2.4,P=13.6);\n"
" device POLY_RES_SUBSTRATE $9 (A=$4,B=VSS,W=BULK) (R=1825,A=5.84,P=30);\n"
" device NMOS $10 (S=VSS,G=IN,D=$3,B=BULK) (L=0.4,W=3.1,AS=1.86,AD=1.86,PS=7.4,PD=7.4);\n"
"end;\n"
);
// compare the collected test data
std::string au = tl::testsrc ();
au = tl::combine_path (au, "testdata");
au = tl::combine_path (au, "algo");
au = tl::combine_path (au, "device_extract_capres_with_bulk_nets.gds");
db::compare_layouts (_this, ly, au);
}
TEST(6_BJT3TransistorExtraction)
{
db::Layout ly;
db::LayerMap lmap;
unsigned int nwell = define_layer (ly, lmap, 1);
unsigned int active = define_layer (ly, lmap, 2);
unsigned int poly = define_layer (ly, lmap, 3);
unsigned int poly_lbl = define_layer (ly, lmap, 3, 1);
unsigned int diff_cont = define_layer (ly, lmap, 4);
unsigned int poly_cont = define_layer (ly, lmap, 5);
unsigned int metal1 = define_layer (ly, lmap, 6);
unsigned int metal1_lbl = define_layer (ly, lmap, 6, 1);
unsigned int via1 = define_layer (ly, lmap, 7);
unsigned int metal2 = define_layer (ly, lmap, 8);
unsigned int metal2_lbl = define_layer (ly, lmap, 8, 1);
unsigned int pplus = define_layer (ly, lmap, 9);
unsigned int nplus = define_layer (ly, lmap, 10);
{
db::LoadLayoutOptions options;
options.get_options<db::CommonReaderOptions> ().layer_map = lmap;
options.get_options<db::CommonReaderOptions> ().create_other_layers = false;
std::string fn (tl::testsrc ());
fn = tl::combine_path (fn, "testdata");
fn = tl::combine_path (fn, "algo");
fn = tl::combine_path (fn, "bipolar_devices_test.oas");
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 ractive (db::RecursiveShapeIterator (ly, tc, active), dss);
db::Region rpoly (db::RecursiveShapeIterator (ly, tc, poly), dss);
db::Region rpoly_lbl (db::RecursiveShapeIterator (ly, tc, poly_lbl), dss);
db::Region rdiff_cont (db::RecursiveShapeIterator (ly, tc, diff_cont), dss);
db::Region rpoly_cont (db::RecursiveShapeIterator (ly, tc, poly_cont), dss);
db::Region rmetal1 (db::RecursiveShapeIterator (ly, tc, metal1), dss);
db::Region rmetal1_lbl (db::RecursiveShapeIterator (ly, tc, metal1_lbl), dss);
db::Region rvia1 (db::RecursiveShapeIterator (ly, tc, via1), dss);
db::Region rmetal2 (db::RecursiveShapeIterator (ly, tc, metal2), dss);
db::Region rmetal2_lbl (db::RecursiveShapeIterator (ly, tc, metal2_lbl), dss);
db::Region rpplus (db::RecursiveShapeIterator (ly, tc, pplus), dss);
db::Region rnplus (db::RecursiveShapeIterator (ly, tc, nplus), dss);
db::Region rbulk (dss);
// derived regions
db::Region rpactive = ractive & rnwell;
db::Region rbase = rpactive.selected_not_interacting (rpoly).selected_interacting (rpplus);
db::Region rpactive_mos = rpactive - rbase;
db::Region rpgate = rpactive_mos & rpoly;
db::Region rpsd = rpactive_mos - rpgate;
db::Region rnactive = ractive - rnwell;
db::Region rngate = rnactive & rpoly;
db::Region rnsd = rnactive - rngate;
db::Region rntie = rnwell & rnplus;
db::Region remitter = rpplus & rbase;
// return the computed layers into the original layout and write it for debugging purposes
unsigned int lgate = ly.insert_layer (db::LayerProperties (10, 0)); // 10/0 -> Gate
unsigned int lsd = ly.insert_layer (db::LayerProperties (11, 0)); // 11/0 -> Source/Drain
unsigned int lpdiff = ly.insert_layer (db::LayerProperties (12, 0)); // 12/0 -> P Diffusion
unsigned int lndiff = ly.insert_layer (db::LayerProperties (13, 0)); // 13/0 -> N Diffusion
unsigned int lbase = ly.insert_layer (db::LayerProperties (14, 0)); // 14/0 -> Base
unsigned int lemitter = ly.insert_layer (db::LayerProperties (15, 0)); // 15/0 -> Base
unsigned int lntie = ly.insert_layer (db::LayerProperties (16, 0)); // 16/0 -> N Tiedown
rpgate.insert_into (&ly, tc.cell_index (), lgate);
rngate.insert_into (&ly, tc.cell_index (), lgate);
rpsd.insert_into (&ly, tc.cell_index (), lsd);
rnsd.insert_into (&ly, tc.cell_index (), lsd);
rpsd.insert_into (&ly, tc.cell_index (), lpdiff);
rnsd.insert_into (&ly, tc.cell_index (), lndiff);
rbase.insert_into (&ly, tc.cell_index (), lbase);
remitter.insert_into (&ly, tc.cell_index (), lemitter);
rntie.insert_into (&ly, tc.cell_index (), lntie);
// perform the extraction
db::Netlist nl;
db::hier_clusters<db::PolygonRef> cl;
db::NetlistDeviceExtractorMOS4Transistor pmos_ex ("PMOS");
db::NetlistDeviceExtractorMOS4Transistor nmos_ex ("NMOS");
db::NetlistDeviceExtractorBJT3Transistor bjt_ex ("PNP");
db::NetlistDeviceExtractor::input_layers dl;
dl["SD"] = &rpsd;
dl["G"] = &rpgate;
dl["W"] = &rnwell;
// terminal patches
dl["tG"] = &rpoly;
dl["tS"] = &rpsd;
dl["tD"] = &rpsd;
pmos_ex.extract (dss, 0, dl, nl, cl);
dl.clear ();
dl["SD"] = &rnsd;
dl["G"] = &rngate;
dl["W"] = &rbulk;
// terminal patches
dl["tG"] = &rpoly;
dl["tS"] = &rnsd;
dl["tD"] = &rnsd;
nmos_ex.extract (dss, 0, dl, nl, cl);
dl.clear ();
dl["E"] = &remitter;
dl["B"] = &rbase;
dl["C"] = &rbulk;
// terminal patches
dl["tB"] = &rnwell;
bjt_ex.extract (dss, 0, dl, nl, cl);
// perform the net extraction
db::NetlistExtractor net_ex;
db::Connectivity conn;
// Intra-layer
conn.connect (rnwell);
conn.connect (rpsd);
conn.connect (rnsd);
conn.connect (rbase);
conn.connect (remitter);
conn.connect (rntie);
conn.connect (rpoly);
conn.connect (rdiff_cont);
conn.connect (rpoly_cont);
conn.connect (rmetal1);
conn.connect (rvia1);
conn.connect (rmetal2);
// Inter-layer
conn.connect (rntie, rnwell);
conn.connect (rntie, rdiff_cont);
conn.connect (remitter, rdiff_cont);
conn.connect (rpsd, rdiff_cont);
conn.connect (rnsd, rdiff_cont);
conn.connect (rpoly, rpoly_cont);
conn.connect (rpoly_cont, rmetal1);
conn.connect (rdiff_cont, rmetal1);
conn.connect (rmetal1, rvia1);
conn.connect (rvia1, rmetal2);
conn.connect (rpoly, rpoly_lbl); // attaches labels
conn.connect (rmetal1, rmetal1_lbl); // attaches labels
conn.connect (rmetal2, rmetal2_lbl); // attaches labels
// Global nets
conn.connect_global (rbulk, "BULK");
// extract the nets
net_ex.extract_nets (dss, 0, conn, nl, cl, "*");
// Flatten device circuits
std::vector<std::string> circuits_to_flatten;
circuits_to_flatten.push_back ("TRANS");
circuits_to_flatten.push_back ("TRANS2");
for (std::vector<std::string>::const_iterator i = circuits_to_flatten.begin (); i != circuits_to_flatten.end (); ++i) {
db::Circuit *c = nl.circuit_by_name (*i);
tl_assert (c != 0);
nl.flatten_circuit (c);
}
// cleanup + completion
nl.combine_devices ();
nl.make_top_level_pins ();
nl.purge ();
EXPECT_EQ (all_net_names_unique (nl), true);
// debug layers produced for nets
// 201/0 -> n well
// 203/0 -> Poly
// 204/0 -> Diffusion contacts
// 205/0 -> Poly contacts
// 206/0 -> Metal1
// 207/0 -> Via1
// 208/0 -> Metal2
// 210/0 -> N source/drain
// 211/0 -> P source/drain
// 212/0 -> Emitter
// 213/0 -> N tiedown
std::map<unsigned int, unsigned int> dump_map;
dump_map [layer_of (rpsd) ] = ly.insert_layer (db::LayerProperties (210, 0));
dump_map [layer_of (rnsd) ] = ly.insert_layer (db::LayerProperties (211, 0));
dump_map [layer_of (remitter) ] = ly.insert_layer (db::LayerProperties (212, 0));
dump_map [layer_of (rntie) ] = ly.insert_layer (db::LayerProperties (213, 0));
dump_map [layer_of (rnwell) ] = ly.insert_layer (db::LayerProperties (201, 0));
dump_map [layer_of (rpoly) ] = ly.insert_layer (db::LayerProperties (203, 0));
dump_map [layer_of (rdiff_cont)] = ly.insert_layer (db::LayerProperties (204, 0));
dump_map [layer_of (rpoly_cont)] = 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 /*with device cells*/);
// compare netlist as string
CHECKPOINT ();
db::compare_netlist (_this, nl,
"circuit TOP (VSS=VSS,IN=IN,OUT=OUT,VDD=VDD,BULK=BULK);\n"
" device PMOS $1 (S=$4,G=VSS,D=VDD,B=VDD) (L=0.4,W=2.3,AS=1.38,AD=1.38,PS=5.8,PD=5.8);\n"
" device PMOS $2 (S=VDD,G=$4,D=OUT,B=VDD) (L=0.4,W=2.3,AS=1.38,AD=1.38,PS=5.8,PD=5.8);\n"
" device PMOS $3 (S=VDD,G=IN,D=$3,B=VDD) (L=0.4,W=2.3,AS=1.38,AD=1.38,PS=5.8,PD=5.8);\n"
" device NMOS $4 (S=VSS,G=$4,D=OUT,B=BULK) (L=0.4,W=4.6,AS=2.185,AD=2.185,PS=8.8,PD=8.8);\n"
" device PNP $6 (C=BULK,B=$3,E=$3) (AE=3.06,PE=7,AB=25.2,PB=21.2,AC=25.2,PC=21.2,NE=1);\n"
" device PNP $7 (C=BULK,B=$3,E=$4) (AE=6.12,PE=14,AB=50.4,PB=42.4,AC=50.4,PC=42.4,NE=2);\n"
" device NMOS $9 (S=VSS,G=IN,D=$3,B=BULK) (L=0.4,W=3.1,AS=1.86,AD=1.86,PS=7.4,PD=7.4);\n"
"end;\n"
);
// compare the collected test data
std::string au = tl::testsrc ();
au = tl::combine_path (au, "testdata");
au = tl::combine_path (au, "algo");
au = tl::combine_path (au, "bipolar_devices_nets.gds");
db::compare_layouts (_this, ly, au);
}
TEST(7_DiodeExtraction)
{
db::Layout ly;
db::LayerMap lmap;
unsigned int nwell = define_layer (ly, lmap, 1);
unsigned int active = define_layer (ly, lmap, 2);
unsigned int diff_cont = define_layer (ly, lmap, 4);
unsigned int metal1 = define_layer (ly, lmap, 6);
unsigned int metal1_lbl = define_layer (ly, lmap, 6, 1);
unsigned int pplus = define_layer (ly, lmap, 9);
unsigned int nplus = define_layer (ly, lmap, 10);
{
db::LoadLayoutOptions options;
options.get_options<db::CommonReaderOptions> ().layer_map = lmap;
options.get_options<db::CommonReaderOptions> ().create_other_layers = false;
std::string fn (tl::testsrc ());
fn = tl::combine_path (fn, "testdata");
fn = tl::combine_path (fn, "algo");
fn = tl::combine_path (fn, "diode_devices_test.oas");
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 ractive (db::RecursiveShapeIterator (ly, tc, active), dss);
db::Region rdiff_cont (db::RecursiveShapeIterator (ly, tc, diff_cont), dss);
db::Region rmetal1 (db::RecursiveShapeIterator (ly, tc, metal1), dss);
db::Region rmetal1_lbl (db::RecursiveShapeIterator (ly, tc, metal1_lbl), dss);
db::Region rpplus (db::RecursiveShapeIterator (ly, tc, pplus), dss);
db::Region rnplus (db::RecursiveShapeIterator (ly, tc, nplus), dss);
// derived regions
db::Region rn = ractive & rnwell;
db::Region rntie = rnwell & rnplus;
// return the computed layers into the original layout and write it for debugging purposes
unsigned int ln = ly.insert_layer (db::LayerProperties (10, 0)); // 10/0 -> N layer
unsigned int lntie = ly.insert_layer (db::LayerProperties (11, 0)); // 11/0 -> N contact
rn.insert_into (&ly, tc.cell_index (), ln);
rntie.insert_into (&ly, tc.cell_index (), lntie);
// perform the extraction
db::Netlist nl;
db::hier_clusters<db::PolygonRef> cl;
db::NetlistDeviceExtractorDiode diode_ex ("DIODE");
db::NetlistDeviceExtractor::input_layers dl;
dl["N"] = &rn;
dl["P"] = &rpplus;
dl["tC"] = &rnwell;
diode_ex.extract (dss, 0, dl, nl, cl);
// perform the net extraction
db::NetlistExtractor net_ex;
db::Connectivity conn;
// Intra-layer
conn.connect (rnwell);
conn.connect (rntie);
conn.connect (rpplus);
conn.connect (rdiff_cont);
conn.connect (rmetal1);
// Inter-layer
conn.connect (rntie, rnwell);
conn.connect (rntie, rdiff_cont);
conn.connect (rpplus, rdiff_cont);
conn.connect (rdiff_cont, rmetal1);
conn.connect (rmetal1, rmetal1_lbl); // attaches labels
// extract the nets
net_ex.extract_nets (dss, 0, conn, nl, cl, "*");
// cleanup + completion
nl.combine_devices ();
nl.make_top_level_pins ();
nl.purge ();
EXPECT_EQ (all_net_names_unique (nl), true);
// debug layers produced for nets
// 201/0 -> n well
// 204/0 -> Diffusion contacts
// 206/0 -> Metal1
// 210/0 -> N tiedown
std::map<unsigned int, unsigned int> dump_map;
dump_map [layer_of (rntie) ] = ly.insert_layer (db::LayerProperties (210, 0));
dump_map [layer_of (rnwell) ] = ly.insert_layer (db::LayerProperties (201, 0));
dump_map [layer_of (rdiff_cont)] = ly.insert_layer (db::LayerProperties (204, 0));
dump_map [layer_of (rmetal1) ] = ly.insert_layer (db::LayerProperties (206, 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 /*with device cells*/);
// compare netlist as string
CHECKPOINT ();
db::compare_netlist (_this, nl,
"circuit TOP (A=A,C=C);\n"
" device DIODE $1 (A=A,C=C) (A=9.18,P=21);\n"
"end;\n"
);
// compare the collected test data
std::string au = tl::testsrc ();
au = tl::combine_path (au, "testdata");
au = tl::combine_path (au, "algo");
au = tl::combine_path (au, "diode_devices_nets.gds");
db::compare_layouts (_this, ly, au);
}

View File

@ -39,10 +39,10 @@ TEST(1_BasicReader)
reader.read (is, nl);
EXPECT_EQ (nl.to_string (),
"circuit TOP ();\n"
" device RES $1 (A='6',B='1') (R=7650);\n"
" device RES $2 (A='3',B='1') (R=7650);\n"
" device RES $3 (A='3',B='2') (R=2670);\n"
"circuit TOP ('1'='1','2'='2','4'='4','7'='7');\n"
" device RES $1 (A='6',B='1') (R=7650,L=0,W=0,A=0,P=0);\n"
" device RES $2 (A='3',B='1') (R=7650,L=0,W=0,A=0,P=0);\n"
" device RES $3 (A='3',B='2') (R=2670,L=0,W=0,A=0,P=0);\n"
" device MHVPMOS $4 (S='6',G='4',D='7',B='7') (L=0.25,W=1.5,AS=0.63,AD=0.63,PS=3.84,PD=3.84);\n"
"end;\n"
);
@ -59,27 +59,27 @@ TEST(2_ReaderWithSubcircuits)
reader.read (is, nl);
EXPECT_EQ (nl.to_string (),
"circuit RINGO ($1='11',$2='12',$3='13',$4='14',$5='15');\n"
" subcircuit ND2X1 $1 ($1='12',$2='1',$3='15',$4='12',$5='11',$6='14',$7='15');\n"
" subcircuit INVX1 $2 ($1='12',$2='2',$3='15',$4='12',$5='1',$6='15');\n"
" subcircuit INVX1 $3 ($1='12',$2='3',$3='15',$4='12',$5='2',$6='15');\n"
" subcircuit INVX1 $4 ($1='12',$2='4',$3='15',$4='12',$5='3',$6='15');\n"
" subcircuit INVX1 $5 ($1='12',$2='5',$3='15',$4='12',$5='4',$6='15');\n"
" subcircuit INVX1 $6 ($1='12',$2='6',$3='15',$4='12',$5='5',$6='15');\n"
" subcircuit INVX1 $7 ($1='12',$2='7',$3='15',$4='12',$5='6',$6='15');\n"
" subcircuit INVX1 $8 ($1='12',$2='8',$3='15',$4='12',$5='7',$6='15');\n"
" subcircuit INVX1 $9 ($1='12',$2='9',$3='15',$4='12',$5='8',$6='15');\n"
" subcircuit INVX1 $10 ($1='12',$2='10',$3='15',$4='12',$5='9',$6='15');\n"
" subcircuit INVX1 $11 ($1='12',$2='11',$3='15',$4='12',$5='10',$6='15');\n"
" subcircuit INVX1 $12 ($1='12',$2='13',$3='15',$4='12',$5='11',$6='15');\n"
"circuit RINGO ('11'='11','12'='12','13'='13','14'='14','15'='15');\n"
" subcircuit ND2X1 $1 ('1'='12','2'='1','3'='15','4'='12','5'='11','6'='14','7'='15');\n"
" subcircuit INVX1 $2 ('1'='12','2'='2','3'='15','4'='12','5'='1','6'='15');\n"
" subcircuit INVX1 $3 ('1'='12','2'='3','3'='15','4'='12','5'='2','6'='15');\n"
" subcircuit INVX1 $4 ('1'='12','2'='4','3'='15','4'='12','5'='3','6'='15');\n"
" subcircuit INVX1 $5 ('1'='12','2'='5','3'='15','4'='12','5'='4','6'='15');\n"
" subcircuit INVX1 $6 ('1'='12','2'='6','3'='15','4'='12','5'='5','6'='15');\n"
" subcircuit INVX1 $7 ('1'='12','2'='7','3'='15','4'='12','5'='6','6'='15');\n"
" subcircuit INVX1 $8 ('1'='12','2'='8','3'='15','4'='12','5'='7','6'='15');\n"
" subcircuit INVX1 $9 ('1'='12','2'='9','3'='15','4'='12','5'='8','6'='15');\n"
" subcircuit INVX1 $10 ('1'='12','2'='10','3'='15','4'='12','5'='9','6'='15');\n"
" subcircuit INVX1 $11 ('1'='12','2'='11','3'='15','4'='12','5'='10','6'='15');\n"
" subcircuit INVX1 $12 ('1'='12','2'='13','3'='15','4'='12','5'='11','6'='15');\n"
"end;\n"
"circuit ND2X1 ($1='1',$2='2',$3='3',$4='4',$5='5',$6='6',$7='7');\n"
"circuit ND2X1 ('1'='1','2'='2','3'='3','4'='4','5'='5','6'='6','7'='7');\n"
" device MLVPMOS $1 (S='2',G='6',D='1',B='4') (L=0.25,W=1.5,AS=0.6375,AD=0.3375,PS=3.85,PD=1.95);\n"
" device MLVPMOS $2 (S='1',G='5',D='2',B='4') (L=0.25,W=1.5,AS=0.3375,AD=0.6375,PS=1.95,PD=3.85);\n"
" device MLVNMOS $3 (S='3',G='6',D='8',B='7') (L=0.25,W=0.95,AS=0.40375,AD=0.21375,PS=2.75,PD=1.4);\n"
" device MLVNMOS $4 (S='8',G='5',D='2',B='7') (L=0.25,W=0.95,AS=0.21375,AD=0.40375,PS=1.4,PD=2.75);\n"
"end;\n"
"circuit INVX1 ($1='1',$2='2',$3='3',$4='4',$5='5',$6='6');\n"
"circuit INVX1 ('1'='1','2'='2','3'='3','4'='4','5'='5','6'='6');\n"
" device MLVPMOS $1 (S='1',G='5',D='2',B='4') (L=0.25,W=1.5,AS=0.6375,AD=0.6375,PS=3.85,PD=3.85);\n"
" device MLVNMOS $2 (S='3',G='5',D='2',B='6') (L=0.25,W=0.95,AS=0.40375,AD=0.40375,PS=2.75,PD=2.75);\n"
"end;\n"
@ -97,29 +97,161 @@ TEST(3_ReaderWithSubcircuitsAltOrder)
reader.read (is, nl);
EXPECT_EQ (nl.to_string (),
"circuit INVX1 ($1='1',$2='2',$3='3',$4='4',$5='5',$6='6');\n"
"circuit INVX1 ('1'='1','2'='2','3'='3','4'='4','5'='5','6'='6');\n"
" device MLVPMOS $1 (S='1',G='5',D='2',B='4') (L=0.25,W=1.5,AS=0,AD=0,PS=0,PD=0);\n"
" device MLVNMOS $2 (S='3',G='5',D='2',B='6') (L=0.25,W=0.95,AS=0,AD=0,PS=0,PD=0);\n"
"end;\n"
"circuit ND2X1 ($1='1',$2='2',$3='3',$4='4',$5='5',$6='6',$7='7');\n"
"circuit ND2X1 ('1'='1','2'='2','3'='3','4'='4','5'='5','6'='6','7'='7');\n"
" device MLVPMOS $1 (S='2',G='6',D='1',B='4') (L=0.25,W=1.5,AS=0,AD=0,PS=0,PD=0);\n"
" device MLVPMOS $2 (S='1',G='5',D='2',B='4') (L=0.25,W=1.5,AS=0,AD=0,PS=0,PD=0);\n"
" device MLVNMOS $3 (S='3',G='6',D='8',B='7') (L=0.25,W=0.95,AS=0,AD=0,PS=0,PD=0);\n"
" device MLVNMOS $4 (S='8',G='5',D='2',B='7') (L=0.25,W=0.95,AS=0,AD=0,PS=0,PD=0);\n"
"end;\n"
"circuit RINGO ($1='11',$2='12',$3='13',$4='14',$5='15');\n"
" subcircuit ND2X1 $1 ($1='12',$2='1',$3='15',$4='12',$5='11',$6='14',$7='15');\n"
" subcircuit INVX1 $2 ($1='12',$2='2',$3='15',$4='12',$5='1',$6='15');\n"
" subcircuit INVX1 $3 ($1='12',$2='3',$3='15',$4='12',$5='2',$6='15');\n"
" subcircuit INVX1 $4 ($1='12',$2='4',$3='15',$4='12',$5='3',$6='15');\n"
" subcircuit INVX1 $5 ($1='12',$2='5',$3='15',$4='12',$5='4',$6='15');\n"
" subcircuit INVX1 $6 ($1='12',$2='6',$3='15',$4='12',$5='5',$6='15');\n"
" subcircuit INVX1 $7 ($1='12',$2='7',$3='15',$4='12',$5='6',$6='15');\n"
" subcircuit INVX1 $8 ($1='12',$2='8',$3='15',$4='12',$5='7',$6='15');\n"
" subcircuit INVX1 $9 ($1='12',$2='9',$3='15',$4='12',$5='8',$6='15');\n"
" subcircuit INVX1 $10 ($1='12',$2='10',$3='15',$4='12',$5='9',$6='15');\n"
" subcircuit INVX1 $11 ($1='12',$2='11',$3='15',$4='12',$5='10',$6='15');\n"
" subcircuit INVX1 $12 ($1='12',$2='13',$3='15',$4='12',$5='11',$6='15');\n"
"circuit RINGO ('11'='11','12'='12','13'='13','14'='14','15'='15');\n"
" subcircuit ND2X1 $1 ('1'='12','2'='1','3'='15','4'='12','5'='11','6'='14','7'='15');\n"
" subcircuit INVX1 $2 ('1'='12','2'='2','3'='15','4'='12','5'='1','6'='15');\n"
" subcircuit INVX1 $3 ('1'='12','2'='3','3'='15','4'='12','5'='2','6'='15');\n"
" subcircuit INVX1 $4 ('1'='12','2'='4','3'='15','4'='12','5'='3','6'='15');\n"
" subcircuit INVX1 $5 ('1'='12','2'='5','3'='15','4'='12','5'='4','6'='15');\n"
" subcircuit INVX1 $6 ('1'='12','2'='6','3'='15','4'='12','5'='5','6'='15');\n"
" subcircuit INVX1 $7 ('1'='12','2'='7','3'='15','4'='12','5'='6','6'='15');\n"
" subcircuit INVX1 $8 ('1'='12','2'='8','3'='15','4'='12','5'='7','6'='15');\n"
" subcircuit INVX1 $9 ('1'='12','2'='9','3'='15','4'='12','5'='8','6'='15');\n"
" subcircuit INVX1 $10 ('1'='12','2'='10','3'='15','4'='12','5'='9','6'='15');\n"
" subcircuit INVX1 $11 ('1'='12','2'='11','3'='15','4'='12','5'='10','6'='15');\n"
" subcircuit INVX1 $12 ('1'='12','2'='13','3'='15','4'='12','5'='11','6'='15');\n"
"end;\n"
);
}
TEST(4_ReaderWithUnconnectedPins)
{
db::Netlist nl;
std::string path = tl::combine_path (tl::combine_path (tl::combine_path (tl::testsrc (), "testdata"), "algo"), "nreader4.cir");
db::NetlistSpiceReader reader;
tl::InputStream is (path);
reader.read (is, nl);
EXPECT_EQ (nl.to_string (),
"circuit RINGO ('1'='1','2'='2','3'='3','4'='4');\n"
" subcircuit INV2PAIR $1 ('1'='4','2'='3','3'='4','4'='1','5'='6','6'='2','7'='3');\n"
" subcircuit INV2PAIR $2 ('1'='4','2'='3','3'='4','4'=(null),'5'='1','6'='5','7'='3');\n"
" subcircuit INV2PAIR $3 ('1'='4','2'='3','3'='4','4'=(null),'5'='5','6'='8','7'='3');\n"
" subcircuit INV2PAIR $4 ('1'='4','2'='3','3'='4','4'=(null),'5'='8','6'='7','7'='3');\n"
" subcircuit INV2PAIR $5 ('1'='4','2'='3','3'='4','4'=(null),'5'='7','6'='6','7'='3');\n"
"end;\n"
"circuit INV2PAIR ('1'='1','2'='2','3'='3','4'='4','5'='5','6'='6','7'='7');\n"
" subcircuit INV2 $1 ('1'='7','2'='5','3'='4','4'='3','5'='2','6'='1');\n"
" subcircuit INV2 $2 ('1'='7','2'='4','3'='6','4'='3','5'='2','6'='1');\n"
"end;\n"
"circuit INV2 ('1'='1','2'='2','3'='3','4'='4','5'='5','6'='6');\n"
" device PMOS $1 (S='3',G='2',D='5',B='1') (L=0.25,W=3.5,AS=1.4,AD=1.4,PS=6.85,PD=6.85);\n"
" device NMOS $3 (S='3',G='2',D='4',B='6') (L=0.25,W=3.5,AS=1.4,AD=1.4,PS=6.85,PD=6.85);\n"
"end;\n"
);
}
TEST(5_CircuitParameters)
{
db::Netlist nl;
std::string path = tl::combine_path (tl::combine_path (tl::combine_path (tl::testsrc (), "testdata"), "algo"), "nreader5.cir");
db::NetlistSpiceReader reader;
tl::InputStream is (path);
reader.read (is, nl);
EXPECT_EQ (nl.to_string (),
"circuit SUBCKT ($1=$1,'A[5]<1>'='A[5]<1>','V42(%)'='V42(%)',Z=Z,GND=GND,GND$1=GND$1);\n"
" subcircuit HVPMOS D_$1 ($1='V42(%)',$2=$3,$3=Z,$4=$1);\n"
" subcircuit HVPMOS D_$2 ($1='V42(%)',$2='A[5]<1>',$3=$3,$4=$1);\n"
" subcircuit HVNMOS D_$3 ($1=GND,$2=$3,$3=GND,$4=GND$1);\n"
" subcircuit HVNMOS D_$4 ($1=GND,$2=$3,$3=Z,$4=GND$1);\n"
" subcircuit HVNMOS D_$5 ($1=GND,$2='A[5]<1>',$3=$3,$4=GND$1);\n"
"end;\n"
"circuit HVPMOS ($1=(null),$2=(null),$3=(null),$4=(null));\n"
"end;\n"
"circuit HVNMOS ($1=(null),$2=(null),$3=(null),$4=(null));\n"
"end;\n"
);
}
class MyNetlistReaderDelegate
: public db::NetlistSpiceReaderDelegate
{
public:
MyNetlistReaderDelegate () : db::NetlistSpiceReaderDelegate () { }
bool wants_subcircuit (const std::string &circuit_name)
{
return circuit_name == "HVNMOS" || circuit_name == "HVPMOS";
}
bool element (db::Circuit *circuit, const std::string &element, const std::string &name, const std::string &model, double value, const std::vector<db::Net *> &nets, const std::map<std::string, double> &params)
{
if (element == "X") {
if (nets.size () != 4) {
error (tl::sprintf ("Device subcircuit '%s' requires four nets", model));
}
db::DeviceClass *cls = circuit->netlist ()->device_class_by_name (model);
if (! cls) {
cls = new db::DeviceClassMOS4Transistor ();
cls->set_name (model);
circuit->netlist ()->add_device_class (cls);
}
db::Device *device = new db::Device (cls);
device->set_name (name);
circuit->add_device (device);
device->connect_terminal (db::DeviceClassMOS4Transistor::terminal_id_S, nets [0]);
device->connect_terminal (db::DeviceClassMOS4Transistor::terminal_id_G, nets [1]);
device->connect_terminal (db::DeviceClassMOS4Transistor::terminal_id_D, nets [2]);
device->connect_terminal (db::DeviceClassMOS4Transistor::terminal_id_B, nets [3]);
const std::vector<db::DeviceParameterDefinition> &td = cls->parameter_definitions ();
for (std::vector<db::DeviceParameterDefinition>::const_iterator i = td.begin (); i != td.end (); ++i) {
std::map<std::string, double>::const_iterator pi = params.find (i->name ());
if (pi != params.end ()) {
device->set_parameter_value (i->id (), pi->second * 1.5);
}
}
return true;
} else {
return db::NetlistSpiceReaderDelegate::element (circuit, element, name, model, value, nets, params);
}
}
};
TEST(6_ReaderWithDelegate)
{
db::Netlist nl;
std::string path = tl::combine_path (tl::combine_path (tl::combine_path (tl::testsrc (), "testdata"), "algo"), "nreader6.cir");
MyNetlistReaderDelegate delegate;
db::NetlistSpiceReader reader (&delegate);
tl::InputStream is (path);
reader.read (is, nl);
EXPECT_EQ (nl.to_string (),
"circuit SUBCKT ($1=$1,A=A,VDD=VDD,Z=Z,GND=GND,GND$1=GND$1);\n"
" device HVPMOS $1 (S=VDD,G=$3,D=Z,B=$1) (L=0.3,W=1.5,AS=0.27,AD=0.27,PS=3.24,PD=3.24);\n"
" device HVPMOS $2 (S=VDD,G=A,D=$3,B=$1) (L=0.3,W=1.5,AS=0.27,AD=0.27,PS=3.24,PD=3.24);\n"
" device HVNMOS $3 (S=GND,G=$3,D=GND,B=GND$1) (L=1.695,W=3.18,AS=0,AD=0,PS=9,PD=9);\n"
" device HVNMOS $4 (S=GND,G=$3,D=Z,B=GND$1) (L=0.6,W=0.6,AS=0.285,AD=0.285,PS=1.74,PD=1.74);\n"
" device HVNMOS $5 (S=GND,G=A,D=$3,B=GND$1) (L=0.6,W=0.6,AS=0.285,AD=0.285,PS=2.64,PD=2.64);\n"
" device RES $1 (A=A,B=Z) (R=100000,L=0,W=0,A=0,P=0);\n"
"end;\n"
"circuit .TOP ();\n"
" subcircuit SUBCKT SUBCKT ($1=(null),A=(null),VDD=(null),Z=(null),GND=VSS,GND$1=VSS);\n"
"end;\n"
);
}

View File

@ -191,7 +191,7 @@ static std::string children2string (const db::Circuit *c)
if (!res.empty ()) {
res += ",";
}
res += (*r)->name ();
res += r->name ();
}
return res;
}
@ -203,7 +203,7 @@ static std::string parents2string (const db::Circuit *c)
if (!res.empty ()) {
res += ",";
}
res += (*r)->name ();
res += r->name ();
}
return res;
}
@ -215,7 +215,7 @@ static std::string td2string (const db::Netlist *nl)
if (!res.empty ()) {
res += ",";
}
res += (*r)->name ();
res += r->name ();
}
return res;
}
@ -227,13 +227,30 @@ static std::string bu2string (const db::Netlist *nl)
if (!res.empty ()) {
res += ",";
}
res += (*r)->name ();
res += r->name ();
}
return res;
}
// ----------------------------------------------------------------------------------------
TEST(0_DeviceClassTemplates)
{
db::DeviceClassMOS3Transistor mos3;
db::DeviceClass generic;
EXPECT_EQ (db::DeviceClassTemplateBase::template_by_name ("MOS3") != 0, true);
EXPECT_EQ (db::DeviceClassTemplateBase::template_by_name ("RES") != 0, true);
EXPECT_EQ (db::DeviceClassTemplateBase::template_by_name ("DOESNTEXIST") == 0, true);
EXPECT_EQ (db::DeviceClassTemplateBase::template_by_name ("MOS3")->is_of (&mos3), true);
EXPECT_EQ (db::DeviceClassTemplateBase::template_by_name ("RES")->is_of (&mos3), false);
EXPECT_EQ (db::DeviceClassTemplateBase::is_a (&mos3) != 0, true);
EXPECT_EQ (db::DeviceClassTemplateBase::is_a (&generic) == 0, true);
EXPECT_EQ (db::DeviceClassTemplateBase::is_a (&mos3)->name (), "MOS3");
}
// ----------------------------------------------------------------------------------------
TEST(1_DeviceTerminalDefinition)
{
db::DeviceTerminalDefinition pd;
@ -314,6 +331,9 @@ TEST(3_CircuitBasic)
c.set_name ("name");
EXPECT_EQ (c.name (), "name");
c.set_boundary (db::DPolygon (db::DBox (0, 1, 2, 3)));
EXPECT_EQ (c.boundary ().to_string (), "(0,1;0,3;2,3;2,1)");
db::Pin p1 = c.add_pin ("p1");
db::Pin p2 = c.add_pin ("p2");
EXPECT_EQ (pins2string (c), "p1#0,p2#1");
@ -1006,8 +1026,8 @@ TEST(12_NetlistTopology)
c2->set_name ("c2");
nl->add_circuit (c2);
EXPECT_EQ (nl->top_circuit_count (), size_t (2));
EXPECT_EQ (td2string (nl.get ()), "c1,c2");
EXPECT_EQ (bu2string (nl.get ()), "c2,c1");
EXPECT_EQ (td2string (nl.get ()), "c2,c1");
EXPECT_EQ (bu2string (nl.get ()), "c1,c2");
std::auto_ptr<db::NetlistLocker> locker (new db::NetlistLocker (nl.get ()));
@ -1017,14 +1037,14 @@ TEST(12_NetlistTopology)
// because we locked, it did not get updated:
EXPECT_EQ (nl->top_circuit_count (), size_t (2));
EXPECT_EQ (td2string (nl.get ()), "c1,c2");
EXPECT_EQ (bu2string (nl.get ()), "c2,c1");
EXPECT_EQ (td2string (nl.get ()), "c2,c1");
EXPECT_EQ (bu2string (nl.get ()), "c1,c2");
locker.reset (0);
// after removing the lock, it's updated
EXPECT_EQ (nl->top_circuit_count (), size_t (3));
EXPECT_EQ (td2string (nl.get ()), "c1,c2,c3");
EXPECT_EQ (bu2string (nl.get ()), "c3,c2,c1");
EXPECT_EQ (td2string (nl.get ()), "c3,c2,c1");
EXPECT_EQ (bu2string (nl.get ()), "c1,c2,c3");
db::SubCircuit *sc1 = new db::SubCircuit (c2);
sc1->set_name ("sc1");
@ -1032,8 +1052,8 @@ TEST(12_NetlistTopology)
EXPECT_EQ (children2string (c1), "c2");
EXPECT_EQ (parents2string (c2), "c1");
EXPECT_EQ (nl->top_circuit_count (), size_t (2));
EXPECT_EQ (td2string (nl.get ()), "c1,c3,c2");
EXPECT_EQ (bu2string (nl.get ()), "c2,c3,c1");
EXPECT_EQ (td2string (nl.get ()), "c3,c1,c2");
EXPECT_EQ (bu2string (nl.get ()), "c2,c1,c3");
db::SubCircuit *sc2 = new db::SubCircuit (c2);
sc2->set_name ("sc2");
@ -1041,8 +1061,8 @@ TEST(12_NetlistTopology)
EXPECT_EQ (children2string (c1), "c2");
EXPECT_EQ (parents2string (c2), "c1");
EXPECT_EQ (nl->top_circuit_count (), size_t (2));
EXPECT_EQ (td2string (nl.get ()), "c1,c3,c2");
EXPECT_EQ (bu2string (nl.get ()), "c2,c3,c1");
EXPECT_EQ (td2string (nl.get ()), "c3,c1,c2");
EXPECT_EQ (bu2string (nl.get ()), "c2,c1,c3");
db::SubCircuit *sc3 = new db::SubCircuit (c3);
sc3->set_name ("sc3");
@ -1053,8 +1073,8 @@ TEST(12_NetlistTopology)
EXPECT_EQ (parents2string (c2), "c1");
EXPECT_EQ (parents2string (c3), "c1");
EXPECT_EQ (nl->top_circuit_count (), size_t (1));
EXPECT_EQ (td2string (nl.get ()), "c1,c2,c3");
EXPECT_EQ (bu2string (nl.get ()), "c3,c2,c1");
EXPECT_EQ (td2string (nl.get ()), "c1,c3,c2");
EXPECT_EQ (bu2string (nl.get ()), "c2,c3,c1");
db::SubCircuit *sc4 = new db::SubCircuit (*sc2);
sc4->set_name ("sc4");
@ -1280,3 +1300,103 @@ TEST(21_FlattenSubCircuit2)
"end;\n"
);
}
TEST(22_BlankCircuit)
{
db::Netlist nl;
db::DeviceClass *dc;
dc = new db::DeviceClassMOS3Transistor ();
dc->set_name ("NMOS");
nl.add_device_class (dc);
dc = new db::DeviceClassMOS3Transistor ();
dc->set_name ("PMOS");
nl.add_device_class (dc);
nl.from_string (
"circuit RINGO (IN=IN,OSC=OSC,VSS=VSS,VDD=VDD);\n"
" subcircuit INV2 INV2_SC1 (IN=$I8,$2=FB,OUT=OSC,$4=VSS,$5=VDD);\n"
" subcircuit INV2 INV2_SC2 (IN=FB,$2=(null),OUT=$I8,$4=VSS,$5=VDD);\n"
"end;\n"
"circuit INV2 (IN=IN,$2=$2,OUT=OUT,$4=$4,$5=$5);\n"
" subcircuit PTRANS SC1 ($1=$5,$2=$2,$3=IN);\n"
" subcircuit NTRANS SC2 ($1=$4,$2=$2,$3=IN);\n"
" subcircuit PTRANS SC3 ($1=$5,$2=OUT,$3=$2);\n"
" subcircuit NTRANS SC4 ($1=$4,$2=OUT,$3=$2);\n"
"end;\n"
"circuit PTRANS ($1=$1,$2=$2,$3=$3);\n"
" device PMOS $1 (S=$1,D=$2,G=$3) (L=0.25,W=0.95);\n"
"end;\n"
"circuit NTRANS ($1=$1,$2=$2,$3=$3);\n"
" device NMOS $1 (S=$1,D=$2,G=$3) (L=0.25,W=0.95);\n"
"end;\n"
);
db::Netlist nl2;
nl2 = nl;
db::Circuit *circuit = nl2.circuit_by_name ("INV2");
nl2.purge_circuit (circuit);
EXPECT_EQ (nl2.to_string (),
"circuit RINGO (IN=IN,OSC=OSC,VSS=VSS,VDD=VDD);\n"
" subcircuit (null);\n"
" subcircuit (null);\n"
"end;\n"
);
nl2 = nl;
circuit = nl2.circuit_by_name ("INV2");
EXPECT_EQ (circuit->dont_purge (), false);
circuit->blank ();
EXPECT_EQ (circuit->dont_purge (), true);
EXPECT_EQ (nl2.to_string (),
"circuit RINGO (IN=IN,OSC=OSC,VSS=VSS,VDD=VDD);\n"
" subcircuit INV2 INV2_SC1 (IN=$I8,$2=FB,OUT=OSC,$4=VSS,$5=VDD);\n"
" subcircuit INV2 INV2_SC2 (IN=FB,$2=(null),OUT=$I8,$4=VSS,$5=VDD);\n"
"end;\n"
"circuit INV2 (IN=(null),$2=(null),OUT=(null),$4=(null),$5=(null));\n"
"end;\n"
);
// purge won't delete INV2
nl2.purge ();
EXPECT_EQ (nl2.to_string (),
"circuit RINGO (IN=(null),OSC=OSC,VSS=VSS,VDD=VDD);\n"
" subcircuit INV2 INV2_SC1 (IN=$I8,$2=FB,OUT=OSC,$4=VSS,$5=VDD);\n"
" subcircuit INV2 INV2_SC2 (IN=FB,$2=(null),OUT=$I8,$4=VSS,$5=VDD);\n"
"end;\n"
"circuit INV2 (IN=(null),$2=(null),OUT=(null),$4=(null),$5=(null));\n"
"end;\n"
);
circuit->set_dont_purge (false);
// now it will ...
nl2.purge ();
EXPECT_EQ (nl2.to_string (),
""
);
nl2 = nl;
circuit = nl2.circuit_by_name ("RINGO");
nl2.purge_circuit (circuit);
EXPECT_EQ (nl2.to_string (),
""
);
nl2 = nl;
circuit = nl2.circuit_by_name ("RINGO");
circuit->blank ();
EXPECT_EQ (nl2.to_string (),
"circuit RINGO (IN=(null),OSC=(null),VSS=(null),VDD=(null));\n"
"end;\n"
);
}

View File

@ -24,6 +24,7 @@
#include "dbNetlistSpiceWriter.h"
#include "dbNetlist.h"
#include "dbNetlistDeviceClasses.h"
#include "dbLayoutToNetlist.h"
#include "tlUnitTest.h"
#include "tlStream.h"
@ -307,9 +308,9 @@ TEST(4_WriterDiodeDevices)
circuit1->add_net (n3);
db::Device *ddev1 = new db::Device (dcls);
ddev1->set_parameter_value (db::DeviceClassDiode::param_id_A, 1.7e-10);
ddev1->set_parameter_value (db::DeviceClassDiode::param_id_A, 1.7);
db::Device *ddev2 = new db::Device (dcls);
ddev2->set_parameter_value (db::DeviceClassDiode::param_id_A, 42e-9);
ddev2->set_parameter_value (db::DeviceClassDiode::param_id_A, 0.42);
circuit1->add_device (ddev1);
circuit1->add_device (ddev2);
@ -867,6 +868,381 @@ TEST(10_WriterLongLines)
compare_netlists (_this, path, au_path);
}
TEST(11_WriterNonConnectedPins)
{
db::Netlist nl;
db::DeviceClass *rcls = new db::DeviceClassResistor ();
db::DeviceClass *ccls = new db::DeviceClassCapacitor ();
db::DeviceClass *lcls = new db::DeviceClassInductor ();
db::DeviceClass *dcls = new db::DeviceClassDiode ();
db::DeviceClass *m3cls = new db::DeviceClassMOS3Transistor ();
db::DeviceClass *m4cls = new db::DeviceClassMOS4Transistor ();
rcls->set_name ("RCLS");
lcls->set_name ("LCLS");
ccls->set_name ("CCLS");
dcls->set_name ("DCLS");
m3cls->set_name ("M3CLS");
m4cls->set_name ("M4CLS");
nl.add_device_class (rcls);
nl.add_device_class (lcls);
nl.add_device_class (ccls);
nl.add_device_class (dcls);
nl.add_device_class (m3cls);
nl.add_device_class (m4cls);
db::Circuit *circuit1 = new db::Circuit ();
circuit1->set_name ("C1");
nl.add_circuit (circuit1);
{
db::Net *n1, *n2, *n3, *n4, *n5;
n1 = new db::Net ();
n1->set_name ("n1");
circuit1->add_net (n1);
n2 = new db::Net ();
n2->set_name ("n2");
circuit1->add_net (n2);
n3 = new db::Net ();
n3->set_name ("n3");
circuit1->add_net (n3);
n4 = new db::Net ();
n4->set_name ("n4");
circuit1->add_net (n4);
n5 = new db::Net ();
n5->set_name ("n5");
circuit1->add_net (n5);
db::Device *ddev1 = new db::Device (m4cls);
ddev1->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_L, 0.25);
ddev1->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_W, 0.18);
ddev1->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_AS, 1.2);
ddev1->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_AD, 0.75);
ddev1->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_PS, 2.2);
ddev1->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_PD, 1.75);
db::Device *ddev2 = new db::Device (m4cls);
ddev2->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_L, 1.4);
ddev2->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_W, 0.25);
ddev2->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_AS, 1.3);
ddev2->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_AD, 0.85);
ddev2->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_PS, 2.3);
ddev2->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_PD, 1.85);
circuit1->add_device (ddev1);
circuit1->add_device (ddev2);
size_t pid1 = circuit1->add_pin ("p1").id ();
size_t pid2 = circuit1->add_pin ("p2").id ();
size_t pid3 = circuit1->add_pin ("p3").id ();
size_t pid4 = circuit1->add_pin ("p4").id ();
circuit1->connect_pin (pid1, n1);
circuit1->connect_pin (pid2, n2);
circuit1->connect_pin (pid3, n4);
circuit1->connect_pin (pid4, n5);
ddev1->connect_terminal (ddev1->device_class ()->terminal_id_for_name ("S"), n1);
ddev1->connect_terminal (ddev1->device_class ()->terminal_id_for_name ("G"), n4);
ddev1->connect_terminal (ddev1->device_class ()->terminal_id_for_name ("D"), n3);
ddev1->connect_terminal (ddev1->device_class ()->terminal_id_for_name ("B"), n5);
ddev2->connect_terminal (ddev2->device_class ()->terminal_id_for_name ("S"), n3);
ddev2->connect_terminal (ddev1->device_class ()->terminal_id_for_name ("G"), n4);
ddev2->connect_terminal (ddev2->device_class ()->terminal_id_for_name ("D"), n2);
ddev2->connect_terminal (ddev1->device_class ()->terminal_id_for_name ("B"), n5);
}
db::Circuit *circuit2 = new db::Circuit ();
circuit2->set_name ("C2");
nl.add_circuit (circuit2);
{
db::Net *n1, *n2, *n3, *n4, *n5;
n1 = new db::Net ();
// this gives a clash with the auto-generated node names with non-connected subcircuit pins
// and terminals - we test proper generation of such names this way
n1->set_name ("nc_10");
circuit2->add_net (n1);
n2 = new db::Net ();
n2->set_name ("n2");
circuit2->add_net (n2);
n3 = new db::Net ();
n3->set_name ("n3");
circuit2->add_net (n3);
n4 = new db::Net ();
n4->set_name ("n4");
circuit2->add_net (n4);
n5 = new db::Net ();
n5->set_name ("n5");
circuit2->add_net (n5);
db::SubCircuit *sc1 = new db::SubCircuit (circuit1, "SC1");
circuit2->add_subcircuit (sc1);
sc1->connect_pin (0, n1);
sc1->connect_pin (1, n3);
// pin 2 unconnected
sc1->connect_pin (3, n3);
db::SubCircuit *sc2 = new db::SubCircuit (circuit1, "SC2");
circuit2->add_subcircuit (sc2);
sc2->connect_pin (0, n3);
// pin 1 unconnected
sc2->connect_pin (2, n4);
sc2->connect_pin (3, n3);
size_t pid1 = circuit2->add_pin ("p1").id ();
size_t pid2 = circuit2->add_pin ("p2").id ();
size_t pid3 = circuit2->add_pin ("p3").id ();
circuit2->connect_pin (pid1, n1);
circuit2->connect_pin (pid2, n2);
circuit2->connect_pin (pid3, n4);
}
// verify against the input
std::string path = tmp_file ("tmp_nwriter11.txt");
{
tl::OutputStream stream (path);
db::NetlistSpiceWriter writer;
writer.write (stream, nl, "written by unit test");
}
std::string au_path = tl::combine_path (tl::combine_path (tl::combine_path (tl::testsrc (), "testdata"), "algo"), "nwriter11_au.txt");
compare_netlists (_this, path, au_path);
path = tmp_file ("tmp_nwriter11b.txt");
{
tl::OutputStream stream (path);
db::NetlistSpiceWriter writer;
writer.set_use_net_names (true);
writer.write (stream, nl, "written by unit test");
}
au_path = tl::combine_path (tl::combine_path (tl::combine_path (tl::testsrc (), "testdata"), "algo"), "nwriter11b_au.txt");
compare_netlists (_this, path, au_path);
}
TEST(12_UniqueNetNames)
{
db::LayoutToNetlist l2n;
std::string l2n_path = tl::combine_path (tl::combine_path (tl::combine_path (tl::testsrc (), "testdata"), "algo"), "same_net_names.l2n");
l2n.load (l2n_path);
// verify against the input
std::string path = tmp_file ("tmp_nwriter12.txt");
{
tl::OutputStream stream (path);
db::NetlistSpiceWriter writer;
writer.write (stream, *l2n.netlist (), "written by unit test");
}
std::string au_path = tl::combine_path (tl::combine_path (tl::combine_path (tl::testsrc (), "testdata"), "algo"), "nwriter12_au.txt");
compare_netlists (_this, path, au_path);
path = tmp_file ("tmp_nwriter12b.txt");
{
tl::OutputStream stream (path);
db::NetlistSpiceWriter writer;
writer.set_use_net_names (true);
writer.write (stream, *l2n.netlist (), "written by unit test");
}
au_path = tl::combine_path (tl::combine_path (tl::combine_path (tl::testsrc (), "testdata"), "algo"), "nwriter12b_au.txt");
compare_netlists (_this, path, au_path);
}
TEST(13_WriterBJT3Devices)
{
db::Netlist nl;
db::DeviceClass *rcls = new db::DeviceClassResistor ();
db::DeviceClass *ccls = new db::DeviceClassCapacitor ();
db::DeviceClass *lcls = new db::DeviceClassInductor ();
db::DeviceClass *dcls = new db::DeviceClassDiode ();
db::DeviceClass *b3cls = new db::DeviceClassBJT3Transistor ();
db::DeviceClass *b4cls = new db::DeviceClassBJT4Transistor ();
rcls->set_name ("RCLS");
lcls->set_name ("LCLS");
ccls->set_name ("CCLS");
dcls->set_name ("DCLS");
b3cls->set_name ("B3CLS");
b4cls->set_name ("B4CLS");
nl.add_device_class (rcls);
nl.add_device_class (lcls);
nl.add_device_class (ccls);
nl.add_device_class (dcls);
nl.add_device_class (b3cls);
nl.add_device_class (b4cls);
db::Circuit *circuit1 = new db::Circuit ();
circuit1->set_name ("C1");
nl.add_circuit (circuit1);
db::Net *n1, *n2, *n3, *n4;
n1 = new db::Net ();
n1->set_name ("n1");
circuit1->add_net (n1);
n2 = new db::Net ();
n2->set_name ("n2");
circuit1->add_net (n2);
n3 = new db::Net ();
n3->set_name ("n3");
circuit1->add_net (n3);
n4 = new db::Net ();
n4->set_name ("n4");
circuit1->add_net (n4);
db::Device *ddev1 = new db::Device (b3cls);
ddev1->set_parameter_value (db::DeviceClassBJT3Transistor::param_id_AE, 0.25);
ddev1->set_parameter_value (db::DeviceClassBJT3Transistor::param_id_PE, 0.18);
ddev1->set_parameter_value (db::DeviceClassBJT3Transistor::param_id_AB, 1.2);
ddev1->set_parameter_value (db::DeviceClassBJT3Transistor::param_id_PB, 0.75);
ddev1->set_parameter_value (db::DeviceClassBJT3Transistor::param_id_AC, 1.0);
ddev1->set_parameter_value (db::DeviceClassBJT3Transistor::param_id_PC, 0.6);
db::Device *ddev2 = new db::Device (b3cls);
ddev2->set_parameter_value (db::DeviceClassBJT3Transistor::param_id_AE, 1.2);
ddev2->set_parameter_value (db::DeviceClassBJT3Transistor::param_id_PE, 2.5);
ddev2->set_parameter_value (db::DeviceClassBJT3Transistor::param_id_AB, 1.4);
ddev2->set_parameter_value (db::DeviceClassBJT3Transistor::param_id_PB, 2.8);
ddev2->set_parameter_value (db::DeviceClassBJT3Transistor::param_id_AC, 1.5);
ddev2->set_parameter_value (db::DeviceClassBJT3Transistor::param_id_PC, 3.0);
circuit1->add_device (ddev1);
circuit1->add_device (ddev2);
size_t pid1 = circuit1->add_pin ("p1").id ();
size_t pid2 = circuit1->add_pin ("p2").id ();
size_t pid3 = circuit1->add_pin ("p3").id ();
circuit1->connect_pin (pid1, n1);
circuit1->connect_pin (pid2, n2);
circuit1->connect_pin (pid3, n4);
ddev1->connect_terminal (ddev1->device_class ()->terminal_id_for_name ("E"), n1);
ddev1->connect_terminal (ddev1->device_class ()->terminal_id_for_name ("B"), n4);
ddev1->connect_terminal (ddev1->device_class ()->terminal_id_for_name ("C"), n3);
ddev2->connect_terminal (ddev2->device_class ()->terminal_id_for_name ("E"), n3);
ddev2->connect_terminal (ddev1->device_class ()->terminal_id_for_name ("B"), n4);
ddev2->connect_terminal (ddev2->device_class ()->terminal_id_for_name ("C"), n2);
// verify against the input
std::string path = tmp_file ("tmp_nwriter13.txt");
{
tl::OutputStream stream (path);
db::NetlistSpiceWriter writer;
writer.write (stream, nl, "written by unit test");
}
std::string au_path = tl::combine_path (tl::combine_path (tl::combine_path (tl::testsrc (), "testdata"), "algo"), "nwriter13_au.txt");
compare_netlists (_this, path, au_path);
}
TEST(14_WriterBJT4Devices)
{
db::Netlist nl;
db::DeviceClass *rcls = new db::DeviceClassResistor ();
db::DeviceClass *ccls = new db::DeviceClassCapacitor ();
db::DeviceClass *lcls = new db::DeviceClassInductor ();
db::DeviceClass *dcls = new db::DeviceClassDiode ();
db::DeviceClass *b3cls = new db::DeviceClassBJT3Transistor ();
db::DeviceClass *b4cls = new db::DeviceClassBJT4Transistor ();
rcls->set_name ("RCLS");
lcls->set_name ("LCLS");
ccls->set_name ("CCLS");
dcls->set_name ("DCLS");
b3cls->set_name ("B3CLS");
b4cls->set_name ("B4CLS");
nl.add_device_class (rcls);
nl.add_device_class (lcls);
nl.add_device_class (ccls);
nl.add_device_class (dcls);
nl.add_device_class (b3cls);
nl.add_device_class (b4cls);
db::Circuit *circuit1 = new db::Circuit ();
circuit1->set_name ("C1");
nl.add_circuit (circuit1);
db::Net *n1, *n2, *n3, *n4, *n5;
n1 = new db::Net ();
n1->set_name ("n1");
circuit1->add_net (n1);
n2 = new db::Net ();
n2->set_name ("n2");
circuit1->add_net (n2);
n3 = new db::Net ();
n3->set_name ("n3");
circuit1->add_net (n3);
n4 = new db::Net ();
n4->set_name ("n4");
circuit1->add_net (n4);
n5 = new db::Net ();
n5->set_name ("n5");
circuit1->add_net (n5);
db::Device *ddev1 = new db::Device (b4cls);
ddev1->set_parameter_value (db::DeviceClassBJT4Transistor::param_id_AE, 0.25);
ddev1->set_parameter_value (db::DeviceClassBJT4Transistor::param_id_PE, 0.18);
ddev1->set_parameter_value (db::DeviceClassBJT4Transistor::param_id_AB, 1.2);
ddev1->set_parameter_value (db::DeviceClassBJT4Transistor::param_id_PB, 0.75);
ddev1->set_parameter_value (db::DeviceClassBJT4Transistor::param_id_AC, 1.0);
ddev1->set_parameter_value (db::DeviceClassBJT4Transistor::param_id_PC, 0.6);
db::Device *ddev2 = new db::Device (b4cls);
ddev2->set_parameter_value (db::DeviceClassBJT4Transistor::param_id_AE, 1.2);
ddev2->set_parameter_value (db::DeviceClassBJT4Transistor::param_id_PE, 2.5);
ddev2->set_parameter_value (db::DeviceClassBJT4Transistor::param_id_AB, 1.4);
ddev2->set_parameter_value (db::DeviceClassBJT4Transistor::param_id_PB, 2.8);
ddev2->set_parameter_value (db::DeviceClassBJT4Transistor::param_id_AC, 1.5);
ddev2->set_parameter_value (db::DeviceClassBJT4Transistor::param_id_PC, 3.0);
circuit1->add_device (ddev1);
circuit1->add_device (ddev2);
size_t pid1 = circuit1->add_pin ("p1").id ();
size_t pid2 = circuit1->add_pin ("p2").id ();
size_t pid3 = circuit1->add_pin ("p3").id ();
size_t pid4 = circuit1->add_pin ("p4").id ();
circuit1->connect_pin (pid1, n1);
circuit1->connect_pin (pid2, n2);
circuit1->connect_pin (pid3, n4);
circuit1->connect_pin (pid4, n5);
ddev1->connect_terminal (ddev1->device_class ()->terminal_id_for_name ("E"), n1);
ddev1->connect_terminal (ddev1->device_class ()->terminal_id_for_name ("B"), n4);
ddev1->connect_terminal (ddev1->device_class ()->terminal_id_for_name ("C"), n3);
ddev1->connect_terminal (ddev1->device_class ()->terminal_id_for_name ("S"), n5);
ddev2->connect_terminal (ddev2->device_class ()->terminal_id_for_name ("E"), n3);
ddev2->connect_terminal (ddev1->device_class ()->terminal_id_for_name ("B"), n4);
ddev2->connect_terminal (ddev2->device_class ()->terminal_id_for_name ("C"), n2);
ddev2->connect_terminal (ddev1->device_class ()->terminal_id_for_name ("S"), n5);
// verify against the input
std::string path = tmp_file ("tmp_nwriter14.txt");
{
tl::OutputStream stream (path);
db::NetlistSpiceWriter writer;
writer.write (stream, nl, "written by unit test");
}
std::string au_path = tl::combine_path (tl::combine_path (tl::combine_path (tl::testsrc (), "testdata"), "algo"), "nwriter14_au.txt");
compare_netlists (_this, path, au_path);
}
namespace {
class MyDelegate

View File

@ -71,7 +71,8 @@ SOURCES = \
dbDeepEdgesTests.cc \
dbDeepEdgePairsTests.cc \
dbNetlistCompareTests.cc \
dbNetlistReaderTests.cc
dbNetlistReaderTests.cc \
dbLayoutVsSchematicTests.cc
INCLUDEPATH += $$TL_INC $$DB_INC $$GSI_INC
DEPENDPATH += $$TL_INC $$DB_INC $$GSI_INC

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,418 @@
# $autorun-early
module DRC
# The DRC netter object
# %DRC%
# @scope
# @name Netter
# @brief DRC Reference: Netter object
# The Netter object provides services related to network extraction
# from a layout. The relevant methods of this object are available
# as global functions too where they act on a default incarnation
# of the netter. Usually it's not required to instantiate a Netter
# object, but it serves as a container for this functionality.
#
# @code
# # create a new Netter object:
# nx = netter
# nx.connect(poly, contact)
# ...
# @/code
#
# Network formation:
#
# A basic service the Netter object provides is the formation of
# connected networks of conductive shapes (netting). To do so, the Netter
# must be given a connection specification. This happens by calling
# "connect" with two polygon layers. The Netter will then regard all
# overlaps of shapes on these layers as connections between the
# respective materials. Networks are the basis for netlist extraction,
# network geometry deduction and the antenna check.
#
# Connections can be cleared with "clear_connections". If not,
# connections add atop of the already defined ones. Here is an
# example for the antenna check:
#
# @code
# # build connction of poly+gate to metal1
# connect(gate, poly)
# connect(poly, contact)
# connect(contact, metal1)
#
# # runs an antenna check for metal1 with a ratio of 50
# m1_antenna_errors = antenna_check(gate, metal1, 50.0)
#
# # add connections to metal2
# connect(metal1, via1)
# connect(via1, metal2)
#
# # runs an antenna check for metal2 with a ratio of 70.0
# m2_antenna_errors = antenna_check(gate, metal2, 70.0)
#
# # this will remove all connections made
# clear_connections
# ...
# @/code
#
# Further functionality of the Netter object:
#
# More methods will be added in the future to support network-related features.
class DRCNetter
def initialize(engine)
@engine = engine
@netlisted = false
@connect_implicit = []
@l2n = nil
@lnum = 0
end
# %DRC%
# @name connect
# @brief Specifies a connection between two layers
# @synopsis connect(a, b)
# a and b must be polygon layers. After calling this function, the
# Netter regards all overlapping or touching shapes on these layers
# to form an electrical connection between the materials formed by
# these layers. This also implies intra-layer connections: shapes
# on these layers touching or overlapping other shapes on these
# layers will form bigger, electrically connected areas.
#
# Multiple connect calls must be made to form larger connectivity
# stacks across multiple layers. Such stacks may include forks and
# joins.
#
# Connections are accumulated. The connections defined so far
# can be cleared with \clear_connections.
def connect(a, b)
a.is_a?(DRC::DRCLayer) || raise("First argument of Netter#connect must be a layer")
b.is_a?(DRC::DRCLayer) || raise("Second argument of Netter#connect must be a layer")
a.requires_region("Netter#connect (first argument)")
b.requires_region("Netter#connect (second argument)")
register_layer(a.data)
register_layer(b.data)
@l2n.connect(a.data)
@l2n.connect(b.data)
@l2n.connect(a.data, b.data)
end
# %DRC%
# @name connect_global
# @brief Connects a layer with a global net
# @synopsis connect_global(l, name)
# Connects the shapes from the given layer l to a global net with the given name.
# Global nets are common to all cells. Global nets automatically connect to parent
# cells throughs implied pins. An example is the substrate (bulk) net which connects
# to shapes belonging to tie-down diodes.
def connect_global(l, name)
l.is_a?(DRC::DRCLayer) || raise("Layer argument of Netter#connect_global must be a layer")
l.requires_region("Netter#connect_global (layer argument)")
register_layer(l.data)
@l2n.connect(l.data)
@l2n.connect_global(l.data, name)
end
# %DRC%
# @name extract_devices
# @brief Extracts devices based on the given extractor class, name and device layer selection
# @synopsis extract_devices(extractor, layer_hash)
# @synopsis extract_devices(extractor_class, name, layer_hash)
# Runs the device extraction for given device extractor class. In the first
# form, the extractor object is given. In the second form, the extractor's
# class object and the new extractor's name is given.
#
# The device extractor is either an instance of one of the predefined extractor
# classes (e.g. obtained from the utility methods such as \mos4) or a custom class.
# It provides the
# algorithms for deriving the device parameters from the device geometry. It needs
# several device recognition layers which are passed in the layer hash.
#
# Each device class (e.g. n-MOS/p-MOS or high Vt/low Vt) needs it's own instance
# of device extractor. The device extractor beside the algorithm and specific
# extraction settings defines the name of the device to be built.
#
# The layer hash is a map of device type specific functional names (key) and
# polygon layers (value). Here is an example:
#
# @code
# deep
#
# nwell = input(1, 0)
# active = input(2, 0)
# poly = input(3, 0)
# bulk = make_layer # renders an empty layer used for putting the terminals on
#
# nactive = active - nwell # active area of NMOS
# nsd = nactive - poly # source/drain area
# gate = nactive & poly # gate area
#
# extract_devices(mos4("NMOS4"), { :SD => nsd, :G => gate, :P => poly, :W => bulk })
# @/code
def extract_devices(devex, layer_selection)
ensure_data
devex.is_a?(RBA::DeviceExtractorBase) || raise("First argument of Netter#extract_devices must be a device extractor instance in the two-arguments form")
layer_selection.is_a?(Hash) || raise("Second argument of Netter#extract_devices must be a hash")
ls = {}
layer_selection.each do |n,l|
l.requires_region("Netter#extract_devices (#{n} layer)")
register_layer(l.data)
ls[n.to_s] = l.data
end
@engine._cmd(@l2n, :extract_devices, devex, ls)
end
# %DRC%
# @name clear_connections
# @brief Clears all connections stored so far
# @synopsis clear_connections
# See \connect for more details.
def clear_connections
@netlisted = false
@connect_implicit = []
_clear_data
end
# %DRC%
# @name connect_implicit
# @brief Specifies a search pattern for labels which create implicit net connections
# @synopsis connect_implicit(label_pattern)
# Use this method to supply label strings 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, nets 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. "connect_implicit" can
# achive this if these islands are labelled with the same text on the top level of the
# component.
#
# The implicit connections are applied on the next net extraction and cleared
# on "clear_connections".
def connect_implicit(arg)
cleanup
@connect_implicit << arg
end
# %DRC%
# @brief Performs an antenna check
# @name antenna_check
# @synopsis antenna_check(gate, metal, ratio, [ diode_specs ... ])
#
# The antenna check is used to avoid plasma induced damage. Physically,
# the damage happes if during the manufacturing of a metal layer with
# plasma etching charge accumulates on the metal islands. On reaching a
# certain threshold, this charge may discarge over gate oxide attached of
# devices attached to such metal areas hence damaging it.
#
# Antenna checks are performed by collecting all connected nets up to
# a certain metal layer and then computing the area of all metal shapes
# and all connected gates of a certain kind (e.g. thin and thick oxide gates).
# The ratio of metal area divided by the gate area must not exceed a certain
# threshold.
#
# A simple antenna check is this:
#
# @code
# poly = ... # poly layer
# diff = ... # diffusion layer
# contact = ... # contact layer
# metal1 = ... # metal layer
#
# # compute gate area
# gate = poly & diff
#
# # note that gate and poly have to be included - gate is
# # a subset of poly, but forms the sensitive area
# connect(gate, poly)
# connect(poly, contact)
# connect(contact, metal1)
# errors = antenna_check(gate, metal1, 50.0)
# @/code
#
# Plasma induced damage can be rectified by including diodes
# which create a safe current path for discharging the metal
# islands. Such diodes can be identified with a recognition layer
# (usually the diffusion area of a certain kind). You can include
# such diode recognition layers in the antenna check. If a connection
# is detected to a diode, the respective network is skipped:
#
# @code
# ...
# diode = ... # diode recognition layer
#
# connect(diode, contact)
# errors = antenna_check(gate, metal1, 50.0, diode)
# @/code
#
# You can also make diode connections decreases the
# sensitivity of the antenna check depending on the size
# of the diode. The following specification makes
# diode connections increase the ratio threshold by
# 10 per square micrometer of diode area:
#
# @code
# ...
# diode = ... # diode recognition layer
#
# connect(diode, contact)
# # each square micrometer of diode area connected to a network
# # will add 10 to the ratio:
# errors = antenna_check(gate, metal1, 50.0, [ diode, 10.0 ])
# @/code
#
# Multiple diode specifications are allowed. Just add them
# to the antenna_check call.
#
# The error shapes produced by the antenna check are copies
# of the metal shapes on the metal layers of each network
# violating the antenna rule.
def antenna_check(gate, metal, ratio, *diodes)
gate.is_a?(DRC::DRCLayer) || raise("gate argument of Netter#antenna_check must be a layer")
gate.requires_region("Netter#antenna_check (gate argument)")
metal.is_a?(DRC::DRCLayer) || raise("metal argument of Netter#antenna_check must be a layer")
metal.requires_region("Netter#antenna_check (metal argument)")
if !ratio.is_a?(1.class) && !ratio.is_a?(Float)
raise("ratio argument Netter#antenna_check is not a number")
end
dl = diodes.collect do |d|
if d.is_a?(Array)
d.size == 2 || raise("diode specification pair expects two elements")
d[0].requires_region("Netter#antenna_check (diode layer)")
[ d[0].data, d[1].to_f ]
else
d.requires_region("Netter#antenna_check (diode layer)")
[ d.data, 0.0 ]
end
end
DRC::DRCLayer::new(@engine, @engine._cmd(l2n_data, :antenna_check, gate.data, metal.data, ratio, dl))
end
# %DRC%
# @name l2n_data
# @brief Gets the internal RBA::LayoutToNetlist object
# @synopsis l2n_data
# The RBA::LayoutToNetlist object provides access to the internal details of
# the netter object.
def l2n_data
ensure_data
# run extraction in a timed environment
if ! @netlisted
# build a glob expression from the parts
exprs = @connect_implicit.collect { |c| c.gsub(/\?\*\[\]\{\},\(\)\\/) { |x| "\\" + x } }
if exprs.size > 1
expr = "{" + exprs.join(",") + "}"
else
expr = exprs[0] || ""
end
@engine._cmd(@l2n, :extract_netlist, expr)
@netlisted = true
end
@l2n
end
# %DRC%
# @name netlist
# @brief Gets the extracted netlist or triggers extraction if not done yet
# @synopsis netlist
# If no extraction has been performed yet, this method will start the
# layout analysis. Hence, all \connect, \connect_global and \connect_implicit
# calls must have been made before this method is used. Further \connect
# statements will clear the netlist and re-extract it again.
def netlist
l2n_data && @l2n.netlist
end
def _finish
clear_connections
end
def _clear_data
@l2n && @l2n._destroy
@l2n = nil
end
def _take_data
l2ndb = self.l2n_data
@l2n = nil
l2ndb
end
private
def cleanup
@netlisted && clear_connections
end
def ensure_data
if !@l2n
@layers = {}
_make_data
end
end
def _make_data
if @engine._dss
@engine._dss.is_singular? || raise("The DRC script features more than one or no layout source - network extraction cannot be performed in such configurations")
@l2n = RBA::LayoutToNetlist::new(@engine._dss)
else
layout = @engine.source.layout
@l2n = RBA::LayoutToNetlist::new(layout.top_cell.name, layout.dbu)
end
end
def register_layer(data)
id = data.data_id
if @layers && @layers[id]
# already registered
return
end
ensure_data
@layers[id] = data
@lnum += 1
# every layer gets registered and intra-layer connections are made
@l2n.register(data, "l" + @lnum.to_s)
end
end
end

View File

@ -0,0 +1,116 @@
# $autorun-early
# Extend the Float class by methods which convert
# values with units, i.e. 1.3nm gives 0.0013
1.0.class.class_eval do
class << self
@dbu = nil
def _dbu=(dbu)
@dbu = dbu
end
def _dbu
@dbu
end
end
def um
self
end
def micron
self
end
def degree
self
end
def nm
self*0.001
end
def mm
self*1000.0
end
def m
self*1000000.0
end
def nm2
self*1e-6
end
def um2
self
end
def micron2
self
end
def mm2
self*1.0e6
end
def m2
self*1.0e12
end
def dbu
self.class._dbu || raise("No layout loaded - cannot determine database unit")
self*self.class._dbu
end
end
# Extend the Fixnum class, so it is possible to
# convert a value to Float with a unit spec, i.e.
# 5.nm -> 0.005. A spec with ".dbu" gives the
# Fixnum value itself. This indicates a value in
# database units for most methods of the DRC
# framework.
1.class.class_eval do
class << self
@dbu = nil
def _dbu=(dbu)
@dbu = dbu
end
def _dbu
@dbu
end
end
def um
self.to_f
end
def micron
self.to_f
end
def degree
self.to_f
end
def nm
self*0.001
end
def mm
self*1000.0
end
def m
self*1000000.0
end
def nm2
self*1.0e-6
end
def um2
self.to_f
end
def micron2
self.to_f
end
def mm2
self*1.0e6
end
def m2
self*1.0e12
end
def dbu
self.class._dbu || raise("No layout loaded - cannot determine database unit")
self*self.class._dbu
end
end

View File

@ -0,0 +1,415 @@
# $autorun-early
module DRC
# A layout source representative object.
# This object describes an input. It consists of a layout reference plus
# some attributes describing how input is to be gathered.
# %DRC%
# @scope
# @name Source
# @brief DRC Reference: Source Object
# The layer object represents a collection of polygons, edges or edge pairs.
# A source specifies where to take layout from. That includes the actual layout,
# the top cell and options such as clip/query boxes, cell filters etc.
class DRCSource
def initialize(engine, layout, layout_var, cell, path)
@engine = engine
@layout = layout
@layout_var = layout_var
@path = path
@cell = cell
@inside = nil
@box = nil
@layers = nil
@sel = []
@clip = false
@overlapping = false
@tmp_layers = []
end
# %DRC%
# @name layout
# @brief Returns the RBA::Layout object associated with this source
# @synopsis layout
def layout
@layout
end
# %DRC%
# @name cell_name
# @brief Returns the name of the currently selected cell
# @synopsis cell_name
def cell_name
@cell.name
end
# %DRC%
# @name cell_obj
# @brief Returns the RBA::Cell object of the currently selected cell
# @synopsis cell_obj
def cell_obj
@cell
end
def finish
@tmp_layers.each do |li|
@layout.delete_layer(li)
end
end
def set_box(method, *args)
box = nil
if args.size == 1
box = args[0]
box.is_a?(RBA::DBox) || raise("'#{method}' method requires a box specification")
elsif args.size == 2
(args[0].is_a?(RBA::DPoint) && args[1].is_a?(RBA::DPoint)) || raise("'#{method}' method requires a box specification with two points")
box = RBA::DBox::new(args[0], args[1])
elsif args.size == 4
box = RBA::DBox::new(*args)
else
raise("Invalid number of arguments for '#{method}' method")
end
@box = RBA::Box::from_dbox(box * (1.0 / @layout.dbu))
self
end
def inplace_clip(*args)
set_box("clip", *args)
@clip = true
@overlapping = true
end
def inplace_touching(*args)
set_box("touching", *args)
@clip = false
@overlapping = false
end
def inplace_overlapping(*args)
set_box("overlapping", *args)
@clip = false
@overlapping = true
end
def inplace_cell(arg)
@cell = layout.cell(arg)
@cell ||= layout.create_cell(arg)
self
end
def inplace_select(*args)
args.each do |a|
a.is_a?(String) || raise("Invalid arguments to 'select' method - must be strings")
@sel.push(a)
end
self
end
# %DRC%
# @name select
# @brief Adds cell name expressions to the cell filters
# @synopsis source.select(filter1, filter2, ...)
# This method will construct a new source object with the given cell filters
# applied.
# Cell filters will enable or disable cells plus their subtree.
# Cells can be switched on and off, which makes the hierarchy traversal
# stop or begin delivering shapes at the given cell. The arguments of
# the select method form a sequence of enabling or disabling instructions
# using cell name pattern in the glob notation ("*" as the wildcard, like shell).
# Disabling instructions start with a "-", enabling instructions with a "+" or
# no specification.
#
# The following options are available:
#
# @ul
# @li @tt+@/tt @i name_filter @/i: Cells matching the name filter will be enabled @/li
# @li @i name_filter @/i: Same as "+name_filter" @/li
# @li @tt-@/tt @i name_filter @/i: Cells matching the name filter will be disabled @/li
# @/ul
#
# To disable the TOP cell but enabled a hypothetical cell B below the top cell, use that
# code:
#
# @code
# layout_with_selection = layout.select("-TOP", "+B")
# l1 = layout_with_selection.input(1, 0)
# ...
# @/code
#
# Please note that the sample above will deliver the children of "B" because there is
# nothing said about how to proceed with cells other than "TOP" or "B".
# The following code will just select "B" without it's children, because in the
# first "-*" selection, all cells including the children of "B" are disabled:
#
# @code
# layout_with_selection = layout.select("-*", "+B")
# l1 = layout_with_selection.input(1, 0)
# ...
# @/code
# %DRC%
# @name cell
# @brief Specifies input from a specific cell
# @synopsis source.cell(name)
# This method will create a new source that delivers shapes from the
# specified cell.
# %DRC%
# @name clip
# @brief Specifies clipped input
# @synopsis source.clip(box)
# @synopsis source.clip(p1, p2)
# @synopsis source.clip(l, b, r, t)
# Creates a source which represents a rectangular part of the
# original input. Three ways are provided to specify the rectangular
# region: a single RBA::DBox object (micron units), two RBA::DPoint
# objects (lower/left and upper/right coordinate in micron units)
# or four coordinates: left, bottom, right and top coordinate.
#
# This method will create a new source which delivers the shapes
# from that region clipped to the rectangle. A method doing the
# same but without clipping is \touching or \overlapping.
# %DRC%
# @name touching
# @brief Specifies input selected from a region in touching mode
# @synopsis source.touching(box)
# @synopsis source.touching(p1, p2)
# @synopsis source.touching(l, b, r, t)
# Like \clip, this method will create a new source delivering shapes
# from a specified rectangular region. In contrast to clip, all shapes
# touching the region with their bounding boxes are delivered as a whole
# and are not clipped. Hence shapes may extent beyond the limits of
# the specified rectangle.
#
# \overlapping is a similar method which delivers shapes overlapping
# the search region with their bounding box (and not just touching)
# %DRC%
# @name overlapping
# @brief Specifies input selected from a region in overlapping mode
# @synopsis source.overlapping(...)
# Like \clip, this method will create a new source delivering shapes
# from a specified rectangular region. In contrast to clip, all shapes
# overlapping the region with their bounding boxes are delivered as a whole
# and are not clipped. Hence shapes may extent beyond the limits of
# the specified rectangle.
#
# \touching is a similar method which delivers shapes touching
# the search region with their bounding box (without the requirement to overlap)
# export inplace_* as * out-of-place
%w(select cell clip touching overlapping).each do |f|
eval <<"CODE"
def #{f}(*args)
s = self.dup
s.inplace_#{f}(*args)
s
end
CODE
end
# %DRC%
# @name extent
# @brief Returns a layer with the bounding box of the selected layout
# @synopsis source.extent
# The extent function is useful to invert a layer:
#
# @code
# inverse_1 = extent.sized(100.0) - input(1, 0)
# @/code
def extent
layer = input
if @box
layer.insert(RBA::DBox::from_ibox(@box) * @layout.dbu)
else
layer.insert(RBA::DBox::from_ibox(@cell.bbox) * @layout.dbu)
end
layer
end
# %DRC%
# @name input
# @brief Specifies input from a source
# @synopsis source.input(layer)
# @synopsis source.input(layer, datatype)
# @synopsis source.input(layer_into)
# @synopsis source.input(filter, ...)
# Creates a layer with the shapes from the given layer of the source.
# The layer can be specified by layer and optionally datatype, by a RBA::LayerInfo
# object or by a sequence of filters.
# Filters are expressions describing ranges
# of layers and/or datatype numbers or layer names. Multiple filters
# can be given and all layers matching at least one of these filter
# expressions are joined to render the input layer for the DRC engine.
#
# Some filter expressions are:
#
# @ul
# @li @tt 1/0-255 @/tt: Datatypes 0 to 255 for layer 1 @/li
# @li @tt 1-10 @/tt: Layers 1 to 10, datatype 0 @/li
# @li @tt METAL @/tt: A layer named "METAL" @/li
# @li @tt METAL (17/0) @/tt: A layer named "METAL" or layer 17, datatype 0 (for GDS, which does
# not have names)@/li
# @/ul
#
# Layers created with "input" contain both texts and polygons. There is a subtle
# difference between flat and deep mode: in flat mode, texts are not visible in polygon
# operations. In deep mode, texts appear as small 2x2 DBU rectangles. In flat mode,
# some operations such as clipping are not fully supported for texts. Also, texts will
# vanish in most polygon operations such as booleans etc.
#
# Texts can later be selected on the layer returned by "input" with the \Layer#texts method.
#
# If you don't want to see texts, use \polygons to create an input layer with polygon data
# only. If you only want to see texts, use \labels to create an input layer with texts only.
#
# Use the global version of "input" without a source object to address the default source.
def input(*args)
layers = parse_input_layers(*args)
DRCLayer::new(@engine, @engine._cmd(@engine, :_input, @layout_var, @cell.cell_index, layers, @sel, @box, @clip, @overlapping, RBA::Shapes::SAll))
end
# %DRC%
# @name labels
# @brief Gets the labels (texts) from an input layer
# @synopsis source.labels(layer)
# @synopsis source.labels(layer, datatype)
# @synopsis source.labels(layer_into)
# @synopsis source.labels(filter, ...)
#
# Creates a layer with the labels from the given layer of the source.
#
# This method is identical to \input, but takes only texts from the given input
# layer.
#
# Use the global version of "labels" without a source object to address the default source.
def labels(*args)
layers = parse_input_layers(*args)
DRCLayer::new(@engine, @engine._cmd(@engine, :_input, @layout_var, @cell.cell_index, layers, @sel, @box, @clip, @overlapping, RBA::Shapes::STexts))
end
# %DRC%
# @name polygons
# @brief Gets the polygon shapes (or shapes that can be converted polygons) from an input layer
# @synopsis source.polygons(layer)
# @synopsis source.polygons(layer, datatype)
# @synopsis source.polygons(layer_into)
# @synopsis source.polygons(filter, ...)
#
# Creates a layer with the polygon shapes from the given layer of the source.
# With "polygon shapes" we mean all kind of shapes that can be converted to polygons.
# Those are boxes, paths and real polygons.
#
# This method is identical to \input with respect to the options supported.
#
# Use the global version of "polygons" without a source object to address the default source.
def polygons(*args)
layers = parse_input_layers(*args)
DRCLayer::new(@engine, @engine._cmd(@engine, :_input, @layout_var, @cell.cell_index, layers, @sel, @box, @clip, @overlapping, RBA::Shapes::SBoxes | RBA::Shapes::SPaths | RBA::Shapes::SPolygons | RBA::Shapes::SEdgePairs))
end
# %DRC%
# @name make_layer
# @brief Creates an empty polygon layer based on the hierarchy of the layout
# @synopsis make_layer
# This method delivers a new empty original layer.
def make_layer
layers = []
DRCLayer::new(@engine, @engine._cmd(@engine, :_input, @layout_var, @cell.cell_index, layers, @sel, @box, @clip, @overlapping, RBA::Shapes::SAll))
end
# %DRC%
# @name layers
# @brief Gets the layers the source contains
# @synopsis source.layers
# Delivers a list of RBA::LayerInfo objects representing the layers
# inside the source.
#
# One application is to read all layers from a source. In the following
# example, the "and" operation is used to perform a clip with the given
# rectangle. Note that this solution is not efficient - it's provided
# as an example only:
#
# @code
# output_cell("Clipped")
#
# clip_box = polygon_layer
# clip_box.insert(box(0.um, -4.um, 4.um, 0.um))
#
# layers.each { |l| (input(l) & clip_box).output(l) }
# @/code
def layers
@layout.layer_indices.collect { |li| @layout.get_info(li) }
end
# %DRC%
# @name path
# @brief Gets the path of the corresponding layout file or nil if there is no path
# @synopsis path
def path
@path
end
private
def parse_input_layers(*args)
layers = []
if args.size == 0
li = @layout.insert_layer(RBA::LayerInfo::new)
li && layers.push(li)
li && @tmp_layers.push(li)
elsif (args.size == 1 && args[0].is_a?(RBA::LayerInfo))
li = @layout.find_layer(args[0])
li && layers.push(li)
elsif (args.size == 1 || args.size == 2) && args[0].is_a?(1.class)
li = @layout.find_layer(args[0], args[1] || 0)
li && layers.push(li)
else
args.each do |a|
if a.is_a?(String)
# use the LayerMap class to fetch the matching layers
lm = RBA::LayerMap::new
lm.map(a, 0)
@layout.layer_indices.each do |li|
if lm.is_mapped?(@layout.get_info(li))
layers.push(li)
end
end
end
end
end
layers
end
end
end

View File

@ -0,0 +1,116 @@
# $autorun-early
module DRC
# A wrapper for a named value which is stored in
# a variable for delayed execution
class DRCVar
def initialize(name)
@name = name
end
def inspect
@name
end
def to_s
@name
end
end
# A wrapper for the sizing mode value
class DRCSizingMode
attr_accessor :value
def initialize(v)
self.value = v
end
end
# A wrapper for the join flag for extended
class DRCJoinFlag
attr_accessor :value
def initialize(v)
self.value = v
end
end
# A wrapper for the angle limit
# The purpose of this wrapper is to identify the
# angle limit specification
class DRCAngleLimit
attr_accessor :value
def initialize(v)
self.value = v
end
end
# A wrapper for a metrics constant
# The purpose of this wrapper is to identify the
# metrics constant by the class.
class DRCMetrics
attr_accessor :value
def initialize(v)
self.value = v
end
end
# A wrapper for the "whole edges" flag for
# the DRC functions. The purpose of this class
# is to identify the value by the class.
class DRCWholeEdges
attr_accessor :value
def initialize(v)
self.value = v
end
end
# A wrapper for the "as_dots" or "as_boxes" flag for
# some DRC functions. The purpose of this class
# is to identify the value by the class.
class DRCAsDots
attr_accessor :value
def initialize(v)
self.value = v
end
end
# A wrapper for a glob-pattern style text selection for
# some DRC functions. The purpose of this class
# is to identify the value by the class.
class DRCPattern
attr_accessor :as_pattern
attr_accessor :pattern
def initialize(f, p)
self.as_pattern = f
self.pattern = p
end
end
# A wrapper for a pair of limit values
# This class is used to identify projection limits for DRC
# functions
class DRCProjectionLimits
attr_accessor :min
attr_accessor :max
def initialize(*a)
if a.size > 2 || a.size == 0
raise("A projection limits specification requires a maximum of two values and at least one argument")
elsif a.size == 1
if !a[0].is_a?(Range) || (!a[0].min.is_a?(Float) && !a[0].min.is_a?(1.class))
raise("A projection limit requires an interval of two length values or two individual length values")
end
self.min = a[0].min
self.max = a[0].max
elsif a.size == 2
if a[0] && !a[0].is_a?(Float) && !a[0].is_a?(1.class)
raise("First argument to a projection limit must be either nil or a length value")
end
if a[1] && !a[1].is_a?(Float) && !a[1].is_a?(1.class)
raise("Second argument to a projection limit must be either nil or a length value")
end
self.min = a[0]
self.max = a[1]
end
end
end
end

View File

@ -0,0 +1,52 @@
<?xml version="1.0" encoding="utf-8"?>
<klayout-macro>
<description/>
<version/>
<category/>
<prolog/>
<epilog/>
<doc/>
<autorun>true</autorun>
<autorun-early>false</autorun-early>
<shortcut/>
<show-in-menu>false</show-in-menu>
<group-name/>
<menu-path/>
<interpreter>ruby</interpreter>
<dsl-interpreter-name/>
<text>
module DRC
# Installs the home menu entries (needs to be done on autorun, not autorun-early)
if RBA::Application::instance &amp;&amp; RBA::Application::instance.main_window
cat = "drc"
name = cat.upcase
mw = RBA::Application::instance.main_window
mw.menu.insert_menu("tools_menu.verification_group+", cat, name)
@new_action = RBA::Action::new
@new_action.title = "New #{name} Script"
@new_action.on_triggered do
mw.show_macro_editor(cat, true)
end
mw.menu.insert_item("tools_menu.#{cat}.end", "new_script", @new_action)
@edit_action = RBA::Action::new
@edit_action.title = "Edit #{name} Script"
@edit_action.on_triggered do
mw.show_macro_editor(cat, false)
end
mw.menu.insert_item("tools_menu.#{cat}.end", "edit_script", @edit_action)
end
end
</text>
</klayout-macro>

View File

@ -68,13 +68,7 @@ module DRC
register("drc-dsl-xml")
# create a template for the macro editor:
mt = create_template("drc")
mt.text = "# Read about DRC scripts in the User Manual under \"Design Rule Check (DRC)\"\n# This is a sample:\n\npoly = input(6)\nactive = input(1)\ngate = poly &amp; active\ngate.width(0.25.micron).output(100, 0)"
mt.show_in_menu = true
mt.menu_path = "tools_menu.drc.end"
mt.group_name = "drc_scripts"
mt.description = "General;;DRC script (*.lydrc)\nA DRC script using KLayout's DRC language"
mt.category = "drc"
create_template(":/drc-templates/drc.lym")
end

View File

@ -1,6 +1,15 @@
<RCC>
<qresource prefix="/built-in-macros">
<file alias="drc.lym">built-in-macros/drc.lym</file>
<file alias="_drc_engine.rb">built-in-macros/_drc_engine.rb</file>
<file alias="_drc_layer.rb">built-in-macros/_drc_layer.rb</file>
<file alias="_drc_netter.rb">built-in-macros/_drc_netter.rb</file>
<file alias="_drc_patch.rb">built-in-macros/_drc_patch.rb</file>
<file alias="_drc_source.rb">built-in-macros/_drc_source.rb</file>
<file alias="_drc_tags.rb">built-in-macros/_drc_tags.rb</file>
<file alias="drc_interpreters.lym">built-in-macros/drc_interpreters.lym</file>
<file alias="drc_install.lym">built-in-macros/drc_install.lym</file>
</qresource>
<qresource prefix="/drc-templates">
<file alias="drc.lym">templates/drc.lym</file>
</qresource>
</RCC>

View File

@ -0,0 +1,28 @@
<?xml version="1.0" encoding="utf-8"?>
<klayout-macro>
<description>General;;DRC script (*.lydrc)\nA DRC script using KLayout's DRC language</description>
<version/>
<category>drc</category>
<prolog/>
<epilog/>
<doc/>
<autorun>false</autorun>
<autorun-early>false</autorun-early>
<shortcut/>
<show-in-menu>true</show-in-menu>
<group-name>drc_scripts</group-name>
<menu-path>tools_menu.drc.end</menu-path>
<interpreter>ruby</interpreter>
<dsl-interpreter-name/>
<text>
# Read about DRC scripts in the User Manual in "Design Rule Check (DRC)"
# This is a sample:
poly = input(6)
active = input(1)
gate = poly &amp; active
gate.width(0.25.micron).output(100, 0)
</text>
</klayout-macro>

View File

@ -964,7 +964,8 @@ public:
ClassBase *non_const_pcls = const_cast<ClassBase *> (cls_decl<P> ());
non_const_pcls->add_child_class (this);
return Class<X, Adapted>::consolidate ();
// no longer required as it is a child now.
return false;
}
};

View File

@ -558,6 +558,104 @@ ClassBase::merge_declarations ()
}
}
static void collect_classes (const gsi::ClassBase *cls, std::list<const gsi::ClassBase *> &unsorted_classes)
{
unsorted_classes.push_back (cls);
for (tl::weak_collection<gsi::ClassBase>::const_iterator cc = cls->begin_child_classes (); cc != cls->end_child_classes (); ++cc) {
tl_assert (cc->declaration () != 0);
collect_classes (cc.operator-> (), unsorted_classes);
}
}
std::list<const gsi::ClassBase *>
ClassBase::classes_in_definition_order (const char *mod_name)
{
std::set<const gsi::ClassBase *> taken;
std::list<const gsi::ClassBase *> sorted_classes;
std::list<const gsi::ClassBase *> unsorted_classes;
for (gsi::ClassBase::class_iterator c = gsi::ClassBase::begin_classes (); c != gsi::ClassBase::end_classes (); ++c) {
if (! mod_name || c->module () == mod_name) {
// only handle top-level classed from the requested modules
// (children or base classes from outside the module may be part of the returned list!)
collect_classes (c.operator-> (), unsorted_classes);
} else {
// we assume that these classes are taken by another run (i.e. "import x" in Python)
taken.insert (c.operator-> ());
}
}
while (! unsorted_classes.empty ()) {
bool any = false;
std::list<const gsi::ClassBase *> more_classes;
for (std::list<const gsi::ClassBase *>::const_iterator c = unsorted_classes.begin (); c != unsorted_classes.end (); ++c) {
// don't handle classes twice
if (taken.find (*c) != taken.end ()) {
continue;
}
if ((*c)->declaration () != *c && taken.find ((*c)->declaration ()) == taken.end ()) {
// can't produce this class yet - it's a reference to another class which is not produced yet.
tl_assert ((*c)->declaration () != 0);
more_classes.push_back (*c);
continue;
}
if ((*c)->parent () != 0 && taken.find ((*c)->parent ()) == taken.end ()) {
// can't produce this class yet - it's a child of a parent that is not produced yet.
more_classes.push_back (*c);
continue;
}
if ((*c)->base () != 0 && taken.find ((*c)->base ()) == taken.end ()) {
// can't produce this class yet. The base class needs to be handled first.
more_classes.push_back (*c);
continue;
}
sorted_classes.push_back (*c);
taken.insert (*c);
any = true;
}
if (! any && ! more_classes.empty ()) {
for (std::list<const gsi::ClassBase *>::const_iterator c = more_classes.begin (); c != more_classes.end (); ++c) {
// don't handle classes twice
if (taken.find (*c) != taken.end ()) {
// not considered.
} else if ((*c)->declaration () != *c && taken.find ((*c)->declaration ()) == taken.end ()) {
// can't produce this class yet - it's a child of a parent that is not produced yet.
tl::error << tl::sprintf ("class %s.%s refers to another class (%s.%s) which is not available", (*c)->module (), (*c)->name (), (*c)->declaration ()->module (), (*c)->declaration ()->name ());
} else if ((*c)->parent () != 0 && taken.find ((*c)->parent ()) == taken.end ()) {
// can't produce this class yet - it's a child of a parent that is not produced yet.
tl::error << tl::sprintf ("parent of class %s.%s not available (%s.%s)", (*c)->module (), (*c)->name (), (*c)->parent ()->module (), (*c)->parent ()->name ());
} else if ((*c)->base () != 0 && taken.find ((*c)->base ()) == taken.end ()) {
// can't produce this class yet. The base class needs to be handled first.
tl::error << tl::sprintf ("base of class %s.%s not available (%s.%s)", (*c)->module (), (*c)->name (), (*c)->base ()->module (), (*c)->base ()->name ());
}
}
// prevent infinite recursion
throw tl::Exception ("Internal error: infinite recursion on class building. See error log for analysis");
}
unsorted_classes.swap (more_classes);
}
return sorted_classes;
}
void
ClassBase::initialize ()
{

View File

@ -243,6 +243,20 @@ public:
return new_collection ().end ();
}
/**
* @brief Returns a list of all classes in definition order
*
* Definition order is:
* - No duplicate class entries
* - Base classes before their derived classes
* - Child classes after their parent classes
*
* If a module name is given, only top-level classes from this
* module will be considered. However, the list may also include
* base classes or child classes from outside the module.
*/
static std::list<const gsi::ClassBase *> classes_in_definition_order (const char *mod_name = 0);
/**
* @brief Iterates the methods (begin)
*/

View File

@ -280,25 +280,14 @@ EnumSpecs<E> enum_const (const std::string &estr, E evalue, const std::string &d
}
/**
* @brief The basic declaration class
* This is a gsi::Class specialization which implements an enum declaration.
* The use is this:
*
* @code
* gsi::Enum<E> e_enum ("A description",
* gsi::enum_const ("a", E::a, "description of a") +
* gsi::enum_const ("b", E::b) +
* ...
* );
* @endcode
* @brief A helper class for the enum implementation
*/
template <class E>
class Enum
: public Class<EnumAdaptor<E>, E>
class EnumImpl
{
public:
Enum (const std::string &module, const std::string &name, const EnumSpecs<E> &specs, const std::string &doc = std::string ())
: Class<EnumAdaptor<E>, E> (module, name, specs.methods (), doc), m_specs (specs)
EnumImpl (const EnumSpecs<E> &specs)
: m_specs (specs)
{
}
@ -331,6 +320,62 @@ private:
EnumSpecs<E> m_specs;
};
/**
* @brief The basic declaration class
* This is a gsi::Class specialization which implements an enum declaration.
* The use is this:
*
* @code
* gsi::Enum<E> e_enum ("A description",
* gsi::enum_const ("a", E::a, "description of a") +
* gsi::enum_const ("b", E::b) +
* ...
* );
* @endcode
*/
template <class E>
class Enum
: public Class<EnumAdaptor<E>, E>, public EnumImpl<E>
{
public:
Enum (const std::string &module, const std::string &name, const EnumSpecs<E> &specs, const std::string &doc = std::string ())
: Class<EnumAdaptor<E>, E> (module, name, specs.methods (), doc), EnumImpl<E> (specs)
{
}
};
/**
* @brief An enum declaration as a child class
*
* @code
* gsi::EnumIn<P, E> e_enum ("A description",
* gsi::enum_const ("a", E::a, "description of a") +
* gsi::enum_const ("b", E::b) +
* ...
* );
* @endcode
*/
template <class P, class E>
class EnumIn
: public Enum<E>
{
public:
EnumIn (const std::string &module, const std::string &name, const EnumSpecs<E> &specs, const std::string &doc = std::string ())
: Enum<E> (module, name, specs, doc)
{
}
virtual bool consolidate () const
{
// TODO: ugly const cast
ClassBase *non_const_pcls = const_cast<ClassBase *> (cls_decl<P> ());
non_const_pcls->add_child_class (this);
// no longer required as it is a child now.
return false;
}
};
#if defined(HAVE_QT)
template <class E>

View File

@ -398,7 +398,7 @@ private:
// pointer iterator method descriptors
template <class X, class R _COMMA _TMPLARG>
template <class X, class R _COMMA _TMPLARG, class Transfer = gsi::arg_default_return_value_preference>
class _NAME(MethodPtrIter)
: public MethodSpecificBase <X>
{
@ -422,7 +422,7 @@ public:
{
this->clear ();
_ADDARGS
this->template set_return<iter_adaptor_type> ();
this->template set_return<iter_adaptor_type, Transfer> ();
}
virtual MethodBase *clone () const
@ -447,7 +447,7 @@ private:
_ARGSPECMEM
};
template <class X, class R _COMMA _TMPLARG>
template <class X, class R _COMMA _TMPLARG, class Transfer = gsi::arg_default_return_value_preference>
class _NAME(MethodPtrConstIter)
: public MethodSpecificBase <X>
{
@ -471,7 +471,7 @@ public:
{
this->clear ();
_ADDARGS
this->template set_return<iter_adaptor_type> ();
this->template set_return<iter_adaptor_type, Transfer> ();
}
virtual MethodBase *clone () const
@ -496,7 +496,7 @@ private:
_ARGSPECMEM
};
template <class X, class R _COMMA _TMPLARG>
template <class X, class R _COMMA _TMPLARG, class Transfer = gsi::arg_default_return_value_preference>
class _NAME(ConstMethodPtrIter)
: public MethodSpecificBase <X>
{
@ -520,7 +520,7 @@ public:
{
this->clear ();
_ADDARGS
this->template set_return<iter_adaptor_type> ();
this->template set_return<iter_adaptor_type, Transfer> ();
}
virtual MethodBase *clone () const
@ -545,7 +545,7 @@ private:
_ARGSPECMEM
};
template <class X, class R _COMMA _TMPLARG>
template <class X, class R _COMMA _TMPLARG, class Transfer = gsi::arg_default_return_value_preference>
class _NAME(ConstMethodPtrConstIter)
: public MethodSpecificBase <X>
{
@ -569,7 +569,7 @@ public:
{
this->clear ();
_ADDARGS
this->template set_return<iter_adaptor_type> ();
this->template set_return<iter_adaptor_type, Transfer> ();
}
virtual MethodBase *clone () const
@ -594,7 +594,7 @@ private:
_ARGSPECMEM
};
template <class X, class R _COMMA _TMPLARG>
template <class X, class R _COMMA _TMPLARG, class Transfer = gsi::arg_default_return_value_preference>
class _NAME(ExtMethodPtrIter)
: public MethodSpecificBase <X>
{
@ -618,7 +618,7 @@ public:
{
this->clear ();
_ADDARGS
this->template set_return<iter_adaptor_type> ();
this->template set_return<iter_adaptor_type, Transfer> ();
}
virtual MethodBase *clone () const
@ -643,7 +643,7 @@ private:
_ARGSPECMEM
};
template <class X, class R _COMMA _TMPLARG>
template <class X, class R _COMMA _TMPLARG, class Transfer = gsi::arg_default_return_value_preference>
class _NAME(ExtMethodPtrConstIter)
: public MethodSpecificBase <X>
{
@ -667,7 +667,7 @@ public:
{
this->clear ();
_ADDARGS
this->template set_return<iter_adaptor_type> ();
this->template set_return<iter_adaptor_type, Transfer> ();
}
virtual MethodBase *clone () const
@ -692,7 +692,7 @@ private:
_ARGSPECMEM
};
template <class R _COMMA _TMPLARG>
template <class R _COMMA _TMPLARG, class Transfer = gsi::arg_default_return_value_preference>
class _NAME(StaticMethodPtrIter)
: public StaticMethodBase
{
@ -716,7 +716,7 @@ public:
{
this->clear ();
_ADDARGS
this->template set_return<iter_adaptor_type> ();
this->template set_return<iter_adaptor_type, Transfer> ();
}
virtual MethodBase *clone () const
@ -741,7 +741,7 @@ private:
_ARGSPECMEM
};
template <class R _COMMA _TMPLARG>
template <class R _COMMA _TMPLARG, class Transfer = gsi::arg_default_return_value_preference>
class _NAME(StaticMethodPtrConstIter)
: public StaticMethodBase
{
@ -765,7 +765,7 @@ public:
{
this->clear ();
_ADDARGS
this->template set_return<iter_adaptor_type> ();
this->template set_return<iter_adaptor_type, Transfer> ();
}
virtual MethodBase *clone () const
@ -792,7 +792,7 @@ private:
// pair iterator method descriptors
template <class X, class I _COMMA _TMPLARG>
template <class X, class I _COMMA _TMPLARG, class Transfer = gsi::arg_default_return_value_preference>
class _NAME(MethodBiIter)
: public MethodSpecificBase <X>
{
@ -815,7 +815,7 @@ public:
{
this->clear ();
_ADDARGS
this->template set_return<iter_adaptor_type> ();
this->template set_return<iter_adaptor_type, Transfer> ();
}
virtual MethodBase *clone () const
@ -840,7 +840,7 @@ private:
_ARGSPECMEM
};
template <class X, class I _COMMA _TMPLARG>
template <class X, class I _COMMA _TMPLARG, class Transfer = gsi::arg_default_return_value_preference>
class _NAME(ConstMethodBiIter)
: public MethodSpecificBase <X>
{
@ -863,7 +863,7 @@ public:
{
this->clear ();
_ADDARGS
this->template set_return<iter_adaptor_type> ();
this->template set_return<iter_adaptor_type, Transfer> ();
}
virtual MethodBase *clone () const
@ -888,7 +888,7 @@ private:
_ARGSPECMEM
};
template <class X, class I _COMMA _TMPLARG>
template <class X, class I _COMMA _TMPLARG, class Transfer = gsi::arg_default_return_value_preference>
class _NAME(ExtMethodBiIter)
: public MethodSpecificBase <X>
{
@ -911,7 +911,7 @@ public:
{
this->clear ();
_ADDARGS
this->template set_return<iter_adaptor_type> ();
this->template set_return<iter_adaptor_type, Transfer> ();
}
virtual MethodBase *clone () const
@ -938,7 +938,7 @@ private:
_ARGSPECMEM
};
template <class I _COMMA _TMPLARG>
template <class I _COMMA _TMPLARG, class Transfer = gsi::arg_default_return_value_preference>
class _NAME(StaticMethodBiIter)
: public StaticMethodBase
{
@ -961,7 +961,7 @@ public:
{
this->clear ();
_ADDARGS
this->template set_return<iter_adaptor_type> ();
this->template set_return<iter_adaptor_type, Transfer> ();
}
virtual MethodBase *clone () const
@ -988,7 +988,7 @@ private:
// free iterator method descriptors
template <class X, class I _COMMA _TMPLARG>
template <class X, class I _COMMA _TMPLARG, class Transfer = gsi::arg_default_return_value_preference>
class _NAME(MethodFreeIter)
: public MethodSpecificBase <X>
{
@ -1011,7 +1011,7 @@ public:
{
this->clear ();
_ADDARGS
this->template set_return<iter_adaptor_type> ();
this->template set_return<iter_adaptor_type, Transfer> ();
}
virtual MethodBase *clone () const
@ -1035,7 +1035,7 @@ private:
_ARGSPECMEM
};
template <class X, class I _COMMA _TMPLARG>
template <class X, class I _COMMA _TMPLARG, class Transfer = gsi::arg_default_return_value_preference>
class _NAME(ConstMethodFreeIter)
: public MethodSpecificBase <X>
{
@ -1058,7 +1058,7 @@ public:
{
this->clear ();
_ADDARGS
this->template set_return<iter_adaptor_type> ();
this->template set_return<iter_adaptor_type, Transfer> ();
}
virtual MethodBase *clone () const
@ -1082,7 +1082,7 @@ private:
_ARGSPECMEM
};
template <class X, class I _COMMA _TMPLARG>
template <class X, class I _COMMA _TMPLARG, class Transfer = gsi::arg_default_return_value_preference>
class _NAME(ExtMethodFreeIter)
: public MethodSpecificBase <X>
{
@ -1105,7 +1105,7 @@ public:
{
this->clear ();
_ADDARGS
this->template set_return<iter_adaptor_type> ();
this->template set_return<iter_adaptor_type, Transfer> ();
}
virtual MethodBase *clone () const
@ -1129,7 +1129,7 @@ private:
_ARGSPECMEM
};
template <class I _COMMA _TMPLARG>
template <class I _COMMA _TMPLARG, class Transfer = gsi::arg_default_return_value_preference>
class _NAME(StaticMethodFreeIter)
: public StaticMethodBase
{
@ -1152,7 +1152,7 @@ public:
{
this->clear ();
_ADDARGS
this->template set_return<iter_adaptor_type> ();
this->template set_return<iter_adaptor_type, Transfer> ();
}
virtual MethodBase *clone () const
@ -1289,7 +1289,7 @@ method (const std::string &name, R (X::*m) (_FUNCARGLIST), const std::string &do
template <class X, class R _COMMA _TMPLARG, class Transfer>
Methods
method (const std::string &name, R (X::*m) (_FUNCARGLIST), const std::string &doc = std::string ())
method (const std::string &name, Transfer, R (X::*m) (_FUNCARGLIST), const std::string &doc = std::string ())
{
return Methods (new _NAME(Method) <X, R _COMMA _FUNCARGLIST, Transfer> (name, m, doc));
}
@ -1304,7 +1304,7 @@ method (const std::string &name, R (X::*m) (_FUNCARGLIST) _COMMA _ARGSPECS, cons
template <class X, class R _COMMA _TMPLARG _COMMA _TMPLARGSPECS, class Transfer>
Methods
method (const std::string &name, R (X::*m) (_FUNCARGLIST) _COMMA _ARGSPECS, const std::string &doc = std::string ())
method (const std::string &name, Transfer, R (X::*m) (_FUNCARGLIST) _COMMA _ARGSPECS, const std::string &doc = std::string ())
{
return Methods ((new _NAME(Method) <X, R _COMMA _FUNCARGLIST, Transfer> (name, m, doc))->add_args (_ARGSPECARGS));
}
@ -1314,7 +1314,7 @@ template <class X, class R _COMMA _TMPLARG>
Methods
factory (const std::string &name, R *(X::*m) (_FUNCARGLIST), const std::string &doc = std::string ())
{
return Methods (new _NAME(Method) <X, R * _COMMA _FUNCARGLIST, gsi::arg_pass_ownership> (name, m, doc));
return Methods (new _NAME(Method) <X, R * _COMMA _FUNCARGLIST, gsi::return_new_object> (name, m, doc));
}
#if _COUNT != 0
@ -1322,7 +1322,7 @@ template <class X, class R _COMMA _TMPLARG _COMMA _TMPLARGSPECS>
Methods
factory (const std::string &name, R *(X::*m) (_FUNCARGLIST) _COMMA _ARGSPECS, const std::string &doc = std::string ())
{
return Methods ((new _NAME(Method) <X, R * _COMMA _FUNCARGLIST, gsi::arg_pass_ownership> (name, m, doc))->add_args (_ARGSPECARGS));
return Methods ((new _NAME(Method) <X, R * _COMMA _FUNCARGLIST, gsi::return_new_object> (name, m, doc))->add_args (_ARGSPECARGS));
}
#endif
@ -1335,7 +1335,7 @@ method_ext (const std::string &name, R (*xm) (X * _COMMA _FUNCARGLIST), const st
template <class X, class R _COMMA _TMPLARG, class Transfer>
Methods
method_ext (const std::string &name, R (*xm) (X * _COMMA _FUNCARGLIST), const std::string &doc = std::string ())
method_ext (const std::string &name, Transfer, R (*xm) (X * _COMMA _FUNCARGLIST), const std::string &doc = std::string ())
{
return Methods (new _NAME(ExtMethod) <X, R _COMMA _FUNCARGLIST, Transfer> (name, xm, doc));
}
@ -1350,7 +1350,7 @@ method_ext (const std::string &name, R (*xm) (X * _COMMA _FUNCARGLIST) _COMMA _A
template <class X, class R _COMMA _TMPLARG _COMMA _TMPLARGSPECS, class Transfer>
Methods
method_ext (const std::string &name, R (*xm) (X * _COMMA _FUNCARGLIST) _COMMA _ARGSPECS, const std::string &doc = std::string ())
method_ext (const std::string &name, Transfer, R (*xm) (X * _COMMA _FUNCARGLIST) _COMMA _ARGSPECS, const std::string &doc = std::string ())
{
return Methods ((new _NAME(ExtMethod) <X, R _COMMA _FUNCARGLIST, Transfer> (name, xm, doc))->add_args (_ARGSPECARGS));
}
@ -1360,7 +1360,7 @@ template <class X, class R _COMMA _TMPLARG>
Methods
factory_ext (const std::string &name, R *(*xm) (X * _COMMA _FUNCARGLIST), const std::string &doc = std::string ())
{
return Methods (new _NAME(ExtMethod) <X, R * _COMMA _FUNCARGLIST, gsi::arg_pass_ownership> (name, xm, doc));
return Methods (new _NAME(ExtMethod) <X, R * _COMMA _FUNCARGLIST, gsi::return_new_object> (name, xm, doc));
}
#if _COUNT != 0
@ -1368,7 +1368,7 @@ template <class X, class R _COMMA _TMPLARG _COMMA _TMPLARGSPECS>
Methods
factory_ext (const std::string &name, R *(*xm) (X * _COMMA _FUNCARGLIST) _COMMA _ARGSPECS, const std::string &doc = std::string ())
{
return Methods ((new _NAME(ExtMethod) <X, R * _COMMA _FUNCARGLIST, gsi::arg_pass_ownership> (name, xm, doc))->add_args (_ARGSPECARGS));
return Methods ((new _NAME(ExtMethod) <X, R * _COMMA _FUNCARGLIST, gsi::return_new_object> (name, xm, doc))->add_args (_ARGSPECARGS));
}
#endif
@ -1376,7 +1376,7 @@ template <class X _COMMA _TMPLARG>
Methods
constructor (const std::string &name, X *(*m) (_FUNCARGLIST), const std::string &doc = std::string ())
{
return Methods (new _NAME(StaticMethod) <X * _COMMA _FUNCARGLIST, gsi::arg_pass_ownership> (name, m, doc));
return Methods (new _NAME(StaticMethod) <X * _COMMA _FUNCARGLIST, gsi::return_new_object> (name, m, doc));
}
#if _COUNT != 0
@ -1384,7 +1384,7 @@ template <class X _COMMA _TMPLARG _COMMA _TMPLARGSPECS>
Methods
constructor (const std::string &name, X *(*m) (_FUNCARGLIST) _COMMA _ARGSPECS, const std::string &doc = std::string ())
{
return Methods ((new _NAME(StaticMethod) <X * _COMMA _FUNCARGLIST, gsi::arg_pass_ownership> (name, m, doc))->add_args (_ARGSPECARGS));
return Methods ((new _NAME(StaticMethod) <X * _COMMA _FUNCARGLIST, gsi::return_new_object> (name, m, doc))->add_args (_ARGSPECARGS));
}
#endif
@ -1397,7 +1397,7 @@ method (const std::string &name, R (*m) (_FUNCARGLIST), const std::string &doc =
template <class R _COMMA _TMPLARG, class Transfer>
Methods
method (const std::string &name, R (*m) (_FUNCARGLIST), const std::string &doc = std::string ())
method (const std::string &name, Transfer, R (*m) (_FUNCARGLIST), const std::string &doc = std::string ())
{
return Methods (new _NAME(StaticMethod) <R _COMMA _FUNCARGLIST, Transfer> (name, m, doc));
}
@ -1412,7 +1412,7 @@ method (const std::string &name, R (*m) (_FUNCARGLIST) _COMMA _ARGSPECS, const s
template <class R _COMMA _TMPLARG _COMMA _TMPLARGSPECS, class Transfer>
Methods
method (const std::string &name, R (*m) (_FUNCARGLIST) _COMMA _ARGSPECS, const std::string &doc = std::string ())
method (const std::string &name, Transfer, R (*m) (_FUNCARGLIST) _COMMA _ARGSPECS, const std::string &doc = std::string ())
{
return Methods ((new _NAME(StaticMethod) <R _COMMA _FUNCARGLIST, Transfer> (name, m, doc))->add_args (_ARGSPECARGS));
}
@ -1422,7 +1422,7 @@ template <class R _COMMA _TMPLARG>
Methods
factory (const std::string &name, R *(*m) (_FUNCARGLIST), const std::string &doc = std::string ())
{
return Methods (new _NAME(StaticMethod) <R * _COMMA _FUNCARGLIST, gsi::arg_pass_ownership> (name, m, doc));
return Methods (new _NAME(StaticMethod) <R * _COMMA _FUNCARGLIST, gsi::return_new_object> (name, m, doc));
}
#if _COUNT != 0
@ -1430,7 +1430,7 @@ template <class R _COMMA _TMPLARG _COMMA _TMPLARGSPECS>
Methods
factory (const std::string &name, R *(*m) (_FUNCARGLIST) _COMMA _ARGSPECS, const std::string &doc = std::string ())
{
return Methods ((new _NAME(StaticMethod) <R * _COMMA _FUNCARGLIST, gsi::arg_pass_ownership> (name, m, doc))->add_args (_ARGSPECARGS));
return Methods ((new _NAME(StaticMethod) <R * _COMMA _FUNCARGLIST, gsi::return_new_object> (name, m, doc))->add_args (_ARGSPECARGS));
}
#endif
@ -1450,11 +1450,18 @@ callback (const std::string &name, R (X::*m) (_FUNCARGLIST), Callback X::*cb _CO
}
#endif
template <class X, class R _COMMA _TMPLARG _COMMA _TMPLARGSPECS, class Transfer>
Methods
callback (const std::string &name, Transfer, R (X::*m) (_FUNCARGLIST), Callback X::*cb _COMMA _ARGSPECS, const std::string &doc = std::string ())
{
return Methods ((new _NAME(Method) <X, R _COMMA _FUNCARGLIST> (name, m, doc, cb))->add_args (_ARGSPECARGS));
}
template <class X, class R _COMMA _TMPLARG>
Methods
factory_callback (const std::string &name, R (X::*m) (_FUNCARGLIST), Callback X::*cb, const std::string &doc = std::string ())
{
return Methods (new _NAME(Method) <X, R _COMMA _FUNCARGLIST, gsi::arg_pass_ownership> (name, m, doc, cb));
return Methods (new _NAME(Method) <X, R _COMMA _FUNCARGLIST, gsi::return_new_object> (name, m, doc, cb));
}
#if _COUNT != 0
@ -1462,7 +1469,7 @@ template <class X, class R _COMMA _TMPLARG _COMMA _TMPLARGSPECS>
Methods
factory_callback (const std::string &name, R (X::*m) (_FUNCARGLIST), Callback X::*cb _COMMA _ARGSPECS, const std::string &doc = std::string ())
{
return Methods ((new _NAME(Method) <X, R _COMMA _FUNCARGLIST, gsi::arg_pass_ownership> (name, m, doc, cb))->add_args (_ARGSPECARGS));
return Methods ((new _NAME(Method) <X, R _COMMA _FUNCARGLIST, gsi::return_new_object> (name, m, doc, cb))->add_args (_ARGSPECARGS));
}
#endif
@ -1475,7 +1482,7 @@ method (const std::string &name, R (X::*m) (_FUNCARGLIST) const, const std::stri
template <class X, class R _COMMA _TMPLARG, class Transfer>
Methods
method (const std::string &name, R (X::*m) (_FUNCARGLIST) const, const std::string &doc = std::string ())
method (const std::string &name, Transfer, R (X::*m) (_FUNCARGLIST) const, const std::string &doc = std::string ())
{
return Methods (new _NAME(ConstMethod) <X, R _COMMA _FUNCARGLIST, Transfer> (name, m, doc));
}
@ -1490,7 +1497,7 @@ method (const std::string &name, R (X::*m) (_FUNCARGLIST) const _COMMA _ARGSPECS
template <class X, class R _COMMA _TMPLARG _COMMA _TMPLARGSPECS, class Transfer>
Methods
method (const std::string &name, R (X::*m) (_FUNCARGLIST) const _COMMA _ARGSPECS, const std::string &doc = std::string ())
method (const std::string &name, Transfer, R (X::*m) (_FUNCARGLIST) const _COMMA _ARGSPECS, const std::string &doc = std::string ())
{
return Methods ((new _NAME(ConstMethod) <X, R _COMMA _FUNCARGLIST, Transfer> (name, m, doc))->add_args (_ARGSPECARGS));
}
@ -1500,7 +1507,7 @@ template <class X, class R _COMMA _TMPLARG>
Methods
factory (const std::string &name, R *(X::*m) (_FUNCARGLIST) const, const std::string &doc = std::string ())
{
return Methods (new _NAME(ConstMethod) <X, R * _COMMA _FUNCARGLIST, gsi::arg_pass_ownership> (name, m, doc));
return Methods (new _NAME(ConstMethod) <X, R * _COMMA _FUNCARGLIST, gsi::return_new_object> (name, m, doc));
}
#if _COUNT != 0
@ -1508,7 +1515,7 @@ template <class X, class R _COMMA _TMPLARG _COMMA _TMPLARGSPECS>
Methods
factory (const std::string &name, R *(X::*m) (_FUNCARGLIST) const _COMMA _ARGSPECS, const std::string &doc = std::string ())
{
return Methods ((new _NAME(ConstMethod) <X, R * _COMMA _FUNCARGLIST, gsi::arg_pass_ownership> (name, m, doc))->add_args (_ARGSPECARGS));
return Methods ((new _NAME(ConstMethod) <X, R * _COMMA _FUNCARGLIST, gsi::return_new_object> (name, m, doc))->add_args (_ARGSPECARGS));
}
#endif
@ -1528,11 +1535,18 @@ callback (const std::string &name, R (X::*m) (_FUNCARGLIST) const, Callback X::*
}
#endif
template <class X, class R _COMMA _TMPLARG _COMMA _TMPLARGSPECS, class Transfer>
Methods
callback (const std::string &name, Transfer, R (X::*m) (_FUNCARGLIST) const, Callback X::*cb _COMMA _ARGSPECS, const std::string &doc = std::string ())
{
return Methods ((new _NAME(ConstMethod) <X, R _COMMA _FUNCARGLIST, Transfer> (name, m, doc, cb))->add_args (_ARGSPECARGS));
}
template <class X, class R _COMMA _TMPLARG>
Methods
factory_callback (const std::string &name, R (X::*m) (_FUNCARGLIST) const, Callback X::*cb, const std::string &doc = std::string ())
{
return Methods (new _NAME(ConstMethod) <X, R _COMMA _FUNCARGLIST, gsi::arg_pass_ownership> (name, m, doc, cb));
return Methods (new _NAME(ConstMethod) <X, R _COMMA _FUNCARGLIST, gsi::return_new_object> (name, m, doc, cb));
}
#if _COUNT != 0
@ -1540,7 +1554,7 @@ template <class X, class R _COMMA _TMPLARG _COMMA _TMPLARGSPECS>
Methods
factory_callback (const std::string &name, R (X::*m) (_FUNCARGLIST) const, Callback X::*cb _COMMA _ARGSPECS, const std::string &doc = std::string ())
{
return Methods ((new _NAME(ConstMethod) <X, R _COMMA _FUNCARGLIST, gsi::arg_pass_ownership> (name, m, doc, cb))->add_args (_ARGSPECARGS));
return Methods ((new _NAME(ConstMethod) <X, R _COMMA _FUNCARGLIST, gsi::return_new_object> (name, m, doc, cb))->add_args (_ARGSPECARGS));
}
#endif
@ -1562,6 +1576,13 @@ iterator (const std::string &name, R *(*b) (_FUNCARGLIST), R *(*e) (_FUNCARGLIST
}
#endif
template <class R _COMMA _TMPLARG _COMMA _TMPLARGSPECS, class Transfer>
Methods
iterator (const std::string &name, Transfer, R *(*b) (_FUNCARGLIST), R *(*e) (_FUNCARGLIST) _COMMA _ARGSPECS, const std::string &doc = std::string ())
{
return Methods ((new _NAME(StaticMethodPtrIter) <R _COMMA _FUNCARGLIST, Transfer> (name, b, e, doc))->add_args (_ARGSPECARGS));
}
template <class R _COMMA _TMPLARG>
Methods
iterator (const std::string &name, R const *(*b) (_FUNCARGLIST), R const *(*e) (_FUNCARGLIST), const std::string &doc = std::string ())
@ -1578,6 +1599,13 @@ iterator (const std::string &name, R const *(*b) (_FUNCARGLIST), R const *(*e) (
}
#endif
template <class R _COMMA _TMPLARG _COMMA _TMPLARGSPECS, class Transfer>
Methods
iterator (const std::string &name, Transfer, R const *(*b) (_FUNCARGLIST), R const *(*e) (_FUNCARGLIST) _COMMA _ARGSPECS, const std::string &doc = std::string ())
{
return Methods ((new _NAME(StaticMethodPtrConstIter) <R _COMMA _FUNCARGLIST, Transfer> (name, b, e, doc))->add_args (_ARGSPECARGS));
}
template <class X, class R _COMMA _TMPLARG>
Methods
iterator (const std::string &name, R *(X::*b) (_FUNCARGLIST), R *(X::*e) (_FUNCARGLIST), const std::string &doc = std::string ())
@ -1594,6 +1622,13 @@ iterator (const std::string &name, R *(X::*b) (_FUNCARGLIST), R *(X::*e) (_FUNCA
}
#endif
template <class X, class R _COMMA _TMPLARG _COMMA _TMPLARGSPECS, class Transfer>
Methods
iterator (const std::string &name, Transfer, R *(X::*b) (_FUNCARGLIST), R *(X::*e) (_FUNCARGLIST) _COMMA _ARGSPECS, const std::string &doc = std::string ())
{
return Methods ((new _NAME(MethodPtrIter) <X, R _COMMA _FUNCARGLIST, Transfer> (name, b, e, doc))->add_args (_ARGSPECARGS));
}
template <class X, class R _COMMA _TMPLARG>
Methods
iterator (const std::string &name, R const *(X::*b) (_FUNCARGLIST), R const *(X::*e) (_FUNCARGLIST), const std::string &doc = std::string ())
@ -1610,6 +1645,13 @@ iterator (const std::string &name, R const *(X::*b) (_FUNCARGLIST), R const *(X:
}
#endif
template <class X, class R _COMMA _TMPLARG _COMMA _TMPLARGSPECS, class Transfer>
Methods
iterator (const std::string &name, Transfer, R const *(X::*b) (_FUNCARGLIST), R const *(X::*e) (_FUNCARGLIST) _COMMA _ARGSPECS, const std::string &doc = std::string ())
{
return Methods ((new _NAME(MethodPtrConstIter) <X, R _COMMA _FUNCARGLIST, Transfer> (name, b, e, doc))->add_args (_ARGSPECARGS));
}
template <class X, class R _COMMA _TMPLARG>
Methods
iterator_ext (const std::string &name, R *(*xb) (X * _COMMA _FUNCARGLIST), R *(*xe) (X * _COMMA _FUNCARGLIST), const std::string &doc = std::string ())
@ -1626,6 +1668,13 @@ iterator_ext (const std::string &name, R *(*xb) (X * _COMMA _FUNCARGLIST), R *(*
}
#endif
template <class X, class R _COMMA _TMPLARG _COMMA _TMPLARGSPECS, class Transfer>
Methods
iterator_ext (const std::string &name, Transfer, R *(*xb) (X * _COMMA _FUNCARGLIST), R *(*xe) (X * _COMMA _FUNCARGLIST) _COMMA _ARGSPECS, const std::string &doc = std::string ())
{
return Methods ((new _NAME(ExtMethodPtrIter) <X, R _COMMA _FUNCARGLIST, Transfer> (name, xb, xe, doc))->add_args (_ARGSPECARGS));
}
template <class X, class R _COMMA _TMPLARG>
Methods
iterator_ext (const std::string &name, R const *(*xb) (X * _COMMA _FUNCARGLIST), R const *(*xe) (X * _COMMA _FUNCARGLIST), const std::string &doc = std::string ())
@ -1642,6 +1691,13 @@ iterator_ext (const std::string &name, R const *(*xb) (X * _COMMA _FUNCARGLIST),
}
#endif
template <class X, class R _COMMA _TMPLARG _COMMA _TMPLARGSPECS, class Transfer>
Methods
iterator_ext (const std::string &name, Transfer, R const *(*xb) (X * _COMMA _FUNCARGLIST), R const *(*xe) (X * _COMMA _FUNCARGLIST) _COMMA _ARGSPECS, const std::string &doc = std::string ())
{
return Methods ((new _NAME(ExtMethodPtrConstIter) <X, R _COMMA _FUNCARGLIST, Transfer> (name, xb, xe, doc))->add_args (_ARGSPECARGS));
}
template <class X, class R _COMMA _TMPLARG>
Methods
iterator (const std::string &name, R *(X::*b) (_FUNCARGLIST) const, R *(X::*e) (_FUNCARGLIST) const, const std::string &doc = std::string ())
@ -1658,6 +1714,13 @@ iterator (const std::string &name, R *(X::*b) (_FUNCARGLIST) const, R *(X::*e) (
}
#endif
template <class X, class R _COMMA _TMPLARG _COMMA _TMPLARGSPECS, class Transfer>
Methods
iterator (const std::string &name, Transfer, R *(X::*b) (_FUNCARGLIST) const, R *(X::*e) (_FUNCARGLIST) const _COMMA _ARGSPECS, const std::string &doc = std::string ())
{
return Methods ((new _NAME(ConstMethodPtrIter) <X, R _COMMA _FUNCARGLIST, Transfer> (name, b, e, doc))->add_args (_ARGSPECARGS));
}
template <class X, class R _COMMA _TMPLARG>
Methods
iterator (const std::string &name, R const *(X::*b) (_FUNCARGLIST) const, R const *(X::*e) (_FUNCARGLIST) const, const std::string &doc = std::string ())
@ -1674,34 +1737,41 @@ iterator (const std::string &name, R const *(X::*b) (_FUNCARGLIST) const, R cons
}
#endif
template <class X, class R _COMMA _TMPLARG _COMMA _TMPLARGSPECS, class Transfer>
Methods
iterator (const std::string &name, Transfer, R const *(X::*b) (_FUNCARGLIST) const, R const *(X::*e) (_FUNCARGLIST) const _COMMA _ARGSPECS, const std::string &doc = std::string ())
{
return Methods ((new _NAME(ConstMethodPtrConstIter) <X, R _COMMA _FUNCARGLIST, Transfer> (name, b, e, doc))->add_args (_ARGSPECARGS));
}
// pair iterators
template <class X, class I _COMMA _TMPLARG>
_NAME(MethodBiIter) <X, I _COMMA _FUNCARGLIST> *
_iterator (const std::string &name, I (X::*b) (_FUNCARGLIST), I (X::*e) (_FUNCARGLIST), const std::string &doc)
template <class X, class I _COMMA _TMPLARG, class Transfer>
_NAME(MethodBiIter) <X, I _COMMA _FUNCARGLIST, Transfer> *
_iterator (const std::string &name, I (X::*b) (_FUNCARGLIST), I (X::*e) (_FUNCARGLIST), Transfer, const std::string &doc)
{
return new _NAME(MethodBiIter) <X, I _COMMA _FUNCARGLIST> (name, b, e, doc);
return new _NAME(MethodBiIter) <X, I _COMMA _FUNCARGLIST, Transfer> (name, b, e, doc);
}
template <class X, class I _COMMA _TMPLARG>
_NAME(ExtMethodBiIter) <X, I _COMMA _FUNCARGLIST> *
_iterator_ext (const std::string &name, I (*xb) (X * _COMMA _FUNCARGLIST), I (*xe) (X * _COMMA _FUNCARGLIST), const std::string &doc)
template <class X, class I _COMMA _TMPLARG, class Transfer>
_NAME(ExtMethodBiIter) <X, I _COMMA _FUNCARGLIST, Transfer> *
_iterator_ext (const std::string &name, I (*xb) (X * _COMMA _FUNCARGLIST), I (*xe) (X * _COMMA _FUNCARGLIST), Transfer, const std::string &doc)
{
return new _NAME(ExtMethodBiIter) <X, I _COMMA _FUNCARGLIST> (name, xb, xe, doc);
return new _NAME(ExtMethodBiIter) <X, I _COMMA _FUNCARGLIST, Transfer> (name, xb, xe, doc);
}
template <class I _COMMA _TMPLARG>
_NAME(StaticMethodBiIter) <I _COMMA _FUNCARGLIST> *
_iterator (const std::string &name, I (*b) (_FUNCARGLIST), I (*e) (_FUNCARGLIST), const std::string &doc)
template <class I _COMMA _TMPLARG, class Transfer>
_NAME(StaticMethodBiIter) <I _COMMA _FUNCARGLIST, Transfer> *
_iterator (const std::string &name, I (*b) (_FUNCARGLIST), I (*e) (_FUNCARGLIST), Transfer, const std::string &doc)
{
return new _NAME(StaticMethodBiIter) <I _COMMA _FUNCARGLIST> (name, b, e, doc);
return new _NAME(StaticMethodBiIter) <I _COMMA _FUNCARGLIST, Transfer> (name, b, e, doc);
}
template <class I _COMMA _TMPLARG>
Methods
iterator (const std::string &name, I (*b) (_FUNCARGLIST), I (*e) (_FUNCARGLIST), const std::string &doc = std::string ())
{
return Methods (_iterator (name, b, e, doc));
return Methods (_iterator (name, b, e, arg_default_return_value_preference (), doc));
}
#if _COUNT != 0
@ -1709,15 +1779,22 @@ template <class I _COMMA _TMPLARG _COMMA _TMPLARGSPECS>
Methods
iterator (const std::string &name, I (*b) (_FUNCARGLIST), I (*e) (_FUNCARGLIST) _COMMA _ARGSPECS, const std::string &doc = std::string ())
{
return Methods (_iterator (name, b, e, doc)->add_args (_ARGSPECARGS));
return Methods (_iterator (name, b, e, arg_default_return_value_preference (), doc)->add_args (_ARGSPECARGS));
}
#endif
template <class I _COMMA _TMPLARG _COMMA _TMPLARGSPECS, class Transfer>
Methods
iterator (const std::string &name, Transfer transfer, I (*b) (_FUNCARGLIST), I (*e) (_FUNCARGLIST) _COMMA _ARGSPECS, const std::string &doc = std::string ())
{
return Methods (_iterator (name, b, e, transfer, doc)->add_args (_ARGSPECARGS));
}
template <class X, class I _COMMA _TMPLARG>
Methods
iterator (const std::string &name, I (X::*b) (_FUNCARGLIST), I (X::*e) (_FUNCARGLIST), const std::string &doc = std::string ())
{
return Methods (_iterator (name, b, e, doc));
return Methods (_iterator (name, b, e, arg_default_return_value_preference (), doc));
}
#if _COUNT != 0
@ -1725,15 +1802,22 @@ template <class X, class I _COMMA _TMPLARG _COMMA _TMPLARGSPECS>
Methods
iterator (const std::string &name, I (X::*b) (_FUNCARGLIST), I (X::*e) (_FUNCARGLIST) _COMMA _ARGSPECS, const std::string &doc = std::string ())
{
return Methods (_iterator (name, b, e, doc)->add_args (_ARGSPECARGS));
return Methods (_iterator (name, b, e, arg_default_return_value_preference (), doc)->add_args (_ARGSPECARGS));
}
#endif
template <class X, class I _COMMA _TMPLARG _COMMA _TMPLARGSPECS, class Transfer>
Methods
iterator (const std::string &name, Transfer transfer, I (X::*b) (_FUNCARGLIST), I (X::*e) (_FUNCARGLIST) _COMMA _ARGSPECS, const std::string &doc = std::string ())
{
return Methods (_iterator (name, b, e, transfer, doc)->add_args (_ARGSPECARGS));
}
template <class X, class I _COMMA _TMPLARG>
Methods
iterator_ext (const std::string &name, I (*xb) (X * _COMMA _FUNCARGLIST), I (*xe) (X * _COMMA _FUNCARGLIST), const std::string &doc = std::string ())
{
return Methods (_iterator_ext (name, xb, xe, doc));
return Methods (_iterator_ext (name, xb, xe, arg_default_return_value_preference (), doc));
}
#if _COUNT != 0
@ -1741,22 +1825,29 @@ template <class X, class I _COMMA _TMPLARG _COMMA _TMPLARGSPECS>
Methods
iterator_ext (const std::string &name, I (*xb) (X * _COMMA _FUNCARGLIST), I (*xe) (X * _COMMA _FUNCARGLIST) _COMMA _ARGSPECS, const std::string &doc = std::string ())
{
return Methods (_iterator_ext (name, xb, xe, doc)->add_args (_ARGSPECARGS));
return Methods (_iterator_ext (name, xb, xe, arg_default_return_value_preference (), doc)->add_args (_ARGSPECARGS));
}
#endif
template <class X, class I _COMMA _TMPLARG>
_NAME(ConstMethodBiIter) <X, I _COMMA _FUNCARGLIST> *
_iterator (const std::string &name, I (X::*b) (_FUNCARGLIST) const, I (X::*e) (_FUNCARGLIST) const, const std::string &doc)
template <class X, class I _COMMA _TMPLARG _COMMA _TMPLARGSPECS, class Transfer>
Methods
iterator_ext (const std::string &name, Transfer transfer, I (*xb) (X * _COMMA _FUNCARGLIST), I (*xe) (X * _COMMA _FUNCARGLIST) _COMMA _ARGSPECS, const std::string &doc = std::string ())
{
return new _NAME(ConstMethodBiIter) <X, I _COMMA _FUNCARGLIST> (name, b, e, doc);
return Methods (_iterator_ext (name, xb, xe, transfer, doc)->add_args (_ARGSPECARGS));
}
template <class X, class I _COMMA _TMPLARG, class Transfer>
_NAME(ConstMethodBiIter) <X, I _COMMA _FUNCARGLIST, Transfer> *
_iterator (const std::string &name, I (X::*b) (_FUNCARGLIST) const, I (X::*e) (_FUNCARGLIST) const, Transfer, const std::string &doc)
{
return new _NAME(ConstMethodBiIter) <X, I _COMMA _FUNCARGLIST, Transfer> (name, b, e, doc);
}
template <class X, class I _COMMA _TMPLARG>
Methods
iterator (const std::string &name, I (X::*b) (_FUNCARGLIST) const, I (X::*e) (_FUNCARGLIST) const, const std::string &doc = std::string ())
{
return Methods (_iterator (name, b, e, doc));
return Methods (_iterator (name, b, e, arg_default_return_value_preference (), doc));
}
#if _COUNT != 0
@ -1764,38 +1855,45 @@ template <class X, class I _COMMA _TMPLARG _COMMA _TMPLARGSPECS>
Methods
iterator (const std::string &name, I (X::*b) (_FUNCARGLIST) const, I (X::*e) (_FUNCARGLIST) const _COMMA _ARGSPECS, const std::string &doc = std::string ())
{
return Methods (_iterator (name, b, e, doc)->add_args (_ARGSPECARGS));
return Methods (_iterator (name, b, e, arg_default_return_value_preference (), doc)->add_args (_ARGSPECARGS));
}
#endif
template <class X, class I _COMMA _TMPLARG _COMMA _TMPLARGSPECS, class Transfer>
Methods
iterator (const std::string &name, Transfer transfer, I (X::*b) (_FUNCARGLIST) const, I (X::*e) (_FUNCARGLIST) const _COMMA _ARGSPECS, const std::string &doc = std::string ())
{
return Methods (_iterator (name, b, e, transfer, doc)->add_args (_ARGSPECARGS));
}
// free iterators
template <class X, class I _COMMA _TMPLARG>
_NAME(MethodFreeIter) <X, I _COMMA _FUNCARGLIST> *
_iterator (const std::string &name, I (X::*i) (_FUNCARGLIST), const std::string &doc)
template <class X, class I _COMMA _TMPLARG, class Transfer>
_NAME(MethodFreeIter) <X, I _COMMA _FUNCARGLIST, Transfer> *
_iterator (const std::string &name, I (X::*i) (_FUNCARGLIST), Transfer, const std::string &doc)
{
return new _NAME(MethodFreeIter) <X, I _COMMA _FUNCARGLIST> (name, i, doc);
return new _NAME(MethodFreeIter) <X, I _COMMA _FUNCARGLIST, Transfer> (name, i, doc);
}
template <class X, class I _COMMA _TMPLARG>
_NAME(ExtMethodFreeIter) <X, I _COMMA _FUNCARGLIST> *
_iterator_ext (const std::string &name, I (*xi) (X * _COMMA _FUNCARGLIST), const std::string &doc)
template <class X, class I _COMMA _TMPLARG, class Transfer>
_NAME(ExtMethodFreeIter) <X, I _COMMA _FUNCARGLIST, Transfer> *
_iterator_ext (const std::string &name, I (*xi) (X * _COMMA _FUNCARGLIST), Transfer, const std::string &doc)
{
return new _NAME(ExtMethodFreeIter) <X, I _COMMA _FUNCARGLIST> (name, xi, doc);
return new _NAME(ExtMethodFreeIter) <X, I _COMMA _FUNCARGLIST, Transfer> (name, xi, doc);
}
template <class I _COMMA _TMPLARG>
_NAME(StaticMethodFreeIter) <I _COMMA _FUNCARGLIST> *
_iterator (const std::string &name, I (*i) (_FUNCARGLIST), const std::string &doc)
template <class I _COMMA _TMPLARG, class Transfer>
_NAME(StaticMethodFreeIter) <I _COMMA _FUNCARGLIST, Transfer> *
_iterator (const std::string &name, I (*i) (_FUNCARGLIST), Transfer, const std::string &doc)
{
return new _NAME(StaticMethodFreeIter) <I _COMMA _FUNCARGLIST> (name, i, doc);
return new _NAME(StaticMethodFreeIter) <I _COMMA _FUNCARGLIST, Transfer> (name, i, doc);
}
template <class I _COMMA _TMPLARG>
Methods
iterator (const std::string &name, I (*i) (_FUNCARGLIST), const std::string &doc = std::string ())
{
return Methods (_iterator (name, i, doc));
return Methods (_iterator (name, i, arg_default_return_value_preference (), doc));
}
#if _COUNT != 0
@ -1803,15 +1901,22 @@ template <class I _COMMA _TMPLARG _COMMA _TMPLARGSPECS>
Methods
iterator (const std::string &name, I (*i) (_FUNCARGLIST) _COMMA _ARGSPECS, const std::string &doc = std::string ())
{
return Methods (_iterator (name, i, doc)->add_args (_ARGSPECARGS));
return Methods (_iterator (name, i, arg_default_return_value_preference (), doc)->add_args (_ARGSPECARGS));
}
#endif
template <class I _COMMA _TMPLARG _COMMA _TMPLARGSPECS, class Transfer>
Methods
iterator (const std::string &name, Transfer transfer, I (*i) (_FUNCARGLIST) _COMMA _ARGSPECS, const std::string &doc = std::string ())
{
return Methods (_iterator (name, i, transfer, doc)->add_args (_ARGSPECARGS));
}
template <class X, class I _COMMA _TMPLARG>
Methods
iterator (const std::string &name, I (X::*i) (_FUNCARGLIST), const std::string &doc = std::string ())
{
return Methods (_iterator (name, i, doc));
return Methods (_iterator (name, i, arg_default_return_value_preference (), doc));
}
#if _COUNT != 0
@ -1819,15 +1924,22 @@ template <class X, class I _COMMA _TMPLARG _COMMA _TMPLARGSPECS>
Methods
iterator (const std::string &name, I (X::*i) (_FUNCARGLIST) _COMMA _ARGSPECS, const std::string &doc = std::string ())
{
return Methods (_iterator (name, i, doc)->add_args (_ARGSPECARGS));
return Methods (_iterator (name, i, arg_default_return_value_preference (), doc)->add_args (_ARGSPECARGS));
}
#endif
template <class X, class I _COMMA _TMPLARG _COMMA _TMPLARGSPECS, class Transfer>
Methods
iterator (const std::string &name, Transfer transfer, I (X::*i) (_FUNCARGLIST) _COMMA _ARGSPECS, const std::string &doc = std::string ())
{
return Methods (_iterator (name, i, transfer, doc)->add_args (_ARGSPECARGS));
}
template <class X, class I _COMMA _TMPLARG>
Methods
iterator_ext (const std::string &name, I (*xi) (X * _COMMA _FUNCARGLIST), const std::string &doc = std::string ())
{
return Methods (_iterator_ext (name, xi, doc));
return Methods (_iterator_ext (name, xi, arg_default_return_value_preference (), doc));
}
#if _COUNT != 0
@ -1835,22 +1947,29 @@ template <class X, class I _COMMA _TMPLARG _COMMA _TMPLARGSPECS>
Methods
iterator_ext (const std::string &name, I (*xi) (X * _COMMA _FUNCARGLIST) _COMMA _ARGSPECS, const std::string &doc = std::string ())
{
return Methods (_iterator_ext (name, xi, doc)->add_args (_ARGSPECARGS));
return Methods (_iterator_ext (name, xi, arg_default_return_value_preference (), doc)->add_args (_ARGSPECARGS));
}
#endif
template <class X, class I _COMMA _TMPLARG>
_NAME(ConstMethodFreeIter) <X, I _COMMA _FUNCARGLIST> *
_iterator (const std::string &name, I (X::*i) (_FUNCARGLIST) const, const std::string &doc)
template <class X, class I _COMMA _TMPLARG _COMMA _TMPLARGSPECS, class Transfer>
Methods
iterator_ext (const std::string &name, Transfer transfer, I (*xi) (X * _COMMA _FUNCARGLIST) _COMMA _ARGSPECS, const std::string &doc = std::string ())
{
return new _NAME(ConstMethodFreeIter) <X, I _COMMA _FUNCARGLIST> (name, i, doc);
return Methods (_iterator_ext (name, xi, transfer, doc)->add_args (_ARGSPECARGS));
}
template <class X, class I _COMMA _TMPLARG, class Transfer>
_NAME(ConstMethodFreeIter) <X, I _COMMA _FUNCARGLIST, Transfer> *
_iterator (const std::string &name, I (X::*i) (_FUNCARGLIST) const, Transfer, const std::string &doc)
{
return new _NAME(ConstMethodFreeIter) <X, I _COMMA _FUNCARGLIST, Transfer> (name, i, doc);
}
template <class X, class I _COMMA _TMPLARG>
Methods
iterator (const std::string &name, I (X::*i) (_FUNCARGLIST) const, const std::string &doc = std::string ())
{
return Methods (_iterator (name, i, doc));
return Methods (_iterator (name, i, arg_default_return_value_preference (), doc));
}
#if _COUNT != 0
@ -1858,9 +1977,16 @@ template <class X, class I _COMMA _TMPLARG _COMMA _TMPLARGSPECS>
Methods
iterator (const std::string &name, I (X::*i) (_FUNCARGLIST) const _COMMA _ARGSPECS, const std::string &doc = std::string ())
{
return Methods (_iterator (name, i, doc)->add_args (_ARGSPECARGS));
return Methods (_iterator (name, i, arg_default_return_value_preference (), doc)->add_args (_ARGSPECARGS));
}
#endif
template <class X, class I _COMMA _TMPLARG _COMMA _TMPLARGSPECS, class Transfer>
Methods
iterator (const std::string &name, Transfer transfer, I (X::*i) (_FUNCARGLIST) const _COMMA _ARGSPECS, const std::string &doc = std::string ())
{
return Methods (_iterator (name, i, transfer, doc)->add_args (_ARGSPECARGS));
}
#undef _COMMA

View File

@ -1410,6 +1410,13 @@ struct arg_make_reference { };
*/
struct arg_default_return_value_preference { };
// All of these modes can be used for arguments (in callbacks) or for
// return values. So we provide aliases to make their names clearer.
typedef arg_pass_ownership return_new_object;
typedef arg_make_copy return_copy;
typedef arg_make_reference return_reference;
/**
* @brief A function computing the "prefer_copy" value
*/

View File

@ -1074,17 +1074,17 @@ static gsi::Class<C_P> decl_c ("", "C",
gsi::method ("call_vfunc", &C_P::call_vfunc) +
gsi::method ("pass_cd_direct", &C_P::pass_cd_direct) +
gsi::method ("pass_cd_cref", &C_P::pass_cd_cref) +
gsi::method<C_P, const CopyDetector &, const CopyDetector &, gsi::arg_make_copy> ("pass_cd_cref_as_copy", &C_P::pass_cd_cref) +
gsi::method<C_P, const CopyDetector &, const CopyDetector &, gsi::arg_make_reference> ("pass_cd_cref_as_ref", &C_P::pass_cd_cref) +
gsi::method ("pass_cd_cref_as_copy", gsi::return_copy (), &C_P::pass_cd_cref) +
gsi::method ("pass_cd_cref_as_ref", gsi::return_reference (), &C_P::pass_cd_cref) +
gsi::method ("pass_cd_cptr", &C_P::pass_cd_cptr) +
gsi::method<C_P, const CopyDetector *, const CopyDetector &, gsi::arg_make_copy> ("pass_cd_cptr_as_copy", &C_P::pass_cd_cptr) +
gsi::method<C_P, const CopyDetector *, const CopyDetector &, gsi::arg_make_reference> ("pass_cd_cptr_as_ref", &C_P::pass_cd_cptr) +
gsi::method ("pass_cd_cptr_as_copy", gsi::return_copy (), &C_P::pass_cd_cptr) +
gsi::method ("pass_cd_cptr_as_ref", gsi::return_reference (), &C_P::pass_cd_cptr) +
gsi::method ("pass_cd_ref", &C_P::pass_cd_ref) +
gsi::method<C_P, CopyDetector &, const CopyDetector &, gsi::arg_make_copy> ("pass_cd_ref_as_copy", &C_P::pass_cd_ref) +
gsi::method<C_P, CopyDetector &, const CopyDetector &, gsi::arg_make_reference> ("pass_cd_ref_as_ref", &C_P::pass_cd_ref) +
gsi::method ("pass_cd_ref_as_copy", gsi::return_copy (), &C_P::pass_cd_ref) +
gsi::method ("pass_cd_ref_as_ref", gsi::return_reference (), &C_P::pass_cd_ref) +
gsi::method ("pass_cd_ptr", &C_P::pass_cd_ptr) +
gsi::method<C_P, CopyDetector *, const CopyDetector &, gsi::arg_make_copy> ("pass_cd_ptr_as_copy", &C_P::pass_cd_ptr) +
gsi::method<C_P, CopyDetector *, const CopyDetector &, gsi::arg_make_reference> ("pass_cd_ptr_as_ref", &C_P::pass_cd_ptr) +
gsi::method ("pass_cd_ptr_as_copy", gsi::return_copy (), &C_P::pass_cd_ptr) +
gsi::method ("pass_cd_ptr_as_ref", gsi::return_reference (), &C_P::pass_cd_ptr) +
gsi::method ("g", &C_P::g) +
gsi::method ("s1", &C::s1) +
gsi::method ("s2", &C::s2) +

View File

@ -2,6 +2,7 @@
TL_INC = $$PWD/tl/tl
DB_INC = $$PWD/db/db
DRC_INC = $$PWD/drc/drc
LVS_INC = $$PWD/lvs/lvs
EDT_INC = $$PWD/edt/edt
EXT_INC = $$PWD/ext/ext
GSI_INC = $$PWD/gsi/gsi

View File

@ -69,9 +69,10 @@ plugins.depends += lib rdb db
}
equals(HAVE_RUBY, "1") {
SUBDIRS += drc
MAIN_DEPENDS += drc
SUBDIRS += drc lvs
MAIN_DEPENDS += drc lvs
drc.depends += rdb lym
lvs.depends += drc
}
equals(HAVE_QTBINDINGS, "1") {
@ -99,3 +100,4 @@ plugins.depends += lib rdb db
}
unit_tests.depends += plugins $$MAIN_DEPENDS

View File

@ -44,6 +44,7 @@
#include "imgForceLink.h"
#if defined(HAVE_RUBY)
#include "drcForceLink.h"
#include "lvsForceLink.h"
#endif
#if defined(HAVE_QTBINDINGS)

Some files were not shown because too many files have changed in this diff Show More