Merge branch 'phaser2'

* phaser2: (157 commits)
  sawg/hbf: tweak pipeline for timing
  fir: register multiplier output
  conda/phaser: build-depend on numpy
  sawg: reduce coefficient width
  sawg: fix latency
  test/fir: needs mpl. don't run by default
  test/sawg: patch spline
  sawg: use ParallelHBFCascade to AA [WIP]
  fir: add ParallelHBFCascade
  fir: add ParallelFIR and test
  gateware/dsp: add FIR and test
  README_PHASER: update
  sawg: documentation
  sawg: extract spline
  sawg: document
  sawg: demo_2tone
  sawg: round to int64
  gateware/phaser -> gateware/ad9154_fmc_ebz
  phaser: fix typo
  sawg: merge set/set64
  ...
This commit is contained in:
Robert Jördens 2016-12-12 17:31:39 +01:00
commit c63fa46430
42 changed files with 6410 additions and 7 deletions

117
README_PHASER.rst Normal file
View File

@ -0,0 +1,117 @@
ARTIQ Phaser
============
This ARTIQ branch contains a proof-of-concept design of a GHz-datarate, multi-channel, interpolating, multi-tone, direct digital synthesizer (DDS) compatible with ARTIQ's RTIO channels.
Ultimately it will be the basis for the ARTIQ Sayma Smart Arbitrary Waveform Generator project. See https://github.com/m-labs/sinara and https://github.com/m-labs/artiq-hardware.
*Features*:
* up to 4 channels
* up to 500 MHz data rate per channel (KC705 limitation)
* up to 8x interpolation to 2.4 GHz DAC sample rate
* Real-time sample-coherent control over amplitude, frequency, phase of each channel through ARTIQ RTIO commands
* Full configurability of the AD9154 and AD9516 through SPI with ARTIQ kernel support
* All SPI registers and register bits exposed as human readable names
* Parametrized JESD204B core (also capable of operation with eight lanes)
* The code can be reconfigured. Possible example configurations are: support 2 channels at 1 GHz datarate, support 4 channels at 300 MHz data rate, no interpolation, and using mix mode to stress the second and third Nyquist zones (150-300 MHz and 300-450 MHz).
The hardware required to use the ARTIQ phaser branch is a KC705 with an AD9154-FMC-EBZ plugged into the HPC connector and a low-noise sample rate reference clock.
This work was supported by the Army Research Lab and the University of Maryland.
The code that was developed for this project is located in several repositories:
* In ARTIQ, the SAWG and Phaser code: https://github.com/m-labs/artiq/compare/phaser2
* The Migen/MiSoC JESD204B core: https://github.com/m-labs/jesd204b
Installation
------------
These installation instructions are a short form of those in the ARTIQ manual.
Please refer to and follow the ARTIQ manual for more details:
https://m-labs.hk/artiq/manual-master/index.html
* Set up a new conda environment and activate it.
* Install the standard ARTIQ runtime/install dependencies.
See ``conda/artiq/meta.yaml`` for a list.
They are all packaged as conda packages in ``m-labs/main``.
* Install the standard ARTIQ build dependencies.
They are all available as conda packages in m-labs/main or m-labs/dev for linux-64:
- migen
- misoc
- jesd204b
- llvm-or1k
- rust-core-or1k
- cargo
- binutils-or1k-linux
* Install a recent version of Vivado (tested and developed with 2016.2).
* Do a checkout of the ARTIQ phaser2 branch: ::
mkdir ~/src
cd ~/src
git clone --recursive -b phaser2 https://github.com/m-labs/artiq.git
cd ../artiq
python setup.py develop
Setup
-----
* Setup the KC705 (jumpers, etc.) observing the ARTIQ manual.
VADJ does not need to be changed.
* On the AD9154-FMC-EBZ put jumpers:
- on XP1, between pin 5 and 6 (will keep the PIC in reset)
- on JP3 (will force output enable on FXLA108)
* Compile the ARTIQ Phaser bitstream, bios, and runtime (c.f. ARTIQ manual): ::
python -m artiq.gateware.targets.phaser
* Generate an ARTIQ configuration flash image with MAC and IP address (see the
documentation for ``artiq_mkfs``). Name it ``phaser_config.bin``.
* Run the following OpenOCD command to flash the ARTIQ phaser design: ::
openocd -f board/kc705.cfg -c "init; jtagspi_init 0 bscan_spi_xc7k325t.bit; jtagspi_program misoc_phaser_kc705/gateware/top.bin 0x000000; jtagspi_program misoc_phaser_kc705/software/bios/bios.bin 0xaf0000; jtagspi_program misoc_phaser_kc705/software/runtime/runtime.fbi 0xb00000;jtagspi_program phaser_config.bin 0xb80000; xc7_program xc7.tap; exit"
The proxy bitstream ``bscan_spi_xc7k325t.bit`` can be found at https://github.com/jordens/bscan_spi_bitstreams or in any ARTIQ conda package for the KC705.
See the source code of ``artiq_flash.py`` from ARTIQ for more details.
If you are using the OpenOCD Conda package:
* locate the OpenOCD scripts directory with: ``python3 -c "import artiq.frontend.artiq_flash as af; print(af.scripts_path)"``
* add ``-s <scripts directory>`` to the OpenOCD command line.
* Refer to the ARTIQ documentation to configure an IP address and other settings for the transmitter device.
If the board was running stock ARTIQ before, the settings will be kept.
* A 300 MHz clock of roughly 10 dBm (0.2 to 3.4 V peak-to-peak into 50 Ohm) must be connected to the AD9154-FMC-EBZ J1.
The external RTIO clock, DAC deviceclock, FPGA deviceclock, and SYSREF are derived from this signal. There is no internal RTIO clock.
* An example device database, several status and test scripts are provided in ``artiq/examples/phaser/``. ::
cd artiq/examples/phaser
* Edit ``device_db.pyon`` to match the hostname or IP address of the core device.
* The ``startup_clock`` needs to be set to internal (``i``) for bootstrapping the clock distribution tree.
* Compile and flash the startup kernel in ``artiq/examples/phaser/startup_kernel.py``.
* Erase any possible idle kernels.
* Use ``ping`` and ``flterm`` to verify that the core device starts up and boots correctly.
Usage
-----
* After each boot, run the ``dac_setup.py`` experiment to establish the JESD204B links (``artiq_run repository/dac_setup.py``).
* Run ``artiq_run repository/ad9154_test_status.py`` to retrieve and print several status registers from the AD9154 DAC.
* Run ``artiq_run repository/ad9154_test_prbs.py`` to test the JESD204B PHY layer for bit errors. Reboot the core device afterwards.
* Run ``artiq_run repository/ad9154_test_stpl.py`` to executes a JESD204B short transport layer test.
* Run ``artiq_run repository/demo.py`` for an example that exercises several different use cases of synchronized phase, amplitude, and frequency updates.
for an example that exercises several different use cases of synchronized phase, amplitude, and frequency updates.
* Run ``artiq_run repository/demo_2tone.py`` for an example that emits a shaped two-tone pulse.
* Implement your own experiments using the SAWG channels.
* Verify clock stability between the sample rate reference clock and the DAC outputs.
* Changes to the AD9154 configuration can also be performed at runtime in experiments.
See the example ``dac_setup.py``.
This can e.g. be used to enable and evaluate mix mode without having to change any other code (bitstream/bios/runtime/startup_kernel).

106
artiq/coredevice/ad9154.py Normal file
View File

@ -0,0 +1,106 @@
from artiq.language.core import kernel, syscall
from artiq.language.types import TInt32, TNone
@syscall(flags={"nounwind", "nowrite"})
def ad9154_init() -> TNone:
raise NotImplementedError("syscall not simulated")
@syscall(flags={"nounwind", "nowrite"})
def ad9154_write(addr: TInt32, data: TInt32) -> TNone:
raise NotImplementedError("syscall not simulated")
@syscall(flags={"nounwind", "nowrite"})
def ad9154_read(addr: TInt32) -> TInt32:
raise NotImplementedError("syscall not simulated")
@syscall(flags={"nounwind", "nowrite"})
def ad9516_write(addr: TInt32, data: TInt32) -> TNone:
raise NotImplementedError("syscall not simulated")
@syscall(flags={"nounwind", "nowrite"})
def ad9516_read(addr: TInt32) -> TInt32:
raise NotImplementedError("syscall not simulated")
@syscall(flags={"nounwind", "nowrite"})
def ad9154_jesd_enable(en: TInt32) -> TNone:
raise NotImplementedError("syscall not simulated")
@syscall(flags={"nounwind", "nowrite"})
def ad9154_jesd_ready() -> TInt32:
raise NotImplementedError("syscall not simulated")
@syscall(flags={"nounwind", "nowrite"})
def ad9154_jesd_prbs(prbs: TInt32) -> TNone:
raise NotImplementedError("syscall not simulated")
@syscall(flags={"nounwind", "nowrite"})
def ad9154_jesd_stpl(prbs: TInt32) -> TNone:
raise NotImplementedError("syscall not simulated")
class AD9154:
"""AD9154-FMC-EBZ SPI support
There are two devices on the SPI bus, a AD9154 DAC and a AD9516 clock
divider/fanout.
Register and bit names are in :mod:`artiq.coredevice.ad9154_reg` and
:mod:`artiq.coredevice.ad9516_reg` respectively.
The SPI bus does not operate over RTIO but directly. This class does not
interact with the timeline.
"""
def __init__(self, dmgr, core_device="core"):
self.core = dmgr.get(core_device)
@kernel
def init(self):
"""Initialize and configure the SPI bus."""
ad9154_init()
@kernel
def dac_write(self, addr, data):
"""Write `data` to AD9154 SPI register at `addr`."""
ad9154_write(addr, data)
@kernel
def dac_read(self, addr):
"""Read AD9154 SPI register at `addr`."""
return ad9154_read(addr)
@kernel
def clock_write(self, addr, data):
"""Write `data` to AD9516 SPI register at `addr`."""
ad9516_write(addr, data)
@kernel
def clock_read(self, addr):
"""Read AD9516 SPI register at `addr`."""
return ad9516_read(addr)
@kernel
def jesd_enable(self, en):
"""Enables the JESD204B core startup sequence."""
ad9154_jesd_enable(en)
@kernel
def jesd_ready(self):
"""Returns `True` if the JESD links are up."""
return ad9154_jesd_ready()
@kernel
def jesd_prbs(self, prbs):
ad9154_jesd_prbs(prbs)
@kernel
def jesd_stpl(self, enable):
ad9154_jesd_stpl(enable)

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,291 @@
# = auto-generated, do not edit
from artiq.language.core import kernel
AD9516_SERIAL_PORT_CONFIGURATION = 0x000
AD9516_SDO_ACTIVE = 1 << 0 # 1, 0x00 R/W
AD9516_LSB_FIRST = 1 << 1 # 1, 0x00 R/W
AD9516_SOFT_RESET = 1 << 2 # 1, 0x00 R/W
AD9516_LONG_INSTRUCTION = 1 << 3 # 1, 0x01 R/W
AD9516_LONG_INSTRUCTION_MIRRORED = 1 << 4 # 1, 0x01 R/W
AD9516_SOFT_RESET_MIRRORED = 1 << 5 # 1, 0x00 R/W
AD9516_LSB_FIRST_MIRRORED = 1 << 6 # 1, 0x00 R/W
AD9516_SDO_ACTIVE_MIRRORED = 1 << 7 # 1, 0x00 R/W
AD9516_PART_ID = 0x003
AD9516_READBACK_CONTROL = 0x004
AD9516_READ_BACK_ACTIVE_REGISTERS = 1 << 0 # 1, 0x00 R/W
AD9516_PFD_AND_CHARGE_PUMP = 0x010
AD9516_PLL_POWER_DOWN = 1 << 0 # 2, 0x01 R/W
AD9516_CHARGE_PUMP_MODE = 1 << 2 # 2, 0x03 R/W
AD9516_CHARGE_PUMP_CURRENT = 1 << 4 # 3, 0x07 R/W
AD9516_PFD_POLARITY = 1 << 7 # 1, 0x00 R/W
AD9516_R_COUNTER_LSB = 0x011
AD9516_R_COUNTER_MSB = 0x012
AD9516_A_COUNTER = 0x013
AD9516_B_COUNTER_LSB = 0x014
AD9516_B_COUNTER_MSB = 0x015
AD9516_PLL_CONTROL_1 = 0x016
AD9516_PRESCALER_P = 1 << 0 # 3, 0x06 R/W
AD9516_B_COUNTER_BYPASS = 1 << 3 # 1, 0x00 R/W
AD9516_RESET_ALL_COUNTERS = 1 << 4 # 1, 0x00 R/W
AD9516_RESET_A_AND_B_COUNTERS = 1 << 5 # 1, 0x00 R/W
AD9516_RESET_R_COUNTER = 1 << 6 # 1, 0x00 R/W
AD9516_SET_CP_PIN_TO_VCP_2 = 1 << 7 # 1, 0x00 R/W
AD9516_PLL_CONTROL_2 = 0x017
AD9516_ANTIBACKLASH_PULSE_WIDTH = 1 << 0 # 2, 0x00 R/W
AD9516_STATUS_PIN_CONTROL = 1 << 2 # 6, 0x00 R/W
AD9516_PLL_CONTROL_3 = 0x018
AD9516_VCO_CAL_NOW = 1 << 0 # 1, 0x00 R/W
AD9516_VCO_CALIBRATION_DIVIDER = 1 << 1 # 2, 0x03 R/W
AD9516_DISABLE_DIGITAL_LOCK_DETECT = 1 << 3 # 1, 0x00 R/W
AD9516_DIGITAL_LOCK_DETECT_WINDOW = 1 << 4 # 1, 0x00 R/W
AD9516_LOCK_DETECT_COUNTER = 1 << 5 # 2, 0x00 R/W
AD9516_PLL_CONTROL_4 = 0x019
AD9516_N_PATH_DELAY = 1 << 0 # 3, 0x00 R/W
AD9516_R_PATH_DELAY = 1 << 3 # 3, 0x00 R/W
AD9516_R_A_B_COUNTERS_SYNC_PIN_RESET = 1 << 6 # 2, 0x00 R/W
AD9516_PLL_CONTROL_5 = 0x01a
AD9516_LD_PIN_CONTROL = 1 << 0 # 6, 0x00 R/W
AD9516_REFERENCE_FREQUENCY_MONITOR_THRESHOLD = 1 << 6 # 1, 0x00 R/W
AD9516_PLL_CONTROL_6 = 0x01b
AD9516_REFMON_PIN_CONTROL = 1 << 0 # 5, 0x00 R/W
AD9516_REF1_REFIN_FREQUENCY_MONITOR = 1 << 5 # 1, 0x00 R/W
AD9516_REF2_REFIN_FREQUENCY_MONITOR = 1 << 6 # 1, 0x00 R/W
AD9516_VCO_FREQUENCY_MONITOR = 1 << 7 # 1, 0x00 R/W
AD9516_PLL_CONTROL_7 = 0x01c
AD9516_DIFFERENTIAL_REFERENCE = 1 << 0 # 1, 0x00 R/W
AD9516_REF1_POWER_ON = 1 << 1 # 1, 0x00 R/W
AD9516_REF2_POWER_ON = 1 << 2 # 1, 0x00 R/W
AD9516_USE_REF_SEL_PIN = 1 << 5 # 1, 0x00 R/W
AD9516_SELECT_REF2 = 1 << 6 # 1, 0x00 R/W
AD9516_DISABLE_SWITCHOVER_DEGLITCH = 1 << 7 # 1, 0x00 R/W
AD9516_PLL_CONTROL_8 = 0x01d
AD9516_HOLDOVER_ENABLE = 1 << 0 # 1, 0x00 R/W
AD9516_EXTERNAL_HOLDOVER_CONTROL = 1 << 1 # 1, 0x00 R/W
AD9516_HOLDOVER_ENABLEreg001D = 1 << 2 # 1, 0x00 R/W
AD9516_LD_PIN_COMPARATOR_ENABLE = 1 << 3 # 1, 0x00 R/W
AD9516_PLL_STATUS_REGISTER_DISABLE = 1 << 4 # 1, 0x00 R/W
AD9516_PLL_READBACK = 0x01f
AD9516_DIGITAL_LOCK_DETECT = 1 << 0 # 1, 0x00 R
AD9516_REF1_FREQUENCY_THRESHOLD = 1 << 1 # 1, 0x00 R
AD9516_REF2_FREQUENCY_THRESHOLD = 1 << 2 # 1, 0x00 R
AD9516_VCO_FREQUENCY_THRESHOLD = 1 << 3 # 1, 0x00 R
AD9516_REF2_SELECTED = 1 << 4 # 1, 0x00 R
AD9516_HOLDOVER_ACTIVE = 1 << 5 # 1, 0x00 R
AD9516_VCO_CAL_FINISHED = 1 << 6 # 1, 0x00 R
AD9516_OUT6_DELAY_BYPASS = 0x0a0
AD9516_OUT6_DELAY_FULL_SCALE = 0x0a1
AD9516_OUT6_RAMP_CURRENT = 1 << 0 # 3, 0x00 R/W
AD9516_OUT6_RAMP_CAPACITORS = 1 << 3 # 3, 0x00 R/W
AD9516_OUT6_DELAY_FRACTION = 0x0a2
AD9516_OUT7_DELAY_BYPASS = 0x0a3
AD9516_OUT7_DELAY_FULL_SCALE = 0x0a4
AD9516_OUT7_RAMP_CURRENT = 1 << 0 # 3, 0x00 R/W
AD9516_OUT7_RAMP_CAPACITORS = 1 << 3 # 3, 0x00 R/W
AD9516_OUT7_DELAY_FRACTION = 0x0a5
AD9516_OUT8_DELAY_BYPASS = 0x0a6
AD9516_OUT8_DELAY_FULL_SCALE = 0x0a7
AD9516_OUT8_RAMP_CURRENT = 1 << 0 # 3, 0x00 R/W
AD9516_OUT8_RAMP_CAPACITORS = 1 << 3 # 3, 0x00 R/W
AD9516_OUT8_DELAY_FRACTION = 0x0a8
AD9516_OUT9_DELAY_BYPASS = 0x0a9
AD9516_OUT9_DELAY_FULL_SCALE = 0x0aa
AD9516_OUT9_RAMP_CURRENT = 1 << 0 # 3, 0x00 R/W
AD9516_OUT9_RAMP_CAPACITORS = 1 << 3 # 3, 0x00 R/W
AD9516_OUT9_DELAY_FRACTION = 0x0ab
AD9516_OUT0 = 0x0f0
AD9516_OUT0_POWER_DOWN = 1 << 0 # 2, 0x00 R/W
AD9516_OUT0_LVPECL_DIFFERENTIAL_VOLTAGE = 1 << 2 # 2, 0x02 R/W
AD9516_OUT0_INVERT = 1 << 4 # 1, 0x00 R/W
AD9516_OUT1 = 0x0f1
AD9516_OUT1_POWER_DOWN = 1 << 0 # 2, 0x02 R/W
AD9516_OUT1_LVPECLDIFFERENTIAL_VOLTAGE = 1 << 2 # 2, 0x02 R/W
AD9516_OUT1_INVERT = 1 << 4 # 1, 0x00 R/W
AD9516_OUT2 = 0x0f2
AD9516_OUT2_POWER_DOWN = 1 << 0 # 2, 0x00 R/W
AD9516_OUT2_LVPECL_DIFFERENTIAL_VOLTAGE = 1 << 2 # 2, 0x02 R/W
AD9516_OUT2_INVERT = 1 << 4 # 1, 0x00 R/W
AD9516_OUT3 = 0x0f3
AD9516_OUT3_POWER_DOWN = 1 << 0 # 2, 0x02 R/W
AD9516_OUT3_LVPECL_DIFFERENTIAL_VOLTAGE = 1 << 2 # 2, 0x02 R/W
AD9516_OUT3_INVERT = 1 << 4 # 1, 0x00 R/W
AD9516_OUT4 = 0x0f4
AD9516_OUT4_POWER_DOWN = 1 << 0 # 2, 0x02 R/W
AD9516_OUT4_LVPECL_DIFFERENTIAL_VOLTAGE = 1 << 2 # 2, 0x02 R/W
AD9516_OUT4_INVERT = 1 << 4 # 1, 0x00 R/W
AD9516_OUT5 = 0x0f5
AD9516_OUT5_POWER_DOWN = 1 << 0 # 2, 0x02 R/W
AD9516_OUT5_LVPECL_DIFFERENTIAL_VOLTAGE = 1 << 2 # 2, 0x02 R/W
AD9516_OUT5_INVERT = 1 << 4 # 1, 0x00 R/W
AD9516_OUT6 = 0x140
AD9516_OUT6_POWER_DOWN = 1 << 0 # 1, 0x00 R/W
AD9516_OUT6_LVDS_OUTPUT_CURRENT = 1 << 1 # 2, 0x01 R/W
AD9516_OUT6_SELECT_LVDS_CMOS = 1 << 3 # 1, 0x00 R/W
AD9516_OUT6_CMOS_B = 1 << 4 # 1, 0x00 R/W
AD9516_OUT6_LVDS_CMOS_OUTPUT_POLARITY = 1 << 5 # 1, 0x00 R/W
AD9516_OUT6_CMOS_OUTPUT_POLARITY = 1 << 6 # 2, 0x01 R/W
AD9516_OUT7 = 0x141
AD9516_OUT7_POWER_DOWN = 1 << 0 # 1, 0x01 R/W
AD9516_OUT7_LVDS_OUTPUT_CURRENT = 1 << 1 # 2, 0x01 R/W
AD9516_OUT7_SELECT_LVDS_CMOS = 1 << 3 # 1, 0x00 R/W
AD9516_OUT7_CMOS_B = 1 << 4 # 1, 0x00 R/W
AD9516_OUT7_LVDS_CMOS_OUTPUT_POLARITY = 1 << 5 # 1, 0x00 R/W
AD9516_OUT7_CMOS_OUTPUT_POLARITY = 1 << 6 # 2, 0x01 R/W
AD9516_OUT8 = 0x142
AD9516_OUT8_POWER_DOWN = 1 << 0 # 1, 0x00 R/W
AD9516_OUT8_LVDS_OUTPUT_CURRENT = 1 << 1 # 2, 0x01 R/W
AD9516_OUT8_SELECT_LVDS_CMOS = 1 << 3 # 1, 0x00 R/W
AD9516_OUT8_CMOS_B = 1 << 4 # 1, 0x00 R/W
AD9516_OUT8_LVDS_CMOS_OUTPUT_POLARITY = 1 << 5 # 1, 0x00 R/W
AD9516_OUT8_CMOS_OUTPUT_POLARITY = 1 << 6 # 2, 0x01 R/W
AD9516_OUT9 = 0x143
AD9516_OUT9_POWER_DOWN = 1 << 0 # 1, 0x01 R/W
AD9516_OUT9_LVDS_OUTPUT_CURRENT = 1 << 1 # 2, 0x01 R/W
AD9516_OUT9_SELECT_LVDS_CMOS = 1 << 3 # 1, 0x00 R/W
AD9516_OUT9_CMOS_B = 1 << 4 # 1, 0x00 R/W
AD9516_OUT9_LVDS_CMOS_OUTPUT_POLARITY = 1 << 5 # 1, 0x00 R/W
AD9516_OUT9_CMOS_OUTPUT_POLARITY = 1 << 6 # 2, 0x01 R/W
AD9516_DIVIDER_0_0 = 0x190
AD9516_DIVIDER_0_HIGH_CYCLES = 1 << 0 # 4, 0x00 R/W
AD9516_DIVIDER_0_LOW_CYCLES = 1 << 4 # 4, 0x00 R/W
AD9516_DIVIDER_0_1 = 0x191
AD9516_DIVIDER_0_PHASE_OFFSET = 1 << 0 # 4, 0x00 R/W
AD9516_DIVIDER_0_START_HIGH = 1 << 4 # 1, 0x00 R/W
AD9516_DIVIDER_0_FORCE_HIGH = 1 << 5 # 1, 0x00 R/W
AD9516_DIVIDER_0_NOSYNC = 1 << 6 # 1, 0x00 R/W
AD9516_DIVIDER_0_BYPASS = 1 << 7 # 1, 0x01 R/W
AD9516_DIVIDER_0_2 = 0x192
AD9516_DIVIDER_0_DCCOFF = 1 << 0 # 1, 0x00 R/W
AD9516_DIVIDER_0_DIRECT_TO_OUTPUT = 1 << 1 # 1, 0x00 R/W
AD9516_DIVIDER_1_0 = 0x193
AD9516_DIVIDER_1_HIGH_CYCLES = 1 << 0 # 4, 0x00 R/W
AD9516_DIVIDER_1_LOW_CYCLES = 1 << 4 # 4, 0x00 R/W
AD9516_DIVIDER_1_1 = 0x194
AD9516_DIVIDER_1_PHASE_OFFSET = 1 << 0 # 4, 0x00 R/W
AD9516_DIVIDER_1_START_HIGH = 1 << 4 # 1, 0x00 R/W
AD9516_DIVIDER_1_FORCE_HIGH = 1 << 5 # 1, 0x00 R/W
AD9516_DIVIDER_1_NOSYNC = 1 << 6 # 1, 0x00 R/W
AD9516_DIVIDER_1_BYPASS = 1 << 7 # 1, 0x00 R/W
AD9516_DIVIDER_1_2 = 0x195
AD9516_DIVIDER_1_DCCOFF = 1 << 0 # 1, 0x00 R/W
AD9516_DIVIDER_1_DIRECT_TO_OUTPUT = 1 << 1 # 1, 0x00 R/W
AD9516_DIVIDER_2_0 = 0x196
AD9516_DIVIDER_2_HIGH_CYCLES = 1 << 0 # 4, 0x00 R/W
AD9516_DIVIDER_2_LOW_CYCLES = 1 << 4 # 4, 0x00 R/W
AD9516_DIVIDER_2_1 = 0x197
AD9516_DIVIDER_2_PHASE_OFFSET = 1 << 0 # 4, 0x00 R/W
AD9516_DIVIDER_2_START_HIGH = 1 << 4 # 1, 0x00 R/W
AD9516_DIVIDER_2_FORCE_HIGH = 1 << 5 # 1, 0x00 R/W
AD9516_DIVIDER_2_NOSYNC = 1 << 6 # 1, 0x00 R/W
AD9516_DIVIDER_2_BYPASS = 1 << 7 # 1, 0x00 R/W
AD9516_DIVIDER_2_2 = 0x198
AD9516_DIVIDER_2_DCCOFF = 1 << 0 # 1, 0x00 R/W
AD9516_DIVIDER_2_DIRECT_TO_OUTPUT = 1 << 1 # 1, 0x00 R/W
AD9516_DIVIDER_3_0 = 0x199
AD9516_DIVIDER_3_HIGH_CYCLES_1 = 1 << 0 # 4, 0x02 R/W
AD9516_DIVIDER_3_LOW_CYCLES_1 = 1 << 4 # 4, 0x02 R/W
AD9516_DIVIDER_3_1 = 0x19a
AD9516_DIVIDER_3_PHASE_OFFSET_1 = 1 << 0 # 4, 0x00 R/W
AD9516_DIVIDER_3_PHASE_OFFSET_2 = 1 << 4 # 4, 0x00 R/W
AD9516_DIVIDER_3_2 = 0x19b
AD9516_DIVIDER_3_HIGH_CYCLES_2 = 1 << 0 # 4, 0x01 R/W
AD9516_DIVIDER_3_LOW_CYCLES_2 = 1 << 4 # 4, 0x01 R/W
AD9516_DIVIDER_3_3 = 0x19c
AD9516_DIVIDER_3_START_HIGH_1 = 1 << 0 # 1, 0x00 R/W
AD9516_DIVIDER_3_START_HIGH_2 = 1 << 1 # 1, 0x00 R/W
AD9516_DIVIDER_3_FORCE_HIGH = 1 << 2 # 1, 0x00 R/W
AD9516_DIVIDER_3_NOSYNC = 1 << 3 # 1, 0x00 R/W
AD9516_DIVIDER_3_BYPASS_1 = 1 << 4 # 1, 0x00 R/W
AD9516_DIVIDER_3_BYPASS_2 = 1 << 5 # 1, 0x00 R/W
AD9516_DIVIDER_3_4 = 0x19d
AD9516_DIVIDER_3_DCCOFF = 1 << 0 # 1, 0x00 R/W
AD9516_DIVIDER_4_0 = 0x19e
AD9516_DIVIDER_4_HIGH_CYCLES_1 = 1 << 0 # 4, 0x02 R/W
AD9516_DIVIDER_4_LOW_CYCLES_1 = 1 << 4 # 4, 0x02 R/W
AD9516_DIVIDER_4_1 = 0x19f
AD9516_DIVIDER_4_PHASE_OFFSET_1 = 1 << 0 # 4, 0x00 R/W
AD9516_DIVIDER_4_PHASE_OFFSET_2 = 1 << 4 # 4, 0x00 R/W
AD9516_DIVIDER_4_2 = 0x1a0
AD9516_DIVIDER_4_HIGH_CYCLES_2 = 1 << 0 # 4, 0x01 R/W
AD9516_DIVIDER_4_LOW_CYCLES_2 = 1 << 4 # 4, 0x01 R/W
AD9516_DIVIDER_4_3 = 0x1a1
AD9516_DIVIDER_4_START_HIGH_1 = 1 << 0 # 1, 0x00 R/W
AD9516_DIVIDER_4_START_HIGH_2 = 1 << 1 # 1, 0x00 R/W
AD9516_DIVIDER_4_FORCE_HIGH = 1 << 2 # 1, 0x00 R/W
AD9516_DIVIDER_4_NOSYNC = 1 << 3 # 1, 0x00 R/W
AD9516_DIVIDER_4_BYPASS_1 = 1 << 4 # 1, 0x00 R/W
AD9516_DIVIDER_4_BYPASS_2 = 1 << 5 # 1, 0x00 R/W
AD9516_DIVIDER_4_4 = 0x1a2
AD9516_DIVIDER_4_DCCOFF = 1 << 0 # 1, 0x00 R/W
AD9516_VCO_DIVIDER = 0x1e0
AD9516_INPUT_CLKS = 0x1e1
AD9516_BYPASS_VCO_DIVIDER = 1 << 0 # 1, 0x00 R/W
AD9516_SELECT_VCO_OR_CLK = 1 << 1 # 1, 0x00 R/W
AD9516_POWER_DOWN_VCO_AND_CLK = 1 << 2 # 1, 0x00 R/W
AD9516_POWER_DOWN_VCO_CLOCK_INTERFACE = 1 << 3 # 1, 0x00 R/W
AD9516_POWER_DOWN_CLOCK_INPUT_SECTION = 1 << 4 # 1, 0x00 R/W
AD9516_POWER_DOWN_AND_SYNC = 0x230
AD9516_SOFT_SYNC = 1 << 0 # 1, 0x00 R/W
AD9516_POWER_DOWN_DISTRIBUTION_REFERENCE = 1 << 1 # 1, 0x00 R/W
AD9516_POWER_DOWN_SYNC = 1 << 2 # 1, 0x00 R/W
AD9516_UPDATE_ALL_REGISTERS = 0x232

