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
|
||||
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
|
||||
|
||||
def insert_object_into(container, object, dbu_trans)
|
||||
|
|
@ -3208,28 +3228,6 @@ CODE
|
|||
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
|
||||
|
||||
# A layout source representative object.
|
||||
|
|
@ -3595,6 +3593,244 @@ CODE
|
|||
|
||||
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
|
||||
|
||||
# %DRC%
|
||||
|
|
@ -3628,6 +3864,7 @@ CODE
|
|||
@log_file = nil
|
||||
@dss = nil
|
||||
@deep = false
|
||||
@netter = nil
|
||||
|
||||
@verbose = false
|
||||
|
||||
|
|
@ -4491,6 +4728,42 @@ CODE
|
|||
CODE
|
||||
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
|
||||
cc = caller.find do |c|
|
||||
c !~ /drc.lym:/ && c !~ /\(eval\)/
|
||||
|
|
@ -4730,7 +5003,15 @@ CODE
|
|||
end
|
||||
|
||||
end
|
||||
|
||||
|
||||
def _dss
|
||||
@dss
|
||||
end
|
||||
|
||||
def _netter
|
||||
@netter ||= DRC::DRCNetter::new(self)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def _make_string(v)
|
||||
|
|
|
|||
Loading…
Reference in New Issue