Debugging Power Management Issues
This page describes techniques that can be used to explore and debug power management features on Zynq UltraScale+ MPSoC devices.
Table of Contents
Introduction
In cases where a board/design is already up and running, it may be desired to inspect the power related knobs like power gates, clock gates and resets to assess the opportunity for any unused blocks that are left turned-on. XSDB (Xilinx Debugger) can be used for this purpose. Common XSDB commands that can be used for this inspection are presented below. Further, scripts with specific power control utilities are provided on this page to abstract out the power sequences.
Power Utility Commands
Powering up/down an island involves a sequence of register writes to PMU GLOBAL registers. These sequences are discussed in the “Platform Management Unit” chapter of UG1085. For ease of use, these sequences are abstracted out into to simple power_up and power_down utility commands in the below code snippet. Running a command without any arguments will dump all valid arguments. Save the contents of this snippet into a tcl file (power_cmds.tcl). This file will be used for examples on this page.
# contents of power_cmds.tcl
proc pwr_mask_poll { addr mask } {
set curval "0x[string range [mrd -force $addr] end-8 end]"
set maskedval [expr {$curval & $mask}]
while { $maskedval != 0 } {
set curval "0x[string range [mrd -force $addr] end-8 end]"
set maskedval [expr {$curval & $mask}]
}
}
proc parse_island { arg } {
set island_list [list ACPU0 ACPU1 ACPU2 ACPU3 PP0 PP1 L2 RPU TCM0A TCM0B TCM1A TCM1B OCM0 OCM1 OCM2 OCM3 USB0 USB1 FP PL]
set bit_list [list 0 1 2 3 4 5 7 10 12 13 14 15 16 17 18 19 20 21 22 23]
set regval 0
foreach island $arg {
set res [lsearch $island_list $island]
#puts "$island : $res"
if {$res >= 0} {
set mask [expr {1<<[lindex $bit_list $res]}]
set regval [expr {$regval | $mask}]
#puts "$island : [format "0x%08x" $regval]"
} else {
puts "Invalid Island ! ($island)"
set regval 0
break
}
}
if { $regval == 0 } {
puts "Use one or more of the following islands as arguments:"
puts "$island_list"
}
return $regval
}
proc power_up { args } {
set islands $args
set regval [parse_island $args]
targets -set -nocase -filter {name =~ "*PSU*"}
if { $regval != 0 } {
puts -nonewline "Initiating Power Up Request (Mask: [format "0x%08X" $regval])..."
#Enable Power Up request for given islands
mwr -force 0xFFD80118 $regval
#Trigger power up request
mwr -force 0xFFD80120 $regval
#Polling for ACK from PMU
pwr_mask_poll 0xFFD80110 $regval
puts "Done"
}
}
proc power_down { args } {
set islands $args
set regval [parse_island $args]
targets -set -nocase -filter {name =~ "*PSU*"}
if { $regval != 0 } {
puts -nonewline "Initiating Power Down Request (Mask: [format "0x%08X" $regval])..."
#Enable power down request for given islands
mwr -force 0xFFD80218 $regval
#Trigger power down request
mwr -force 0xFFD80220 $regval
#Polling for ACK from PMU
pwr_mask_poll 0xFFD80210 $regval
puts "Done"
}
}
The code snippet below provides an example on using the power_up and power_down commands provided above (power_cmds.tcl) to power cycle the GPU Pixel Processor PP0 island .
xsdb% source power_cmds.tcl
# Get Power Status of all islands
xsdb% rrd pmu_global pwr_state
pwr_state: 00fffcbf
pl (Bits [23]): 1 fp (Bits [22]): 1 usb1 (Bits [21]): 1
usb0 (Bits [20]): 1 ocm_bank3 (Bits [19]): 1 ocm_bank2 (Bits [18]): 1
ocm_bank1 (Bits [17]): 1 ocm_bank0 (Bits [16]): 1 tcm1b (Bits [15]): 1
tcm1a (Bits [14]): 1 tcm0b (Bits [13]): 1 tcm0a (Bits [12]): 1
r5_1 (Bits [11]): 1 r5_0 (Bits [10]): 1 l2_bank0 (Bits [7]): 1
pp1 (Bits [5]): 1 pp0 (Bits [4]): 1 acpu3 (Bits [3]): 1
acpu2 (Bits [2]): 1 acpu1 (Bits [1]): 1 acpu0 (Bits [0]): 1
# Power down PP0 island
xsdb% power_down PP0
Initiating Power Down Request (Mask: 0x00000010)...Done
# Get the power island status. Notice that PP0 status shows '0' - powered off
xsdb% rrd pmu_global pwr_state
pwr_state: 00fffcaf
pl (Bits [23]): 1 fp (Bits [22]): 1 usb1 (Bits [21]): 1
usb0 (Bits [20]): 1 ocm_bank3 (Bits [19]): 1 ocm_bank2 (Bits [18]): 1
ocm_bank1 (Bits [17]): 1 ocm_bank0 (Bits [16]): 1 tcm1b (Bits [15]): 1
tcm1a (Bits [14]): 1 tcm0b (Bits [13]): 1 tcm0a (Bits [12]): 1
r5_1 (Bits [11]): 1 r5_0 (Bits [10]): 1 l2_bank0 (Bits [7]): 1
pp1 (Bits [5]): 1 pp0 (Bits [4]): 0 acpu3 (Bits [3]): 1
acpu2 (Bits [2]): 1 acpu1 (Bits [1]): 1 acpu0 (Bits [0]): 1
# Power back the PP0 island
xsdb% power_up PP0
Initiating Power Up Request (Mask: 0x00000010)...Done
# Get the power island status. Notice that PP0 status shows '1' - powewred on
xsdb% rrd pmu_global pwr_state
pwr_state: 00fffcbf
pl (Bits [23]): 1 fp (Bits [22]): 1 usb1 (Bits [21]): 1
usb0 (Bits [20]): 1 ocm_bank3 (Bits [19]): 1 ocm_bank2 (Bits [18]): 1
ocm_bank1 (Bits [17]): 1 ocm_bank0 (Bits [16]): 1 tcm1b (Bits [15]): 1
tcm1a (Bits [14]): 1 tcm0b (Bits [13]): 1 tcm0a (Bits [12]): 1
r5_1 (Bits [11]): 1 r5_0 (Bits [10]): 1 l2_bank0 (Bits [7]): 1
pp1 (Bits [5]): 1 pp0 (Bits [4]): 1 acpu3 (Bits [3]): 1
acpu2 (Bits [2]): 1 acpu1 (Bits [1]): 1 acpu0 (Bits [0]): 1
Clocks and resets state can be explored by reading the CRL_APB and CRF_APB registers. Here is a snippet of XSDB commands showing the clock and reset states:
targets -set -nocase -filter {name =~ "*PSU*"}
# List all Clock and Reset control registers
rrd crl_apb
#Output from above command #
err_ctrl: 01 ir_status: 00
ir_mask: 01 ir_enable
ir_disable crl_wprot: 00
iopll_ctrl: 00015a00 iopll_cfg: 7e4b0c82
iopll_frac_cfg: 00000000 rpll_ctrl: 00015a09
rpll_cfg: 7e4b0c82 rpll_frac_cfg: 00000000
pll_status: 00000019 iopll_to_fpd_ctrl: 0300
rpll_to_fpd_ctrl: 0300 usb3_dual_ref_ctrl: 00052000
gem0_ref_ctrl: 02011800 gem1_ref_ctrl: 02011800
gem2_ref_ctrl: 02011800 gem3_ref_ctrl: 06010c00
usb0_bus_ref_ctrl: 00010c00 usb1_bus_ref_ctrl: 00010c00
qspi_ref_ctrl: 01000800 sdio0_ref_ctrl: 01000f00
sdio1_ref_ctrl: 01010800 uart0_ref_ctrl: 01010f00
uart1_ref_ctrl: 00010f00 spi0_ref_ctrl: 01001800
spi1_ref_ctrl: 01001800 can0_ref_ctrl: 01012000
can1_ref_ctrl: 01012000 cpu_r5_ctrl: 01000302
iou_switch_ctrl: 01000602 csu_pll_ctrl: 01000800
pcap_ctrl: 01000800 lpd_switch_ctrl: 01000302
lpd_lsbus_ctrl: 01000f02 dbg_lpd_ctrl: 01000602
nand_ref_ctrl: 00052000 adma_ref_ctrl: 01000302
pl0_ref_ctrl: 01010800 pl1_ref_ctrl: 01010800
pl2_ref_ctrl: 01010800 pl3_ref_ctrl: 00052000
pl0_thr_ctrl: 00008001 pl0_thr_cnt: 0000
pl1_thr_ctrl: 00008001 pl1_thr_cnt: 0000
pl2_thr_ctrl: 00008001 pl2_thr_cnt: 0000
pl3_thr_ctrl: 00008001 pl3_thr_cnt: 0000
gem_tsu_ref_ctrl: 01010600 dll_ref_ctrl: 00
ams_ref_ctrl: 01011e02 i2c0_ref_ctrl: 00010f00
i2c1_ref_ctrl: 00010f00 timestamp_ref_ctrl: 01000f00
saftey_chk: 00000000 clkmon_status: 0000
clkmon_mask: ffff clkmon_enable
clkmon_disable clkmon_trigger
chkr0_clka_upper: 00000000 chkr0_clka_lower: 00000000
chkr0_clkb_cnt: 00000000 chkr0_ctrl: 0000
chkr1_clka_upper: 00000000 chkr1_clka_lower: 00000000
chkr1_clkb_cnt: 00000000 chkr1_ctrl: 0000
chkr2_clka_upper: 00000000 chkr2_clka_lower: 00000000
chkr2_clkb_cnt: 00000000 chkr2_ctrl: 0000
chkr3_clka_upper: 00000000 chkr3_clka_lower: 00000000
chkr3_clkb_cnt: 00000000 chkr3_ctrl: 0000
chkr4_clka_upper: 00000000 chkr4_clka_lower: 00000000
chkr4_clkb_cnt: 00000000 chkr4_ctrl: 0000
chkr5_clka_upper: 00000000 chkr5_clka_lower: 00000000
chkr5_clkb_cnt: 00000000 chkr5_ctrl: 0000
chkr6_clka_upper: 00000000 chkr6_clka_lower: 00000000
chkr6_clkb_cnt: 00000000 chkr6_ctrl: 0000
chkr7_clka_upper: 00000000 chkr7_clka_lower: 00000000
chkr7_clkb_cnt: 00000000 chkr7_ctrl: 0000
boot_mode_user: 0000000e boot_mode_por: 0eee
reset_ctrl: 00 blockonly_rst: 00
reset_reason: 0001 rst_lpd_iou0: 0007
rst_lpd_iou2: 00010180 rst_lpd_top: 00000fd3
rst_lpd_dbg: 0000 bank3_ctrl0: 03ff
bank3_ctrl1: 03ff bank3_ctrl2: 03ff
bank3_ctrl3: 03ff bank3_ctrl4: 03ff
bank3_ctrl5: 0000 bank3_status: 0001
#---------End Output-----#
#Read GEM3 clock state
rrd crl_apb gem3_ref_ctrl
#Output from above command #
gem3_ref_ctrl: 06010c00
rx_clkact (Bits [26]): 1 clkact (Bits [25]): 1
divisor1 (Bits [21:16]): 1 divisor0 (Bits [13:8]): c
srcsel (Bits [2:0]): 0
#---------End Output-----#
Refer to UG1087 for complete list of registers and their descriptions.
Note that the power/clock/reset states are directly queried from HW and do not use EEMI APIs. So these commands are useful for debugging Power Management related issues directly at HW level. They can also be used for experimenting with power states, in cases where EEMI software stack is not used. Note that changing HW state directly when EEMI is used can cause issues due to conflict or race conditions.
Debugging PMUFW
Debug prints are disabled by default in PMUFW. For debugging power/clock related issues, it would be useful to enable prints, particularly for the PM calls in PMUFW. This can be achieved by setting the following build flags:
DEBUG_MODE | Enable debug logging and prints |
DEBUG_CLK | Enables dumping clock and PLL state functions |
DEBUG_PM | Enables debug functions for PM |
PM_LOG_LEVEL | This enables print based debug functions for PM module. Possible values are: 1: Alerts 2: Errors 3: Warnings 4: Information |
Note that enabling PM debug prints may cause cluttering of prints in some cases due to both Linux and PMUFW printing to the same UART. This can be solved by using different UARTs for Linux and PMUFW. The UART used by PMUFW should be disabled in Linux device tree so that Linux doesn’t interfere with the settings.
More details on the PMUFW debug flags are provided at : PMU Firmware
Related Links
© Copyright 2019 - 2022 Xilinx Inc. Privacy Policy