Beispiel #1
0
    def init(self):
        """Initialize the servo, Sampler and both Urukuls.

        Leaves the servo disabled (see :meth:`set_config`), resets and
        configures all DDS.

        Urukul initialization is performed blindly as there is no readback from
        the DDS or the CPLDs.

        This method does not alter the profile configuration memory
        or the channel controls.
        """
        self.set_config(enable=0)
        delay(3*us)  # pipeline flush

        self.pgia.set_config_mu(
            sampler.SPI_CONFIG | spi.SPI_END,
            16, 4, sampler.SPI_CS_PGIA)

        self.cpld0.init(blind=True)
        cfg0 = self.cpld0.cfg_reg
        self.cpld0.cfg_write(cfg0 | (0xf << urukul.CFG_MASK_NU))
        self.dds0.init(blind=True)
        self.cpld0.cfg_write(cfg0)

        self.cpld1.init(blind=True)
        cfg1 = self.cpld1.cfg_reg
        self.cpld1.cfg_write(cfg1 | (0xf << urukul.CFG_MASK_NU))
        self.dds1.init(blind=True)
        self.cpld1.cfg_write(cfg1)
Beispiel #2
0
    def init(self):
        """Initialize and configure the DDS.

        Sets up SPI mode, confirms chip presence, powers down unused blocks,
        configures the PLL, waits for PLL lock. Uses the
        IO_UPDATE signal multiple times.
        """
        # Set SPI mode
        self.write32(_AD9910_REG_CFR1, 0x00000002)
        self.cpld.io_update.pulse(2*us)
        # Use the AUX DAC setting to identify and confirm presence
        aux_dac = self.read32(_AD9910_REG_AUX_DAC)
        if aux_dac & 0xff != 0x7f:
            raise ValueError("Urukul AD9910 AUX_DAC mismatch")
        delay(50*us)  # slack
        # Configure PLL settings and bring up PLL
        self.write32(_AD9910_REG_CFR2, 0x01400020)
        self.cpld.io_update.pulse(2*us)
        cfr3 = (0x0807c100 | (self.pll_vco << 24) |
                (self.pll_cp << 19) | (self.pll_n << 1))
        self.write32(_AD9910_REG_CFR3, cfr3 | 0x400)  # PFD reset
        self.cpld.io_update.pulse(100*us)
        self.write32(_AD9910_REG_CFR3, cfr3)
        self.cpld.io_update.pulse(100*us)
        # Wait for PLL lock, up to 100 ms
        for i in range(100):
            sta = self.cpld.sta_read()
            lock = urukul_sta_pll_lock(sta)
            delay(1*ms)
            if lock & (1 << self.chip_select - 4):
                return
        raise ValueError("PLL lock timeout")
Beispiel #3
0
    def burst_mu(self, data, dt_mu, ctrl=0):
        """Acquire a burst of samples.

        If the burst is too long and the sample rate too high, there will be
        RTIO input overflows.

        High sample rates lead to gain errors since the impedance between the
        instrumentation amplifier and the ADC is high.

        :param data: List of data values to write result packets into.
            In machine units.
        :param dt: Sample interval in machine units.
        :param ctrl: ADC control word to write during each result packet
            transfer.
        """
        self.bus.set_config_mu(SPI_CONFIG | spi.SPI_INPUT | spi.SPI_END,
                               24, self.div, SPI_CS_ADC)
        for i in range(len(data)):
            t0 = now_mu()
            self.cnv.pulse(40*ns)  # t_CNVH
            delay(560*ns)  # t_CONV max
            self.bus.write(ctrl << 24)
            at_mu(t0 + dt_mu)
        for i in range(len(data)):
            data[i] = self.bus.read()
Beispiel #4
0
    def init(self, blind=False):
        """Configures the SPI bus, drives LDAC and CLR high, programmes
        the offset DACs, and enables overtemperature shutdown.

        This method must be called before any other method at start-up or if
        the SPI bus has been accessed by another device.

        :param blind: If ``True``, do not attempt to read back control register
            or check for overtemperature.
        """
        self.ldac.on()
        self.clr.on()
        self.bus.set_config_mu(SPI_AD53XX_CONFIG, 24, self.div_write,
                               self.chip_select)
        self.write_offset_dacs_mu(self.offset_dacs)
        if not blind:
            ctrl = self.read_reg(channel=0, op=AD53XX_READ_CONTROL)
            if ctrl & 0b10000:
                raise ValueError("DAC over temperature")
            delay(25*us)
        self.bus.write(  # enable power and overtemperature shutdown
            (AD53XX_CMD_SPECIAL | AD53XX_SPECIAL_CONTROL | 0b0010) << 8)
        if not blind:
            ctrl = self.read_reg(channel=0, op=AD53XX_READ_CONTROL)
            if (ctrl & 0b10111) != 0b00010:
                raise ValueError("DAC CONTROL readback mismatch")
            delay(15*us)
