1############################################################################ 2## 3## Copyright (c) 2019, The Regents of the University of California 4## All rights reserved. 5## 6## BSD 3-Clause License 7## 8## Redistribution and use in source and binary forms, with or without 9## modification, are permitted provided that the following conditions are met: 10## 11## * Redistributions of source code must retain the above copyright notice, this 12## list of conditions and the following disclaimer. 13## 14## * Redistributions in binary form must reproduce the above copyright notice, 15## this list of conditions and the following disclaimer in the documentation 16## and/or other materials provided with the distribution. 17## 18## * Neither the name of the copyright holder nor the names of its 19## contributors may be used to endorse or promote products derived from 20## this software without specific prior written permission. 21## 22## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 23## AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 24## IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 25## ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 26## LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 27## CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 28## SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 29## INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 30## CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 31## ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 32## POSSIBILITY OF SUCH DAMAGE. 33## 34############################################################################ 35 36# Functions/variables common to metrics scripts. 37 38proc define_metric { name short_name fmt cmp_op margin_expr } { 39 variable metrics 40 dict set metrics $name [list $short_name $fmt $cmp_op $margin_expr] 41} 42 43proc metric_names {} { 44 variable metrics 45 return [dict keys $metrics] 46} 47 48proc metric_short_name { name } { 49 variable metrics 50 lassign [dict get $metrics $name] short_name fmt cmp_op margin_expr 51 return $short_name 52} 53 54proc metric_format { name } { 55 variable metrics 56 lassign [dict get $metrics $name] short_name fmt cmp_op margin_expr 57 return $fmt 58} 59 60proc metric_json_key { name } { 61 return $name 62} 63 64proc metric_cmp_op { name } { 65 variable metrics 66 lassign [dict get $metrics $name] short_name fmt cmp_op margin_expr 67 return $cmp_op 68} 69 70proc metric_margin_expr { name } { 71 variable metrics 72 lassign [dict get $metrics $name] short_name fmt cmp_op margin_expr 73 return $margin_expr 74} 75 76proc cmp_op_negated { cmp_op } { 77 if { $cmp_op == "<" } { 78 return ">=" 79 } elseif { $cmp_op == "<=" } { 80 return ">" 81 } elseif { $cmp_op == ">" } { 82 return "<=" 83 } elseif { $cmp_op == ">=" } { 84 return "<" 85 } elseif { $cmp_op == "==" } { 86 return "!=" 87 } else { 88 error "unknown comparison operator" 89 } 90} 91 92# make format field width to match short name width 93define_metric "instance_count" " insts" "%7d" "<" {$value * .2} 94define_metric "design_area" " area" "%7.0f" "<" {$value * .2} 95define_metric "utilization" "util" "%4.1f" "<" {$value * .2} 96define_metric "worst_slack_min" "slack_min" "%9.3f" ">" {-$clock_period * .1} 97define_metric "worst_slack_max" "slack_max" "%9.3f" ">" {-$clock_period * .1} 98define_metric "tns_max" " tns_max" "%8.1f" ">" {-$clock_period * .1 * $instance_count * .1} 99define_metric "clock_skew" "clk_skew" "%8.3f" "<=" {$value * .2} 100define_metric "max_slew_violations" "max_slew" "%8d" "<=" {0} 101define_metric "max_capacitance_violations" "max_cap" "%7d" "<=" {0} 102define_metric "max_fanout_violations" "max_fanout" "%10d" "<=" {0} 103define_metric "DPL::errors" "DPL" "%3d" "<=" {0} 104define_metric "ANT::errors" "ANT" "%3d" "<=" {0} 105define_metric "DRT::drv" "drv" "%3d" "<=" {0} 106define_metric "clock_period" "" "%d" "<=" {0} 107 108################################################################ 109 110# Used by regression.tcl to check pass/fail metrics test. 111# Returns "pass" or failing metric comparison string. 112proc check_test_metrics { test } { 113 # Don't require json until it is really needed. 114 package require json 115 116 set metrics_file [test_metrics_result_file $test] 117 set metrics_limits_file [test_metrics_limits_file $test] 118 if { ![file exists $metrics_file] } { 119 return "missing metrics file" 120 } 121 set stream [open $metrics_file r] 122 set json_string [read $stream] 123 close $stream 124 if { [catch {json::json2dict $json_string} metrics_dict] } { 125 return "error parsing metrics json" 126 } 127 128 if { ![file exists $metrics_limits_file] } { 129 return "missing metrics limits file" 130 } 131 set stream [open $metrics_limits_file r] 132 set json_string [read $stream] 133 close $stream 134 if { [catch {json::json2dict $json_string} metrics_limits_dict] } { 135 return "error parsing metrics limits json" 136 } 137 138 set failures "" 139 foreach name [metric_names] { 140 set json_key [metric_json_key $name] 141 set cmp_op [metric_cmp_op $name] 142 if { [dict exists $metrics_dict $json_key] } { 143 set value [dict get $metrics_dict $json_key] 144 if { [dict exists $metrics_limits_dict $json_key] } { 145 set limit [dict get $metrics_limits_dict $json_key] 146 if { ![expr $value $cmp_op $limit] } { 147 fail "$name [format [metric_format $name] $value] [cmp_op_negated $cmp_op] [format [metric_format $name] $limit]" 148 } 149 } else { 150 fail "missing $name in metric limits" 151 } 152 } else { 153 fail "missing $name in metrics" 154 } 155 } 156 if { $failures == "" } { 157 return "pass" 158 } else { 159 return $failures 160 } 161} 162 163proc fail { msg } { 164 upvar 1 failures failures 165 166 if { $failures != "" } { 167 set failures [concat $failures "; $msg"] 168 } else { 169 set failures $msg 170 } 171} 172 173################################################################ 174 175proc report_flow_metrics_main {} { 176 global argc argv test_groups 177 if { $argc == 0 } { 178 set tests $test_groups(flow) 179 } else { 180 set tests [expand_tests $argv] 181 } 182 183 report_metrics_header 184 foreach test $tests { 185 report_test_metrics $test 186 } 187} 188 189proc report_metrics_header {} { 190 puts -nonewline [format "%-20s" ""] 191 foreach name [metric_names] { 192 set short_name [metric_short_name $name] 193 if { $short_name != "" } { 194 puts -nonewline " $short_name" 195 } 196 } 197 puts "" 198} 199 200proc report_test_metrics { test } { 201 # Don't require json until it is really needed. 202 package require json 203 204 set metrics_result_file [test_metrics_result_file $test] 205 if { [file exists $metrics_result_file] } { 206 set stream [open $metrics_result_file r] 207 set json_string [read $stream] 208 close $stream 209 puts -nonewline [format "%-20s" $test] 210 if { ![catch {json::json2dict $json_string} metrics_dict] } { 211 foreach name [metric_names] { 212 set short_name [metric_short_name $name] 213 if { $short_name != "" } { 214 set key [metric_json_key $name] 215 if { [dict exists $metrics_dict $key] } { 216 set value [dict get $metrics_dict $key] 217 set field [format [metric_format $name] $value] 218 } else { 219 set field [format "%[string length $short_name]s" "N/A"] 220 } 221 puts -nonewline " $field" 222 } 223 } 224 } 225 puts "" 226 } 227} 228 229################################################################ 230 231proc compare_flow_metrics_main {} { 232 global argc argv test_groups 233 if { $argv == "help" || $argv == "-help" } { 234 puts {Usage: save_flow_metrics [test1]...} 235 } else { 236 set relative 0 237 if { $argc >= 1 && [lindex $argv 0] == "-relative" } { 238 set relative 1 239 set argc [expr $argc - 1] 240 set argv [lrange $argv 1 end] 241 } 242 if { $argc == 0 } { 243 set tests $test_groups(flow) 244 } else { 245 set tests [expand_tests $argv] 246 } 247 248 report_metrics_header 249 foreach test $tests { 250 compare_test_metrics $test $relative 251 } 252 } 253} 254 255proc compare_test_metrics { test relative } { 256 # Don't require json until it is really needed. 257 package require json 258 259 set metrics_file [test_metrics_file $test] 260 set result_file [test_metrics_result_file $test] 261 if { [file exists $metrics_file] \ 262 && [file exists $result_file] } { 263 set metrics_stream [open $metrics_file r] 264 set results_stream [open $result_file r] 265 set metrics_json [read $metrics_stream] 266 set results_json [read $results_stream] 267 close $metrics_stream 268 close $results_stream 269 puts -nonewline [format "%-20s" $test] 270 if { ![catch {json::json2dict $metrics_json} metrics_dict] \ 271 && ![catch {json::json2dict $results_json} results_dict]} { 272 foreach name [metric_names] { 273 set short_name [metric_short_name $name] 274 if { $short_name != "" } { 275 set key [metric_json_key $name] 276 if { [dict exists $metrics_dict $key] \ 277 && [dict exists $results_dict $key]} { 278 set metrics_value [dict get $metrics_dict $key] 279 set result_value [dict get $results_dict $key] 280 if { $metrics_value != 0 && $relative } { 281 set delta [expr ($result_value - $metrics_value) * 100.0 / abs($metrics_value)] 282 set field [format "%+.1f%%" $delta] 283 set field [format "%[string length $short_name]s" $field] 284 } else { 285 set delta [expr $result_value - $metrics_value] 286 set field [format [metric_format $name] $delta] 287 } 288 } else { 289 set field [format "%[string length $short_name]s" "N/A"] 290 } 291 puts -nonewline " $field" 292 } 293 } 294 } 295 puts "" 296 } 297} 298 299################################################################ 300 301# Copy result metrics to test results saved in the repository. 302proc save_flow_metrics_main {} { 303 global argc argv 304 305 if { $argv == "help" || $argv == "-help" } { 306 puts {Usage: save_flow_metrics [test1]...} 307 } else { 308 if { $argc == 0 } { 309 set tests [expand_tests "flow"] 310 } else { 311 set tests [expand_tests $argv] 312 } 313 foreach test $tests { 314 save_metrics $test 315 } 316 } 317} 318 319proc save_metrics { test } { 320 if { [lsearch [group_tests "all"] $test] == -1 } { 321 puts "Error: test $test not found." 322 } else { 323 set metrics_result_file [test_metrics_result_file $test] 324 set metrics_file [test_metrics_file $test] 325 file copy -force $metrics_result_file $metrics_file 326 } 327} 328 329################################################################ 330 331# Save result metrics + margins as metric limits. 332proc save_flow_metric_limits_main {} { 333 global argc argv 334 if { $argv == "help" || $argv == "-help" } { 335 puts {Usage: save_flow_metric_limits [failures] test1 [test2]...} 336 } else { 337 if { $argc == 0 } { 338 set tests [expand_tests "flow"] 339 } else { 340 set tests [expand_tests $argv] 341 } 342 foreach test $tests { 343 save_metric_limits $test 344 } 345 } 346} 347 348proc save_metric_limits { test } { 349 # Don't require json until it is really needed. 350 package require json 351 352 set metrics_file [test_metrics_result_file $test] 353 set metrics_limits [test_metrics_limits_file $test] 354 if { ! [file exists $metrics_file] } { 355 puts "Error: metrics file $metrics_file not found." 356 } else { 357 set stream [open $metrics_file r] 358 set json_string [read $stream] 359 close $stream 360 if { ![catch {json::json2dict $json_string} metrics_dict] } { 361 set limits_stream [open $metrics_limits w] 362 puts $limits_stream "{" 363 set first 1 364 # Find value of variables used in margin expr's. 365 foreach var {"clock_period" "instance_count"} { 366 set key [metric_json_key $var] 367 if { [dict exists $metrics_dict $key] } { 368 set value [dict get $metrics_dict $key] 369 set $var $value 370 } else { 371 puts "Error: $test missing $var metric." 372 return 373 } 374 } 375 foreach name [metric_names] { 376 set key [metric_json_key $name] 377 if { [dict exists $metrics_dict $key] } { 378 set value [dict get $metrics_dict $key] 379 set margin_expr [metric_margin_expr $name] 380 set margin [expr $margin_expr] 381 set value_limit [expr $value + $margin] 382 if { $first } { 383 puts -nonewline $limits_stream " " 384 } else { 385 puts -nonewline $limits_stream " ," 386 } 387 puts $limits_stream "\"$key\" : \"$value_limit\"" 388 set first 0 389 } 390 } 391 392 puts $limits_stream "}" 393 close $limits_stream 394 } else { 395 puts "Error: json parse error $metrics_dict" 396 } 397 } 398} 399 400################################################################ 401 402proc test_metrics_file { test } { 403 global test_dir 404 return [file join $test_dir "$test.metrics"] 405} 406 407proc test_metrics_result_file { test } { 408 global test_dir 409 return [file join $test_dir "results" "$test.metrics"] 410} 411 412proc test_metrics_limits_file { test } { 413 global test_dir 414 return [file join $test_dir "$test.metrics_limits"] 415} 416