Ejemplo n.º 1
0
    def __init__(self, label, log):
        self.log = log.getChild("RfdcRegs")
        self.regs = UIO(label=label, read_only=False)
        self.poke32 = self.regs.poke32
        self.peek32 = self.regs.peek32

        # Index corresponds to dboard number.
        self._converter_chains_in_reset = True
Ejemplo n.º 2
0
 def __init__(self, label, log):
     self.log = log
     self.regs = UIO(
         label=label,
         read_only=False
     )
     self.periph_peek32 = lambda addr: self.regs.peek32(addr + self.PERIPH_INTERCON_BASE)
     self.periph_poke32 = lambda addr, data: self.regs.poke32(addr + self.PERIPH_INTERCON_BASE, data)
Ejemplo n.º 3
0
 def __init__(self, label, log):
     self.log = log
     self.regs = UIO(
         label=label,
         read_only=False
     )
     self.poke32 = self.regs.poke32
     self.peek32 = self.regs.peek32
Ejemplo n.º 4
0
 def dump_jesd_core(self):
     " Debug method to dump all JESD core regs "
     dboard_ctrl_regs = UIO(label="dboard-regs-{}".format(self.slot_idx),
                            read_only=False)
     for i in range(0x2000, 0x2110, 0x10):
         print(("0x%04X " % i), end=' ')
         for j in range(0, 0x10, 0x4):
             print(("%08X" % dboard_ctrl_regs.peek32(i + j)), end=' ')
         print("")
     dboard_ctrl_regs = None
Ejemplo n.º 5
0
 def dump_jesd_core(self):
     """
     Debug for reading out all JESD core registers via RPC shell
     """
     radio_regs = UIO(label="dboard-regs-{}".format(self.slot_idx))
     for i in range(0x2000, 0x2110, 0x10):
         print(("0x%04X " % i), end=' ')
         for j in range(0, 0x10, 0x4):
             print(("%08X" % radio_regs.peek32(i + j)), end=' ')
         print("")
Ejemplo n.º 6
0
 def dbcore_peek(self, addr):
     """
     Debug for accessing the DB Core registers via the RPC shell.
     """
     dboard_ctrl_regs = UIO(label="dboard-regs-{}".format(self.slot_idx),
                            read_only=False)
     rd_data = dboard_ctrl_regs.peek32(addr)
     self.log.trace("DB Core Register 0x{:04X} response: 0x{:08X}".format(
         addr, rd_data))
     dboard_ctrl_regs = None
     return rd_data
Ejemplo n.º 7
0
 def dbcore_poke(self, addr, data):
     """
     Debug for accessing the DB Core registers via the RPC shell.
     """
     dboard_ctrl_regs = UIO(label="dboard-regs-{}".format(self.slot_idx),
                            read_only=False)
     self.log.trace(
         "Writing DB Core Register 0x{:04X} with 0x{:08X}...".format(
             addr, data))
     dboard_ctrl_regs.poke32(addr, data)
     dboard_ctrl_regs = None
Ejemplo n.º 8
0
 def _init_dboard_regs():
     " Create a UIO object to talk to dboard regs "
     self.log.trace("Getting uio...")
     return UIO(
         label="dboard-regs-{}".format(self.slot_idx),
         read_only=False
     )
Ejemplo n.º 9
0
 def __init__(self, label, log):
     self.log = log
     self.regs = UIO(
         label=label,
         read_only=False
     )
     self.periph_peek32 = lambda addr: self.regs.peek32(addr + self.PERIPH_INTERCON_BASE)
     self.periph_poke32 = lambda addr, data: self.regs.poke32(addr + self.PERIPH_INTERCON_BASE, data)
Ejemplo n.º 10
0
 def __init__(self, label, log):
     self.log = log.getChild("CtrlportRegs")
     self._regs_uio_opened = False
     try:
         self.regs = UIO(label=label, read_only=False)
     except RuntimeError:
         self.log.warning('Ctrlport regs could not be found. ' \
                          'MPM Endpoint to the FPGA is not part of this image.')
         self.regs = None
     # Initialize SPI interface to MB PL CPLD and DB CPLDs
     self.set_mb_pl_cpld_divider(self.min_mb_cpld_spi_divider)
     self.set_db_divider_value(self.min_db_cpld_spi_divider)
     self.mb_pl_cpld_regs = self.MbPlCpldIface(self, self.MB_PL_CPLD,
                                               self.log)
     self.mb_pl_cpld_regs.check_signature()
     self.mb_pl_cpld_regs.check_revision()
     self.db_0_regs = self.DbCpldIface(self, self.DB_0_CPLD)
     self.db_1_regs = self.DbCpldIface(self, self.DB_1_CPLD)
Ejemplo n.º 11
0
def main():
    " Dawaj, dawaj! "
    args = parse_args()
    # Initialize logger for downstream components
    mpm.get_main_logger().getChild('main')
    master_core_uio = UIO(label=args.uio_dev, read_only=False)
    master_core_uio.open()
    if args.slave_uio_dev:
        slave_core_uio = UIO(label=args.uio_dev, read_only=False)
        slave_core_uio.open()
    try:
        master_core = AuroraControl(master_core_uio, args.base_addr)
        slave_core = None if args.slave_uio_dev is None else AuroraControl(
            slave_core_uio,
            args.slave_base_addr,
        )
        if args.loopback:
            master_core.reset_core()
            master_core.set_loopback(enable=True)
            return True
        # Run BIST
        if args.test == 'ber':
            print("Performing BER BIST test.")
            master_core.run_ber_loopback_bist(
                args.duration,
                args.rate * 8e6,
                slave_core,
            )
        else:
            print("Performing Latency BIST test.")
            master_core.run_latency_loopback_bist(
                args.duration,
                args.rate * 8e6,
                slave_core,
            )
    except Exception as ex:
        print("Unexpected exception: {}".format(str(ex)))
        return False
    finally:
        master_core_uio.close()
        if args.slave_uio_dev:
            slave_core_uio.close()
