forked from M-Labs/artiq
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:
commit
c63fa46430
|
@ -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).
|
|
@ -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
|
@ -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
|
|
@ -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")
|
||||
|
||||
|
|
|
@ -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.)
|
|
@ -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.")
|
|
@ -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}
|
||||
}
|
||||
}
|
|
@ -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)
|
|
@ -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)
|
|
@ -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)
|
|
@ -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)
|
|
@ -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))
|
|
@ -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)
|
|
@ -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)
|
|
@ -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"))
|
||||
),
|
||||
]
|
|
@ -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),
|
||||
),
|
||||
]
|
|
@ -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
|
|
@ -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))
|
|
@ -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
|
|
@ -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
|
|
@ -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))
|
|
@ -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)
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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
|
||||
|
||||
|
||||
|
|
|
@ -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()
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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),
|
||||
];
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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()
|
|
@ -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()
|
|
@ -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()
|
|
@ -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()
|
|
@ -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()
|
|
@ -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()
|
|
@ -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()))
|
|
@ -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
|
|
@ -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'
|
|
@ -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
|
||||
-----------
|
||||
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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
|
||||
---------------------------
|
||||
|
|
Loading…
Reference in New Issue