mirror of https://github.com/KLayout/klayout.git
Integration of LayoutToNetlist into DRC framework ('Netter')
First steps are made towards providing an antenna check
This commit is contained in:
parent
261fb027fd
commit
5c9b652771
|
|
@ -3159,6 +3159,26 @@ CODE
|
||||||
@data
|
@data
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def requires_region(f)
|
||||||
|
@data.is_a?(RBA::Region) || raise("#{f}: Requires a polygon layer")
|
||||||
|
end
|
||||||
|
|
||||||
|
def requires_edge_pairs(f)
|
||||||
|
@data.is_a?(RBA::EdgePairs) || raise("#{f}: Requires a edge pair layer")
|
||||||
|
end
|
||||||
|
|
||||||
|
def requires_edges(f)
|
||||||
|
@data.is_a?(RBA::Edges) || raise("#{f}: Requires an edge layer")
|
||||||
|
end
|
||||||
|
|
||||||
|
def requires_edges_or_region(f)
|
||||||
|
@data.is_a?(RBA::Edges) || @data.is_a?(RBA::Region) || raise("#{f}: Requires an edge or polygon layer")
|
||||||
|
end
|
||||||
|
|
||||||
|
def requires_same_type(other, f)
|
||||||
|
@data.class == other.data.class || raise("#{f}: Requires input of the same kind")
|
||||||
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def insert_object_into(container, object, dbu_trans)
|
def insert_object_into(container, object, dbu_trans)
|
||||||
|
|
@ -3208,28 +3228,6 @@ CODE
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
protected
|
|
||||||
|
|
||||||
def requires_region(f)
|
|
||||||
@data.is_a?(RBA::Region) || raise("#{f}: Requires a polygon layer")
|
|
||||||
end
|
|
||||||
|
|
||||||
def requires_edge_pairs(f)
|
|
||||||
@data.is_a?(RBA::EdgePairs) || raise("#{f}: Requires a edge pair layer")
|
|
||||||
end
|
|
||||||
|
|
||||||
def requires_edges(f)
|
|
||||||
@data.is_a?(RBA::Edges) || raise("#{f}: Requires an edge layer")
|
|
||||||
end
|
|
||||||
|
|
||||||
def requires_edges_or_region(f)
|
|
||||||
@data.is_a?(RBA::Edges) || @data.is_a?(RBA::Region) || raise("#{f}: Requires an edge or polygon layer")
|
|
||||||
end
|
|
||||||
|
|
||||||
def requires_same_type(other, f)
|
|
||||||
@data.class == other.data.class || raise("#{f}: Requires input of the same kind")
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
# A layout source representative object.
|
# A layout source representative object.
|
||||||
|
|
@ -3595,6 +3593,244 @@ CODE
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# The 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.
|
||||||
|
#
|
||||||
|
# An individual netter object can be created, if the netter results
|
||||||
|
# need to be kept for multiple extractions. If you really need
|
||||||
|
# a Netter object, use the global \\netter function:
|
||||||
|
#
|
||||||
|
# @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. 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
|
||||||
|
|
||||||
|
class DRCNetter
|
||||||
|
|
||||||
|
def initialize(engine)
|
||||||
|
@engine = engine
|
||||||
|
clear_connections
|
||||||
|
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)")
|
||||||
|
[ a, b ].each { |l| @layers[l.data.data_id] = l.data }
|
||||||
|
@connections << [ a, b ].collect { |l| l.data.data_id }
|
||||||
|
modified
|
||||||
|
end
|
||||||
|
|
||||||
|
# %DRC%
|
||||||
|
# @name clear_connections
|
||||||
|
# @brief Clears all connections stored so far
|
||||||
|
# @synopsis clear_connections
|
||||||
|
# See \connect for more details.
|
||||||
|
|
||||||
|
def clear_connections
|
||||||
|
@connections = []
|
||||||
|
@layers = {}
|
||||||
|
modified
|
||||||
|
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 a copy
|
||||||
|
# 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
|
||||||
|
|
||||||
|
@l2n || make_l2n
|
||||||
|
DRC::DRCLayer::new(@engine, @engine._cmd(@l2n, :antenna_check, gate.data, metal.data, ratio, dl))
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def modified
|
||||||
|
@l2n && @l2n._destroy
|
||||||
|
@l2n = nil
|
||||||
|
end
|
||||||
|
|
||||||
|
def make_l2n
|
||||||
|
|
||||||
|
if @engine._dss
|
||||||
|
# TODO: check whether all layers are deep and come from the dss and layout index,
|
||||||
|
# then use this layout index. This will remove the need for this check:
|
||||||
|
@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
|
||||||
|
|
||||||
|
@layers.each { |id,l| @l2n.register(l, "l" + id.to_s) }
|
||||||
|
@layers.each { |id,l| @l2n.connect(l) }
|
||||||
|
@connections.each { |a,b| @l2n.connect(@layers[a], @layers[b]) }
|
||||||
|
|
||||||
|
# run extraction in a timed environment
|
||||||
|
@engine._cmd(@l2n, :extract_netlist)
|
||||||
|
@l2n
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
# The DRC engine
|
# The DRC engine
|
||||||
|
|
||||||
# %DRC%
|
# %DRC%
|
||||||
|
|
@ -3628,6 +3864,7 @@ CODE
|
||||||
@log_file = nil
|
@log_file = nil
|
||||||
@dss = nil
|
@dss = nil
|
||||||
@deep = false
|
@deep = false
|
||||||
|
@netter = nil
|
||||||
|
|
||||||
@verbose = false
|
@verbose = false
|
||||||
|
|
||||||
|
|
@ -4491,6 +4728,42 @@ CODE
|
||||||
CODE
|
CODE
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# %DRC%
|
||||||
|
# @name netter
|
||||||
|
# @brief Creates a new netter object
|
||||||
|
# @synopsis netter
|
||||||
|
# See \Netter for more details
|
||||||
|
|
||||||
|
def netter
|
||||||
|
DRC::DRCNetter::new
|
||||||
|
end
|
||||||
|
|
||||||
|
# %DRC%
|
||||||
|
# @name connect
|
||||||
|
# @brief Specifies a connection between two layers
|
||||||
|
# @synopsis connect(a, b)
|
||||||
|
# See \Netter#connect for a description of that function.
|
||||||
|
|
||||||
|
# %DRC%
|
||||||
|
# @name clear_connections
|
||||||
|
# @brief Clears all connections stored so far
|
||||||
|
# @synopsis clear_connections
|
||||||
|
# See \Netter#clear_connections for a description of that function
|
||||||
|
|
||||||
|
# %DRC%
|
||||||
|
# @name antenna_check
|
||||||
|
# @brief Performs an antenna check
|
||||||
|
# @synopsis antenna_check(gate, metal, ratio, [ diode_specs ... ])
|
||||||
|
# See \Netter#antenna_check for a description of that function
|
||||||
|
|
||||||
|
%w(connect clear_connections antenna_check).each do |f|
|
||||||
|
eval <<"CODE"
|
||||||
|
def #{f}(*args)
|
||||||
|
_netter.#{f}(*args)
|
||||||
|
end
|
||||||
|
CODE
|
||||||
|
end
|
||||||
|
|
||||||
def src_line
|
def src_line
|
||||||
cc = caller.find do |c|
|
cc = caller.find do |c|
|
||||||
c !~ /drc.lym:/ && c !~ /\(eval\)/
|
c !~ /drc.lym:/ && c !~ /\(eval\)/
|
||||||
|
|
@ -4730,7 +5003,15 @@ CODE
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def _dss
|
||||||
|
@dss
|
||||||
|
end
|
||||||
|
|
||||||
|
def _netter
|
||||||
|
@netter ||= DRC::DRCNetter::new(self)
|
||||||
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def _make_string(v)
|
def _make_string(v)
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue