diff --git a/artiq/coredevice/ad9834.py b/artiq/coredevice/ad9834.py index 306aa712d..6166be516 100644 --- a/artiq/coredevice/ad9834.py +++ b/artiq/coredevice/ad9834.py @@ -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/app-notes/an-1070.pdf +from numpy import int32 + from artiq.coredevice import spi2 as spi from artiq.experiment import * from artiq.language.core import * @@ -64,167 +66,21 @@ class AD9834: self.ctrl_reg = 0x0000 # Reset control register @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: - - Configuring the SPI bus parameters (clock polarity, data width, and frequency). - - Putting the AD9834 into a reset state to ensure proper initialization. + This method sends a 16-bit data word to the AD9834 via the SPI bus. The input + data is left-shifted by 16 bits to ensure proper alignment for the SPI controller, + 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 - 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 is used internally by other methods to update the control registers + and frequency settings of the AD9834. It should not be called directly unless + low-level register manipulation is required. - This method should be called before any other operations are performed - on the AD9834 to ensure that the device is in a known state. + :param data: The 16-bit word to be sent to the AD9834. """ - self.bus.set_config(spi.SPI_CLK_POLARITY | spi.SPI_END, 16, self.spi_freq, 1) - 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) + self.bus.write(data << 16) @kernel def enable_reset(self): @@ -255,6 +111,158 @@ class AD9834: self.ctrl_reg &= ~AD9834_RESET 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 def sleep(self, dac_pd: bool = False, clk_dis: bool = False): """ @@ -329,19 +337,13 @@ class AD9834: self.ctrl_reg &= ~AD9834_OPBITEN elif msb_2: self.ctrl_reg |= AD9834_OPBITEN - self.ctrl_reg &= ~AD9834_MODE - self.ctrl_reg &= ~AD9834_SIGN_PIB - self.ctrl_reg &= ~AD9834_DIV2 + self.ctrl_reg &= ~(AD9834_MODE | AD9834_SIGN_PIB | AD9834_DIV2) elif msb: - self.ctrl_reg |= AD9834_OPBITEN - self.ctrl_reg &= ~AD9834_MODE - self.ctrl_reg &= ~AD9834_SIGN_PIB - self.ctrl_reg |= AD9834_DIV2 + self.ctrl_reg |= AD9834_OPBITEN | AD9834_DIV2 + self.ctrl_reg &= ~(AD9834_MODE | AD9834_SIGN_PIB) 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_SIGN_PIB - self.ctrl_reg |= AD9834_DIV2 else: self.ctrl_reg &= ~AD9834_OPBITEN @@ -380,18 +382,56 @@ class AD9834: self.write(self.ctrl_reg) @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 - data is left-shifted by 16 bits to ensure proper alignment for the SPI controller, - allowing for accurate processing of the command by the AD9834. + This method updates the specified frequency and phase registers with the provided + machine units, selects the corresponding registers, and enables the output. - This method is used internally by other methods to update the control registers - and frequency settings of the AD9834. It should not be called directly unless - low-level register manipulation is required. - - :param data: The 16-bit word to be sent to the AD9834. + :param freq_word: Frequency tuning word (28-bit). + :param phase_word: Phase tuning word (12-bit). + :param freq_reg: Frequency register to write to (0 or 1). + :param phase_reg: Phase register to write to (0 or 1). """ - 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) diff --git a/artiq/test/coredevice/test_ad9834.py b/artiq/test/coredevice/test_ad9834.py index 0a1eadb36..4eb3d590c 100644 --- a/artiq/test/coredevice/test_ad9834.py +++ b/artiq/test/coredevice/test_ad9834.py @@ -11,8 +11,6 @@ from artiq.coredevice.ad9834 import ( AD9834_SIGN_PIB, AD9834_SLEEP1, AD9834_SLEEP12, - FREQ_REGS, - PHASE_REGS, ) from artiq.experiment import * from artiq.language.units import MHz @@ -34,165 +32,251 @@ class AD9834Exp(EnvExperiment): @kernel def init(self): - self.core.break_realtime() + self.core.reset() self.dev.init() self.set_dataset("spi_freq", self.dev.spi_freq) self.set_dataset("clk_freq", self.dev.clk_freq) self.set_dataset("ctrl_reg", self.dev.ctrl_reg) @kernel - def set_frequency_reg_fail1(self): - self.core.break_realtime() - frequency = 10 * MHz - self.dev.set_frequency_reg(19, frequency) + def frequency_to_ftw_fail(self): + self.core.reset() + self.dev.init() + self.dev.frequency_to_ftw(37.6 * MHz) @kernel - def set_frequency_reg_fail2(self): - self.core.break_realtime() - self.dev.set_frequency_reg(FREQ_REGS[0], 37.6 * MHz) + def turns_to_phase_fail(self): + self.core.reset() + 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 def set_frequency_reg(self): - self.core.break_realtime() + self.core.reset() 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) @kernel def set_frequency_reg_msb(self): - self.core.break_realtime() + self.core.reset() self.dev.init() 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) @kernel def set_frequency_reg_lsb(self): - self.core.break_realtime() + self.core.reset() self.dev.init() 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) @kernel def select_frequency_reg_0(self): - self.core.break_realtime() - self.dev.ctrl_reg |= AD9834_FSEL | AD9834_PIN_SW - self.dev.select_frequency_reg(FREQ_REGS[0]) - self.set_dataset("ctrl_reg", self.dev.ctrl_reg) + self.core.reset() + self.dev.init() + self.dev.select_frequency_reg(0) + self.set_dataset("ctrl_reg", self.dev.ctrl_reg & ~(AD9834_FSEL | AD9834_PIN_SW)) @kernel def select_frequency_reg_1(self): - self.core.break_realtime() - self.dev.ctrl_reg |= AD9834_PIN_SW - self.dev.select_frequency_reg(FREQ_REGS[1]) - self.set_dataset("ctrl_reg", self.dev.ctrl_reg) + self.core.reset() + self.dev.init() + self.dev.select_frequency_reg(1) + self.set_dataset("ctrl_reg", (self.dev.ctrl_reg | AD9834_FSEL) & ~AD9834_PIN_SW) @kernel def set_phase_reg_fail(self): - self.core.break_realtime() + self.core.reset() + self.dev.init() self.dev.set_phase_reg(19, 0x123) @kernel def set_phase_reg(self): - self.core.break_realtime() + self.core.reset() 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) @kernel def select_phase_reg_0(self): - self.core.break_realtime() - self.dev.ctrl_reg |= AD9834_PSEL | AD9834_PIN_SW - self.dev.select_phase_reg(PHASE_REGS[0]) - self.set_dataset("ctrl_reg", self.dev.ctrl_reg) + self.core.reset() + self.dev.init() + self.dev.select_phase_reg(0) + self.set_dataset("ctrl_reg", self.dev.ctrl_reg & ~(AD9834_PSEL | AD9834_PIN_SW)) @kernel def select_phase_reg_1(self): - self.core.break_realtime() - self.dev.ctrl_reg |= AD9834_PIN_SW - self.dev.select_phase_reg(PHASE_REGS[1]) - self.set_dataset("ctrl_reg", self.dev.ctrl_reg) - - @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) + self.core.reset() + self.dev.init() + self.dev.select_phase_reg(1) + self.set_dataset("ctrl_reg", (self.dev.ctrl_reg | AD9834_PSEL) & ~AD9834_PIN_SW) @kernel def sleep_dac_powerdown(self): - self.core.break_realtime() + self.core.reset() + self.dev.init() self.dev.sleep(dac_pd=True) self.set_dataset("ctrl_reg", self.dev.ctrl_reg) @kernel def sleep_internal_clk_disable(self): - self.core.break_realtime() + self.core.reset() + self.dev.init() self.dev.sleep(clk_dis=True) self.set_dataset("ctrl_reg", self.dev.ctrl_reg) @kernel def sleep(self): - self.core.break_realtime() + self.core.reset() + self.dev.init() self.dev.sleep(dac_pd=True, clk_dis=True) self.set_dataset("ctrl_reg", self.dev.ctrl_reg) @kernel def awake(self): - self.core.break_realtime() - self.dev.ctrl_reg |= AD9834_SLEEP1 | AD9834_SLEEP12 - self.dev.sleep() - self.set_dataset("ctrl_reg", self.dev.ctrl_reg) + self.core.reset() + self.dev.init() + self.dev.awake() + self.set_dataset( + "ctrl_reg", self.dev.ctrl_reg & ~(AD9834_SLEEP1 | AD9834_SLEEP12) + ) @kernel def sign_bit_high_z(self): - self.core.break_realtime() - self.dev.ctrl_reg |= AD9834_OPBITEN + self.core.reset() + self.dev.init() 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 def sign_bit_msb_2(self): - self.core.break_realtime() - self.dev.ctrl_reg |= AD9834_MODE | AD9834_SIGN_PIB | AD9834_DIV2 + self.core.reset() + self.dev.init() 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 def sign_bit_msb(self): - self.core.break_realtime() - self.dev.ctrl_reg |= AD9834_MODE | AD9834_SIGN_PIB + self.core.reset() + self.dev.init() 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 def sign_bit_comp_out(self): - self.core.break_realtime() - self.dev.ctrl_reg |= AD9834_MODE + self.core.reset() + self.dev.init() 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 def enable_triangular_waveform(self): - self.core.break_realtime() - self.dev.ctrl_reg |= AD9834_OPBITEN + self.core.reset() + self.dev.init() 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 def disable_triangular_waveform(self): - self.core.break_realtime() - self.dev.ctrl_reg |= AD9834_MODE + self.core.reset() + self.dev.init() 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): @@ -208,21 +292,27 @@ class AD9834Test(ExperimentCase): self.assertEqual(clk_freq, 75 * MHz) self.assertEqual(ctrl_reg, 0x0000 | AD9834_RESET) - def test_set_frequency_reg_fail(self): - with self.assertRaises(ValueError): - self.execute(AD9834Exp, "set_frequency_reg_fail1") + def test_frequency_to_ftw_fail(self): 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): self.execute(AD9834Exp, "set_frequency_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): self.execute(AD9834Exp, "set_frequency_reg_msb") 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): self.execute(AD9834Exp, "set_frequency_reg_lsb") @@ -232,15 +322,15 @@ class AD9834Test(ExperimentCase): def test_select_frequency_reg_0(self): self.execute(AD9834Exp, "select_frequency_reg_0") 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): self.execute(AD9834Exp, "select_frequency_reg_1") 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): - with self.assertRaises(ValueError): + with self.assertRaises(AssertionError): self.execute(AD9834Exp, "set_phase_reg_fail") def test_set_phase_reg(self): @@ -251,71 +341,85 @@ class AD9834Test(ExperimentCase): def test_select_phase_reg_0(self): self.execute(AD9834Exp, "select_phase_reg_0") 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): self.execute(AD9834Exp, "select_phase_reg_1") ctrl_reg = self.dataset_mgr.get("ctrl_reg") - self.assertEqual(ctrl_reg, 0x0000 | AD9834_PSEL) - - 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) + self.assertEqual(ctrl_reg, 0x0000 | AD9834_PSEL | AD9834_RESET) def test_sleep_dac_powerdown(self): self.execute(AD9834Exp, "sleep_dac_powerdown") 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): self.execute(AD9834Exp, "sleep_internal_clk_disable") 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): self.execute(AD9834Exp, "sleep") 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): self.execute(AD9834Exp, "awake") 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): self.execute(AD9834Exp, "sign_bit_high_z") 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): self.execute(AD9834Exp, "sign_bit_msb_2") 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): self.execute(AD9834Exp, "sign_bit_msb") 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): self.execute(AD9834Exp, "sign_bit_comp_out") ctrl_reg = self.dataset_mgr.get("ctrl_reg") 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): self.execute(AD9834Exp, "enable_triangular_waveform") 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): self.execute(AD9834Exp, "disable_triangular_waveform") 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")