!
        @brief Prepare the flash algorithm for performing operations.
        
        First, the target is prepared to execute flash algo operations, including loading the algo
        to target RAM. This step is skipped if the target is already prepared, i.e., init() has been
        called, but cleanup() not called yet.
        
        Next, the algo's Init() function is called with the provided parameters. If the algo does
        not have an Init() function, this step is skipped. Calling Init() is also skipped if the
        algo was previously inited for the same operation without an intervening uninit. If the
        algo is already inited for a different operation, uninit() is automatically called prior
        to intiting for the new operation.
        
        @exception FlashFailure

def init(self, operation, address=None, clock=0, reset=True):
        """!
        @brief Prepare the flash algorithm for performing operations.
        
        First, the target is prepared to execute flash algo operations, including loading the algo
        to target RAM. This step is skipped if the target is already prepared, i.e., init() has been
        called, but cleanup() not called yet.
        
        Next, the algo's Init() function is called with the provided parameters. If the algo does
        not have an Init() function, this step is skipped. Calling Init() is also skipped if the
        algo was previously inited for the same operation without an intervening uninit. If the
        algo is already inited for a different operation, uninit() is automatically called prior
        to intiting for the new operation.
        
        @exception FlashFailure
        """
        if address is None:
            address = self.get_flash_info().rom_start
        
        assert isinstance(operation, self.Operation)
        assert (self._did_prepare_target) or (not self._did_prepare_target and self._active_operation is None)
        
        self.target.halt()
        
        # Handle the algo already being inited.
        if self._active_operation is not None:
            # Uninit if the algo was left inited for a different operation.
            if self._active_operation != operation:
                self.uninit()
            # Don't need to reinit for the same operation.
            else:
                return

        # Setup target for running the flash algo.
        if not self._did_prepare_target:
            if reset:
                self.target.reset_and_halt(Target.ResetType.SW)
            self.prepare_target()

            # Load flash algo code into target RAM.
            self.target.write_memory_block32(self.flash_algo['load_address'], self.flash_algo['instructions'])

            self._did_prepare_target = True

        # update core register to execute the init subroutine
        if self._is_api_valid('pc_init'):
            result = self._call_function_and_wait(self.flash_algo['pc_init'],
                                              r0=address, r1=clock, r2=operation.value, init=True)

            # check the return code
            if result != 0:
                raise FlashFailure('init error: %i' % result, result_code=result)
        
        self._active_operation = operation