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 :

 

Related Links

 

© Copyright 2019 - 2022 Xilinx Inc. Privacy Policy