Ejemplo n.º 12
0
class WhiteRabbitRegsControl(object):
    """
    Control and read the FPGA White Rabbit core registers
    """
    # Memory Map
    #  0x00000000: I/D Memory
    #  0x00020000: Peripheral interconnect
    #      +0x000: Minic
    #      +0x100: Endpoint
    #      +0x200: Softpll
    #      +0x300: PPS gen
    #      +0x400: Syscon
    #      +0x500: UART
    #      +0x600: OneWire
    #      +0x700: Auxillary space (Etherbone config, etc)
    #      +0x800: WRPC diagnostics registers
    PERIPH_INTERCON_BASE = 0x20000

    # PPS_GEN Map
    PPSG_ESCR = 0x31C

    def __init__(self, label, log):
        self.log = log
        self.regs = UIO(
            label=label,
            read_only=False
        )
        self.periph_peek32 = lambda addr: self.regs.peek32(addr + self.PERIPH_INTERCON_BASE)
        self.periph_poke32 = lambda addr, data: self.regs.poke32(addr + self.PERIPH_INTERCON_BASE, data)

    def get_time_lock_status(self):
        """
        Retrieves and decodes the lock status for the PPS out of the WR core.
        """
        with self.regs.open():
            ext_sync_status = self.periph_peek32(self.PPSG_ESCR)
        # bit 2: PPS_VALID
        # bit 3: TM_VALID (timecode)
        # All other bits MUST be ignored since they are not guaranteed to be zero or
        # stable!
        return (ext_sync_status & 0b1100) == 0b1100
Ejemplo n.º 13
0
class WhiteRabbitRegsControl(object):
    """
    Control and read the FPGA White Rabbit core registers
    """
    # Memory Map
    #  0x00000000: I/D Memory
    #  0x00020000: Peripheral interconnect
    #      +0x000: Minic
    #      +0x100: Endpoint
    #      +0x200: Softpll
    #      +0x300: PPS gen
    #      +0x400: Syscon
    #      +0x500: UART
    #      +0x600: OneWire
    #      +0x700: Auxillary space (Etherbone config, etc)
    #      +0x800: WRPC diagnostics registers
    PERIPH_INTERCON_BASE = 0x20000

    # PPS_GEN Map
    PPSG_ESCR = 0x31C

    def __init__(self, label, log):
        self.log = log
        self.regs = UIO(
            label=label,
            read_only=False
        )
        self.periph_peek32 = lambda addr: self.regs.peek32(addr + self.PERIPH_INTERCON_BASE)
        self.periph_poke32 = lambda addr, data: self.regs.poke32(addr + self.PERIPH_INTERCON_BASE, data)

    def get_time_lock_status(self):
        """
        Retrieves and decodes the lock status for the PPS out of the WR core.
        """
        with self.regs:
            ext_sync_status = self.periph_peek32(self.PPSG_ESCR)
        # bit 2: PPS_VALID
        # bit 3: TM_VALID (timecode)
        # All other bits MUST be ignored since they are not guaranteed to be zero or
        # stable!
        return (ext_sync_status & 0b1100) == 0b1100
Ejemplo n.º 14
0
class LiberioDispatcherTable(object):
    """
    Controls a Liberio DMA dispatcher table.

    label -- A label that can be used by udev to find a UIO device
    """

    def __init__(self, label):
        self.log = get_logger(label)
        self._regs = UIO(label=label, read_only=False)
        self.poke32 = self._regs.poke32
        self.peek32 = self._regs.peek32

    def set_route(self, sid, dma_channel):
        """
        Sets up routing in the Liberio dispatcher. From sid, only the
        destination part is important. After this call, any CHDR packet with the
        appropriate destination address will get routed to `dma_channel`.

        sid -- Full SID, but only destination part matters.
        dma_channel -- The DMA channel to which these packets should get routed.
        """
        self.log.debug(
            "Routing SID `{sid}' to DMA channel `{chan}'.".format(
                sid=str(sid), chan=dma_channel
            )
        )
        def poke_and_trace(addr, data):
            " Do a poke32() and log.trace() "
            self.log.trace("Writing to address 0x{:04X}: 0x{:04X}".format(
                addr, data
            ))
            self.poke32(addr, data)
        # Poke reg for destination channel
        try:
            with self._regs.open():
                poke_and_trace(
                    0 + 4 * sid.dst_ep,
                    dma_channel,
                )
        except Exception as ex:
            self.log.error(
                "Unexpected exception while setting route: %s",
                str(ex),
            )
            raise
