Exemple #1
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
Exemple #2
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
Exemple #3
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
Exemple #4
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)
Exemple #5
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