aboutsummaryrefslogtreecommitdiffstats
path: root/mpm/python/usrp_mpm/chips
diff options
context:
space:
mode:
Diffstat (limited to 'mpm/python/usrp_mpm/chips')
-rw-r--r--mpm/python/usrp_mpm/chips/CMakeLists.txt6
-rwxr-xr-xmpm/python/usrp_mpm/chips/ic_reg_maps/CMakeLists.txt8
-rw-r--r--mpm/python/usrp_mpm/chips/lmx2572.py338
-rw-r--r--mpm/python/usrp_mpm/chips/max10_cpld_flash_ctrl.py235
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