Beispiel #5
0
    def init(self):
        """Initialize and detect Urukul.

        Resets the DDS and verifies correct CPLD gateware version.
        """
        cfg = self.cfg_reg
        self.cfg_reg = cfg | (1 << CFG_RST) | (1 << CFG_IO_RST)
        proto_rev = urukul_sta_proto_rev(self.sta_read())
        if proto_rev != STA_PROTO_REV_MATCH:
            raise ValueError("Urukul proto_rev mismatch")
        delay(20*us)  # slack, reset
        self.cfg_write(cfg)
        delay(1*ms)  # DDS wake up
Beispiel #6
0
    def get_att_mu(self):
        """Return the digital step attenuator settings in machine units.

        :return: 32 bit attenuator settings
        """
        self.bus.set_config_mu(SPI_CONFIG | spi.SPI_INPUT, 32,
                               SPIT_ATT_RD, CS_ATT)
        self.bus.write(0)  # shift in zeros, shift out current value
        self.bus.set_config_mu(SPI_CONFIG | spi.SPI_END, 32,
                               SPIT_ATT_WR, CS_ATT)
        delay(10*us)
        self.att_reg = self.bus.read()
        self.bus.write(self.att_reg)  # shift in current value again and latch
        return self.att_reg
Beispiel #7
0
    def sample_mu(self, next_ctrl=0):
        """Acquire a sample:

        Perform a conversion and transfer the sample.

        :param next_ctrl: ADC control word for the next sample
        :return: The ADC result packet (machine units)
        """
        self.cnv.pulse(40*ns)  # t_CNVH
        delay(560*ns)  # t_CONV max
        self.bus.set_config_mu(SPI_CONFIG | spi.SPI_INPUT | spi.SPI_END,
                               24, self.div, SPI_CS_ADC)
        self.bus.write(next_ctrl << 24)
        return self.bus.read()
Beispiel #8
0
 def set(self, data):
     """Sets the values of the latch outputs. This does not
     advance the timeline and the waveform is generated before
     `now`."""
     delay(-2*(self.n + 1)*self.dt)
     for i in range(self.n):
         if (data >> (self.n-i-1)) & 1 == 0:
             self.ser.off()
         else:
             self.ser.on()
         self.clk.off()
         delay(self.dt)
         self.clk.on()
         delay(self.dt)
     self.clk.off()
     self.latch.on()
     delay(self.dt)
     self.latch.off()
     delay(self.dt)
Beispiel #9
0
    def get_profile_mu(self, profile, data):
        """Retrieve profile data.

        Profile data is returned in the ``data`` argument in machine units
        packed as: ``[ftw >> 16, b1, pow, adc | (delay << 8), offset, a1,
        ftw & 0xffff, b0]``.

        .. seealso:: The individual fields are described in
            :meth:`set_iir_mu` and :meth:`set_dds_mu`.

        This method advances the timeline by 32 µs and consumes all slack.

        :param profile: Profile number (0-31)
        :param data: List of 8 integers to write the profile data into
        """
        base = (self.servo_channel << 8) | (profile << 3)
        for i in range(len(data)):
            data[i] = self.servo.read(base + i)
            delay(4*us)
Beispiel #10
0
    def read_reg(self, channel=0, op=AD53XX_READ_X1A):
        """Read a DAC register.

        This method advances the timeline by the duration of two SPI transfers
        plus two RTIO coarse cycles plus 270 ns and consumes all slack.

        :param channel: Channel number to read from (default: 0)
        :param op: Operation to perform, one of :const:`AD53XX_READ_X1A`,
          :const:`AD53XX_READ_X1B`, :const:`AD53XX_READ_OFFSET`,
          :const:`AD53XX_READ_GAIN` etc. (default: :const:`AD53XX_READ_X1A`).
        :return: The 16 bit register value
        """
        self.bus.write(ad53xx_cmd_read_ch(channel, op) << 8)
        self.bus.set_config_mu(SPI_AD53XX_CONFIG | spi.SPI_INPUT, 24,
                               self.div_read, self.chip_select)
        delay(270*ns)  # t_21 min sync high in readback
        self.bus.write((AD53XX_CMD_SPECIAL | AD53XX_SPECIAL_NOP) << 8)
        self.bus.set_config_mu(SPI_AD53XX_CONFIG, 24, self.div_write,
                               self.chip_select)
        # FIXME: the int32 should not be needed to resolve unification
        return self.bus.read() & int32(0xffff)
