2
0
mirror of https://github.com/m-labs/artiq.git synced 2024-12-24 10:54:02 +08:00

add AD9834 core device driver (#2596)

This commit is contained in:
newell 2024-10-16 23:14:45 -07:00 committed by GitHub
parent 700812471c
commit 30b9479437
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 752 additions and 3 deletions

View File

@ -20,6 +20,7 @@ ARTIQ-9 (Unreleased)
* Compiler can now give automatic suggestions for ``kernel_invariants``.
* Idle kernels now restart when written with ``artiq_coremgmt`` and stop when erased/removed from config.
* New support for the EBAZ4205 Zynq-SoC control card.
* New core device driver for the AD9834 DDS, tested with the ZonRi Technology Co., Ltd. AD9834-Module.
ARTIQ-8
-------

397
artiq/coredevice/ad9834.py Normal file
View File

@ -0,0 +1,397 @@
"""
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 artiq.coredevice import spi2 as spi
from artiq.experiment import *
from artiq.language.core import *
from artiq.language.types import *
from artiq.language.units import *
AD9834_B28 = 1 << 13
AD9834_HLB = 1 << 12
AD9834_FSEL = 1 << 11
AD9834_PSEL = 1 << 10
AD9834_PIN_SW = 1 << 9
AD9834_RESET = 1 << 8
AD9834_SLEEP1 = 1 << 7
AD9834_SLEEP12 = 1 << 6
AD9834_OPBITEN = 1 << 5
AD9834_SIGN_PIB = 1 << 4
AD9834_DIV2 = 1 << 3
AD9834_MODE = 1 << 1
AD9834_FREQ_REG_0 = 0b01 << 14
AD9834_FREQ_REG_1 = 0b10 << 14
FREQ_REGS = [AD9834_FREQ_REG_0, AD9834_FREQ_REG_1]
AD9834_PHASE_REG = 0b11 << 14
AD9834_PHASE_REG_0 = AD9834_PHASE_REG | (0 << 13)
AD9834_PHASE_REG_1 = AD9834_PHASE_REG | (1 << 13)
PHASE_REGS = [AD9834_PHASE_REG_0, AD9834_PHASE_REG_1]
class AD9834:
"""
AD9834 DDS driver.
This class provides control for the DDS AD9834.
The driver utilizes bit-controlled :const:`AD9834_FSEL`, :const:`AD9834_PSEL`, and
:const:`AD9834_RESET`. To pin control ``FSELECT``, ``PSELECT``, and ``RESET`` set
:const:`AD9834_PIN_SW`. The ``ctrl_reg`` attribute is used to maintain the state of
the control register, enabling persistent management of various configurations.
:param spi_device: SPI bus device name.
:param spi_freq: SPI bus clock frequency (default: 10 MHz, max: 40 MHz).
:param clk_freq: DDS clock frequency (default: 75 MHz).
:param core_device: Core device name (default: "core").
"""
kernel_invariants = {"core", "bus", "spi_freq", "clk_freq"}
def __init__(
self, dmgr, spi_device, spi_freq=10 * MHz, clk_freq=75 * MHz, core_device="core"
):
self.core = dmgr.get(core_device)
self.bus = dmgr.get(spi_device)
assert spi_freq <= 40 * MHz, "SPI frequency exceeds maximum value of 40 MHz"
self.spi_freq = spi_freq
self.clk_freq = clk_freq
self.ctrl_reg = 0x0000 # Reset control register
@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(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
def enable_reset(self):
"""
Enable the DDS reset.
This method sets :const:`AD9834_RESET`, putting the AD9834 into a reset state.
While in this state, the digital-to-analog converter (DAC) is not operational.
This method should be called during initialization or when a reset is required
to reinitialize the device and ensure proper operation.
"""
self.ctrl_reg |= AD9834_RESET
self.write(self.ctrl_reg)
@kernel
def output_enable(self):
"""
Disable the DDS reset and start signal generation.
This method clears :const:`AD9834_RESET`, allowing the AD9834 to begin generating
signals. Once this method is called, the device will resume normal operation and
output the generated waveform.
This method should be called after configuration of the frequency and phase
settings to activate the output.
"""
self.ctrl_reg &= ~AD9834_RESET
self.write(self.ctrl_reg)
@kernel
def sleep(self, dac_pd: bool = False, clk_dis: bool = False):
"""
Put the AD9834 into sleep mode by selectively powering down the DAC and/or disabling
the internal clock.
This method controls the sleep mode behavior of the AD9834 by setting or clearing the
corresponding bits in the control register. Two independent options can be specified:
:param dac_pd: Set to ``True`` to power down the DAC (:const:`AD9834_SLEEP12` is set).
``False`` will leave the DAC active.
:param clk_dis: Set to ``True`` to disable the internal clock (:const:`AD9834_SLEEP1` is set).
``False`` will keep the clock running.
Both options can be enabled independently, allowing the DAC and/or clock to be powered down as needed.
The method updates the control register and writes the changes to the AD9834 device.
"""
if dac_pd:
self.ctrl_reg |= AD9834_SLEEP12
else:
self.ctrl_reg &= ~AD9834_SLEEP12
if clk_dis:
self.ctrl_reg |= AD9834_SLEEP1
else:
self.ctrl_reg &= ~AD9834_SLEEP1
self.write(self.ctrl_reg)
@kernel
def awake(self):
"""
Exit sleep mode and restore normal operation.
This method brings the AD9834 out of sleep mode by clearing any DAC power-down or
internal clock disable settings. It calls :meth:`sleep()` with no arguments,
effectively setting both ``dac_powerdown`` and ``internal_clk_disable`` to ``False``.
The device will resume generating output based on the current frequency and phase
settings.
"""
self.sleep()
@kernel
def config_sign_bit_out(
self,
high_z: bool = False,
msb_2: bool = False,
msb: bool = False,
comp_out: bool = False,
):
"""
Configure the ``SIGN BIT OUT`` pin for various output modes.
This method sets the output mode for the ``SIGN BIT OUT`` pin of the AD9834 based on the provided flags.
The user can enable one of several modes, including high impedance, MSB/2 output, MSB output,
or comparator output. These modes are mutually exclusive, and passing ``True`` to one flag will
configure the corresponding mode, while other flags should be left as ``False``.
:param high_z: Set to ``True`` to place the ``SIGN BIT OUT`` pin in high impedance (disabled) mode.
:param msb_2: Set to ``True`` to output DAC Data MSB divided by 2 on the ``SIGN BIT OUT`` pin.
:param msb: Set to ``True`` to output DAC Data MSB on the ``SIGN BIT OUT`` pin.
:param comp_out: Set to ``True`` to output the comparator signal on the ``SIGN BIT OUT`` pin.
Only one flag should be set to ``True`` at a time. If no valid mode is selected, the ``SIGN BIT OUT``
pin will default to high impedance mode.
The method updates the control register with the appropriate configuration and writes it to the AD9834.
"""
if high_z:
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
elif msb:
self.ctrl_reg |= AD9834_OPBITEN
self.ctrl_reg &= ~AD9834_MODE
self.ctrl_reg &= ~AD9834_SIGN_PIB
self.ctrl_reg |= AD9834_DIV2
elif comp_out:
self.ctrl_reg |= AD9834_OPBITEN
self.ctrl_reg &= ~AD9834_MODE
self.ctrl_reg |= AD9834_SIGN_PIB
self.ctrl_reg |= AD9834_DIV2
else:
self.ctrl_reg &= ~AD9834_OPBITEN
self.write(self.ctrl_reg)
@kernel
def enable_triangular_waveform(self):
"""
Enable triangular waveform generation.
This method configures the AD9834 to output a triangular waveform. It does so
by clearing :const:`AD9834_OPBITEN` in the control register and setting :const:`AD9834_MODE`.
Once this method is called, the AD9834 will begin generating a triangular waveform
at the frequency set for the selected frequency register.
This method should be called when a triangular waveform is desired for signal
generation. Ensure that the frequency is set appropriately before invoking this method.
"""
self.ctrl_reg &= ~AD9834_OPBITEN
self.ctrl_reg |= AD9834_MODE
self.write(self.ctrl_reg)
@kernel
def disable_triangular_waveform(self):
"""
Disable triangular waveform generation.
This method disables the triangular waveform output by clearing :const:`AD9834_MODE`.
After invoking this method, the AD9834 will cease generating a triangular waveform.
The device can then be configured to output other waveform types if needed.
This method should be called when switching to a different waveform type or
when the triangular waveform is no longer required.
"""
self.ctrl_reg &= ~AD9834_MODE
self.write(self.ctrl_reg)
@kernel
def write(self, data: TInt32):
"""
Write a 16-bit word to the AD9834.
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 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.
"""
self.bus.write(data << 16)

View File

@ -0,0 +1,321 @@
from artiq.coredevice.ad9834 import (
AD9834_B28,
AD9834_DIV2,
AD9834_FSEL,
AD9834_HLB,
AD9834_MODE,
AD9834_OPBITEN,
AD9834_PIN_SW,
AD9834_PSEL,
AD9834_RESET,
AD9834_SIGN_PIB,
AD9834_SLEEP1,
AD9834_SLEEP12,
FREQ_REGS,
PHASE_REGS,
)
from artiq.experiment import *
from artiq.language.units import MHz
from artiq.test.hardware_testbench import ExperimentCase
class AD9834Exp(EnvExperiment):
def build(self, runner):
self.setattr_device("core")
self.dev = self.get_device("dds0")
self.runner = runner
def run(self):
getattr(self, self.runner)()
@kernel
def instantiate(self):
pass
@kernel
def init(self):
self.core.break_realtime()
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)
@kernel
def set_frequency_reg_fail2(self):
self.core.break_realtime()
self.dev.set_frequency_reg(FREQ_REGS[0], 37.6 * MHz)
@kernel
def set_frequency_reg(self):
self.core.break_realtime()
self.dev.init()
self.dev.set_frequency_reg(FREQ_REGS[1], 19 * MHz)
self.set_dataset("ctrl_reg", self.dev.ctrl_reg)
@kernel
def set_frequency_reg_msb(self):
self.core.break_realtime()
self.dev.init()
self.dev.ctrl_reg |= AD9834_B28
self.dev.set_frequency_reg_msb(FREQ_REGS[0], 0x1111)
self.set_dataset("ctrl_reg", self.dev.ctrl_reg)
@kernel
def set_frequency_reg_lsb(self):
self.core.break_realtime()
self.dev.init()
self.dev.ctrl_reg |= AD9834_B28 | AD9834_HLB
self.dev.set_frequency_reg_lsb(FREQ_REGS[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)
@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)
@kernel
def set_phase_reg_fail(self):
self.core.break_realtime()
self.dev.set_phase_reg(19, 0x123)
@kernel
def set_phase_reg(self):
self.core.break_realtime()
self.dev.init()
self.dev.set_phase_reg(PHASE_REGS[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)
@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)
@kernel
def sleep_dac_powerdown(self):
self.core.break_realtime()
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.dev.sleep(clk_dis=True)
self.set_dataset("ctrl_reg", self.dev.ctrl_reg)
@kernel
def sleep(self):
self.core.break_realtime()
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)
@kernel
def sign_bit_high_z(self):
self.core.break_realtime()
self.dev.ctrl_reg |= AD9834_OPBITEN
self.dev.config_sign_bit_out()
self.set_dataset("ctrl_reg", self.dev.ctrl_reg)
@kernel
def sign_bit_msb_2(self):
self.core.break_realtime()
self.dev.ctrl_reg |= AD9834_MODE | AD9834_SIGN_PIB | AD9834_DIV2
self.dev.config_sign_bit_out(msb_2=True)
self.set_dataset("ctrl_reg", self.dev.ctrl_reg)
@kernel
def sign_bit_msb(self):
self.core.break_realtime()
self.dev.ctrl_reg |= AD9834_MODE | AD9834_SIGN_PIB
self.dev.config_sign_bit_out(msb=True)
self.set_dataset("ctrl_reg", self.dev.ctrl_reg)
@kernel
def sign_bit_comp_out(self):
self.core.break_realtime()
self.dev.ctrl_reg |= AD9834_MODE
self.dev.config_sign_bit_out(comp_out=True)
self.set_dataset("ctrl_reg", self.dev.ctrl_reg)
@kernel
def enable_triangular_waveform(self):
self.core.break_realtime()
self.dev.ctrl_reg |= AD9834_OPBITEN
self.dev.enable_triangular_waveform()
self.set_dataset("ctrl_reg", self.dev.ctrl_reg)
@kernel
def disable_triangular_waveform(self):
self.core.break_realtime()
self.dev.ctrl_reg |= AD9834_MODE
self.dev.disable_triangular_waveform()
self.set_dataset("ctrl_reg", self.dev.ctrl_reg)
class AD9834Test(ExperimentCase):
def test_instantiate(self):
self.execute(AD9834Exp, "instantiate")
def test_init(self):
self.execute(AD9834Exp, "init")
spi_freq = self.dataset_mgr.get("spi_freq")
clk_freq = self.dataset_mgr.get("clk_freq")
ctrl_reg = self.dataset_mgr.get("ctrl_reg")
self.assertEqual(spi_freq, 10 * MHz)
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")
with self.assertRaises(AssertionError):
self.execute(AD9834Exp, "set_frequency_reg_fail2")
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)
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)
def test_set_frequency_reg_lsb(self):
self.execute(AD9834Exp, "set_frequency_reg_lsb")
ctrl_reg = self.dataset_mgr.get("ctrl_reg")
self.assertEqual(ctrl_reg, 0x0000 | AD9834_RESET)
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)
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)
def test_set_phase_reg_fail(self):
with self.assertRaises(ValueError):
self.execute(AD9834Exp, "set_phase_reg_fail")
def test_set_phase_reg(self):
self.execute(AD9834Exp, "set_phase_reg")
ctrl_reg = self.dataset_mgr.get("ctrl_reg")
self.assertEqual(ctrl_reg, 0x0000 | AD9834_RESET)
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)
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)
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)
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)
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)
def test_awake(self):
self.execute(AD9834Exp, "awake")
ctrl_reg = self.dataset_mgr.get("ctrl_reg")
self.assertEqual(ctrl_reg, 0x0000)
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)
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)
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)
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
)
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)
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)

View File

@ -156,15 +156,39 @@ Common KC705 problems
* When connected, the CLOCK adapter breaks the JTAG chain due to TDI not being connected to TDO on the FMC mezzanine.
* On some boards, the JTAG USB connector is not correctly soldered.
VADJ
""""
With the NIST CLOCK and QC2 adapters, for safe operation of the DDS buses (to prevent damage to the IO banks of the FPGA), the FMC VADJ rail of the KC705 should be changed to 3.3V. Plug the Texas Instruments USB-TO-GPIO PMBus adapter into the PMBus connector in the corner of the KC705 and use the Fusion Digital Power Designer software to configure (requires Windows). Write to chip number U55 (address 52), channel 4, which is the VADJ rail, to make it 3.3V instead of 2.5V. Power cycle the KC705 board to check that the startup voltage on the VADJ rail is now 3.3V.
EBAZ4205
^^^^^^^^
The `EBAZ4205 <https://github.com/xjtuecho/EBAZ4205>`_ Zynq-SoC control card, originally used in the Ebit E9+ BTC miner, is a low-cost development board (around $20-$30 USD), making it an ideal option for experimenting with ARTIQ. To use the EBAZ4205, it's important to carefully follow the board documentation to configure it to boot from the SD card, as network booting via ``artiq_netboot`` is currently unsupported. This is because the Ethernet PHY is routed through the EMIO, requiring the FPGA to be programmed before the board can establish a network connection.
VADJ
""""
One useful application of the EBAZ4205 is controlling external devices like the AD9834 DDS Module from ZonRi Technology Co., Ltd. To establish communication between the EBAZ4205 and the AD9834 module, proper configuration of the SPI interface pins is essential. The board's flexibility allows for straightforward control of the DDS once the correct pinout is known. The table below details the necessary connections between the EBAZ4205 and the AD9834 module, including power, ground, and SPI signals.
With the NIST CLOCK and QC2 adapters, for safe operation of the DDS buses (to prevent damage to the IO banks of the FPGA), the FMC VADJ rail of the KC705 should be changed to 3.3V. Plug the Texas Instruments USB-TO-GPIO PMBus adapter into the PMBus connector in the corner of the KC705 and use the Fusion Digital Power Designer software to configure (requires Windows). Write to chip number U55 (address 52), channel 4, which is the VADJ rail, to make it 3.3V instead of 2.5V. Power cycle the KC705 board to check that the startup voltage on the VADJ rail is now 3.3V.
+--------------------------+---------------------+----------------------------+
| Pin on AD9834 Module | Chip Function | Connection on EBAZ4205 |
+==========================+=====================+============================+
| SCLK | SCLK | CLK: DATA3-19 (Pin V20) |
+--------------------------+---------------------+----------------------------+
| DATA | SDATA | MOSI: DATA3-17 (Pin U20) |
+--------------------------+---------------------+----------------------------+
| SYNC | FSYNC | CS_N: DATA3-15 (Pin P19) |
+--------------------------+---------------------+----------------------------+
| FSE (Tied to GND) | FSELECT | N/A: Bit Controlled |
+--------------------------+---------------------+----------------------------+
| PSE (Tied to GND) | PSELECT | N/A: Bit Controlled |
+--------------------------+---------------------+----------------------------+
| GND | Ground | GND: J8-1, J8-3 |
+--------------------------+---------------------+----------------------------+
| VIN | AVDD/DVDD | 3.3V: J8-2 |
+--------------------------+---------------------+----------------------------+
| RESET (Unused) | RESET | N/A: Bit Controlled |
+--------------------------+---------------------+----------------------------+
For a step-by-step guide, see the `EBAZ4205 and AD9834 setup guide <https://newell.github.io/projects/ebaz4205>`_.
Variant details
---------------

View File

@ -85,6 +85,12 @@ RF generation drivers
.. automodule:: artiq.coredevice.ad9914
:members:
:mod:`artiq.coredevice.ad9834` module
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
.. automodule:: artiq.coredevice.ad9834
:members:
:mod:`artiq.coredevice.mirny` module
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^