forked from M-Labs/artiq
ad9910: rewire io_delay tuning
This now reliably locates the SYNC_CLK-IO_UPDATE edge by doing two scans at different delays between start and stop IO_UPDATE. It also works well when one delay is very close to the edge. And it correctly identifies which (start or stop) pulse hit or crossed the SYNC_CLK edge. for #1143 Signed-off-by: Robert Jördens <rj@quartiq.de>
This commit is contained in:
parent
fe3d6661eb
commit
14b6b63916
|
@ -466,17 +466,18 @@ class AD9910:
|
|||
raise ValueError("no valid window/delay")
|
||||
|
||||
@kernel
|
||||
def measure_io_update_alignment(self, io_up_delay):
|
||||
def measure_io_update_alignment(self, delay_start, delay_stop):
|
||||
"""Use the digital ramp generator to locate the alignment between
|
||||
IO_UPDATE and SYNC_CLK.
|
||||
|
||||
The ramp generator is set up to a linear frequency ramp
|
||||
(dFTW/t_SYNC_CLK=1) and started at a RTIO time stamp.
|
||||
(dFTW/t_SYNC_CLK=1) and started at a coarse RTIO time stamp plus
|
||||
`delay_start` and stopped at a coarse RTIO time stamp plus
|
||||
`delay_stop`.
|
||||
|
||||
After scanning the alignment, an IO_UPDATE delay midway between two
|
||||
edges should be chosen.
|
||||
|
||||
:return: odd/even SYNC_CLK cycle indicator
|
||||
:param delay_start: Start IO_UPDATE delay in machine units.
|
||||
:param delay_stop: Stop IO_UPDATE delay in machine units.
|
||||
:return: Odd/even SYNC_CLK cycle indicator.
|
||||
"""
|
||||
# set up DRG
|
||||
# DRG ACC autoclear and LRR on io update
|
||||
|
@ -489,14 +490,16 @@ class AD9910:
|
|||
self.write32(_AD9910_REG_DRAMPR, 0x00010000)
|
||||
# dFTW = 1, (work around negative slope)
|
||||
self.write64(_AD9910_REG_DRAMPS, -1, 0)
|
||||
at_mu(now_mu() + 0x10 & ~0xf) # align to RTIO/2
|
||||
self.cpld.io_update.pulse_mu(8)
|
||||
# delay io_update after RTIO/2 edge
|
||||
t = now_mu() + 0x10 & ~0xf
|
||||
at_mu(t + delay_start)
|
||||
self.cpld.io_update.pulse_mu(32 - delay_start) # realign
|
||||
# disable DRG autoclear and LRR on io_update
|
||||
self.write32(_AD9910_REG_CFR1, 0x00000002)
|
||||
# stop DRG
|
||||
self.write64(_AD9910_REG_DRAMPS, 0, 0)
|
||||
at_mu((now_mu() + 0x10 & ~0xf) + io_up_delay) # delay
|
||||
self.cpld.io_update.pulse_mu(32 - io_up_delay) # realign
|
||||
at_mu(t + 0x1000 + delay_stop)
|
||||
self.cpld.io_update.pulse_mu(32 - delay_stop) # realign
|
||||
ftw = self.read32(_AD9910_REG_FTW) # read out effective FTW
|
||||
delay(100*us) # slack
|
||||
# disable DRG
|
||||
|
@ -510,22 +513,36 @@ class AD9910:
|
|||
|
||||
Scan through increasing IO_UPDATE delays until a delay is found that
|
||||
lets IO_UPDATE be registered in the next SYNC_CLK cycle. Return a
|
||||
IO_UPDATE delay that is midway between two such SYNC_CLK transitions.
|
||||
IO_UPDATE delay that is as far away from that SYNC_CLK edge
|
||||
as possible.
|
||||
|
||||
This method assumes that the IO_UPDATE TTLOut device has one machine
|
||||
unit resolution (SERDES) and that the ratio between fine RTIO frequency
|
||||
(RTIO time machine units) and SYNC_CLK is 4.
|
||||
unit resolution (SERDES).
|
||||
|
||||
:return: Stable IO_UPDATE delay to be passed to the constructor
|
||||
:class:`AD9910` via the device database.
|
||||
"""
|
||||
period = 4 # f_RTIO/f_SYNC = 4
|
||||
max_delay = 8 # mu, 1 ns
|
||||
d0 = self.io_update_delay
|
||||
t0 = int32(self.measure_io_update_alignment(d0))
|
||||
for i in range(max_delay - 1):
|
||||
t = self.measure_io_update_alignment(
|
||||
(d0 + i + 1) & (max_delay - 1))
|
||||
if t != t0:
|
||||
return (d0 + i + period//2) & (period - 1)
|
||||
period = self.sysclk_per_mu * 4 # SYNC_CLK period
|
||||
repeat = 100
|
||||
for i in range(period):
|
||||
t = 0
|
||||
# check whether the sync edge is strictly between i, i+2
|
||||
for j in range(repeat):
|
||||
t += self.measure_io_update_alignment(i, i + 2)
|
||||
if t != 0: # no certain edge
|
||||
continue
|
||||
# check left/right half: i,i+1 and i+1,i+2
|
||||
t1 = [0, 0]
|
||||
for j in range(repeat):
|
||||
t1[0] += self.measure_io_update_alignment(i, i + 1)
|
||||
t1[1] += self.measure_io_update_alignment(i + 1, i + 2)
|
||||
if ((t1[0] == 0 and t1[1] == repeat) or # edge left of i + 1
|
||||
(t1[0] == repeat and t1[1] == 0) or # edge right of i + 1
|
||||
(t1[0] != 0 and t1[1] != 0 and # edge very close to i + 1
|
||||
t1[0] != repeat and t1[1] != repeat)):
|
||||
# the good delay is period//2 after the edge
|
||||
return (i + 1 + period//2) & (period - 1)
|
||||
else: # can't interpret result
|
||||
raise ValueError(
|
||||
"no clear IO_UPDATE-SYNC_CLK alignment edge found")
|
||||
raise ValueError("no IO_UPDATE-SYNC_CLK alignment edge found")
|
||||
|
|
|
@ -104,18 +104,21 @@ class AD9910Exp(EnvExperiment):
|
|||
self.core.break_realtime()
|
||||
self.dev.cpld.init()
|
||||
self.dev.init()
|
||||
bins = [0]*8
|
||||
self.scan_io_delay(bins)
|
||||
self.set_dataset("bins", bins)
|
||||
self.set_dataset("dly", self.dev.io_update_delay)
|
||||
bins1 = [0]*4
|
||||
bins2 = [0]*4
|
||||
self.scan_io_delay(bins1, bins2)
|
||||
self.set_dataset("bins1", bins1)
|
||||
self.set_dataset("bins2", bins2)
|
||||
self.set_dataset("dly", self.dev.tune_io_update_delay())
|
||||
|
||||
@kernel
|
||||
def scan_io_delay(self, bins):
|
||||
def scan_io_delay(self, bins1, bins2):
|
||||
delay(100*us)
|
||||
n = 100
|
||||
for i in range(n):
|
||||
for phase in range(len(bins)):
|
||||
bins[phase] += self.dev.measure_io_update_alignment(phase)
|
||||
for j in range(len(bins1)):
|
||||
bins1[j] += self.dev.measure_io_update_alignment(j, j + 1)
|
||||
bins2[j] += self.dev.measure_io_update_alignment(j, j + 2)
|
||||
delay(10*ms)
|
||||
|
||||
@kernel
|
||||
|
@ -174,12 +177,14 @@ class AD9910Test(ExperimentCase):
|
|||
def test_io_update_delay(self):
|
||||
self.execute(AD9910Exp, "io_update_delay")
|
||||
dly = self.dataset_mgr.get("dly")
|
||||
bins = self.dataset_mgr.get("bins")
|
||||
print(dly, bins)
|
||||
n = max(bins)
|
||||
# test for 4-periodicity (SYNC_CLK) and maximal contrast
|
||||
for i in range(len(bins)):
|
||||
self.assertEqual(abs(bins[i] - bins[(i + 4) % 8]), n)
|
||||
bins1 = self.dataset_mgr.get("bins1")
|
||||
bins2 = self.dataset_mgr.get("bins2")
|
||||
print(dly, bins1, bins2)
|
||||
n = max(bins2)
|
||||
# no edge at optimal delay
|
||||
self.assertEqual(bins2[(dly + 1) & 3], 0)
|
||||
# edge at expected position
|
||||
self.assertEqual(bins2[(dly + 3) & 3], n)
|
||||
|
||||
def test_sw_readback(self):
|
||||
self.execute(AD9910Exp, "sw_readback")
|
||||
|
|
Loading…
Reference in New Issue