Beispiel #11
0
    def measure_io_update_alignment(self, delay_start, delay_stop):
        """Use the digital ramp generator to locate the alignment between
        IO_UPDATE and SYNC_CLK.

        The ramp generator is set up to a linear frequency ramp
        (dFTW/t_SYNC_CLK=1) and started at a coarse RTIO time stamp plus
        `delay_start` and stopped at a coarse RTIO time stamp plus
        `delay_stop`.

        :param delay_start: Start IO_UPDATE delay in machine units.
        :param delay_stop: Stop IO_UPDATE delay in machine units.
        :return: Odd/even SYNC_CLK cycle indicator.
        """
        # set up DRG
        self.set_cfr1(drg_load_lrr=1, drg_autoclear=1)
        # DRG -> FTW, DRG enable
        self.write32(_AD9910_REG_CFR2, 0x01090000)
        # no limits
        self.write64(_AD9910_REG_RAMP_LIMIT, -1, 0)
        # DRCTL=0, dt=1 t_SYNC_CLK
        self.write32(_AD9910_REG_RAMP_RATE, 0x00010000)
        # dFTW = 1, (work around negative slope)
        self.write64(_AD9910_REG_RAMP_STEP, -1, 0)
        # delay io_update after RTIO edge
        t = now_mu() + 8 & ~7
        at_mu(t + delay_start)
        # assumes a maximum t_SYNC_CLK period
        self.cpld.io_update.pulse_mu(16 - delay_start)  # realign
        # disable DRG autoclear and LRR on io_update
        self.set_cfr1()
        # stop DRG
        self.write64(_AD9910_REG_RAMP_STEP, 0, 0)
        at_mu(t + 0x1000 + delay_stop)
        self.cpld.io_update.pulse_mu(16 - delay_stop)  # realign
        ftw = self.read32(_AD9910_REG_FTW)  # read out effective FTW
        delay(100*us)  # slack
        # disable DRG
        self.write32(_AD9910_REG_CFR2, 0x01010000)
        self.cpld.io_update.pulse_mu(8)
        return ftw & 1
