mirror of https://github.com/KLayout/klayout.git
commit
1a25b19ab1
Binary file not shown.
|
|
@ -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
|
||||
|
|
@ -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>
|
||||
|
|
@ -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("&", "&").
|
||||
gsub("<", "<").
|
||||
gsub(">", ">").
|
||||
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
|
||||
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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") {
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 ();
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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 ()) {
|
||||
|
|
|
|||
|
|
@ -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
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -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 ();
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 ®ion, 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 ®ion, 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 ®ion) 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 ®ion) 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 ®ion) 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 ®ion, const Tr &tr)
|
||||
static bool deliver_shape (const db::PolygonRef &pr, db::Region ®ion, 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 ®ion, 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 ();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 ®ion, 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 ®ion) 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);
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
|
@ -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;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
@ -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");
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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
|
||||
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
|
|
|
|||
|
|
@ -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
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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 ®ion)
|
||||
{
|
||||
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);
|
||||
|
|
|
|||
|
|
@ -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 ®ion);
|
||||
|
||||
/**
|
||||
* @brief Defines a device terminal in the layout (a polygon)
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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> ¶ms)
|
||||
{
|
||||
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 ();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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> ¶ms);
|
||||
|
||||
/**
|
||||
* @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);
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 ());
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
|
|
@ -80,6 +80,11 @@ private:
|
|||
{
|
||||
m_id = id;
|
||||
}
|
||||
|
||||
void set_name (const std::string &name)
|
||||
{
|
||||
m_name = name;
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -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 ®ion) 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 ®ion) 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 ®ion, 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 ());
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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."
|
||||
);
|
||||
|
||||
}
|
||||
|
|
@ -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> ¶ms)
|
||||
{
|
||||
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> ¶ms)
|
||||
{
|
||||
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."
|
||||
);
|
||||
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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 ());
|
||||
|
||||
}
|
||||
|
|
@ -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."
|
||||
);
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
@ -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
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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> ¶ms)
|
||||
{
|
||||
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"
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
File diff suppressed because it is too large
Load Diff
|
|
@ -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
|
||||
|
||||
|
|
@ -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
|
||||
|
||||
|
|
@ -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
|
||||
|
||||
|
|
@ -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
|
||||
|
||||
|
|
@ -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 && 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>
|
||||
|
||||
|
|
@ -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 & 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
|
||||
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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 & active
|
||||
gate.width(0.25.micron).output(100, 0)
|
||||
</text>
|
||||
</klayout-macro>
|
||||
|
||||
|
|
@ -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;
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -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 ()
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -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) +
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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
Loading…
Reference in New Issue