text: # pyOCD debugger
  # Copyright (c) 2015-2020 Arm Limited
  # Copyright (c) 2021 Chris Reed
  # Copyright (c) 2021 Jacob Berg Potter
  # SPDX-License-Identifier: Apache-2.0
  #
  # Licensed under the Apache License, Version 2.0 (the "License");
  # you may not use this file except in compliance with the License.
  # You may obtain a copy of the License at
  #
  #     http://www.apache.org/licenses/LICENSE-2.0
  #
  # Unless required by applicable law or agreed to in writing, software
  # distributed under the License is distributed on an "AS IS" BASIS,
  # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  # See the License for the specific language governing permissions and
  # limitations under the License.
  
  import logging
  from time import sleep
  
  from ..core import exceptions
  from .component import CoreSightComponent
  from .gpr import GPR
  from .component_ids import COMPONENT_MAP
  from ..utility.conversion import pairwise
  from ..utility.mask import (bit_invert, align_down)
  from ..utility.timeout import Timeout
  
  LOG = logging.getLogger(__name__)
  
  class CoreSightComponentID(object):
      """@brief Reads and parses CoreSight architectural component ID registers.
  
      Reads the CIDR, PIDR, DEVID, and DEVARCH registers present at well known offsets
      in the memory map of all CoreSight components. The various fields from these
      registers are made available as attributes.
      """
  
      # Component identification register offsets.
      PIDR4 = 0xfd0
      PIDR0 = 0xfe0
      CIDR0 = 0xff0
      IDR_END = 0x1000
  
      # Range of identification registers to read at once and offsets in results.
      #
      # To improve component identification performance, we read all of a component's
      # ID registers in a single read.
      IDR_READ_START = PIDR4
      IDR_READ_COUNT = (IDR_END - IDR_READ_START) // 4
      PIDR4_OFFSET = (PIDR4 - IDR_READ_START) // 4
      PIDR0_OFFSET = (PIDR0 - IDR_READ_START) // 4
      CIDR0_OFFSET = (CIDR0 - IDR_READ_START) // 4
  
      # CoreSight identification register offsets.
      DEVARCH = 0xfbc
      DEVTYPE = 0xfcc
      CORESIGHT_IDR_END = 0xfd0
  
      # Range of CoreSight-specific registers to read. Non-CoreSight components may not
      # implement these registers, and may even error on attempting to read them, so we
      # only read them if the component's class is CORESIGHT_CLASS.
      CORESIGHT_IDR_READ_START = DEVARCH
      CORESIGHT_IDR_READ_COUNT = (CORESIGHT_IDR_END - CORESIGHT_IDR_READ_START) // 4
      DEVARCH_OFFSET = (DEVARCH - CORESIGHT_IDR_READ_START) // 4
      DEVTYPE_OFFSET = (DEVTYPE - CORESIGHT_IDR_READ_START) // 4
  
      # Component ID register fields.
      CIDR_PREAMBLE_MASK = 0xffff0fff
      CIDR_PREAMBLE_VALUE = 0xb105000d
  
      CIDR_COMPONENT_CLASS_MASK = 0x0000f000
      CIDR_COMPONENT_CLASS_SHIFT = 12
  
      # Component classes.
      ROM_TABLE_CLASS = 0x1
      CORESIGHT_CLASS = 0x9
  
      # Peripheral ID register fields.
      PIDR_PART_MASK = 0x00000fff
      PIDR_DESIGNER_MASK = 0x0007f000 # JEP106 ID
      PIDR_DESIGNER_SHIFT = 12
      PIDR_REVISION_MASK = 0x00f00000
      PIDR_REVISION_SHIFT = 20
      PIDR_DESIGNER2_MASK = 0x0f00000000 # JEP106 continuation
      PIDR_DESIGNER2_SHIFT = 32
  
      # DEVARCH register fields.
      DEVARCH_ARCHITECT_MASK = 0x7ff
      DEVARCH_ARCHITECT_SHIFT = 21
      DEVARCH_PRESENT_MASK = (1<<20)
      DEVARCH_REVISION_MASK = 0x000f0000
      DEVARCH_REVISION_SHIFT = 16
      DEVARCH_ARCHID_MASK = 0xffff
  
      CLASS_0X9_ROM_TABLE_ARCHID = 0x0af7
  
      def __init__(self, parent_rom_table, ap, top_addr, power_id=None):
          self.parent_rom_table = parent_rom_table
          self.ap = ap
          self.address = top_addr
          self.top_address = top_addr
          self.power_id = power_id
          self.component_class = 0
          self.is_rom_table = False
          self.cidr = 0
          self.pidr = 0
          self.designer = 0
          self.part = 0
          self.devarch = 0
          self.archid = 0
          self.devtype = 0
          self.devid = [0, 0, 0]
          self.name = ''
          self.product_name = None
          self.factory = None
          self.valid = False
  
      def read_id_registers(self):
          """@brief Read Component ID, Peripheral ID, and DEVID/DEVARCH registers."""
          # Read registers as a single block read for performance reasons.
          regs = self.ap.read_memory_block32(self.top_address + self.IDR_READ_START, self.IDR_READ_COUNT)
          self.cidr = self._extract_id_register_value(regs, self.CIDR0_OFFSET)
          self.pidr = (self._extract_id_register_value(regs, self.PIDR4_OFFSET) << 32) \
                      | self._extract_id_register_value(regs, self.PIDR0_OFFSET)
  
          # Check if the component has a valid CIDR value
          if (self.cidr & self.CIDR_PREAMBLE_MASK) != self.CIDR_PREAMBLE_VALUE:
              LOG.warning("Invalid coresight component, cidr=0x%x", self.cidr)
              return
  
          # Extract class.
          self.component_class = (self.cidr & self.CIDR_COMPONENT_CLASS_MASK) >> self.CIDR_COMPONENT_CLASS_SHIFT
  
          # Extract JEP106 designer ID.
          self.designer = ((self.pidr & self.PIDR_DESIGNER_MASK) >> self.PIDR_DESIGNER_SHIFT) \
                          | ((self.pidr & self.PIDR_DESIGNER2_MASK) >> (self.PIDR_DESIGNER2_SHIFT - 8))
          self.part = self.pidr & self.PIDR_PART_MASK
  
          # Handle Class 0x1 and Type 0x9 components.
          if self.component_class == self.ROM_TABLE_CLASS:
              # Class 0x1 ROM table.
              self.is_rom_table = True
          elif self.component_class == self.CORESIGHT_CLASS:
               coresight_regs = self.ap.read_memory_block32(
                   self.top_address + self.CORESIGHT_IDR_READ_START, self.CORESIGHT_IDR_READ_COUNT)
  
              # For CoreSight-class components, extract additional fields.
               self.devarch = coresight_regs[self.DEVARCH_OFFSET]
               self.devid = coresight_regs[1:4]
               self.devid.reverse()
               self.devtype = coresight_regs[self.DEVTYPE_OFFSET]
  
               if self.devarch & self.DEVARCH_PRESENT_MASK:
                   self.archid = self.devarch & self.DEVARCH_ARCHID_MASK
  
               # Identify a Class 0x9 ROM table.
               self.is_rom_table = (self.archid == self.CLASS_0X9_ROM_TABLE_ARCHID)
  
          # Determine component name.
          if self.is_rom_table:
              self.name = 'ROM'
              self.factory = ROMTable.create
          else:
              key = (self.designer, self.component_class, self.part, self.devtype, self.archid)
              info = COMPONENT_MAP.get(key, None)
              if info is None:
                  # Try just the archid with no partno or devtype as backup.
                  key = (self.designer, self.component_class, None, None, self.archid)
                  info = COMPONENT_MAP.get(key, None)
              if info is not None:
                  self.name = info.name
                  self.product_name = info.product
                  self.factory = info.factory
              else:
                  self.name = '???'
  
          self.valid = True
  
      def _extract_id_register_value(self, regs, offset):
          result = 0
          for i in range(4):
              value = regs[offset + i]
              result |= (value & 0xff) << (i * 8)
          return result
  
      def __repr__(self):
          name = self.name
          if self.product_name:
              name += " " + self.product_name
          if not self.valid:
              return "<%08x:%s cidr=%x, pidr=%x, component invalid>" % (self.address, name, self.cidr, self.pidr)
          if self.power_id is not None:
              pwrid = " pwrid=%d" % self.power_id
          else:
              pwrid = ""
          if self.component_class == self.CORESIGHT_CLASS:
              return "<%08x:%s class=%d designer=%03x part=%03x devtype=%02x archid=%04x devid=%x:%x:%x%s>" % (
                  self.address, name, self.component_class, self.designer, self.part,
                  self.devtype, self.archid, self.devid[0], self.devid[1], self.devid[2], pwrid)
          else:
              return "<%08x:%s class=%d designer=%03x part=%03x%s>" % (
                  self.address, name,self.component_class, self.designer, self.part, pwrid)
  
  
  class ROMTable(CoreSightComponent):
      """@brief CoreSight ROM table base class.
  
      This abstract class provides common functionality for ROM tables. Most importantly it has the
      static create() factory method.
  
      After a ROMTable is created, its init() method should be called. This will read and parse the
      table and any child ROM tables. For every component it finds in the table(s), it creates a
      CoreSightComponentID instance. The full collection of component IDs is available in the
      _components_ property. The for_each() method will execute a callable for all of the receiving
      ROM table and its children's components.
  
      Power domains controlled by Granular Power Requestor components are supported. They are
      automatically enabled as parsing proceeds so that components can be accessed to read their ID
      registers.
      """
  
      # 9 entries is enough entries to cover the standard Cortex-M4 ROM table for devices with ETM.
      ROM_TABLE_ENTRY_READ_COUNT = 9
  
      @staticmethod
      def create(memif, cmpid, addr=None, parent_table=None):
          """@brief Factory method for creating ROM table components.
  
          This static method instantiates the appropriate subclass for the ROM table component
          described by the cmpid parameter.
  
          @param memif MemoryInterface used to access the ROM table.
          @param cmpid The CoreSightComponentID instance for this ROM table.
          @param addr Optional base address for this ROM table, if already known.
          @param parent_table Optional ROM table that pointed to this one.
          """
          assert cmpid is not None
  
          # Create appropriate ROM table class.
          if cmpid.component_class == CoreSightComponentID.ROM_TABLE_CLASS:
              return Class1ROMTable(memif, cmpid, addr, parent_table)
          elif cmpid.component_class == CoreSightComponentID.CORESIGHT_CLASS:
              return Class9ROMTable(memif, cmpid, addr, parent_table)
          else:
              raise exceptions.DebugError("unexpected ROM table device class (%s)" % cmpid)
  
      def __init__(self, ap, cmpid=None, addr=None, parent_table=None):
          """@brief Constructor."""
          assert cmpid is not None
          assert cmpid.is_rom_table
          super(ROMTable, self).__init__(ap, cmpid, addr)
          assert self.address is not None
          if parent_table is not None:
              parent_table.add_child(self)
          self._depth = (self.parent.depth + 1) if self.parent else 0
          self._components = []
          self.name = 'ROM'
          self.gpr = None
  
      @property
      def depth(self):
          """@brief Number of parent ROM tables."""
          return self._depth
  
      @property
      def components(self):
          """@brief List of CoreSightComponentID instances for components found in this table.
  
          This property contains only the components for this ROM table, not any child tables.
  
          Child ROM tables will be represented in the list by ROMTable instances rather than
          CoreSightComponentID.
          """
          return self._components
  
      @property
      def depth_indent(self):
          """@brief String of whitespace with a width corresponding to the table's depth.'"""
          return "  " * self._depth
  
      def init(self):
          """@brief Read and parse the ROM table.
  
          As table entries for CoreSight components are read, a CoreSightComponentID instance will be
          created and the ID registers read. These ID objects are added to the _components_ property.
          If any child ROM tables are discovered, they will automatically be created and inited.
          """
          LOG.info("%s%s Class 0x%x ROM table #%d @ 0x%08x (designer=%03x part=%03x)",
              self.depth_indent, self.ap.short_description, self.cmpid.component_class, self.depth,
              self.address, self.cmpid.designer, self.cmpid.part)
          self._components = []
  
          self._read_table()
  
      def _read_table(self):
          raise NotImplementedError()
  
      def for_each(self, action, filter=None):
          """@brief Apply an action to every component defined in the ROM table and child tables.
  
          This method iterates over every entry in the ROM table. For each entry it calls the
          filter function if provided. If the filter passes (returns True or was not provided) then
          the action function is called.
  
          The ROM table must have been initialized by calling init() prior to using this method.
  
          @param self This object.
          @param action Callable that accepts a single parameter, a CoreSightComponentID instance.
          @param filter Optional filter callable. Must accept a CoreSightComponentID instance and
              return a boolean indicating whether to perform the action (True applies action).
          """
          for component in self._components:
              # Recurse into child ROM tables.
              if isinstance(component, ROMTable):
                  component.for_each(action, filter)
                  continue
  
              # Skip component if the filter returns False.
              if filter is not None and not filter(component):
                  continue
  
              # Perform the action.
              action(component)
  
  class Class1ROMTable(ROMTable):
      """@brief CoreSight Class 0x1 ROM table component and parser.
  
      An object of this class represents a CoreSight Class 0x1 ROM table. It supports reading the table
      and any child tables. For each entry in the table, a CoreSightComponentID object is created
      that further reads the component's CoreSight identification registers.
  
      Granular Power Requestor (GPR) components are supported to automatically enable power domains
      required to access components, as indicated by the component entry in the ROM table.
      """
  
      # Constants for Class 0x1 ROM tables.
      ROM_TABLE_ENTRY_PRESENT_MASK = 0x1
  
      # Mask for ROM table entry size. 1 if 32-bit entries.
      ROM_TABLE_32BIT_FORMAT_MASK = 0x2
  
      # ROM table entry power ID fields.
      ROM_TABLE_POWERIDVALID_MASK = 0x4
      ROM_TABLE_POWERID_MASK = 0x01f0
      ROM_TABLE_POWERID_SHIFT = 4
  
      # 2's complement offset to debug component from ROM table base address.
      ROM_TABLE_ADDR_OFFSET_NEG_MASK = 0x80000000
      ROM_TABLE_ADDR_OFFSET_MASK = 0xfffff000
  
      ROM_TABLE_MAX_ENTRIES = 960
  
      def _read_table(self):
          entryAddress = self.address
          foundEnd = False
          entriesRead = 0
          entryNumber = 0
          while not foundEnd and entriesRead < self.ROM_TABLE_MAX_ENTRIES:
              # Read several entries at a time for performance.
              readCount = min(self.ROM_TABLE_MAX_ENTRIES - entriesRead, self.ROM_TABLE_ENTRY_READ_COUNT)
              entries = self.ap.read_memory_block32(entryAddress, readCount)
              entriesRead += readCount
  
              for entry in entries:
                  # Zero entry indicates the end of the table.
                  if entry == 0:
                      foundEnd = True
                      break
                  try:
                      self._handle_table_entry(entry, entryNumber)
                  except exceptions.TransferError as err:
                      LOG.error("Error attempting to probe CoreSight component referenced by "
                              "ROM table entry #%d: %s", entryNumber, err,
                              exc_info=self.ap.dp.session.get_current().log_tracebacks)
  
                  entryAddress += 4
                  entryNumber += 1
  
      def _power_component(self, number, powerid, entry):
          if self.gpr is None:
              LOG.warning("ROM table entry #%d specifies power ID #%d, but no power requestor "
                  "component has been seen; skipping component (entry=0x%08x)",
                  number, powerid, entry)
              return False
  
          # Power up the domain.
          if not self.gpr.power_up_one(powerid):
              LOG.error("Failed to power up power domain #%d", powerid)
              return False
          else:
              LOG.info("Enabled power to power domain #%d", powerid)
              return True
  
      def _handle_table_entry(self, entry, number):
          # Nonzero entries can still be disabled, so check the present bit before handling.
          if (entry & self.ROM_TABLE_ENTRY_PRESENT_MASK) == 0:
              LOG.debug("%s[%d]<%08x not present>", self.depth_indent, number, entry)
              return
          # Verify the entry format is 32-bit.
          if (entry & self.ROM_TABLE_32BIT_FORMAT_MASK) == 0:
              LOG.debug("%s[%d]<%08x unsupported 8-bit format>", self.depth_indent, number, entry)
              return
  
          # Get the component's top 4k address.
          offset = entry & self.ROM_TABLE_ADDR_OFFSET_MASK
          if (entry & self.ROM_TABLE_ADDR_OFFSET_NEG_MASK) != 0:
              offset = ~bit_invert(offset)
          address = self.address + offset
          # Handle address going negative, since python doesn't have unsigned ints.
          if address < 0:
              address = 0x100000000 + address
  
          # Check power ID.
          if (entry & self.ROM_TABLE_POWERIDVALID_MASK) != 0:
              powerid = (entry & self.ROM_TABLE_POWERID_MASK) >> self.ROM_TABLE_POWERID_SHIFT
  
              # Attempt to power up this component. Skip this component if we the attempt fails.
              if not self._power_component(number, powerid, entry):
                  return
          else:
              powerid = None
  
          # Create component instance.
          cmpid = CoreSightComponentID(self, self.ap, address, powerid)
          cmpid.read_id_registers()
  
          # Is this component a power requestor?
          if cmpid.factory == GPR.factory:
              # Create the GPR instance and stash it.
              self.gpr = cmpid.factory(self.ap, cmpid, None)
              self.gpr.init()
  
          LOG.info("%s[%d]%s", self.depth_indent, number, str(cmpid))
  
          # Recurse into child ROM tables.
          if cmpid.is_rom_table:
              cmp = ROMTable.create(self.ap, cmpid, address, parent_table=self)
              cmp.init()
          else:
              cmp = cmpid
  
          if cmp is not None:
              self.components.append(cmp)
  
  class Class9ROMTable(ROMTable):
      """@brief CoreSight Class 0x9 ROM table component and parser.
  
      Handles parsing of class 0x9 ROM tables as defined in ADIv6.
  
      In addition to GPR (Granular Power Requestor) components for power domain management, this class
      supports the optional power request functionality present in class 0x9 ROM tables.
      """
  
      # Constants for Class 0x9 ROM tables.
      ROM_TABLE_ENTRY_PRESENT_MASK = 0x3
      ROM_TABLE_ENTRY_POWERIDVALID_MASK = 0x4
      ROM_TABLE_ENTRY_POWERID_MASK = 0x01f0
      ROM_TABLE_ENTRY_POWERID_SHIFT = 4
  
      ROM_TABLE_ENTRY_NOT_PRESENT_FINAL = 0x0
      ROM_TABLE_ENTRY_NOT_PRESENT_NOT_FINAL = 0x2
      ROM_TABLE_ENTRY_PRESENT = 0x3
  
      ROM_TABLE_DBGPCRn = 0xa00
      ROM_TABLE_DBGPSRn = 0xa80
      ROM_TABLE_SYSPCRn = 0xb00
      ROM_TABLE_SYSPSRn = 0xb80
      ROM_TABLE_PRIDR0 = 0xc00
      ROM_TABLE_DBGRSTRR = 0xc10
      ROM_TABLE_DBGRSTAR = 0xc14
      ROM_TABLE_SYSRSTRR = 0xc18
      ROM_TABLE_SYSRSTAR = 0xc1c
      ROM_TABLE_AUTHSTATUS = 0xfb8
  
      ROM_TABLE_DBGPCRn_PRESENT_MASK = 0x00000001
      ROM_TABLE_DBGPCRn_PR_MASK = 0x00000002
      ROM_TABLE_DBGPSRn_PS_MASK = 0x00000003
  
      ROM_TABLE_DBGPSRn_PS_MAYBE_NOT_POWERED = 0x0
      ROM_TABLE_DBGPSRn_PS_IS_POWERED = 0x1
      ROM_TABLE_DBGPSRn_PS_MUST_REMAIN_POWERED = 0x3
  
      ROM_TABLE_PRIDR0_VERSION_MASK = 0x0000000f
      ROM_TABLE_PRIDR0_VERSION = 1 # Current version number of the power request functionality.
  
      ROM_TABLE_DEVID_CP_MASK = 0x00000040
      ROM_TABLE_DEVID_PRR_MASK = 0x00000020
      ROM_TABLE_DEVID_SYSMEM_MASK = 0x00000010
      ROM_TABLE_DEVID_FORMAT_MASK = 0x0000000f
  
      ROM_TABLE_FORMAT_32BIT = 0x0
      ROM_TABLE_FORMAT_64BIT = 0x1
  
      ROM_TABLE_MAX_ENTRIES = 512 # Maximum 32-bit entries.
  
      # 2's complement offset to debug component from ROM table base address.
      ROM_TABLE_ADDR_OFFSET_NEG_MASK = { 32: (1 << 31), 64: (1 << 63) }
      ROM_TABLE_ADDR_OFFSET_MASK = { 32: 0xfffff000, 64: 0xfffffffffffff000 }
  
      # 5 second timeout on power domain requests.
      POWER_REQUEST_TIMEOUT = 5.0
  
      def __init__(self, ap, cmpid=None, addr=None, parent_table=None):
          """@brief Component constructor."""
          super(Class9ROMTable, self).__init__(ap, cmpid, addr, parent_table)
  
          self._pridr_version = None
  
          # Extract flags from DEVID.
          self._has_com_port = ((self.cmpid.devid[0] & self.ROM_TABLE_DEVID_CP_MASK) != 0)
          self._has_prr = ((self.cmpid.devid[0] & self.ROM_TABLE_DEVID_PRR_MASK) != 0)
          self._is_sysmem = ((self.cmpid.devid[0] & self.ROM_TABLE_DEVID_SYSMEM_MASK) != 0)
          is_64bit = ((self.cmpid.devid[0] & self.ROM_TABLE_DEVID_FORMAT_MASK) != 0)
          self._width = 64 if is_64bit else 32
          LOG.debug("cp=%d prr=%d sysmem=%d w=%d", self._has_com_port, self._has_prr, self._is_sysmem, self._width)
  
      @property
      def has_com_port(self):
          """@brief Whether the ROM table includes COM Port functionality."""
          return self._has_com_port
  
      @property
      def has_prr(self):
          """@brief Whether the ROM table includes power and reset requesting functionality."""
          return self._has_prr
  
      @property
      def is_sysmem(self):
          """@brief Whether the ROM table is present in system memory."""
          return self._is_sysmem
  
      def _read_table(self):
          """@brief Reads and parses the ROM table."""
          # Compute multipliers for 32- or 64-bit.
          entrySizeMultiplier = self._width // 32
          actualMaxEntries = self.ROM_TABLE_MAX_ENTRIES // entrySizeMultiplier
          # Ensure 64-bit format is read as pairs of 32-bit values.
          entryReadCount = align_down(self.ROM_TABLE_ENTRY_READ_COUNT, entrySizeMultiplier)
  
          entryAddress = self.address
          foundEnd = False
          entriesRead = 0
          entryNumber = 0
  
          while not foundEnd and entriesRead < actualMaxEntries:
              # Read several entries at a time for performance.
              readCount = min(actualMaxEntries - entriesRead, entryReadCount)
              entries = self.ap.read_memory_block32(entryAddress, readCount)
              entriesRead += readCount
  
              # For 64-bit entries, combine pairs of 32-bit values into single 64-bit value.
              if self._width == 64:
                  entries = [(lo | (hi << 32)) for lo, hi in pairwise(entries)]
  
              for entry in entries:
                  present = entry & self.ROM_TABLE_ENTRY_PRESENT_MASK
  
                  # Zero entry indicates the end of the table.
                  if present == self.ROM_TABLE_ENTRY_NOT_PRESENT_FINAL:
                      foundEnd = True
                      break
                  elif present == self.ROM_TABLE_ENTRY_PRESENT:
                      try:
                          self._handle_table_entry(entry, entryNumber)
                      except exceptions.TransferError as err:
                          LOG.error("Error attempting to probe CoreSight component referenced by "
                                  "ROM table entry #%d: %s", entryNumber, err,
                                  exc_info=self.ap.dp.session.get_current().log_tracebacks)
                  else:
                      LOG.debug("%s[%d]<%08x not present>", self.depth_indent, entryNumber, entry)
  
                  entryAddress += 4 * entrySizeMultiplier
                  entryNumber += 1
  
      def _power_component(self, number, powerid, entry):
          """@brief Enable power to a component defined by a ROM table entry."""
          if not self._has_prr:
              # Attempt GPR method of power domain enabling.
              return super(Class9ROMTable, self)._power_component(number, powerid, entry)
  
          # Check power request functionality version here so we can provide a nice warning message.
          if not self.check_power_request_version():
              LOG.warning("Class 0x9 ROM table #%d @ 0x%08x has unsupported version (%d) of power "
                          "request functionality, needed for entry #%d (entry=0x%08x). Skipping "
                          "component.", self.depth, self.address, self._pridr_version, number, entry)
              return False
  
          if not self.power_debug_domain(powerid):
              LOG.error("Failed to power up power domain #%d", powerid)
              return False
          else:
              LOG.info("Enabled power to power domain #%d", powerid)
              return True
  
      def _handle_table_entry(self, entry, number):
          """@brief Parse one ROM table entry."""
          # Get the component's top 4k address.
          offset = entry & self.ROM_TABLE_ADDR_OFFSET_MASK[self._width]
          if (entry & self.ROM_TABLE_ADDR_OFFSET_NEG_MASK[self._width]) != 0:
              offset = ~bit_invert(offset, width=self._width)
          address = self.address + offset
          # Handle address going negative, since python doesn't have unsigned ints.
          if address < 0:
              address = (1 << self._width) + address
  
          # Check power ID.
          if (entry & self.ROM_TABLE_ENTRY_POWERIDVALID_MASK) != 0:
              powerid = (entry & self.ROM_TABLE_ENTRY_POWERID_MASK) >> self.ROM_TABLE_ENTRY_POWERID_SHIFT
  
              # Attempt to power up this component. Skip this component if we the attempt fails.
              if not self._power_component(number, powerid, entry):
                  return
          else:
              powerid = None
  
          # Create component instance.
          cmpid = CoreSightComponentID(self, self.ap, address, powerid)
          cmpid.read_id_registers()
  
          # Is this component a power requestor?
          if cmpid.factory == GPR.factory:
              # Create the GPR instance and stash it.
              self.gpr = cmpid.factory(self.ap, cmpid, None)
              self.gpr.init()
  
          LOG.info("%s[%d]%s", self.depth_indent, number, str(cmpid))
  
          # Recurse into child ROM tables.
          if cmpid.is_rom_table:
              cmp = ROMTable.create(self.ap, cmpid, address, parent_table=self)
              cmp.init()
          else:
              cmp = cmpid
  
          if cmp is not None:
              self._components.append(cmp)
  
      def check_power_request_version(self):
          """@brief Verify the power request functionality version."""
          # Cache the PRIDR0 VERSION field the first time.
          if self._pridr_version is None:
              pridr = self.ap.read32(self.address + self.ROM_TABLE_PRIDR0)
              self._pridr_version = pridr & self.ROM_TABLE_PRIDR0_VERSION_MASK
  
          return self._pridr_version == self.ROM_TABLE_PRIDR0_VERSION
  
      def power_debug_domain(self, domain_id, enable=True):
          """@brief Control power for a specified power domain managed by this ROM table."""
          # Compute register addresses for this power domain.
          dbgpcr_addr = self.address + self.ROM_TABLE_DBGPCRn + (4 * domain_id)
          dbgpsr_addr = self.address + self.ROM_TABLE_DBGPSRn + (4 * domain_id)
  
          # Check the domain request PRESENT bit.
          dbgpcr = self.ap.read32(dbgpcr_addr)
          if (dbgpcr & self.ROM_TABLE_DBGPCRn_PRESENT_MASK) == 0:
              LOG.warning("Power request functionality for power domain #%d is not present.",
                  domain_id)
              return False
  
          # Check if the PR bit matches our request.
          pr = (dbgpcr & self.ROM_TABLE_DBGPCRn_PR_MASK) != 0
          if pr == enable:
              return True
  
          # If enabling, we need to check the power domain status. If the domain is in the "must
          # remain powered" state, then we have to wait for it to exit this state before re-enabling
          # power. See Figure D4-3 "Debug power request process", page D4-317, in the ADIv6
          # specification [IHI0074B].
          if enable:
              with Timeout(self.POWER_REQUEST_TIMEOUT) as time_out:
                  while time_out.check():
                      power_status = self.ap.read32(dbgpsr_addr) & self.ROM_TABLE_DBGPSRn_PS_MASK
                      if power_status != self.ROM_TABLE_DBGPSRn_PS_MUST_REMAIN_POWERED:
                          break
                  else:
                      LOG.warning("Power request handshake did not complete for power domain #%d.",
                          domain_id)
                      return False
  
          # Change power enable bit.
          if enable:
              dbgpcr |= self.ROM_TABLE_DBGPCRn_PR_MASK
          else:
              dbgpcr &= ~self.ROM_TABLE_DBGPCRn_PR_MASK
          self.ap.write32(dbgpcr_addr, dbgpcr)
  
          # Wait for status bits to update.
          with Timeout(self.POWER_REQUEST_TIMEOUT) as time_out:
              while time_out.check():
                  power_status = self.ap.read32(dbgpsr_addr)
                  if power_status == self.ROM_TABLE_DBGPSRn_PS_IS_POWERED:
                      # Sleep for 100 ms.
                      sleep(0.1)
                      break
                  elif power_status == self.ROM_TABLE_DBGPSRn_PS_MUST_REMAIN_POWERED:
                      break
              else:
                  LOG.warning("Power request handshake did not complete for power domain #%d.",
                      domain_id)
                  return False
  
          # Successfully changed state of the power domain.
          return True
  