From 5c9b6527714f751e03485b40c7337e06dc814a17 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sat, 2 Mar 2019 10:41:07 +0100 Subject: [PATCH] Integration of LayoutToNetlist into DRC framework ('Netter') First steps are made towards providing an antenna check --- src/drc/drc/built-in-macros/drc.lym | 327 ++++++++++++++++++++++++++-- 1 file changed, 304 insertions(+), 23 deletions(-) diff --git a/src/drc/drc/built-in-macros/drc.lym b/src/drc/drc/built-in-macros/drc.lym index 358649fb2..11a5a532c 100644 --- a/src/drc/drc/built-in-macros/drc.lym +++ b/src/drc/drc/built-in-macros/drc.lym @@ -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)