Ejemplo n.º 15
0
class MboardRegsControl(object):
    """
    Control the FPGA Motherboard registers
    """
    # Motherboard registers
    M_COMPAT_NUM = 0x0000
    MB_DATESTAMP = 0x0004
    MB_GIT_HASH = 0x0008
    MB_SCRATCH = 0x000C
    MB_NUM_CE = 0x0010
    MB_NUM_IO_CE = 0x0014
    MB_CLOCK_CTRL = 0x0018
    MB_XADC_RB = 0x001C
    MB_BUS_CLK_RATE = 0x0020
    MB_BUS_COUNTER = 0x0024
    MB_SFP0_INFO = 0x0028
    MB_SFP1_INFO = 0x002C
    MB_GPIO_MASTER = 0x0030
    MB_GPIO_RADIO_SRC = 0x0034

    # Bitfield locations for the MB_CLOCK_CTRL register.
    MB_CLOCK_CTRL_PPS_SEL_INT_10 = 0  # pps_sel is one-hot encoded!
    MB_CLOCK_CTRL_PPS_SEL_INT_25 = 1
    MB_CLOCK_CTRL_PPS_SEL_EXT = 2
    MB_CLOCK_CTRL_PPS_SEL_GPSDO = 3
    MB_CLOCK_CTRL_PPS_OUT_EN = 4  # output enabled = 1
    MB_CLOCK_CTRL_MEAS_CLK_RESET = 12  # set to 1 to reset mmcm, default is 0
    MB_CLOCK_CTRL_MEAS_CLK_LOCKED = 13  # locked indication for meas_clk mmcm

    def __init__(self, label, log):
        self.log = log
        self.regs = UIO(label=label, read_only=False)
        self.poke32 = self.regs.poke32
        self.peek32 = self.regs.peek32

    def get_compat_number(self):
        """get FPGA compat number

        This function reads back FPGA compat number.
        The return is a tuple of
        2 numbers: (major compat number, minor compat number )
        """
        with self.regs.open():
            compat_number = self.peek32(self.M_COMPAT_NUM)
        minor = compat_number & 0xff
        major = (compat_number >> 16) & 0xff
        return (major, minor)

    def set_fp_gpio_master(self, value):
        """set driver for front panel GPIO
        Arguments:
            value {unsigned} -- value is a single bit bit mask of 12 pins GPIO
        """
        with self.regs.open():
            return self.poke32(self.MB_GPIO_MASTER, value)

    def get_fp_gpio_master(self):
        """get "who" is driving front panel gpio
           The return value is a bit mask of 12 pins GPIO.
           0: means the pin is driven by PL
           1: means the pin is driven by PS
        """
        with self.regs.open():
            return self.peek32(self.MB_GPIO_MASTER) & 0xfff

    def set_fp_gpio_radio_src(self, value):
        """set driver for front panel GPIO
        Arguments:
            value {unsigned} -- value is 2-bit bit mask of 12 pins GPIO
           00: means the pin is driven by radio 0
           01: means the pin is driven by radio 1
           10: means the pin is driven by radio 2
           11: means the pin is driven by radio 3
        """
        with self.regs.open():
            return self.poke32(self.MB_GPIO_RADIO_SRC, value)

    def get_fp_gpio_radio_src(self):
        """get which radio is driving front panel gpio
           The return value is 2-bit bit mask of 12 pins GPIO.
           00: means the pin is driven by radio 0
           01: means the pin is driven by radio 1
           10: means the pin is driven by radio 2
           11: means the pin is driven by radio 3
        """
        with self.regs.open():
            return self.peek32(self.MB_GPIO_RADIO_SRC) & 0xffffff

    def get_build_timestamp(self):
        """
        Returns the build date/time for the FPGA image.
        The return is datetime string with the  ISO 8601 format
        (YYYY-MM-DD HH:MM:SS.mmmmmm)
        """
        with self.regs.open():
            datestamp_rb = self.peek32(self.MB_DATESTAMP)
        if datestamp_rb > 0:
            dt_str = datetime.datetime(year=((datestamp_rb >> 17) & 0x3F) +
                                       2000,
                                       month=(datestamp_rb >> 23) & 0x0F,
                                       day=(datestamp_rb >> 27) & 0x1F,
                                       hour=(datestamp_rb >> 12) & 0x1F,
                                       minute=(datestamp_rb >> 6) & 0x3F,
                                       second=((datestamp_rb >> 0) & 0x3F))
            self.log.trace("FPGA build timestamp: {}".format(str(dt_str)))
            return str(dt_str)
        else:
            # Compatibility with FPGAs without datestamp capability
            return ''

    def get_git_hash(self):
        """
        Returns the GIT hash for the FPGA build.
        The return is a tuple of
        2 numbers: (short git hash, bool: is the tree dirty?)
        """
        with self.regs.open():
            git_hash_rb = self.peek32(self.MB_GIT_HASH)
        git_hash = git_hash_rb & 0x0FFFFFFF
        tree_dirty = ((git_hash_rb & 0xF0000000) > 0)
        dirtiness_qualifier = 'dirty' if tree_dirty else 'clean'
        self.log.trace("FPGA build GIT Hash: {:07x} ({})".format(
            git_hash, dirtiness_qualifier))
        return (git_hash, dirtiness_qualifier)

    def set_time_source(self, time_source, ref_clk_freq):
        """
        Set time source
        """
        pps_sel_val = 0x0
        if time_source == 'internal':
            assert ref_clk_freq in (10e6, 25e6)
            if ref_clk_freq == 10e6:
                self.log.trace("Setting time source to internal "
                               "(10 MHz reference)...")
                pps_sel_val = 0b1 << self.MB_CLOCK_CTRL_PPS_SEL_INT_10
            elif ref_clk_freq == 25e6:
                self.log.trace("Setting time source to internal "
                               "(25 MHz reference)...")
                pps_sel_val = 0b1 << self.MB_CLOCK_CTRL_PPS_SEL_INT_25
        elif time_source == 'external':
            self.log.trace("Setting time source to external...")
            pps_sel_val = 0b1 << self.MB_CLOCK_CTRL_PPS_SEL_EXT
        elif time_source == 'gpsdo':
            self.log.trace("Setting time source to gpsdo...")
            pps_sel_val = 0b1 << self.MB_CLOCK_CTRL_PPS_SEL_GPSDO
        else:
            assert False
        with self.regs.open():
            reg_val = self.peek32(self.MB_CLOCK_CTRL) & 0xFFFFFFF0
            reg_val = reg_val | (pps_sel_val & 0xF)
            self.log.trace("Writing MB_CLOCK_CTRL to 0x{:08X}".format(reg_val))
            self.poke32(self.MB_CLOCK_CTRL, reg_val)

    def enable_pps_out(self, enable):
        """
        Enables the PPS/Trig output on the back panel
        """
        self.log.trace("%s PPS/Trig output!",
                       "Enabling" if enable else "Disabling")
        mask = 0xFFFFFFFF ^ (0b1 << self.MB_CLOCK_CTRL_PPS_OUT_EN)
        with self.regs.open():
            # mask the bit to clear it:
            reg_val = self.peek32(self.MB_CLOCK_CTRL) & mask
            if enable:
                # set the bit if desired:
                reg_val = reg_val | (0b1 << self.MB_CLOCK_CTRL_PPS_OUT_EN)
            self.log.trace("Writing MB_CLOCK_CTRL to 0x{:08X}".format(reg_val))
            self.poke32(self.MB_CLOCK_CTRL, reg_val)

    def reset_meas_clk_mmcm(self, reset=True):
        """
        Reset or unreset the MMCM for the measurement clock in the FPGA TDC.
        """
        self.log.trace("%s measurement clock MMCM reset...",
                       "Asserting" if reset else "Clearing")
        mask = 0xFFFFFFFF ^ (0b1 << self.MB_CLOCK_CTRL_MEAS_CLK_RESET)
        with self.regs.open():
            # mask the bit to clear it
            reg_val = self.peek32(self.MB_CLOCK_CTRL) & mask
            if reset:
                # set the bit if desired
                reg_val = reg_val | (0b1 << self.MB_CLOCK_CTRL_MEAS_CLK_RESET)
            self.log.trace("Writing MB_CLOCK_CTRL to 0x{:08X}".format(reg_val))
            self.poke32(self.MB_CLOCK_CTRL, reg_val)

    def get_meas_clock_mmcm_lock(self):
        """
        Check the status of the MMCM for the measurement clock in the FPGA TDC.
        """
        mask = 0b1 << self.MB_CLOCK_CTRL_MEAS_CLK_LOCKED
        with self.regs.open():
            reg_val = self.peek32(self.MB_CLOCK_CTRL)
        locked = (reg_val & mask) > 0
        if not locked:
            self.log.warning("Measurement clock MMCM reporting unlocked. "
                             "MB_CLOCK_CTRL reg: 0x{:08X}".format(reg_val))
        else:
            self.log.trace("Measurement clock MMCM locked!")
        return locked

    def get_fpga_type(self):
        """
        Reads the type of the FPGA image currently loaded
        Returns a string with the type (ie HG, XG, AA, etc.)
        """
        with self.regs.open():
            sfp0_info_rb = self.peek32(self.MB_SFP0_INFO)
            sfp1_info_rb = self.peek32(self.MB_SFP1_INFO)
        # Print the registers values as 32-bit hex values
        self.log.trace("SFP0 Info: 0x{0:0{1}X}".format(sfp0_info_rb, 8))
        self.log.trace("SFP1 Info: 0x{0:0{1}X}".format(sfp1_info_rb, 8))

        sfp0_type = N3XX_SFP_TYPES.get((sfp0_info_rb & 0x0000FF00) >> 8, "")
        sfp1_type = N3XX_SFP_TYPES.get((sfp1_info_rb & 0x0000FF00) >> 8, "")
        self.log.trace("SFP types: ({}, {})".format(sfp0_type, sfp1_type))
        if (sfp0_type == "") or (sfp1_type == ""):
            return ""
        elif (sfp0_type == "1G") and (sfp1_type == "10G"):
            return "HG"
        elif (sfp0_type == "10G") and (sfp1_type == "10G"):
            return "XG"
        elif (sfp0_type == "10G") and (sfp1_type == "A"):
            return "XA"
        elif (sfp0_type == "A") and (sfp1_type == "A"):
            return "AA"
        else:
            self.log.warning(
                "Unrecognized SFP type combination: ({}, {})".format(
                    sfp0_type, sfp1_type))
            return ""