Beispiel #12
0
    def init(self):
        """Initialize and configure the DDS.

        Sets up SPI mode, confirms chip presence, powers down unused blocks,
        and configures the PLL. Does not wait for PLL lock. Uses the
        IO_UPDATE signal multiple times.
        """
        # SPI mode
        self.write(AD9912_SER_CONF, 0x99, length=1)
        self.cpld.io_update.pulse(2*us)
        # Verify chip ID and presence
        prodid = self.read(AD9912_PRODIDH, length=2)
        if (prodid != 0x1982) and (prodid != 0x1902):
            raise ValueError("Urukul AD9912 product id mismatch")
        delay(50*us)
        # HSTL power down, CMOS power down
        self.write(AD9912_PWRCNTRL1, 0x80, length=1)
        self.cpld.io_update.pulse(2*us)
        self.write(AD9912_N_DIV, self.pll_n//2 - 2, length=1)
        self.cpld.io_update.pulse(2*us)
        # I_cp = 375 µA, VCO high range
        self.write(AD9912_PLLCFG, 0b00000101, length=1)
        self.cpld.io_update.pulse(2*us)
Beispiel #13
0
    def sample_mu(self, data):
        """Acquire a set of samples.

        Perform a conversion and transfer the samples.

        This assumes that the input FIFO of the ADC SPI RTIO channel is deep
        enough to buffer the samples (half the length of `data` deep).
        If it is not, there will be RTIO input overflows.

        :param data: List of data samples to fill. Must have even length.
            Samples are always read from the last channel (channel 7) down.
            The `data` list will always be filled with the last item
            holding to the sample from channel 7.
        """
        self.cnv.pulse(30*ns)  # t_CNVH
        delay(450*ns)  # t_CONV
        mask = 1 << 15
        for i in range(len(data)//2):
            self.bus_adc.write(0)
        for i in range(len(data) - 1, -1, -2):
            val = self.bus_adc.read()
            data[i] = val >> 16
            val &= 0xffff
            data[i - 1] = -(val & mask) + (val & ~mask)
Beispiel #14
0
    def init(self, blind=False):
        """Initialize and detect Urukul.

        Resets the DDS I/O interface and verifies correct CPLD gateware
        version.
        Does not pulse the DDS MASTER_RESET as that confuses the AD9910.

        :param blind: Do not attempt to verify presence and compatibility.
        """
        cfg = self.cfg_reg
        # Don't pulse MASTER_RESET (m-labs/artiq#940)
        self.cfg_reg = cfg | (0 << CFG_RST) | (1 << CFG_IO_RST)
        if blind:
            self.cfg_write(self.cfg_reg)
        else:
            proto_rev = urukul_sta_proto_rev(self.sta_read())
            if proto_rev != STA_PROTO_REV_MATCH:
                raise ValueError("Urukul proto_rev mismatch")
        delay(100*us)  # reset, slack
        self.cfg_write(cfg)
        if self.sync_div:
            at_mu(now_mu() & ~0xf)  # align to RTIO/2
            self.set_sync_div(self.sync_div)  # 125 MHz/2 = 1 GHz/16
        delay(1*ms)  # DDS wake up
Beispiel #15
0
    def tune_sync_delay(self, search_seed=15):
        """Find a stable SYNC_IN delay.

        This method first locates a valid SYNC_IN delay at zero validation
        window size (setup/hold margin) by scanning around `search_seed`. It
        then looks for similar valid delays at successively larger validation
        window sizes until none can be found. It then decreases the validation
        window a bit to provide some slack and stability and returns the
        optimal values.

        This method and :meth:`tune_io_update_delay` can be run in any order.

        :param search_seed: Start value for valid SYNC_IN delay search.
            Defaults to 15 (half range).
        :return: Tuple of optimal delay and window size.
        """
        if not self.cpld.sync_div:
            raise ValueError("parent cpld does not drive SYNC")
        search_span = 31
        # FIXME https://github.com/sinara-hw/Urukul/issues/16
        # should both be 2-4 once kasli sync_in jitter is identified
        min_window = 0
        margin = 1  # 1*75ps setup and hold
        for window in range(16):
            next_seed = -1
            for in_delay in range(search_span - 2*window):
                # alternate search direction around search_seed
                if in_delay & 1:
                    in_delay = -in_delay
                in_delay = search_seed + (in_delay >> 1)
                if in_delay < 0 or in_delay > 31:
                    continue
                self.set_sync(in_delay, window)
                self.clear_smp_err()
                # integrate SMP_ERR statistics for a few hundred cycles
                delay(100*us)
                err = urukul_sta_smp_err(self.cpld.sta_read())
                delay(100*us)  # slack
                if not (err >> (self.chip_select - 4)) & 1:
                    next_seed = in_delay
                    break
            if next_seed >= 0:  # valid delay found, scan next window
                search_seed = next_seed
                continue
            elif window > min_window:
                # no valid delay found here, roll back and add margin
                window = max(min_window, window - 1 - margin)
                self.set_sync(search_seed, window)
                self.clear_smp_err()
                delay(100*us)  # slack
                return search_seed, window
            else:
                break
        raise ValueError("no valid window/delay")
Beispiel #16
0
    def smooth(self, start: TFloat, stop: TFloat, duration: TFloat,
               order: TInt32):
        """Initiate an interpolated value change.

        For zeroth order (step) interpolation, the step is at
        ``start + duration/2``.

        First order interpolation corresponds to a linear value ramp from
        ``start`` to ``stop`` over ``duration``.

        The third order interpolation is constrained to have zero first
        order derivative at both `start` and `stop`.

        For first order and third order interpolation (linear and cubic)
        the interpolator needs to be stopped explicitly at the stop time
        (e.g. by setting spline coefficient data or starting a new
        :meth:`smooth` interpolation).

        This method advances the timeline by ``duration``.

        :param start: Initial value of the change. In physical units.
        :param stop: Final value of the change. In physical units.
        :param duration: Duration of the interpolation. In physical units.
        :param order: Order of the interpolation. Only 0, 1,
            and 3 are valid: step, linear, cubic.
        """
        if order == 0:
            delay(duration/2.)
            self.set_coeff([stop])
            delay(duration/2.)
        elif order == 1:
            self.set_coeff([start, (stop - start)/duration])
            delay(duration)
        elif order == 3:
            v2 = 6.*(stop - start)/(duration*duration)
            self.set_coeff([start, 0., v2, -2.*v2/duration])
            delay(duration)
        else:
            raise ValueError("Invalid interpolation order. "
                             "Supported orders are: 0, 1, 3.")
Beispiel #17
0
    def run(self):
        self.core.reset()
        delay(1 * ms)

        freq = 1 * MHz
        cs = 1

        # run a couple of clock cycles with miso high to wake up the card
        self.spi_mmc.set_config(_SDCARD_SPI_CONFIG, 32, freq, 0)
        for i in range(10):
            self.spi_mmc.write(0xffffffff)
        self.spi_mmc.set_config(_SDCARD_SPI_CONFIG | spi.SPI_END, 32, freq, 0)
        self.spi_mmc.write(0xffffffff)
        delay(200 * us)

        self.spi_mmc.set_config(_SDCARD_SPI_CONFIG, 8, freq, cs)
        self.spi_mmc.write(0x40 << 24)  # CMD
        self.spi_mmc.set_config(_SDCARD_SPI_CONFIG, 32, freq, cs)
        self.spi_mmc.write(0x00000000)  # ARG
        self.spi_mmc.set_config(_SDCARD_SPI_CONFIG, 8, freq, cs)
        self.spi_mmc.write(0x95 << 24)  # CRC
        self.spi_mmc.set_config(_SDCARD_SPI_CONFIG | spi.SPI_INPUT, 8, freq,
                                cs)
        idle = False
        response = 0
        for i in range(8):
            self.spi_mmc.write(0xff << 24)  # NCR
            response = self.spi_mmc.read()
            delay(100 * us)
            if response == 0x01:
                idle = True
                break
        self.spi_mmc.set_config(_SDCARD_SPI_CONFIG | spi.SPI_END, 8, freq, cs)
        self.spi_mmc.write(0xff << 24)
        if not idle:
            print(response)
            raise ValueError("SD Card did not reply with IDLE")
Beispiel #18
0
    def dac_iotest(self, pattern) -> TInt32:
        """Performs a DAC IO test according to the datasheet.

        :param pattern: List of four int32 containing the pattern
        :return: Bit error mask (16 bits)
        """
        if len(pattern) != 4:
            raise ValueError("pattern length out of bounds")
        for addr in range(len(pattern)):
            self.dac_write(0x25 + addr, pattern[addr])
            # repeat the pattern twice
            self.dac_write(0x29 + addr, pattern[addr])
        delay(.1 * ms)
        for ch in range(2):
            channel = self.channel[ch]
            channel.set_duc_cfg(select=1)  # test
            # dac test data is i msb, q lsb
            data = pattern[2 * ch] | (pattern[2 * ch + 1] << 16)
            channel.set_dac_test(data)
            if channel.get_dac_data() != data:
                raise ValueError("DAC test data readback failed")
            delay(.1 * ms)
        cfg = self.dac_read(0x01)
        delay(.1 * ms)
        self.dac_write(0x01, cfg | 0x8000)  # iotest_ena
        self.dac_write(0x04, 0x0000)  # clear iotest_result
        delay(.2 * ms)  # let it rip
        # no need to go through the alarm register,
        # just read the error mask
        # self.clear_dac_alarms()
        alarms = self.get_dac_alarms()
        delay(.1 * ms)  # slack
        if alarms & 0x0080:  # alarm_from_iotest
            errors = self.dac_read(0x04)
            delay(.1 * ms)  # slack
        else:
            errors = 0
        self.dac_write(0x01, cfg)  # clear config
        self.dac_write(0x04, 0x0000)  # clear iotest_result
        return errors
Beispiel #19
0
    def init(self, debug=False):
        """Initialize the board.

        Verifies board and chip presence, resets components, performs
        communication and configuration tests and establishes initial
        conditions.
        """
        board_id = self.read8(PHASER_ADDR_BOARD_ID)
        if board_id != PHASER_BOARD_ID:
            raise ValueError("invalid board id")
        delay(.1 * ms)  # slack

        hw_rev = self.read8(PHASER_ADDR_HW_REV)
        delay(.1 * ms)  # slack
        is_baseband = hw_rev & PHASER_HW_REV_VARIANT

        gw_rev = self.read8(PHASER_ADDR_GW_REV)
        if debug:
            print("gw_rev:", gw_rev)
            self.core.break_realtime()
        delay(.1 * ms)  # slack

        # allow a few errors during startup and alignment since boot
        if self.get_crc_err() > 20:
            raise ValueError("large number of frame CRC errors")
        delay(.1 * ms)  # slack

        # determine the origin for frame-aligned timestamps
        self.measure_frame_timestamp()
        if self.frame_tstamp < 0:
            raise ValueError("frame timestamp measurement timed out")

        # reset
        self.set_cfg(dac_resetb=0,
                     dac_sleep=1,
                     dac_txena=0,
                     trf0_ps=1,
                     trf1_ps=1,
                     att0_rstn=0,
                     att1_rstn=0)
        self.set_leds(0x00)
        self.set_fan_mu(0)
        # bring dac out of reset, keep tx off
        self.set_cfg(clk_sel=self.clk_sel,
                     dac_txena=0,
                     trf0_ps=1,
                     trf1_ps=1,
                     att0_rstn=0,
                     att1_rstn=0)
        delay(.1 * ms)  # slack

        # crossing dac_clk (reference) edges with sync_dly
        # changes the optimal fifo_offset by 4
        self.set_sync_dly(self.sync_dly)

        # 4 wire SPI, sif4_enable
        self.dac_write(0x02, 0x0080)
        if self.dac_read(0x7f) != 0x5409:
            raise ValueError("DAC version readback invalid")
        delay(.1 * ms)
        if self.dac_read(0x00) != 0x049c:
            raise ValueError("DAC config0 reset readback invalid")
        delay(.1 * ms)

        t = self.get_dac_temperature()
        delay(.1 * ms)
        if t < 10 or t > 90:
            raise ValueError("DAC temperature out of bounds")

        for data in self.dac_mmap:
            self.dac_write(data >> 16, data)
            delay(40 * us)
        self.dac_sync()
        delay(40 * us)

        # pll_ndivsync_ena disable
        config18 = self.dac_read(0x18)
        delay(.1 * ms)
        self.dac_write(0x18, config18 & ~0x0800)

        patterns = [
            [0xf05a, 0x05af, 0x5af0, 0xaf05],  # test channel/iq/byte/nibble
            [0x7a7a, 0xb6b6, 0xeaea, 0x4545],  # datasheet pattern a
            [0x1a1a, 0x1616, 0xaaaa, 0xc6c6],  # datasheet pattern b
        ]
        # A data delay of 2*50 ps heuristically and reproducibly matches
        # FPGA+board+DAC skews. There is plenty of margin (>= 250 ps
        # either side) and no need to tune at runtime.
        # Parity provides another level of safety.
        for i in range(len(patterns)):
            delay(.5 * ms)
            errors = self.dac_iotest(patterns[i])
            if errors:
                raise ValueError("DAC iotest failure")

        delay(2 * ms)  # let it settle
        lvolt = self.dac_read(0x18) & 7
        delay(.1 * ms)
        if lvolt < 2 or lvolt > 5:
            raise ValueError("DAC PLL lock failed, check clocking")

        if self.tune_fifo_offset:
            fifo_offset = self.dac_tune_fifo_offset()
            if debug:
                print("fifo_offset:", fifo_offset)
                self.core.break_realtime()

        # self.dac_write(0x20, 0x0000)  # stop fifo sync
        # alarm = self.get_sta() & 1
        # delay(.1*ms)
        self.clear_dac_alarms()
        delay(2 * ms)  # let it run a bit
        alarms = self.get_dac_alarms()
        delay(.1 * ms)  # slack
        if alarms & ~0x0040:  # ignore PLL alarms (see DS)
            if debug:
                print("alarms:", alarms)
                self.core.break_realtime()
                # ignore alarms
            else:
                raise ValueError("DAC alarm")

        # avoid malformed output for: mixer_ena=1, nco_ena=0 after power up
        self.dac_write(self.dac_mmap[2] >> 16, self.dac_mmap[2] | (1 << 4))
        delay(40 * us)
        self.dac_sync()
        delay(100 * us)
        self.dac_write(self.dac_mmap[2] >> 16, self.dac_mmap[2])
        delay(40 * us)
        self.dac_sync()
        delay(100 * us)

        # power up trfs, release att reset
        self.set_cfg(clk_sel=self.clk_sel, dac_txena=0)

        for ch in range(2):
            channel = self.channel[ch]
            # test attenuator write and readback
            channel.set_att_mu(0x5a)
            if channel.get_att_mu() != 0x5a:
                raise ValueError("attenuator test failed")
            delay(.1 * ms)
            channel.set_att_mu(0x00)  # minimum attenuation

            # test oscillators and DUC
            for i in range(len(channel.oscillator)):
                oscillator = channel.oscillator[i]
                asf = 0
                if i == 0:
                    asf = 0x7fff
                # 6pi/4 phase
                oscillator.set_amplitude_phase_mu(asf=asf, pow=0xc000, clr=1)
                delay(1 * us)
            # 3pi/4
            channel.set_duc_phase_mu(0x6000)
            channel.set_duc_cfg(select=0, clr=1)
            self.duc_stb()
            delay(.1 * ms)  # settle link, pipeline and impulse response
            data = channel.get_dac_data()
            delay(1 * us)
            channel.oscillator[0].set_amplitude_phase_mu(asf=0,
                                                         pow=0xc000,
                                                         clr=1)
            delay(.1 * ms)
            sqrt2 = 0x5a81  # 0x7fff/sqrt(2)
            data_i = data & 0xffff
            data_q = (data >> 16) & 0xffff
            # allow ripple
            if (data_i < sqrt2 - 30 or data_i > sqrt2
                    or abs(data_i - data_q) > 2):
                raise ValueError("DUC+oscillator phase/amplitude test failed")

            if is_baseband:
                continue

            if channel.trf_read(0) & 0x7f != 0x68:
                raise ValueError("TRF identification failed")
            delay(.1 * ms)

            delay(.2 * ms)
            for data in channel.trf_mmap:
                channel.trf_write(data)
            channel.cal_trf_vco()

            delay(2 * ms)  # lock
            if not (self.get_sta() & (PHASER_STA_TRF0_LD << ch)):
                raise ValueError("TRF lock failure")
            delay(.1 * ms)
            if channel.trf_read(0) & 0x1000:
                raise ValueError("TRF R_SAT_ERR")
            delay(.1 * ms)
            channel.en_trf_out()

        # enable dac tx
        self.set_cfg(clk_sel=self.clk_sel)
Beispiel #20
0
    def run(self):
        self.core.reset()
        self.core.break_realtime()
        response = 0xff
        self.spi_mmc.set_config(_SDCARD_SPI_CONFIG, 500 * kHz, 500 * kHz)
        self.spi_mmc.set_xfer(0, 8, 0)

        for i in range(10):
            self.spi_mmc.write(0xffffffff)
            delay(-5 * us)

        delay(100 * us)

        self.spi_mmc.set_xfer(1, 8, 0)
        self.spi_mmc.write(0x40000000)
        delay(-5 * us)
        self.spi_mmc.write(0x00000000)
        delay(-5 * us)
        self.spi_mmc.write(0x00000000)
        delay(-5 * us)
        self.spi_mmc.write(0x00000000)
        delay(-5 * us)
        self.spi_mmc.write(0x00000000)
        delay(-5 * us)
        self.spi_mmc.write(0x95000000)
        delay(-5 * us)

        self.spi_mmc.set_xfer(1, 0, 24)
        self.spi_mmc.write(0xffffffff)
        response = self.spi_mmc.read_sync()

        sd_response = False
        for i in range(3):
            if ((response >> 8 * i) & 0x0000ff) == 0x01:
                sd_response = True
                break
        self.set_dataset("sd_response", sd_response)
Beispiel #21
0
 def io_rst(self):
     delay(1 * us)
     self.cfg_write(self.cfg_reg | (1 << CFG_IO_RST))
     delay(1 * us)
     self.cfg_write(self.cfg_reg & ~(1 << CFG_IO_RST))
     delay(1 * us)
Beispiel #22
0
    def init(self, blind=False):
        """Initialize and configure the DDS.

        Sets up SPI mode, confirms chip presence, powers down unused blocks,
        configures the PLL, waits for PLL lock. Uses the
        IO_UPDATE signal multiple times.

        :param blind: Do not read back DDS identity and do not wait for lock.
        """
        # Set SPI mode
        self.set_cfr1()
        self.cpld.io_update.pulse(1*us)
        delay(1*ms)
        if not blind:
            # Use the AUX DAC setting to identify and confirm presence
            aux_dac = self.read32(_AD9910_REG_AUX_DAC)
            if aux_dac & 0xff != 0x7f:
                raise ValueError("Urukul AD9910 AUX_DAC mismatch")
            delay(50*us)  # slack
        # Configure PLL settings and bring up PLL
        # enable amplitude scale from profiles
        # read effective FTW
        # sync timing validation disable (enabled later)
        self.write32(_AD9910_REG_CFR2, 0x01010020)
        self.cpld.io_update.pulse(1*us)
        cfr3 = (0x0807c000 | (self.pll_vco << 24) |
                (self.pll_cp << 19) | (self.pll_en << 8) |
                (self.pll_n << 1))
        self.write32(_AD9910_REG_CFR3, cfr3 | 0x400)  # PFD reset
        self.cpld.io_update.pulse(1*us)
        if self.pll_en:
            self.write32(_AD9910_REG_CFR3, cfr3)
            self.cpld.io_update.pulse(1*us)
            if blind:
                delay(100*ms)
            else:
                # Wait for PLL lock, up to 100 ms
                for i in range(100):
                    sta = self.cpld.sta_read()
                    lock = urukul_sta_pll_lock(sta)
                    delay(1*ms)
                    if lock & (1 << self.chip_select - 4):
                        break
                    if i >= 100 - 1:
                        raise ValueError("PLL lock timeout")
        delay(10*us)  # slack
        if self.sync_delay_seed >= 0:
            self.tune_sync_delay(self.sync_delay_seed)
        delay(1*ms)
Beispiel #23
0
 def pulse(self, frequency, duration):
     time.manager.event(("pulse", self.name, frequency, duration))
     delay(duration)
Beispiel #24
0
 def gate_both(self, duration):
     time.manager.event(("gate_both", self.name, duration))
     delay(duration)
Beispiel #25
0
 def gate_falling(self, duration):
     time.manager.event(("gate_falling", self.name, duration))
     delay(duration)
Beispiel #26
0
 def _update_register(self, ch):
     self.mirny_cpld.write_ext(
         ALMAZNY_REG_BASE + ch, 8,
         self._flip_mu_bits(self.att_mu[ch]) | (self.channel_sw[ch] << 6),
         ALMAZNY_SPIT_WR)
     delay(100 * us)
Beispiel #27
0
 def count_gate(self, duration):
     result = self.prng.randrange(0, 100)
     time.manager.event(("count_gate", self.name, duration, result))
     delay(duration)
     return result
Beispiel #28
0
 def wait_edge(self):
     duration = self.prng.randrange(0, 20) * units.ms
     time.manager.event(("wait_edge", self.name, duration))
     delay(duration)
Beispiel #29
0
 def wait_edge(self):
     duration = self.prng.randrange(0, 20)*units.ms
     time.manager.event(("wait_edge", self.name, duration))
     delay(duration)
Beispiel #30
0
 def pulse(self, frequency, duration):
     time.manager.event(("pulse", self.name, frequency, duration))
     delay(duration)
Beispiel #31
0
 def pulse(self, t):
     cfg = self.cpld.cfg_reg
     self.cpld.cfg_write(cfg | (1 << CFG_IO_UPDATE))
     delay(t)
     self.cpld.cfg_write(cfg)
Beispiel #32
0
 def count_gate(self, duration):
     result = self.prng.randrange(0, 100)
     time.manager.event(("count_gate", self.name, duration, result))
     delay(duration)
     return result
Beispiel #33
0
 def pulse(self, t):
     cfg = self.cpld.cfg_reg
     self.cpld.cfg_write(cfg | (1 << CFG_IO_UPDATE))
     delay(t)
     self.cpld.cfg_write(cfg)
Beispiel #34
0
    def init(self, blind: TBool = False):
        """Initialize and configure the DDS.

        Sets up SPI mode, confirms chip presence, powers down unused blocks,
        configures the PLL, waits for PLL lock. Uses the
        IO_UPDATE signal multiple times.

        :param blind: Do not read back DDS identity and do not wait for lock.
        """
        self.sync_data.init()
        if self.sync_data.sync_delay_seed >= 0 and not self.cpld.sync_div:
            raise ValueError("parent cpld does not drive SYNC")
        if self.sync_data.sync_delay_seed >= 0:
            if self.sysclk_per_mu != self.sysclk * self.core.ref_period:
                raise ValueError("incorrect clock ratio for synchronization")
        delay(50 * ms)  # slack

        # Set SPI mode
        self.set_cfr1()
        self.cpld.io_update.pulse(1 * us)
        delay(1 * ms)
        if not blind:
            # Use the AUX DAC setting to identify and confirm presence
            aux_dac = self.read32(_AD9910_REG_AUX_DAC)
            if aux_dac & 0xff != 0x7f:
                raise ValueError("Urukul AD9910 AUX_DAC mismatch")
            delay(50 * us)  # slack
        # Configure PLL settings and bring up PLL
        # enable amplitude scale from profiles
        # read effective FTW
        # sync timing validation disable (enabled later)
        self.set_cfr2(sync_validation_disable=1)
        self.cpld.io_update.pulse(1 * us)
        cfr3 = (0x0807c000 | (self.pll_vco << 24) |
                (self.pll_cp << 19) | (self.pll_en << 8) |
                (self.pll_n << 1))
        self.write32(_AD9910_REG_CFR3, cfr3 | 0x400)  # PFD reset
        self.cpld.io_update.pulse(1 * us)
        if self.pll_en:
            self.write32(_AD9910_REG_CFR3, cfr3)
            self.cpld.io_update.pulse(1 * us)
            if blind:
                delay(100 * ms)
            else:
                # Wait for PLL lock, up to 100 ms
                for i in range(100):
                    sta = self.cpld.sta_read()
                    lock = urukul_sta_pll_lock(sta)
                    delay(1 * ms)
                    if lock & (1 << self.chip_select - 4):
                        break
                    if i >= 100 - 1:
                        raise ValueError("PLL lock timeout")
        delay(10 * us)  # slack
        if self.sync_data.sync_delay_seed >= 0 and not blind:
            self.tune_sync_delay(self.sync_data.sync_delay_seed)
        delay(1 * ms)
Beispiel #35
0
    def run(self):
        self.core.reset()
        self.core.break_realtime()
        response = 0xff
        self.spi_mmc.set_config(_SDCARD_SPI_CONFIG, 500*kHz, 500*kHz)
        self.spi_mmc.set_xfer(0, 8, 0)

        for i in range(10):
            self.spi_mmc.write(0xffffffff)
            delay(-5*us)

        delay(100*us)

        self.spi_mmc.set_xfer(1, 8, 0)
        self.spi_mmc.write(0x40000000)
        delay(-5*us)
        self.spi_mmc.write(0x00000000)
        delay(-5*us)
        self.spi_mmc.write(0x00000000)
        delay(-5*us)
        self.spi_mmc.write(0x00000000)
        delay(-5*us)
        self.spi_mmc.write(0x00000000)
        delay(-5*us)
        self.spi_mmc.write(0x95000000)
        delay(-5*us)

        self.spi_mmc.set_xfer(1, 0, 24)
        self.spi_mmc.write(0xffffffff)
        response = self.spi_mmc.read_sync()

        sd_response = False
        for i in range(3):
            if ((response >> 8*i) & 0x0000ff) == 0x01:
                sd_response = True
                break
        self.set_dataset("sd_response", sd_response)