From 56e4bd8ce1ab867bdb1f8c2ad2953a78ab462529 Mon Sep 17 00:00:00 2001 From: nataliakokoromyti <126305457+nataliakokoromyti@users.noreply.github.com> Date: Sat, 20 Dec 2025 18:13:15 +0200 Subject: [PATCH] Report power as JSON (#342) * fix power_json.tcl * get rid of the if/else statements throughout --- power/Power.tcl | 95 ++++++++++++++++++++++++++++++++++++++++++--- tcl/Util.tcl | 11 ++++++ test/power_json.tcl | 24 ++++++++++++ 3 files changed, 125 insertions(+), 5 deletions(-) create mode 100644 test/power_json.tcl diff --git a/power/Power.tcl b/power/Power.tcl index 420a97a9..492037f0 100644 --- a/power/Power.tcl +++ b/power/Power.tcl @@ -35,13 +35,14 @@ define_cmd_args "report_power" \ [-highest_power_instances count]\ [-corner corner]\ [-digits digits]\ + [-format format]\ [> filename] [>> filename] } proc_redirect report_power { global sta_report_default_digits parse_key_args "report_power" args \ - keys {-instances -highest_power_instances -corner -digits} flags {} + keys {-instances -highest_power_instances -corner -digits -format} flags {} check_argc_eq0 "report_power" $args @@ -56,16 +57,37 @@ proc_redirect report_power { } set corner [parse_corner keys] + if { [info exists keys(-format)] } { + set format $keys(-format) + if { $format != "text" && $format != "json" } { + sta_error 311 "unknown power report -format $format" + } + } else { + set format "text" + } + if { [info exists keys(-instances)] } { set insts [get_instances_error "-instances" $keys(-instances)] - report_power_insts $insts $corner $digits + if { $format == "json" } { + report_power_insts_json $insts $corner $digits + } else { + report_power_insts $insts $corner $digits + } } elseif { [info exists keys(-highest_power_instances)] } { set count $keys(-highest_power_instances) check_positive_integer "-highest_power_instances" $count set insts [highest_power_instances $count $corner] - report_power_insts $insts $corner $digits + if { $format == "json" } { + report_power_insts_json $insts $corner $digits + } else { + report_power_insts $insts $corner $digits + } } else { - report_power_design $corner $digits + if { $format == "json" } { + report_power_design_json $corner $digits + } else { + report_power_design $corner $digits + } } } @@ -104,6 +126,35 @@ proc report_power_design { corner digits } { report_line "[format %-20s {}][power_col_percent $design_internal $design_total $field_width][power_col_percent $design_switching $design_total $field_width][power_col_percent $design_leakage $design_total $field_width]" } +proc report_power_design_json { corner digits } { + set power_result [design_power $corner] + set totals [lrange $power_result 0 3] + set sequential [lrange $power_result 4 7] + set combinational [lrange $power_result 8 11] + set clock [lrange $power_result 12 15] + set macro [lrange $power_result 16 19] + set pad [lrange $power_result 20 end] + + report_line "\{" + report_power_row_json "Sequential" $sequential $digits "," + report_power_row_json "Combinational" $combinational $digits "," + report_power_row_json "Clock" $clock $digits "," + report_power_row_json "Macro" $macro $digits "," + report_power_row_json "Pad" $pad $digits "," + report_power_row_json "Total" $totals $digits "" + report_line "\}" +} + +proc report_power_row_json { name row_result digits separator } { + lassign $row_result internal switching leakage total + report_line " \"$name\": \{" + report_line " \"internal\": [format %.${digits}e $internal]," + report_line " \"switching\": [format %.${digits}e $switching]," + report_line " \"leakage\": [format %.${digits}e $leakage]," + report_line " \"total\": [format %.${digits}e $total]" + report_line " \}$separator" +} + proc max { x y } { if { $x >= $y } { return $x @@ -206,6 +257,40 @@ proc report_power_insts { insts corner digits } { } } +proc report_power_insts_json { insts corner digits } { + set inst_pwrs {} + foreach inst $insts { + set power_result [instance_power $inst $corner] + lappend inst_pwrs [list $inst $power_result] + } + set inst_pwrs [lsort -command inst_pwr_cmp $inst_pwrs] + + report_line "\[" + set first 1 + foreach inst_pwr $inst_pwrs { + set inst [lindex $inst_pwr 0] + set power [lindex $inst_pwr 1] + if { !$first } { + report_line "," + } + set first 0 + report_power_inst_json $inst $power $digits + } + report_line "\]" +} + +proc report_power_inst_json { inst power digits } { + lassign $power internal switching leakage total + set inst_name [get_full_name $inst] + report_line "\{" + report_line " \"name\": \"$inst_name\"," + report_line " \"internal\": [format %.${digits}e $internal]," + report_line " \"switching\": [format %.${digits}e $switching]," + report_line " \"leakage\": [format %.${digits}e $leakage]," + report_line " \"total\": [format %.${digits}e $total]" + report_line "\}" +} + proc inst_pwr_cmp { inst_pwr1 inst_pwr2 } { set pwr1 [lindex $inst_pwr1 1] set pwr2 [lindex $inst_pwr2 1] @@ -287,7 +372,7 @@ proc set_power_activity { args } { if { [info exists keys(-input_ports)] } { set ports [get_ports_error "input_ports" $keys(-input_ports)] foreach port $ports { - if { [get_property $port "direction"] == "input" } { + if { [get_property $port "direction"] == "input" || [get_property $port "direction"] == "in" } { if { [is_clock_src [sta::get_port_pin $port]] } { sta_warn 310 "activity cannot be set on clock ports." } else { diff --git a/tcl/Util.tcl b/tcl/Util.tcl index 0cb2083b..ad47cdce 100644 --- a/tcl/Util.tcl +++ b/tcl/Util.tcl @@ -185,6 +185,17 @@ proc define_hidden_cmd_args { cmd arglist } { namespace export $cmd } +# "Optional Upvar" +# If $other_var is not empty, the upvar is executed. +# Otherwise, $my_var is set to empty. +proc upvar_opt { level other_var my_var } { + if { $other_var != "" } { + uplevel 1 "upvar $level $other_var $my_var" + } else { + uplevel 1 "set $my_var \"\"" + } +} + ################################################################ proc sta_warn { msg_id msg } { diff --git a/test/power_json.tcl b/test/power_json.tcl new file mode 100644 index 00000000..2a770364 --- /dev/null +++ b/test/power_json.tcl @@ -0,0 +1,24 @@ +# report_power reg1_asap7 +set sta_report_default_digits 4 +read_liberty ../examples/asap7_small.lib.gz +read_verilog ../examples/reg1_asap7.v +link_design top + +create_clock -name clk1 -period 500 clk1 +create_clock -name clk2 -period 500 clk2 +create_clock -name clk3 -period 500 clk3 + +set_input_delay -clock clk1 1 {in1 in2} +set_input_delay -clock clk2 1 {in1 in2} +set_input_delay -clock clk3 1 {in1 in2} +set_input_transition 10 {in1 in2 clk1 clk2 clk3} + +set_propagated_clock {clk1 clk2 clk3} + +read_spef ../examples/reg1_asap7.spef + +set_power_activity -input -activity .1 +set_power_activity -input_port reset -activity 0 + +report_power -format json +report_power -format json -instances "[get_cells -filter "name=~clkbuf*"]" \ No newline at end of file