View File

@ -3,12 +3,13 @@ from artiq.language.types import TInt64, TInt32, TNone, TList
@syscall(flags={"nowrite"})
def rtio_output(time_mu: TInt64, channel: TInt32, addr: TInt32, data: TInt32) -> TNone:
def rtio_output(time_mu: TInt64, channel: TInt32, addr: TInt32, data: TInt32
) -> TNone:
raise NotImplementedError("syscall not simulated")
@syscall(flags={"nowrite"})
def rtio_output_list(time_mu: TInt64, channel: TInt32, addr: TInt32,
def rtio_output_wide(time_mu: TInt64, channel: TInt32, addr: TInt32,
data: TList(TInt32)) -> TNone:
raise NotImplementedError("syscall not simulated")

59
artiq/coredevice/sawg.py Normal file
View File

@ -0,0 +1,59 @@
from artiq.coredevice.spline import Spline
class SAWG:
"""Smart arbitrary waveform generator channel.
The channel is parametrized as: ::
oscillators = exp(2j*pi*(frequency0*t + phase0))*(
amplitude1*exp(2j*pi*(frequency1*t + phase1)) +
amplitude2*exp(2j*pi*(frequency2*t + phase2)))
output = (offset +
i_enable*Re(oscillators) +
q_enable*Im(buddy_oscillators))
The nine spline interpolators are accessible as attributes:
* :attr:`offset`, :attr:`amplitude1`, :attr:`amplitude2`: in units
of full scale
* :attr:`phase0`, :attr:`phase1`, :attr:`phase2`: in units of turns
* :attr:`frequency0`, :attr:`frequency1`, :attr:`frequency2`: in units
of Hz
:param channel_base: RTIO channel number of the first channel (amplitude).
Frequency and Phase are then assumed to be successive channels.
:param parallelism: Number of output samples per coarse RTIO clock cycle.
:param core_device: Name of the core device that this SAWG is on.
"""
kernel_invariants = {"channel_base", "core",
"amplitude1", "frequency1", "phase1",
"amplitude2", "frequency2", "phase2",
"frequency0", "phase0", "offset"}
def __init__(self, dmgr, channel_base, parallelism, core_device="core"):
self.core = dmgr.get(core_device)
self.channel_base = channel_base
width = 16
time_width = 16
cordic_gain = 1.646760258057163 # Cordic(width=16, guard=None).gain
# cfg: channel_base
self.offset = Spline(width, time_width, channel_base + 1,
self.core, 2.)
self.amplitude1 = Spline(width, time_width, channel_base + 2,
self.core, 2*cordic_gain**2)
self.frequency1 = Spline(3*width, time_width, channel_base + 3,
self.core, 1/self.core.coarse_ref_period)
self.phase1 = Spline(width, time_width, channel_base + 4,
self.core, 1.)
self.amplitude2 = Spline(width, time_width, channel_base + 5,
self.core, 2*cordic_gain**2)
self.frequency2 = Spline(3*width, time_width, channel_base + 6,
self.core, 1/self.core.coarse_ref_period)
self.phase2 = Spline(width, time_width, channel_base + 7,
self.core, 1.)
self.frequency0 = Spline(2*width, time_width, channel_base + 8,
self.core,
parallelism/self.core.coarse_ref_period)
self.phase0 = Spline(width, time_width, channel_base + 9,
self.core, 1.)

222
artiq/coredevice/spline.py Normal file
View File

@ -0,0 +1,222 @@
from numpy import int32, int64
from artiq.language.core import kernel, now_mu, portable, delay
from artiq.coredevice.rtio import rtio_output, rtio_output_wide
from artiq.language.types import TInt32, TInt64, TFloat
class Spline:
r"""Spline interpolating RTIO channel.
One knot of a polynomial basis spline (B-spline) :math:`u(t)`
is defined by the coefficients :math:`u_n` up to order :math:`n = k`.
If the knot is evaluated starting at time :math:`t_0`, the output
:math:`u(t)` for :math:`t > t_0, t_0` is:
.. math::
u(t) &= \sum_{n=0}^k \frac{u_n}{n!} (t - t_0)^n \\
&= u_0 + u_1 (t - t_0) + \frac{u_2}{2} (t - t_0)^2 + \dots
:param width: Width in bits of the quantity that this spline controls
:param time_width: Width in bits of the time counter of this spline
:param channel: RTIO channel number
:param core_device: Core device that this spline is attached to
:param scale: Scale for conversion between machine units and physical
units; to be given as the "full scale physical value".
"""
kernel_invariants = {"channel", "core", "scale", "width",
"time_width", "time_scale"}
def __init__(self, width, time_width, channel, core_device, scale=1.):
self.core = core_device
self.channel = channel
self.width = width
self.scale = float((int64(1) << width) / scale)
self.time_width = time_width
self.time_scale = float((1 << time_width) *
core_device.coarse_ref_period)
@portable(flags={"fast-math"})
def to_mu(self, value: TFloat) -> TInt32:
"""Convert floating point `value` from physical units to 32 bit
integer machine units."""
return int32(round(value*self.scale))
@portable(flags={"fast-math"})
def from_mu(self, value: TInt32) -> TFloat:
"""Convert 32 bit integer `value` from machine units to floating point
physical units."""
return value/self.scale
@portable(flags={"fast-math"})
def to_mu64(self, value: TFloat) -> TInt64:
"""Convert floating point `value` from physical units to 64 bit
integer machine units."""
return int64(round(value*self.scale))
@kernel
def set_mu(self, value: TInt32):
"""Set spline value (machine units).
:param value: Spline value in integer machine units.
"""
rtio_output(now_mu(), self.channel, 0, value)
@kernel(flags={"fast-math"})
def set(self, value: TFloat):
"""Set spline value.
:param value: Spline value relative to full-scale.
"""
if self.width > 32:
l = [int32(0)] * 2
self.pack_coeff_mu([self.to_mu64(value)], l)
rtio_output_wide(now_mu(), self.channel, 0, l)
else:
rtio_output(now_mu(), self.channel, 0, self.to_mu(value))
@kernel
def set_coeff_mu(self, value): # TList(TInt32)
"""Set spline raw values.
:param value: Spline packed raw values.
"""
rtio_output_wide(now_mu(), self.channel, 0, value)
@portable(flags={"fast-math"})
def pack_coeff_mu(self, coeff, packed): # TList(TInt64), TList(TInt32)
"""Pack coefficients into RTIO data
:param coeff: TList(TInt64) list of machine units spline coefficients.
Lowest (zeroth) order first. The coefficient list is zero-extended
by the RTIO gateware.
:param packed: TList(TInt32) list for packed RTIO data. Must be
pre-allocated. Length in bits is
`n*width + (n - 1)*n//2*time_width`
"""
pos = 0
for i in range(len(coeff)):
wi = self.width + i*self.time_width
ci = coeff[i]
while wi != 0:
j = pos//32
used = pos - 32*j
avail = 32 - used
if avail > wi:
avail = wi
cij = int32(ci)
if avail != 32:
cij &= (1 << avail) - 1
packed[j] |= cij << used
ci >>= avail
wi -= avail
pos += avail
@portable(flags={"fast-math"})
def coeff_to_mu(self, coeff, coeff64): # TList(TFloat), TList(TInt64)
"""Convert a floating point list of coefficients into a 64 bit
integer (preallocated).
:param coeff: TList(TFloat) list of coefficients in physical units.
:param coeff64: TList(TInt64) preallocated list of coefficients in
machine units.
"""
for i in range(len(coeff)):
vi = coeff[i] * self.scale
for j in range(i):
vi *= self.time_scale
ci = int64(round(vi))
coeff64[i] = ci
# artiq.wavesynth.coefficients.discrete_compensate:
if i == 2:
coeff64[1] += ci >> self.time_width + 1
elif i == 3:
coeff64[2] += ci >> self.time_width
coeff64[1] += ci // 6 >> 2*self.time_width
def coeff_as_packed_mu(self, coeff64):
"""Pack 64 bit integer machine units coefficients into 32 bit integer
RTIO data list.
This is a host-only method that can be used to generate packed
spline knot data to be frozen into kernels at compile time.
"""
n = len(coeff64)
width = n*self.width + (n - 1)*n//2*self.time_width
packed = [int32(0)] * ((width + 31)//32)
self.pack_coeff_mu(coeff64, packed)
return packed
def coeff_as_packed(self, coeff):
"""Convert floating point spline coefficients into 32 bit integer
packed data.
This is a host-only method that can be used to generate packed
spline knot data to be frozen into kernels at compile time.
"""
coeff64 = [int64(0)] * len(coeff)
self.coeff_to_mu(coeff, coeff64)
return self.coeff_as_packed_mu(coeff64)
@kernel(flags={"fast-math"})
def set_coeff(self, coeff): # TList(TFloat)
"""Set spline coefficients.
Missing coefficients (high order) are zero-extended byt the RTIO
gateware.
If more coefficients are supplied than the gateware supports the extra
coefficients are ignored.
:param value: List of floating point spline knot coefficients,
lowest order (constant) coefficient first. Units are the
unit of this spline's value times increasing powers of 1/s.
"""
n = len(coeff)
coeff64 = [int64(0)] * n
self.coeff_to_mu(coeff, coeff64)
width = n*self.width + (n - 1)*n//2*self.time_width
packed = [int32(0)] * ((width + 31)//32)
self.pack_coeff_mu(coeff64, packed)
self.set_coeff_mu(packed)
@kernel(flags={"fast-math"})
def smooth(self, start: TFloat, stop: TFloat, duration: TFloat,
order: TInt32):
"""Initiate an interpolated value change.
For zeroth order (step) interpolation, the step is at
`start + duration/2`.
First order interpolation corresponds to a linear value ramp from
`start` to `stop` over `duration`.
The third order interpolation is constrained to have zero first
order derivative at both `start` and `stop`.
For first order and third order interpolation (linear and cubic)
the interpolator needs to be stopped (or fed a new spline knot)
explicitly at the stop time.
This method advances the timeline by `duration`.
:param start: Initial value of the change. In physical units.
:param stop: Final value of the change. In physical units.
:param duration: Duration of the interpolation. In physical units.
:param order: Order of the interpolation. Only 0, 1,
and 3 are valid: step, linear, cubic.
"""
if order == 0:
delay(duration/2.)
self.set_coeff([stop])
delay(duration/2.)
elif order == 1:
self.set_coeff([start, (stop - start)/duration])
delay(duration)
elif order == 3:
v2 = 6.*(stop - start)/(duration*duration)
self.set_coeff([start, 0., v2, -2.*v2/duration])
delay(duration)
else:
raise ValueError("Invalid interpolation order. "
"Supported orders are: 0, 1, 3.")

View File

@ -0,0 +1,77 @@
# The RTIO channel numbers here are for Phaser on KC705.
{
"comm": {
"type": "local",
"module": "artiq.coredevice.comm_tcp",
"class": "Comm",
"arguments": {"host": "kc705aux.lab.m-labs.hk"}
},
"core": {
"type": "local",
"module": "artiq.coredevice.core",
"class": "Core",
"arguments": {
"ref_period": 5e-9/6,
"external_clock": True
}
},
"core_cache": {
"type": "local",
"module": "artiq.coredevice.cache",
"class": "CoreCache"
},
"ad9154": {
"type": "local",
"module": "artiq.coredevice.ad9154",
"class": "AD9154"
},
"ttl_sma": {
"type": "local",
"module": "artiq.coredevice.ttl",
"class": "TTLInOut",
"arguments": {"channel": 0}
},
"led": {
"type": "local",
"module": "artiq.coredevice.ttl",
"class": "TTLOut",
"arguments": {"channel": 1}
},
"sysref": {
"type": "local",
"module": "artiq.coredevice.ttl",
"class": "TTLInOut",
"arguments": {"channel": 2}
},
"sync": {
"type": "local",
"module": "artiq.coredevice.ttl",
"class": "TTLInOut",
"arguments": {"channel": 3}
},
"sawg0": {
"type": "local",
"module": "artiq.coredevice.sawg",
"class": "SAWG",
"arguments": {"channel_base": 4, "parallelism": 2}
},
"sawg1": {
"type": "local",
"module": "artiq.coredevice.sawg",
"class": "SAWG",
"arguments": {"channel_base": 14, "parallelism": 2}
},
"sawg2": {
"type": "local",
"module": "artiq.coredevice.sawg",
"class": "SAWG",
"arguments": {"channel_base": 24, "parallelism": 2}
},
"sawg3": {
"type": "local",
"module": "artiq.coredevice.sawg",
"class": "SAWG",
"arguments": {"channel_base": 34, "parallelism": 2}
}
}

View File

@ -0,0 +1,362 @@
from jesd204b.common import (JESD204BPhysicalSettings,
JESD204BTransportSettings,
JESD204BSettings)
from artiq.experiment import *
from artiq.coredevice.ad9154_reg import *
# ad9154 mode 2:
ps = JESD204BPhysicalSettings(
l=4, # lanes
m=4, # converters
n=16, # bits/converter
np=16, # bits/sample
)
ts = JESD204BTransportSettings(
f=2, # octets/(lane and frame)
s=1, # samples/(converter and frame)
k=16, # frames/multiframe
cs=1, #
)
jesd_settings = JESD204BSettings(ps, ts, did=0x5a, bid=0x5)
jesd_checksum = jesd_settings.get_configuration_checksum()
# external clk=300MHz
# pclock=150MHz
# deviceclock_fpga=150MHz
# deviceclock_dac=300MHz
class DACSetup(EnvExperiment):
def build(self):
self.setattr_device("core")
self.setattr_device("led")
self.setattr_device("ad9154")
self.setattr_device("sync")
@kernel
def run(self):
# TODO; remove when
# https://github.com/m-labs/jesd204b/issues/6
# is resolved
for i in range(99):
try:
self.cfg()
return
except:
pass
self.cfg()
@kernel
def cfg(self):
self.core.reset()
self.ad9154.jesd_enable(0)
self.ad9154.jesd_prbs(0)
self.ad9154.jesd_stpl(0)
self.busywait_us(10000)
self.ad9154.jesd_enable(1)
self.ad9154.init()
self.dac_setup()
self.ad9154.jesd_enable(0)
self.busywait_us(10000)
self.ad9154.jesd_enable(1)
self.monitor()
while not self.ad9154.jesd_ready():
pass
self.busywait_us(10000)
if self.ad9154.dac_read(AD9154_CODEGRPSYNCFLG) != 0x0f:
raise ValueError("bad CODEGRPSYNCFLG")
self.core.break_realtime()
if not self.sync.sample_get_nonrt():
raise ValueError("bad SYNC")
if self.ad9154.dac_read(AD9154_FRAMESYNCFLG) != 0x0f:
raise ValueError("bad FRAMESYNCFLG")
if self.ad9154.dac_read(AD9154_GOODCHKSUMFLG) != 0x0f:
raise ValueError("bad GOODCHECKSUMFLG")
if self.ad9154.dac_read(AD9154_INITLANESYNCFLG) != 0x0f:
raise ValueError("bad INITLANESYNCFLG")
@kernel
def busywait_us(self, t):
t = self.core.get_rtio_counter_mu() + self.core.seconds_to_mu(t*us)
while self.core.get_rtio_counter_mu() < t:
pass
@kernel
def dac_setup(self):
# reset
self.ad9154.dac_write(AD9154_SPI_INTFCONFA,
AD9154_SOFTRESET_M_SET(1) | AD9154_SOFTRESET_SET(1) |
AD9154_LSBFIRST_M_SET(0) | AD9154_LSBFIRST_SET(0) |
AD9154_ADDRINC_M_SET(0) | AD9154_ADDRINC_SET(0) |
AD9154_SDOACTIVE_M_SET(1) | AD9154_SDOACTIVE_SET(1))
self.busywait_us(100)
self.ad9154.dac_write(AD9154_SPI_INTFCONFA,
AD9154_SOFTRESET_M_SET(0) | AD9154_SOFTRESET_SET(0) |
AD9154_LSBFIRST_M_SET(0) | AD9154_LSBFIRST_SET(0) |
AD9154_ADDRINC_M_SET(0) | AD9154_ADDRINC_SET(0) |
AD9154_SDOACTIVE_M_SET(1) | AD9154_SDOACTIVE_SET(1))
self.busywait_us(100)
if ((self.ad9154.dac_read(AD9154_PRODIDH) << 8) |
self.ad9154.dac_read(AD9154_PRODIDL) != 0x9154):
raise ValueError("AD9154 not found")
self.ad9154.dac_write(AD9154_PWRCNTRL0,
AD9154_PD_DAC0_SET(0) | AD9154_PD_DAC1_SET(0) |
AD9154_PD_DAC2_SET(0) | AD9154_PD_DAC3_SET(0) |
AD9154_PD_BG_SET(0))
self.busywait_us(100)
self.ad9154.dac_write(AD9154_TXENMASK1, AD9154_DACA_MASK_SET(0) |
AD9154_DACB_MASK_SET(0)) # TX not controlled by TXEN pins
self.ad9154.dac_write(AD9154_CLKCFG0,
AD9154_REF_CLKDIV_EN_SET(0) | AD9154_RF_SYNC_EN_SET(1) |
AD9154_DUTY_EN_SET(1) | AD9154_PD_CLK_REC_SET(0) |
AD9154_PD_SERDES_PCLK_SET(0) | AD9154_PD_CLK_DIG_SET(0) |
AD9154_PD_CLK23_SET(0) | AD9154_PD_CLK01_SET(0))
self.ad9154.dac_write(AD9154_DACPLLCNTRL,
AD9154_ENABLE_DACPLL_SET(0) | AD9154_RECAL_DACPLL_SET(0))
self.ad9154.dac_write(AD9154_SYSREF_ACTRL0, # jesd204b subclass 1
AD9154_HYS_CNTRL1_SET(0) | AD9154_SYSREF_RISE_SET(0) |
AD9154_HYS_ON_SET(0) | AD9154_PD_SYSREF_BUFFER_SET(0))
self.ad9154.dac_write(AD9154_DEVICE_CONFIG_REG_0, 0x8b) # magic
self.ad9154.dac_write(AD9154_DEVICE_CONFIG_REG_1, 0x01) # magic
self.ad9154.dac_write(AD9154_DEVICE_CONFIG_REG_2, 0x01) # magic
self.ad9154.dac_write(AD9154_SPI_PAGEINDX, 0x3) # A and B dual
self.ad9154.dac_write(AD9154_INTERP_MODE, 0) # 1x
self.ad9154.dac_write(AD9154_MIX_MODE, 0)
self.ad9154.dac_write(AD9154_DATA_FORMAT, AD9154_BINARY_FORMAT_SET(0)) # s16
self.ad9154.dac_write(AD9154_DATAPATH_CTRL,
AD9154_I_TO_Q_SET(0) | AD9154_SEL_SIDEBAND_SET(0) |
AD9154_MODULATION_TYPE_SET(0) | AD9154_PHASE_ADJ_ENABLE_SET(0) |
AD9154_DIG_GAIN_ENABLE_SET(1) | AD9154_INVSINC_ENABLE_SET(0))
self.ad9154.dac_write(AD9154_IDAC_DIG_GAIN0, 0x00)
self.ad9154.dac_write(AD9154_IDAC_DIG_GAIN1, 0x8)
self.ad9154.dac_write(AD9154_QDAC_DIG_GAIN0, 0x00)
self.ad9154.dac_write(AD9154_QDAC_DIG_GAIN1, 0x8)
self.ad9154.dac_write(AD9154_DC_OFFSET_CTRL, 0)
self.ad9154.dac_write(AD9154_IPATH_DC_OFFSET_1PART0, 0x00)
self.ad9154.dac_write(AD9154_IPATH_DC_OFFSET_1PART1, 0x00)
self.ad9154.dac_write(AD9154_IPATH_DC_OFFSET_2PART, 0x00)
self.ad9154.dac_write(AD9154_QPATH_DC_OFFSET_1PART0, 0x00)
self.ad9154.dac_write(AD9154_QPATH_DC_OFFSET_1PART1, 0x00)
self.ad9154.dac_write(AD9154_QPATH_DC_OFFSET_2PART, 0x00)
self.ad9154.dac_write(AD9154_PHASE_ADJ0, 0)
self.ad9154.dac_write(AD9154_PHASE_ADJ1, 0)
self.ad9154.dac_write(AD9154_GROUP_DLY, AD9154_COARSE_GROUP_DELAY_SET(0x8) |
AD9154_GROUP_DELAY_RESERVED_SET(0x8))
self.ad9154.dac_write(AD9154_GROUPDELAY_COMP_BYP,
AD9154_GROUPCOMP_BYPQ_SET(1) |
AD9154_GROUPCOMP_BYPI_SET(1))
self.ad9154.dac_write(AD9154_GROUPDELAY_COMP_I, 0)
self.ad9154.dac_write(AD9154_GROUPDELAY_COMP_Q, 0)
self.ad9154.dac_write(AD9154_PDP_AVG_TIME, AD9154_PDP_ENABLE_SET(0))
self.ad9154.dac_write(AD9154_MASTER_PD, 0)
self.ad9154.dac_write(AD9154_PHY_PD, 0x0f) # power down lanes 0-3
self.ad9154.dac_write(AD9154_GENERIC_PD,
AD9154_PD_SYNCOUT0B_SET(0) |
AD9154_PD_SYNCOUT1B_SET(1))
self.ad9154.dac_write(AD9154_GENERAL_JRX_CTRL_0,
AD9154_LINK_EN_SET(0x0) | AD9154_LINK_PAGE_SET(0) |
AD9154_LINK_MODE_SET(0) | AD9154_CHECKSUM_MODE_SET(0))
self.ad9154.dac_write(AD9154_ILS_DID, jesd_settings.did)
self.ad9154.dac_write(AD9154_ILS_BID, jesd_settings.bid)
self.ad9154.dac_write(AD9154_ILS_LID0, 0x00) # lane id
self.ad9154.dac_write(AD9154_ILS_SCR_L,
AD9154_L_1_SET(jesd_settings.phy.l - 1) |
AD9154_SCR_SET(1))
self.ad9154.dac_write(AD9154_ILS_F, jesd_settings.transport.f - 1)
self.ad9154.dac_write(AD9154_ILS_K, jesd_settings.transport.k - 1)
self.ad9154.dac_write(AD9154_ILS_M, jesd_settings.phy.m - 1)
self.ad9154.dac_write(AD9154_ILS_CS_N,
AD9154_N_1_SET(jesd_settings.phy.n - 1) |
AD9154_CS_SET(0))
self.ad9154.dac_write(AD9154_ILS_NP,
AD9154_NP_1_SET(jesd_settings.phy.np - 1) |
AD9154_SUBCLASSV_SET(jesd_settings.phy.subclassv))
self.ad9154.dac_write(AD9154_ILS_S,
AD9154_S_1_SET(jesd_settings.transport.s - 1) |
AD9154_JESDV_SET(jesd_settings.phy.jesdv))
self.ad9154.dac_write(AD9154_ILS_HD_CF,
AD9154_HD_SET(0) | AD9154_CF_SET(0))
self.ad9154.dac_write(AD9154_ILS_CHECKSUM, jesd_checksum)
self.ad9154.dac_write(AD9154_LANEDESKEW, 0x0f)
for i in range(8):
self.ad9154.dac_write(AD9154_BADDISPARITY, AD9154_RST_IRQ_DIS_SET(0) |
AD9154_DISABLE_ERR_CNTR_DIS_SET(0) |
AD9154_RST_ERR_CNTR_DIS_SET(1) | AD9154_LANE_ADDR_DIS_SET(i))
self.ad9154.dac_write(AD9154_BADDISPARITY, AD9154_RST_IRQ_DIS_SET(0) |
AD9154_DISABLE_ERR_CNTR_DIS_SET(0) |
AD9154_RST_ERR_CNTR_DIS_SET(0) | AD9154_LANE_ADDR_DIS_SET(i))
self.ad9154.dac_write(AD9154_NIT_W, AD9154_RST_IRQ_NIT_SET(0) |
AD9154_DISABLE_ERR_CNTR_NIT_SET(0) |
AD9154_RST_ERR_CNTR_NIT_SET(1) | AD9154_LANE_ADDR_NIT_SET(i))
self.ad9154.dac_write(AD9154_NIT_W, AD9154_RST_IRQ_NIT_SET(0) |
AD9154_DISABLE_ERR_CNTR_NIT_SET(0) |
AD9154_RST_ERR_CNTR_NIT_SET(0) | AD9154_LANE_ADDR_NIT_SET(i))
self.ad9154.dac_write(AD9154_UNEXPECTEDCONTROL_W, AD9154_RST_IRQ_UCC_SET(0) |
AD9154_DISABLE_ERR_CNTR_UCC_SET(0) |
AD9154_RST_ERR_CNTR_UCC_SET(1) | AD9154_LANE_ADDR_UCC_SET(i))
self.ad9154.dac_write(AD9154_BADDISPARITY, AD9154_RST_IRQ_UCC_SET(0) |
AD9154_DISABLE_ERR_CNTR_UCC_SET(0) |
AD9154_RST_ERR_CNTR_UCC_SET(0) | AD9154_LANE_ADDR_UCC_SET(i))
self.ad9154.dac_write(AD9154_CTRLREG1, jesd_settings.transport.f)
self.ad9154.dac_write(AD9154_CTRLREG2, AD9154_ILAS_MODE_SET(0) |
AD9154_THRESHOLD_MASK_EN_SET(0))
self.ad9154.dac_write(AD9154_KVAL, 1) # *4*K multiframes during ILAS
self.ad9154.dac_write(AD9154_LANEENABLE, 0x0f) # CGS _after_ this
self.ad9154.dac_write(AD9154_TERM_BLK1_CTRLREG0, 1)
self.ad9154.dac_write(AD9154_TERM_BLK2_CTRLREG0, 1)
self.ad9154.dac_write(AD9154_SERDES_SPI_REG, 1)
self.ad9154.dac_write(AD9154_CDR_OPERATING_MODE_REG_0,
AD9154_CDR_OVERSAMP_SET(0) | AD9154_CDR_RESERVED_SET(0x2) |
AD9154_ENHALFRATE_SET(1))
self.ad9154.dac_write(AD9154_CDR_RESET, 0)
self.ad9154.dac_write(AD9154_CDR_RESET, 1)
self.ad9154.dac_write(AD9154_REF_CLK_DIVIDER_LDO,
AD9154_SPI_CDR_OVERSAMP_SET(0x0) |
AD9154_SPI_LDO_BYPASS_FILT_SET(1) |
AD9154_SPI_LDO_REF_SEL_SET(0))
self.ad9154.dac_write(AD9154_LDO_FILTER_1, 0x62) # magic
self.ad9154.dac_write(AD9154_LDO_FILTER_2, 0xc9) # magic
self.ad9154.dac_write(AD9154_LDO_FILTER_3, 0x0e) # magic
self.ad9154.dac_write(AD9154_CP_CURRENT_SPI,
AD9154_SPI_CP_CURRENT_SET(0x12) |
AD9154_SPI_SERDES_LOGEN_POWER_MODE_SET(0))
self.ad9154.dac_write(AD9154_VCO_LDO, 0x7b) # magic
self.ad9154.dac_write(AD9154_PLL_RD_REG,
AD9154_SPI_SERDES_LOGEN_PD_CORE_SET(0) |
AD9154_SPI_SERDES_LDO_PD_SET(0) | AD9154_SPI_SYN_PD_SET(0) |
AD9154_SPI_VCO_PD_ALC_SET(0) | AD9154_SPI_VCO_PD_PTAT_SET(0) |
AD9154_SPI_VCO_PD_SET(0))
self.ad9154.dac_write(AD9154_ALC_VARACTOR,
AD9154_SPI_VCO_VARACTOR_SET(0x9) |
AD9154_SPI_INIT_ALC_VALUE_SET(0x8))
self.ad9154.dac_write(AD9154_VCO_OUTPUT,
AD9154_SPI_VCO_OUTPUT_LEVEL_SET(0xc) |
AD9154_SPI_VCO_OUTPUT_RESERVED_SET(0x4))
self.ad9154.dac_write(AD9154_CP_CONFIG,
AD9154_SPI_CP_TEST_SET(0) |
AD9154_SPI_CP_CAL_EN_SET(1) |
AD9154_SPI_CP_FORCE_CALBITS_SET(0) |
AD9154_SPI_CP_OFFSET_OFF_SET(0) |
AD9154_SPI_CP_ENABLE_MACHINE_SET(1) |
AD9154_SPI_CP_DITHER_MODE_SET(0) |
AD9154_SPI_CP_HALF_VCO_CAL_CLK_SET(0))
self.ad9154.dac_write(AD9154_VCO_BIAS_1,
AD9154_SPI_VCO_BIAS_REF_SET(0x3) |
AD9154_SPI_VCO_BIAS_TCF_SET(0x3))
self.ad9154.dac_write(AD9154_VCO_BIAS_2,
AD9154_SPI_PRESCALE_BIAS_SET(0x1) |
AD9154_SPI_LAST_ALC_EN_SET(1) |
AD9154_SPI_PRESCALE_BYPASS_R_SET(0x1) |
AD9154_SPI_VCO_COMP_BYPASS_BIASR_SET(0) |
AD9154_SPI_VCO_BYPASS_DAC_R_SET(0))
self.ad9154.dac_write(AD9154_VCO_PD_OVERRIDES,
AD9154_SPI_VCO_PD_OVERRIDE_VCO_BUF_SET(0) |
AD9154_SPI_VCO_PD_OVERRIDE_CAL_TCF_SET(1) |
AD9154_SPI_VCO_PD_OVERRIDE_VAR_REF_TCF_SET(0) |
AD9154_SPI_VCO_PD_OVERRIDE_VAR_REF_SET(0))
self.ad9154.dac_write(AD9154_VCO_CAL,
AD9154_SPI_FB_CLOCK_ADV_SET(0x2) |
AD9154_SPI_VCO_CAL_COUNT_SET(0x3) |
AD9154_SPI_VCO_CAL_ALC_WAIT_SET(0) |
AD9154_SPI_VCO_CAL_EN_SET(1))
self.ad9154.dac_write(AD9154_CP_LEVEL_DETECT,
AD9154_SPI_CP_LEVEL_THRESHOLD_HIGH_SET(0x2) |
AD9154_SPI_CP_LEVEL_THRESHOLD_LOW_SET(0x5) |
AD9154_SPI_CP_LEVEL_DET_PD_SET(0))
self.ad9154.dac_write(AD9154_VCO_VARACTOR_CTRL_0,
AD9154_SPI_VCO_VARACTOR_OFFSET_SET(0xe) |
AD9154_SPI_VCO_VARACTOR_REF_TCF_SET(0x7))
self.ad9154.dac_write(AD9154_VCO_VARACTOR_CTRL_1,
AD9154_SPI_VCO_VARACTOR_REF_SET(0x6))
# ensure link is txing
#self.ad9154.dac_write(AD9154_SERDESPLL_ENABLE_CNTRL,
# AD9154_ENABLE_SERDESPLL_SET(1) | AD9154_RECAL_SERDESPLL_SET(1))
self.ad9154.dac_write(AD9154_SERDESPLL_ENABLE_CNTRL,
AD9154_ENABLE_SERDESPLL_SET(1) | AD9154_RECAL_SERDESPLL_SET(0))
while not AD9154_SERDES_PLL_LOCK_RB_GET(self.ad9154.dac_read(AD9154_PLL_STATUS)):
pass
self.ad9154.dac_write(AD9154_EQ_BIAS_REG, AD9154_EQ_BIAS_RESERVED_SET(0x22) |
AD9154_EQ_POWER_MODE_SET(1))
self.ad9154.dac_write(AD9154_GENERAL_JRX_CTRL_1, 1) # subclass 1
self.ad9154.dac_write(AD9154_LMFC_DELAY_0, 0)
self.ad9154.dac_write(AD9154_LMFC_DELAY_1, 0)
self.ad9154.dac_write(AD9154_LMFC_VAR_0, 0x0a) # receive buffer delay
self.ad9154.dac_write(AD9154_LMFC_VAR_1, 0x0a)
self.ad9154.dac_write(AD9154_SYNC_ERRWINDOW, 0) # +- 1/2 DAC clock
self.ad9154.dac_write(AD9154_SYNC_CONTROL,
AD9154_SYNCMODE_SET(0x9) | AD9154_SYNCENABLE_SET(0) |
AD9154_SYNCARM_SET(0) | AD9154_SYNCCLRSTKY_SET(1) |
AD9154_SYNCCLRLAST_SET(1))
self.ad9154.dac_write(AD9154_SYNC_CONTROL,
AD9154_SYNCMODE_SET(0x9) | AD9154_SYNCENABLE_SET(1) |
AD9154_SYNCARM_SET(0) | AD9154_SYNCCLRSTKY_SET(1) |
AD9154_SYNCCLRLAST_SET(1))
self.ad9154.dac_write(AD9154_SYNC_CONTROL,
AD9154_SYNCMODE_SET(0x9) | AD9154_SYNCENABLE_SET(1) |
AD9154_SYNCARM_SET(1) | AD9154_SYNCCLRSTKY_SET(0) |
AD9154_SYNCCLRLAST_SET(0))
self.busywait_us(1000) # ensure at leas one sysref edge
if not AD9154_SYNC_LOCK_GET(self.ad9154.dac_read(AD9154_SYNC_STATUS)):
raise ValueError("no sync lock")
self.ad9154.dac_write(AD9154_XBAR_LN_0_1,
AD9154_LOGICAL_LANE0_SRC_SET(7) | AD9154_LOGICAL_LANE1_SRC_SET(6))
self.ad9154.dac_write(AD9154_XBAR_LN_2_3,
AD9154_LOGICAL_LANE2_SRC_SET(5) | AD9154_LOGICAL_LANE3_SRC_SET(4))
self.ad9154.dac_write(AD9154_XBAR_LN_4_5,
AD9154_LOGICAL_LANE4_SRC_SET(0) | AD9154_LOGICAL_LANE5_SRC_SET(0))
self.ad9154.dac_write(AD9154_XBAR_LN_6_7,
AD9154_LOGICAL_LANE6_SRC_SET(0) | AD9154_LOGICAL_LANE7_SRC_SET(0))
self.ad9154.dac_write(AD9154_JESD_BIT_INVERSE_CTRL, 0x00)
self.ad9154.dac_write(AD9154_GENERAL_JRX_CTRL_0,
AD9154_LINK_EN_SET(0x1) | AD9154_LINK_PAGE_SET(0) |
AD9154_LINK_MODE_SET(0) | AD9154_CHECKSUM_MODE_SET(0))
@kernel
def monitor(self):
self.ad9154.dac_write(AD9154_IRQ_STATUS0, 0x00)
self.ad9154.dac_write(AD9154_IRQ_STATUS1, 0x00)
self.ad9154.dac_write(AD9154_IRQ_STATUS2, 0x00)
self.ad9154.dac_write(AD9154_IRQ_STATUS3, 0x00)
self.ad9154.dac_write(AD9154_IRQEN_STATUSMODE0,
AD9154_IRQEN_SMODE_LANEFIFOERR_SET(1) |
AD9154_IRQEN_SMODE_SERPLLLOCK_SET(1) |
AD9154_IRQEN_SMODE_SERPLLLOST_SET(1) |
AD9154_IRQEN_SMODE_DACPLLLOCK_SET(1) |
AD9154_IRQEN_SMODE_DACPLLLOST_SET(1))
self.ad9154.dac_write(AD9154_IRQEN_STATUSMODE1,
AD9154_IRQEN_SMODE_PRBS0_SET(1) |
AD9154_IRQEN_SMODE_PRBS1_SET(1) |
AD9154_IRQEN_SMODE_PRBS2_SET(1) |
AD9154_IRQEN_SMODE_PRBS3_SET(1))
self.ad9154.dac_write(AD9154_IRQEN_STATUSMODE2,
AD9154_IRQEN_SMODE_SYNC_TRIP0_SET(1) |
AD9154_IRQEN_SMODE_SYNC_WLIM0_SET(1) |
AD9154_IRQEN_SMODE_SYNC_ROTATE0_SET(1) |
AD9154_IRQEN_SMODE_SYNC_LOCK0_SET(1) |
AD9154_IRQEN_SMODE_NCO_ALIGN0_SET(1) |
AD9154_IRQEN_SMODE_BLNKDONE0_SET(1) |
AD9154_IRQEN_SMODE_PDPERR0_SET(1))
self.ad9154.dac_write(AD9154_IRQEN_STATUSMODE3,
AD9154_IRQEN_SMODE_SYNC_TRIP1_SET(1) |
AD9154_IRQEN_SMODE_SYNC_WLIM1_SET(1) |
AD9154_IRQEN_SMODE_SYNC_ROTATE1_SET(1) |
AD9154_IRQEN_SMODE_SYNC_LOCK1_SET(1) |
AD9154_IRQEN_SMODE_NCO_ALIGN1_SET(1) |
AD9154_IRQEN_SMODE_BLNKDONE1_SET(1) |
AD9154_IRQEN_SMODE_PDPERR1_SET(1))
self.ad9154.dac_write(AD9154_IRQ_STATUS0, 0x00)
self.ad9154.dac_write(AD9154_IRQ_STATUS1, 0x00)
self.ad9154.dac_write(AD9154_IRQ_STATUS2, 0x00)
self.ad9154.dac_write(AD9154_IRQ_STATUS3, 0x00)

View File

@ -0,0 +1,50 @@
from artiq.experiment import *
class SAWGTest(EnvExperiment):
def build(self):
self.setattr_device("core")
self.setattr_device("led")
self.setattr_device("ttl_sma")
self.setattr_device("sawg0")
self.setattr_device("sawg1")
self.setattr_device("sawg2")
self.setattr_device("sawg3")
@kernel
def run(self):
self.core.break_realtime()
self.ttl_sma.output()
while True:
self.sawg0.amplitude1.set(0.)
self.sawg0.frequency0.set(0*MHz)
self.sawg1.amplitude1.set(0.)
self.sawg1.frequency0.set(0*MHz)
delay(20*ms)
self.sawg0.amplitude1.set(.4)
self.sawg0.frequency0.set(10*MHz)
self.sawg0.phase0.set(0.)
self.sawg1.amplitude1.set(.4)
self.sawg1.frequency0.set(10*MHz)
self.sawg1.phase0.set(0.)
self.ttl_sma.pulse(200*ns)
self.sawg1.amplitude1.set(.1)
delay(200*ns)
self.sawg1.amplitude1.set(-.4)
self.ttl_sma.pulse(200*ns)
self.sawg1.amplitude1.set(.4)
delay(200*ns)
self.sawg1.phase0.set(.25)
self.ttl_sma.pulse(200*ns)
self.sawg1.phase0.set(.5)
delay(200*ns)
self.sawg0.phase0.set(.5)
self.ttl_sma.pulse(200*ns)
self.sawg1.frequency0.set(30*MHz)
delay(200*ns)
self.sawg1.frequency0.set(10*MHz)
self.sawg1.phase0.set(0.)
self.ttl_sma.pulse(200*ns)

View File

@ -0,0 +1,46 @@
from artiq.experiment import *
class SAWGTest(EnvExperiment):
def build(self):
self.setattr_device("core")
self.setattr_device("led")
self.setattr_device("ttl_sma")
self.setattr_device("sawg0")
self.setattr_device("sawg1")
self.setattr_device("sawg2")
self.setattr_device("sawg3")
@kernel
def run(self):
self.core.break_realtime()
self.ttl_sma.output()
while True:
t_up = t_hold = t_down = 800*ns
a1 = .3
a2 = .4
order = 3
delay(20*ms)
self.sawg0.frequency0.set(10*MHz)
self.sawg0.phase0.set(0.)
self.sawg0.frequency1.set(1*MHz)
self.sawg0.phase1.set(0.)
self.sawg0.frequency2.set(9*MHz)
self.sawg0.phase2.set(0.)
with parallel:
self.sawg0.amplitude1.smooth(.0, a1, t_up, order)
self.sawg0.amplitude2.smooth(.0, a2, t_up, order)
self.sawg0.amplitude1.set(a1)
self.sawg0.amplitude2.set(a2)
delay(t_hold)
with parallel:
self.sawg0.amplitude1.smooth(a1, .0, t_down, order)
self.sawg0.amplitude2.smooth(a2, .0, t_down, order)
self.sawg0.amplitude1.set(.0)
self.sawg0.amplitude2.set(.0)
self.sawg1.amplitude1.set(.0)
self.sawg1.amplitude2.set(.0)

View File

@ -0,0 +1,60 @@
from artiq.coredevice.ad9154_reg import *
from artiq.experiment import *
class Test(EnvExperiment):
def build(self):
self.setattr_device("core")
self.setattr_device("ad9154")
@kernel
def run(self):
for e in range(2):
for i in range(3): # prbs7, prbs15, prbs31
self.prbs(i, 10, e)
@kernel
def busywait_us(self, t):
t = self.core.get_rtio_counter_mu() + self.core.seconds_to_mu(t*us)
while self.core.get_rtio_counter_mu() < t:
pass
def p(self, f, *a):
print(f.format(*a))
@kernel
def prbs(self, p, t, inject_errors):
self.p("---\nprbs sequence {}, threshold {}, inject_errors {}:",
p, t, inject_errors)
self.ad9154.jesd_prbs((1 << p) | (inject_errors << 3))
self.ad9154.dac_write(AD9154_PHY_PRBS_TEST_CTRL,
AD9154_PHY_PRBS_PAT_SEL_SET(p))
self.ad9154.dac_write(AD9154_PHY_PRBS_TEST_EN, 0xff)
self.ad9154.dac_write(AD9154_PHY_PRBS_TEST_CTRL,
AD9154_PHY_PRBS_PAT_SEL_SET(p) | AD9154_PHY_TEST_RESET_SET(1))
self.ad9154.dac_write(AD9154_PHY_PRBS_TEST_CTRL,
AD9154_PHY_PRBS_PAT_SEL_SET(p))
self.ad9154.dac_write(AD9154_PHY_PRBS_TEST_THRESHOLD_LOBITS, t)
self.ad9154.dac_write(AD9154_PHY_PRBS_TEST_THRESHOLD_MIDBITS, t >> 8)
self.ad9154.dac_write(AD9154_PHY_PRBS_TEST_THRESHOLD_MIDBITS, t >> 16)
self.ad9154.dac_write(AD9154_PHY_PRBS_TEST_CTRL, AD9154_PHY_PRBS_PAT_SEL_SET(p))
self.ad9154.dac_write(AD9154_PHY_PRBS_TEST_CTRL,
AD9154_PHY_PRBS_PAT_SEL_SET(p) | AD9154_PHY_TEST_START_SET(1))
self.busywait_us(1000000)
self.ad9154.dac_write(AD9154_PHY_PRBS_TEST_CTRL, AD9154_PHY_PRBS_PAT_SEL_SET(p))
self.p("prbs status: {:#04x}", self.ad9154.dac_read(AD9154_PHY_PRBS_TEST_STATUS))
for i in range(8):
self.ad9154.dac_write(AD9154_PHY_PRBS_TEST_CTRL, AD9154_PHY_SRC_ERR_CNT_SET(i))
self.p("prbs errors[{}]: {:#08x}", i,
self.ad9154.dac_read(AD9154_PHY_PRBS_TEST_ERRCNT_LOBITS) |
(self.ad9154.dac_read(AD9154_PHY_PRBS_TEST_ERRCNT_MIDBITS) << 8) |
(self.ad9154.dac_read(AD9154_PHY_PRBS_TEST_ERRCNT_HIBITS) << 16))
self.ad9154.jesd_prbs(0)

View File

@ -0,0 +1,138 @@
from artiq.coredevice.ad9154_reg import *
from artiq.experiment import *
class Test(EnvExperiment):
def build(self):
self.setattr_device("core")
self.setattr_device("ad9154")
@kernel
def run(self):
self.print_status()
self.print_temp()
def p(self, f, *a):
print(f % a)
@kernel
def print_temp(self):
self.ad9154.dac_write(AD9154_DIE_TEMP_CTRL0, AD9154_AUXADC_RESERVED_SET(0x10) |
AD9154_AUXADC_ENABLE_SET(1))
self.ad9154.dac_write(AD9154_DIE_TEMP_UPDATE, 1)
self.p("temp_code %d", self.ad9154.dac_read(AD9154_DIE_TEMP0) |
(self.ad9154.dac_read(AD9154_DIE_TEMP1) << 8))
self.ad9154.dac_write(AD9154_DIE_TEMP_CTRL0, AD9154_AUXADC_RESERVED_SET(0x10) |
AD9154_AUXADC_ENABLE_SET(0))
@kernel
def print_status(self):
x = self.ad9154.dac_read(AD9154_IRQ_STATUS0)
self.p("LANEFIFOERR: %d, SERPLLLOCK: %d, SERPLLLOST: %d, "
"DACPLLLOCK: %d, DACPLLLOST: %d",
AD9154_LANEFIFOERR_GET(x), AD9154_SERPLLLOCK_GET(x),
AD9154_SERPLLLOST_GET(x), AD9154_DACPLLLOCK_GET(x),
AD9154_DACPLLLOST_GET(x))
x = self.ad9154.dac_read(AD9154_IRQ_STATUS1)
self.p("PRBS0: %d, PRBS1: %d, PRBS2: %d, PRBS3: %d",
AD9154_PRBS0_GET(x), AD9154_PRBS1_GET(x),
AD9154_PRBS2_GET(x), AD9154_PRBS3_GET(x))
x = self.ad9154.dac_read(AD9154_IRQ_STATUS2)
self.p("SYNC_TRIP0: %d, SYNC_WLIM0: %d, SYNC_ROTATE0: %d, "
"SYNC_LOCK0: %d, NCO_ALIGN0: %d, BLNKDONE0: %d, "
"PDPERR0: %d",
AD9154_SYNC_TRIP0_GET(x), AD9154_SYNC_WLIM0_GET(x),
AD9154_SYNC_ROTATE0_GET(x), AD9154_SYNC_LOCK0_GET(x),
AD9154_NCO_ALIGN0_GET(x), AD9154_BLNKDONE0_GET(x),
AD9154_PDPERR0_GET(x))
x = self.ad9154.dac_read(AD9154_IRQ_STATUS3)
self.p("SYNC_TRIP1: %d, SYNC_WLIM1: %d, SYNC_ROTATE1: %d, "
"SYNC_LOCK1: %d, NCO_ALIGN1: %d, BLNKDONE1: %d, "
"PDPERR1: %d",
AD9154_SYNC_TRIP1_GET(x), AD9154_SYNC_WLIM1_GET(x),
AD9154_SYNC_ROTATE1_GET(x), AD9154_SYNC_LOCK1_GET(x),
AD9154_NCO_ALIGN1_GET(x), AD9154_BLNKDONE1_GET(x),
AD9154_PDPERR1_GET(x))
x = self.ad9154.dac_read(AD9154_JESD_CHECKS)
self.p("ERR_INTSUPP: %d, ERR_SUBCLASS: %d, ERR_KUNSUPP: %d, "
"ERR_JESDBAD: %d, ERR_WINLIMIT: %d, ERR_DLYOVER: %d",
AD9154_ERR_INTSUPP_GET(x), AD9154_ERR_SUBCLASS_GET(x),
AD9154_ERR_KUNSUPP_GET(x), AD9154_ERR_JESDBAD_GET(x),
AD9154_ERR_WINLIMIT_GET(x), AD9154_ERR_DLYOVER_GET(x))
x = self.ad9154.dac_read(AD9154_DACPLLSTATUS)
self.p("DACPLL_LOCK: %d, VCO_CAL_PROGRESS: %d, CP_CAL_VALID: %d, "
"CP_OVERRANGE_L: %d, CP_OVERRANGE_H: %d",
AD9154_DACPLL_LOCK_GET(x), AD9154_VCO_CAL_PROGRESS_GET(x),
AD9154_CP_CAL_VALID_GET(x), AD9154_CP_OVERRANGE_L_GET(x),
AD9154_CP_OVERRANGE_H_GET(x))
x = self.ad9154.dac_read(AD9154_PLL_STATUS)
self.p("PLL_LOCK_RB: %d, CURRENTS_READY_RB: %d, "
"VCO_CAL_IN_PROGRESS_RB: %d, PLL_CAL_VALID_RB: %d, "
"PLL_OVERRANGE_L_RB: %d, PLL_OVERRANGE_H_RB: %d",
AD9154_SERDES_PLL_LOCK_RB_GET(x),
AD9154_SERDES_CURRENTS_READY_RB_GET(x),
AD9154_SERDES_VCO_CAL_IN_PROGRESS_RB_GET(x),
AD9154_SERDES_PLL_CAL_VALID_RB_GET(x),
AD9154_SERDES_PLL_OVERRANGE_L_RB_GET(x),
AD9154_SERDES_PLL_OVERRANGE_H_RB_GET(x))
self.p("CODEGRPSYNC: 0x%02x", self.ad9154.dac_read(AD9154_CODEGRPSYNCFLG))
self.p("FRAMESYNC: 0x%02x", self.ad9154.dac_read(AD9154_FRAMESYNCFLG))
self.p("GOODCHECKSUM: 0x%02x", self.ad9154.dac_read(AD9154_GOODCHKSUMFLG))
self.p("INITIALLANESYNC: 0x%02x", self.ad9154.dac_read(AD9154_INITLANESYNCFLG))
x = self.ad9154.dac_read(AD9154_SYNC_CURRERR_H)
self.p("SYNC_CURRERR: 0x%04x", self.ad9154.dac_read(AD9154_SYNC_CURRERR_L) |
(AD9154_CURRERROR_H_GET(x) << 8))
self.p("SYNC_CURROVER: %d, SYNC_CURRUNDER: %d",
AD9154_CURROVER_GET(x), AD9154_CURRUNDER_GET(x))
x = self.ad9154.dac_read(AD9154_SYNC_LASTERR_H)
self.p("SYNC_LASTERR: 0x%04x", self.ad9154.dac_read(AD9154_SYNC_LASTERR_L) |
(AD9154_LASTERROR_H_GET(x) << 8))
self.p("SYNC_LASTOVER: %d, SYNC_LASTUNDER: %d",
AD9154_LASTOVER_GET(x), AD9154_LASTUNDER_GET(x))
x = self.ad9154.dac_read(AD9154_SYNC_STATUS)
self.p("SYNC_TRIP: %d, SYNC_WLIM: %d, SYNC_ROTATE: %d, "
"SYNC_LOCK: %d, SYNC_BUSY: %d",
AD9154_SYNC_TRIP_GET(x), AD9154_SYNC_WLIM_GET(x),
AD9154_SYNC_ROTATE_GET(x), AD9154_SYNC_LOCK_GET(x),
AD9154_SYNC_BUSY_GET(x))
self.p("LANE_FIFO_FULL: 0x%02x", self.ad9154.dac_read(AD9154_FIFO_STATUS_REG_0))
self.p("LANE_FIFO_EMPTY: 0x%02x", self.ad9154.dac_read(AD9154_FIFO_STATUS_REG_1))
self.p("DID_REG: 0x%02x", self.ad9154.dac_read(AD9154_DID_REG))
self.p("BID_REG: 0x%02x", self.ad9154.dac_read(AD9154_BID_REG))
self.p("SCR_L_REG: 0x%02x", self.ad9154.dac_read(AD9154_SCR_L_REG))
self.p("F_REG: 0x%02x", self.ad9154.dac_read(AD9154_F_REG))
self.p("K_REG: 0x%02x", self.ad9154.dac_read(AD9154_K_REG))
self.p("M_REG: 0x%02x", self.ad9154.dac_read(AD9154_M_REG))
self.p("CS_N_REG: 0x%02x", self.ad9154.dac_read(AD9154_CS_N_REG))
self.p("NP_REG: 0x%02x", self.ad9154.dac_read(AD9154_NP_REG))
self.p("S_REG: 0x%02x", self.ad9154.dac_read(AD9154_S_REG))
self.p("HD_CF_REG: 0x%02x", self.ad9154.dac_read(AD9154_HD_CF_REG))
self.p("RES1_REG: 0x%02x", self.ad9154.dac_read(AD9154_RES1_REG))
self.p("RES2_REG: 0x%02x", self.ad9154.dac_read(AD9154_RES2_REG))
self.p("LIDx_REG: 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x",
self.ad9154.dac_read(AD9154_LID0_REG), self.ad9154.dac_read(AD9154_LID1_REG),
self.ad9154.dac_read(AD9154_LID2_REG), self.ad9154.dac_read(AD9154_LID3_REG),
self.ad9154.dac_read(AD9154_LID4_REG), self.ad9154.dac_read(AD9154_LID5_REG),
self.ad9154.dac_read(AD9154_LID6_REG), self.ad9154.dac_read(AD9154_LID7_REG))
self.p("CHECKSUMx_REG: 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x",
self.ad9154.dac_read(AD9154_CHECKSUM0_REG), self.ad9154.dac_read(AD9154_CHECKSUM1_REG),
self.ad9154.dac_read(AD9154_CHECKSUM2_REG), self.ad9154.dac_read(AD9154_CHECKSUM3_REG),
self.ad9154.dac_read(AD9154_CHECKSUM4_REG), self.ad9154.dac_read(AD9154_CHECKSUM5_REG),
self.ad9154.dac_read(AD9154_CHECKSUM6_REG), self.ad9154.dac_read(AD9154_CHECKSUM7_REG))
self.p("COMPSUMx_REG: 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x",
self.ad9154.dac_read(AD9154_COMPSUM0_REG), self.ad9154.dac_read(AD9154_COMPSUM1_REG),
self.ad9154.dac_read(AD9154_COMPSUM2_REG), self.ad9154.dac_read(AD9154_COMPSUM3_REG),
self.ad9154.dac_read(AD9154_COMPSUM4_REG), self.ad9154.dac_read(AD9154_COMPSUM5_REG),
self.ad9154.dac_read(AD9154_COMPSUM6_REG), self.ad9154.dac_read(AD9154_COMPSUM7_REG))
self.p("BADDISPARITY: 0x%02x", self.ad9154.dac_read(AD9154_BADDISPARITY))
self.p("NITDISPARITY: 0x%02x", self.ad9154.dac_read(AD9154_NIT_W))
self.p("UNEXPECTEDCONTROL: 0x%02x", self.ad9154.dac_read(AD9154_UNEXPECTEDCONTROL_W))
self.p("DYN_LINK_LATENCY_0: 0x%02x",
self.ad9154.dac_read(AD9154_DYN_LINK_LATENCY_0))
self.p("DYN_LINK_LATENCY_1: 0x%02x",
self.ad9154.dac_read(AD9154_DYN_LINK_LATENCY_1))

View File

@ -0,0 +1,50 @@
from jesd204b.transport import seed_to_data
from artiq.coredevice.ad9154_reg import *
from artiq.experiment import *
class Test(EnvExperiment):
def build(self):
self.setattr_device("core")
self.setattr_device("ad9154")
def run(self):
self.ad9154.jesd_stpl(1)
# short transport layer test
for i in range(4):
data = seed_to_data(i << 8, True)
fail = self.stpl(i, data)
print("channel", i, "FAIL" if fail else "PASS")
self.ad9154.jesd_stpl(0)
@kernel
def stpl(self, i, data):
# select dac
self.ad9154.dac_write(AD9154_SHORT_TPL_TEST_0,
AD9154_SHORT_TPL_TEST_EN_SET(0) |
AD9154_SHORT_TPL_TEST_RESET_SET(0) |
AD9154_SHORT_TPL_DAC_SEL_SET(i) |
AD9154_SHORT_TPL_SP_SEL_SET(0))
# set expected value
self.ad9154.dac_write(AD9154_SHORT_TPL_TEST_1, data & 0xff)
self.ad9154.dac_write(AD9154_SHORT_TPL_TEST_2, (data & 0xff00) >> 8)
# enable stpl
self.ad9154.dac_write(AD9154_SHORT_TPL_TEST_0,
AD9154_SHORT_TPL_TEST_EN_SET(1) |
AD9154_SHORT_TPL_TEST_RESET_SET(0) |
AD9154_SHORT_TPL_DAC_SEL_SET(i) |
AD9154_SHORT_TPL_SP_SEL_SET(0))
# reset stpl
self.ad9154.dac_write(AD9154_SHORT_TPL_TEST_0,
AD9154_SHORT_TPL_TEST_EN_SET(1) |
AD9154_SHORT_TPL_TEST_RESET_SET(1) |
AD9154_SHORT_TPL_DAC_SEL_SET(i) |
AD9154_SHORT_TPL_SP_SEL_SET(0))
# release reset stpl
self.ad9154.dac_write(AD9154_SHORT_TPL_TEST_0,
AD9154_SHORT_TPL_TEST_EN_SET(1) |
AD9154_SHORT_TPL_TEST_RESET_SET(0) |
AD9154_SHORT_TPL_DAC_SEL_SET(i) |
AD9154_SHORT_TPL_SP_SEL_SET(0))
return self.ad9154.dac_read(AD9154_SHORT_TPL_TEST_3)

View File

@ -0,0 +1,73 @@
from artiq.experiment import *
from artiq.coredevice.ad9516_reg import *
class StartupKernel(EnvExperiment):
def build(self):
self.setattr_device("core")
self.setattr_device("led")
self.setattr_device("ad9154")
@kernel
def run(self):
self.core.reset()
self.ad9154.jesd_enable(0)
self.ad9154.init()
self.clock_setup()
@kernel
def clock_setup(self):
# reset
self.ad9154.clock_write(AD9516_SERIAL_PORT_CONFIGURATION,
AD9516_SOFT_RESET | AD9516_SOFT_RESET_MIRRORED |
AD9516_LONG_INSTRUCTION | AD9516_LONG_INSTRUCTION_MIRRORED |
AD9516_SDO_ACTIVE | AD9516_SDO_ACTIVE_MIRRORED)
self.ad9154.clock_write(AD9516_SERIAL_PORT_CONFIGURATION,
AD9516_LONG_INSTRUCTION | AD9516_LONG_INSTRUCTION_MIRRORED |
AD9516_SDO_ACTIVE | AD9516_SDO_ACTIVE_MIRRORED)
if self.ad9154.clock_read(AD9516_PART_ID) != 0x41:
raise ValueError("AD9516 not found")
# use clk input, dclk=clk/2
self.ad9154.clock_write(AD9516_PFD_AND_CHARGE_PUMP, 1*AD9516_PLL_POWER_DOWN |
0*AD9516_CHARGE_PUMP_MODE)
self.ad9154.clock_write(AD9516_VCO_DIVIDER, 0)
self.ad9154.clock_write(AD9516_INPUT_CLKS, 0*AD9516_SELECT_VCO_OR_CLK |
0*AD9516_BYPASS_VCO_DIVIDER)
self.ad9154.clock_write(AD9516_OUT0, 2*AD9516_OUT0_POWER_DOWN)
self.ad9154.clock_write(AD9516_OUT2, 2*AD9516_OUT2_POWER_DOWN)
self.ad9154.clock_write(AD9516_OUT3, 2*AD9516_OUT3_POWER_DOWN)
self.ad9154.clock_write(AD9516_OUT4, 2*AD9516_OUT4_POWER_DOWN)
self.ad9154.clock_write(AD9516_OUT5, 2*AD9516_OUT5_POWER_DOWN)
self.ad9154.clock_write(AD9516_OUT8, 1*AD9516_OUT8_POWER_DOWN)
# DAC deviceclk, clk/1
self.ad9154.clock_write(AD9516_DIVIDER_0_2, AD9516_DIVIDER_0_DIRECT_TO_OUTPUT)
self.ad9154.clock_write(AD9516_OUT1, 0*AD9516_OUT1_POWER_DOWN |
2*AD9516_OUT1_LVPECLDIFFERENTIAL_VOLTAGE)
# FPGA deviceclk, dclk/1
self.ad9154.clock_write(AD9516_DIVIDER_4_3, 0*AD9516_DIVIDER_4_NOSYNC |
1*AD9516_DIVIDER_4_BYPASS_1 | 1*AD9516_DIVIDER_4_BYPASS_2)
self.ad9154.clock_write(AD9516_DIVIDER_4_4, 0*AD9516_DIVIDER_4_DCCOFF)
self.ad9154.clock_write(AD9516_OUT9, 1*AD9516_OUT9_LVDS_OUTPUT_CURRENT |
2*AD9516_OUT9_LVDS_CMOS_OUTPUT_POLARITY |
0*AD9516_OUT9_SELECT_LVDS_CMOS)
# sysref f_data*S/(K*F), dclk/16
self.ad9154.clock_write(AD9516_DIVIDER_3_0, (16//2-1)*AD9516_DIVIDER_3_HIGH_CYCLES_1 |
(16//2-1)*AD9516_DIVIDER_3_LOW_CYCLES_1)
self.ad9154.clock_write(AD9516_DIVIDER_3_1, 0*AD9516_DIVIDER_3_PHASE_OFFSET_1 |
0*AD9516_DIVIDER_3_PHASE_OFFSET_2)
self.ad9154.clock_write(AD9516_DIVIDER_3_3, 0*AD9516_DIVIDER_3_NOSYNC |
0*AD9516_DIVIDER_3_BYPASS_1 | 1*AD9516_DIVIDER_3_BYPASS_2)
self.ad9154.clock_write(AD9516_DIVIDER_3_4, 0*AD9516_DIVIDER_3_DCCOFF)
self.ad9154.clock_write(AD9516_OUT6, 1*AD9516_OUT6_LVDS_OUTPUT_CURRENT |
2*AD9516_OUT6_LVDS_CMOS_OUTPUT_POLARITY |
0*AD9516_OUT6_SELECT_LVDS_CMOS)
self.ad9154.clock_write(AD9516_OUT7, 1*AD9516_OUT7_LVDS_OUTPUT_CURRENT |
2*AD9516_OUT7_LVDS_CMOS_OUTPUT_POLARITY |
0*AD9516_OUT7_SELECT_LVDS_CMOS)
self.ad9154.clock_write(AD9516_UPDATE_ALL_REGISTERS, 1)

View File

@ -0,0 +1,74 @@
from migen.build.generic_platform import *
ad9154_fmc_ebz = [
("ad9154_spi", 0,
# AD9154 should give control of SPI to FMC when USB cable is unplugged,
# It's the case, but the PIC18F24J50 is introducing noise on SPI SCK
# (???) To workaround that, add 2 jumpers:
# - on XP1, between pin 5 and 6 (will keep the PIC in reset)
# - on JP3 (will force output enable on FXLA108)
Subsignal("clk", Pins("HPC:LA03_P")),
Subsignal("cs_n", Pins("HPC:LA04_N", "HPC:LA05_P")),
Subsignal("mosi", Pins("HPC:LA03_N")),
Subsignal("miso", Pins("HPC:LA04_P")),
Subsignal("en", Pins("HPC:LA05_N")),
IOStandard("LVCMOS25"),
),
("ad9154_txen", 0, Pins("HPC:LA07_P"), IOStandard("LVCMOS25")),
("ad9154_txen", 1, Pins("HPC:LA07_N"), IOStandard("LVCMOS25")),
("ad9154_refclk", 0,
Subsignal("p", Pins("HPC:GBTCLK0_M2C_P")),
Subsignal("n", Pins("HPC:GBTCLK0_M2C_N")),
),
("ad9154_sysref", 0,
Subsignal("p", Pins("HPC:LA00_CC_P")),
Subsignal("n", Pins("HPC:LA00_CC_N")),
IOStandard("LVDS_25"),
Misc("DIFF_TERM=TRUE"),
),
("ad9154_sync", 0,
Subsignal("p", Pins("HPC:LA01_CC_P")),
Subsignal("n", Pins("HPC:LA01_CC_N")),
IOStandard("LVDS_25"),
Misc("DIFF_TERM=TRUE"),
),
("ad9154_sync", 1,
Subsignal("p", Pins("HPC:LA02_P")),
Subsignal("n", Pins("HPC:LA02_N")),
IOStandard("LVDS_25"),
Misc("DIFF_TERM=TRUE"),
),
("ad9154_jesd", 0, # AD9154's SERDIN7
Subsignal("txp", Pins("HPC:DP0_C2M_P")),
Subsignal("txn", Pins("HPC:DP0_C2M_N"))
),
("ad9154_jesd", 1, # AD9154's SERDIN6
Subsignal("txp", Pins("HPC:DP1_C2M_P")),
Subsignal("txn", Pins("HPC:DP1_C2M_N"))
),
("ad9154_jesd", 2, # AD9154's SERDIN5
Subsignal("txp", Pins("HPC:DP2_C2M_P")),
Subsignal("txn", Pins("HPC:DP2_C2M_N"))
),
("ad9154_jesd", 3, # AD9154's SERDIN4
Subsignal("txp", Pins("HPC:DP3_C2M_P")),
Subsignal("txn", Pins("HPC:DP3_C2M_N"))
),
("ad9154_jesd", 4, # AD9154's SERDIN2
Subsignal("txp", Pins("HPC:DP4_C2M_P")),
Subsignal("txn", Pins("HPC:DP4_C2M_N"))
),
("ad9154_jesd", 5, # AD9154's SERDIN0
Subsignal("txp", Pins("HPC:DP5_C2M_P")),
Subsignal("txn", Pins("HPC:DP5_C2M_N"))
),
("ad9154_jesd", 6, # AD9154's SERDIN1
Subsignal("txp", Pins("HPC:DP6_C2M_P")),
Subsignal("txn", Pins("HPC:DP6_C2M_N"))
),
("ad9154_jesd", 7, # AD9154's SERDIN3
Subsignal("txp", Pins("HPC:DP7_C2M_P")),
Subsignal("txn", Pins("HPC:DP7_C2M_N"))
),
]

View File

112
artiq/gateware/dsp/accu.py Normal file
View File

@ -0,0 +1,112 @@
from migen import *
from misoc.interconnect.stream import Endpoint
class Accu(Module):
def __init__(self, width, meta=[]):
self.i = Endpoint([("p", width), ("f", width), ("clr", 1)])
self.o = Endpoint([("z", width)])
self.latency = 1
###
f = Signal.like(self.i.f)
p = Signal.like(self.i.p)
self.comb += self.i.ack.eq(~self.o.stb | self.o.ack)
self.sync += [
If(self.o.ack,
self.o.stb.eq(0),
),
If(self.i.ack,
self.o.stb.eq(1),
If(self.i.stb,
self.o.z.eq(self.i.p + Mux(self.i.clr, 0, self.o.z + p)),
f.eq(self.i.f),
p.eq(self.i.f - self.i.p),
).Else(
self.o.z.eq(self.o.z + f),
)
)
]
class MCM(Module):
def __init__(self, width, constants):
n = len(constants)
self.i = i = Signal(width)
self.o = o = [Signal.like(self.i) for i in range(n)]
###
# TODO: improve MCM
assert range(n) == constants
assert n <= 9
if n > 0:
self.comb += o[0].eq(0)
if n > 1:
self.comb += o[1].eq(i)
if n > 2:
self.comb += o[2].eq(i << 1)
if n > 3:
self.comb += o[3].eq(i + (i << 1))
if n > 4:
self.comb += o[4].eq(i << 2)
if n > 5:
self.comb += o[5].eq(i + (i << 2))
if n > 6:
self.comb += o[6].eq(o[3] << 1)
if n > 7:
self.comb += o[7].eq((i << 3) - i)
if n > 8:
self.comb += o[8].eq(i << 3)
class PhasedAccu(Module):
def __init__(self, width, parallelism=8):
self.i = Endpoint([("p", width), ("f", width), ("clr", 1)])
self.o = Endpoint([("z{}".format(i), width) for i in
range(parallelism)])
self.parallelism = parallelism
self.latency = 2
###
a = MCM(width, range(parallelism + 1))
self.submodules += a
z = [Signal(width) for i in range(parallelism)]
o = self.o.payload.flatten()
load = Signal()
clr = Signal()
p = Signal.like(self.i.p)
f = Signal.like(self.i.f)
fp = Signal.like(self.i.f)
self.comb += [
self.i.ack.eq(self.o.ack),
a.i.eq(self.i.f),
]
self.sync += [
If(self.o.ack,
self.o.stb.eq(0),
),
If(~self.o.stb | self.o.ack,
self.o.stb.eq(1),
If(load,
load.eq(0),
[oi.eq(Mux(clr, 0, o[0] + fp) + zi)
for oi, zi in zip(o, z)],
fp.eq(f),
).Else(
[oi.eq(oi + fp) for oi in o],
),
),
If(self.i.stb & self.i.ack,
[zi.eq(self.i.p - Mux(self.i.clr, 0, p) + aoi)
for zi, aoi in zip(z, a.o)],
clr.eq(self.i.clr),
p.eq(self.i.p),
f.eq(a.o[parallelism]),
load.eq(1),
),
]

157
artiq/gateware/dsp/fir.py Normal file
View File

@ -0,0 +1,157 @@
from operator import add
from functools import reduce
import numpy as np
from migen import *
def halfgen4(width, n):
"""
http://recycle.lbl.gov/~ldoolitt/halfband
params:
* `up` is the passband/stopband width, as a fraction of
input sampling rate
* `n is the order of half-band filter to generate
returns:
* `a` is the full set of FIR coefficients, `4*n-1` long.
implement wisely.
"""
npt = n*40
wmax = 2*np.pi*width
wfit = (1 - np.linspace(0, 1, npt)[:, None]**2)*wmax
target = .5*np.ones_like(wfit)
basis = np.cos(wfit*np.arange(1, 2*n, 2))
l = np.linalg.pinv(basis)@target
weight = np.ones_like(wfit)
for i in range(40):
err = np.fabs(basis@l - .5)
weight[err > .99*np.max(err)] *= 1 + 1.5/(i + 11)
l = np.linalg.pinv(basis*weight)@(target*weight)
a = np.c_[l, np.zeros_like(l)].ravel()[:-1]
a = np.r_[a[::-1], 1, a]/2
return a
class FIR(Module):
"""Full-rate finite impulse response filter.
:param coefficients: integer taps.
:param width: bit width of input and output.
:param shift: scale factor (as power of two).
"""
def __init__(self, coefficients, width=16, shift=None):
self.width = width
self.i = Signal((width, True))
self.o = Signal((width, True))
n = len(coefficients)
self.latency = (n + 1)//2 + 2
###
# Delay line: increasing delay
x = [Signal((width, True)) for _ in range(n)]
self.sync += [xi.eq(xj) for xi, xj in zip(x, [self.i] + x)]
if shift is None:
shift = width - 1
# Make products
o = []
for i, c in enumerate(coefficients):
# simplify for halfband and symmetric filters
if c == 0 or c in coefficients[i + 1:]:
continue
m = Signal((width + shift, True))
self.sync += m.eq(c*reduce(add, [
xj for xj, cj in zip(x[::-1], coefficients) if cj == c
]))
o.append(m)
# Make sum
self.sync += self.o.eq(reduce(add, o) >> shift)
class ParallelFIR(Module):
"""Full-rate parallelized finite impulse response filter.
:param coefficients: integer taps.
:param parallelism: number of samples per cycle.
:param width: bit width of input and output.
:param shift: scale factor (as power of two).
"""
def __init__(self, coefficients, parallelism, width=16, shift=None):
self.width = width
self.parallelism = p = parallelism
n = len(coefficients)
# input and output: old to young, decreasing delay
self.i = [Signal((width, True)) for i in range(p)]
self.o = [Signal((width, True)) for i in range(p)]
self.latency = (n + 1)//2//parallelism + 3 # minus one sample
###
# Delay line: young to old, increasing delay
x = [Signal((width, True)) for _ in range(n + p - 1)]
self.sync += [xi.eq(xj) for xi, xj in zip(x, self.i[::-1] + x)]
if shift is None:
shift = width - 1
for j in range(p):
# Make products
o = []
for i, c in enumerate(coefficients):
# simplify for halfband and symmetric filters
if c == 0 or c in coefficients[i + 1:]:
continue
m = Signal((width + shift, True))
self.sync += m.eq(c*reduce(add, [
xj for xj, cj in zip(x[-1 - j::-1], coefficients) if cj == c
]))
o.append(m)
# Make sum
self.sync += self.o[j].eq(reduce(add, o) >> shift)
def halfgen4_cascade(rate, width, order=None):
"""Generate coefficients for cascaded half-band filters.
:param rate: upsampling rate. power of two
:param width: passband/stopband width in units of input sampling rate.
:param order: highest order, defaults to :param:`rate`"""
if order is None:
order = rate
coeff = []
p = 1
while p < rate:
p *= 2
coeff.append(halfgen4(width*p/rate/2, order*p//rate))
return coeff
class ParallelHBFUpsampler(Module):
"""Parallel, power-of-two, half-band, cascading upsampler.
Coefficients should be normalized to overall gain of 2
(highest/center coefficient being 1)."""
def __init__(self, coefficients, width=16, **kwargs):
self.parallelism = 1
self.latency = 0
self.width = width
self.i = Signal((width, True))
###
i = [self.i]
for coeff in coefficients:
self.parallelism *= 2
# assert coeff[len(coeff)//2 + 1] == 1
hbf = ParallelFIR(coeff, self.parallelism, width, **kwargs)
self.submodules += hbf
self.comb += [a.eq(b) for a, b in zip(hbf.i[::2], i)]
i = hbf.o
self.latency += hbf.latency
self.o = i

201
artiq/gateware/dsp/sawg.py Normal file
View File

@ -0,0 +1,201 @@
from collections import namedtuple
from migen import *
from misoc.interconnect.stream import Endpoint
from misoc.cores.cordic import Cordic
from .accu import PhasedAccu
from .tools import eqh, Delay, SatAddMixin
from .spline import Spline
from .fir import ParallelHBFUpsampler, halfgen4_cascade
_Widths = namedtuple("_Widths", "t a p f")
_Orders = namedtuple("_Orders", "a p f")
class ParallelDDS(Module):
def __init__(self, widths, parallelism=1, a_delay=0):
self.i = Endpoint([("x", widths.a), ("y", widths.a),
("f", widths.f), ("p", widths.f), ("clr", 1)])
self.parallelism = parallelism
self.widths = widths
###
accu = PhasedAccu(widths.f, parallelism)
cordic = [Cordic(width=widths.a, widthz=widths.p, guard=None,
eval_mode="pipelined") for i in range(parallelism)]
self.xo = [c.xo for c in cordic]
self.yo = [c.yo for c in cordic]
a_delay += accu.latency
xy_delay = Delay(2*widths.a, max(0, a_delay))
z_delay = Delay(parallelism*widths.p, max(0, -a_delay))
self.submodules += accu, xy_delay, z_delay, cordic
self.latency = max(0, a_delay) + cordic[0].latency
self.gain = cordic[0].gain
self.comb += [
xy_delay.i.eq(Cat(self.i.x, self.i.y)),
z_delay.i.eq(Cat(zi[-widths.p:]
for zi in accu.o.payload.flatten())),
eqh(accu.i.p, self.i.p),
accu.i.f.eq(self.i.f),
accu.i.clr.eq(self.i.clr),
accu.i.stb.eq(self.i.stb),
self.i.ack.eq(accu.i.ack),
accu.o.ack.eq(1),
[Cat(c.xi, c.yi).eq(xy_delay.o) for c in cordic],
Cat(c.zi for c in cordic).eq(z_delay.o),
]
class SplineParallelDUC(ParallelDDS):
def __init__(self, widths, orders, **kwargs):
p = Spline(order=orders.p, width=widths.p)
f = Spline(order=orders.f, width=widths.f)
self.f = f.tri(widths.t)
self.p = p.tri(widths.t)
self.submodules += p, f
self.ce = Signal(reset=1)
self.clr = Signal()
super().__init__(widths._replace(p=len(self.p.a0), f=len(self.f.a0)),
**kwargs)
self.latency += f.latency
###
assert p.latency == f.latency
self.comb += [
p.o.ack.eq(self.ce),
f.o.ack.eq(self.ce),
eqh(self.i.f, f.o.a0),
eqh(self.i.p, p.o.a0),
self.i.stb.eq(p.o.stb | f.o.stb),
]
assert p.latency == 1
self.sync += [
self.i.clr.eq(0),
If(p.i.stb,
self.i.clr.eq(self.clr),
),
]
class SplineParallelDDS(SplineParallelDUC):
def __init__(self, widths, orders, **kwargs):
a = Spline(order=orders.a, width=widths.a)
self.a = a.tri(widths.t)
self.submodules += a
super().__init__(widths._replace(a=len(self.a.a0)), orders, **kwargs)
###
self.comb += [
a.o.ack.eq(self.ce),
eqh(self.i.x, a.o.a0),
self.i.y.eq(0),
]
class Config(Module):
def __init__(self, width):
self.clr = Signal(4, reset=0b1111)
self.iq_en = Signal(2, reset=0b01)
self.limits = [[Signal((width, True), reset=-(1 << width - 1)),
Signal((width, True), reset=(1 << width - 1) - 1)]
for i in range(3)]
self.clipped = [Signal(2) for i in range(3)] # TODO
self.i = Endpoint([("addr", bits_for(4 + len(self.limits))),
("data", 16)])
self.ce = Signal()
###
div = Signal(16, reset=0)
n = Signal.like(div)
pad = Signal()
reg = Array([Cat(div, n), self.clr, self.iq_en, pad] +
[Cat(*l) for l in self.limits])
self.comb += [
self.i.ack.eq(1),
self.ce.eq(n == 0),
]
self.sync += [
n.eq(n - 1),
If(self.ce,
n.eq(div),
),
If(self.i.stb,
reg[self.i.addr].eq(self.i.data),
),
]
class Channel(Module, SatAddMixin):
def __init__(self, width=16, parallelism=4, widths=None, orders=None):
if orders is None:
orders = _Orders(a=4, f=2, p=1)
if widths is None:
widths = _Widths(t=width, a=orders.a*width, p=orders.p*width,
f=(orders.f + 2)*width)
self.submodules.a1 = a1 = SplineParallelDDS(widths, orders)
self.submodules.a2 = a2 = SplineParallelDDS(widths, orders)
coeff = [[int(round((1 << 18)*ci)) for ci in c]
for c in halfgen4_cascade(parallelism, width=.4, order=8)]
hbf = [ParallelHBFUpsampler(coeff, width=width, shift=17)
for i in range(2)]
self.submodules.b = b = SplineParallelDUC(
widths._replace(a=len(a1.xo[0]), f=widths.f - width), orders,
parallelism=parallelism, a_delay=-a1.latency-hbf[0].latency)
cfg = Config(widths.a)
u = Spline(width=widths.a, order=orders.a)
du = Delay(width, a1.latency + hbf[0].latency + b.latency - u.latency)
self.submodules += cfg, u, du, hbf
self.u = u.tri(widths.t)
self.i = [cfg.i, self.u, a1.a, a1.f, a1.p, a2.a, a2.f, a2.p, b.f, b.p]
self.i_names = "cfg u a1 f1 p1 a2 f2 p2 f0 p0".split()
self.i_named = dict(zip(self.i_names, self.i))
self.y_in = [Signal((width, True)) for i in range(parallelism)]
self.o = [Signal((width, True)) for i in range(parallelism)]
self.widths = widths
self.orders = orders
self.parallelism = parallelism
self.latency = a1.latency + hbf[0].latency + b.latency + 2
self.cordic_gain = a1.gain*b.gain
###
self.comb += [
a1.ce.eq(cfg.ce),
a2.ce.eq(cfg.ce),
b.ce.eq(cfg.ce),
u.o.ack.eq(cfg.ce),
Cat(a1.clr, a2.clr, b.clr).eq(cfg.clr),
b.i.x.eq(hbf[0].o[0]), # FIXME: rip up
b.i.y.eq(hbf[1].o[0]),
]
self.sync += [
hbf[0].i.eq(self.sat_add(a1.xo[0], a2.xo[0],
limits=cfg.limits[0],
clipped=cfg.clipped[0])),
hbf[1].i.eq(self.sat_add(a1.yo[0], a2.yo[0],
limits=cfg.limits[1],
clipped=cfg.clipped[1])),
eqh(du.i, u.o.a0),
]
# wire up outputs and q_{i,o} exchange
for o, x, y in zip(self.o, b.xo, self.y_in):
self.sync += [
o.eq(self.sat_add(
du.o, Mux(cfg.iq_en[0], x, 0), Mux(cfg.iq_en[1], y, 0),
limits=cfg.limits[2], clipped=cfg.clipped[2])),
]
def connect_y(self, buddy):
self.comb += Cat(buddy.y_in).eq(Cat(self.b.yo))

View File

@ -0,0 +1,46 @@
from migen import *
from misoc.interconnect.stream import Endpoint
class Spline(Module):
def __init__(self, order, width, step=1, time_width=None):
if not (step == 1 or order <= 2):
raise ValueError("For non-linear splines, "
"`step` needs to be one.")
layout = [("a{}".format(i), (width, True)) for i in range(order)]
self.i = Endpoint(layout)
self.o = Endpoint(layout)
self.latency = 1
###
o = self.o.payload.flatten()
self.comb += self.i.ack.eq(~self.o.stb | self.o.ack)
self.sync += [
If(self.o.ack,
self.o.stb.eq(0),
),
If(self.i.ack,
self.o.stb.eq(1),
[o[i].eq(o[i] + (o[i + 1] << log2_int(step)))
for i in range(order - 1)],
If(self.i.stb,
self.o.payload.eq(self.i.payload),
),
),
]
def tri(self, time_width):
layout = [(name, (length - i*time_width, signed))
for i, (name, (length, signed), dir) in
enumerate(self.i.payload.layout[::-1])]
layout.reverse()
i = Endpoint(layout)
self.comb += [
self.i.stb.eq(i.stb),
i.ack.eq(self.i.ack),
[i0[-len(i1):].eq(i1) for i0, i1 in
zip(self.i.payload.flatten(), i.payload.flatten())]
]
return i

View File

@ -0,0 +1,61 @@
from operator import add
from functools import reduce
from migen import *
class Delay(Module):
def __init__(self, i, delay, o=None):
if isinstance(i, (int, tuple)):
z = [Signal(i) for j in range(delay + 1)]
elif isinstance(i, list):
z = [Record(i) for j in range(delay + 1)]
elif isinstance(i, Record):
z = [Record(i.layout) for j in range(delay + 1)]
else:
z = [Signal.like(i) for j in range(delay + 1)]
self.i = z[0]
self.o = z[-1]
if not isinstance(i, (int, list, tuple)):
self.comb += self.i.eq(i)
if o is not None:
self.comb += o.eq(self.o)
self.latency = delay
self.sync += [z[j + 1].eq(z[j]) for j in range(delay)]
def eqh(a, b):
return a[-len(b):].eq(b[-len(a):])
class SatAddMixin:
"""Signed saturating addition mixin"""
def sat_add(self, *a, limits=None, clipped=None):
a = list(a)
# assert all(value_bits_sign(ai)[1] for ai in a)
length = max(len(ai) for ai in a)
carry = log2_int(len(a), need_pow2=False)
full = Signal((length + carry, True))
limited = Signal((length, True))
clip = Signal(2)
if clipped is not None:
clipped.eq(clip)
self.comb += [
full.eq(reduce(add, a)),
]
if limits is None:
self.comb += [
If(full[-1-carry:] == Replicate(full[-1], carry + 1),
limited.eq(full),
clip.eq(0),
).Else(
limited.eq(Cat(Replicate(~full[-1], length - 1), full[-1])),
clip.eq(Cat(full[-1], ~full[-1])),
)
]
else:
self.comb += [
clip.eq(Cat(full < limits[0], full > limits[1])),
limited.eq(Array([full, limits[0], limits[1], 0])[clip]),
]
return limited

View File

@ -0,0 +1,27 @@
from collections import namedtuple
from migen import *
from artiq.gateware.rtio import rtlink
from artiq.gateware.dsp.sawg import Channel as _Channel
_Phy = namedtuple("Phy", "rtlink probes overrides")
_ChannelPHY = ClockDomainsRenamer("rio_phy")(_Channel)
class Channel(_ChannelPHY):
def __init__(self, *args, **kwargs):
_ChannelPHY.__init__(self, *args, **kwargs)
self.phys = []
for i in self.i:
rl = rtlink.Interface(rtlink.OInterface(len(i.payload)))
self.comb += [
i.stb.eq(rl.o.stb),
rl.o.busy.eq(~i.ack),
i.payload.raw_bits().eq(rl.o.data),
]
# TODO probes, overrides
self.phys.append(_Phy(rl, [], []))
self.phys_named = dict(zip(self.i_names, self.phys))

View File

@ -31,6 +31,32 @@ class _OSERDESE2_8X(Module):
o_O=pad, o_OB=pad_n)
class _ISERDESE2_8X(Module):
def __init__(self, pad, pad_n=None):
self.o = Signal(8)
self.i = Signal(8)
self.oe = Signal()
# # #
pad_i = Signal()
i = self.i
self.specials += Instance("ISERDESE2", p_DATA_RATE="DDR",
p_DATA_WIDTH=8,
p_INTERFACE_TYPE="NETWORKING", p_NUM_CE=1,
o_Q1=i[7], o_Q2=i[6], o_Q3=i[5], o_Q4=i[4],
o_Q5=i[3], o_Q6=i[2], o_Q7=i[1], o_Q8=i[0],
i_D=pad_i,
i_CLK=ClockSignal("rtiox4"),
i_CLKB=~ClockSignal("rtiox4"),
i_CE1=1, i_RST=0,
i_CLKDIV=ClockSignal("rio_phy"))
if pad_n is None:
self.comb += pad_i.eq(pad)
else:
self.specials += Instance("IBUFDS", o_O=pad_i, i_I=pad, i_IB=pad_n)
class _IOSERDESE2_8X(Module):
def __init__(self, pad, pad_n=None):
self.o = Signal(8)
@ -80,3 +106,10 @@ class Inout_8X(ttl_serdes_generic.Inout):
serdes = _IOSERDESE2_8X(pad, pad_n)
self.submodules += serdes
ttl_serdes_generic.Inout.__init__(self, serdes)
class Input_8X(ttl_serdes_generic.Inout):
def __init__(self, pad, pad_n=None):
serdes = _ISERDESE2_8X(pad, pad_n)
self.submodules += serdes
ttl_serdes_generic.Inout.__init__(self, serdes)

View File

@ -27,6 +27,43 @@ class Output(Module):
]
class Input(Module):
def __init__(self, pad):
self.rtlink = rtlink.Interface(
rtlink.OInterface(2, 2),
rtlink.IInterface(1))
self.overrides = []
self.probes = []
# # #
sensitivity = Signal(2)
sample = Signal()
self.sync.rio += [
sample.eq(0),
If(self.rtlink.o.stb & self.rtlink.o.address[1],
sensitivity.eq(self.rtlink.o.data),
If(self.rtlink.o.address[0], sample.eq(1))
)
]
i = Signal()
i_d = Signal()
self.specials += MultiReg(pad, i, "rio_phy")
self.sync.rio_phy += i_d.eq(i)
self.comb += [
self.rtlink.i.stb.eq(
sample |
(sensitivity[0] & ( i & ~i_d)) |
(sensitivity[1] & (~i & i_d))
),
self.rtlink.i.data.eq(i)
]
self.probes += [i]
class Inout(Module):
def __init__(self, pad):
self.rtlink = rtlink.Interface(

View File

@ -10,15 +10,14 @@ from migen.build.xilinx.vivado import XilinxVivadoToolchain
from migen.build.xilinx.ise import XilinxISEToolchain
from misoc.interconnect.csr import *
from misoc.interconnect import wishbone
from misoc.cores import gpio
from misoc.integration.soc_core import mem_decoder
from misoc.targets.kc705 import MiniSoC, soc_kc705_args, soc_kc705_argdict
from misoc.integration.builder import builder_args, builder_argdict
from artiq.gateware.soc import AMPSoC, build_artiq_soc
from artiq.gateware import rtio, nist_clock, nist_qc2
from artiq.gateware.rtio.phy import ttl_simple, ttl_serdes_7series, dds, spi
from artiq.gateware.rtio.phy import (ttl_simple, ttl_serdes_7series,
dds, spi)
from artiq import __version__ as artiq_version

266
artiq/gateware/targets/phaser.py Executable file
View File

@ -0,0 +1,266 @@
#!/usr/bin/env python3.5
import argparse
from migen import *
from migen.genlib.resetsync import AsyncResetSynchronizer
from migen.genlib.cdc import MultiReg
from migen.genlib.io import DifferentialInput
from jesd204b.common import (JESD204BTransportSettings,
JESD204BPhysicalSettings,
JESD204BSettings)
from jesd204b.phy.gtx import GTXQuadPLL
from jesd204b.phy import JESD204BPhyTX
from jesd204b.core import JESD204BCoreTX
from jesd204b.core import JESD204BCoreTXControl
from misoc.interconnect.csr import *
from misoc.cores import gpio
from misoc.cores import spi as spi_csr
from misoc.targets.kc705 import MiniSoC, soc_kc705_args, soc_kc705_argdict
from misoc.integration.builder import builder_args, builder_argdict
from artiq.gateware.soc import AMPSoC, build_artiq_soc
from artiq.gateware import rtio
from artiq.gateware.ad9154_fmc_ebz import ad9154_fmc_ebz
from artiq.gateware.rtio.phy import (ttl_simple, ttl_serdes_7series,
sawg)
from artiq import __version__ as artiq_version
class _PhaserCRG(Module, AutoCSR):
def __init__(self, platform, refclk):
self._clock_sel = CSRStorage()
self._pll_reset = CSRStorage(reset=1)
self._pll_locked = CSRStatus()
self.clock_domains.cd_rtio = ClockDomain()
self.clock_domains.cd_rtiox4 = ClockDomain(reset_less=True)
pll_locked = Signal()
rtio_clk = Signal()
rtiox4_clk = Signal()
self.specials += [
Instance("PLLE2_ADV",
p_STARTUP_WAIT="FALSE", o_LOCKED=pll_locked,
p_REF_JITTER1=0.01, p_REF_JITTER2=0.01,
p_CLKIN1_PERIOD=20/3, p_CLKIN2_PERIOD=20/3,
i_CLKIN1=0, i_CLKIN2=refclk,
# Warning: CLKINSEL=0 means CLKIN2 is selected
i_CLKINSEL=~self._clock_sel.storage,
# VCO @ 1.2GHz when using 150MHz input
p_CLKFBOUT_MULT=8, p_DIVCLK_DIVIDE=1,
i_CLKFBIN=self.cd_rtio.clk,
i_RST=self._pll_reset.storage,
o_CLKFBOUT=rtio_clk,
p_CLKOUT0_DIVIDE=2, p_CLKOUT0_PHASE=0.0,
o_CLKOUT0=rtiox4_clk,
),
Instance("BUFG", i_I=rtio_clk, o_O=self.cd_rtio.clk),
Instance("BUFG", i_I=rtiox4_clk, o_O=self.cd_rtiox4.clk),
AsyncResetSynchronizer(self.cd_rtio, ~pll_locked),
MultiReg(pll_locked | ~self._clock_sel.storage,
self._pll_locked.status)
]
self.cd_rtio.clk.attr.add("keep")
platform.add_period_constraint(self.cd_rtio.clk, 20/3)
class AD9154JESD(Module, AutoCSR):
def __init__(self, platform):
ps = JESD204BPhysicalSettings(l=4, m=4, n=16, np=16)
ts = JESD204BTransportSettings(f=2, s=1, k=16, cs=1)
settings = JESD204BSettings(ps, ts, did=0x5a, bid=0x5)
linerate = 6e9
refclk_freq = 150e6
fabric_freq = 150*1000*1000
sync_pads = platform.request("ad9154_sync")
self.jsync = Signal()
self.specials += DifferentialInput(
sync_pads.p, sync_pads.n, self.jsync)
refclk = Signal()
self.clock_domains.cd_jesd = ClockDomain()
refclk_pads = platform.request("ad9154_refclk")
self.specials += [
Instance("IBUFDS_GTE2", i_CEB=0,
i_I=refclk_pads.p, i_IB=refclk_pads.n, o_O=refclk),
Instance("BUFG", i_I=refclk, o_O=self.cd_jesd.clk),
AsyncResetSynchronizer(self.cd_jesd, ResetSignal("rio_phy")),
]
self.cd_jesd.clk.attr.add("keep")
platform.add_period_constraint(self.cd_jesd.clk, 1e9/refclk_freq)
qpll = GTXQuadPLL(refclk, refclk_freq, linerate)
self.submodules += qpll
self.phys = []
for i in range(4):
phy = JESD204BPhyTX(
qpll, platform.request("ad9154_jesd", i), fabric_freq)
phy.gtx.cd_tx.clk.attr.add("keep")
platform.add_period_constraint(phy.gtx.cd_tx.clk, 40*1e9/linerate)
platform.add_false_path_constraints(self.cd_jesd.clk,
phy.gtx.cd_tx.clk)
self.phys.append(phy)
to_jesd = ClockDomainsRenamer("jesd")
self.submodules.core = to_jesd(JESD204BCoreTX(self.phys, settings,
converter_data_width=32))
self.submodules.control = to_jesd(JESD204BCoreTXControl(self.core))
self.comb += [
platform.request("ad9154_txen", 0).eq(1),
platform.request("ad9154_txen", 1).eq(1),
self.core.start.eq(self.jsync),
platform.request("user_led", 3).eq(self.jsync),
]
# blinking leds for transceiver reset status
for i in range(4):
counter = Signal(max=fabric_freq)
self.comb += platform.request("user_led", 4 + i).eq(counter[-1])
sync = getattr(self.sync, "phy{}_tx".format(i))
sync += [
counter.eq(counter - 1),
If(counter == 0,
counter.eq(fabric_freq - 1)
)
]
class AD9154(Module, AutoCSR):
def __init__(self, platform):
ad9154_spi = platform.request("ad9154_spi")
self.comb += ad9154_spi.en.eq(1)
self.submodules.spi = spi_csr.SPIMaster(ad9154_spi)
self.submodules.jesd = AD9154JESD(platform)
self.sawgs = [sawg.Channel(width=16, parallelism=2) for i in range(4)]
self.submodules += self.sawgs
for conv, ch in zip(self.jesd.core.sink.flatten(), self.sawgs):
self.sync.jesd += conv.eq(Cat(ch.o))
class Phaser(MiniSoC, AMPSoC):
mem_map = {
"timer_kernel": 0x10000000, # (shadow @0x90000000)
"rtio": 0x20000000, # (shadow @0xa0000000)
"i2c": 0x30000000, # (shadow @0xb0000000)
"mailbox": 0x70000000, # (shadow @0xf0000000)
"ad9154": 0x50000000,
}
mem_map.update(MiniSoC.mem_map)
def __init__(self, cpu_type="or1k", **kwargs):
MiniSoC.__init__(self,
cpu_type=cpu_type,
sdram_controller_type="minicon",
l2_size=128*1024,
with_timer=False,
ident=artiq_version,
**kwargs)
AMPSoC.__init__(self)
self.platform.toolchain.bitstream_commands.extend([
"set_property BITSTREAM.GENERAL.COMPRESS True [current_design]",
])
platform = self.platform
platform.add_extension(ad9154_fmc_ebz)
self.submodules.leds = gpio.GPIOOut(Cat(
platform.request("user_led", 0),
platform.request("user_led", 1)))
self.csr_devices.append("leds")
i2c = platform.request("i2c")
self.submodules.i2c = gpio.GPIOTristate([i2c.scl, i2c.sda])
self.register_kernel_cpu_csrdevice("i2c")
self.config["I2C_BUS_COUNT"] = 1
self.submodules.ad9154 = AD9154(platform)
self.register_kernel_cpu_csrdevice("ad9154")
self.config["AD9154_DAC_CS"] = 1 << 0
self.config["AD9154_CLK_CS"] = 1 << 1
rtio_channels = []
phy = ttl_serdes_7series.Inout_8X(
platform.request("user_sma_gpio_n"))
self.submodules += phy
rtio_channels.append(rtio.Channel.from_phy(phy, ififo_depth=128))
phy = ttl_simple.Output(platform.request("user_led", 2))
self.submodules += phy
rtio_channels.append(rtio.Channel.from_phy(phy))
sysref_pads = platform.request("ad9154_sysref")
phy = ttl_serdes_7series.Input_8X(sysref_pads.p, sysref_pads.n)
self.submodules += phy
rtio_channels.append(rtio.Channel.from_phy(phy, ififo_depth=32,
ofifo_depth=2))
phy = ttl_simple.Input(self.ad9154.jesd.jsync)
self.submodules += phy
rtio_channels.append(rtio.Channel.from_phy(phy, ififo_depth=32,
ofifo_depth=2))
self.config["RTIO_REGULAR_TTL_COUNT"] = len(rtio_channels)
self.config["RTIO_FIRST_SAWG_CHANNEL"] = len(rtio_channels)
rtio_channels.extend(rtio.Channel.from_phy(phy)
for sawg in self.ad9154.sawgs
for phy in sawg.phys)
self.config["RTIO_LOG_CHANNEL"] = len(rtio_channels)
rtio_channels.append(rtio.LogChannel())
self.config["RTIO_FIRST_DDS_CHANNEL"] = len(rtio_channels)
self.config["RTIO_DDS_COUNT"] = 1
self.config["DDS_CHANNELS_PER_BUS"] = 1
self.config["DDS_AD9914"] = None
self.config["DDS_ONEHOT_SEL"] = None
self.config["DDS_RTIO_CLK_RATIO"] = 8
self.submodules.rtio_crg = _PhaserCRG(
platform, self.ad9154.jesd.cd_jesd.clk)
self.csr_devices.append("rtio_crg")
self.submodules.rtio = rtio.RTIO(rtio_channels)
self.register_kernel_cpu_csrdevice("rtio")
self.submodules.rtio_moninj = rtio.MonInj(rtio_channels)
self.csr_devices.append("rtio_moninj")
self.submodules.rtio_analyzer = rtio.Analyzer(
self.rtio, self.get_native_sdram_if())
self.csr_devices.append("rtio_analyzer")
self.config["RTIO_FINE_TS_WIDTH"] = self.rtio.fine_ts_width
platform.add_false_path_constraints(
self.crg.cd_sys.clk, self.rtio_crg.cd_rtio.clk)
platform.add_false_path_constraints(
self.crg.cd_sys.clk, self.ad9154.jesd.cd_jesd.clk)
for phy in self.ad9154.jesd.phys:
platform.add_false_path_constraints(
self.crg.cd_sys.clk, phy.gtx.cd_tx.clk)
def main():
parser = argparse.ArgumentParser(
description="ARTIQ core device builder for "
"KC705+AD9154 hardware")
builder_args(parser)
soc_kc705_args(parser)
args = parser.parse_args()
soc = Phaser(**soc_kc705_argdict(args))
build_artiq_soc(soc, builder_argdict(args))
if __name__ == "__main__":
main()

View File

@ -0,0 +1,75 @@
use board::csr;
pub extern fn init() {
unsafe {
csr::ad9154::spi_offline_write(1);
csr::ad9154::spi_cs_polarity_write(0);
csr::ad9154::spi_clk_polarity_write(0);
csr::ad9154::spi_clk_phase_write(0);
csr::ad9154::spi_lsb_first_write(0);
csr::ad9154::spi_half_duplex_write(0);
csr::ad9154::spi_clk_div_write_write(16);
csr::ad9154::spi_clk_div_read_write(16);
csr::ad9154::spi_xfer_len_write_write(24);
csr::ad9154::spi_xfer_len_read_write(0);
csr::ad9154::spi_cs_write(csr::CONFIG_AD9154_DAC_CS);
csr::ad9154::spi_offline_write(0);
}
}
const AD9_READ: u16 = 1 << 15;
pub extern fn dac_write(addr: u16, data: u8) {
unsafe {
csr::ad9154::spi_data_write_write(
((addr as u32) << 16) | ((data as u32) << 8));
while csr::ad9154::spi_pending_read() != 0 {}
while csr::ad9154::spi_active_read() != 0 {}
}
}
pub extern fn dac_read(addr: u16) -> u8 {
unsafe {
dac_write(AD9_READ | addr, 0);
csr::ad9154::spi_data_read_read() as u8
}
}
pub extern fn clk_write(addr: u16, data: u8) {
unsafe {
csr::ad9154::spi_cs_write(csr::CONFIG_AD9154_CLK_CS);
dac_write(addr, data);
csr::ad9154::spi_cs_write(csr::CONFIG_AD9154_DAC_CS);
}
}
pub extern fn clk_read(addr: u16) -> u8 {
unsafe {
clk_write(AD9_READ | addr, 0);
csr::ad9154::spi_data_read_read() as u8
}
}
pub extern fn jesd_enable(en: u32) {
unsafe {
csr::ad9154::jesd_control_enable_write(en);
}
}
pub extern fn jesd_ready() {
unsafe {
csr::ad9154::jesd_control_ready_read();
}
}
pub extern fn jesd_prbs(p: u32) {
unsafe {
csr::ad9154::jesd_control_prbs_config_write(p);
}
}
pub extern fn jesd_stpl(en: u32) {
unsafe {
csr::ad9154::jesd_control_stpl_enable_write(en);
}
}

View File

@ -116,4 +116,23 @@ static mut API: &'static [(&'static str, *const ())] = &[
api!(i2c_write = ::i2c::write),
#[cfg(has_i2c)]
api!(i2c_read = ::i2c::read),
#[cfg(has_ad9154)]
api!(ad9154_init = ::ad9154::init),
#[cfg(has_ad9154)]
api!(ad9154_write = ::ad9154::dac_write),
#[cfg(has_ad9154)]
api!(ad9154_read = ::ad9154::dac_read),
#[cfg(has_ad9154)]
api!(ad9516_write = ::ad9154::clk_write),
#[cfg(has_ad9154)]
api!(ad9516_read = ::ad9154::clk_read),
#[cfg(has_ad9154)]
api!(ad9154_jesd_enable = ::ad9154::jesd_enable),
#[cfg(has_ad9154)]
api!(ad9154_jesd_ready = ::ad9154::jesd_ready),
#[cfg(has_ad9154)]
api!(ad9154_jesd_prbs = ::ad9154::jesd_prbs),
#[cfg(has_ad9154)]
api!(ad9154_jesd_stpl = ::ad9154::jesd_stpl),
];

View File

@ -52,6 +52,8 @@ macro_rules! artiq_raise {
mod rtio;
#[cfg(has_i2c)]
mod i2c;
#[cfg(has_ad9154)]
mod ad9154;
use core::{mem, ptr, slice, str};
use std::io::Cursor;

103
artiq/test/gateware/fir.py Normal file
View File

@ -0,0 +1,103 @@
import numpy as np
import matplotlib.pyplot as plt
from migen import *
from migen.fhdl import verilog
from artiq.gateware.dsp import fir
class Transfer(Module):
def __init__(self, dut):
self.submodules.dut = dut
def drive(self, x):
for xi in x:
yield self.dut.i.eq(int(xi))
yield
def record(self, y):
for i in range(self.dut.latency):
yield
for i in range(len(y)):
yield
y[i] = (yield self.dut.o)
def run(self, samples, amplitude=1.):
w = 2**(self.dut.width - 1) - 1
x = np.round(np.random.uniform(
-amplitude*w, amplitude*w, samples))
y = np.empty_like(x)
run_simulation(self, [self.drive(x), self.record(y)],
vcd_name="fir.vcd")
x /= w
y /= w
return x, y
def analyze(self, x, y):
fig, ax = plt.subplots(3)
ax[0].plot(x, "c-.", label="input")
ax[0].plot(y, "r-", label="output")
ax[0].legend(loc="right")
ax[0].set_xlabel("time (1/fs)")
ax[0].set_ylabel("signal")
n = len(x)
w = np.hanning(n)
x = (x.reshape(-1, n)*w).sum(0)
y = (y.reshape(-1, n)*w).sum(0)
t = (np.fft.rfft(y)/np.fft.rfft(x))
f = np.fft.rfftfreq(n)*2
fmin = f[1]
ax[1].plot(f, 20*np.log10(np.abs(t)), "r-")
ax[1].set_ylim(-70, 3)
ax[1].set_xlim(fmin, 1.)
# ax[1].set_xscale("log")
ax[1].set_xlabel("frequency (fs/2)")
ax[1].set_ylabel("magnitude (dB)")
ax[1].grid(True)
ax[2].plot(f, np.rad2deg(np.angle(t)), "r-")
ax[2].set_xlim(fmin, 1.)
# ax[2].set_xscale("log")
ax[2].set_xlabel("frequency (fs/2)")
ax[2].set_ylabel("phase (deg)")
ax[2].grid(True)
return fig
class ParallelTransfer(Transfer):
def drive(self, x):
for xi in x.reshape(-1, self.dut.parallelism):
yield [ij.eq(int(xj)) for ij, xj in zip(self.dut.i, xi)]
yield
def record(self, y):
for i in range(self.dut.latency):
yield
for yi in y.reshape(-1, self.dut.parallelism):
yield
yi[:] = (yield from [(yield o) for o in self.dut.o])
def _main():
coeff = fir.halfgen4(.4/2, 8)
coeff_int = [int(round(c * (1 << 16 - 1))) for c in coeff]
if False:
coeff = [[int(round((1 << 26) * ci)) for ci in c]
for c in fir.halfgen4_cascade(8, width=.4, order=8)]
dut = fir.ParallelHBFUpsampler(coeff, width=16, shift=25)
print(verilog.convert(dut, ios=set([dut.i] + dut.o)))
elif True:
dut = fir.ParallelFIR(coeff_int, parallelism=4, width=16)
# print(verilog.convert(dut, ios=set(dut.i + dut.o)))
tb = ParallelTransfer(dut)
else:
dut = fir.FIR(coeff_int, width=16)
# print(verilog.convert(dut, ios={dut.i, dut.o}))
tb = Transfer(dut)
x, y = tb.run(samples=1 << 10, amplitude=.8)
tb.analyze(x, y)
plt.show()
if __name__ == "__main__":
_main()

View File

@ -0,0 +1,46 @@
import numpy as np
from migen import *
from migen.fhdl.verilog import convert
from artiq.gateware.dsp.accu import Accu, PhasedAccu
from .tools import xfer
def read(o, n):
p = []
for i in range(n):
p.append((yield from [(yield pi) for pi in o.payload.flatten()]))
yield
return p
def _test_gen_accu(dut, o):
yield dut.o.ack.eq(1)
yield from xfer(dut, i=dict(p=0, f=1, clr=1))
o.extend((yield from read(dut.o, 8)))
yield from xfer(dut, i=dict(p=0, f=2, clr=0))
o.extend((yield from read(dut.o, 8)))
yield from xfer(dut, i=dict(p=0, f=2, clr=1))
o.extend((yield from read(dut.o, 8)))
yield from xfer(dut, i=dict(p=8, f=-1, clr=1))
o.extend((yield from read(dut.o, 8)))
yield from xfer(dut, i=dict(p=0, f=0, clr=1))
yield from xfer(dut, i=dict(p=1, f=0, clr=0))
o.extend((yield from read(dut.o, 8)))
def _test_accu():
dut = PhasedAccu(8, parallelism=8)
if False:
print(convert(dut))
else:
o = []
run_simulation(dut, _test_gen_accu(dut, o), vcd_name="accu.vcd")
o = np.array(o)
print(o)
if __name__ == "__main__":
_test_accu()

View File

@ -0,0 +1,36 @@
import numpy as np
from migen import *
from migen.fhdl.verilog import convert
from artiq.gateware.dsp import sawg
from .tools import xfer
def _test_gen_dds(dut, o):
yield from xfer(dut,
a=dict(a0=10),
p=dict(a0=0),
f=dict(a0=1),
)
for i in range(256//dut.parallelism):
yield
o.append((yield from [(yield _) for _ in dut.xo]))
def _test_channel():
widths = sawg._Widths(t=8, a=4*8, p=8, f=16)
orders = sawg._Orders(a=4, p=1, f=2)
dut = sawg.SplineParallelDDS(widths, orders, parallelism=2)
if False:
print(convert(dut))
else:
o = []
run_simulation(dut, _test_gen_dds(dut, o), vcd_name="dds.vcd")
o = np.array(o)
print(o[:, :])
if __name__ == "__main__":
_test_channel()

View File

@ -0,0 +1,225 @@
import unittest
import migen as mg
from numpy import int32
from artiq.coredevice import sawg, spline
from artiq.language import (at_mu, now_mu, delay,
core as core_language)
from artiq.gateware.rtio.phy.sawg import Channel
from artiq.sim import devices as sim_devices, time as sim_time
class RTIOManager:
def __init__(self):
self.outputs = []
def rtio_output(self, now, channel, addr, data):
self.outputs.append((now, channel, addr, data))
def rtio_output_wide(self, *args, **kwargs):
self.rtio_output(*args, **kwargs)
def patch(self, mod):
assert not hasattr(mod, "_saved")
mod._saved = {}
for name in "rtio_output rtio_output_wide".split():
mod._saved[name] = getattr(mod, name, None)
setattr(mod, name, getattr(self, name))
def unpatch(self, mod):
mod.__dict__.update(mod._saved)
del mod._saved
class SAWGTest(unittest.TestCase):
def setUp(self):
core_language.set_time_manager(sim_time.Manager())
self.rtio_manager = RTIOManager()
self.rtio_manager.patch(spline)
self.core = sim_devices.Core({})
self.core.coarse_ref_period = 6.66666
self.t = self.core.coarse_ref_period
self.channel = mg.ClockDomainsRenamer({"rio_phy": "sys"})(
Channel(width=16, parallelism=2))
self.driver = sawg.SAWG({"core": self.core}, channel_base=0,
parallelism=self.channel.parallelism)
def tearDown(self):
self.rtio_manager.unpatch(spline)
def test_instantiate(self):
pass
def test_make_events(self):
d = self.driver
d.offset.set(.9)
delay(2*self.t)
d.frequency0.set(.1)
d.frequency1.set(.1)
delay(2*self.t)
d.offset.set(0)
v = int(round((1 << 48) * .1 * self.t))
self.assertEqual(
self.rtio_manager.outputs, [
(0., 1, 0, int(round(
(1 << self.driver.offset.width - 1)*.9))),
(2.*self.t, 8, 0, int(round(
(1 << self.driver.frequency0.width) *
self.t/self.channel.parallelism*.1))),
(2.*self.t, 3, 0, [int32(v), int32(v >> 32)]),
(4.*self.t, 1, 0, 0),
])
def run_channel(self, events):
def gen(dut, events):
c = 0
for time, channel, address, data in events:
time //= self.t
assert c <= time
while c < time:
yield
c += 1
for phy in dut.phys:
yield phy.rtlink.o.stb.eq(0)
rt = dut.phys[channel].rtlink.o
if isinstance(data, list):
data = sum(int(d) << (i*32) for i, d in enumerate(data))
yield rt.data.eq(int(data))
yield rt.stb.eq(1)
assert not (yield rt.busy)
# print("{}: set ch {} to {}".format(time, channel, hex(data)))
def log(dut, data, n):
for i in range(dut.latency):
yield
for i in range(n):
yield
data.append((yield from [(yield _) for _ in dut.o]))
data = []
# print(int(events[-1][0]) + 1)
mg.run_simulation(self.channel, [
gen(self.channel, events),
log(self.channel, data, int(events[-1][0]//self.t) + 1)],
vcd_name="dds.vcd")
return data
def test_run_channel(self):
self.test_make_events()
self.run_channel(self.rtio_manager.outputs)
def test_coeff(self):
import struct
# these get discrete_compensate
# [.1, .01, -.00001], [.1, .01, .00001, -.000000001]
for v in [-.1], [.1, -.01]:
ch = self.driver.offset
p = ch.coeff_as_packed(v)
t = ch.time_width
w = ch.width
p = [_ & 0xffffffff for _ in p]
p0 = [int(round(vi*ch.scale*ch.time_scale**i))
for i, vi in enumerate(v)]
p0 = [struct.pack("<" + "_bhiiqqqq"[(w + i*t)//8], vi
)[:(w + i*t)//8]
for i, vi in enumerate(p0)]
p0 = b"".join(p0)
if len(p0) % 4:
p0 += b"\x00"*(4 - len(p0) % 4)
p0 = list(struct.unpack("<" + "I"*((len(p0) + 3)//4), p0))
with self.subTest(v):
self.assertEqual(p, p0)
def test_linear(self):
d = self.driver
d.offset.set_coeff_mu([100, 10])
delay(10*self.t)
d.offset.set_coeff([0])
delay(1*self.t)
out = self.run_channel(self.rtio_manager.outputs)
for i in range(len(out) - 1):
with self.subTest(i):
v = 100 + i*10
self.assertEqual(out[i], [v, v])
self.assertEqual(out[-1], [0, 0])
def test_pack(self):
ch = self.driver.offset
self.assertEqual(ch.coeff_as_packed_mu([1]), [1])
self.assertEqual(ch.coeff_as_packed_mu([1, 1 << 16]), [1, 1])
self.assertEqual(ch.coeff_as_packed_mu([1, 1 << 32]), [1, 0])
self.assertEqual(ch.coeff_as_packed_mu([0x1234, 0xa5a5a5a5]),
[0xa5a51234, 0xa5a5])
self.assertEqual(ch.coeff_as_packed_mu([1, 2, 3, 4]),
[0x20001, 0x30000, 0, 4, 0])
self.assertEqual(ch.coeff_as_packed_mu([-1, -2, -3, -4]),
[0xfffeffff, 0xfffdffff, -1, -4, -1])
self.assertEqual(ch.coeff_as_packed_mu([0, -1, 0, -1]),
[0xffff0000, 0x0000ffff, 0, -1, -1])
def test_smooth_linear(self):
ch = self.driver.offset
ch.smooth(.1, .2, 13*self.t, 1)
ch.set(.2)
delay(1*self.t)
out = self.run_channel(self.rtio_manager.outputs)
a = int(round(.1*ch.scale))
da = int(round(.1*ch.scale*(1 << ch.width)//13))
for i in range(len(out) - 1):
with self.subTest(i):
v = a + (i*da >> ch.width)
self.assertEqual(out[i], [v, v])
a = int(round(.2*ch.scale))
self.assertEqual(out[-1], [a, a])
def test_smooth_cubic(self):
ch = self.driver.offset
ch.smooth(.1, .2, 13, 3)
ch.set(.2)
delay(1*self.t)
out = self.run_channel(self.rtio_manager.outputs)
out = sum(out, [])
if False:
import matplotlib.pyplot as plt
plt.plot(out)
plt.show()
@unittest.skip("needs artiq.sim.time.TimeManager tweak for "
"reverse timeline jumps")
def test_demo_2tone(self):
MHz = 1e-3
ns = 1.
self.sawg0 = self.driver
t_up = t_hold = t_down = 400*ns
a1 = .3
a2 = .4
order = 3
self.sawg0.frequency0.set(10*MHz)
self.sawg0.phase0.set(0.)
self.sawg0.frequency1.set(1*MHz)
self.sawg0.phase1.set(0.)
self.sawg0.frequency2.set(13*MHz)
self.sawg0.phase2.set(0.)
t = now_mu()
self.sawg0.amplitude1.smooth(.0, a1, t_up, order)
at_mu(t)
self.sawg0.amplitude2.smooth(.0, a2, t_up, order)
self.sawg0.amplitude1.set(a1)
self.sawg0.amplitude2.set(a2)
delay(t_hold)
t = now_mu()
self.sawg0.amplitude1.smooth(a1, .0, t_down, order)
at_mu(t)
self.sawg0.amplitude2.smooth(a2, .0, t_down, order)
self.sawg0.amplitude1.set(.0)
self.sawg0.amplitude2.set(.0)
out = self.run_channel(self.rtio_manager.outputs)
out = sum(out, [])
if True:
import matplotlib.pyplot as plt
plt.plot(out)
plt.show()

View File

@ -0,0 +1,70 @@
import numpy as np
from operator import or_
from migen import *
from migen.fhdl.verilog import convert
from artiq.gateware.rtio.phy.sawg import Channel
from .tools import rtio_xfer
def pack_tri(port, *v):
r = 0
w = 0
for vi, p in zip(v, port.payload.flatten()):
w += len(p)
r |= int(vi*(1 << w))
return r
def gen_rtio(dut):
yield
yield from rtio_xfer(
dut,
a1=pack_tri(dut.a1.a, .1),
f0=pack_tri(dut.b.f, .01234567),
f1=pack_tri(dut.a1.f, .01234567),
a2=pack_tri(dut.a1.a, .05),
f2=pack_tri(dut.a1.f, .00534567),
)
def gen_log(dut, o, n):
for i in range(3 + dut.latency):
yield
for i in range(n):
yield
o.append((yield from [(yield _) for _ in dut.o]))
#o.append([(yield dut.a1.xo[0])])
def _test_channel():
width = 16
dut = ClockDomainsRenamer({"rio_phy": "sys"})(
Channel(width=width, parallelism=4)
)
if False:
print(convert(dut))
return
o = []
run_simulation(
dut,
[gen_rtio(dut), gen_log(dut, o, 128)],
vcd_name="dds.vcd")
o = np.array(o)/(1 << (width - 1))
o = o.ravel()
np.savez_compressed("dds.npz", o=o)
import matplotlib.pyplot as plt
fig, ax = plt.subplots(2)
ax[0].step(np.arange(o.size), o)
ax[1].psd(o, 1 << 10, Fs=1, noverlap=1 << 9, scale_by_freq=False)
fig.savefig("dds.pdf")
plt.show()
if __name__ == "__main__":
_test_channel()

View File

@ -0,0 +1,31 @@
import numpy as np
from migen import *
from migen.fhdl.verilog import convert
from artiq.gateware.dsp.spline import Spline
from .tools import xfer
def _test_gen_spline(dut, o):
yield dut.o.ack.eq(1)
yield from xfer(dut, i=dict(a0=0, a1=1, a2=2))
for i in range(20):
yield
o.append((yield dut.o.a0))
def _test_spline():
dut = Spline(order=3, width=16, step=1)
if False:
print(convert(dut))
else:
o = []
run_simulation(dut, _test_gen_spline(dut, o), vcd_name="spline.vcd")
o = np.array(o)
print(o)
if __name__ == "__main__":
_test_spline()

View File

@ -0,0 +1,49 @@
def set_dict(e, **k):
for k, v in k.items():
if isinstance(v, dict):
yield from set_dict(getattr(e, k), **v)
else:
yield getattr(e, k).eq(v)
def xfer(dut, **kw):
ep = []
for e, v in kw.items():
e = getattr(dut, e)
yield from set_dict(e, **v)
ep.append(e)
for e in ep:
yield e.stb.eq(1)
while ep:
yield
for e in ep[:]:
if hasattr(e, "busy") and (yield e.busy):
raise ValueError(e, "busy")
if not hasattr(e, "ack") or (yield e.ack):
yield e.stb.eq(0)
ep.remove(e)
def szip(*iters):
active = {it: None for it in iters}
while active:
for it in list(active):
while True:
try:
val = it.send(active[it])
except StopIteration:
del active[it]
break
if val is None:
break
else:
active[it] = (yield val)
val = (yield None)
for it in active:
active[it] = val
def rtio_xfer(dut, **kwargs):
yield from szip(*(
xfer(dut.phys_named[k].rtlink, o={"data": v})
for k, v in kwargs.items()))

View File

@ -0,0 +1,14 @@
#!/bin/bash
BUILD_SETTINGS_FILE=$HOME/.m-labs/build_settings.sh
[ -f $BUILD_SETTINGS_FILE ] && . $BUILD_SETTINGS_FILE
SOC_PREFIX=$PREFIX/lib/python3.5/site-packages/artiq/binaries/kc705-phaser
mkdir -p $SOC_PREFIX
$PYTHON -m artiq.gateware.targets.phaser --toolchain vivado $MISOC_EXTRA_VIVADO_CMDLINE
cp misoc_phaser_kc705/gateware/top.bit $SOC_PREFIX
cp misoc_phaser_kc705/software/bios/bios.bin $SOC_PREFIX
cp misoc_phaser_kc705/software/runtime/runtime.fbi $SOC_PREFIX
wget -P $SOC_PREFIX https://raw.githubusercontent.com/jordens/bscan_spi_bitstreams/master/bscan_spi_xc7k325t.bit

View File

@ -0,0 +1,29 @@
package:
name: artiq-kc705-phaser
version: {{ environ.get("GIT_DESCRIBE_TAG", "") }}
source:
git_url: ../..
build:
noarch_python: true
number: {{ environ.get("GIT_DESCRIBE_NUMBER", 0) }}
string: py_{{ environ.get("GIT_DESCRIBE_NUMBER", 0) }}+git{{ environ.get("GIT_DESCRIBE_HASH", "")[1:] }}
requirements:
build:
- migen 0.5.dev
- misoc 0.5.dev
- jesd204b 0.2
- llvm-or1k
- binutils-or1k-linux >=2.27
- rust-core-or1k
- cargo
- numpy
run:
- artiq {{ "{tag} py_{number}+git{hash}".format(tag=environ.get("GIT_DESCRIBE_TAG"), number=environ.get("GIT_DESCRIBE_NUMBER"), hash=environ.get("GIT_DESCRIBE_HASH")[1:]) if "GIT_DESCRIBE_TAG" in environ else "" }}
about:
home: https://m-labs.hk/artiq
license: GPL
summary: 'Bitstream, BIOS and runtime for Phaser on the KC705 board'

View File

@ -130,6 +130,30 @@ To avoid I/O contention, the startup kernel should first program the TCA6424A ex
See :mod:`artiq.coredevice.i2c` for more details.
Phaser
++++++
The Phaser adapter is an AD9154-FMC-EBZ, a 4 channel 2.4 GHz DAC on an FMC HPC card.
+--------------+------------+--------------+
| RTIO channel | TTL line | Capability |
+==============+============+==============+
| 0 | SMA_GPIO_N | Input+Output |
+--------------+------------+--------------+
| 1 | LED | Output |
+--------------+------------+--------------+
| 2 | SYSREF | Input |
+--------------+------------+--------------+
| 3 | SYNC | Input |
+--------------+------------+--------------+
The SAWG channels start with RTIO channel number 4, each occupying 3 channels.
The board has one non-RTIO SPI bus that is accessible through
:mod:`artiq.coredevice.ad9154`.
Pipistrello
-----------

View File

@ -10,7 +10,7 @@ These drivers are for the core device and the peripherals closely integrated int
:members:
:mod:`artiq.coredevice.ttl` module
-----------------------------------
----------------------------------
.. automodule:: artiq.coredevice.ttl
:members:
@ -43,7 +43,7 @@ These drivers are for the core device and the peripherals closely integrated int
:members:
:mod:`artiq.coredevice.cache` module
-----------------------------------------
------------------------------------
.. automodule:: artiq.coredevice.cache
:members:
@ -53,3 +53,21 @@ These drivers are for the core device and the peripherals closely integrated int
.. automodule:: artiq.coredevice.exceptions
:members:
:mod:`artiq.coredevice.spline` module
-------------------------------------
.. automodule:: artiq.coredevice.spline
:members:
:mod:`artiq.coredevice.sawg` module
-----------------------------------
.. automodule:: artiq.coredevice.sawg
:members:
:mod:`artiq.coredevice.ad9154` module
-------------------------------------
.. automodule:: artiq.coredevice.ad9154
:members:

View File

@ -141,6 +141,7 @@ Then, you can flash the board:
For the KC705, the next step is to flash the MAC and IP addresses to the board. See :ref:`those instructions <flash-mac-ip-addr>`.
.. _configuring-core-device:
Configuring the core device
---------------------------