forked from M-Labs/artiq
Compare commits
11 Commits
Author | SHA1 | Date | |
---|---|---|---|
63db4af1fc | |||
626c709b4e | |||
8ff433596b | |||
dc21f0b6dd | |||
087eb514c1 | |||
|
7534a8fe04 | ||
7669cfce3d | |||
99fe642cab | |||
d8184cfb56 | |||
5b52f187d0 | |||
9c99d116bb |
@ -25,7 +25,7 @@ Core technologies employed include `Python <https://www.python.org/>`_, `Migen <
|
|||||||
License
|
License
|
||||||
=======
|
=======
|
||||||
|
|
||||||
Copyright (C) 2014-2024 M-Labs Limited.
|
Copyright (C) 2014-2025 M-Labs Limited.
|
||||||
|
|
||||||
ARTIQ is free software: you can redistribute it and/or modify
|
ARTIQ is free software: you can redistribute it and/or modify
|
||||||
it under the terms of the GNU Lesser General Public License as published by
|
it under the terms of the GNU Lesser General Public License as published by
|
||||||
|
@ -5,6 +5,8 @@ RTIO Driver for the Analog Devices AD9834 DDS via 3-wire SPI interface.
|
|||||||
# https://www.analog.com/media/en/technical-documentation/data-sheets/AD9834.pdf
|
# https://www.analog.com/media/en/technical-documentation/data-sheets/AD9834.pdf
|
||||||
# https://www.analog.com/media/en/technical-documentation/app-notes/an-1070.pdf
|
# https://www.analog.com/media/en/technical-documentation/app-notes/an-1070.pdf
|
||||||
|
|
||||||
|
from numpy import int32
|
||||||
|
|
||||||
from artiq.coredevice import spi2 as spi
|
from artiq.coredevice import spi2 as spi
|
||||||
from artiq.experiment import *
|
from artiq.experiment import *
|
||||||
from artiq.language.core import *
|
from artiq.language.core import *
|
||||||
@ -64,167 +66,21 @@ class AD9834:
|
|||||||
self.ctrl_reg = 0x0000 # Reset control register
|
self.ctrl_reg = 0x0000 # Reset control register
|
||||||
|
|
||||||
@kernel
|
@kernel
|
||||||
def init(self):
|
def write(self, data: TInt32):
|
||||||
"""
|
"""
|
||||||
Initialize the AD9834: configure the SPI bus and reset the DDS.
|
Write a 16-bit word to the AD9834.
|
||||||
|
|
||||||
This method performs the necessary setup for the AD9834 device, including:
|
This method sends a 16-bit data word to the AD9834 via the SPI bus. The input
|
||||||
- Configuring the SPI bus parameters (clock polarity, data width, and frequency).
|
data is left-shifted by 16 bits to ensure proper alignment for the SPI controller,
|
||||||
- Putting the AD9834 into a reset state to ensure proper initialization.
|
allowing for accurate processing of the command by the AD9834.
|
||||||
|
|
||||||
The SPI bus is configured to use 16 bits of data width with the clock frequency
|
This method is used internally by other methods to update the control registers
|
||||||
provided as a parameter when creating the AD9834 instance. After configuring
|
and frequency settings of the AD9834. It should not be called directly unless
|
||||||
the SPI bus, the method invokes :meth:`enable_reset()` to reset the AD9834.
|
low-level register manipulation is required.
|
||||||
This is an essential step to prepare the device for subsequent configuration
|
|
||||||
of frequency and phase.
|
|
||||||
|
|
||||||
This method should be called before any other operations are performed
|
:param data: The 16-bit word to be sent to the AD9834.
|
||||||
on the AD9834 to ensure that the device is in a known state.
|
|
||||||
"""
|
"""
|
||||||
self.bus.set_config(spi.SPI_CLK_POLARITY | spi.SPI_END, 16, self.spi_freq, 1)
|
self.bus.write(data << 16)
|
||||||
self.enable_reset()
|
|
||||||
|
|
||||||
@kernel
|
|
||||||
def set_frequency_reg(self, freq_reg, frequency: TFloat):
|
|
||||||
"""
|
|
||||||
Set the frequency for the specified frequency register.
|
|
||||||
|
|
||||||
This method calculates the frequency word based on the provided frequency in Hz
|
|
||||||
and writes it to the specified frequency register.
|
|
||||||
|
|
||||||
:param freq_reg: The frequency register to write to, must be one of
|
|
||||||
:const:`AD9834_FREQ_REG_0` or :const:`AD9834_FREQ_REG_1`.
|
|
||||||
:param frequency: The desired frequency in Hz, which will be converted to a
|
|
||||||
frequency word suitable for the AD9834.
|
|
||||||
|
|
||||||
The frequency word is calculated using the formula:
|
|
||||||
|
|
||||||
``freq_word = (frequency * (1 << 28)) / clk_freq``
|
|
||||||
|
|
||||||
The result is limited to the lower 28 bits for compatibility with the AD9834.
|
|
||||||
|
|
||||||
The method first sets the control register to enable the appropriate settings,
|
|
||||||
then sends the fourteen least significant bits LSBs and fourteen most significant
|
|
||||||
bits MSBs in two consecutive writes to the specified frequency register.
|
|
||||||
"""
|
|
||||||
if freq_reg not in FREQ_REGS:
|
|
||||||
raise ValueError("Invalid frequency register")
|
|
||||||
assert frequency <= 37.5 * MHz, "Frequency exceeds maximum value of 37.5 MHz"
|
|
||||||
freq_word = int((frequency * (1 << 28)) / self.clk_freq) & 0x0FFFFFFF
|
|
||||||
self.ctrl_reg |= AD9834_B28
|
|
||||||
self.write(self.ctrl_reg)
|
|
||||||
lsb = freq_word & 0x3FFF
|
|
||||||
msb = (freq_word >> 14) & 0x3FFF
|
|
||||||
self.write(freq_reg | lsb)
|
|
||||||
self.write(freq_reg | msb)
|
|
||||||
|
|
||||||
@kernel
|
|
||||||
def set_frequency_reg_msb(self, freq_reg, word: TInt32):
|
|
||||||
"""
|
|
||||||
Set the fourteen most significant bits MSBs of the specified frequency register.
|
|
||||||
|
|
||||||
This method updates the specified frequency register with the provided MSB value.
|
|
||||||
It configures the control register to indicate that the MSB is being set.
|
|
||||||
|
|
||||||
:param freq_reg: The frequency register to update, must be one of
|
|
||||||
:const:`AD9834_FREQ_REG_0` or :const:`AD9834_FREQ_REG_1`.
|
|
||||||
:param word: The value to be written to the fourteen MSBs of the frequency register.
|
|
||||||
|
|
||||||
The method first clears the appropriate control bits, sets :const:`AD9834_HLB` to
|
|
||||||
indicate that the MSB is being sent, and then writes the updated control register
|
|
||||||
followed by the MSB value to the specified frequency register.
|
|
||||||
"""
|
|
||||||
if freq_reg not in FREQ_REGS:
|
|
||||||
raise ValueError("Invalid frequency register")
|
|
||||||
self.ctrl_reg &= ~AD9834_B28
|
|
||||||
self.ctrl_reg |= AD9834_HLB
|
|
||||||
self.write(self.ctrl_reg)
|
|
||||||
self.write(freq_reg | (word & 0x3FFF))
|
|
||||||
|
|
||||||
@kernel
|
|
||||||
def set_frequency_reg_lsb(self, freq_reg, word: TInt32):
|
|
||||||
"""
|
|
||||||
Set the fourteen least significant bits LSBs of the specified frequency register.
|
|
||||||
|
|
||||||
This method updates the specified frequency register with the provided LSB value.
|
|
||||||
It configures the control register to indicate that the LSB is being set.
|
|
||||||
|
|
||||||
:param freq_reg: The frequency register to update, must be one of
|
|
||||||
:const:`AD9834_FREQ_REG_0` or :const:`AD9834_FREQ_REG_1`.
|
|
||||||
:param word: The value to be written to the fourteen LSBs of the frequency register.
|
|
||||||
|
|
||||||
The method first clears the appropriate control bits and writes the updated control
|
|
||||||
register followed by the LSB value to the specified frequency register.
|
|
||||||
"""
|
|
||||||
if freq_reg not in FREQ_REGS:
|
|
||||||
raise ValueError("Invalid frequency register")
|
|
||||||
self.ctrl_reg &= ~AD9834_B28
|
|
||||||
self.ctrl_reg &= ~AD9834_HLB
|
|
||||||
self.write(self.ctrl_reg)
|
|
||||||
self.write(freq_reg | (word & 0x3FFF))
|
|
||||||
|
|
||||||
@kernel
|
|
||||||
def select_frequency_reg(self, freq_reg):
|
|
||||||
"""
|
|
||||||
Select the active frequency register for the phase accumulator.
|
|
||||||
|
|
||||||
This method chooses between the two available frequency registers in the AD9834 to
|
|
||||||
control the frequency of the output waveform. The control register is updated
|
|
||||||
to reflect the selected frequency register.
|
|
||||||
|
|
||||||
:param freq_reg: The frequency register to select. Must be one of
|
|
||||||
:const:`AD9834_FREQ_REG_0` or :const:`AD9834_FREQ_REG_1`.
|
|
||||||
"""
|
|
||||||
if freq_reg not in FREQ_REGS:
|
|
||||||
raise ValueError("Invalid frequency register")
|
|
||||||
if freq_reg == FREQ_REGS[0]:
|
|
||||||
self.ctrl_reg &= ~AD9834_FSEL
|
|
||||||
else:
|
|
||||||
self.ctrl_reg |= AD9834_FSEL
|
|
||||||
|
|
||||||
self.ctrl_reg &= ~AD9834_PIN_SW
|
|
||||||
self.write(self.ctrl_reg)
|
|
||||||
|
|
||||||
@kernel
|
|
||||||
def set_phase_reg(self, phase_reg, phase: TInt32):
|
|
||||||
"""
|
|
||||||
Set the phase for the specified phase register.
|
|
||||||
|
|
||||||
This method updates the specified phase register with the provided phase value.
|
|
||||||
|
|
||||||
:param phase_reg: The phase register to update, must be one of
|
|
||||||
:const:`AD9834_PHASE_REG_0` or :const:`AD9834_PHASE_REG_1`.
|
|
||||||
:param phase: The value to be written to the phase register.
|
|
||||||
|
|
||||||
The method masks the phase value to ensure it fits within the 12-bit limit
|
|
||||||
and writes it to the specified phase register.
|
|
||||||
"""
|
|
||||||
if phase_reg not in PHASE_REGS:
|
|
||||||
raise ValueError("Invalid phase register")
|
|
||||||
phase_word = phase & 0x0FFF
|
|
||||||
self.write(phase_reg | phase_word)
|
|
||||||
|
|
||||||
@kernel
|
|
||||||
def select_phase_reg(self, phase_reg):
|
|
||||||
"""
|
|
||||||
Select the active phase register for the phase accumulator.
|
|
||||||
|
|
||||||
This method chooses between the two available phase registers in the AD9834 to
|
|
||||||
control the phase of the output waveform. The control register is updated
|
|
||||||
to reflect the selected phase register.
|
|
||||||
|
|
||||||
:param phase_reg: The phase register to select. Must be one of
|
|
||||||
:const:`AD9834_PHASE_REG_0` or :const:`AD9834_PHASE_REG_1`.
|
|
||||||
"""
|
|
||||||
if phase_reg not in PHASE_REGS:
|
|
||||||
raise ValueError("Invalid phase register")
|
|
||||||
if phase_reg == PHASE_REGS[0]:
|
|
||||||
self.ctrl_reg &= ~AD9834_PSEL
|
|
||||||
else:
|
|
||||||
self.ctrl_reg |= AD9834_PSEL
|
|
||||||
|
|
||||||
self.ctrl_reg &= ~AD9834_PIN_SW
|
|
||||||
self.write(self.ctrl_reg)
|
|
||||||
|
|
||||||
@kernel
|
@kernel
|
||||||
def enable_reset(self):
|
def enable_reset(self):
|
||||||
@ -255,6 +111,158 @@ class AD9834:
|
|||||||
self.ctrl_reg &= ~AD9834_RESET
|
self.ctrl_reg &= ~AD9834_RESET
|
||||||
self.write(self.ctrl_reg)
|
self.write(self.ctrl_reg)
|
||||||
|
|
||||||
|
@kernel
|
||||||
|
def init(self):
|
||||||
|
"""
|
||||||
|
Initialize the AD9834: configure the SPI bus and reset the DDS.
|
||||||
|
|
||||||
|
This method performs the necessary setup for the AD9834 device, including:
|
||||||
|
- Configuring the SPI bus parameters (clock polarity, data width, and frequency).
|
||||||
|
- Putting the AD9834 into a reset state to ensure proper initialization.
|
||||||
|
|
||||||
|
The SPI bus is configured to use 16 bits of data width with the clock frequency
|
||||||
|
provided as a parameter when creating the AD9834 instance. After configuring
|
||||||
|
the SPI bus, the method invokes :meth:`enable_reset()` to reset the AD9834.
|
||||||
|
This is an essential step to prepare the device for subsequent configuration
|
||||||
|
of frequency and phase.
|
||||||
|
|
||||||
|
This method should be called before any other operations are performed
|
||||||
|
on the AD9834 to ensure that the device is in a known state.
|
||||||
|
"""
|
||||||
|
self.bus.set_config(spi.SPI_CLK_POLARITY | spi.SPI_END, 16, self.spi_freq, 1)
|
||||||
|
self.enable_reset()
|
||||||
|
|
||||||
|
@kernel
|
||||||
|
def set_frequency_reg_msb(self, freq_reg: TInt32, word: TInt32):
|
||||||
|
"""
|
||||||
|
Set the fourteen most significant bits MSBs of the specified frequency register.
|
||||||
|
|
||||||
|
This method updates the specified frequency register with the provided MSB value.
|
||||||
|
It configures the control register to indicate that the MSB is being set.
|
||||||
|
|
||||||
|
:param freq_reg: The frequency register to write to (0-1).
|
||||||
|
:param word: The value to be written to the fourteen MSBs of the frequency register.
|
||||||
|
|
||||||
|
The method first clears the appropriate control bits, sets :const:`AD9834_HLB` to
|
||||||
|
indicate that the MSB is being sent, and then writes the updated control register
|
||||||
|
followed by the MSB value to the specified frequency register.
|
||||||
|
"""
|
||||||
|
assert 0 <= freq_reg <= 1, "Invalid frequency register index"
|
||||||
|
self.ctrl_reg &= ~AD9834_B28
|
||||||
|
self.ctrl_reg |= AD9834_HLB
|
||||||
|
self.write(self.ctrl_reg)
|
||||||
|
self.write(FREQ_REGS[freq_reg] | (word & 0x3FFF))
|
||||||
|
|
||||||
|
@kernel
|
||||||
|
def set_frequency_reg_lsb(self, freq_reg: TInt32, word: TInt32):
|
||||||
|
"""
|
||||||
|
Set the fourteen least significant bits LSBs of the specified frequency register.
|
||||||
|
|
||||||
|
This method updates the specified frequency register with the provided LSB value.
|
||||||
|
It configures the control register to indicate that the LSB is being set.
|
||||||
|
|
||||||
|
:param freq_reg: The frequency register to write to (0-1).
|
||||||
|
:param word: The value to be written to the fourteen LSBs of the frequency register.
|
||||||
|
|
||||||
|
The method first clears the appropriate control bits and writes the updated control
|
||||||
|
register followed by the LSB value to the specified frequency register.
|
||||||
|
"""
|
||||||
|
assert 0 <= freq_reg <= 1, "Invalid frequency register index"
|
||||||
|
self.ctrl_reg &= ~AD9834_B28
|
||||||
|
self.ctrl_reg &= ~AD9834_HLB
|
||||||
|
self.write(self.ctrl_reg)
|
||||||
|
self.write(FREQ_REGS[freq_reg] | (word & 0x3FFF))
|
||||||
|
|
||||||
|
@kernel
|
||||||
|
def set_frequency_reg(self, freq_reg: TInt32, freq_word: TInt32):
|
||||||
|
"""
|
||||||
|
Set the frequency for the specified frequency register using a precomputed frequency word.
|
||||||
|
|
||||||
|
This writes to the 28-bit frequency register in one transfer.
|
||||||
|
|
||||||
|
:param freq_reg: The frequency register to write to (0-1).
|
||||||
|
:param freq_word: The precomputed frequency word.
|
||||||
|
"""
|
||||||
|
assert 0 <= freq_reg <= 1, "Invalid frequency register index"
|
||||||
|
self.ctrl_reg |= AD9834_B28
|
||||||
|
self.write(self.ctrl_reg)
|
||||||
|
lsb = freq_word & 0x3FFF
|
||||||
|
msb = (freq_word >> 14) & 0x3FFF
|
||||||
|
self.write(FREQ_REGS[freq_reg] | lsb)
|
||||||
|
self.write(FREQ_REGS[freq_reg] | msb)
|
||||||
|
|
||||||
|
@portable(flags={"fast-math"})
|
||||||
|
def frequency_to_ftw(self, frequency: TFloat) -> TInt32:
|
||||||
|
"""Return the 28-bit frequency tuning word corresponding to the given
|
||||||
|
frequency.
|
||||||
|
"""
|
||||||
|
assert frequency <= 37.5 * MHz, "Frequency exceeds maximum value of 37.5 MHz"
|
||||||
|
return int((frequency * (1 << 28)) / self.clk_freq) & 0x0FFFFFFF
|
||||||
|
|
||||||
|
@portable(flags={"fast-math"})
|
||||||
|
def turns_to_pow(self, turns: TFloat) -> TInt32:
|
||||||
|
"""Return the 12-bit phase offset word corresponding to the given phase
|
||||||
|
in turns."""
|
||||||
|
assert 0.0 <= turns <= 1.0, "Turns exceeds range 0.0 - 1.0"
|
||||||
|
return int32(round(turns * 0x1000)) & int32(0x0FFF)
|
||||||
|
|
||||||
|
@kernel
|
||||||
|
def select_frequency_reg(self, freq_reg: TInt32):
|
||||||
|
"""
|
||||||
|
Select the active frequency register for the phase accumulator.
|
||||||
|
|
||||||
|
This method chooses between the two available frequency registers in the AD9834 to
|
||||||
|
control the frequency of the output waveform. The control register is updated
|
||||||
|
to reflect the selected frequency register.
|
||||||
|
|
||||||
|
:param freq_reg: The frequency register to write to (0-1).
|
||||||
|
"""
|
||||||
|
assert 0 <= freq_reg <= 1, "Invalid frequency register index"
|
||||||
|
if freq_reg:
|
||||||
|
self.ctrl_reg |= AD9834_FSEL
|
||||||
|
else:
|
||||||
|
self.ctrl_reg &= ~AD9834_FSEL
|
||||||
|
|
||||||
|
self.ctrl_reg &= ~AD9834_PIN_SW
|
||||||
|
self.write(self.ctrl_reg)
|
||||||
|
|
||||||
|
@kernel
|
||||||
|
def set_phase_reg(self, phase_reg: TInt32, phase: TInt32):
|
||||||
|
"""
|
||||||
|
Set the phase for the specified phase register.
|
||||||
|
|
||||||
|
This method updates the specified phase register with the provided phase value.
|
||||||
|
|
||||||
|
:param phase_reg: The phase register to write to (0-1).
|
||||||
|
:param phase: The value to be written to the phase register.
|
||||||
|
|
||||||
|
The method masks the phase value to ensure it fits within the 12-bit limit
|
||||||
|
and writes it to the specified phase register.
|
||||||
|
"""
|
||||||
|
assert 0 <= phase_reg <= 1, "Invalid phase register index"
|
||||||
|
phase_word = phase & 0x0FFF
|
||||||
|
self.write(PHASE_REGS[phase_reg] | phase_word)
|
||||||
|
|
||||||
|
@kernel
|
||||||
|
def select_phase_reg(self, phase_reg: TInt32):
|
||||||
|
"""
|
||||||
|
Select the active phase register for the phase accumulator.
|
||||||
|
|
||||||
|
This method chooses between the two available phase registers in the AD9834 to
|
||||||
|
control the phase of the output waveform. The control register is updated
|
||||||
|
to reflect the selected phase register.
|
||||||
|
|
||||||
|
:param phase_reg: The phase register to write to (0-1).
|
||||||
|
"""
|
||||||
|
assert 0 <= phase_reg <= 1, "Invalid phase register index"
|
||||||
|
if phase_reg:
|
||||||
|
self.ctrl_reg |= AD9834_PSEL
|
||||||
|
else:
|
||||||
|
self.ctrl_reg &= ~AD9834_PSEL
|
||||||
|
|
||||||
|
self.ctrl_reg &= ~AD9834_PIN_SW
|
||||||
|
self.write(self.ctrl_reg)
|
||||||
|
|
||||||
@kernel
|
@kernel
|
||||||
def sleep(self, dac_pd: bool = False, clk_dis: bool = False):
|
def sleep(self, dac_pd: bool = False, clk_dis: bool = False):
|
||||||
"""
|
"""
|
||||||
@ -329,19 +337,13 @@ class AD9834:
|
|||||||
self.ctrl_reg &= ~AD9834_OPBITEN
|
self.ctrl_reg &= ~AD9834_OPBITEN
|
||||||
elif msb_2:
|
elif msb_2:
|
||||||
self.ctrl_reg |= AD9834_OPBITEN
|
self.ctrl_reg |= AD9834_OPBITEN
|
||||||
self.ctrl_reg &= ~AD9834_MODE
|
self.ctrl_reg &= ~(AD9834_MODE | AD9834_SIGN_PIB | AD9834_DIV2)
|
||||||
self.ctrl_reg &= ~AD9834_SIGN_PIB
|
|
||||||
self.ctrl_reg &= ~AD9834_DIV2
|
|
||||||
elif msb:
|
elif msb:
|
||||||
self.ctrl_reg |= AD9834_OPBITEN
|
self.ctrl_reg |= AD9834_OPBITEN | AD9834_DIV2
|
||||||
self.ctrl_reg &= ~AD9834_MODE
|
self.ctrl_reg &= ~(AD9834_MODE | AD9834_SIGN_PIB)
|
||||||
self.ctrl_reg &= ~AD9834_SIGN_PIB
|
|
||||||
self.ctrl_reg |= AD9834_DIV2
|
|
||||||
elif comp_out:
|
elif comp_out:
|
||||||
self.ctrl_reg |= AD9834_OPBITEN
|
self.ctrl_reg |= AD9834_OPBITEN | AD9834_SIGN_PIB | AD9834_DIV2
|
||||||
self.ctrl_reg &= ~AD9834_MODE
|
self.ctrl_reg &= ~AD9834_MODE
|
||||||
self.ctrl_reg |= AD9834_SIGN_PIB
|
|
||||||
self.ctrl_reg |= AD9834_DIV2
|
|
||||||
else:
|
else:
|
||||||
self.ctrl_reg &= ~AD9834_OPBITEN
|
self.ctrl_reg &= ~AD9834_OPBITEN
|
||||||
|
|
||||||
@ -380,18 +382,56 @@ class AD9834:
|
|||||||
self.write(self.ctrl_reg)
|
self.write(self.ctrl_reg)
|
||||||
|
|
||||||
@kernel
|
@kernel
|
||||||
def write(self, data: TInt32):
|
def set_mu(
|
||||||
|
self,
|
||||||
|
freq_word: TInt32 = 0,
|
||||||
|
phase_word: TInt32 = 0,
|
||||||
|
freq_reg: TInt32 = 0,
|
||||||
|
phase_reg: TInt32 = 0,
|
||||||
|
):
|
||||||
"""
|
"""
|
||||||
Write a 16-bit word to the AD9834.
|
Set DDS frequency and phase in machine units.
|
||||||
|
|
||||||
This method sends a 16-bit data word to the AD9834 via the SPI bus. The input
|
This method updates the specified frequency and phase registers with the provided
|
||||||
data is left-shifted by 16 bits to ensure proper alignment for the SPI controller,
|
machine units, selects the corresponding registers, and enables the output.
|
||||||
allowing for accurate processing of the command by the AD9834.
|
|
||||||
|
|
||||||
This method is used internally by other methods to update the control registers
|
:param freq_word: Frequency tuning word (28-bit).
|
||||||
and frequency settings of the AD9834. It should not be called directly unless
|
:param phase_word: Phase tuning word (12-bit).
|
||||||
low-level register manipulation is required.
|
:param freq_reg: Frequency register to write to (0 or 1).
|
||||||
|
:param phase_reg: Phase register to write to (0 or 1).
|
||||||
:param data: The 16-bit word to be sent to the AD9834.
|
|
||||||
"""
|
"""
|
||||||
self.bus.write(data << 16)
|
assert 0 <= freq_reg <= 1, "Invalid frequency register index"
|
||||||
|
assert 0 <= phase_reg <= 1, "Invalid phase register index"
|
||||||
|
|
||||||
|
self.set_frequency_reg_lsb(freq_reg, freq_word & 0x3FFF)
|
||||||
|
self.set_frequency_reg_msb(freq_reg, (freq_word >> 14) & 0x3FFF)
|
||||||
|
self.set_phase_reg(phase_reg, phase_word)
|
||||||
|
self.select_frequency_reg(freq_reg)
|
||||||
|
self.select_phase_reg(phase_reg)
|
||||||
|
self.output_enable()
|
||||||
|
|
||||||
|
@kernel
|
||||||
|
def set(
|
||||||
|
self,
|
||||||
|
frequency: TFloat = 0.0,
|
||||||
|
phase: TFloat = 0.0,
|
||||||
|
freq_reg: TInt32 = 0,
|
||||||
|
phase_reg: TInt32 = 0,
|
||||||
|
):
|
||||||
|
"""
|
||||||
|
Set DDS frequency in Hz and phase using fractional turns.
|
||||||
|
|
||||||
|
This method converts the specified frequency and phase to their corresponding
|
||||||
|
machine units, updates the selected registers, and enables the output.
|
||||||
|
|
||||||
|
:param frequency: Frequency in Hz.
|
||||||
|
:param phase: Phase in fractional turns (e.g., 0.5 for 180 degrees).
|
||||||
|
:param freq_reg: Frequency register to write to (0 or 1).
|
||||||
|
:param phase_reg: Phase register to write to (0 or 1).
|
||||||
|
"""
|
||||||
|
assert 0 <= freq_reg <= 1, "Invalid frequency register index"
|
||||||
|
assert 0 <= phase_reg <= 1, "Invalid phase register index"
|
||||||
|
|
||||||
|
freq_word = self.frequency_to_ftw(frequency)
|
||||||
|
phase_word = self.turns_to_pow(phase)
|
||||||
|
self.set_mu(freq_word, phase_word, freq_reg, phase_reg)
|
||||||
|
@ -534,9 +534,17 @@ class AD9910:
|
|||||||
After the SPI transfer, the shared IO update pin is pulsed to
|
After the SPI transfer, the shared IO update pin is pulsed to
|
||||||
activate the data.
|
activate the data.
|
||||||
|
|
||||||
.. seealso: :meth:`AD9910.set_phase_mode` for a definition of the different
|
.. seealso:: :meth:`AD9910.set_phase_mode` for a definition of the different
|
||||||
phase modes.
|
phase modes.
|
||||||
|
|
||||||
|
.. warning::
|
||||||
|
Deterministic phase control depends on correct alignment of operations
|
||||||
|
to a 4ns grid (``SYNC_CLK``). This function uses :meth:`~artiq.language.core.now_mu()`
|
||||||
|
to ensure such alignment automatically. When replayed over DMA, however, the ensuing
|
||||||
|
event sequence *must* be started at the same offset relative to ``SYNC_CLK``, or
|
||||||
|
unstable ``SYNC_CLK`` cycle assignment (i.e. inconsistent delays of exactly 4ns) will
|
||||||
|
result.
|
||||||
|
|
||||||
:param ftw: Frequency tuning word: 32-bit.
|
:param ftw: Frequency tuning word: 32-bit.
|
||||||
:param pow_: Phase tuning word: 16-bit unsigned.
|
:param pow_: Phase tuning word: 16-bit unsigned.
|
||||||
:param asf: Amplitude scale factor: 14-bit unsigned.
|
:param asf: Amplitude scale factor: 14-bit unsigned.
|
||||||
|
@ -728,15 +728,7 @@ def decoded_dump_to_target(manager, devices, dump, uniform_interval):
|
|||||||
logger.warning("unable to determine DDS sysclk")
|
logger.warning("unable to determine DDS sysclk")
|
||||||
dds_sysclk = 3e9 # guess
|
dds_sysclk = 3e9 # guess
|
||||||
|
|
||||||
if isinstance(dump.messages[-1], StoppedMessage):
|
messages = sorted(dump.messages, key=get_message_time)
|
||||||
m = dump.messages[-1]
|
|
||||||
end_time = get_message_time(m)
|
|
||||||
manager.set_end_time(end_time)
|
|
||||||
messages = dump.messages[:-1]
|
|
||||||
else:
|
|
||||||
logger.warning("StoppedMessage missing")
|
|
||||||
messages = dump.messages
|
|
||||||
messages = sorted(messages, key=get_message_time)
|
|
||||||
|
|
||||||
channel_handlers = create_channel_handlers(
|
channel_handlers = create_channel_handlers(
|
||||||
manager, devices, ref_period,
|
manager, devices, ref_period,
|
||||||
@ -752,6 +744,8 @@ def decoded_dump_to_target(manager, devices, dump, uniform_interval):
|
|||||||
interval = manager.get_channel("interval", 64, ty=WaveformType.ANALOG)
|
interval = manager.get_channel("interval", 64, ty=WaveformType.ANALOG)
|
||||||
slack = manager.get_channel("rtio_slack", 64, ty=WaveformType.ANALOG)
|
slack = manager.get_channel("rtio_slack", 64, ty=WaveformType.ANALOG)
|
||||||
|
|
||||||
|
stopped_messages = []
|
||||||
|
|
||||||
manager.set_time(0)
|
manager.set_time(0)
|
||||||
start_time = 0
|
start_time = 0
|
||||||
for m in messages:
|
for m in messages:
|
||||||
@ -762,7 +756,10 @@ def decoded_dump_to_target(manager, devices, dump, uniform_interval):
|
|||||||
manager.set_start_time(start_time)
|
manager.set_start_time(start_time)
|
||||||
t0 = start_time
|
t0 = start_time
|
||||||
for i, message in enumerate(messages):
|
for i, message in enumerate(messages):
|
||||||
if message.channel in channel_handlers:
|
if isinstance(message, StoppedMessage):
|
||||||
|
stopped_messages.append(message)
|
||||||
|
logger.debug(f"StoppedMessage at {get_message_time(message)}")
|
||||||
|
elif message.channel in channel_handlers:
|
||||||
t = get_message_time(message)
|
t = get_message_time(message)
|
||||||
if t >= 0:
|
if t >= 0:
|
||||||
if uniform_interval:
|
if uniform_interval:
|
||||||
@ -776,3 +773,9 @@ def decoded_dump_to_target(manager, devices, dump, uniform_interval):
|
|||||||
if isinstance(message, OutputMessage):
|
if isinstance(message, OutputMessage):
|
||||||
slack.set_value_double(
|
slack.set_value_double(
|
||||||
(message.timestamp - message.rtio_counter)*ref_period)
|
(message.timestamp - message.rtio_counter)*ref_period)
|
||||||
|
|
||||||
|
if not stopped_messages:
|
||||||
|
logger.warning("StoppedMessage missing")
|
||||||
|
else:
|
||||||
|
end_time = get_message_time(stopped_messages[-1])
|
||||||
|
manager.set_end_time(end_time)
|
||||||
|
@ -705,8 +705,13 @@ class CommKernel:
|
|||||||
python_exn_type = embedding_map.retrieve_object(core_exn.id)
|
python_exn_type = embedding_map.retrieve_object(core_exn.id)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
python_exn = python_exn_type(
|
message = nested_exceptions[0][1].format(*nested_exceptions[0][2])
|
||||||
nested_exceptions[-1][1].format(*nested_exceptions[0][2]))
|
except:
|
||||||
|
message = nested_exceptions[0][1]
|
||||||
|
logger.error("Couldn't format exception message", exc_info=True)
|
||||||
|
|
||||||
|
try:
|
||||||
|
python_exn = python_exn_type(message)
|
||||||
except Exception as ex:
|
except Exception as ex:
|
||||||
python_exn = RuntimeError(
|
python_exn = RuntimeError(
|
||||||
f"Exception type={python_exn_type}, which couldn't be "
|
f"Exception type={python_exn_type}, which couldn't be "
|
||||||
|
@ -30,7 +30,12 @@ OSError = builtins.OSError
|
|||||||
|
|
||||||
|
|
||||||
class CoreException:
|
class CoreException:
|
||||||
"""Information about an exception raised or passed through the core device."""
|
"""Information about an exception raised or passed through the core device.
|
||||||
|
|
||||||
|
If the exception message contains positional format arguments, it
|
||||||
|
will attempt to substitute them with the provided parameters.
|
||||||
|
If the substitution fails, the original message will remain unchanged.
|
||||||
|
"""
|
||||||
def __init__(self, exceptions, exception_info, traceback, stack_pointers):
|
def __init__(self, exceptions, exception_info, traceback, stack_pointers):
|
||||||
self.exceptions = exceptions
|
self.exceptions = exceptions
|
||||||
self.exception_info = exception_info
|
self.exception_info = exception_info
|
||||||
@ -92,7 +97,10 @@ class CoreException:
|
|||||||
exn_id = int(exn_id)
|
exn_id = int(exn_id)
|
||||||
else:
|
else:
|
||||||
exn_id = 0
|
exn_id = 0
|
||||||
|
try:
|
||||||
lines.append("{}({}): {}".format(name, exn_id, message.format(*params)))
|
lines.append("{}({}): {}".format(name, exn_id, message.format(*params)))
|
||||||
|
except:
|
||||||
|
lines.append("{}({}): {}".format(name, exn_id, message))
|
||||||
zipped.append(((exception[3], exception[4], exception[5], exception[6],
|
zipped.append(((exception[3], exception[4], exception[5], exception[6],
|
||||||
None, []), None))
|
None, []), None))
|
||||||
|
|
||||||
|
@ -500,7 +500,7 @@ pub extern fn main() -> i32 {
|
|||||||
println!(r"|_| |_|_|____/ \___/ \____|");
|
println!(r"|_| |_|_|____/ \___/ \____|");
|
||||||
println!("");
|
println!("");
|
||||||
println!("MiSoC Bootloader");
|
println!("MiSoC Bootloader");
|
||||||
println!("Copyright (c) 2017-2024 M-Labs Limited");
|
println!("Copyright (c) 2017-2025 M-Labs Limited");
|
||||||
println!("");
|
println!("");
|
||||||
|
|
||||||
#[cfg(has_ethmac)]
|
#[cfg(has_ethmac)]
|
||||||
|
@ -240,7 +240,7 @@ fn startup() {
|
|||||||
let subkernel_mutex = subkernel_mutex.clone();
|
let subkernel_mutex = subkernel_mutex.clone();
|
||||||
let drtio_routing_table = drtio_routing_table.clone();
|
let drtio_routing_table = drtio_routing_table.clone();
|
||||||
let up_destinations = up_destinations.clone();
|
let up_destinations = up_destinations.clone();
|
||||||
io.spawn(8192, move |io| { analyzer::thread(io, &aux_mutex, &ddma_mutex, &subkernel_mutex, &drtio_routing_table, &up_destinations) });
|
io.spawn(16384, move |io| { analyzer::thread(io, &aux_mutex, &ddma_mutex, &subkernel_mutex, &drtio_routing_table, &up_destinations) });
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(has_grabber)]
|
#[cfg(has_grabber)]
|
||||||
|
@ -11,8 +11,6 @@ from artiq.coredevice.ad9834 import (
|
|||||||
AD9834_SIGN_PIB,
|
AD9834_SIGN_PIB,
|
||||||
AD9834_SLEEP1,
|
AD9834_SLEEP1,
|
||||||
AD9834_SLEEP12,
|
AD9834_SLEEP12,
|
||||||
FREQ_REGS,
|
|
||||||
PHASE_REGS,
|
|
||||||
)
|
)
|
||||||
from artiq.experiment import *
|
from artiq.experiment import *
|
||||||
from artiq.language.units import MHz
|
from artiq.language.units import MHz
|
||||||
@ -34,165 +32,251 @@ class AD9834Exp(EnvExperiment):
|
|||||||
|
|
||||||
@kernel
|
@kernel
|
||||||
def init(self):
|
def init(self):
|
||||||
self.core.break_realtime()
|
self.core.reset()
|
||||||
self.dev.init()
|
self.dev.init()
|
||||||
self.set_dataset("spi_freq", self.dev.spi_freq)
|
self.set_dataset("spi_freq", self.dev.spi_freq)
|
||||||
self.set_dataset("clk_freq", self.dev.clk_freq)
|
self.set_dataset("clk_freq", self.dev.clk_freq)
|
||||||
self.set_dataset("ctrl_reg", self.dev.ctrl_reg)
|
self.set_dataset("ctrl_reg", self.dev.ctrl_reg)
|
||||||
|
|
||||||
@kernel
|
@kernel
|
||||||
def set_frequency_reg_fail1(self):
|
def frequency_to_ftw_fail(self):
|
||||||
self.core.break_realtime()
|
self.core.reset()
|
||||||
frequency = 10 * MHz
|
self.dev.init()
|
||||||
self.dev.set_frequency_reg(19, frequency)
|
self.dev.frequency_to_ftw(37.6 * MHz)
|
||||||
|
|
||||||
@kernel
|
@kernel
|
||||||
def set_frequency_reg_fail2(self):
|
def turns_to_phase_fail(self):
|
||||||
self.core.break_realtime()
|
self.core.reset()
|
||||||
self.dev.set_frequency_reg(FREQ_REGS[0], 37.6 * MHz)
|
self.dev.init()
|
||||||
|
self.dev.turns_to_phase(1.1)
|
||||||
|
|
||||||
|
@kernel
|
||||||
|
def set_frequency_reg_fail(self):
|
||||||
|
self.core.reset()
|
||||||
|
self.dev.init()
|
||||||
|
self.dev.set_frequency_reg(19, self.dev.frequency_to_ftw(10 * MHz))
|
||||||
|
|
||||||
@kernel
|
@kernel
|
||||||
def set_frequency_reg(self):
|
def set_frequency_reg(self):
|
||||||
self.core.break_realtime()
|
self.core.reset()
|
||||||
self.dev.init()
|
self.dev.init()
|
||||||
self.dev.set_frequency_reg(FREQ_REGS[1], 19 * MHz)
|
self.dev.set_frequency_reg(1, self.dev.frequency_to_ftw(19 * MHz))
|
||||||
self.set_dataset("ctrl_reg", self.dev.ctrl_reg)
|
self.set_dataset("ctrl_reg", self.dev.ctrl_reg)
|
||||||
|
|
||||||
@kernel
|
@kernel
|
||||||
def set_frequency_reg_msb(self):
|
def set_frequency_reg_msb(self):
|
||||||
self.core.break_realtime()
|
self.core.reset()
|
||||||
self.dev.init()
|
self.dev.init()
|
||||||
self.dev.ctrl_reg |= AD9834_B28
|
self.dev.ctrl_reg |= AD9834_B28
|
||||||
self.dev.set_frequency_reg_msb(FREQ_REGS[0], 0x1111)
|
self.dev.set_frequency_reg_msb(0, 0x1111)
|
||||||
self.set_dataset("ctrl_reg", self.dev.ctrl_reg)
|
self.set_dataset("ctrl_reg", self.dev.ctrl_reg)
|
||||||
|
|
||||||
@kernel
|
@kernel
|
||||||
def set_frequency_reg_lsb(self):
|
def set_frequency_reg_lsb(self):
|
||||||
self.core.break_realtime()
|
self.core.reset()
|
||||||
self.dev.init()
|
self.dev.init()
|
||||||
self.dev.ctrl_reg |= AD9834_B28 | AD9834_HLB
|
self.dev.ctrl_reg |= AD9834_B28 | AD9834_HLB
|
||||||
self.dev.set_frequency_reg_lsb(FREQ_REGS[1], 0xFFFF)
|
self.dev.set_frequency_reg_lsb(1, 0xFFFF)
|
||||||
self.set_dataset("ctrl_reg", self.dev.ctrl_reg)
|
self.set_dataset("ctrl_reg", self.dev.ctrl_reg)
|
||||||
|
|
||||||
@kernel
|
@kernel
|
||||||
def select_frequency_reg_0(self):
|
def select_frequency_reg_0(self):
|
||||||
self.core.break_realtime()
|
self.core.reset()
|
||||||
self.dev.ctrl_reg |= AD9834_FSEL | AD9834_PIN_SW
|
self.dev.init()
|
||||||
self.dev.select_frequency_reg(FREQ_REGS[0])
|
self.dev.select_frequency_reg(0)
|
||||||
self.set_dataset("ctrl_reg", self.dev.ctrl_reg)
|
self.set_dataset("ctrl_reg", self.dev.ctrl_reg & ~(AD9834_FSEL | AD9834_PIN_SW))
|
||||||
|
|
||||||
@kernel
|
@kernel
|
||||||
def select_frequency_reg_1(self):
|
def select_frequency_reg_1(self):
|
||||||
self.core.break_realtime()
|
self.core.reset()
|
||||||
self.dev.ctrl_reg |= AD9834_PIN_SW
|
self.dev.init()
|
||||||
self.dev.select_frequency_reg(FREQ_REGS[1])
|
self.dev.select_frequency_reg(1)
|
||||||
self.set_dataset("ctrl_reg", self.dev.ctrl_reg)
|
self.set_dataset("ctrl_reg", (self.dev.ctrl_reg | AD9834_FSEL) & ~AD9834_PIN_SW)
|
||||||
|
|
||||||
@kernel
|
@kernel
|
||||||
def set_phase_reg_fail(self):
|
def set_phase_reg_fail(self):
|
||||||
self.core.break_realtime()
|
self.core.reset()
|
||||||
|
self.dev.init()
|
||||||
self.dev.set_phase_reg(19, 0x123)
|
self.dev.set_phase_reg(19, 0x123)
|
||||||
|
|
||||||
@kernel
|
@kernel
|
||||||
def set_phase_reg(self):
|
def set_phase_reg(self):
|
||||||
self.core.break_realtime()
|
self.core.reset()
|
||||||
self.dev.init()
|
self.dev.init()
|
||||||
self.dev.set_phase_reg(PHASE_REGS[0], 0x123)
|
self.dev.set_phase_reg(0, 0x123)
|
||||||
self.set_dataset("ctrl_reg", self.dev.ctrl_reg)
|
self.set_dataset("ctrl_reg", self.dev.ctrl_reg)
|
||||||
|
|
||||||
@kernel
|
@kernel
|
||||||
def select_phase_reg_0(self):
|
def select_phase_reg_0(self):
|
||||||
self.core.break_realtime()
|
self.core.reset()
|
||||||
self.dev.ctrl_reg |= AD9834_PSEL | AD9834_PIN_SW
|
self.dev.init()
|
||||||
self.dev.select_phase_reg(PHASE_REGS[0])
|
self.dev.select_phase_reg(0)
|
||||||
self.set_dataset("ctrl_reg", self.dev.ctrl_reg)
|
self.set_dataset("ctrl_reg", self.dev.ctrl_reg & ~(AD9834_PSEL | AD9834_PIN_SW))
|
||||||
|
|
||||||
@kernel
|
@kernel
|
||||||
def select_phase_reg_1(self):
|
def select_phase_reg_1(self):
|
||||||
self.core.break_realtime()
|
self.core.reset()
|
||||||
self.dev.ctrl_reg |= AD9834_PIN_SW
|
self.dev.init()
|
||||||
self.dev.select_phase_reg(PHASE_REGS[1])
|
self.dev.select_phase_reg(1)
|
||||||
self.set_dataset("ctrl_reg", self.dev.ctrl_reg)
|
self.set_dataset("ctrl_reg", (self.dev.ctrl_reg | AD9834_PSEL) & ~AD9834_PIN_SW)
|
||||||
|
|
||||||
@kernel
|
|
||||||
def enable_reset(self):
|
|
||||||
self.core.break_realtime()
|
|
||||||
self.dev.enable_reset()
|
|
||||||
self.set_dataset("ctrl_reg", self.dev.ctrl_reg)
|
|
||||||
|
|
||||||
@kernel
|
|
||||||
def output_enable(self):
|
|
||||||
self.core.break_realtime()
|
|
||||||
self.dev.ctrl_reg |= AD9834_RESET
|
|
||||||
self.dev.output_enable()
|
|
||||||
self.set_dataset("ctrl_reg", self.dev.ctrl_reg)
|
|
||||||
|
|
||||||
@kernel
|
@kernel
|
||||||
def sleep_dac_powerdown(self):
|
def sleep_dac_powerdown(self):
|
||||||
self.core.break_realtime()
|
self.core.reset()
|
||||||
|
self.dev.init()
|
||||||
self.dev.sleep(dac_pd=True)
|
self.dev.sleep(dac_pd=True)
|
||||||
self.set_dataset("ctrl_reg", self.dev.ctrl_reg)
|
self.set_dataset("ctrl_reg", self.dev.ctrl_reg)
|
||||||
|
|
||||||
@kernel
|
@kernel
|
||||||
def sleep_internal_clk_disable(self):
|
def sleep_internal_clk_disable(self):
|
||||||
self.core.break_realtime()
|
self.core.reset()
|
||||||
|
self.dev.init()
|
||||||
self.dev.sleep(clk_dis=True)
|
self.dev.sleep(clk_dis=True)
|
||||||
self.set_dataset("ctrl_reg", self.dev.ctrl_reg)
|
self.set_dataset("ctrl_reg", self.dev.ctrl_reg)
|
||||||
|
|
||||||
@kernel
|
@kernel
|
||||||
def sleep(self):
|
def sleep(self):
|
||||||
self.core.break_realtime()
|
self.core.reset()
|
||||||
|
self.dev.init()
|
||||||
self.dev.sleep(dac_pd=True, clk_dis=True)
|
self.dev.sleep(dac_pd=True, clk_dis=True)
|
||||||
self.set_dataset("ctrl_reg", self.dev.ctrl_reg)
|
self.set_dataset("ctrl_reg", self.dev.ctrl_reg)
|
||||||
|
|
||||||
@kernel
|
@kernel
|
||||||
def awake(self):
|
def awake(self):
|
||||||
self.core.break_realtime()
|
self.core.reset()
|
||||||
self.dev.ctrl_reg |= AD9834_SLEEP1 | AD9834_SLEEP12
|
self.dev.init()
|
||||||
self.dev.sleep()
|
self.dev.awake()
|
||||||
self.set_dataset("ctrl_reg", self.dev.ctrl_reg)
|
self.set_dataset(
|
||||||
|
"ctrl_reg", self.dev.ctrl_reg & ~(AD9834_SLEEP1 | AD9834_SLEEP12)
|
||||||
|
)
|
||||||
|
|
||||||
@kernel
|
@kernel
|
||||||
def sign_bit_high_z(self):
|
def sign_bit_high_z(self):
|
||||||
self.core.break_realtime()
|
self.core.reset()
|
||||||
self.dev.ctrl_reg |= AD9834_OPBITEN
|
self.dev.init()
|
||||||
self.dev.config_sign_bit_out()
|
self.dev.config_sign_bit_out()
|
||||||
self.set_dataset("ctrl_reg", self.dev.ctrl_reg)
|
self.set_dataset("ctrl_reg", self.dev.ctrl_reg & ~AD9834_OPBITEN)
|
||||||
|
|
||||||
@kernel
|
@kernel
|
||||||
def sign_bit_msb_2(self):
|
def sign_bit_msb_2(self):
|
||||||
self.core.break_realtime()
|
self.core.reset()
|
||||||
self.dev.ctrl_reg |= AD9834_MODE | AD9834_SIGN_PIB | AD9834_DIV2
|
self.dev.init()
|
||||||
self.dev.config_sign_bit_out(msb_2=True)
|
self.dev.config_sign_bit_out(msb_2=True)
|
||||||
self.set_dataset("ctrl_reg", self.dev.ctrl_reg)
|
self.set_dataset(
|
||||||
|
"ctrl_reg",
|
||||||
|
(self.dev.ctrl_reg | AD9834_OPBITEN)
|
||||||
|
& ~(AD9834_MODE | AD9834_SIGN_PIB | AD9834_DIV2),
|
||||||
|
)
|
||||||
|
|
||||||
@kernel
|
@kernel
|
||||||
def sign_bit_msb(self):
|
def sign_bit_msb(self):
|
||||||
self.core.break_realtime()
|
self.core.reset()
|
||||||
self.dev.ctrl_reg |= AD9834_MODE | AD9834_SIGN_PIB
|
self.dev.init()
|
||||||
self.dev.config_sign_bit_out(msb=True)
|
self.dev.config_sign_bit_out(msb=True)
|
||||||
self.set_dataset("ctrl_reg", self.dev.ctrl_reg)
|
self.set_dataset(
|
||||||
|
"ctrl_reg",
|
||||||
|
(self.dev.ctrl_reg | AD9834_MODE | AD9834_SIGN_PIB)
|
||||||
|
& ~(AD9834_MODE | AD9834_SIGN_PIB),
|
||||||
|
)
|
||||||
|
|
||||||
@kernel
|
@kernel
|
||||||
def sign_bit_comp_out(self):
|
def sign_bit_comp_out(self):
|
||||||
self.core.break_realtime()
|
self.core.reset()
|
||||||
self.dev.ctrl_reg |= AD9834_MODE
|
self.dev.init()
|
||||||
self.dev.config_sign_bit_out(comp_out=True)
|
self.dev.config_sign_bit_out(comp_out=True)
|
||||||
self.set_dataset("ctrl_reg", self.dev.ctrl_reg)
|
self.set_dataset(
|
||||||
|
"ctrl_reg",
|
||||||
|
(self.dev.ctrl_reg | AD9834_OPBITEN | AD9834_SIGN_PIB | AD9834_DIV2)
|
||||||
|
& ~AD9834_MODE,
|
||||||
|
)
|
||||||
|
|
||||||
@kernel
|
@kernel
|
||||||
def enable_triangular_waveform(self):
|
def enable_triangular_waveform(self):
|
||||||
self.core.break_realtime()
|
self.core.reset()
|
||||||
self.dev.ctrl_reg |= AD9834_OPBITEN
|
self.dev.init()
|
||||||
self.dev.enable_triangular_waveform()
|
self.dev.enable_triangular_waveform()
|
||||||
self.set_dataset("ctrl_reg", self.dev.ctrl_reg)
|
self.set_dataset("ctrl_reg", self.dev.ctrl_reg | AD9834_MODE)
|
||||||
|
|
||||||
@kernel
|
@kernel
|
||||||
def disable_triangular_waveform(self):
|
def disable_triangular_waveform(self):
|
||||||
self.core.break_realtime()
|
self.core.reset()
|
||||||
self.dev.ctrl_reg |= AD9834_MODE
|
self.dev.init()
|
||||||
self.dev.disable_triangular_waveform()
|
self.dev.disable_triangular_waveform()
|
||||||
self.set_dataset("ctrl_reg", self.dev.ctrl_reg)
|
self.set_dataset("ctrl_reg", self.dev.ctrl_reg & ~AD9834_MODE)
|
||||||
|
|
||||||
|
## The following tests should be hooked up to an oscilloscope
|
||||||
|
## to monitor the waveforms
|
||||||
|
@kernel
|
||||||
|
def single_tone(self):
|
||||||
|
self.core.reset()
|
||||||
|
self.dev.init()
|
||||||
|
self.dev.set_frequency_reg(0, self.dev.frequency_to_ftw(1 * MHz))
|
||||||
|
self.dev.select_frequency_reg(0)
|
||||||
|
self.dev.output_enable()
|
||||||
|
delay(5 * s)
|
||||||
|
self.dev.enable_reset()
|
||||||
|
self.core.wait_until_mu(now_mu())
|
||||||
|
|
||||||
|
@kernel
|
||||||
|
def toggle_frequency(self):
|
||||||
|
self.core.reset()
|
||||||
|
self.dev.init()
|
||||||
|
self.dev.set_frequency_reg(0, self.dev.frequency_to_ftw(1 * MHz))
|
||||||
|
self.dev.set_frequency_reg(1, self.dev.frequency_to_ftw(2 * MHz))
|
||||||
|
self.dev.select_frequency_reg(0)
|
||||||
|
self.dev.output_enable()
|
||||||
|
|
||||||
|
for _ in range(6):
|
||||||
|
self.dev.select_frequency_reg(0)
|
||||||
|
delay(1 * s)
|
||||||
|
self.dev.select_frequency_reg(1)
|
||||||
|
delay(1 * s)
|
||||||
|
|
||||||
|
self.dev.enable_reset()
|
||||||
|
self.core.wait_until_mu(now_mu())
|
||||||
|
|
||||||
|
@kernel
|
||||||
|
def toggle_phase(self):
|
||||||
|
self.core.reset()
|
||||||
|
self.dev.init()
|
||||||
|
self.dev.set_frequency_reg(0, self.dev.frequency_to_ftw(1 * MHz))
|
||||||
|
self.dev.select_frequency_reg(0)
|
||||||
|
self.dev.set_phase_reg(0, 0x0)
|
||||||
|
self.dev.set_phase_reg(1, 0x7FF)
|
||||||
|
self.dev.output_enable()
|
||||||
|
|
||||||
|
for _ in range(300000):
|
||||||
|
self.dev.select_phase_reg(0)
|
||||||
|
delay(10 * us)
|
||||||
|
self.dev.select_phase_reg(1)
|
||||||
|
delay(10 * us)
|
||||||
|
|
||||||
|
self.dev.enable_reset()
|
||||||
|
self.core.wait_until_mu(now_mu())
|
||||||
|
|
||||||
|
@kernel
|
||||||
|
def set_mu(self):
|
||||||
|
self.core.reset()
|
||||||
|
self.dev.init()
|
||||||
|
freq_word = self.dev.frequency_to_ftw(1 * MHz)
|
||||||
|
phase_word = self.dev.turns_to_phase(0.5)
|
||||||
|
self.dev.set_mu(freq_word, phase_word, 0, 1)
|
||||||
|
|
||||||
|
delay(5 * s)
|
||||||
|
|
||||||
|
self.dev.enable_reset()
|
||||||
|
self.core.wait_until_mu(now_mu())
|
||||||
|
|
||||||
|
@kernel
|
||||||
|
def set(self):
|
||||||
|
self.core.reset()
|
||||||
|
self.dev.init()
|
||||||
|
self.dev.set(2 * MHz, 0.5, 1, 0)
|
||||||
|
|
||||||
|
delay(5 * s)
|
||||||
|
|
||||||
|
self.dev.enable_reset()
|
||||||
|
self.core.wait_until_mu(now_mu())
|
||||||
|
|
||||||
|
|
||||||
class AD9834Test(ExperimentCase):
|
class AD9834Test(ExperimentCase):
|
||||||
@ -208,21 +292,27 @@ class AD9834Test(ExperimentCase):
|
|||||||
self.assertEqual(clk_freq, 75 * MHz)
|
self.assertEqual(clk_freq, 75 * MHz)
|
||||||
self.assertEqual(ctrl_reg, 0x0000 | AD9834_RESET)
|
self.assertEqual(ctrl_reg, 0x0000 | AD9834_RESET)
|
||||||
|
|
||||||
def test_set_frequency_reg_fail(self):
|
def test_frequency_to_ftw_fail(self):
|
||||||
with self.assertRaises(ValueError):
|
|
||||||
self.execute(AD9834Exp, "set_frequency_reg_fail1")
|
|
||||||
with self.assertRaises(AssertionError):
|
with self.assertRaises(AssertionError):
|
||||||
self.execute(AD9834Exp, "set_frequency_reg_fail2")
|
self.execute(AD9834Exp, "frequency_to_ftw_fail")
|
||||||
|
|
||||||
|
def test_turns_to_phase_fail(self):
|
||||||
|
with self.assertRaises(AssertionError):
|
||||||
|
self.execute(AD9834Exp, "turns_to_phase_fail")
|
||||||
|
|
||||||
|
def test_set_frequency_reg_fail(self):
|
||||||
|
with self.assertRaises(AssertionError):
|
||||||
|
self.execute(AD9834Exp, "set_frequency_reg_fail")
|
||||||
|
|
||||||
def test_set_frequency_reg(self):
|
def test_set_frequency_reg(self):
|
||||||
self.execute(AD9834Exp, "set_frequency_reg")
|
self.execute(AD9834Exp, "set_frequency_reg")
|
||||||
ctrl_reg = self.dataset_mgr.get("ctrl_reg")
|
ctrl_reg = self.dataset_mgr.get("ctrl_reg")
|
||||||
self.assertEqual(ctrl_reg, 0x0000 | AD9834_RESET | AD9834_B28)
|
self.assertEqual(ctrl_reg, 0x0000 | AD9834_B28 | AD9834_RESET)
|
||||||
|
|
||||||
def test_set_frequency_reg_msb(self):
|
def test_set_frequency_reg_msb(self):
|
||||||
self.execute(AD9834Exp, "set_frequency_reg_msb")
|
self.execute(AD9834Exp, "set_frequency_reg_msb")
|
||||||
ctrl_reg = self.dataset_mgr.get("ctrl_reg")
|
ctrl_reg = self.dataset_mgr.get("ctrl_reg")
|
||||||
self.assertEqual(ctrl_reg, 0x0000 | AD9834_RESET | AD9834_HLB)
|
self.assertEqual(ctrl_reg, 0x0000 | AD9834_HLB | AD9834_RESET)
|
||||||
|
|
||||||
def test_set_frequency_reg_lsb(self):
|
def test_set_frequency_reg_lsb(self):
|
||||||
self.execute(AD9834Exp, "set_frequency_reg_lsb")
|
self.execute(AD9834Exp, "set_frequency_reg_lsb")
|
||||||
@ -232,15 +322,15 @@ class AD9834Test(ExperimentCase):
|
|||||||
def test_select_frequency_reg_0(self):
|
def test_select_frequency_reg_0(self):
|
||||||
self.execute(AD9834Exp, "select_frequency_reg_0")
|
self.execute(AD9834Exp, "select_frequency_reg_0")
|
||||||
ctrl_reg = self.dataset_mgr.get("ctrl_reg")
|
ctrl_reg = self.dataset_mgr.get("ctrl_reg")
|
||||||
self.assertEqual(ctrl_reg, 0x0000)
|
self.assertEqual(ctrl_reg, 0x0000 | AD9834_RESET)
|
||||||
|
|
||||||
def test_select_frequency_reg_1(self):
|
def test_select_frequency_reg_1(self):
|
||||||
self.execute(AD9834Exp, "select_frequency_reg_1")
|
self.execute(AD9834Exp, "select_frequency_reg_1")
|
||||||
ctrl_reg = self.dataset_mgr.get("ctrl_reg")
|
ctrl_reg = self.dataset_mgr.get("ctrl_reg")
|
||||||
self.assertEqual(ctrl_reg, 0x0000 | AD9834_FSEL)
|
self.assertEqual(ctrl_reg, 0x0000 | AD9834_FSEL | AD9834_RESET)
|
||||||
|
|
||||||
def test_set_phase_reg_fail(self):
|
def test_set_phase_reg_fail(self):
|
||||||
with self.assertRaises(ValueError):
|
with self.assertRaises(AssertionError):
|
||||||
self.execute(AD9834Exp, "set_phase_reg_fail")
|
self.execute(AD9834Exp, "set_phase_reg_fail")
|
||||||
|
|
||||||
def test_set_phase_reg(self):
|
def test_set_phase_reg(self):
|
||||||
@ -251,71 +341,85 @@ class AD9834Test(ExperimentCase):
|
|||||||
def test_select_phase_reg_0(self):
|
def test_select_phase_reg_0(self):
|
||||||
self.execute(AD9834Exp, "select_phase_reg_0")
|
self.execute(AD9834Exp, "select_phase_reg_0")
|
||||||
ctrl_reg = self.dataset_mgr.get("ctrl_reg")
|
ctrl_reg = self.dataset_mgr.get("ctrl_reg")
|
||||||
self.assertEqual(ctrl_reg, 0x0000)
|
self.assertEqual(ctrl_reg, 0x0000 | AD9834_RESET)
|
||||||
|
|
||||||
def test_select_phase_reg_1(self):
|
def test_select_phase_reg_1(self):
|
||||||
self.execute(AD9834Exp, "select_phase_reg_1")
|
self.execute(AD9834Exp, "select_phase_reg_1")
|
||||||
ctrl_reg = self.dataset_mgr.get("ctrl_reg")
|
ctrl_reg = self.dataset_mgr.get("ctrl_reg")
|
||||||
self.assertEqual(ctrl_reg, 0x0000 | AD9834_PSEL)
|
self.assertEqual(ctrl_reg, 0x0000 | AD9834_PSEL | AD9834_RESET)
|
||||||
|
|
||||||
def test_enable_reset(self):
|
|
||||||
self.execute(AD9834Exp, "enable_reset")
|
|
||||||
ctrl_reg = self.dataset_mgr.get("ctrl_reg")
|
|
||||||
self.assertEqual(ctrl_reg, 0x0000 | AD9834_RESET)
|
|
||||||
|
|
||||||
def test_output_enable(self):
|
|
||||||
self.execute(AD9834Exp, "output_enable")
|
|
||||||
ctrl_reg = self.dataset_mgr.get("ctrl_reg")
|
|
||||||
self.assertEqual(ctrl_reg, 0x0000)
|
|
||||||
|
|
||||||
def test_sleep_dac_powerdown(self):
|
def test_sleep_dac_powerdown(self):
|
||||||
self.execute(AD9834Exp, "sleep_dac_powerdown")
|
self.execute(AD9834Exp, "sleep_dac_powerdown")
|
||||||
ctrl_reg = self.dataset_mgr.get("ctrl_reg")
|
ctrl_reg = self.dataset_mgr.get("ctrl_reg")
|
||||||
self.assertEqual(ctrl_reg, 0x0000 | AD9834_SLEEP12)
|
self.assertEqual(ctrl_reg, 0x0000 | AD9834_SLEEP12 | AD9834_RESET)
|
||||||
|
|
||||||
def test_sleep_internal_clk_disable(self):
|
def test_sleep_internal_clk_disable(self):
|
||||||
self.execute(AD9834Exp, "sleep_internal_clk_disable")
|
self.execute(AD9834Exp, "sleep_internal_clk_disable")
|
||||||
ctrl_reg = self.dataset_mgr.get("ctrl_reg")
|
ctrl_reg = self.dataset_mgr.get("ctrl_reg")
|
||||||
self.assertEqual(ctrl_reg, 0x0000 | AD9834_SLEEP1)
|
self.assertEqual(ctrl_reg, 0x0000 | AD9834_SLEEP1 | AD9834_RESET)
|
||||||
|
|
||||||
def test_sleep(self):
|
def test_sleep(self):
|
||||||
self.execute(AD9834Exp, "sleep")
|
self.execute(AD9834Exp, "sleep")
|
||||||
ctrl_reg = self.dataset_mgr.get("ctrl_reg")
|
ctrl_reg = self.dataset_mgr.get("ctrl_reg")
|
||||||
self.assertEqual(ctrl_reg, 0x0000 | AD9834_SLEEP1 | AD9834_SLEEP12)
|
self.assertEqual(
|
||||||
|
ctrl_reg, 0x0000 | AD9834_SLEEP1 | AD9834_SLEEP12 | AD9834_RESET
|
||||||
|
)
|
||||||
|
|
||||||
def test_awake(self):
|
def test_awake(self):
|
||||||
self.execute(AD9834Exp, "awake")
|
self.execute(AD9834Exp, "awake")
|
||||||
ctrl_reg = self.dataset_mgr.get("ctrl_reg")
|
ctrl_reg = self.dataset_mgr.get("ctrl_reg")
|
||||||
self.assertEqual(ctrl_reg, 0x0000)
|
self.assertEqual(ctrl_reg, 0x0000 | AD9834_RESET)
|
||||||
|
|
||||||
def test_sign_bit_high_z(self):
|
def test_sign_bit_high_z(self):
|
||||||
self.execute(AD9834Exp, "sign_bit_high_z")
|
self.execute(AD9834Exp, "sign_bit_high_z")
|
||||||
ctrl_reg = self.dataset_mgr.get("ctrl_reg")
|
ctrl_reg = self.dataset_mgr.get("ctrl_reg")
|
||||||
self.assertEqual(ctrl_reg, 0x0000)
|
self.assertEqual(ctrl_reg, 0x0000 | AD9834_RESET)
|
||||||
|
|
||||||
def test_sign_bit_msb_2(self):
|
def test_sign_bit_msb_2(self):
|
||||||
self.execute(AD9834Exp, "sign_bit_msb_2")
|
self.execute(AD9834Exp, "sign_bit_msb_2")
|
||||||
ctrl_reg = self.dataset_mgr.get("ctrl_reg")
|
ctrl_reg = self.dataset_mgr.get("ctrl_reg")
|
||||||
self.assertEqual(ctrl_reg, 0x0000 | AD9834_OPBITEN)
|
self.assertEqual(ctrl_reg, 0x0000 | AD9834_OPBITEN | AD9834_RESET)
|
||||||
|
|
||||||
def test_sign_bit_msb(self):
|
def test_sign_bit_msb(self):
|
||||||
self.execute(AD9834Exp, "sign_bit_msb")
|
self.execute(AD9834Exp, "sign_bit_msb")
|
||||||
ctrl_reg = self.dataset_mgr.get("ctrl_reg")
|
ctrl_reg = self.dataset_mgr.get("ctrl_reg")
|
||||||
self.assertEqual(ctrl_reg, 0x0000 | AD9834_OPBITEN | AD9834_DIV2)
|
self.assertEqual(ctrl_reg, 0x0000 | AD9834_OPBITEN | AD9834_DIV2 | AD9834_RESET)
|
||||||
|
|
||||||
def test_sign_bit_comp_out(self):
|
def test_sign_bit_comp_out(self):
|
||||||
self.execute(AD9834Exp, "sign_bit_comp_out")
|
self.execute(AD9834Exp, "sign_bit_comp_out")
|
||||||
ctrl_reg = self.dataset_mgr.get("ctrl_reg")
|
ctrl_reg = self.dataset_mgr.get("ctrl_reg")
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
ctrl_reg, 0x0000 | AD9834_OPBITEN | AD9834_SIGN_PIB | AD9834_DIV2
|
ctrl_reg,
|
||||||
|
0x0000 | AD9834_OPBITEN | AD9834_SIGN_PIB | AD9834_DIV2 | AD9834_RESET,
|
||||||
)
|
)
|
||||||
|
|
||||||
def test_enble_triangular_waveform(self):
|
def test_enble_triangular_waveform(self):
|
||||||
self.execute(AD9834Exp, "enable_triangular_waveform")
|
self.execute(AD9834Exp, "enable_triangular_waveform")
|
||||||
ctrl_reg = self.dataset_mgr.get("ctrl_reg")
|
ctrl_reg = self.dataset_mgr.get("ctrl_reg")
|
||||||
self.assertEqual(ctrl_reg, 0x0000 | AD9834_MODE)
|
self.assertEqual(ctrl_reg, 0x0000 | AD9834_MODE | AD9834_RESET)
|
||||||
|
|
||||||
def test_disble_triangular_waveform(self):
|
def test_disble_triangular_waveform(self):
|
||||||
self.execute(AD9834Exp, "disable_triangular_waveform")
|
self.execute(AD9834Exp, "disable_triangular_waveform")
|
||||||
ctrl_reg = self.dataset_mgr.get("ctrl_reg")
|
ctrl_reg = self.dataset_mgr.get("ctrl_reg")
|
||||||
self.assertEqual(ctrl_reg, 0x0000)
|
self.assertEqual(ctrl_reg, 0x0000 | AD9834_RESET)
|
||||||
|
|
||||||
|
## Waveform Tests
|
||||||
|
def test_single_tone(self):
|
||||||
|
print("Running waveform test:", self._testMethodName)
|
||||||
|
self.execute(AD9834Exp, "single_tone")
|
||||||
|
|
||||||
|
def test_toggle_frequency(self):
|
||||||
|
print("Running waveform test:", self._testMethodName)
|
||||||
|
self.execute(AD9834Exp, "toggle_frequency")
|
||||||
|
|
||||||
|
def test_toggle_phase(self):
|
||||||
|
print("Running waveform test:", self._testMethodName)
|
||||||
|
self.execute(AD9834Exp, "toggle_phase")
|
||||||
|
|
||||||
|
def test_set_mu(self):
|
||||||
|
print("Running waveform test:", self._testMethodName)
|
||||||
|
self.execute(AD9834Exp, "set_mu")
|
||||||
|
|
||||||
|
def test_set(self):
|
||||||
|
print("Running waveform test:", self._testMethodName)
|
||||||
|
self.execute(AD9834Exp, "set")
|
||||||
|
@ -1,10 +1,94 @@
|
|||||||
import unittest
|
import re
|
||||||
import artiq.coredevice.exceptions as exceptions
|
|
||||||
|
|
||||||
from artiq.experiment import *
|
from artiq.experiment import *
|
||||||
|
from artiq.master.worker_db import DeviceError
|
||||||
from artiq.test.hardware_testbench import ExperimentCase
|
from artiq.test.hardware_testbench import ExperimentCase
|
||||||
from artiq.compiler.embedding import EmbeddingMap
|
from artiq.compiler.embedding import EmbeddingMap
|
||||||
from artiq.coredevice.core import test_exception_id_sync
|
from artiq.coredevice.core import test_exception_id_sync
|
||||||
|
import artiq.coredevice.exceptions as exceptions
|
||||||
|
|
||||||
|
|
||||||
|
class CustomException(Exception):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class KernelFmtException(EnvExperiment):
|
||||||
|
def build(self):
|
||||||
|
self.setattr_device("core")
|
||||||
|
|
||||||
|
@kernel
|
||||||
|
def run(self):
|
||||||
|
self.throw()
|
||||||
|
|
||||||
|
def throw(self):
|
||||||
|
raise CustomException("{foo}")
|
||||||
|
|
||||||
|
|
||||||
|
class KernelNestedFmtException(EnvExperiment):
|
||||||
|
def build(self):
|
||||||
|
self.setattr_device("core")
|
||||||
|
|
||||||
|
@kernel
|
||||||
|
def run(self):
|
||||||
|
try:
|
||||||
|
self.throw_foo()
|
||||||
|
except:
|
||||||
|
try:
|
||||||
|
raise RTIOUnderflow("{bar}")
|
||||||
|
except:
|
||||||
|
try:
|
||||||
|
raise RTIOOverflow("{bizz}")
|
||||||
|
except:
|
||||||
|
self.throw_buzz()
|
||||||
|
|
||||||
|
def throw_foo(self):
|
||||||
|
raise CustomException("{foo}")
|
||||||
|
|
||||||
|
def throw_buzz(self):
|
||||||
|
raise RTIOUnderflow("{buzz}")
|
||||||
|
|
||||||
|
|
||||||
|
class KernelRTIOUnderflow(EnvExperiment):
|
||||||
|
def build(self):
|
||||||
|
self.setattr_device("core")
|
||||||
|
try:
|
||||||
|
self.setattr_device("led")
|
||||||
|
except DeviceError:
|
||||||
|
self.led = self.get_device("led0")
|
||||||
|
|
||||||
|
@kernel
|
||||||
|
def run(self):
|
||||||
|
self.core.reset()
|
||||||
|
at_mu(self.core.get_rtio_counter_mu() - 1000); self.led.on()
|
||||||
|
|
||||||
|
|
||||||
|
class ExceptionFormatTest(ExperimentCase):
|
||||||
|
def test_custom_formatted_kernel_exception(self):
|
||||||
|
with self.assertLogs() as captured:
|
||||||
|
with self.assertRaisesRegex(CustomException, r"CustomException\(\d+\): \{foo\}"):
|
||||||
|
self.execute(KernelFmtException)
|
||||||
|
captured_lines = captured.output[0].split('\n')
|
||||||
|
self.assertEqual([captured_lines[0], captured_lines[-1]],
|
||||||
|
["ERROR:artiq.coredevice.comm_kernel:Couldn't format exception message", "KeyError: 'foo'"])
|
||||||
|
|
||||||
|
def test_nested_formatted_kernel_exception(self):
|
||||||
|
with self.assertLogs() as captured:
|
||||||
|
with self.assertRaisesRegex(CustomException,
|
||||||
|
re.compile(
|
||||||
|
r"CustomException\(\d+\): \{foo\}.*?RTIOUnderflow\(\d+\): \{bar\}.*?RTIOOverflow\(\d+\): \{bizz\}.*?RTIOUnderflow\(\d+\): \{buzz\}",
|
||||||
|
re.DOTALL)):
|
||||||
|
self.execute(KernelNestedFmtException)
|
||||||
|
captured_lines = captured.output[0].split('\n')
|
||||||
|
self.assertEqual([captured_lines[0], captured_lines[-1]],
|
||||||
|
["ERROR:artiq.coredevice.comm_kernel:Couldn't format exception message", "KeyError: 'foo'"])
|
||||||
|
|
||||||
|
def test_rtio_underflow(self):
|
||||||
|
with self.assertRaisesRegex(RTIOUnderflow,
|
||||||
|
re.compile(
|
||||||
|
r"RTIO underflow at channel 0x[0-9a-fA-F]*?:led\d*?, \d+? mu, slack -\d+? mu.*?RTIOUnderflow\(\d+\): RTIO underflow at channel 0x([0-9a-fA-F]+?):led\d*?, \d+? mu, slack -\d+? mu",
|
||||||
|
re.DOTALL)):
|
||||||
|
self.execute(KernelRTIOUnderflow)
|
||||||
|
|
||||||
|
|
||||||
"""
|
"""
|
||||||
Test sync in exceptions raised between host and kernel
|
Test sync in exceptions raised between host and kernel
|
||||||
@ -38,7 +122,7 @@ class _TestExceptionSync(EnvExperiment):
|
|||||||
test_exception_id_sync(id)
|
test_exception_id_sync(id)
|
||||||
|
|
||||||
|
|
||||||
class ExceptionTest(ExperimentCase):
|
class ExceptionSyncTest(ExperimentCase):
|
||||||
def test_raise_exceptions_kernel(self):
|
def test_raise_exceptions_kernel(self):
|
||||||
exp = self.create(_TestExceptionSync)
|
exp = self.create(_TestExceptionSync)
|
||||||
|
|
||||||
@ -56,4 +140,3 @@ class ExceptionTest(ExperimentCase):
|
|||||||
name = name.split('.')[-1].split(':')[-1]
|
name = name.split('.')[-1].split(':')[-1]
|
||||||
with self.assertRaises(getattr(exceptions, name)) as ctx:
|
with self.assertRaises(getattr(exceptions, name)) as ctx:
|
||||||
exp.raise_exception_host(id)
|
exp.raise_exception_host(id)
|
||||||
|
|
||||||
|
@ -86,9 +86,7 @@ Nix development environment
|
|||||||
|
|
||||||
- Enable flakes, for example by adding ``experimental-features = nix-command flakes`` to ``nix.conf``. See also the `NixOS Wiki on flakes <https://nixos.wiki/wiki/flakes>`_.
|
- Enable flakes, for example by adding ``experimental-features = nix-command flakes`` to ``nix.conf``. See also the `NixOS Wiki on flakes <https://nixos.wiki/wiki/flakes>`_.
|
||||||
- Add ``/opt`` (or your Vivado location) as an Nix sandbox, for example by adding ``extra-sandbox-paths = /opt`` to ``nix.conf``.
|
- Add ``/opt`` (or your Vivado location) as an Nix sandbox, for example by adding ``extra-sandbox-paths = /opt`` to ``nix.conf``.
|
||||||
- Create a file called ``trusted-settings.json`` in ``~/.local/share/nix/``, if it doesn't exist already. Make sure it contains the following:
|
- Make sure that you have accepted and marked as permanent the additional settings described in :ref:`installing-details`. You can check on this manually by ensuring the file ``trusted-settings.json`` in ``~/.local/share/nix/`` exists and contains the following: ::
|
||||||
|
|
||||||
::
|
|
||||||
|
|
||||||
{
|
{
|
||||||
"extra-sandbox-paths":{
|
"extra-sandbox-paths":{
|
||||||
@ -102,7 +100,7 @@ Nix development environment
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
- If using NixOS, instead make the equivalent changes to your ``configuration.nix``.
|
- If using NixOS, make the equivalent changes to your ``configuration.nix`` instead.
|
||||||
|
|
||||||
* Clone `the ARTIQ Git repository <https://github.com/m-labs/artiq>`_, or `the ARTIQ-Zynq repository <https://git.m-labs.hk/M-Labs/artiq-zynq>`__ for :ref:`Zynq devices <devices-table>` (Kasli-SoC, ZC706, or EBAZ4205). By default, you are working with the ``master`` branch, which represents the beta version and is not stable (see :doc:`releases`). Checkout the most recent release (``git checkout release-[number]``) for a stable version.
|
* Clone `the ARTIQ Git repository <https://github.com/m-labs/artiq>`_, or `the ARTIQ-Zynq repository <https://git.m-labs.hk/M-Labs/artiq-zynq>`__ for :ref:`Zynq devices <devices-table>` (Kasli-SoC, ZC706, or EBAZ4205). By default, you are working with the ``master`` branch, which represents the beta version and is not stable (see :doc:`releases`). Checkout the most recent release (``git checkout release-[number]``) for a stable version.
|
||||||
* If your Vivado installation is not in its default location ``/opt``, open ``flake.nix`` and edit it accordingly (note that the edits must be made in the main ARTIQ flake, even if you are working with Zynq, see also tip below).
|
* If your Vivado installation is not in its default location ``/opt``, open ``flake.nix`` and edit it accordingly (note that the edits must be made in the main ARTIQ flake, even if you are working with Zynq, see also tip below).
|
||||||
@ -120,7 +118,7 @@ Once you have run ``nix develop`` you are in the ARTIQ development environment.
|
|||||||
|
|
||||||
$ export PYTHONPATH=/absolute/path/to/your/artiq:$PYTHONPATH
|
$ export PYTHONPATH=/absolute/path/to/your/artiq:$PYTHONPATH
|
||||||
|
|
||||||
Note that this only applies for incremental builds. If you want to use ``nix build``, or make changes to the dependencies, look into changing the inputs of the ``flake.nix`` instead. You can do this by replacing the URL of the GitHub ARTIQ repository with ``path:/absolute/path/to/your/artiq``; remember that Nix caches dependencies, so to incorporate new changes you will need to exit the development shell, update the Nix cache with ``nix flake update``, and re-run ``nix develop``.
|
Note that this only applies for incremental builds. If you want to use ``nix build``, or make changes to the dependencies, look into changing the inputs of the ``flake.nix`` instead. You can do this by replacing the URL of the GitHub ARTIQ repository with ``path:/absolute/path/to/your/artiq``; remember that Nix pins dependencies, so to incorporate new changes you will need to exit the development shell, update the environment with ``nix flake update``, and re-run ``nix develop``.
|
||||||
|
|
||||||
Building only standard binaries
|
Building only standard binaries
|
||||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
@ -138,7 +136,7 @@ The parallel command does exist for ARTIQ-Zynq: ::
|
|||||||
|
|
||||||
$ nix develop git+https://git.m-labs.hk/m-labs/artiq-zynq\?ref=release-[number]
|
$ nix develop git+https://git.m-labs.hk/m-labs/artiq-zynq\?ref=release-[number]
|
||||||
|
|
||||||
but if you are building ARTIQ-Zynq without intention to change the source, it is not actually necessary to enter the development environment at all; Nix is capable of accessing the official flake remotely for the build itself, eliminating the requirement for any particular environment.
|
but if you are building ARTIQ-Zynq without intention to change the source, it is not actually necessary to enter the development environment at all; Nix is capable of accessing the official flake directly to set up the build, eliminating the requirement for any particular environment.
|
||||||
|
|
||||||
This is equally possible for original ARTIQ, but not as useful, as the development environment (specifically the ``#boards`` shell) is still the easiest way to access the necessary tools for flashing the board. On the other hand, Zynq boards can also be flashed by writing to the SD card directly, which requires no further special tools. As long as you have a functioning Nix/Vivado installation with flakes enabled, you can progress directly to the building instructions below.
|
This is equally possible for original ARTIQ, but not as useful, as the development environment (specifically the ``#boards`` shell) is still the easiest way to access the necessary tools for flashing the board. On the other hand, Zynq boards can also be flashed by writing to the SD card directly, which requires no further special tools. As long as you have a functioning Nix/Vivado installation with flakes enabled, you can progress directly to the building instructions below.
|
||||||
|
|
||||||
|
@ -90,7 +90,7 @@ master_doc = 'index'
|
|||||||
|
|
||||||
# General information about the project.
|
# General information about the project.
|
||||||
project = 'ARTIQ'
|
project = 'ARTIQ'
|
||||||
copyright = '2014-2024, M-Labs Limited'
|
copyright = '2014-2025, M-Labs Limited'
|
||||||
|
|
||||||
# The version info for the project you're documenting, acts as replacement for
|
# The version info for the project you're documenting, acts as replacement for
|
||||||
# |version| and |release|, also used in various other places throughout the
|
# |version| and |release|, also used in various other places throughout the
|
||||||
|
@ -1,31 +1,35 @@
|
|||||||
Installing ARTIQ
|
Installing ARTIQ
|
||||||
================
|
================
|
||||||
|
|
||||||
ARTIQ can be installed using the Nix (on Linux) or MSYS2 (on Windows) package managers. Using Conda is also possible on both platforms but not recommended.
|
M-Labs recommends installing ARTIQ through Nix (on Linux) or MSYS2 (on Windows). It is also possible to use Conda (on either platform), but this is not preferred, and likely to become unsupported in the near future.
|
||||||
|
|
||||||
Installing via Nix (Linux)
|
Installing via Nix (Linux)
|
||||||
--------------------------
|
--------------------------
|
||||||
|
|
||||||
First install the Nix package manager. Some distributions provide a package for it; otherwise, it can be installed via the script on the `Nix website <http://nixos.org/nix/>`_. Make sure you get Nix version 2.4 or higher. Prefer a single-user installation for simplicity.
|
First, install the Nix package manager. Some distributions provide a package for it. Otherwise, use the official install script, as described on the `Nix website <https://nixos.org/download/>`_, e.g.: ::
|
||||||
|
|
||||||
Once Nix is installed, enable flakes, for example by running: ::
|
$ sh <(curl -L https://nixos.org/nix/install) --no-daemon
|
||||||
|
|
||||||
|
Prefer the single-user installation for simplicity. Enable `Nix flakes <https://nixos.wiki/wiki/flakes>`_, for example by running: ::
|
||||||
|
|
||||||
$ mkdir -p ~/.config/nix
|
$ mkdir -p ~/.config/nix
|
||||||
$ echo "experimental-features = nix-command flakes" >> ~/.config/nix/nix.conf
|
$ echo "experimental-features = nix-command flakes" >> ~/.config/nix/nix.conf
|
||||||
|
|
||||||
See also the different options for enabling flakes on `the NixOS wiki <https://nixos.wiki/wiki/flakes>`_.
|
User environment installation
|
||||||
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
The easiest way to obtain ARTIQ is to install it into the user environment with ::
|
There are few options for accessing ARTIQ through Nix. The easiest way is to install it into the user environment: ::
|
||||||
|
|
||||||
$ nix profile install git+https://github.com/m-labs/artiq.git
|
$ nix profile install git+https://github.com/m-labs/artiq.git
|
||||||
|
|
||||||
Answer "Yes" to the questions about setting Nix configuration options (for more details see 'Troubleshooting' below.) You should now have a minimal installation of ARTIQ, where the usual front-end commands (:mod:`~artiq.frontend.artiq_run`, :mod:`~artiq.frontend.artiq_master`, :mod:`~artiq.frontend.artiq_dashboard`, etc.) are all available to you.
|
Answer "Yes" to the questions about setting Nix configuration options (for more details see :ref:`installing-details` below.) You should now have a minimal installation of ARTIQ, where the usual front-end commands (:mod:`~artiq.frontend.artiq_run`, :mod:`~artiq.frontend.artiq_master`, :mod:`~artiq.frontend.artiq_dashboard`, etc.) are all available to you.
|
||||||
|
|
||||||
This installation is however quite limited, as Nix creates a dedicated Python environment for the ARTIQ commands alone. This means that other useful Python packages, which ARTIQ is not dependent on but which you may want to use in your experiments (pandas, matplotlib...), are not available.
|
This installation is however relatively limited. Without further instructions, Nix takes its cues from the main ARTIQ flake (the ``flake.nix`` file at the root of the repository linked in the command) and creates a dedicated Python environment for the ARTIQ commands alone. This means that other useful Python packages, which are not necessary to run ARTIQ but which you might want to use in experiments (pandas, matplotlib...), are not available.
|
||||||
|
|
||||||
Installing multiple packages and making them visible to the ARTIQ commands requires using the Nix language. Create an empty directory with a file ``flake.nix`` with the following contents:
|
Flake custom environments
|
||||||
|
^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
::
|
Modifying the environment and making additional packages visible to the ARTIQ commands requires using the Nix language and writing your own flake. Create an empty directory with a file ``flake.nix`` containing the following: ::
|
||||||
|
|
||||||
{
|
{
|
||||||
inputs.extrapkg.url = "git+https://git.m-labs.hk/M-Labs/artiq-extrapkg.git";
|
inputs.extrapkg.url = "git+https://git.m-labs.hk/M-Labs/artiq-extrapkg.git";
|
||||||
@ -34,13 +38,14 @@ Installing multiple packages and making them visible to the ARTIQ commands requi
|
|||||||
pkgs = extrapkg.pkgs;
|
pkgs = extrapkg.pkgs;
|
||||||
artiq = extrapkg.packages.x86_64-linux;
|
artiq = extrapkg.packages.x86_64-linux;
|
||||||
in {
|
in {
|
||||||
defaultPackage.x86_64-linux = pkgs.buildEnv {
|
# This section defines the new environment
|
||||||
|
packages.x86_64-linux.default = pkgs.buildEnv {
|
||||||
name = "artiq-env";
|
name = "artiq-env";
|
||||||
paths = [
|
paths = [
|
||||||
# ========================================
|
# ========================================
|
||||||
# EDIT BELOW
|
# ADD PACKAGES BELOW
|
||||||
# ========================================
|
# ========================================
|
||||||
(pkgs.python3.withPackages(ps: [
|
(pkgs.python3.withPackages(ps : [
|
||||||
# List desired Python packages here.
|
# List desired Python packages here.
|
||||||
artiq.artiq
|
artiq.artiq
|
||||||
#ps.paramiko # needed if and only if flashing boards remotely (artiq_flash -H)
|
#ps.paramiko # needed if and only if flashing boards remotely (artiq_flash -H)
|
||||||
@ -60,35 +65,42 @@ Installing multiple packages and making them visible to the ARTIQ commands requi
|
|||||||
#ps.qiskit
|
#ps.qiskit
|
||||||
# Note that NixOS also provides packages ps.numpy and ps.scipy, but it is
|
# Note that NixOS also provides packages ps.numpy and ps.scipy, but it is
|
||||||
# not necessary to explicitly add these, since they are dependencies of
|
# not necessary to explicitly add these, since they are dependencies of
|
||||||
# ARTIQ and available with an ARTIQ install anyway.
|
# ARTIQ and incorporated with an ARTIQ install anyway.
|
||||||
]))
|
]))
|
||||||
|
# List desired non-Python packages here
|
||||||
|
# Additional NDSPs can be included:
|
||||||
#artiq.korad_ka3005p
|
#artiq.korad_ka3005p
|
||||||
#artiq.novatech409b
|
#artiq.novatech409b
|
||||||
# List desired non-Python packages here
|
|
||||||
# Other potentially interesting non-Python packages from the NixOS package collection:
|
# Other potentially interesting non-Python packages from the NixOS package collection:
|
||||||
#pkgs.gtkwave
|
#pkgs.gtkwave
|
||||||
#pkgs.spyder
|
#pkgs.spyder
|
||||||
#pkgs.R
|
#pkgs.R
|
||||||
#pkgs.julia
|
#pkgs.julia
|
||||||
# ========================================
|
# ========================================
|
||||||
# EDIT ABOVE
|
# ADD PACKAGES ABOVE
|
||||||
# ========================================
|
# ========================================
|
||||||
];
|
];
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
# This section configures additional settings to be able to use M-Labs binary caches
|
||||||
nixConfig = { # work around https://github.com/NixOS/nix/issues/6771
|
nixConfig = { # work around https://github.com/NixOS/nix/issues/6771
|
||||||
extra-trusted-public-keys = "nixbld.m-labs.hk-1:5aSRVA5b320xbNvu30tqxVPXpld73bhtOeH6uAjRyHc=";
|
extra-trusted-public-keys = "nixbld.m-labs.hk-1:5aSRVA5b320xbNvu30tqxVPXpld73bhtOeH6uAjRyHc=";
|
||||||
extra-substituters = "https://nixbld.m-labs.hk";
|
extra-substituters = "https://nixbld.m-labs.hk";
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
You can now spawn a shell containing these packages by running ``$ nix shell`` in the directory containing the ``flake.nix``. This should make both the ARTIQ commands and all the additional packages available to you. You can exit the shell with Control+D or with the command ``exit``. A first execution of ``$ nix shell`` may take some time, but for any future repetitions Nix will use cached packages and startup should be much faster.
|
To spawn a shell in this environment, navigate to the directory containing the ``flake.nix`` and run: ::
|
||||||
|
|
||||||
You might be interested in creating multiple directories containing different ``flake.nix`` files which represent different sets of packages for different purposes. If you are familiar with Conda, using Nix in this way is similar to having multiple Conda environments.
|
$ nix shell
|
||||||
|
|
||||||
|
The resulting shell will have access to ARTIQ as well as any additional packages you may have added. You can exit this shell at any time with CTRL+D or with the command ``exit``. Note that a first execution of ``nix shell`` on a given flake may take some time; repetitions of the same command will use stored versions of packages and run much more quickly.
|
||||||
|
|
||||||
|
You might be interested in creating multiple directories containing separate ``flake.nix`` files to represent different sets of packages for different purposes. If you are familiar with Conda, using Nix in this way is similar to having multiple Conda environments.
|
||||||
|
|
||||||
To find more packages you can browse the `Nix package search <https://search.nixos.org/packages>`_ website. If your favorite package is not available with Nix, contact M-Labs using the helpdesk@ email.
|
To find more packages you can browse the `Nix package search <https://search.nixos.org/packages>`_ website. If your favorite package is not available with Nix, contact M-Labs using the helpdesk@ email.
|
||||||
|
|
||||||
.. note::
|
.. note::
|
||||||
|
|
||||||
If you find you prefer using flakes to your original ``nix profile`` installation, you can remove it from your system by running: ::
|
If you find you prefer using flakes to your original ``nix profile`` installation, you can remove it from your system by running: ::
|
||||||
|
|
||||||
$ nix profile list
|
$ nix profile list
|
||||||
@ -97,35 +109,39 @@ To find more packages you can browse the `Nix package search <https://search.nix
|
|||||||
|
|
||||||
$ nix profile remove [index]
|
$ nix profile remove [index]
|
||||||
|
|
||||||
While using flakes, ARTIQ is not 'installed' as such in any permanent way. However, Nix will preserve independent cached packages in ``/nix/store`` for each flake, which over time or with many different flakes and versions can take up large amounts of storage space. To clear this cache, run ``$ nix-collect-garbage``.
|
While using flakes, ARTIQ is not strictly 'installed' in a permanent way. However, Nix will keep collected packages in ``/nix/store`` for each flake, which over time or with many different flakes and versions can take up large amounts of storage space. To clear this cache, run ``nix-collect-garbage``. (After a garbage collection, ``nix shell`` will require some time again when first used).
|
||||||
|
|
||||||
.. _installing-troubleshooting:
|
.. _installing-details:
|
||||||
|
|
||||||
Troubleshooting
|
Installation details
|
||||||
^^^^^^^^^^^^^^^
|
^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
"Do you want to allow configuration setting... (y/N)?"
|
"Do you want to allow configuration setting... (y/N)?"
|
||||||
""""""""""""""""""""""""""""""""""""""""""""""""""""""
|
""""""""""""""""""""""""""""""""""""""""""""""""""""""
|
||||||
|
|
||||||
When installing and initializing ARTIQ using commands like ``nix shell``, ``nix develop``, or ``nix profile install``, you may encounter prompts to modify certain configuration settings. These settings correspond to the ``nixConfig`` flag within the ARTIQ flake: ::
|
When installing and initializing ARTIQ using commands like ``nix shell``, ``nix develop``, or ``nix profile install``, you may encounter prompts to modify certain configuration settings. These settings correspond to the ``nixConfig`` section in the ARTIQ flake: ::
|
||||||
|
|
||||||
do you want to allow configuration setting 'extra-sandbox-paths' to be set to '/opt' (y/N)?
|
do you want to allow configuration setting 'extra-sandbox-paths' to be set to '/opt' (y/N)?
|
||||||
do you want to allow configuration setting 'extra-substituters' to be set to 'https://nixbld.m-labs.hk' (y/N)?
|
do you want to allow configuration setting 'extra-substituters' to be set to 'https://nixbld.m-labs.hk' (y/N)?
|
||||||
do you want to allow configuration setting 'extra-trusted-public-keys' to be set to 'nixbld.m-labs.hk-1:5aSRVA5b320xbNvu30tqxVPXpld73bhtOeH6uAjRyHc=' (y/N)?
|
do you want to allow configuration setting 'extra-trusted-public-keys' to be set to 'nixbld.m-labs.hk-1:5aSRVA5b320xbNvu30tqxVPXpld73bhtOeH6uAjRyHc=' (y/N)?
|
||||||
|
|
||||||
We recommend accepting these settings by responding with ``y``. If asked to permanently mark these values as trusted, choose ``y`` again. This action saves the configuration to ``~/.local/share/nix/trusted-settings.json``, allowing future prompts to be bypassed.
|
.. note::
|
||||||
|
The first is necessary in order to be able to use Vivado to build ARTIQ gateware (e.g. :doc:`building_developing`). The latter two are necessary in order to use the M-Labs nixbld server as a binary cache; refusing these will result in Nix attempting to build these binaries from source, which is possible to do, but requires a considerable amount of time (on the order of hours) on most machines.
|
||||||
|
|
||||||
Alternatively, you can also use the option `accept-flake-config <https://nix.dev/manual/nix/stable/command-ref/conf-file#conf-accept-flake-config>`_ by appending ``--accept-flake-config`` to your nix command, for example: ::
|
It is recommended to accept all three settings by responding with ``y``. If asked to permanently mark these values as trusted, choose ``y`` again. This action saves the configuration to ``~/.local/share/nix/trusted-settings.json``, allowing future prompts to be bypassed.
|
||||||
|
|
||||||
nix develop --accept-flake-config
|
Alternatively, you can also use the option `accept-flake-config <https://nix.dev/manual/nix/stable/command-ref/conf-file#conf-accept-flake-config>`_ on a per-command basis by appending ``--accept-flake-config``, for example: ::
|
||||||
|
|
||||||
Or add the option to ``~/.config/nix/nix.conf`` to make the setting more permanent: ::
|
nix shell --accept-flake-config
|
||||||
|
|
||||||
|
Or add the option to ``~/.config/nix/nix.conf`` to make the setting apply to all commands by default: ::
|
||||||
|
|
||||||
extra-experimental-features = flakes
|
extra-experimental-features = flakes
|
||||||
accept-flake-config = true
|
accept-flake-config = true
|
||||||
|
|
||||||
.. note::
|
.. note::
|
||||||
Should you wish to revert to the default settings, you can do so by editing the appropriate options in the aforementioned configuration files.
|
|
||||||
|
Should you wish to revert to the default settings, you can do so at any time by editing the appropriate options in the aforementioned configuration files.
|
||||||
|
|
||||||
"Ignoring untrusted substituter, you are not a trusted user"
|
"Ignoring untrusted substituter, you are not a trusted user"
|
||||||
""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
|
""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
|
||||||
@ -135,7 +151,7 @@ If the following message displays when running ``nix shell`` or ``nix develop``
|
|||||||
warning: ignoring untrusted substituter 'https://nixbld.m-labs.hk', you are not a trusted user.
|
warning: ignoring untrusted substituter 'https://nixbld.m-labs.hk', you are not a trusted user.
|
||||||
Run `man nix.conf` for more information on the `substituters` configuration option.
|
Run `man nix.conf` for more information on the `substituters` configuration option.
|
||||||
|
|
||||||
and Nix proceeds to build some packages from source, this means that you are using `multi-user mode <https://nix.dev/manual/nix/stable/installation/multi-user>`_ in Nix, which may be the case for example when Nix is installed via ``pacman`` in Arch Linux. By default, users accessing Nix in multi-user mode are "unprivileged" and cannot use untrusted substituters. To change this, edit ``/etc/nix/nix.conf`` and add the following line (or append to the key if the key already exists): ::
|
and Nix tries to build some packages from source, this means that you are using `multi-user mode <https://nix.dev/manual/nix/stable/installation/multi-user>`_ in Nix, which may be the case for example when Nix is installed via ``pacman`` in Arch Linux. By default, users accessing Nix in multi-user mode are "unprivileged" and cannot use untrusted substituters. To change this, edit ``/etc/nix/nix.conf`` and add the following line (or append to the key if the key already exists): ::
|
||||||
|
|
||||||
trusted-substituters = https://nixbld.m-labs.hk
|
trusted-substituters = https://nixbld.m-labs.hk
|
||||||
|
|
||||||
@ -143,9 +159,9 @@ This will add the substituter as a trusted substituter for all users using Nix.
|
|||||||
|
|
||||||
Alternatively, add the following line: ::
|
Alternatively, add the following line: ::
|
||||||
|
|
||||||
trusted-users = <username> # Replace <username> with the user invoking `nix`
|
trusted-users = <username> # Replace <username> with your username
|
||||||
|
|
||||||
This will set your user as a trusted user, allowing the use of any untrusted substituters.
|
This will set your user as a trusted user, allowing you to specify untrusted substituters.
|
||||||
|
|
||||||
.. warning::
|
.. warning::
|
||||||
|
|
||||||
@ -216,9 +232,9 @@ Upgrading ARTIQ
|
|||||||
Upgrading with Nix
|
Upgrading with Nix
|
||||||
^^^^^^^^^^^^^^^^^^
|
^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
Run ``$ nix profile upgrade`` if you installed ARTIQ into your user profile. If you used a ``flake.nix`` shell environment, make a back-up copy of the ``flake.lock`` file to enable rollback, then run ``$ nix flake update`` and re-enter the environment with ``$ nix shell``.
|
Run ``$ nix profile upgrade`` if you installed ARTIQ into your user profile. If you use a ``flake.nix`` shell environment, make a back-up copy of the ``flake.lock`` file to enable rollback, then run ``$ nix flake update`` and re-enter the environment with ``$ nix shell``. If you use multiple flakes, each has its own ``flake.lock`` and can be updated or rolled back separately.
|
||||||
|
|
||||||
To rollback to the previous version, respectively use ``$ nix profile rollback`` or restore the backed-up version of the ``flake.lock`` file.
|
To rollback to the previous version, respectively use ``$ nix profile rollback`` or restore the backed-up versions of the ``flake.lock`` files.
|
||||||
|
|
||||||
Upgrading with MSYS2
|
Upgrading with MSYS2
|
||||||
^^^^^^^^^^^^^^^^^^^^
|
^^^^^^^^^^^^^^^^^^^^
|
||||||
|
@ -23,4 +23,4 @@ Core technologies employed include `Python <https://www.python.org/>`_, `Migen <
|
|||||||
|
|
||||||
`Cite ARTIQ <http://dx.doi.org/10.5281/zenodo.51303>`_ as ``Bourdeauducq, Sébastien et al. (2016). ARTIQ 1.0. Zenodo. 10.5281/zenodo.51303``.
|
`Cite ARTIQ <http://dx.doi.org/10.5281/zenodo.51303>`_ as ``Bourdeauducq, Sébastien et al. (2016). ARTIQ 1.0. Zenodo. 10.5281/zenodo.51303``.
|
||||||
|
|
||||||
Copyright (C) 2014-2024 M-Labs Limited. Licensed under GNU LGPL version 3+.
|
Copyright (C) 2014-2025 M-Labs Limited. Licensed under GNU LGPL version 3+.
|
||||||
|
26
flake.lock
generated
26
flake.lock
generated
@ -11,11 +11,11 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1720768567,
|
"lastModified": 1734270714,
|
||||||
"narHash": "sha256-3VoK7o5MtHtbHLrc6Pv+eQWFtaz5Gd/YWyV5TD3c5Ss=",
|
"narHash": "sha256-7bzGn/hXLIsLQHGQsvo+uoIFUrw9DjXSlMC449BY4ME=",
|
||||||
"owner": "m-labs",
|
"owner": "m-labs",
|
||||||
"repo": "artiq-comtools",
|
"repo": "artiq-comtools",
|
||||||
"rev": "f93570d8f2ed5a3cfb3e1c16ab00f2540551e994",
|
"rev": "7e3152314af8f5987370e33b347b2ec2697567ed",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@ -44,11 +44,11 @@
|
|||||||
},
|
},
|
||||||
"nixpkgs": {
|
"nixpkgs": {
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1733940404,
|
"lastModified": 1736012469,
|
||||||
"narHash": "sha256-Pj39hSoUA86ZePPF/UXiYHHM7hMIkios8TYG29kQT4g=",
|
"narHash": "sha256-/qlNWm/IEVVH7GfgAIyP6EsVZI6zjAx1cV5zNyrs+rI=",
|
||||||
"owner": "NixOS",
|
"owner": "NixOS",
|
||||||
"repo": "nixpkgs",
|
"repo": "nixpkgs",
|
||||||
"rev": "5d67ea6b4b63378b9c13be21e2ec9d1afc921713",
|
"rev": "8f3e1f807051e32d8c95cd12b9b421623850a34d",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@ -113,11 +113,11 @@
|
|||||||
"src-migen": {
|
"src-migen": {
|
||||||
"flake": false,
|
"flake": false,
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1727677091,
|
"lastModified": 1735131698,
|
||||||
"narHash": "sha256-Zg3SQnTwMM/VkOGKogbPyuCC2NhLy8HB2SPEUWWNgCU=",
|
"narHash": "sha256-P4vaF+9iVekRAC2/mc9G7IwI6baBpPAxiDQ8uye4sAs=",
|
||||||
"owner": "m-labs",
|
"owner": "m-labs",
|
||||||
"repo": "migen",
|
"repo": "migen",
|
||||||
"rev": "c19ae9f8ae162ffe2d310a92bfce53ac2a821bc8",
|
"rev": "4c2ae8dfeea37f235b52acb8166f12acaaae4f7c",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@ -129,11 +129,11 @@
|
|||||||
"src-misoc": {
|
"src-misoc": {
|
||||||
"flake": false,
|
"flake": false,
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1729234629,
|
"lastModified": 1736302987,
|
||||||
"narHash": "sha256-TLsTCXV5AC2xh+bS7EhBVBKqdqIU3eKrnlWcFF9LtAM=",
|
"narHash": "sha256-DMbaAxjtyZDlA20FTaObalH6ov2roE5Gsh6lVdnzPVY=",
|
||||||
"ref": "refs/heads/master",
|
"ref": "refs/heads/master",
|
||||||
"rev": "6085a312bca26adeca6584e37d08c8ba2e1d6e38",
|
"rev": "adb3f111750cb458f7e390bd242deb1d86ad69cb",
|
||||||
"revCount": 2460,
|
"revCount": 2463,
|
||||||
"submodules": true,
|
"submodules": true,
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/m-labs/misoc.git"
|
"url": "https://github.com/m-labs/misoc.git"
|
||||||
|
@ -604,11 +604,15 @@
|
|||||||
# Read "Ok" line when remote successfully locked
|
# Read "Ok" line when remote successfully locked
|
||||||
read LOCK_OK
|
read LOCK_OK
|
||||||
|
|
||||||
|
export ARTIQ_ROOT=`python -c "import artiq; print(artiq.__path__[0])"`/examples/kc705_nist_clock
|
||||||
|
export ARTIQ_LOW_LATENCY=1
|
||||||
|
|
||||||
|
artiq_rtiomap --device-db $ARTIQ_ROOT/device_db.py device_map.bin
|
||||||
|
artiq_mkfs -s ip `python -c "import artiq.examples.kc705_nist_clock.device_db as ddb; print(ddb.core_addr)"`/24 -f device_map device_map.bin kc705_nist_clock.config
|
||||||
|
artiq_flash -t kc705 -H rpi-1 storage -f kc705_nist_clock.config
|
||||||
artiq_flash -t kc705 -H rpi-1 -d ${packages.x86_64-linux.artiq-board-kc705-nist_clock}
|
artiq_flash -t kc705 -H rpi-1 -d ${packages.x86_64-linux.artiq-board-kc705-nist_clock}
|
||||||
sleep 30
|
sleep 30
|
||||||
|
|
||||||
export ARTIQ_ROOT=`python -c "import artiq; print(artiq.__path__[0])"`/examples/kc705_nist_clock
|
|
||||||
export ARTIQ_LOW_LATENCY=1
|
|
||||||
python -m unittest discover -v artiq.test.coredevice
|
python -m unittest discover -v artiq.test.coredevice
|
||||||
)
|
)
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user