From 65f8a97b56656e321e3a36b7d187b805723034cb Mon Sep 17 00:00:00 2001 From: Etienne Wodey Date: Wed, 1 Sep 2021 17:39:08 +0200 Subject: [PATCH] phaser: add helpers to align updates to the RTIO timeline Signed-off-by: Etienne Wodey --- RELEASE_NOTES.rst | 1 + artiq/coredevice/phaser.py | 33 ++++++++++++++++++++++++++++++++- 2 files changed, 33 insertions(+), 1 deletion(-) diff --git a/RELEASE_NOTES.rst b/RELEASE_NOTES.rst index 862ed148b..b1a762752 100644 --- a/RELEASE_NOTES.rst +++ b/RELEASE_NOTES.rst @@ -18,6 +18,7 @@ Highlights: - Improved documentation - Expose the DAC coarse mixer and ``sif_sync`` - Exposes upconverter calibration and enabling/disabling of upconverter LO & RF outputs. + - Add helpers to align Phaser updates to the RTIO timeline (``get_next_frame_timestamp()``) * ``get()``, ``get_mu()``, ``get_att()``, and ``get_att_mu()`` functions added for AD9910 and AD9912 * New hardware support: - HVAMP_8CH 8 channel HV amplifier for Fastino / Zotino diff --git a/artiq/coredevice/phaser.py b/artiq/coredevice/phaser.py index c8c3c0938..f6a84fd72 100644 --- a/artiq/coredevice/phaser.py +++ b/artiq/coredevice/phaser.py @@ -1,7 +1,8 @@ from numpy import int32, int64 from artiq.language.core import kernel, delay_mu, delay -from artiq.coredevice.rtio import rtio_output, rtio_input_data +from artiq.coredevice.rtio import rtio_output, rtio_input_data, rtio_input_timestamp +from artiq.coredevice.core import rtio_get_counter from artiq.language.units import us, ns, ms, MHz from artiq.language.types import TInt32 from artiq.coredevice.dac34h84 import DAC34H84 @@ -160,6 +161,7 @@ class Phaser: # self.core.seconds_to_mu(10*8*4*ns) # unfortunately this returns 319 assert self.core.ref_period == 1*ns self.t_frame = 10*8*4 + self.frame_tstamp = int64(0) self.clk_sel = clk_sel self.tune_fifo_offset = tune_fifo_offset self.sync_dly = sync_dly @@ -197,6 +199,9 @@ class Phaser: raise ValueError("large number of frame CRC errors") delay(.1*ms) # slack + # determine the origin for frame-aligned timestamps + self.measure_frame_timestamp() + # reset self.set_cfg(dac_resetb=0, dac_sleep=1, dac_txena=0, trf0_ps=1, trf1_ps=1, @@ -468,6 +473,32 @@ class Phaser: """ return self.read8(PHASER_ADDR_CRC_ERR) + @kernel + def measure_frame_timestamp(self): + """Perform a register read and record the exact frame timing in `self.frame_tstamp`. + + Deterministic timing requires updates to be schedule at multiples of `self.t_frame` later. + See `get_next_frame_timestamp()`. + """ + rtio_output((self.channel_base << 8) | (PHASER_ADDR_BOARD_ID & 0x7f), 0) # can read any register + delay_mu(int64(self.t_frame)) + self.frame_tstamp = rtio_input_timestamp(rtio_get_counter() + 0xffffff, self.channel_base) + delay(10*ms) + + @kernel + def get_next_frame_timestamp(self, after_timestamp_mu = int64(-1)): + """Return the RTIO timestamp of the next frame after `after_timestamp_mu`. + + If `after_timestamp_mu < 0`, return the next frame after `now_mu()`. + + Updates scheduled at this timestamp and multiples of `self.t_frame` later will + have deterministic latency with respect to the RTIO timeline. + """ + if after_timestamp_mu < 0: + after_timestamp_mu = now_mu() + n = int64((after_timestamp_mu - self.frame_tstamp) / self.t_frame) + return self.frame_tstamp + (n + 1) * self.t_frame + @kernel def set_sync_dly(self, dly): """Set SYNC delay.