Ejemplo n.º 16
0
 def __init__(self, label):
     self.log = get_logger(label)
     self._regs = UIO(label=label, read_only=False)
     self.poke32 = self._regs.poke32
     self.peek32 = self._regs.peek32
Ejemplo n.º 17
0
class RfdcRegsControl:
    """
    Control the FPGA RFDC registers external to the XRFdc API
    """
    # pylint: disable=bad-whitespace
    IQ_SWAP_OFFSET = 0x10000
    MMCM_RESET_BASE_OFFSET = 0x11000
    RF_RESET_CONTROL_OFFSET = 0x12000
    RF_RESET_STATUS_OFFSET = 0x12008
    RF_STATUS_OFFSET = 0x13000
    FABRIC_DSP_INFO_OFFSET = 0x13008
    CAL_DATA_OFFSET = 0x14000
    CAL_ENABLE_OFFSET = 0x14008
    THRESHOLD_STATUS_OFFSET = 0x15000
    RF_PLL_CONTROL_OFFSET = 0x16000
    RF_PLL_STATUS_OFFSET = 0x16008

    # pylint: enable=bad-whitespace

    def __init__(self, label, log):
        self.log = log.getChild("RfdcRegs")
        self.regs = UIO(label=label, read_only=False)
        self.poke32 = self.regs.poke32
        self.peek32 = self.regs.peek32

        # Index corresponds to dboard number.
        self._converter_chains_in_reset = True

    def get_threshold_status(self, slot_id, channel, threshold_idx):
        """
        Retrieves the status bit for the given threshold block
        """
        BITMASKS = {
            (0, 0, 0): 0x04,
            (0, 0, 1): 0x08,
            (0, 1, 0): 0x01,
            (0, 1, 1): 0x02,
            (1, 0, 0): 0x400,
            (1, 0, 1): 0x800,
            (1, 1, 0): 0x100,
            (1, 1, 1): 0x200,
        }
        assert (slot_id, channel, threshold_idx) in BITMASKS
        status = self.peek(self.THRESHOLD_STATUS_OFFSET)
        status_bool = (status
                       & BITMASKS[(slot_id, channel, threshold_idx)]) != 0
        return 1 if status_bool else 0

    def set_cal_data(self, i, q):
        assert 0 <= i < 2**16
        assert 0 <= q < 2**16
        self.poke(self.CAL_DATA_OFFSET, (q << 16) | i)

    def set_cal_enable(self, channel, enable):
        assert 0 <= channel <= 3
        assert enable in [False, True]
        en = self.peek(self.CAL_ENABLE_OFFSET)
        bit_offsets = {
            0: 0,
            1: 1,
            2: 4,
            3: 5,
        }
        en_mask = 1 << bit_offsets[channel]
        en = en & ~en_mask
        self.poke(self.CAL_ENABLE_OFFSET, en | (en_mask if enable else 0))

    def enable_iq_swap(self, enable, db_id, block_id, is_dac):
        iq_swap_bit = (int(is_dac) * 8) + (db_id * 4) + block_id

        # Write IQ swap bit with a mask
        reg_val = self.peek(self.IQ_SWAP_OFFSET)
        reg_val = (reg_val & ~(1 << iq_swap_bit)) \
                    | (enable << iq_swap_bit)
        self.poke(self.IQ_SWAP_OFFSET, reg_val)

    def set_reset_mmcm(self, reset=True):
        if reset:
            # Put the MMCM in reset (active low)
            self.poke(self.MMCM_RESET_BASE_OFFSET, 0)
        else:
            # Take the MMCM out of reset
            self.poke(self.MMCM_RESET_BASE_OFFSET, 1)

    def wait_for_mmcm_locked(self, timeout=0.001):
        """
        Wait for MMCM to come to a stable locked state.
        The datasheet specifies a 100us max lock time
        """
        DATA_CLK_PLL_LOCKED = 1 << 20

        POLL_SLEEP = 0.0002
        for _ in range(int(timeout / POLL_SLEEP)):
            time.sleep(POLL_SLEEP)
            status = self.peek(self.RF_PLL_STATUS_OFFSET)
            if status & DATA_CLK_PLL_LOCKED:
                self.log.trace("RF MMCM lock detected.")
                return
        self.log.error("MMCM failed to lock in the expected time.")
        raise RuntimeError("MMCM failed to lock within the expected time.")

    def set_gated_clock_enables(self, value=True):
        """
        Controls the clock enable for data_clk and
        data_clk_2x
        """
        ENABLE_DATA_CLK = 1
        ENABLE_DATA_CLK_2X = 1 << 4
        ENABLE_RF_CLK = 1 << 8
        ENABLE_RF_CLK_2X = 1 << 12
        if value:
            # Enable buffers gating the clocks
            self.poke(
                self.RF_PLL_CONTROL_OFFSET, ENABLE_DATA_CLK
                | ENABLE_DATA_CLK_2X | ENABLE_RF_CLK | ENABLE_RF_CLK_2X)
        else:
            # Disable clock buffers to have clocks gated.
            self.poke(self.RF_PLL_CONTROL_OFFSET, 0)

    def get_fabric_dsp_info(self, dboard):
        """
        Read the DSP information register and returns the
        DSP bandwidth, rx channel count and tx channel count
        """
        # Offsets
        DSP_BW = 0 + 16 * dboard
        DSP_RX_CNT = 12 + 16 * dboard
        DSP_TX_CNT = 14 + 16 * dboard
        # Masks
        DSP_BW_MSK = 0xFFF
        DSP_RX_CNT_MSK = 0x3
        DSP_TX_CNT_MSK = 0x3

        dsp_info = self.peek(self.FABRIC_DSP_INFO_OFFSET)
        self.log.trace("Fabric DSP for dboard %d...", dboard)
        dsp_bw = (dsp_info >> DSP_BW) & DSP_BW_MSK
        self.log.trace("  Bandwidth (MHz):  %d", dsp_bw)
        dsp_rx_cnt = (dsp_info >> DSP_RX_CNT) & DSP_RX_CNT_MSK
        self.log.trace("  Rx channel count: %d", dsp_rx_cnt)
        dsp_tx_cnt = (dsp_info >> DSP_TX_CNT) & DSP_TX_CNT_MSK
        self.log.trace("  Tx channel count: %d", dsp_tx_cnt)

        return [dsp_bw, dsp_rx_cnt, dsp_tx_cnt]

    def get_rfdc_resampling_factor(self, dboard):
        """
        Returns the appropriate decimation/interpolation factor to set in the RFDC.
        """
        # DSP vs. RFDC decimation/interpolation dictionary
        # Key: bandwidth in MHz
        # Value: (RFDC resampling factor, is Half-band resampling used?)
        RFDC_RESAMPLING_FACTOR = {
            100: (8, False),  # 100 MHz BW requires 8x RFDC resampling
            200: (2, True),  # 200 MHz BW requires 2x RFDC resampling
            # (400 MHz RFDC DSP used w/ half-band resampling)
            400: (2, False)  # 400 MHz BW requires 2x RFDC resampling
        }
        dsp_bw, _, _ = self.get_fabric_dsp_info(dboard)
        # When no RF fabric DSP is present (dsp_bw = 0), MPM should
        # simply use the default RFDC resampling factor (400 MHz).
        if dsp_bw in RFDC_RESAMPLING_FACTOR:
            rfdc_resampling_factor, halfband = RFDC_RESAMPLING_FACTOR[dsp_bw]
        else:
            rfdc_resampling_factor, halfband = RFDC_RESAMPLING_FACTOR[400]
            self.log.trace("  Using default resampling!")
        self.log.trace("  RFDC resampling:  %d", rfdc_resampling_factor)
        return (rfdc_resampling_factor, halfband)

    def set_reset_adc_dac_chains(self, reset=True):
        """ Resets or enables the ADC and DAC chain for the given dboard """
        def _wait_for_done(done_bit, timeout=5):
            """
            Wait for the specified sequence done bit when resetting or
            enabling an ADC or DAC chain. Throws an error on timeout.
            """
            status = self.peek(self.RF_RESET_STATUS_OFFSET)
            if (status & done_bit):
                return
            for _ in range(0, timeout):
                time.sleep(0.001)  # 1 ms
                status = self.peek(self.RF_RESET_STATUS_OFFSET)
                if (status & done_bit):
                    return
            self.log.error(
                "Timeout while resetting or enabling ADC/DAC chains.")
            raise RuntimeError(
                "Timeout while resetting or enabling ADC/DAC chains.")

        # CONTROL OFFSET
        ADC_RESET = 1 << 4
        DAC_RESET = 1 << 8
        # STATUS OFFSET
        ADC_SEQ_DONE = 1 << 7
        DAC_SEQ_DONE = 1 << 11

        if reset:
            if self._converter_chains_in_reset:
                self.log.debug('Converters are already in reset. '
                               'The reset bit will NOT be toggled.')
                return
            # Reset the ADC and DAC chains
            self.log.trace('Resetting ADC chain')
            self.poke(self.RF_RESET_CONTROL_OFFSET, ADC_RESET)
            _wait_for_done(ADC_SEQ_DONE)
            self.poke(self.RF_RESET_CONTROL_OFFSET, 0x0)

            self.log.trace('Resetting DAC chain')
            self.poke(self.RF_RESET_CONTROL_OFFSET, DAC_RESET)
            _wait_for_done(DAC_SEQ_DONE)
            self.poke(self.RF_RESET_CONTROL_OFFSET, 0x0)

            self._converter_chains_in_reset = True
        else:  # enable
            self._converter_chains_in_reset = False

    def log_status(self):
        status = self.peek(self.RF_STATUS_OFFSET)
        self.log.debug("Daughterboard 0")
        self.log.debug("  @RFDC")
        self.log.debug("    DAC(1:0) TREADY    : {:02b}".format((status >> 0)
                                                                & 0x3))
        self.log.debug("    DAC(1:0) TVALID    : {:02b}".format((status >> 2)
                                                                & 0x3))
        self.log.debug("    ADC(1:0) I TREADY  : {:02b}".format((status >> 6)
                                                                & 0x3))
        self.log.debug("    ADC(1:0) I TVALID  : {:02b}".format((status >> 10)
                                                                & 0x3))
        self.log.debug("    ADC(1:0) Q TREADY  : {:02b}".format((status >> 4)
                                                                & 0x3))
        self.log.debug("    ADC(1:0) Q TVALID  : {:02b}".format((status >> 8)
                                                                & 0x3))
        self.log.debug("  @USER")
        self.log.debug("    ADC(1:0) OUT TVALID: {:02b}".format((status >> 12)
                                                                & 0x3))
        self.log.debug("    ADC(1:0) OUT TREADY: {:02b}".format((status >> 14)
                                                                & 0x3))
        self.log.debug("Daughterboard 1")
        self.log.debug("  @RFDC")
        self.log.debug("    DAC(1:0) TREADY    : {:02b}".format((status >> 16)
                                                                & 0x3))
        self.log.debug("    DAC(1:0) TVALID    : {:02b}".format((status >> 18)
                                                                & 0x3))
        self.log.debug("    ADC(1:0) I TREADY  : {:02b}".format((status >> 22)
                                                                & 0x3))
        self.log.debug("    ADC(1:0) I TVALID  : {:02b}".format((status >> 26)
                                                                & 0x3))
        self.log.debug("    ADC(1:0) Q TREADY  : {:02b}".format((status >> 20)
                                                                & 0x3))
        self.log.debug("    ADC(1:0) Q TVALID  : {:02b}".format((status >> 24)
                                                                & 0x3))
        self.log.debug("  @USER")
        self.log.debug("    ADC(1:0) OUT TVALID: {:02b}".format((status >> 28)
                                                                & 0x3))
        self.log.debug("    ADC(1:0) OUT TREADY: {:02b}".format((status >> 30)
                                                                & 0x3))

    def poke(self, addr, val):
        with self.regs:
            self.regs.poke32(addr, val)

    def peek(self, addr):
        with self.regs:
            result = self.regs.peek32(addr)
            return result
