forked from The-OpenROAD-Project/OpenSTA
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathGraph.tcl
410 lines (371 loc) · 11.7 KB
/
Graph.tcl
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
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
# OpenSTA, Static Timing Analyzer
# Copyright (c) 2022, Parallax Software, Inc.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.
# Graph utilities
namespace eval sta {
define_cmd_args "report_edges" {[-from from_pin] [-to to_pin]}
proc report_edges { args } {
parse_key_args "report_edges" args keys {-from -to} flags {}
check_argc_eq0 "report_edges" $args
if { [info exists keys(-from)] && [info exists keys(-to)] } {
set from_pin [get_port_pin_error "from_pin" $keys(-from)]
set to_pin [get_port_pin_error "to_pin" $keys(-to)]
foreach from_vertex [$from_pin vertices] {
foreach to_vertex [$to_pin vertices] {
report_edges_between_ $from_vertex $to_vertex
}
}
} elseif [info exists keys(-from)] {
set from_pin [get_port_pin_error "from_pin" $keys(-from)]
foreach from_vertex [$from_pin vertices] {
report_edges_ $from_vertex out_edge_iterator \
vertex_port_name vertex_path_name
}
} elseif [info exists keys(-to)] {
set to_pin [get_port_pin_error "to_pin" $keys(-to)]
foreach to_vertex [$to_pin vertices] {
report_edges_ $to_vertex in_edge_iterator \
vertex_path_name vertex_port_name
}
}
}
proc report_edges_between_ { from_vertex to_vertex } {
set iter [$from_vertex out_edge_iterator]
while {[$iter has_next]} {
set edge [$iter next]
if { [$edge to] == $to_vertex } {
if { [$edge role] == "wire" } {
report_edge_ $edge vertex_path_name vertex_path_name
} else {
report_edge_ $edge vertex_port_name vertex_port_name
}
}
}
$iter finish
}
proc report_edges_ { vertex iter_proc wire_from_name_proc wire_to_name_proc } {
# First report edges internal to the device.
set device_header 0
set iter [$vertex $iter_proc]
while {[$iter has_next]} {
set edge [$iter next]
if { [$edge role] != "wire" } {
if { !$device_header } {
set pin [$vertex pin]
if { ![$pin is_top_level_port] } {
set inst [$pin instance]
}
set device_header 1
}
report_edge_ $edge vertex_port_name vertex_port_name
}
}
$iter finish
# Then wires.
set iter [$vertex $iter_proc]
while {[$iter has_next]} {
set edge [$iter next]
if { [$edge role] == "wire" } {
report_edge_ $edge $wire_from_name_proc $wire_to_name_proc
}
}
$iter finish
}
proc report_edge_ { edge vertex_from_name_proc vertex_to_name_proc } {
global sta_report_default_digits
set latch_enable [$edge latch_d_to_q_en]
if { $latch_enable != "" } {
set latch_enable " enable $latch_enable"
}
report_line "[$vertex_from_name_proc [$edge from]] -> [$vertex_to_name_proc [$edge to]] [$edge role]$latch_enable"
set disables [edge_disable_reason $edge]
if { $disables != "" } {
report_line " Disabled by $disables"
}
set cond [$edge cond]
if { $cond != "" } {
report_line " Condition: $cond"
}
set mode_name [$edge mode_name]
if { $mode_name != "" } {
report_line " Mode: $mode_name [$edge mode_value]"
}
foreach arc [$edge timing_arcs] {
set delays [$edge arc_delay_strings $arc $sta_report_default_digits]
set delays_fmt [format_delays $delays]
set disable_reason ""
if { [timing_arc_disabled $edge $arc] } {
set disable_reason " disabled"
}
report_line " [$arc from_edge] -> [$arc to_edge] $delays_fmt$disable_reason"
}
}
# Separate list elements with colons.
proc format_times { values digits } {
set result ""
foreach value $values {
if { $result != "" } {
append result ":"
}
append result [format_time $value $digits]
}
return $result
}
# Separate delay list elements with colons.
proc format_delays { values } {
set result ""
foreach value $values {
if { $result != "" } {
append result ":"
}
append result $value
}
return $result
}
proc edge_disable_reason { edge } {
set disables ""
if [$edge is_disabled_constraint] {
append disables "constraint"
}
if [$edge is_disabled_constant] {
if { $disables != "" } { append disables ", " }
append disables "constant"
}
if [$edge is_disabled_cond_default] {
if { $disables != "" } { append disables ", " }
append disables "cond_default"
}
if [$edge is_disabled_loop] {
if { $disables != "" } { append disables ", " }
append disables "loop"
}
if [$edge is_disabled_bidirect_inst_path] {
if { $disables != "" } { append disables ", " }
append disables "bidirect instance path"
}
if [$edge is_disabled_bidirect_net_path] {
if { $disables != "" } { append disables ", " }
append disables "bidirect net path"
}
if { [$edge is_disabled_preset_clear] } {
if { $disables != "" } { append disables ", " }
append disables "timing_enable_preset_clear_arcs"
}
return $disables
}
################################################################
define_cmd_args "report_constant" {pin|instance|net}
proc report_constant { obj } {
parse_inst_port_pin_net_arg $obj insts pins nets
foreach pin $pins {
report_pin_constant $pin
}
foreach inst $insts {
set pin_iter [$inst pin_iterator]
while {[$pin_iter has_next]} {
set pin [$pin_iter next]
report_pin_constant $pin
}
$pin_iter finish
}
foreach net $nets {
set pin_iter [$net pin_iterator]
while {[$pin_iter has_next]} {
set pin [$pin_iter next]
report_pin_constant $pin
}
$pin_iter finish
}
}
proc report_pin_constant { pin } {
set sim_value [pin_sim_logic_value $pin]
set case_value [pin_case_logic_value $pin]
if { $case_value != "X" } {
set case " case=$case_value"
} else {
set case ""
}
set logic_value [pin_logic_value $pin]
if { $logic_value != "X" } {
set logic " logic=$logic_value"
} else {
set logic ""
}
report_line "[pin_property $pin lib_pin_name] $sim_value$case$logic"
}
################################################################
proc report_disabled_edges {} {
foreach edge [disabled_edges_sorted] {
if { [$edge role] == "wire" } {
set from_pin_name [get_full_name [[$edge from] pin]]
set to_pin_name [get_full_name [[$edge to] pin]]
report_line "$from_pin_name $to_pin_name [edge_disable_reason_verbose $edge]"
} else {
set from_pin [[$edge from] pin]
set to_pin [[$edge to] pin]
set inst_name [get_full_name [$from_pin instance]]
set from_port_name [get_name [$from_pin port]]
set to_port_name [get_name [$to_pin port]]
set cond [$edge cond]
if { $cond != "" } {
set when " when: $cond"
} else {
set when ""
}
report_line "$inst_name $from_port_name $to_port_name$when [edge_disable_reason_verbose $edge]"
}
}
}
proc edge_disable_reason_verbose { edge } {
set disables ""
if { [$edge is_disabled_constraint] } {
append disables "constraint"
}
if { [$edge is_disabled_constant] } {
if { $disables != "" } { append disables ", " }
append disables "constant"
set sense [$edge sim_timing_sense]
if { $sense == "positive_unate" || $sense == "negative_unate" } {
append disables " $sense"
}
set const_pins [$edge disabled_constant_pins]
foreach pin [sort_by_full_name $const_pins] {
set port_name [pin_property $pin lib_pin_name]
set value [pin_sim_logic_value $pin]
append disables " $port_name=$value"
}
}
if { [$edge is_disabled_cond_default] } {
if { $disables != "" } { append disables ", " }
append disables "cond_default"
}
if { [$edge is_disabled_loop] } {
if { $disables != "" } { append disables ", " }
append disables "loop"
}
if { [$edge is_disabled_preset_clear] } {
if { $disables != "" } { append disables ", " }
append disables "timing_enable_preset_clear_arcs"
}
return $disables
}
################################################################
define_cmd_args "report_slews" {[-corner corner] pin}
proc report_slews { args } {
global sta_report_default_digits
parse_key_args "report_slews" args keys {-corner} flags {}
check_argc_eq1 "report_slews" $args
set corner [parse_corner_or_all keys]
set pin [get_port_pin_error "pin" [lindex $args 0]]
set digits $sta_report_default_digits
foreach vertex [$pin vertices] {
if { $corner == "NULL" } {
report_line "[vertex_path_name $vertex] [rise_short_name] [format_time [$vertex slew rise min] $digits]:[format_time [$vertex slew rise max] $digits] [fall_short_name] [format_time [$vertex slew fall min] $digits]:[format_time [$vertex slew fall max] $digits]"
} else {
report_line "[vertex_path_name $vertex] [rise_short_name] [format_time [$vertex slew_corner rise $corner min] $digits]:[format_time [$vertex slew_corner rise $corner max] $digits] [fall_short_name] [format_time [$vertex slew_corner fall $corner min] $digits]:[format_time [$vertex slew_corner fall $corner max] $digits]"
}
}
}
proc vertex_path_name { vertex } {
set pin [$vertex pin]
set pin_name [get_full_name $pin]
return [vertex_name_ $vertex $pin $pin_name]
}
proc vertex_port_name { vertex } {
set pin [$vertex pin]
set pin_name [pin_property $pin lib_pin_name]
return [vertex_name_ $vertex $pin $pin_name]
}
# Append driver/load for bidirect pin vertices.
proc vertex_name_ { vertex pin pin_name } {
if { [pin_direction $pin] == "bidirect" } {
if [$vertex is_bidirect_driver] {
return "$pin_name driver"
} else {
return "$pin_name load "
}
} else {
return $pin_name
}
}
# Return a list of hierarchical pins crossed by a graph edge.
proc hier_pins_crossed_by_edge { edge } {
set from_pins [hier_pins_above [[$edge from] pin]]
set to_pins [hier_pins_above [[$edge to] pin]]
foreach p $to_pins { report_line [$p path_name] }
while { [llength $from_pins] > 0 \
&& [llength $to_pins] > 0 \
&& [lindex $from_pins 0] == [lindex $to_pins 0] } {
set from_pins [lrange $from_pins 1 end]
set to_pins [lrange $to_pins 1 end]
}
return [concat $from_pins $to_pins]
}
proc hier_pins_above { pin } {
set pins_above {}
set inst [$pin instance]
set net [$pin net]
set parent [$inst parent]
while { $parent != "NULL" } {
set found 0
set parent_pin_iter [$parent pin_iterator]
while {[$parent_pin_iter has_next]} {
set parent_pin [$parent_pin_iter next]
if {[$parent_pin net] == $net} {
set pins_above [concat [list $parent_pin] $pins_above]
set found 1
break
}
}
$parent_pin_iter finish
if { !$found} {
break
}
set parent [$parent parent]
}
return $pins_above
}
proc report_level { pin } {
set pin1 [get_port_pin_error "pin" $pin]
foreach vertex [$pin1 vertices] {
if { $vertex != "NULL" } {
report_line "[vertex_path_name $vertex] level = [$vertex level]"
}
}
}
# Show how many pins are at each level from an input.
proc report_level_distribution {} {
set max_level 0
set iter [vertex_iterator]
while {[$iter has_next]} {
set vertex [$iter next]
set level [$vertex level]
if { $level > $max_level } {
set max_level $level
}
if [info exists count($level)] {
incr count($level)
} else {
set count($level) 1
}
}
$iter finish
report_line "level pin count"
for { set level 0 } { $level < $max_level } { incr level } {
report_line " $level $count($level)"
}
}
# sta namespace end
}