Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions flow/scripts/pdn.tcl
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,21 @@ erase_non_stage_variables floorplan
load_design 2_3_floorplan_tapcell.odb 2_1_floorplan.sdc
source_step_tcl PRE PDN

# A supply pin on no net changes no logic when it is connected: lift
# dont_touch for the platform's global connections, restore it after.
set lifted [dont_touch_unsupplied]
foreach inst $lifted {
$inst setDoNotTouch 0
}
source $::env(PDN_TCL)
foreach inst $lifted {
$inst setDoNotTouch 1
}
if { [llength $lifted] > 0 } {
utl::info FLW 18 "[llength $lifted] dont_touch instances had a supply pin\
on no net; connected by the global connections"
}

pdngen

source_step_tcl POST PDN
Expand Down
22 changes: 22 additions & 0 deletions flow/scripts/util.tcl
Original file line number Diff line number Diff line change
Expand Up @@ -506,3 +506,25 @@ proc synth_verilog_files { } {
}
return $copies
}

# global_connect skips dont_touch instances (ODB-0383, a warning each),
# so an instance made dont_touch before the PDN step keeps its supply
# pins on no net: unpowered in the netlist, LVS and power analysis. The
# dont_touch instances with a supply pin on no net, for the PDN step to
# lift dont_touch from while it makes the global connections.
proc dont_touch_unsupplied { } {
set insts {}
foreach inst [[ord::get_db_block] getInsts] {
if { ![$inst isDoNotTouch] } {
continue
}
foreach iterm [$inst getITerms] {
set type [[$iterm getMTerm] getSigType]
if { ($type eq "POWER" || $type eq "GROUND") && [$iterm getNet] eq "NULL" } {
lappend insts $inst
break
}
}
}
return $insts
}
116 changes: 116 additions & 0 deletions flow/test/test_pdn_dont_touch_supplies.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
#!/usr/bin/env python3
"""The PDN step connects the supply pins of dont_touch instances.

global_connect skips a dont_touch instance, so an instance made
dont_touch before the PDN step kept its supply pins on no net.
dont_touch_unsupplied (util.tcl) names those instances, the PDN step
lifts dont_touch from them for the global connections and restores it
after; an instance whose supply pins are already on a net, or that is
not dont_touch, is not named.

Skipped when no openroad is available: OPENROAD_EXE or openroad on PATH.
"""

import os
import shutil
import subprocess
import tempfile
import unittest

FLOW = os.path.join(os.path.dirname(os.path.abspath(__file__)), "..")
UTIL_TCL = os.path.join(FLOW, "scripts", "util.tcl")
LEF = os.path.join(FLOW, "platforms", "nangate45", "lef")

DESIGN = """\
module top (input a, output y0, output y1, output y2);
INV_X1 kept (.A(a), .ZN(y0));
INV_X1 free (.A(a), .ZN(y1));
INV_X1 supplied (.A(a), .ZN(y2));
endmodule
"""

SETUP = f"""\
set ::env(REPORTS_DIR) [pwd]
source {{{UTIL_TCL}}}
read_lef {{{os.path.join(LEF, "NangateOpenCellLibrary.tech.lef")}}}
read_lef {{{os.path.join(LEF, "NangateOpenCellLibrary.macro.mod.lef")}}}
read_verilog top.v
link_design top
set block [ord::get_db_block]
proc supply_net {{ name }} {{
set net [[[ord::get_db_block] findInst $name] findITerm VDD]
set net [$net getNet]
if {{ $net eq "NULL" }} {{
return none
}}
return [$net getName]
}}
foreach pin {{VDD VSS}} {{
[[$block findInst supplied] findITerm $pin] connect \\
[odb::dbNet_create $block supplied_[string tolower $pin]]
}}
[$block findInst supplied] setDoNotTouch 1
[$block findInst kept] setDoNotTouch 1
"""

# The platform's PDN script: add_global_connection connects at once.
GLOBAL_CONNECTIONS = """\
add_global_connection -net VDD -pin_pattern {^VDD$} -power
add_global_connection -net VSS -pin_pattern {^VSS$} -ground
global_connect
"""


def openroad_command():
openroad = os.environ.get("OPENROAD_EXE") or shutil.which("openroad")
return [openroad, "-no_init", "-no_splash", "-exit"] if openroad else None


class TestPdnDontTouchSupplies(unittest.TestCase):
def run_openroad(self, body):
cmd = openroad_command()
if cmd is None:
self.skipTest("no openroad")
with tempfile.TemporaryDirectory() as tmp:
with open(os.path.join(tmp, "top.v"), "w") as f:
f.write(DESIGN)
with open(os.path.join(tmp, "test.tcl"), "w") as f:
f.write(SETUP + body)
result = subprocess.run(
cmd + ["test.tcl"], cwd=tmp, capture_output=True, text=True
)
output = result.stdout + result.stderr
self.assertEqual(result.returncode, 0, output)
return output

def test_global_connect_skips_dont_touch(self):
# The problem the PDN step works around, so this test notices
# when global_connect stops skipping dont_touch instances.
output = self.run_openroad(
GLOBAL_CONNECTIONS
+ 'puts "kept=[supply_net kept] free=[supply_net free]"\n'
)
self.assertIn("kept=none free=VDD", output)

def test_names_only_unsupplied_dont_touch(self):
output = self.run_openroad(
'puts "lifted=[lmap i [dont_touch_unsupplied] { $i getName }]"\n'
)
self.assertIn("lifted=kept\n", output)

def test_lifted_for_global_connections(self):
# pdn.tcl's sequence around the platform's PDN script
output = self.run_openroad(
"set lifted [dont_touch_unsupplied]\n"
"foreach inst $lifted { $inst setDoNotTouch 0 }\n"
+ GLOBAL_CONNECTIONS
+ "foreach inst $lifted { $inst setDoNotTouch 1 }\n"
'puts "kept=[supply_net kept] supplied=[supply_net supplied]"\n'
'puts "dont_touch=[[$block findInst kept] isDoNotTouch]"\n'
)
self.assertIn("kept=VDD supplied=supplied_vdd", output)
self.assertIn("dont_touch=1", output)


if __name__ == "__main__":
unittest.main()