Ejemplo n.º 18
0
class EthDispatcherTable(object):
    """
    Controls an Ethernet dispatcher table.
    """
    DEFAULT_VITA_PORT = (49153, 49154)
    # Address offsets:
    OWN_IP_OFFSET = 0x0000
    OWN_PORT_OFFSET = 0x0004
    FORWARD_ETH_BCAST_OFFSET = 0x0008
    SID_IP_OFFSET = 0x1000
    SID_PORT_MAC_HI_OFFSET = 0x1400
    SID_MAC_LO_OFFSET = 0x1800

    def __init__(self, label):
        self.log = get_logger(label)
        self._regs = UIO(label=label, read_only=False)
        self.poke32 = self._regs.poke32
        self.peek32 = self._regs.peek32

    def set_ipv4_addr(self, ip_addr):
        """
        Set the own IPv4 address for this Ethernet dispatcher.
        Outgoing packets will have this IP address.
        """
        self.log.debug("Setting my own IP address to `{}'".format(ip_addr))
        ip_addr_int = int(netaddr.IPAddress(ip_addr))
        with self._regs.open():
            self.poke32(self.OWN_IP_OFFSET, ip_addr_int)

    def set_vita_port(self, port_value=None, port_idx=None):
        """
        Set the port that is used for incoming VITA traffic. This is used to
        distinguish traffic that goes to the FPGA from that going to the ARM.
        """
        port_idx = port_idx or 0
        port_value = port_value or self.DEFAULT_VITA_PORT[port_idx]
        assert port_idx in (0)  #FIXME: Fix port_idx = 1
        port_reg_addr = self.OWN_PORT_OFFSET
        with self._regs.open():
            self.poke32(port_reg_addr, port_value)

    def set_route(self, sid, ip_addr, udp_port, mac_addr=None):
        """
        Sets up routing in the Ethernet dispatcher. From sid, only the
        destination part is important. After this call, any CHDR packet
        reaching this Ethernet dispatcher will get routed to `ip_addr' and
        `udp_port'.

        It automatically looks up the MAC address of the destination unless a
        MAC address is given.

        sid -- Full SID, but only destination part matters.
        ip_addr -- IPv4 destination address. String format ("1.2.3.4").
        udp_addr -- Destination UDP port.
        mac_addr -- If given, this will replace an ARP lookup for the MAC
                    address. String format, ("aa:bb:cc:dd:ee:ff"), case does
                    not matter.
        """
        udp_port = int(udp_port)
        if mac_addr is None:
            mac_addr = get_mac_addr(ip_addr)
        if mac_addr is None:
            self.log.error(
                "Could not resolve a MAC address for IP address `{}'".format(
                    ip_addr))
        dst_ep = sid.dst_ep
        self.log.debug(
            "Routing SID `{sid}' (endpoint `{ep}') to IP address `{ip}', " \
            "MAC address `{mac}', port `{port}'".format(
                sid=str(sid),
                ep=dst_ep,
                ip=ip_addr,
                mac=mac_addr,
                port=udp_port
            )
        )
        ip_addr_int = int(netaddr.IPAddress(ip_addr))
        mac_addr_int = int(netaddr.EUI(mac_addr))
        sid_offset = 4 * dst_ep

        def poke_and_trace(addr, data):
            " Do a poke32() and log.trace() "
            self.log.trace("Writing to address 0x{:04X}: 0x{:04X}".format(
                addr, data))
            self.poke32(addr, data)

        with self._regs.open():
            poke_and_trace(self.SID_IP_OFFSET + sid_offset, ip_addr_int)
            poke_and_trace(
                self.SID_MAC_LO_OFFSET + sid_offset,
                mac_addr_int & 0xFFFFFFFF,
            )
            poke_and_trace(self.SID_PORT_MAC_HI_OFFSET + sid_offset,
                           (udp_port << 16) | (mac_addr_int >> 32))

    def set_forward_policy(self, forward_eth, forward_bcast):
        """
        Forward Ethernet packet not matching OWN_IP to CROSSOVER
        Forward broadcast packet to CPU and CROSSOVER
        """
        reg_value = int(bool(forward_eth) << 1) | int(bool(forward_bcast))
        self.log.trace("Writing to address 0x{:04X}: 0x{:04X}".format(
            self.FORWARD_ETH_BCAST_OFFSET, reg_value))
        with self._regs.open():
            self.poke32(self.FORWARD_ETH_BCAST_OFFSET, reg_value)
