diff --git a/docs/user/FlowVariables.md b/docs/user/FlowVariables.md index 7ea1adf6aa1..48668d0a1be 100644 --- a/docs/user/FlowVariables.md +++ b/docs/user/FlowVariables.md @@ -277,7 +277,7 @@ configuration file. | SC_LEF| Path to technology standard cell LEF file.| | | SDC_FILE| The path to design constraint (SDC) file.| | | SEAL_GDS| Seal macro to place around the design.| | -| SEC_CHECK| Perform a sequential equivalence check between the initial synthesis netlist and final netlist with the Kepler Formal PDR engine. If this fails, report an issue to OpenROAD.| 0| +| SEC_CHECK| Perform a sequential equivalence check between the initial synthesis netlist and final netlist with the Kepler Formal PDR engine. Hard macros, the sub-blocks listed in BLOCKS and every BLOCK-class cell such as SRAM macros, are checked as black boxes (proof boundaries): they are the same cells in both netlists and their abstracts carry no logic. If this fails, report an issue to OpenROAD.| $(if $(wildcard $(KEPLER_FORMAL_EXE)),1,0)| | SETUP_MOVE_SEQUENCE| Passed as -sequence to repair_timing. This should be a string of move keywords separated by commas.| | | SETUP_REPAIR_PHASES| Passed as -phases to repair_timing. This should be a string of setup repair phase names separated by spaces. Set to PARALLEL_EVAL to repair setup timing with the experimental parallel evaluation policy. When unset, repair_timing runs its default phases.| | | SETUP_SLACK_MARGIN| Specifies a time margin for the slack when fixing setup violations. This option allows you to overfix or underfix(negative value, terminate retiming before 0 or positive slack). See HOLD_SLACK_MARGIN for more details.| 0| diff --git a/flow/designs/asap7/chi_async_bridge/config.mk b/flow/designs/asap7/chi_async_bridge/config.mk index e8691faa2d3..1f88d5ae1b5 100644 --- a/flow/designs/asap7/chi_async_bridge/config.mk +++ b/flow/designs/asap7/chi_async_bridge/config.mk @@ -37,4 +37,8 @@ export VERILOG_TOP_PARAMS = REQ_W 32 RSP_W 16 DAT_W 64 SNP_W 24 DEPTH 4 else ifeq ($(FLOW_VARIANT),medium) export VERILOG_TOP_PARAMS = DEPTH 4 endif + +# Temporary disabling: the asynchronous clock-domain crossings are outside +# the sequential check. +export SEC_CHECK = 0 export SETUP_REPAIR_PHASES = PARALLEL_EVAL diff --git a/flow/designs/asap7/ethmac/config.mk b/flow/designs/asap7/ethmac/config.mk index 4c14fa8f65f..b6d8a06abd0 100644 --- a/flow/designs/asap7/ethmac/config.mk +++ b/flow/designs/asap7/ethmac/config.mk @@ -13,3 +13,7 @@ export PLACE_DENSITY = 0.75 export GPL_PLACE_IOS = 1 export HOLD_SLACK_MARGIN = -10 + +# Temporary disabling: the design has several clock domains and the +# sequential check does not yet cover logic crossing between them. +export SEC_CHECK = 0 diff --git a/flow/designs/asap7/ethmac_lvt/config.mk b/flow/designs/asap7/ethmac_lvt/config.mk index eee9cdc19a9..0b4cd0ce173 100644 --- a/flow/designs/asap7/ethmac_lvt/config.mk +++ b/flow/designs/asap7/ethmac_lvt/config.mk @@ -17,4 +17,7 @@ export ASAP7_USE_VT = LVT export RECOVER_POWER = 1 +# Temporary disabling: the design has several clock domains and the +# sequential check does not yet cover logic crossing between them. +export SEC_CHECK = 0 export SETUP_REPAIR_PHASES = PARALLEL_EVAL diff --git a/flow/designs/ihp-sg13g2/i2c-gpio-expander/config.mk b/flow/designs/ihp-sg13g2/i2c-gpio-expander/config.mk index 53f618cb07a..24fbdc6b837 100644 --- a/flow/designs/ihp-sg13g2/i2c-gpio-expander/config.mk +++ b/flow/designs/ihp-sg13g2/i2c-gpio-expander/config.mk @@ -55,3 +55,7 @@ export PDN_TCL = $(DESIGN_HOME)/$(PLATFORM)/$(DESIGN_NICKNAME)/pdn.tcl export BLOCKS = I2cDeviceCtrl + +# Temporary disabling: the bidirectional IO pads are outside the sequential +# check. +export SEC_CHECK = 0 diff --git a/flow/designs/nangate45/mempool_group/config.mk b/flow/designs/nangate45/mempool_group/config.mk index 8cca19fda0f..8be9e2bcf9e 100644 --- a/flow/designs/nangate45/mempool_group/config.mk +++ b/flow/designs/nangate45/mempool_group/config.mk @@ -78,3 +78,7 @@ export CORE_AREA = 10 12 1090 1090 export RTLMP_MIN_CHANNEL_SIZE = 20 20 export SYNTH_HDL_FRONTEND = slang + +# Temporary disabling: the hierarchical netlist leaves memory write-mask pins +# undriven, which the sequential check cannot verify. +export SEC_CHECK = 0 diff --git a/flow/designs/nangate45/swerv/config.mk b/flow/designs/nangate45/swerv/config.mk index ecb13be9cf1..b66f6e80f4e 100644 --- a/flow/designs/nangate45/swerv/config.mk +++ b/flow/designs/nangate45/swerv/config.mk @@ -13,3 +13,7 @@ export TNS_END_PERCENT = 100 export SWAP_ARITH_OPERATORS = 1 export OPENROAD_HIERARCHICAL = 1 + +# Temporary disabling: the design has several clock domains and the +# sequential check does not yet cover logic crossing between them. +export SEC_CHECK = 0 diff --git a/flow/designs/nangate45/swerv_wrapper/config.mk b/flow/designs/nangate45/swerv_wrapper/config.mk index 861bc792a46..6921c366a5c 100644 --- a/flow/designs/nangate45/swerv_wrapper/config.mk +++ b/flow/designs/nangate45/swerv_wrapper/config.mk @@ -24,3 +24,7 @@ export TNS_END_PERCENT = 100 export SWAP_ARITH_OPERATORS = 1 export OPENROAD_HIERARCHICAL = 1 + +# Temporary disabling: the design has several clock domains and the +# sequential check does not yet cover logic crossing between them. +export SEC_CHECK = 0 diff --git a/flow/designs/sky130hd/chameleon/config.mk b/flow/designs/sky130hd/chameleon/config.mk index 471431dc901..18db3023dc9 100644 --- a/flow/designs/sky130hd/chameleon/config.mk +++ b/flow/designs/sky130hd/chameleon/config.mk @@ -51,3 +51,7 @@ export TNS_END_PERCENT = 100 export SETUP_REPAIR_PHASES = PARALLEL_EVAL export RTLMP_MIN_CHANNEL_SIZE = 70 70 + +# Temporary disabling: the hard macros carry no logic in their Liberty, which +# leaves no output for the sequential check to verify. +export SEC_CHECK = 0 diff --git a/flow/designs/sky130hd/microwatt/config.mk b/flow/designs/sky130hd/microwatt/config.mk index b813db69b2d..a6837f25ae8 100644 --- a/flow/designs/sky130hd/microwatt/config.mk +++ b/flow/designs/sky130hd/microwatt/config.mk @@ -49,5 +49,7 @@ endif export SWAP_ARITH_OPERATORS = 1 export OPENROAD_HIERARCHICAL = 1 -# Temporary disabling +# Temporary disabling: the hierarchical netlist is rejected by the +# equivalence checker (duplicate wire declaration). export LEC_CHECK = 0 +export SEC_CHECK = 0 diff --git a/flow/platforms/ihp-sg13g2/config.mk b/flow/platforms/ihp-sg13g2/config.mk index 577d5c41ea2..1783cbb61d0 100644 --- a/flow/platforms/ihp-sg13g2/config.mk +++ b/flow/platforms/ihp-sg13g2/config.mk @@ -168,7 +168,7 @@ export KLAYOUT_DRC_FILE ?= $(PLATFORM_DIR)/drc/sg13g2_minimal.lydrc export CDL_FILE ?= $(PLATFORM_DIR)/cdl/sg13g2_stdcell.cdl #export KLAYOUT_LVS_FILE = $(PLATFORM_DIR)/lvs/$(PLATFORM).lylvs -export REMOVE_CELLS_FOR_LEC ?= "bondpad_70* sg13g2*" +export REMOVE_CELLS_FOR_LEC ?= "bondpad_70*" # --------------------------------------------------------- # Final diff --git a/flow/scripts/formal_check.tcl b/flow/scripts/formal_check.tcl index 10c3187c20e..e5d079c882c 100644 --- a/flow/scripts/formal_check.tcl +++ b/flow/scripts/formal_check.tcl @@ -67,6 +67,86 @@ proc write_lec_script { step file1 file2 } { close $outfile } +# Hardened sub-blocks (BLOCKS) enter the parent netlist as timing-only +# abstracts with no logic model, so a formal check would treat every output +# cone that reaches one as opaque. Return the instance paths of all block +# instances in the loaded design; both netlists come from the same run, so the +# same path exists in both and each instance can be paired with itself. +# Escapes every unescaped `/` in an instance name as `\/`, the OpenSTA SDC +# convention kepler-formal reads: `/` separates hierarchy levels and `\/` is a +# literal slash inside one name. Existing escapes are kept as they are. +proc formal_escape_slashes { name } { + set escaped "" + set length [string length $name] + for { set i 0 } { $i < $length } { incr i } { + set ch [string index $name $i] + if { $ch eq "\\" } { + incr i + append escaped "\\" [string index $name $i] + } elseif { $ch eq "/" } { + append escaped "\\/" + } else { + append escaped $ch + } + } + return $escaped +} + +# Mirrors dbNetwork::stripParentPrefix: in a hierarchical database write_verilog +# names a leaf by what follows the last unescaped `/` of its database name. +proc formal_strip_parent_prefix { name } { + set pos [string last "/" $name] + while { $pos >= 0 } { + if { $pos == 0 || [string index $name [expr { $pos - 1 }]] ne "\\" } { + return [string range $name [expr { $pos + 1 }] end] + } + set pos [string last "/" $name [expr { $pos - 2 }]] + } + return $name +} + +# Boundary path of a block instance exactly as write_verilog names it: in a flat +# database the whole instance name is one leaf (`\u0/r0 ` in Verilog, so the +# path is `u0\/r0`); in a hierarchical database the leaf keeps its module +# instance path and loses the parent prefix. Paths are never reinterpreted. +proc formal_boundary_path { inst } { + set name [$inst getName] + set prefix "" + if { [[ord::get_db] hasHierarchy] } { + set mod_inst [[$inst getModule] getModInst] + if { $mod_inst != "NULL" } { + set prefix "[$mod_inst getHierarchicalName]/" + } + set name [formal_strip_parent_prefix $name] + } + return "$prefix[formal_escape_slashes $name]" +} + +# Hard macros are proof boundaries: the instances of the masters listed in +# BLOCKS, and every instance of a BLOCK-class master (SRAM macros, hardened +# sub-blocks). A macro is the same cell in both netlists, so only its +# connections need checking; expanding its contents into state would only make +# the proof slower. +proc formal_block_instance_paths { } { + set paths {} + set blocks {} + if { [env_var_exists_and_non_empty BLOCKS] } { + set blocks $::env(BLOCKS) + } + set block [ord::get_db_block] + foreach inst [$block getInsts] { + set master [$inst getMaster] + if { [$master isBlock] || [lsearch -exact $blocks [$master getName]] >= 0 } { + lappend paths [formal_boundary_path $inst] + } + } + return [lsort $paths] +} + +proc formal_yaml_quote { text } { + return "\"[string map {\\ \\\\ \" \\\"} $text]\"" +} + proc write_sec_script { step file1 file2 } { set outfile [open "$::env(OBJECTS_DIR)/${step}_sec_test.yml" w] puts $outfile "format: verilog" @@ -79,6 +159,17 @@ proc write_sec_script { step file1 file2 } { foreach libFile $::env(LIB_FILES) { puts $outfile " - $libFile" } + # Each macro instance becomes a proof boundary: its input pins are compared + # across the two netlists and its output pins are shared free inputs, so the + # macro is checked as a black box instead of hiding the cones behind it. + set boundaries [formal_block_instance_paths] + if { [llength $boundaries] > 0 } { + puts $outfile "set_as_boundary:" + foreach path $boundaries { + set quoted [formal_yaml_quote $path] + puts $outfile " - \[$quoted, $quoted\]" + } + } puts $outfile "log_file: $::env(LOG_DIR)/${step}_sec_check.log" close $outfile } diff --git a/flow/scripts/variables.json b/flow/scripts/variables.json index d1e156a8082..70406f9c1a9 100644 --- a/flow/scripts/variables.json +++ b/flow/scripts/variables.json @@ -1146,8 +1146,8 @@ "description": "Seal macro to place around the design.\n" }, "SEC_CHECK": { - "default": 0, - "description": "Perform a sequential equivalence check between the initial synthesis netlist and final netlist with the Kepler Formal PDR engine. If this fails, report an issue to OpenROAD.\n", + "default": "$(if $(wildcard $(KEPLER_FORMAL_EXE)),1,0)", + "description": "Perform a sequential equivalence check between the initial synthesis netlist and final netlist with the Kepler Formal PDR engine. Hard macros, the sub-blocks listed in BLOCKS and every BLOCK-class cell such as SRAM macros, are checked as black boxes (proof boundaries): they are the same cells in both netlists and their abstracts carry no logic. If this fails, report an issue to OpenROAD.\n", "stages": [ "synth", "final" diff --git a/flow/scripts/variables.yaml b/flow/scripts/variables.yaml index 1af977b0e68..aff64df54b9 100644 --- a/flow/scripts/variables.yaml +++ b/flow/scripts/variables.yaml @@ -1689,9 +1689,12 @@ LEC_CHECK: SEC_CHECK: description: > Perform a sequential equivalence check between the initial synthesis - netlist and final netlist with the Kepler Formal PDR engine. If this - fails, report an issue to OpenROAD. - default: 0 + netlist and final netlist with the Kepler Formal PDR engine. Hard macros, + the sub-blocks listed in BLOCKS and every BLOCK-class cell such as SRAM + macros, are checked as black boxes (proof boundaries): they are the same + cells in both netlists and their abstracts carry no logic. If this fails, + report an issue to OpenROAD. + default: $(if $(wildcard $(KEPLER_FORMAL_EXE)),1,0) stages: - synth - final diff --git a/tools/kepler-formal b/tools/kepler-formal index f70c2e38592..46113b2f6d2 160000 --- a/tools/kepler-formal +++ b/tools/kepler-formal @@ -1 +1 @@ -Subproject commit f70c2e38592d704c8cccf68433b091e82c8118f0 +Subproject commit 46113b2f6d295e4771623e48b754b6f8fd95efae