diff --git a/power/Power.tcl b/power/Power.tcl index 420a97a99..6e2132e33 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,19 +57,39 @@ proc_redirect report_power { } set corner [parse_corner keys] + if { ![info exists keys(-format)] } { + set keys(-format) "text" + } + if { $keys(-format) != "text" && $keys(-format) != "json" } { + sta_error 311 "unknown power report -format $keys(-format)" + } + if { [info exists keys(-instances)] } { set insts [get_instances_error "-instances" $keys(-instances)] - report_power_insts $insts $corner $digits + report_power_insts $insts $corner $digits $keys(-format) } 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 + report_power_insts $insts $corner $digits $keys(-format) } else { - report_power_design $corner $digits + report_power_design $corner $digits $keys(-format) + } +} + +define_cmd_args "report_internal_power_components" { [> filename] [>> filename] } +proc_redirect report_internal_power_components { + global sta_report_default_digits + # Set the default corner + set corner [cmd_corner] + if { ![liberty_libraries_exist] } { + sta_error 304 "No liberty libraries have been read." } + set power_result [internal_power_components $corner] + report_line $power_result } + proc liberty_libraries_exist {} { set lib_iter [liberty_library_iterator] set have_liberty 0 @@ -79,7 +100,7 @@ proc liberty_libraries_exist {} { return $have_liberty } -proc report_power_design { corner digits } { +proc report_power_design { corner digits {report_format "text"} } { set power_result [design_power $corner] set totals [lrange $power_result 0 3] set sequential [lrange $power_result 4 7] @@ -90,18 +111,29 @@ proc report_power_design { corner digits } { lassign $totals design_internal design_switching design_leakage design_total set field_width [max [expr $digits + 6] 10] - report_power_title5 "Group" "Internal" "Switching" "Leakage" "Total" $field_width - report_power_title5_units " " "Power" "Power" "Power" "Power" "(Watts)" $field_width - report_title_dashes5 $field_width - report_power_row "Sequential" $sequential $design_total $field_width $digits - report_power_row "Combinational" $combinational $design_total $field_width $digits - report_power_row "Clock" $clock $design_total $field_width $digits - report_power_row "Macro" $macro $design_total $field_width $digits - report_power_row "Pad" $pad $design_total $field_width $digits - report_title_dashes5 $field_width - report_power_row "Total" $power_result $design_total $field_width $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]" + if { $report_format == "text" } { + report_power_title5 "Group" "Internal" "Switching" "Leakage" "Total" $field_width + report_power_title5_units " " "Power" "Power" "Power" "Power" "(Watts)" $field_width + report_title_dashes5 $field_width + } elseif { $report_format == "json" } { + report_line "\{" + } + + set last_row_var "" + report_power_row "Sequential" $sequential $design_total $field_width $digits $report_format last_row_var + report_power_row "Combinational" $combinational $design_total $field_width $digits $report_format last_row_var + report_power_row "Clock" $clock $design_total $field_width $digits $report_format last_row_var + report_power_row "Macro" $macro $design_total $field_width $digits $report_format last_row_var + report_power_row "Pad" $pad $design_total $field_width $digits $report_format last_row_var + if { $report_format == "text" } { + report_title_dashes5 $field_width + } + report_power_row "Total" $power_result $design_total $field_width $digits $report_format last_row_var + if { $report_format == "text" } { + 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]" + } elseif { $report_format == "json" } { + report_line "\}" + } } proc max { x y } { @@ -146,14 +178,28 @@ proc report_title_dashes { count } { report_line $line } -proc report_power_row { type row_result design_total field_width digits } { +proc report_power_row { type row_result design_total field_width digits {report_format "text"} {last_row_var ""} } { lassign $row_result internal switching leakage total if { $design_total == 0.0 || [is_nan $design_total] } { set percent 0.0 } else { set percent [expr $total / $design_total * 100] } - report_line "[format %-20s $type][power_col $internal $field_width $digits][power_col $switching $field_width $digits][power_col $leakage $field_width $digits][power_col $total $field_width $digits] [format %5.1f $percent]%" + if { "$report_format" == "json" } { + upvar_opt 1 $last_row_var last_row + if { $last_row != "" } { + report_line " ," + } + report_line " \"$type\": {" + report_line " \"internal\":[power_col $internal $field_width $digits]," + report_line " \"switching\":[power_col $switching $field_width $digits]," + report_line " \"leakage\":[power_col $leakage $field_width $digits]," + report_line " \"total\":[power_col $total $field_width $digits]" + report_line " }" + set last_row $type + } else { + report_line "[format %-20s $type][power_col $internal $field_width $digits][power_col $switching $field_width $digits][power_col $leakage $field_width $digits][power_col $total $field_width $digits] [format %5.1f $percent]%" + } } proc is_nan { str } { @@ -185,7 +231,7 @@ proc report_power_line { type pwr digits } { } } -proc report_power_insts { insts corner digits } { +proc report_power_insts { insts corner digits {report_format "text"} } { set inst_pwrs {} foreach inst $insts { set power_result [instance_power $inst $corner] @@ -195,14 +241,23 @@ proc report_power_insts { insts corner digits } { set field_width [max [expr $digits + 6] 10] - report_power_title4 "Internal" "Switching" "Leakage" "Total" $field_width - report_power_title4_units "Power" "Power" "Power" "Power" "(Watts)" $field_width - report_title_dashes4 $field_width + if { $report_format == "text" } { + report_power_title4 "Internal" "Switching" "Leakage" "Total" $field_width + report_power_title4_units "Power" "Power" "Power" "Power" "(Watts)" $field_width + report_title_dashes4 $field_width + } else { + report_line {[} + } + set last_inst_var "" foreach inst_pwr $inst_pwrs { set inst [lindex $inst_pwr 0] set power [lindex $inst_pwr 1] - report_power_inst $inst $power $field_width $digits + report_power_inst $inst $power $field_width $digits $report_format last_inst_var + } + + if { $report_format == "json" } { + report_line {]} } } @@ -220,9 +275,24 @@ proc inst_pwr_cmp { inst_pwr1 inst_pwr2 } { } } -proc report_power_inst { inst power_result field_width digits } { +proc report_power_inst { inst power_result field_width digits {report_format "text"} {last_inst_var ""}} { lassign $power_result internal switching leakage total - report_line "[power_col $internal $field_width $digits][power_col $switching $field_width $digits][power_col $leakage $field_width $digits][power_col $total $field_width $digits] [get_full_name $inst]" + if { $report_format == "json" } { + upvar_opt 1 $last_inst_var last_inst + if { $last_inst != "" } { + report_line "," + } + report_line "{" + report_line " \"name\": \"[get_full_name $inst]\"," + report_line " \"internal\":[power_col $internal $field_width $digits]," + report_line " \"switching\":[power_col $switching $field_width $digits]," + report_line " \"leakage\":[power_col $leakage $field_width $digits]," + report_line " \"total\":[power_col $total $field_width $digits]" + report_line "}" + set last_inst $inst + } elseif { $report_format == "text" } { + report_line "[power_col $internal $field_width $digits][power_col $switching $field_width $digits][power_col $leakage $field_width $digits][power_col $total $field_width $digits] [get_full_name $inst]" + } } ################################################################ @@ -287,7 +357,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 0cb2083bf..ad47cdce2 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 000000000..2a770364b --- /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