|
1 | 1 | source $::env(SCRIPTS_DIR)/util.tcl |
2 | 2 |
|
3 | | -if { [string match "*openroad" $::env(OPENROAD_EXE)] } { |
4 | | - puts "Reading LEF files for OpenROAD" |
5 | | - log_cmd read_lef $::env(TECH_LEF) |
6 | | - log_cmd read_lef $::env(SC_LEF) |
7 | | - if { [env_var_exists_and_non_empty ADDITIONAL_LEFS] } { |
8 | | - foreach lef $::env(ADDITIONAL_LEFS) { |
9 | | - log_cmd read_lef $lef |
| 3 | +# --- Helper Procedures --- |
| 4 | + |
| 5 | +proc read_lefs { } { |
| 6 | + if { [string match "*openroad" $::env(OPENROAD_EXE)] } { |
| 7 | + puts "Reading LEF files for OpenROAD" |
| 8 | + log_cmd read_lef $::env(TECH_LEF) |
| 9 | + log_cmd read_lef $::env(SC_LEF) |
| 10 | + if { [env_var_exists_and_non_empty ADDITIONAL_LEFS] } { |
| 11 | + foreach lef $::env(ADDITIONAL_LEFS) { |
| 12 | + log_cmd read_lef $lef |
| 13 | + } |
10 | 14 | } |
11 | 15 | } |
12 | 16 | } |
13 | 17 |
|
14 | | -source $::env(LOAD_POWER_TCL) |
15 | | - |
16 | | -# OpenSTA reports reg2reg paths inside macros, |
17 | | -# whereas these paths are hidden to OpenROAD that |
18 | | -# uses a .lib file for the macros. |
19 | | -log_cmd report_checks |
| 18 | +proc get_total_power { } { |
| 19 | + return [lindex [sta::design_power [sta::corners]] 3] |
| 20 | +} |
20 | 21 |
|
21 | | -log_cmd report_power |
| 22 | +# Verify that pins have activity annotated from VCD |
| 23 | +proc verify_vcd_annotation { vcd_file pins } { |
| 24 | + set no_vcd_activity {} |
| 25 | + foreach pin $pins { |
| 26 | + set activity [get_property $pin activity] |
| 27 | + set origin [lindex $activity 2] |
| 28 | + |
| 29 | + # Skip criteria |
| 30 | + if { |
| 31 | + $origin == "vcd" || $origin == "constant" || $origin == "unknown" || |
| 32 | + $origin == "clock" || [get_property $pin is_hierarchical] || |
| 33 | + [get_property $pin direction] == "internal" |
| 34 | + } { |
| 35 | + continue |
| 36 | + } |
22 | 37 |
|
23 | | -set fp [open $::env(RESULTS_DIR)/activity.tcl w] |
24 | | -set pins [get_pins -hierarchical *] |
25 | | -set clock_period [expr [get_property [get_clocks] period] * 1e-12] |
26 | | -foreach pin $pins { |
27 | | - set activity [get_property $pin activity] |
28 | | - set activity_origin [lindex $activity 2] |
29 | | - if { $activity_origin != "vcd" } { |
30 | | - continue |
| 38 | + lappend no_vcd_activity "[get_full_name $pin] $activity" |
| 39 | + if { [llength $no_vcd_activity] >= 10 } { |
| 40 | + break |
| 41 | + } |
31 | 42 | } |
32 | | - set duty [lindex $activity 1] |
33 | | - if { $duty > 1.0 } { # this generates an sta error |
34 | | - set duty 1.0 |
| 43 | + |
| 44 | + if { [llength $no_vcd_activity] > 0 } { |
| 45 | + puts "Error: Found [llength $no_vcd_activity] pins without VCD activity from $vcd_file:" |
| 46 | + foreach item $no_vcd_activity { puts $item } |
| 47 | + exit 1 |
35 | 48 | } |
36 | | - puts $fp "set_power_activity \ |
37 | | - -pin \[get_pins \{[get_property $pin full_name]\}\] \ |
38 | | - -activity [expr [lindex $activity 0] * $clock_period] \ |
39 | | - -duty $duty" |
40 | 49 | } |
41 | | -close $fp |
42 | | - |
43 | | -puts "Total number of pins: [llength [get_pins -hierarchical *]]" |
44 | | -set no_vcd_activity {} |
45 | | -foreach pin $pins { |
46 | | - set activity [get_property $pin activity] |
47 | | - set activity_origin [lindex $activity 2] |
48 | | - if { $activity_origin == "vcd" } { |
49 | | - continue |
50 | | - } |
51 | | - if { $activity_origin == "constant" } { |
52 | | - continue |
53 | | - } |
54 | | - if { $activity_origin == "unknown" } { |
55 | | - continue |
56 | | - } |
57 | | - if { [get_property $pin is_hierarchical] } { |
58 | | - continue |
59 | | - } |
60 | | - if { $activity_origin == "clock" } { |
61 | | - continue |
62 | | - } |
63 | | - set direction [get_property $pin direction] |
64 | | - if { $direction == "internal" } { |
65 | | - continue |
66 | | - } |
67 | | - lappend no_vcd_activity "[get_full_name $pin] $activity $direction" |
68 | | - if { [llength $no_vcd_activity] >= 10 } { |
69 | | - break |
| 50 | + |
| 51 | +# Generate a Tcl file to set and verify user-defined activity |
| 52 | +proc write_user_activity_tcl { filename pins clock_period } { |
| 53 | + set fp [open $filename w] |
| 54 | + puts $fp "set clock_period $clock_period" |
| 55 | + puts $fp {proc read_back_and_compare { pin_name expected_activity expected_duty clock_period } { |
| 56 | + set pin [get_pins $pin_name] |
| 57 | + set activity [get_property $pin activity] |
| 58 | + set rb_activity [expr [lindex $activity 0] * $clock_period] |
| 59 | + set rb_duty [lindex $activity 1] |
| 60 | + if { abs($rb_activity - $expected_activity) > 1e-3 } { |
| 61 | + puts "Error: activity mismatch on $pin_name: expected $expected_activity, got $rb_activity" |
| 62 | + exit 1 |
| 63 | + } |
| 64 | + if { abs($rb_duty - $expected_duty) > 1e-3 } { |
| 65 | + puts "Error: duty mismatch on $pin_name: expected $expected_duty, got $rb_duty" |
| 66 | + exit 1 |
| 67 | + } |
| 68 | + }} |
| 69 | + |
| 70 | + set set_cmds {} |
| 71 | + set check_cmds {} |
| 72 | + foreach pin $pins { |
| 73 | + set activity [get_property $pin activity] |
| 74 | + if { [lindex $activity 2] != "vcd" } { continue } |
| 75 | + |
| 76 | + set duty [lindex $activity 1] |
| 77 | + if { $duty > 1.0 } { set duty 1.0 } |
| 78 | + |
| 79 | + set activity_val [expr [lindex $activity 0] * $clock_period] |
| 80 | + set pin_name [get_property $pin full_name] |
| 81 | + |
| 82 | + lappend set_cmds "set_power_activity -pin \[get_pins \{$pin_name\}\] \ |
| 83 | + -activity $activity_val -duty $duty" |
| 84 | + lappend check_cmds "read_back_and_compare \{$pin_name\} $activity_val $duty \$clock_period" |
70 | 85 | } |
| 86 | + |
| 87 | + foreach cmd $set_cmds { puts $fp $cmd } |
| 88 | + foreach cmd $check_cmds { puts $fp $cmd } |
| 89 | + close $fp |
71 | 90 | } |
72 | 91 |
|
73 | | -if { [llength $no_vcd_activity] > 0 } { |
74 | | - puts "Error: Listing [llength $no_vcd_activity] pins without activity from $vcd_file:" |
75 | | - foreach pin $no_vcd_activity { |
76 | | - puts $pin |
77 | | - } |
| 92 | +# --- Main Flow --- |
| 93 | + |
| 94 | +read_lefs |
| 95 | +source $::env(LOAD_MOCK_ARRAY_TCL) |
| 96 | + |
| 97 | +#------------------------------------------------ |
| 98 | +# Stage 1: Estimated Power (Before reading VCD) |
| 99 | +#------------------------------------------------ |
| 100 | +set pwr_est [get_total_power] |
| 101 | +puts "Power (Estimation): $pwr_est" |
| 102 | +log_cmd report_power |
| 103 | + |
| 104 | +#------------------------------------------------ |
| 105 | +# Stage 2: VCD-based Power |
| 106 | +#------------------------------------------------ |
| 107 | +set vcd_file $::env(VCD_STIMULI) |
| 108 | +log_cmd read_vcd -scope TOP/MockArray $vcd_file |
| 109 | +set pwr_vcd [get_total_power] |
| 110 | +puts "Power (VCD): $pwr_vcd" |
| 111 | + |
| 112 | +# Verification: VCD should change the power value |
| 113 | +if { $pwr_est == $pwr_vcd } { |
| 114 | + puts "Error: VCD activity had no effect on power calculation (remains $pwr_vcd)" |
78 | 115 | exit 1 |
79 | 116 | } |
80 | 117 |
|
| 118 | +# Standard reports |
| 119 | +log_cmd report_checks |
| 120 | +log_cmd report_power |
| 121 | + |
| 122 | +# Prepare for User Activity Stage |
| 123 | +set pins [get_pins -hierarchical *] |
| 124 | +set clock_period [expr [get_property [get_clocks] period] * 1e-12] |
| 125 | +set activity_file "$::env(RESULTS_DIR)/activity.tcl" |
| 126 | + |
| 127 | +verify_vcd_annotation $vcd_file $pins |
| 128 | +write_user_activity_tcl $activity_file $pins $clock_period |
| 129 | + |
| 130 | +# Report power for array instances |
81 | 131 | set ces {} |
82 | 132 | for { set x 0 } { $x < $::env(ARRAY_COLS) } { incr x } { |
83 | 133 | for { set y 0 } { $y < $::env(ARRAY_ROWS) } { incr y } { |
84 | 134 | lappend ces ces_${x}_${y} |
85 | 135 | } |
86 | 136 | } |
87 | | - |
88 | | -puts {report_power -instances [get_cells $ces]} |
| 137 | +puts "report_power -instances \[get_cells \$ces\]" |
89 | 138 | report_power -instances [get_cells $ces] |
90 | 139 |
|
91 | | -proc total_power { } { |
92 | | - return [lindex [sta::design_power [sta::corners]] 3] |
93 | | -} |
94 | | - |
95 | | - |
96 | | -set total_power_vcd [total_power] |
| 140 | +#------------------------------------------------ |
| 141 | +# Stage 3: User Activity Power |
| 142 | +#------------------------------------------------ |
| 143 | +source $activity_file |
97 | 144 | log_cmd report_power |
| 145 | +set pwr_user [get_total_power] |
98 | 146 |
|
99 | | -source $::env(RESULTS_DIR)/activity.tcl |
100 | | -log_cmd report_power |
101 | | -set total_power_user_activity [total_power] |
102 | | - |
103 | | -puts "Total power from VCD: $total_power_vcd" |
104 | | -puts "Total power from user activity: $total_power_user_activity" |
105 | | - |
106 | | -if { $total_power_vcd == $total_power_user_activity } { |
107 | | - puts "Error: setting user power activity had no effect, expected some loss in accuracy" |
108 | | - exit 1 |
109 | | -} |
| 147 | +puts "\nPower Comparison Summary:" |
| 148 | +puts " - Power (Estimation): $pwr_est" |
| 149 | +puts " - Power (VCD): $pwr_vcd" |
| 150 | +puts " - Power (User Activity): $pwr_user" |
110 | 151 |
|
111 | | -if { abs($total_power_vcd - $total_power_user_activity) > 1e-3 } { |
112 | | - puts "Error: Total power mismatch between VCD and user activity:\ |
113 | | - $total_power_vcd vs $total_power_user_activity" |
| 152 | +# Verification: User activity power should match VCD power |
| 153 | +# - Note that float accumulation error is allowed. |
| 154 | +if { abs($pwr_vcd - $pwr_user) > $pwr_vcd * 0.001 } { |
| 155 | + puts "Error: Large error between VCD power and user activity power" |
114 | 156 | exit 1 |
115 | 157 | } |
116 | 158 |
|
|
0 commit comments