Ejemplo n.º 19
0
class MboardRegsControl(object):
    """
    Control the FPGA Motherboard registers
    """
    # Motherboard registers
    M_COMPAT_NUM = 0x0000
    MB_DATESTAMP = 0x0004
    MB_GIT_HASH = 0x0008
    MB_SCRATCH = 0x000C
    MB_NUM_CE = 0x0010
    MB_NUM_IO_CE = 0x0014
    MB_CLOCK_CTRL = 0x0018
    MB_XADC_RB = 0x001C
    MB_BUS_CLK_RATE = 0x0020
    MB_BUS_COUNTER = 0x0024

    # Bitfield locations for the MB_CLOCK_CTRL register.
    MB_CLOCK_CTRL_PPS_SEL_INT_10 = 0  # pps_sel is one-hot encoded!
    MB_CLOCK_CTRL_PPS_SEL_INT_25 = 1
    MB_CLOCK_CTRL_PPS_SEL_EXT = 2
    MB_CLOCK_CTRL_PPS_SEL_GPSDO = 3
    MB_CLOCK_CTRL_PPS_OUT_EN = 4  # output enabled = 1
    MB_CLOCK_CTRL_MEAS_CLK_RESET = 12  # set to 1 to reset mmcm, default is 0
    MB_CLOCK_CTRL_MEAS_CLK_LOCKED = 13  # locked indication for meas_clk mmcm

    def __init__(self, label, log):
        self.log = log
        self.regs = UIO(label=label, read_only=False)
        self.poke32 = self.regs.poke32
        self.peek32 = self.regs.peek32

    def get_compat_number(self):
        """get FPGA compat number

        This function reads back FPGA compat number.
        The return is a tuple of
        2 numbers: (major compat number, minor compat number )
        """
        with self.regs.open():
            compat_number = self.peek32(self.M_COMPAT_NUM)
        minor = compat_number & 0xff
        major = (compat_number >> 16) & 0xff
        return (major, minor)

    def get_build_timestamp(self):
        """
        Returns the build date/time for the FPGA image.
        The return is datetime string with the  ISO 8601 format
        (YYYY-MM-DD HH:MM:SS.mmmmmm)
        """
        with self.regs.open():
            datestamp_rb = self.peek32(self.MB_DATESTAMP)
        if datestamp_rb > 0:
            dt_str = datetime.datetime(year=((datestamp_rb >> 17) & 0x3F) +
                                       2000,
                                       month=(datestamp_rb >> 23) & 0x0F,
                                       day=(datestamp_rb >> 27) & 0x1F,
                                       hour=(datestamp_rb >> 12) & 0x1F,
                                       minute=(datestamp_rb >> 6) & 0x3F,
                                       second=((datestamp_rb >> 0) & 0x3F))
            self.log.trace("FPGA build timestamp: {}".format(str(dt_str)))
            return str(dt_str)
        else:
            # Compatibility with FPGAs without datestamp capability
            return ''

    def get_git_hash(self):
        """
        Returns the GIT hash for the FPGA build.
        The return is a tuple of
        2 numbers: (short git hash, bool: is the tree dirty?)
        """
        with self.regs.open():
            git_hash_rb = self.peek32(self.MB_GIT_HASH)
        git_hash = git_hash_rb & 0x0FFFFFFF
        tree_dirty = ((git_hash_rb & 0xF0000000) > 0)
        dirtiness_qualifier = 'dirty' if tree_dirty else 'clean'
        self.log.trace("FPGA build GIT Hash: {:07x} ({})".format(
            git_hash, dirtiness_qualifier))
        return (git_hash, dirtiness_qualifier)

    def set_time_source(self, time_source, ref_clk_freq):
        """
        Set time source
        """
        pps_sel_val = 0x0
        if time_source == 'internal':
            assert ref_clk_freq in (10e6, 25e6)
            if ref_clk_freq == 10e6:
                self.log.trace("Setting time source to internal "
                               "(10 MHz reference)...")
                pps_sel_val = 0b1 << self.MB_CLOCK_CTRL_PPS_SEL_INT_10
            elif ref_clk_freq == 25e6:
                self.log.trace("Setting time source to internal "
                               "(25 MHz reference)...")
                pps_sel_val = 0b1 << self.MB_CLOCK_CTRL_PPS_SEL_INT_25
        elif time_source == 'external':
            self.log.trace("Setting time source to external...")
            pps_sel_val = 0b1 << self.MB_CLOCK_CTRL_PPS_SEL_EXT
        elif time_source == 'gpsdo':
            self.log.trace("Setting time source to gpsdo...")
            pps_sel_val = 0b1 << self.MB_CLOCK_CTRL_PPS_SEL_GPSDO
        else:
            assert False
        with self.regs.open():
            reg_val = self.peek32(self.MB_CLOCK_CTRL) & 0xFFFFFFF0
            reg_val = reg_val | (pps_sel_val & 0xF)
            self.log.trace("Writing MB_CLOCK_CTRL to 0x{:08X}".format(reg_val))
            self.poke32(self.MB_CLOCK_CTRL, reg_val)

    def enable_pps_out(self, enable):
        """
        Enables the PPS/Trig output on the back panel
        """
        self.log.trace("%s PPS/Trig output!",
                       "Enabling" if enable else "Disabling")
        mask = 0xFFFFFFFF ^ (0b1 << self.MB_CLOCK_CTRL_PPS_OUT_EN)
        with self.regs.open():
            # mask the bit to clear it:
            reg_val = self.peek32(self.MB_CLOCK_CTRL) & mask
            if enable:
                # set the bit if desired:
                reg_val = reg_val | (0b1 << self.MB_CLOCK_CTRL_PPS_OUT_EN)
            self.log.trace("Writing MB_CLOCK_CTRL to 0x{:08X}".format(reg_val))
            self.poke32(self.MB_CLOCK_CTRL, reg_val)

    def reset_meas_clk_mmcm(self, reset=True):
        """
        Reset or unreset the MMCM for the measurement clock in the FPGA TDC.
        """
        self.log.trace("%s measurement clock MMCM reset...",
                       "Asserting" if reset else "Clearing")
        mask = 0xFFFFFFFF ^ (0b1 << self.MB_CLOCK_CTRL_MEAS_CLK_RESET)
        with self.regs.open():
            # mask the bit to clear it
            reg_val = self.peek32(self.MB_CLOCK_CTRL) & mask
            if reset:
                # set the bit if desired
                reg_val = reg_val | (0b1 << self.MB_CLOCK_CTRL_MEAS_CLK_RESET)
            self.log.trace("Writing MB_CLOCK_CTRL to 0x{:08X}".format(reg_val))
            self.poke32(self.MB_CLOCK_CTRL, reg_val)

    def get_meas_clock_mmcm_lock(self):
        """
        Check the status of the MMCM for the measurement clock in the FPGA TDC.
        """
        mask = 0b1 << self.MB_CLOCK_CTRL_MEAS_CLK_LOCKED
        with self.regs.open():
            reg_val = self.peek32(self.MB_CLOCK_CTRL)
        locked = (reg_val & mask) > 0
        if not locked:
            self.log.warning("Measurement clock MMCM reporting unlocked. "
                             "MB_CLOCK_CTRL reg: 0x{:08X}".format(reg_val))
        else:
            self.log.trace("Measurement clock MMCM locked!")
        return locked
