! @brief Return a dictionary representing a pyOCD flash algorithm, or None.
        
        The most interesting operation this method performs is dynamically allocating memory
        for the flash algo from a given RAM region. Note that the .data and .bss sections are
        concatenated with .text. That's why there isn't a specific allocation for those sections.
        
        Double buffering is supported as long as there is enough RAM.
        
        Memory layout:
        ```
        [stack] [code] [buf1] [buf2]
        ```
        
        @param self
        @param blocksize The size to use for page buffers, normally the erase block size.
        @param ram_region A RamRegion object where the flash algo will be allocated.
        @return A pyOCD-style flash algo dictionary. If None is returned, the flash algo did
            not fit into the provided ram_region.

def get_pyocd_flash_algo(self, blocksize, ram_region):
        """! @brief Return a dictionary representing a pyOCD flash algorithm, or None.
        
        The most interesting operation this method performs is dynamically allocating memory
        for the flash algo from a given RAM region. Note that the .data and .bss sections are
        concatenated with .text. That's why there isn't a specific allocation for those sections.
        
        Double buffering is supported as long as there is enough RAM.
        
        Memory layout:
        ```
        [stack] [code] [buf1] [buf2]
        ```
        
        @param self
        @param blocksize The size to use for page buffers, normally the erase block size.
        @param ram_region A RamRegion object where the flash algo will be allocated.
        @return A pyOCD-style flash algo dictionary. If None is returned, the flash algo did
            not fit into the provided ram_region.
        """
        instructions = self._FLASH_BLOB_HEADER + byte_list_to_u32le_list(self.algo_data)

        offset = 0

        # Stack
        offset += FLASH_ALGO_STACK_SIZE
        addr_stack = ram_region.start + offset

        # Load address
        addr_load = ram_region.start + offset
        offset += len(instructions) * 4

        # Data buffer 1
        addr_data = ram_region.start + offset
        offset += blocksize

        if offset > ram_region.length:
            # Not enough space for flash algorithm
            LOG.warning("Not enough space for flash algorithm")
            return None

        # Data buffer 2
        addr_data2 = ram_region.start + offset
        offset += blocksize

        if offset > ram_region.length:
            page_buffers = [addr_data]
        else:
            page_buffers = [addr_data, addr_data2]

        # TODO - analyzer support

        code_start = addr_load + self._FLASH_BLOB_HEADER_SIZE
        flash_algo = {
            "load_address": addr_load,
            "instructions": instructions,
            "pc_init": code_start + self.symbols["Init"],
            "pc_unInit": code_start + self.symbols["UnInit"],
            "pc_eraseAll": code_start + self.symbols["EraseChip"],
            "pc_erase_sector": code_start + self.symbols["EraseSector"],
            "pc_program_page": code_start + self.symbols["ProgramPage"],
            "page_buffers": page_buffers,
            "begin_data": page_buffers[0],
            "begin_stack": addr_stack,
            "static_base": code_start + self.rw_start,
            "min_program_length": self.page_size,
            "analyzer_supported": False
        }
        return flash_algo