From ed41af9b4bb5a86e63451bf166dc7429f8ecaccf Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Fri, 28 Jun 2019 12:50:55 +0200 Subject: [PATCH] WIP: added documentation to LVS script. --- src/drc/drc/built-in-macros/_drc_netter.rb | 19 +-- src/drc/drc/built-in-macros/_lvs_engine.rb | 51 ++++++- src/drc/drc/built-in-macros/_lvs_netter.rb | 160 ++++++++++++++++++--- 3 files changed, 196 insertions(+), 34 deletions(-) diff --git a/src/drc/drc/built-in-macros/_drc_netter.rb b/src/drc/drc/built-in-macros/_drc_netter.rb index 9932a0fbe..2ab764519 100644 --- a/src/drc/drc/built-in-macros/_drc_netter.rb +++ b/src/drc/drc/built-in-macros/_drc_netter.rb @@ -13,21 +13,6 @@ module DRC # 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. - - # %LVS% - # @scope - # @name Netter - # @brief LVS Reference: Netter object - # The Netter object provides services related to network extraction - # from a layout plus comparison against a reference netlist. - # Similar to the DRC netter (which lacks the compare ability), the - # relevant method of this object are available as global functions too - # where they act on a default incarnation. Usually it's not required - # to instantiate a Netter object explicitly. - # - # 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: @@ -394,11 +379,11 @@ module DRC def ensure_data if !@l2n @layers = {} - make_data + _make_data end end - def make_data + 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") diff --git a/src/drc/drc/built-in-macros/_lvs_engine.rb b/src/drc/drc/built-in-macros/_lvs_engine.rb index 8e9cf61cb..d859b7e8d 100644 --- a/src/drc/drc/built-in-macros/_lvs_engine.rb +++ b/src/drc/drc/built-in-macros/_lvs_engine.rb @@ -66,7 +66,56 @@ module LVS @output_lvsdb_file = filename end - # ... + # %DRC% + # @name schematic + # @brief Reads the reference netlist + # @synopsis schematic(filename) + # @synopsis schematic(filename, reader) + # @synopsis schematic(netlist) + # See \Netter#schematic for a description of that function. + + # %DRC% + # @name compare + # @brief Compares the extracted netlist vs. the schematic + # @synopsis compare + # See \Netter#compare for a description of that function. + + # %DRC% + # @name same_nets + # @brief Establishes an equivalence between the nets + # @synopsis same_nets(circuit, net_a, net_b) + # @synopsis same_nets(circuit_a, net_a, circuit_b, net_b) + # See \Netter#same_nets for a description of that function. + + # %DRC% + # @name same_circuits + # @brief Establishes an equivalence between the circuits + # @synopsis same_circuits(circuit_a, circuit_b) + # See \Netter#same_circuits for a description of that function. + + # %DRC% + # @name same_device_classes + # @brief Establishes an equivalence between the device_classes + # @synopsis same_device_classes(class_a, class_b) + # See \Netter#same_device_classes for a description of that function. + + # %DRC% + # @name equivalent_pins + # @brief Marks pins as equivalent + # @synopsis equivalent_pins(circuit, pins ...) + # See \Netter#equivalen_pins for a description of that function. + + # %LVS% + # @name min_caps + # @brief Ignores capacitors with a capacitance below a certain value + # @synopsis min_caps(threshold) + # See \Netter#min_caps for a description of that function. + + # %LVS% + # @name max_res + # @brief Ignores resistors with a resistance above a certain value + # @synopsis max_res(threshold) + # See \Netter#max_res for a description of that function. %w(schematic compare same_nets same_circuits same_device_classes equivalent_pins min_caps max_res max_depth max_branch_complexity).each do |f| eval <<"CODE" diff --git a/src/drc/drc/built-in-macros/_lvs_netter.rb b/src/drc/drc/built-in-macros/_lvs_netter.rb index c523eb138..11d349c24 100644 --- a/src/drc/drc/built-in-macros/_lvs_netter.rb +++ b/src/drc/drc/built-in-macros/_lvs_netter.rb @@ -16,6 +16,17 @@ module LVS # relevant method of this object are available as global functions too # where they act on a default incarnation. Usually it's not required # to instantiate a Netter object explicitly. + # + # An individual netter object can be created, if the netter results + # need to be kept for multiple extractions or when different configurations + # need to be used in the same script. If you really want + # a Netter object, use the global \netter function: + # The Netter object provides services related to network extraction + # from a layout plus comparison against a reference netlist. + # Similar to the DRC netter (which lacks the compare ability), the + # relevant method of this object are available as global functions too + # where they act on a default incarnation. Usually it's not required + # to instantiate a Netter object explicitly. # # An individual netter object can be created, if the netter results # need to be kept for multiple extractions. If you really need @@ -24,11 +35,23 @@ module LVS # @code # # create a new Netter object: # nx = netter + # + # # build connectivity # nx.connect(poly, contact) # ... - # @/code # + # # read the reference netlist + # nx.schematic("reference.cir") + # + # # configure the netlist compare + # nx.same_circuits("A", "B") # ... + # + # # runs the compare + # if ! nx.compare + # puts("no equivalence!") + # end + # @/code class LVSNetter < DRCNetter @@ -36,7 +59,7 @@ module LVS super end - def make_data + def _make_data if @engine._dss @engine._dss.is_singular? || raise("The LVS script features more than one or no layout source - network extraction cannot be performed in such configurations") @@ -51,6 +74,13 @@ module LVS end + # %LVS% + # @name lvs_data + # @brief Gets the internal RBA::LayoutVsSchematic object + # @synopsis lvs_data + # The RBA::LayoutVsSchematic object provides access to the internal details of + # the netter object. + def lvs_data l2n_data @lvs @@ -67,6 +97,17 @@ module LVS data end + # %LVS% + # @name compare + # @brief Compares the extracted netlist vs. the schematic + # @synopsis compare + # Before using this method, a schematic netlist has to be loaded with \schematic. + # The compare can be configured in more details using \same_nets, \same_circuits, + # \same_device_classes and \equivalent_pins. + # + # This method will return true, if the netlists are equivalent and false + # otherwise. + def compare @lvs.compare(@comparer) end @@ -80,6 +121,21 @@ module LVS end + # %LVS% + # @name same_nets + # @brief Establishes an equivalence between the nets + # @synopsis same_nets(circuit, net_a, net_b) + # @synopsis same_nets(circuit_a, net_a, circuit_b, net_b) + # This method will force an equivalence between the net_a and net_b from circuit_a + # and circuit_b (circuit in the three-argument form is for both circuit_a and circuit_b). + # Circuit and nets are string giving a circuit and net by name. + # After using this function, the compare algorithm will consider these nets equivalent. + # Use this method to provide hints for the comparer in cases which are difficult to + # resolve otherwise. + # + # Before this method can be used, a schematic netlist needs to be loaded with + # \schematic. + def same_nets(*args) pins.each do |a| @@ -111,6 +167,17 @@ module LVS end + # %LVS% + # @name same_circuits + # @brief Establishes an equivalence between the circuits + # @synopsis same_circuits(circuit_a, circuit_b) + # This method will force an equivalence between the two circuits. + # By default, circuits are identified by name. If names are different, this + # method allows establishing an explicit correspondence. + # + # Before this method can be used, a schematic netlist needs to be loaded with + # \schematic. + def same_circuits(a, b) a.is_a?(String) || b.is_a?(String) || raise("Both arguments of 'same_circuits' need to be strings") @@ -124,6 +191,18 @@ module LVS end + # %LVS% + # @name same_device_classes + # @brief Establishes an equivalence between the device classes + # @synopsis same_device_classes(class_a, class_b) + # This method will force an equivalence between the two device classes. + # Device classes are also known as "models". + # By default, device classes are identified by name. If names are different, this + # method allows establishing an explicit correspondence. + # + # Before this method can be used, a schematic netlist needs to be loaded with + # \schematic. + def same_device_classes(a, b) a.is_a?(String) || b.is_a?(String) || raise("Both arguments of 'same_device_classes' need to be strings") @@ -137,6 +216,22 @@ module LVS end + # %LVS% + # @name equivalent_pins + # @brief Marks pins as equivalent + # @synopsis equivalent_pins(circuit, pins ...) + # This method will mark the given pins as equivalent. This gives the compare algorithm + # more degrees of freedom when establishing net correspondence. Typically this method + # is used to declare inputs from gates are equivalent where are are logically, but not + # physically (e.g. in a CMOS NAND gate): + # + # @code + # netter.equivalent_pins("NAND2", "A", "B") + # @/code + # + # Before this method can be used, a schematic netlist needs to be loaded with + # \schematic. + def equivalent_pins(circuit, *pins) circuit.is_a?(String) || raise("Circuit arguments of 'equivalent_pins' needs to be a string") @@ -157,31 +252,64 @@ module LVS end - def schematic(filename, reader = nil) + # %LVS% + # @name schematic + # @brief Reads the reference netlist + # @synopsis schematic(filename) + # @synopsis schematic(filename, reader) + # @synopsis schematic(netlist) + # If a filename is given (first two forms), the netlist is read from the given file. + # If no reader is provided, Spice format will be assumed. The reader object is a + # RBA::NetlistReader object and allows detailed customization of the reader process. + # + # Alternatively, a RBA::Netlist object can be given which is obtained from any other + # source. + + def schematic(schematic, reader = nil) - filename.is_a?(String) || raise("First argument must be string in 'schematic'") - - if reader - reader.is_a?(RBA::NetlistReader) || raise("Second argument must be netlist reader object in 'schematic'") + if schematic.is_a?(RBA::Netlist) + lvs_data.reference = netlist else - reader = RBA::NetlistSpiceReader::new + + schematic.is_a?(String) || raise("First argument must be string or netlist in 'schematic'") + + if reader + reader.is_a?(RBA::NetlistReader) || raise("Second argument must be netlist reader object in 'schematic'") + else + reader = RBA::NetlistSpiceReader::new + end + + netlist_file = @engine._make_path(schematic) + @engine.info("Reading netlist: #{netlist_file} ..") + + netlist = RBA::Netlist::new + netlist.read(netlist_file, reader) + + lvs_data.reference = netlist + end - netlist_file = @engine._make_path(filename) - @engine.info("Reading netlist: #{netlist_file} ..") - - netlist = RBA::Netlist::new - netlist.read(netlist_file, reader) - - lvs_data.reference = netlist - end + # %LVS% + # @name min_caps + # @brief Ignores capacitors with a capacitance below a certain value + # @synopsis min_caps(threshold) + # After using this method, the netlist compare will ignore capacitance devices + # with a capacitance values below the given threshold (in Farad). + def min_caps(value) lvs_data @comparer.min_capacitance = value.to_f end + # %LVS% + # @name max_res + # @brief Ignores resistors with a resistance above a certain value + # @synopsis max_res(threshold) + # After using this method, the netlist compare will ignore resistor devices + # with a resistance value above the given threshold (in Farad). + def max_res(value) lvs_data @comparer.max_resistance = value.to_f