class_docstr: Arm(R) Ethos(TM)-U NPU config class
class_docstr_tok: ["Arm","(","R",")","Ethos","(","TM",")","-U","NPU","config","class"]
docstr: Get all of the valid block configs
  
  Parameters
  ----------
  ifm_propagator: Propagator,
      The propagator containing the data dependencies between input and output
  op_attrs: Dict,
      Dictionary containing operator attributes
  ofm_shape: List[int],
      Shape of the output tensor
  ofm_channels: int,
      Number of output channels
  ifm_channels: int,
      Number of input channels
  output_layout: str,
      The layout of the Output Feature Map tensor. Can be "NHWC" or "NHCWB16".
  input_layout: str,
      The layout of the Input Feature Map tensor. Can be "NHWC" or "NHCWB16".
  ifm_dtype: str,
      Datatype of the Input Feature Map tensor (IFM)
  ofm_dtype: str,
      Datatype of the Output Feature Map tensor (OFM)
  kernel_h: int,
      Height of kernel
  kernel_h: int
      Width of kernel
  
  Returns
  ----------
  List[BlockConfig]
      List containing all of the valid block configs
docstr_tok: ["Get","all","of","the","valid","block","configs","Parameters","--","--","--","--","--","ifm_propagator",":","Propagator",",","The","propagator","containing","the","data","dependencies","between","input","and","output","op_attrs",":","Dict",",","Dictionary","containing","operator","attributes","ofm_shape",":","List","[","int","]",",","Shape","of","the","output","tensor","ofm_channels",":","int",",","Number","of","output","channels","ifm_channels",":","int",",","Number","of","input","channels","output_layout",":","str",",","The","layout","of","the","Output","Feature","Map","tensor",".","Can","be","``","NHWC","''","or","``","NHCWB16","''",".","input_layout",":","str",",","The","layout","of","the","Input","Feature","Map","tensor",".","Can","be","``","NHWC","''","or","``","NHCWB16","''",".","ifm_dtype",":","str",",","Datatype","of","the","Input","Feature","Map","tensor","(","IFM",")","ofm_dtype",":","str",",","Datatype","of","the","Output","Feature","Map","tensor","(","OFM",")","kernel_h",":","int",",","Height","of","kernel","kernel_h",":","int","Width","of","kernel","Returns","--","--","--","--","--","List","[","BlockConfig","]","List","containing","all","of","the","valid","block","configs"]
code: def get_valid_block_configs(
      self,
      ifm_propagator: Propagator,
      op_attrs: Dict,
      ofm_shape: List[int],
      ofm_channels: int,
      ifm_channels: int,
      output_layout: str,
      input_layout: str,
      ifm_dtype: str,
      ofm_dtype: str,
      kernel_h: int = 1,
      kernel_w: int = 1,
  ) -> List[BlockConfig]:
      """Get all of the valid block configs
  
      Parameters
      ----------
      ifm_propagator: Propagator,
          The propagator containing the data dependencies between input and output
      op_attrs: Dict,
          Dictionary containing operator attributes
      ofm_shape: List[int],
          Shape of the output tensor
      ofm_channels: int,
          Number of output channels
      ifm_channels: int,
          Number of input channels
      output_layout: str,
          The layout of the Output Feature Map tensor. Can be "NHWC" or "NHCWB16".
      input_layout: str,
          The layout of the Input Feature Map tensor. Can be "NHWC" or "NHCWB16".
      ifm_dtype: str,
          Datatype of the Input Feature Map tensor (IFM)
      ofm_dtype: str,
          Datatype of the Output Feature Map tensor (OFM)
      kernel_h: int,
          Height of kernel
      kernel_h: int
          Width of kernel
  
      Returns
      ----------
      List[BlockConfig]
          List containing all of the valid block configs
      """
      valid_block_configs = []
  
      op_type = op_attrs.get("op")
      op_str = op_attrs.get("op_str")
      activation = op_attrs.get("activation", "NONE")
      upscaling_factor = (
          1 if op_attrs.get("upscale", "NONE") == "NONE" else 2
      )
  
      if output_layout == "NHCWB16":
          output_shape = _Shape(
              [1, ofm_shape[1], ofm_shape[3], ofm_channels]
          )
      else:
          output_shape = _Shape(ofm_shape)
  
      # Define search space
      max_height = min(
          output_shape.height, self._max_block_shape.height
      )
      min_height = max(self._micro_block.height, upscaling_factor)
  
      max_width = min(output_shape.width, self._max_block_shape.width)
      min_width = max(self._micro_block.width, upscaling_factor)
  
      max_depth = min(ofm_channels, self._max_block_shape.depth)
      min_depth = max(self._micro_block.depth, upscaling_factor)
  
      heights = range(min_height, max_height + min_height, min_height)
      widths = range(min_width, max_width + min_width, min_width)
      depths = range(min_depth, max_depth + min_depth, min_depth)
  
      # Handle user-forced block config
      options = tvm.transform.PassContext.current().config.get(
          "relay.ext.ethos-u.options", None
      )
      forced = False
      if options and options.dev_force_block_config:
          block_config = [
              int(v) for v in options.dev_force_block_config.split("x")
          ]
          assert len(block_config) == 3
          heights = [block_config[0]]
          widths = [block_config[1]]
          depths = [block_config[2]]
          forced = True
  
      input_bytewidth = 1 if ifm_dtype == "int8" else 2
      acc_bytewidth = self._get_accumulator_width(op_type, ifm_dtype)
      banks_available = self._total_banks - self._reserved_banks
      if activation == "LUT" and not self._lut_reserved:
          banks_available -= 2
  
      # Input block depth has additional limitations for operators that require full input depth
      input_block_depth = 0
      partkernel = self.is_partkernel(
          op_type, ifm_channels, ifm_dtype, kernel_h * kernel_w
      )
      if op_type == "ethosu_conv2d":
          if partkernel:
              input_block_depth = min(ifm_channels, 16)
          else:
              input_block_depth = min(ifm_channels, 32)
  
      for depth in reversed(depths):
          if (
              (depth < output_shape.depth)
              and (depth % self._split_depth != 0)
              and not forced
          ):
              # Block depth has to be less than full depth or a multiple of the split depth
              continue
  
          subkernel_propagator = self._get_subkernel_propagator(
              op_attrs,
              ifm_propagator,
              input_layout,
              output_layout,
              depth,
          )
  
          for width in reversed(widths):
              for height in reversed(heights):
                  if output_layout == "NHCWB16":
                      output_block = (
                          1,
                          height,
                          1 + ((depth - 1) // 16),
                          width,
                          16,
                      )
                      order = [1, 2, 4, 3, 0]
                  else:
                      output_block = (1, height, width, depth)
                      order = [1, 2, 3, 4]
  
                  offset = [0] * len(output_block)
                  stripes = [1] * len(output_block)
                  block_stripe_config = StripeConfig(
                      output_block,
                      output_block,
                      output_block,
                      order,
                      stripes,
                      offset,
                  )
  
                  # Propagate output block
                  input_block = subkernel_propagator.propagate(
                      block_stripe_config
                  )
  
                  input_block_shape = _Shape(
                      input_block.shape, input_layout
                  )
                  input_block_shape.round_up(self._input_micro_block)
  
                  output_block_shape = _Shape(
                      output_block, output_layout
                  )
  
                  if op_type == "ethosu_conv2d":
                      input_block_shape.depth = input_block_depth
  
                  # Banks required for input block
                  input_banks = self._get_input_banks(
                      input_block_shape, input_bytewidth
                  )
                  # Banks required for accumulation
                  acc_banks = self._get_accumulator_banks(
                      output_block_shape, acc_bytewidth
                  )
  
                  if (input_banks + acc_banks) <= banks_available:
                      output_cycles = self._get_output_cycles(
                          op_type,
                          op_str,
                          ifm_dtype,
                          ofm_dtype,
                          activation,
                      )
                      output_cycles *= np.prod(output_block).tolist()
                      output_cycles = int(math.ceil(output_cycles))
                      compute_cycles = (
                          self._estimate_compute_cycles_per_block(
                              op_type,
                              output_block_shape,
                              input_block_shape,
                              kernel_h,
                              kernel_w,
                              ifm_channels,
                              "int8",
                              partkernel,
                          )
                      )
                      block_config = BlockConfig(
                          input_block_shape.as_list(),
                          output_block,
                          compute_cycles,
                          output_cycles,
                      )
  
                      if self._disable_block_culling:
                          # Block culling disabled - add all block configs that fit
                          valid_block_configs.append(block_config)
                      else:
                          # Add block config only if it's not dominated by an existing block.
                          # A block config is dominated by another if its output_shape is greater
                          # or equal in every dimension and strictly greater in at least one
                          # dimension.
                          dominated = False
                          for valid_block in valid_block_configs:
                              if block_config < valid_block:
                                  dominated = True
                                  break
  
                          if not dominated:
                              valid_block_configs.append(block_config)
  
                          # Every consecutive block in the innermost loop will be dominated by
                          # this one so break
                          break
  
      return valid_block_configs
  
imports: from typing import Tuple, List, Dict, Optional
  from functools import reduce
  import math
  import numpy
  import tvm
  from .None import BlockConfig
  from .None import StripeConfig
  from .None import Propagator
label_desc: Use image node_id 19 for calling the EthosuDeviceConfig obj's underlying member method code with example usage: obj.get_valid_block_configs(ifm_propagator, op_attrs, ofm_shape, ofm_channels, ifm_channels, output_layout, input_layout, ifm_dtype, ofm_dtype, kernel_h, kernel_w) and returns: valid_block_configs