diff options
Diffstat (limited to 'mpm/python/usrp_mpm/chips')
-rw-r--r-- | mpm/python/usrp_mpm/chips/CMakeLists.txt | 6 | ||||
-rwxr-xr-x | mpm/python/usrp_mpm/chips/ic_reg_maps/CMakeLists.txt | 8 | ||||
-rw-r--r-- | mpm/python/usrp_mpm/chips/lmx2572.py | 338 | ||||
-rw-r--r-- | mpm/python/usrp_mpm/chips/max10_cpld_flash_ctrl.py | 235 |
4 files changed, 585 insertions, 2 deletions
diff --git a/mpm/python/usrp_mpm/chips/CMakeLists.txt b/mpm/python/usrp_mpm/chips/CMakeLists.txt index 5c5670b14..692e4f11d 100644 --- a/mpm/python/usrp_mpm/chips/CMakeLists.txt +++ b/mpm/python/usrp_mpm/chips/CMakeLists.txt @@ -7,12 +7,14 @@ set(USRP_MPM_FILES ${USRP_MPM_FILES}) set(USRP_MPM_CHIP_FILES ${CMAKE_CURRENT_SOURCE_DIR}/__init__.py + ${CMAKE_CURRENT_SOURCE_DIR}/adf400x.py + ${CMAKE_CURRENT_SOURCE_DIR}/ds125df410.py ${CMAKE_CURRENT_SOURCE_DIR}/lmk04828.py ${CMAKE_CURRENT_SOURCE_DIR}/lmk04832.py ${CMAKE_CURRENT_SOURCE_DIR}/lmk03328.py - ${CMAKE_CURRENT_SOURCE_DIR}/adf400x.py - ${CMAKE_CURRENT_SOURCE_DIR}/ds125df410.py ${CMAKE_CURRENT_SOURCE_DIR}/lmk05318.py + ${CMAKE_CURRENT_SOURCE_DIR}/lmx2572.py + ${CMAKE_CURRENT_SOURCE_DIR}/max10_cpld_flash_ctrl.py ) list(APPEND USRP_MPM_FILES ${USRP_MPM_CHIP_FILES}) add_subdirectory(ic_reg_maps) diff --git a/mpm/python/usrp_mpm/chips/ic_reg_maps/CMakeLists.txt b/mpm/python/usrp_mpm/chips/ic_reg_maps/CMakeLists.txt index 631f30264..1a49daef6 100755 --- a/mpm/python/usrp_mpm/chips/ic_reg_maps/CMakeLists.txt +++ b/mpm/python/usrp_mpm/chips/ic_reg_maps/CMakeLists.txt @@ -34,6 +34,14 @@ if(ENABLE_REGMAPS) ${UHD_HOST_ROOT}/lib/ic_reg_maps/gen_lmk04816_regs.py ${CMAKE_CURRENT_BINARY_DIR}/lmk04816_regs.py ) + REG_MAPS_GEN_SOURCE( + ${UHD_HOST_ROOT}/lib/ic_reg_maps/gen_lmx2572_regs.py + ${CMAKE_CURRENT_BINARY_DIR}/lmx2572_regs.py + ) + REG_MAPS_GEN_SOURCE( + ${UHD_HOST_ROOT}/lib/ic_reg_maps/gen_zbx_cpld_regs.py + ${CMAKE_CURRENT_BINARY_DIR}/zbx_cpld_regs.py + ) # add an ic_reg_maps target which can be referenced outside of this subdirectory add_custom_target(ic_reg_maps DEPENDS ${IC_REG_MAPS}) diff --git a/mpm/python/usrp_mpm/chips/lmx2572.py b/mpm/python/usrp_mpm/chips/lmx2572.py new file mode 100644 index 000000000..e480dd53b --- /dev/null +++ b/mpm/python/usrp_mpm/chips/lmx2572.py @@ -0,0 +1,338 @@ +# +# Copyright 2019-2020 Ettus Research, a National Instruments Brand +# +# SPDX-License-Identifier: GPL-3.0-or-later +# +""" +LMX2572 parent driver class +""" + +import math +from builtins import object +from usrp_mpm.mpmlog import get_logger +from usrp_mpm.chips.ic_reg_maps import lmx2572_regs_t + +NUMBER_OF_LMX2572_REGISTERS = 126 + +class LMX2572(object): + """ + Generic driver class for LMX2572 access. + """ + + READ_ONLY_REGISTERS = [107, 108, 109, 110, 111, 112, 113] + + def __init__(self, regs_iface, parent_log = None): + self.log = parent_log + + self.regs_iface = regs_iface + assert hasattr(self.regs_iface, 'peek16') + assert hasattr(self.regs_iface, 'poke16') + self._poke16 = regs_iface.poke16 + self._peek16 = regs_iface.peek16 + + self._lmx2572_regs = lmx2572_regs_t() + + self._need_recalculation = True + self._enabled = False + + @property + def enabled(self): + return self._enabled + + @enabled.setter + def enabled(self, enable): + self._set_chip_enable(bool(enable)) + self._enabled = bool(enable) + + def reset(self): + """ + Performs a reset of the LMX2572 by using the software reset register. + """ + self._lmx2572_regs = lmx2572_regs_t() + self._lmx2572_regs.reset = lmx2572_regs_t.reset_t.RESET_RESET + self._poke16(0, self._lmx2572_regs.get_reg(0)) + self._lmx2572_regs.reset = lmx2572_regs_t.reset_t.RESET_NORMAL_OPERATION + self._set_default_values() + self._power_up_sequence() + + def commit(self): + """ + Calculates the settings when needed and writes the settings to the device + """ + if self._need_recalculation: + self._calculate_settings() + self._need_recalculation = False + self._write_registers_reference_chain() + self._write_registers_frequency_tuning() + + def check_pll_locked(self): + """ + Returns True if the PLL is locked, False otherwise. + """ + # SPI MISO is multiplexed to lock detect and register readback. Reading any + # register when the mux is set to the lock detect will return just the lock detect signal + self._lmx2572_regs.muxout_ld_sel = lmx2572_regs_t.muxout_ld_sel_t.MUXOUT_LD_SEL_LOCK_DETECT + self._poke16(0, self._lmx2572_regs.get_reg(0)) + + # If the PLL is locked we expect to read 0xFFFF from any read, else 0x0000 + value_read = self._peek16(0) + + return value_read == 0xFFFF + + def set_synchronization(self, enable_synchronization): + """ + Enables and disables the phase synchronization + """ + vco_phase_sync = lmx2572_regs_t.vco_phase_sync_en_t.VCO_PHASE_SYNC_EN_PHASE_SYNC_MODE if \ + enable_synchronization else \ + lmx2572_regs_t.vco_phase_sync_en_t.VCO_PHASE_SYNC_EN_NORMAL_OPERATION + self._lmx2572_regs.vco_phase_sync_en = vco_phase_sync + self._need_recalculation = True + + def get_synchronization(self): + """ + Returns the enabled/disabled state of the phase synchronization + """ + return self._lmx2572_regs.vco_phase_sync_en == \ + lmx2572_regs_t.vco_phase_sync_en_t.VCO_PHASE_SYNC_EN_PHASE_SYNC_MODE + + def set_output_enable_all(self, enable_output): + """ + Enables or disables the output on both ports + """ + self._set_output_a_enable(enable_output) + self._set_output_b_enable(enable_output) + + def _set_chip_enable(self, chip_enable): + """ + Enables or disables the LMX2572 using the powerdown register + All other registers are maintained during powerdown + """ + powerdown = lmx2572_regs_t.powerdown_t.POWERDOWN_NORMAL_OPERATION if chip_enable else \ + lmx2572_regs_t.powerdown_t.POWERDOWN_POWER_DOWN + self._lmx2572_regs.powerdown = powerdown + self._poke16(0, self._lmx2572_regs.get_reg(0)) + + def peek16(self, address): + """ + Wraps _peek16 to account for mux_ld_sel + """ + # SPI MISO is multiplexed to lock detect and register readback. Set the mux to register + # readback before trying to read the register. + self._lmx2572_regs.muxout_ld_sel = \ + lmx2572_regs_t.muxout_ld_sel_t.MUXOUT_LD_SEL_REGISTER_READBACK + self._poke16(0, self._lmx2572_regs.get_reg(0)) + + value_read = self._peek16(address) + + self._lmx2572_regs.muxout_ld_sel = lmx2572_regs_t.muxout_ld_sel_t.MUXOUT_LD_SEL_LOCK_DETECT + self._poke16(0, self._lmx2572_regs.get_reg(0)) + + return value_read + + + def _calculate_settings(self): + """ + This function is intended to be called for calculating the register settings, + however it is implementation, not chip specific, so it is defined but not implemented + """ + raise NotImplementedError("This function is meant to be overriden by a child class.") + + def _set_default_values(self): + """ + The register map has all base class defaults. + Subclasses can override this function to have the values populated on reset. + """ + pass + + def _pokes16(self, addr_vals): + """ + Apply a series of pokes. + pokes16((0,1),(0,2)) is the same as calling poke16(0,1), poke16(0,2). + """ + for addr, val in addr_vals: + self._poke16(addr, val) + + def _set_output_a_enable(self, enable_output): + """ + Sets output A (OUTA_PD) + """ + new_value = lmx2572_regs_t.outa_pd_t.OUTA_PD_NORMAL_OPERATION if enable_output \ + else lmx2572_regs_t.outa_pd_t.OUTA_PD_POWER_DOWN + self._lmx2572_regs.outa_pd = new_value + + def _set_output_b_enable(self, enable_output): + """ + Sets output B (OUTB_PD) + """ + new_value = lmx2572_regs_t.outb_pd_t.OUTB_PD_NORMAL_OPERATION if enable_output \ + else lmx2572_regs_t.outb_pd_t.OUTB_PD_POWER_DOWN + self._lmx2572_regs.outb_pd = new_value + + def _set_output_a_power(self, power): + """ + Sets the output A power + """ + self._lmx2572_regs.outa_pwr = power & self._lmx2572_regs.outa_pwr_mask + + def _set_output_b_power(self, power): + """ + Sets the output B power + """ + self._lmx2572_regs.outb_pwr = power & self._lmx2572_regs.outb_pwr_mask + + def _set_fcal_hpfd_adj(self, phase_detector_frequency): + """ + Sets the FCAL_HPFD_ADJ value based on the Fpfd + """ + # These magic number frequency constants are from the data sheet + if phase_detector_frequency <= 37.5e6: + self._lmx2572_regs.fcal_hpfd_adj = 0x0 + elif 37.5e6 < phase_detector_frequency <= 75e6: + self._lmx2572_regs.fcal_hpfd_adj = 0x1 + elif 75e6 < phase_detector_frequency <= 100e6: + self._lmx2572_regs.fcal_hpfd_adj = 0x2 + else: # 100MHz < phase_detector_frequency + self._lmx2572_regs.fcal_hpfd_adj = 0x3 + + def _set_fcal_lpfd_adj(self, phase_detector_frequency): + """ + Sets the FCAL_LPFD_ADJ value based on the Fpfd + """ + # These magic number frequency constants are from the data sheet + if phase_detector_frequency >= 10e6: + self._lmx2572_regs.fcal_lpfd_adj = 0x0 + elif 10e6 > phase_detector_frequency >= 5e6: + self._lmx2572_regs.fcal_lpfd_adj = 0x1 + elif 5e6 > phase_detector_frequency >= 2.5e6: + self._lmx2572_regs.fcal_lpfd_adj = 0x2 + else: # phase_detector_frequency < 2.5MHz + self._lmx2572_regs.fcal_lpfd_adj = 0x3 + + def _set_pll_n(self, n): + """ + Sets the pll_n registers + """ + self._lmx2572_regs.pll_n_upper_3_bits = (n >> 16) & \ + self._lmx2572_regs.pll_n_upper_3_bits_mask + self._lmx2572_regs.pll_n_lower_16_bits = n & self._lmx2572_regs.pll_n_lower_16_bits_mask + + def _set_pll_den(self, den): + """ + Sets the pll_den registers + """ + self._lmx2572_regs.pll_den_upper = (den >> 16) & self._lmx2572_regs.pll_den_upper_mask + self._lmx2572_regs.pll_den_lower = den & self._lmx2572_regs.pll_den_lower_mask + + def _set_mash_seed(self, mash_seed): + """ + Sets the mash seed register + """ + self._lmx2572_regs.mash_seed_upper = (mash_seed >> 16) & \ + self._lmx2572_regs.mash_seed_upper_mask + self._lmx2572_regs.mash_seed_lower = mash_seed & self._lmx2572_regs.mash_seed_lower_mask + + def _set_pll_num(self, num): + """ + Sets the pll_num registers + """ + self._lmx2572_regs.pll_num_upper = (num >> 16) & self._lmx2572_regs.pll_num_upper_mask + self._lmx2572_regs.pll_num_lower = num & self._lmx2572_regs.pll_num_lower_mask + + def _set_mash_rst_count(self, mash_rst_count): + """ + Sets the mash_rst_count registers + """ + self._lmx2572_regs.mash_rst_count_upper = (mash_rst_count >> 16) & \ + self._lmx2572_regs.mash_rst_count_upper_mask + self._lmx2572_regs.mash_rst_count_lower = mash_rst_count & \ + self._lmx2572_regs.mash_rst_count_lower_mask + + def _compute_and_set_mult_hi(self, reference_frequency): + multiplier_output_frequency = (reference_frequency*(int(self._lmx2572_regs.osc_2x.value)\ + +1)*self._lmx2572_regs.mult) / self._lmx2572_regs.pll_r_pre + new_mult_hi = lmx2572_regs_t.mult_hi_t.MULT_HI_GREATER_THAN_100M \ + if self._lmx2572_regs.mult > 1 and multiplier_output_frequency > 100e6 else \ + lmx2572_regs_t.mult_hi_t.MULT_HI_LESS_THAN_EQUAL_TO_100M + self._lmx2572_regs.mult_hi = new_mult_hi + + def _power_up_sequence(self): + """ + Performs the intial register writes for the LMX2572 + """ + for register in reversed(range(NUMBER_OF_LMX2572_REGISTERS)): + if register in LMX2572.READ_ONLY_REGISTERS: + continue + self._poke16(register, self._lmx2572_regs.get_reg(register)) + + def _write_registers_frequency_tuning(self): + """ + This function writes just the registers for frequency tuning + """ + # Write PLL_N to registers R34 and R36 + self._poke16(34, self._lmx2572_regs.get_reg(34)) + self._poke16(36, self._lmx2572_regs.get_reg(36)) + # Write PLL_DEN to registers R38 and R39 + self._poke16(38, self._lmx2572_regs.get_reg(38)) + self._poke16(39, self._lmx2572_regs.get_reg(39)) + # Write PLL_NUM to registers R42 and R43 + self._poke16(42, self._lmx2572_regs.get_reg(42)) + self._poke16(43, self._lmx2572_regs.get_reg(43)) + + # MASH_SEED to registers R40 and R41 + self._poke16(40, self._lmx2572_regs.get_reg(40)) + self._poke16(41, self._lmx2572_regs.get_reg(41)) + + # Write OUTA_PWR to register R44 or OUTB_PWR to register R45 + # Write OUTA_MUX to register R45 and/or OUTB_MUX to register R46 + self._poke16(44, self._lmx2572_regs.get_reg(44)) + self._poke16(45, self._lmx2572_regs.get_reg(45)) + self._poke16(46, self._lmx2572_regs.get_reg(46)) + + # Write CHDIV to register R75 + self._poke16(75, self._lmx2572_regs.get_reg(75)) + + # Write CPG to register R14 + self._poke16(14, self._lmx2572_regs.get_reg(14)) + + # Write PFD_DLY_SEL to register R37 + self._poke16(37, self._lmx2572_regs.get_reg(37)) + + # Write VCO_SEL to register R20 + self._poke16(20, self._lmx2572_regs.get_reg(20)) + + # Write VCO_DACISET_STRT to register R17 + self._poke16(17, self._lmx2572_regs.get_reg(17)) + + # Write VCO_CALCTRL_STRT to register R78 + self._poke16(78, self._lmx2572_regs.get_reg(78)) + + # Write R0 to latch double buffered registers + self._poke16(0, self._lmx2572_regs.get_reg(0)) + self._poke16(0, self._lmx2572_regs.get_reg(0)) + + def _write_registers_reference_chain(self): + """ + This function writes the registers that are used for setting the reference chain + """ + # Write FCAL_HPFD_ADJ to register R0 + # Write FCAL_LPFD_ADJ to register R0 + self._poke16(0, self._lmx2572_regs.get_reg(0)) + + # Write MULT_HI to register R9 + # Write OSC_2X to register R9 + self._poke16(9, self._lmx2572_regs.get_reg(9)) + + # Write MULT to register R10 + self._poke16(10, self._lmx2572_regs.get_reg(10)) + + # Write PLL_R to register R11 + self._poke16(11, self._lmx2572_regs.get_reg(11)) + # Write PLL_R_PRE to register R12 + self._poke16(12, self._lmx2572_regs.get_reg(12)) + + # if Phase SYNC being used: + # Write MASH_RST_COUNT to registers R69 and 70 + if self.get_synchronization(): + self._poke16(70, self._lmx2572_regs.get_reg(70)) + self._poke16(69, self._lmx2572_regs.get_reg(69)) diff --git a/mpm/python/usrp_mpm/chips/max10_cpld_flash_ctrl.py b/mpm/python/usrp_mpm/chips/max10_cpld_flash_ctrl.py new file mode 100644 index 000000000..c1e756124 --- /dev/null +++ b/mpm/python/usrp_mpm/chips/max10_cpld_flash_ctrl.py @@ -0,0 +1,235 @@ +#!/usr/bin/env python3 +# +# Copyright 2020 Ettus Research, a National Instruments Brand +# +# SPDX-License-Identifier: GPL-3.0-or-later +# +""" +Update the CPLD image using the on-chip flash on Intel MAX10 devices +""" +import os +import time + +class Max10CpldFlashCtrl(): + """ + Context manager class used to handle CPLD Flash reconfiguration. + Calling "with" using an instance of this class will disable write + protection for the duration of that context. + """ + REVISION_REG = 0x0004 + # Addresses relative to reconfiguration register offset + FLASH_STATUS_REG = 0x0000 + FLASH_CONTROL_REG = 0x0004 + FLASH_ADDR_REG = 0x0008 + FLASH_WRITE_DATA_REG = 0x000C + FLASH_READ_DATA_REG = 0x0010 + FLASH_CFM0_START_ADDR_REG = 0x0014 + FLASH_CFM0_END_ADDR_REG = 0x0018 + + # Masks for FLASH_STATUS_REG + FLASH_MEM_INIT_ENABLED_MASK = 0x10000 + + def __init__(self, logger, regs, reconfig_regs_offset, cpld_min_revision): + if logger == None: + logger = get_logger('update_cpld') + self.log = logger + self.regs = regs + self.reconfig_regs_offset = reconfig_regs_offset + self.cpld_min_revision = cpld_min_revision + + def peek32(self, addr): + return self.regs.peek32(addr + self.reconfig_regs_offset) + + def poke32(self, addr, val): + self.regs.poke32(addr + self.reconfig_regs_offset, val) + + def __enter__(self): + self.enabled_write_protection(enable=False) + + def __exit__(self, exc_type, exc_value, traceback): + self.enabled_write_protection(enable=True) + + def enabled_write_protection(self, enable=True): + if enable: + self.poke32(self.FLASH_CONTROL_REG, 1 << 0) # FLASH_ENABLE_WP_STB + else: + self.poke32(self.FLASH_CONTROL_REG, 1 << 1) # FLASH_DISABLE_WP_STB + + def check_revision(self): + self.log.debug('Checking for compatible CPLD revision') + cpld_revision = self.regs.peek32(self.REVISION_REG) + if cpld_revision < self.cpld_min_revision: + self.log.error("Unexpected CPLD revision 0x{:X}".format(cpld_revision)) + return False + return True + + def get_start_addr(self): + return self.peek32(self.FLASH_CFM0_START_ADDR_REG) + + def get_end_addr(self): + return self.peek32(self.FLASH_CFM0_END_ADDR_REG) + + def is_memory_initialization_enabled(self): + return self.peek32(self.FLASH_STATUS_REG) & self.FLASH_MEM_INIT_ENABLED_MASK + + # expected value of 0x1110 indicates idle state of read, write, and erase + # routines (see function wait_for_idle for details), 0x0001 indicates the + # write protection is enabled + def check_reconfig_engine_status(self, expected_value=0x1111): + status = self.peek32(self.FLASH_STATUS_REG) + status = status & ~self.FLASH_MEM_INIT_ENABLED_MASK + if (status != expected_value): + self.log.error("Unexpected reconfig engine status 0x%08X" % status) + return False + return True + + def wait_for_idle(self, operation, timeout=350): + """ + Wait for the idle bit to assert for the given operation. + If the idle bit is not True before the timeout (given in ms), + return False. + """ + if operation == 'write': + status_bit = 1 << 12 # FLASH_WRITE_IDLE + elif operation == 'erase': + status_bit = 1 << 8 # FLASH_ERASE_IDLE + elif operation == 'read': + status_bit = 1 << 4 # FLASH_READ_IDLE + else: + self.log.error('Cannot wait for unknown operation {}'.format(operation)) + raise RuntimeError('Cannot wait for unknown operation {}'.format(operation)) + for _ in range(0, timeout): + status = self.peek32(self.FLASH_STATUS_REG) + if (status & status_bit): + return True + time.sleep(0.001) # 1 ms + return False + + def erase_flash_memory(self): + with self: + # check for sectors to be erased: + if self.is_memory_initialization_enabled(): + sectors = [2, 3, 4] + else: + sectors = [4] + # erase each sector individually + for sector in sectors: + # start erase + self.poke32(self.FLASH_CONTROL_REG, (1 << 4) | ((sector & 0x7) << 5)) + # wait for erase to finish + if not self.wait_for_idle('erase', timeout=350): + self.log.error('There was a timeout waiting for ' + 'Flash erase to complete!') + return False + return True + + def program_flash_memory(self, raw_data): + with self: + # write words one at a time + for i, data in enumerate(raw_data): + # status display + if (i%1000 == 0): + self.log.debug('%d%% written', i*4/self.file_size*100) + # write address and data + self.poke32(self.FLASH_ADDR_REG, self.cpld_start_address+i) + self.poke32(self.FLASH_WRITE_DATA_REG, data) + # start write operation + self.poke32(self.FLASH_CONTROL_REG, 1 << 3) + # wait for write to finish + if not self.wait_for_idle('write', timeout=2): + self.log.error('There was a timeout waiting for ' + 'Flash write to complete!') + return False + if not self.check_reconfig_engine_status(expected_value=0x1110): + return False + return True + + def verify_flash_memory(self, raw_data): + # read words one at a time + for i, data in enumerate(raw_data): + # status display + if (i%1000 == 0): + self.log.debug('%d%% done', i*4/self.file_size*100) + # write address + self.poke32(self.FLASH_ADDR_REG, self.cpld_start_address+i) + # start read operation + self.poke32(self.FLASH_CONTROL_REG, 1 << 2) + # wait for read to finish + if not self.wait_for_idle('read', timeout=1): + self.log.error('There was a timeout waiting for ' + 'Flash read to complete!') + return False + # read data from device + device_data = self.peek32(self.FLASH_READ_DATA_REG) + if (data != device_data): + self.log.error("Data mismatch! address %d, expected value 0x%08X," + " read value 0x%08X" % + (i+self.cpld_start_address, data, device_data)) + return False + return True + + def reverse_bits_in_byte(self, n): + result = 0 + for _ in range(8): + result <<= 1 + result |= n & 1 + n >>= 1 + return result + + def update(self, filename): + if not self.check_revision(): + return False + + self.log.debug('Checking CPLD image file') + self.file_size = os.path.getsize(filename) + self.cpld_start_address = self.get_start_addr() + cpld_end_address = self.get_end_addr() + expected_size = (cpld_end_address+1-self.cpld_start_address)*4 + if (self.file_size != expected_size): + self.log.error("Unexpected file size! Required size: %d bytes" % expected_size) + return False + + # Convert data from bytes to 32-bit words and reverse bit order + # to be compatible with Altera's on-chip flash IP + raw_data = [] + with open(filename, 'rb') as binary_file: + for _ in range(self.file_size//4): + number = 0 + for _ in range(4): + number <<= 8 + number |= self.reverse_bits_in_byte(int.from_bytes(binary_file.read(1), 'big')) + raw_data.append(number) + + if not self.check_reconfig_engine_status(): + return False + + self.log.debug('Erasing CPLD flash memory...') + if not (self.erase_flash_memory() + and self.check_reconfig_engine_status()): + self.log.error('There was an error while reprogramming the CPLD image. ' + 'Please program the CPLD again with a valid image before power ' + 'cycling the board to ensure it is in a valid state.') + return False + self.log.debug('CPLD flash memory erased.') + + self.log.debug('Programming flash memory...') + if not (self.program_flash_memory(raw_data) + and self.check_reconfig_engine_status()): + self.log.error('There was an error while reprogramming the CPLD image. ' + 'Please program the CPLD again with a valid image before power ' + 'cycling the board to ensure it is in a valid state.') + return False + self.log.debug('Flash memory programming complete.') + + self.log.debug('Verifying image in flash...') + if not (self.verify_flash_memory(raw_data) + and self.check_reconfig_engine_status()): + self.log.error('There was an error while reprogramming the CPLD image. ' + 'Please program the CPLD again with a valid image before power ' + 'cycling the board to ensure it is in a valid state.') + return False + self.log.debug('Flash image verification complete.') + + self.log.info('CPLD reprogrammed! Please power-cycle the device.') + + return True |