Ejemplo n.º 20
0
def main():
    " Dawaj, dawaj! "
    args = parse_args()
    # Initialize logger for downstream components
    mpm.get_main_logger().getChild('main')
    master_core_uio = UIO(label=args.uio_dev, read_only=False)
    master_core_uio.open()
    if args.slave_uio_dev:
        slave_core_uio = UIO(label=args.uio_dev, read_only=False)
        slave_core_uio.open()
    try:
        master_core = AuroraControl(master_core_uio, args.base_addr)
        slave_core = None if args.slave_uio_dev is None else AuroraControl(
            slave_core_uio,
            args.slave_base_addr,
        )
        if args.loopback:
            master_core.reset_core()
            master_core.set_loopback(enable=True)
            return True
        # Run BIST
        if args.test == 'ber':
            print("Performing BER BIST test.")
            master_core.run_ber_loopback_bist(
                args.duration,
                args.rate * 8e6,
                slave_core,
            )
        else:
            print("Performing Latency BIST test.")
            master_core.run_latency_loopback_bist(
                args.duration,
                args.rate * 8e6,
                slave_core,
            )
    except Exception as ex:
        print("Unexpected exception: {}".format(str(ex)))
        return False
    finally:
        master_core_uio.close()
        if args.slave_uio_dev:
            slave_core_uio.close()
