forked from The-OpenROAD-Project/OpenROAD-flow-scripts
-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathutil.tcl
More file actions
315 lines (284 loc) · 9.7 KB
/
Copy pathutil.tcl
File metadata and controls
315 lines (284 loc) · 9.7 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
# Extract cell names
proc get_liberty_cell_names { } {
return [tee -q -s result.string select -list-mod =A:liberty_cell]
}
proc log_cmd { cmd args } {
# log the command, escape arguments with spaces
set log_cmd "$cmd[join [lmap arg $args { format " %s" [expr { [string match {* *} $arg] ? "\"$arg\"" : "$arg" }] }] ""]" ;# tclint-disable-line line-length
puts $log_cmd
# Tcl's `clock` lives in clock.tcl, auto-loaded from TCL_LIBRARY. Hermetic
# build environments (e.g. Bazel linking yosys against @tcl_lang) don't
# always ship the Tcl library in runfiles, so `clock` may be undefined.
# The timing log is cosmetic — skip it when `clock` isn't available.
set has_clock [expr { [info commands clock] ne "" }]
if { $has_clock } {
set start [clock seconds]
}
set result [uplevel 1 [list $cmd {*}$args]]
if { $has_clock } {
set time [expr { [clock seconds] - $start }]
if { $time >= 5 } {
# Ideally we'd use a single line, but the command can output text
# and we don't want to mix it with the log, so output the time it took afterwards.
puts "Took $time seconds: $log_cmd"
}
}
return $result
}
proc repair_timing_helper { args } {
set additional_args {}
append_env_var additional_args SETUP_SLACK_MARGIN -setup_margin 1
append_env_var additional_args HOLD_SLACK_MARGIN -hold_margin 1
append_env_var additional_args SETUP_MOVE_SEQUENCE -sequence 1
append_env_var additional_args TNS_END_PERCENT -repair_tns 1
append_env_var additional_args SKIP_PIN_SWAP -skip_pin_swap 0
append_env_var additional_args SKIP_GATE_CLONING -skip_gate_cloning 0
append_env_var additional_args SKIP_BUFFER_REMOVAL -skip_buffer_removal 0
append_env_var additional_args SKIP_LAST_GASP -skip_last_gasp 0
append_env_var additional_args SKIP_VT_SWAP -skip_vt_swap 0
append_env_var additional_args SKIP_CRIT_VT_SWAP -skip_crit_vt_swap 0
append_env_var additional_args MATCH_CELL_FOOTPRINT -match_cell_footprint 0
lappend additional_args {*}$args -verbose
log_cmd repair_timing {*}$additional_args
}
proc repair_design_helper { } {
puts "Perform buffer insertion and gate resizing..."
set additional_args "-verbose"
append_env_var additional_args CAP_MARGIN -cap_margin 1
append_env_var additional_args SLEW_MARGIN -slew_margin 1
append_env_var additional_args MATCH_CELL_FOOTPRINT -match_cell_footprint 0
log_cmd repair_design {*}$additional_args
}
proc recover_power_helper { } {
if { $::env(RECOVER_POWER) == 0 } {
return
}
puts "Downsizing/switching to higher Vt for non critical gates for power recovery"
puts "Percent of paths optimized $::env(RECOVER_POWER)"
report_tns
report_wns
report_power
set additional_args "-verbose"
append_env_var additional_args RECOVER_POWER -recover_power 1
append_env_var additional_args MATCH_CELL_FOOTPRINT -match_cell_footprint 0
log_cmd repair_timing {*}$additional_args
report_tns
report_wns
report_power
}
proc extract_stage { input_file } {
# Match the stage prefix on the basename, not the full path: an ancestor
# dir like ".../4_something/3_place.odb" would otherwise match "4_" -> stage 4.
set stage_file [file tail $input_file]
if { ![regexp {^([0-9])_(?:([0-9])_)?} $stage_file match num1 num2] } {
puts "Error: Could not determine design stage from $input_file"
exit 1
}
lappend number_groups $num1
if { $num2 != "" } {
lappend number_groups $num2
} else {
lappend number_groups "0"
}
}
proc find_sdc_file { input_file } {
# canonicalize input file, sometimes it is called with an input
# file relative to $::env(RESULTS_DIR), other times with
# an absolute path
if { ![file exists $input_file] } {
set input_file [file join $::env(RESULTS_DIR) $input_file]
}
set input_file [file normalize $input_file]
set stage [extract_stage $input_file]
set design_stage [lindex $stage 0]
set sdc_file ""
set exact_sdc [string map {.odb .sdc} $input_file]
set sdc_files \
[glob -nocomplain -directory $::env(RESULTS_DIR) -types f "\[1-9+\]_\[1-9_A-Za-z\]*\.sdc"]
set sdc_files [lsort -decreasing -dictionary $sdc_files]
set sdc_files [lmap file $sdc_files { file normalize $file }]
foreach name $sdc_files {
if { [lindex [lsort -decreasing -dictionary [list $name $exact_sdc]] 0] == $exact_sdc } {
set sdc_file $name
break
}
}
return [list $design_stage $sdc_file]
}
proc env_var_equals { env_var value } {
return [expr { [info exists ::env($env_var)] && $::env($env_var) == $value }]
}
proc env_var_exists_and_non_empty { env_var } {
return [expr { [info exists ::env($env_var)] && ![string equal $::env($env_var) ""] }]
}
proc append_env_var { list_name var_name prefix has_arg } {
upvar $list_name list
if {
(!$has_arg && [env_var_equals $var_name 1]) ||
($has_arg && [env_var_exists_and_non_empty $var_name])
} {
lappend list $prefix
if { $has_arg } {
lappend list $::env($var_name)
}
}
}
# Non-empty defaults should go into variables.yaml, generally
proc env_var_or_empty { env_var } {
if { [env_var_exists_and_non_empty $env_var] } {
return $::env($env_var)
}
return ""
}
proc find_macros { } {
set macros ""
set db [ord::get_db]
set block [[$db getChip] getBlock]
foreach inst [$block getInsts] {
set inst_master [$inst getMaster]
# BLOCK means MACRO cells
if { [string match [$inst_master getType] "BLOCK"] } {
append macros " " $inst
}
}
return $macros
}
proc erase_non_stage_variables { stage_name } {
if { $::env(KEEP_VARS) } {
return
}
# "$::env(SCRIPTS_DIR)/stage_variables.py stage_name" returns list of
# variables to erase.
#
# Tcl yaml package can't be imported in the sta/openroad environment:
#
# https://github.com/The-OpenROAD-Project/OpenROAD/issues/5875
set variables [exec $::env(SCRIPTS_DIR)/non_stage_variables.py $stage_name]
foreach var $variables {
if { [info exists ::env($var)] } {
unset ::env($var)
}
}
}
set global_route_congestion_report $::env(REPORTS_DIR)/congestion.rpt
proc place_density_with_lb_addon { } {
if { [env_var_exists_and_non_empty PLACE_DENSITY_LB_ADDON] } {
# check the lower boundary of the PLACE_DENSITY and add PLACE_DENSITY_LB_ADDON
set place_density_lb [gpl::get_global_placement_uniform_density \
-pad_left $::env(CELL_PAD_IN_SITES_GLOBAL_PLACEMENT) \
-pad_right $::env(CELL_PAD_IN_SITES_GLOBAL_PLACEMENT)]
set place_density \
[expr $place_density_lb + ((1.0 - $place_density_lb) * $::env(PLACE_DENSITY_LB_ADDON)) + 0.01]
if { $place_density > 1.0 } {
utl::error FLW 24 \
"Place density exceeds 1.0 (current PLACE_DENSITY_LB_ADDON = \
$::env(PLACE_DENSITY_LB_ADDON)). Please check if the value of \
PLACE_DENSITY_LB_ADDON is between 0 and 0.99."
}
puts "Placement density is $place_density, computed from PLACE_DENSITY_LB_ADDON \
$::env(PLACE_DENSITY_LB_ADDON) and lower bound $place_density_lb"
} else {
set place_density $::env(PLACE_DENSITY)
}
return $place_density
}
proc source_env_var_if_exists { env_var } {
if { [env_var_exists_and_non_empty $env_var] } {
log_cmd source $::env($env_var)
}
}
# Feature toggle for now, eventually the -hier option
# will be default and this code will be deleted.
proc hier_options { } {
if {
([env_var_exists_and_non_empty SYNTH_WRAPPED_OPERATORS] ||
[env_var_exists_and_non_empty SWAP_ARITH_OPERATORS]) &&
!$::env(OPENROAD_HIERARCHICAL)
} {
error "SYNTH_WRAPPED_OPERATORS or SWAP_ARITH_OPERATORS require OPENROAD_HIERARCHICAL to be set."
}
if { $::env(OPENROAD_HIERARCHICAL) } {
return "-hier"
} else {
return ""
}
}
proc is_physical_only_master { master } {
set physical_only_type_patterns [list \
"COVER" \
"COVER_BUMP" \
"RING" \
"PAD_SPACER" \
"CORE_FEEDTHROUGH" \
"CORE_SPACER" \
"CORE_ANTENNACELL" \
"CORE_WELLTAP" \
"ENDCAP*"]
set master_type [$master getType]
foreach pattern $physical_only_type_patterns {
if { [string match $pattern $master_type] } {
return 1
}
}
return 0
}
# Returns 1 if the master has no signal pins (only power/ground or none).
proc has_signal_pins { master } {
foreach mterm [$master getMTerms] {
set sig_type [$mterm getSigType]
if { $sig_type != "POWER" && $sig_type != "GROUND" } {
return 1
}
}
return 0
}
# Returns 1 if the master has a corresponding liberty cell.
proc has_liberty_cell { master } {
set master_name [$master getName]
set lib_cells [get_lib_cells -quiet */$master_name]
if { $lib_cells == {} } {
return 0
}
return 1
}
# Finds all physical-only masters in the current database and
# returns their names.
proc find_physical_only_masters { } {
set db [::ord::get_db]
set libs [$db getLibs]
set physical_only_masters [list]
foreach lib $libs {
foreach master [$lib getMasters] {
set master_name [$master getName]
if { [is_physical_only_master $master] } {
lappend physical_only_masters $master_name
continue
}
# Consider cells with no signal pins and no liberty cell as physical-only
if { [has_liberty_cell $master] == 0 } {
if { [has_signal_pins $master] == 0 } {
lappend physical_only_masters $master_name
} else {
puts "Warning: master $master_name has signal pins but no liberty cell"
}
}
}
}
return $physical_only_masters
}
proc orfs_write_db { output_file } {
if { !$::env(WRITE_ODB_AND_SDC_EACH_STAGE) } {
return
}
log_cmd write_db $output_file
}
proc orfs_write_sdc { output_file } {
if { !$::env(WRITE_ODB_AND_SDC_EACH_STAGE) } {
return
}
log_cmd write_sdc -no_timestamp $output_file
}
proc source_step_tcl { hook_type step_name } {
set env_var "${hook_type}_${step_name}_TCL"
source_env_var_if_exists $env_var
}