Tcl · Module 23
Power Planning Commands
The power structure is built early and everything is placed around it. An unconnected net found after routing costs a rebuild; found right after the structure is built, it costs a rerun.
Intermediate15 min readtcledapart-iiphysical-designpowerconnectivity
Part II · Module 23 · Chapter 23.2 · Power Planning Commands
Every cell needs power and ground. The power structure — rings, straps, rails — is built right after the floorplan, and everything afterwards is placed and routed around it.
Which is why a mistake here has the worst cost curve in the flow.
1. The cost of finding it late
An unconnected power net discovered after routing means the structure has to change; the structure changing means placement and routing built on top of it are invalid; so the fix is not a fix but a rebuild of most of the implementation.
The same net discovered immediately after the structure is built costs one command rerun.
Nothing about the check changes between those two moments. Only when you run it.
2. Building the structure
add_power_strap M7 vertical 40 4
add_power_strap M8 horizontal 40 4=== build the structure ===
M7 vertical pitch 40 width 4
M8 horizontal pitch 40 width 4Real commands take far more than four parameters, and the names are vendor-specific. What is consistent is the shape: a structure described by numbers, usually on upper metal layers, in two directions so the mesh connects.
Because the parameters are numbers in a script, they can be computed rather than written — a pitch derived from the available routing resource, a width from the current the block draws. That is Part I arithmetic applied to a physical decision, the same as Chapter 23.1 §3's utilisation.
3. A structure is not automatically a mesh
The two commands in §2 built straps in two directions. Nothing required them to.
add_power_strap M7 vertical 40 4
add_power_strap M9 vertical 80 6Both commands succeed. Two layers of straps exist. And there is no mesh, because straps running the same way never cross — so nothing ties the two layers together and nothing distributes current across the block.
So count the directions:
proc strap_directions {} {
set d [dict create]
foreach s $::pd::straps {
lassign $s layer dir pitch width
dict incr d $dir
}
return $d
}
proc check_mesh {} {
set d [strap_directions]
foreach dir {vertical horizontal} {
if {![dict exists $d $dir]} {
error "no $dir straps: this is not a mesh" {} [list PD NOMESH $dir]
}
}
return [dict get $d vertical],[dict get $d horizontal]
}=== a structure that is not a mesh ===
M7 vertical pitch 40 width 4
M9 vertical pitch 80 width 6
vertical straps : 2
check_mesh : 1 / no horizontal straps: this is not a mesh
=== the same structure with the other direction added ===
M7 vertical pitch 40 width 4
M9 vertical pitch 80 width 6
M8 horizontal pitch 40 width 4
vertical straps : 2
horizontal straps : 1
check_mesh : 0 / 2,1Three Part I details carry the whole check.
dict incr builds the tally without a separate "does this key exist yet" step — it treats a missing key as zero. That is Chapter 7.2's counting idiom, and it is why strap_directions is five lines rather than ten.
dict exists rather than dict get in the loop. dict get on a missing key raises, which would produce key "horizontal" not known in dictionary — a true statement about a dictionary, and useless to the engineer who needs to be told their power structure has no mesh. Chapter 7.3 is about exactly that substitution.
The loop iterates over the directions, not over the dictionary. Iterating $d can only visit directions that are present, so it could never detect an absent one. Checking for absence means looping over the expected set — the same inversion §4 applies to the net list, and the reason that list has to be declared at all.
check_mesh returns the pair 2,1 rather than just succeeding, so the log records what it saw. A bare success tells a later reader the check ran; 2,1 tells them what it ran on.
4. Declare what must be connected
The check needs something to check against, and that is a declared list:
declare_power_nets {VDD VSS VDD_AON}This is the expectation idea from Chapter 19.5 §6 and Chapter 21.8 §4: stating what should be true makes the absence detectable. Without it, "are all the power nets connected" has no answer, because nothing says what all means.
5. Verify
connect_power {VDD VSS}
set r [report_power_connectivity]
puts [format " required : %d" [dict get $r required]]
puts [format " connected : %d" [dict get $r connected]]
puts [format " missing : %s" ...]=== connect, but miss one ===
required : 3
connected : 2
missing : VDD_AON6. The check that stops the flow
proc check_power {} {
set r [report_power_connectivity]
set m [dict get $r missing]
if {[llength $m]} {
error "[llength $m] power net(s) not connected: [join $m {, }]" {} [list PD NOPOWER $m]
}
return [dict get $r connected]
}=== the check that refuses to continue ===
with VDD_AON missing : 1 / 1 power net(s) not connected: VDD_AON
after connecting it : 3 net(s) connectedIt raises rather than warning, and this is the right call here — unlike Chapter 23.1 §6's oversized die, there is no circumstance in which proceeding with an unconnected supply is useful. The cost curve from §1 is the justification: continuing wastes hours and produces something that must be rebuilt anyway.
7. What the declared list cannot catch
Being honest about the limits, because a check that is trusted beyond its scope is worse than none (Chapter 22.9 §7).
A declared-versus-connected comparison establishes that each named net was reached. It does not establish that:
- Every cell is connected to the structure. A cell placed somewhere the mesh does not reach is a different failure, and the tool's own connectivity check is what finds it.
- The structure is adequate. Connected is not the same as able to carry the current without excessive drop — that is an IR-drop analysis, a separate step.
- The declared list is complete. If a domain was added and nobody updated the list, the check passes. The list has the same review obligation as Chapter 19.3 §5's allowlist: an entry nobody can justify, or a domain nobody added, is the failure mode.
So: run the tool's own power-connectivity check as well, and treat the declared list as the thing that catches the common case cheaply and immediately.
8. Record it
The same discipline as everywhere in Part II — print what was built, into the log:
puts " power nets : [join $::pd::need_power {, }]"
puts " straps : [llength $::pd::straps]"
foreach s $::pd::straps { puts [format " %-3s %-10s pitch %s width %s" {*}$s] }
puts " connected : [check_power] of [llength $::pd::need_power]"A later question — "which supplies did this block's structure actually carry" — then has an answer in the run's own log rather than requiring somebody to open the database.
9. Exercises
Exercise 1. Why does a power-planning mistake have the worst cost curve in the flow?
Exercise 2. What does the check cost when run immediately, versus when the problem is found after routing?
Exercise 3. Two layers of vertical straps are added and both commands succeed. Why is that not a power mesh, and why does the connectivity check pass it anyway?
Exercise 4. Why does check_mesh loop over {vertical horizontal} rather than over the tally dictionary?
Exercise 5. Why dict exists rather than dict get inside that loop?
Exercise 6. Why does the connectivity check need a declared list?
Exercise 7. Why is an always-on net the realistic omission?
Exercise 8. Why does check_power raise rather than warn, when the floorplan check warned on an oversized die?
Exercise 9. Name two things a declared-versus-connected check cannot establish.
Exercise 10. What is the review obligation on the declared list itself?
Where this fits
Part of the Tcl curriculum.