Ejemplo n.º 21
0
class CtrlportRegs:
    """
    Control the FPGA Ctrlport registers
    """
    # pylint: disable=bad-whitespace
    IPASS_OFFSET = 0x000010
    MB_PL_SPI_CONFIG = 0x000020
    DB_SPI_CONFIG = 0x000024
    MB_PL_CPLD = 0x008000
    DB_0_CPLD = 0x010000
    DB_1_CPLD = 0x018000
    # pylint: enable=bad-whitespace

    min_mb_cpld_spi_divider = 2
    min_db_cpld_spi_divider = 5

    class MbPlCpldIface:
        """ Exposes access to register mapped MB PL CPLD register space """
        SIGNATURE_OFFSET = 0x0000
        REVISION_OFFSET = 0x0004

        SIGNATURE = 0x3FDC5C47
        MIN_REQ_REVISION = 0x20082009

        def __init__(self, regs_iface, offset, log):
            self.log = log
            self.offset = offset
            self.regs = regs_iface

        def peek32(self, addr):
            return self.regs.peek32(addr + self.offset)

        def poke32(self, addr, val):
            self.regs.poke32(addr + self.offset, val)

        def check_signature(self):
            read_signature = self.peek32(self.SIGNATURE_OFFSET)
            if self.SIGNATURE != read_signature:
                self.log.error('MB PL CPLD signature {:X} does not match '
                               'expected value {:X}'.format(
                                   read_signature, self.SIGNATURE))
                raise RuntimeError('MB PL CPLD signature {:X} does not match '
                                   'expected value {:X}'.format(
                                       read_signature, self.SIGNATURE))

        def check_revision(self):
            read_revision = self.peek32(self.REVISION_OFFSET)
            if read_revision < self.MIN_REQ_REVISION:
                error_message = (
                    'MB PL CPLD revision {:X} is out of date. '
                    'Expected value {:X}. Update your CPLD image.'.format(
                        read_revision, self.MIN_REQ_REVISION))
                self.log.error(error_message)
                raise RuntimeError(error_message)

    class DbCpldIface:
        """ Exposes access to register mapped DB CPLD register spaces """
        def __init__(self, regs_iface, offset):
            self.offset = offset
            self.regs = regs_iface

        def peek32(self, addr):
            return self.regs.peek32(addr + self.offset)

        def poke32(self, addr, val):
            self.regs.poke32(addr + self.offset, val)

    def __init__(self, label, log):
        self.log = log.getChild("CtrlportRegs")
        self._regs_uio_opened = False
        try:
            self.regs = UIO(label=label, read_only=False)
        except RuntimeError:
            self.log.warning('Ctrlport regs could not be found. ' \
                             'MPM Endpoint to the FPGA is not part of this image.')
            self.regs = None
        # Initialize SPI interface to MB PL CPLD and DB CPLDs
        self.set_mb_pl_cpld_divider(self.min_mb_cpld_spi_divider)
        self.set_db_divider_value(self.min_db_cpld_spi_divider)
        self.mb_pl_cpld_regs = self.MbPlCpldIface(self, self.MB_PL_CPLD,
                                                  self.log)
        self.mb_pl_cpld_regs.check_signature()
        self.mb_pl_cpld_regs.check_revision()
        self.db_0_regs = self.DbCpldIface(self, self.DB_0_CPLD)
        self.db_1_regs = self.DbCpldIface(self, self.DB_1_CPLD)

    def init(self):
        if not self._regs_uio_opened:
            self.regs._open()
            self._regs_uio_opened = True

    def deinit(self):
        if self._regs_uio_opened:
            self.regs._close()
            self._regs_uio_opened = False

    def peek32(self, addr):
        if self.regs is None:
            raise RuntimeError('The ctrlport registers were never configured!')
        if self._regs_uio_opened:
            return self.regs.peek32(addr)
        else:
            with self.regs:
                return self.regs.peek32(addr)

    def poke32(self, addr, val):
        if self.regs is None:
            raise RuntimeError('The ctrlport registers were never configured!')
        if self._regs_uio_opened:
            return self.regs.poke32(addr, val)
        else:
            with self.regs:
                return self.regs.poke32(addr, val)

    def set_mb_pl_cpld_divider(self, divider_value):
        if not self.min_mb_cpld_spi_divider <= divider_value <= 0xFFFF:
            self.log.error(
                'Cannot set MB CPLD SPI divider to invalid value {}'.format(
                    divider_value))
            raise RuntimeError(
                'Cannot set MB CPLD SPI divider to invalid value {}'.format(
                    divider_value))
        self.poke32(self.MB_PL_SPI_CONFIG, divider_value)

    def set_db_divider_value(self, divider_value):
        if not self.min_db_cpld_spi_divider <= divider_value <= 0xFFFF:
            self.log.error(
                'Cannot set DB SPI divider to invalid value {}'.format(
                    divider_value))
            raise RuntimeError(
                'Cannot set DB SPI divider to invalid value {}'.format(
                    divider_value))
        self.poke32(self.DB_SPI_CONFIG, divider_value)

    def get_db_cpld_iface(self, db_id):
        return self.db_0_regs if db_id == 0 else self.db_1_regs

    def get_mb_pl_cpld_iface(self):
        return self.mb_pl_cpld_regs

    def enable_cable_present_forwarding(self, enable=True):
        value = 1 if enable else 0
        self.poke32(self.IPASS_OFFSET, value)