forked from M-Labs/artiq
Merge branch 'master' into new
This commit is contained in:
commit
1b841805f6
|
@ -26,22 +26,22 @@ _AD9910_REG_CFR1 = 0x00
|
||||||
_AD9910_REG_CFR2 = 0x01
|
_AD9910_REG_CFR2 = 0x01
|
||||||
_AD9910_REG_CFR3 = 0x02
|
_AD9910_REG_CFR3 = 0x02
|
||||||
_AD9910_REG_AUX_DAC = 0x03
|
_AD9910_REG_AUX_DAC = 0x03
|
||||||
_AD9910_REG_IO_UPD = 0x04
|
_AD9910_REG_IO_UPDATE = 0x04
|
||||||
_AD9910_REG_FTW = 0x07
|
_AD9910_REG_FTW = 0x07
|
||||||
_AD9910_REG_POW = 0x08
|
_AD9910_REG_POW = 0x08
|
||||||
_AD9910_REG_ASF = 0x09
|
_AD9910_REG_ASF = 0x09
|
||||||
_AD9910_REG_MSYNC = 0x0A
|
_AD9910_REG_SYNC = 0x0a
|
||||||
_AD9910_REG_DRAMPL = 0x0B
|
_AD9910_REG_RAMP_LIMIT = 0x0b
|
||||||
_AD9910_REG_DRAMPS = 0x0C
|
_AD9910_REG_RAMP_STEP = 0x0c
|
||||||
_AD9910_REG_DRAMPR = 0x0D
|
_AD9910_REG_RAMP_RATE = 0x0d
|
||||||
_AD9910_REG_PR0 = 0x0E
|
_AD9910_REG_PROFILE0 = 0x0e
|
||||||
_AD9910_REG_PR1 = 0x0F
|
_AD9910_REG_PROFILE1 = 0x0f
|
||||||
_AD9910_REG_PR2 = 0x10
|
_AD9910_REG_PROFILE2 = 0x10
|
||||||
_AD9910_REG_PR3 = 0x11
|
_AD9910_REG_PROFILE3 = 0x11
|
||||||
_AD9910_REG_PR4 = 0x12
|
_AD9910_REG_PROFILE4 = 0x12
|
||||||
_AD9910_REG_PR5 = 0x13
|
_AD9910_REG_PROFILE5 = 0x13
|
||||||
_AD9910_REG_PR6 = 0x14
|
_AD9910_REG_PROFILE6 = 0x14
|
||||||
_AD9910_REG_PR7 = 0x15
|
_AD9910_REG_PROFILE7 = 0x15
|
||||||
_AD9910_REG_RAM = 0x16
|
_AD9910_REG_RAM = 0x16
|
||||||
|
|
||||||
|
|
||||||
|
@ -256,7 +256,7 @@ class AD9910:
|
||||||
|
|
||||||
@kernel
|
@kernel
|
||||||
def set_mu(self, ftw, pow=0, asf=0x3fff, phase_mode=_PHASE_MODE_DEFAULT,
|
def set_mu(self, ftw, pow=0, asf=0x3fff, phase_mode=_PHASE_MODE_DEFAULT,
|
||||||
ref_time=-1):
|
ref_time=-1, profile=0):
|
||||||
"""Set profile 0 data in machine units.
|
"""Set profile 0 data in machine units.
|
||||||
|
|
||||||
This uses machine units (FTW, POW, ASF). The frequency tuning word
|
This uses machine units (FTW, POW, ASF). The frequency tuning word
|
||||||
|
@ -276,6 +276,7 @@ class AD9910:
|
||||||
by :meth:`set_phase_mode` for this call.
|
by :meth:`set_phase_mode` for this call.
|
||||||
:param ref_time: Fiducial time used to compute absolute or tracking
|
:param ref_time: Fiducial time used to compute absolute or tracking
|
||||||
phase updates. In machine units as obtained by `now_mu()`.
|
phase updates. In machine units as obtained by `now_mu()`.
|
||||||
|
:param profile: Profile number to set (0-7, default: 0).
|
||||||
:return: Resulting phase offset word after application of phase
|
:return: Resulting phase offset word after application of phase
|
||||||
tracking offset. When using :const:`PHASE_MODE_CONTINUOUS` in
|
tracking offset. When using :const:`PHASE_MODE_CONTINUOUS` in
|
||||||
subsequent calls, use this value as the "current" phase.
|
subsequent calls, use this value as the "current" phase.
|
||||||
|
@ -297,7 +298,7 @@ class AD9910:
|
||||||
# is equivalent to an output pipeline latency.
|
# is equivalent to an output pipeline latency.
|
||||||
dt = int32(now_mu()) - int32(ref_time)
|
dt = int32(now_mu()) - int32(ref_time)
|
||||||
pow += dt*ftw*self.sysclk_per_mu >> 16
|
pow += dt*ftw*self.sysclk_per_mu >> 16
|
||||||
self.write64(_AD9910_REG_PR0, (asf << 16) | pow, ftw)
|
self.write64(_AD9910_REG_PROFILE0 + profile, (asf << 16) | pow, ftw)
|
||||||
delay_mu(int64(self.io_update_delay))
|
delay_mu(int64(self.io_update_delay))
|
||||||
self.cpld.io_update.pulse_mu(8) # assumes 8 mu > t_SYSCLK
|
self.cpld.io_update.pulse_mu(8) # assumes 8 mu > t_SYSCLK
|
||||||
at_mu(now_mu() & ~0xf)
|
at_mu(now_mu() & ~0xf)
|
||||||
|
@ -332,7 +333,7 @@ class AD9910:
|
||||||
|
|
||||||
@kernel
|
@kernel
|
||||||
def set(self, frequency, phase=0.0, amplitude=1.0,
|
def set(self, frequency, phase=0.0, amplitude=1.0,
|
||||||
phase_mode=_PHASE_MODE_DEFAULT, ref_time=-1):
|
phase_mode=_PHASE_MODE_DEFAULT, ref_time=-1, profile=0):
|
||||||
"""Set profile 0 data in SI units.
|
"""Set profile 0 data in SI units.
|
||||||
|
|
||||||
.. seealso:: :meth:`set_mu`
|
.. seealso:: :meth:`set_mu`
|
||||||
|
@ -342,11 +343,12 @@ class AD9910:
|
||||||
:param asf: Amplitude in units of full scale
|
:param asf: Amplitude in units of full scale
|
||||||
:param phase_mode: Phase mode constant
|
:param phase_mode: Phase mode constant
|
||||||
:param ref_time: Fiducial time stamp in machine units
|
:param ref_time: Fiducial time stamp in machine units
|
||||||
|
:param profile: Profile to affect
|
||||||
:return: Resulting phase offset in turns
|
:return: Resulting phase offset in turns
|
||||||
"""
|
"""
|
||||||
return self.pow_to_turns(self.set_mu(
|
return self.pow_to_turns(self.set_mu(
|
||||||
self.frequency_to_ftw(frequency), self.turns_to_pow(phase),
|
self.frequency_to_ftw(frequency), self.turns_to_pow(phase),
|
||||||
self.amplitude_to_asf(amplitude), phase_mode, ref_time))
|
self.amplitude_to_asf(amplitude), phase_mode, ref_time, profile))
|
||||||
|
|
||||||
@kernel
|
@kernel
|
||||||
def set_att_mu(self, att):
|
def set_att_mu(self, att):
|
||||||
|
@ -389,7 +391,7 @@ class AD9910:
|
||||||
:param window: Symmetric SYNC_IN validation window (0-15) in
|
:param window: Symmetric SYNC_IN validation window (0-15) in
|
||||||
steps of ~75ps for both hold and setup margin.
|
steps of ~75ps for both hold and setup margin.
|
||||||
"""
|
"""
|
||||||
self.write32(_AD9910_REG_MSYNC,
|
self.write32(_AD9910_REG_SYNC,
|
||||||
(window << 28) | # SYNC S/H validation delay
|
(window << 28) | # SYNC S/H validation delay
|
||||||
(1 << 27) | # SYNC receiver enable
|
(1 << 27) | # SYNC receiver enable
|
||||||
(0 << 26) | # SYNC generator disable
|
(0 << 26) | # SYNC generator disable
|
||||||
|
@ -466,17 +468,18 @@ class AD9910:
|
||||||
raise ValueError("no valid window/delay")
|
raise ValueError("no valid window/delay")
|
||||||
|
|
||||||
@kernel
|
@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
|
"""Use the digital ramp generator to locate the alignment between
|
||||||
IO_UPDATE and SYNC_CLK.
|
IO_UPDATE and SYNC_CLK.
|
||||||
|
|
||||||
The ramp generator is set up to a linear frequency ramp
|
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
|
:param delay_start: Start IO_UPDATE delay in machine units.
|
||||||
edges should be chosen.
|
:param delay_stop: Stop IO_UPDATE delay in machine units.
|
||||||
|
:return: Odd/even SYNC_CLK cycle indicator.
|
||||||
:return: odd/even SYNC_CLK cycle indicator
|
|
||||||
"""
|
"""
|
||||||
# set up DRG
|
# set up DRG
|
||||||
# DRG ACC autoclear and LRR on io update
|
# DRG ACC autoclear and LRR on io update
|
||||||
|
@ -484,19 +487,21 @@ class AD9910:
|
||||||
# DRG -> FTW, DRG enable
|
# DRG -> FTW, DRG enable
|
||||||
self.write32(_AD9910_REG_CFR2, 0x01090000)
|
self.write32(_AD9910_REG_CFR2, 0x01090000)
|
||||||
# no limits
|
# no limits
|
||||||
self.write64(_AD9910_REG_DRAMPL, -1, 0)
|
self.write64(_AD9910_REG_RAMP_LIMIT, -1, 0)
|
||||||
# DRCTL=0, dt=1 t_SYNC_CLK
|
# DRCTL=0, dt=1 t_SYNC_CLK
|
||||||
self.write32(_AD9910_REG_DRAMPR, 0x00010000)
|
self.write32(_AD9910_REG_RAMP_RATE, 0x00010000)
|
||||||
# dFTW = 1, (work around negative slope)
|
# dFTW = 1, (work around negative slope)
|
||||||
self.write64(_AD9910_REG_DRAMPS, -1, 0)
|
self.write64(_AD9910_REG_RAMP_STEP, -1, 0)
|
||||||
at_mu(now_mu() + 0x10 & ~0xf) # align to RTIO/2
|
# delay io_update after RTIO/2 edge
|
||||||
self.cpld.io_update.pulse_mu(8)
|
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
|
# disable DRG autoclear and LRR on io_update
|
||||||
self.write32(_AD9910_REG_CFR1, 0x00000002)
|
self.write32(_AD9910_REG_CFR1, 0x00000002)
|
||||||
# stop DRG
|
# stop DRG
|
||||||
self.write64(_AD9910_REG_DRAMPS, 0, 0)
|
self.write64(_AD9910_REG_RAMP_STEP, 0, 0)
|
||||||
at_mu((now_mu() + 0x10 & ~0xf) + io_up_delay) # delay
|
at_mu(t + 0x1000 + delay_stop)
|
||||||
self.cpld.io_update.pulse_mu(32 - io_up_delay) # realign
|
self.cpld.io_update.pulse_mu(32 - delay_stop) # realign
|
||||||
ftw = self.read32(_AD9910_REG_FTW) # read out effective FTW
|
ftw = self.read32(_AD9910_REG_FTW) # read out effective FTW
|
||||||
delay(100*us) # slack
|
delay(100*us) # slack
|
||||||
# disable DRG
|
# disable DRG
|
||||||
|
@ -510,22 +515,35 @@ class AD9910:
|
||||||
|
|
||||||
Scan through increasing IO_UPDATE delays until a delay is found that
|
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
|
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
|
This method assumes that the IO_UPDATE TTLOut device has one machine
|
||||||
unit resolution (SERDES) and that the ratio between fine RTIO frequency
|
unit resolution (SERDES).
|
||||||
(RTIO time machine units) and SYNC_CLK is 4.
|
|
||||||
|
|
||||||
:return: Stable IO_UPDATE delay to be passed to the constructor
|
:return: Stable IO_UPDATE delay to be passed to the constructor
|
||||||
:class:`AD9910` via the device database.
|
:class:`AD9910` via the device database.
|
||||||
"""
|
"""
|
||||||
period = 4 # f_RTIO/f_SYNC = 4
|
period = self.sysclk_per_mu * 4 # SYNC_CLK period
|
||||||
max_delay = 8 # mu, 1 ns
|
repeat = 100
|
||||||
d0 = self.io_update_delay
|
for i in range(period):
|
||||||
t0 = int32(self.measure_io_update_alignment(d0))
|
t = 0
|
||||||
for i in range(max_delay - 1):
|
# check whether the sync edge is strictly between i, i+2
|
||||||
t = self.measure_io_update_alignment(
|
for j in range(repeat):
|
||||||
(d0 + i + 1) & (max_delay - 1))
|
t += self.measure_io_update_alignment(i, i + 2)
|
||||||
if t != t0:
|
if t != 0: # no certain edge
|
||||||
return (d0 + i + period//2) & (period - 1)
|
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] == 0) or
|
||||||
|
(t1[0] == repeat and t1[1] == repeat)):
|
||||||
|
# edge is not close to i + 1, can't interpret result
|
||||||
|
raise ValueError(
|
||||||
|
"no clear IO_UPDATE-SYNC_CLK alignment edge found")
|
||||||
|
else:
|
||||||
|
# the good delay is period//2 after the edge
|
||||||
|
return (i + 1 + period//2) & (period - 1)
|
||||||
raise ValueError("no IO_UPDATE-SYNC_CLK alignment edge found")
|
raise ValueError("no IO_UPDATE-SYNC_CLK alignment edge found")
|
||||||
|
|
|
@ -14,7 +14,8 @@ SPI_CONFIG = (0*spi.SPI_OFFLINE | 0*spi.SPI_END |
|
||||||
# SPI clock write and read dividers
|
# SPI clock write and read dividers
|
||||||
SPIT_CFG_WR = 2
|
SPIT_CFG_WR = 2
|
||||||
SPIT_CFG_RD = 16
|
SPIT_CFG_RD = 16
|
||||||
SPIT_ATT_WR = 2
|
# 30 MHz fmax, 20 ns setup, 40 ns shift to latch (limiting)
|
||||||
|
SPIT_ATT_WR = 6
|
||||||
SPIT_ATT_RD = 16
|
SPIT_ATT_RD = 16
|
||||||
SPIT_DDS_WR = 2
|
SPIT_DDS_WR = 2
|
||||||
SPIT_DDS_RD = 16
|
SPIT_DDS_RD = 16
|
||||||
|
@ -174,7 +175,7 @@ class CPLD:
|
||||||
self.cfg_reg = urukul_cfg(rf_sw=rf_sw, led=0, profile=0,
|
self.cfg_reg = urukul_cfg(rf_sw=rf_sw, led=0, profile=0,
|
||||||
io_update=0, mask_nu=0, clk_sel=clk_sel,
|
io_update=0, mask_nu=0, clk_sel=clk_sel,
|
||||||
sync_sel=sync_sel, rst=0, io_rst=0)
|
sync_sel=sync_sel, rst=0, io_rst=0)
|
||||||
self.att_reg = att
|
self.att_reg = int32(att)
|
||||||
self.sync_div = sync_div
|
self.sync_div = sync_div
|
||||||
|
|
||||||
@kernel
|
@kernel
|
||||||
|
@ -299,7 +300,8 @@ class CPLD:
|
||||||
|
|
||||||
:param channel: Attenuator channel (0-3).
|
:param channel: Attenuator channel (0-3).
|
||||||
:param att: Attenuation setting in dB. Higher value is more
|
:param att: Attenuation setting in dB. Higher value is more
|
||||||
attenuation.
|
attenuation. Minimum attenuation is 0*dB, maximum attenuation is
|
||||||
|
31.5*dB.
|
||||||
"""
|
"""
|
||||||
self.set_att_mu(channel, 255 - int32(round(att*8)))
|
self.set_att_mu(channel, 255 - int32(round(att*8)))
|
||||||
|
|
||||||
|
@ -333,3 +335,15 @@ class CPLD:
|
||||||
ftw = ftw_max//div
|
ftw = ftw_max//div
|
||||||
assert ftw*div == ftw_max
|
assert ftw*div == ftw_max
|
||||||
self.sync.set_mu(ftw)
|
self.sync.set_mu(ftw)
|
||||||
|
|
||||||
|
@kernel
|
||||||
|
def set_profile(self, profile):
|
||||||
|
"""Set the PROFILE pins.
|
||||||
|
|
||||||
|
The PROFILE pins are common to all four DDS channels.
|
||||||
|
|
||||||
|
:param profile: PROFILE pins in numeric representation (0-7).
|
||||||
|
"""
|
||||||
|
cfg = self.cfg_reg & ~(7 << CFG_PROFILE)
|
||||||
|
cfg |= (profile & 7) << CFG_PROFILE
|
||||||
|
self.cfg_write(cfg)
|
||||||
|
|
|
@ -242,6 +242,12 @@ mod ddr {
|
||||||
ddrphy::dly_sel_write(1 << (DQS_SIGNAL_COUNT - n - 1));
|
ddrphy::dly_sel_write(1 << (DQS_SIGNAL_COUNT - n - 1));
|
||||||
|
|
||||||
ddrphy::rdly_dq_rst_write(1);
|
ddrphy::rdly_dq_rst_write(1);
|
||||||
|
#[cfg(soc_platform = "kasli")]
|
||||||
|
{
|
||||||
|
for _ in 0..3 {
|
||||||
|
ddrphy::rdly_dq_bitslip_write(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
for _ in 0..DDRPHY_MAX_DELAY {
|
for _ in 0..DDRPHY_MAX_DELAY {
|
||||||
let mut working = true;
|
let mut working = true;
|
||||||
|
@ -327,6 +333,12 @@ mod ddr {
|
||||||
let mut max_seen_valid = 0;
|
let mut max_seen_valid = 0;
|
||||||
|
|
||||||
ddrphy::rdly_dq_rst_write(1);
|
ddrphy::rdly_dq_rst_write(1);
|
||||||
|
#[cfg(soc_platform = "kasli")]
|
||||||
|
{
|
||||||
|
for _ in 0..3 {
|
||||||
|
ddrphy::rdly_dq_bitslip_write(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
for delay in 0..DDRPHY_MAX_DELAY {
|
for delay in 0..DDRPHY_MAX_DELAY {
|
||||||
let mut valid = true;
|
let mut valid = true;
|
||||||
|
@ -384,6 +396,12 @@ mod ddr {
|
||||||
|
|
||||||
// Set delay to the middle
|
// Set delay to the middle
|
||||||
ddrphy::rdly_dq_rst_write(1);
|
ddrphy::rdly_dq_rst_write(1);
|
||||||
|
#[cfg(soc_platform = "kasli")]
|
||||||
|
{
|
||||||
|
for _ in 0..3 {
|
||||||
|
ddrphy::rdly_dq_bitslip_write(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
for _ in 0..mean_delay {
|
for _ in 0..mean_delay {
|
||||||
ddrphy::rdly_dq_inc_write(1);
|
ddrphy::rdly_dq_inc_write(1);
|
||||||
}
|
}
|
||||||
|
|
|
@ -541,7 +541,7 @@ class SUServo(_EEM):
|
||||||
target.submodules += phy
|
target.submodules += phy
|
||||||
target.rtio_channels.append(rtio.Channel.from_phy(phy, ififo_depth=4))
|
target.rtio_channels.append(rtio.Channel.from_phy(phy, ififo_depth=4))
|
||||||
|
|
||||||
pads = target.platform.request("{}_dds_reset".format(eem_urukul1))
|
pads = target.platform.request("{}_dds_reset_sync_in".format(eem_urukul1))
|
||||||
target.specials += DifferentialOutput(0, pads.p, pads.n)
|
target.specials += DifferentialOutput(0, pads.p, pads.n)
|
||||||
|
|
||||||
for i, signal in enumerate("sw0 sw1 sw2 sw3".split()):
|
for i, signal in enumerate("sw0 sw1 sw2 sw3".split()):
|
||||||
|
|
|
@ -275,6 +275,78 @@ class _RangeScan(LayoutWidget):
|
||||||
randomize.setChecked(state["randomize"])
|
randomize.setChecked(state["randomize"])
|
||||||
|
|
||||||
|
|
||||||
|
class _CenterScan(LayoutWidget):
|
||||||
|
def __init__(self, procdesc, state):
|
||||||
|
LayoutWidget.__init__(self)
|
||||||
|
|
||||||
|
scale = procdesc["scale"]
|
||||||
|
|
||||||
|
def apply_properties(widget):
|
||||||
|
widget.setDecimals(procdesc["ndecimals"])
|
||||||
|
if procdesc["global_min"] is not None:
|
||||||
|
widget.setMinimum(procdesc["global_min"]/scale)
|
||||||
|
else:
|
||||||
|
widget.setMinimum(float("-inf"))
|
||||||
|
if procdesc["global_max"] is not None:
|
||||||
|
widget.setMaximum(procdesc["global_max"]/scale)
|
||||||
|
else:
|
||||||
|
widget.setMaximum(float("inf"))
|
||||||
|
if procdesc["global_step"] is not None:
|
||||||
|
widget.setSingleStep(procdesc["global_step"]/scale)
|
||||||
|
if procdesc["unit"]:
|
||||||
|
widget.setSuffix(" " + procdesc["unit"])
|
||||||
|
|
||||||
|
center = ScientificSpinBox()
|
||||||
|
disable_scroll_wheel(center)
|
||||||
|
apply_properties(center)
|
||||||
|
center.setPrecision()
|
||||||
|
center.setRelativeStep()
|
||||||
|
center.setValue(state["center"])
|
||||||
|
self.addWidget(center, 0, 1)
|
||||||
|
self.addWidget(QtWidgets.QLabel("Center:"), 0, 0)
|
||||||
|
|
||||||
|
span = ScientificSpinBox()
|
||||||
|
disable_scroll_wheel(span)
|
||||||
|
apply_properties(span)
|
||||||
|
span.setPrecision()
|
||||||
|
span.setRelativeStep()
|
||||||
|
span.setMinimum(0)
|
||||||
|
span.setValue(state["span"])
|
||||||
|
self.addWidget(span, 1, 1)
|
||||||
|
self.addWidget(QtWidgets.QLabel("Span:"), 1, 0)
|
||||||
|
|
||||||
|
step = ScientificSpinBox()
|
||||||
|
disable_scroll_wheel(step)
|
||||||
|
apply_properties(step)
|
||||||
|
step.setPrecision()
|
||||||
|
step.setRelativeStep()
|
||||||
|
step.setMinimum(0)
|
||||||
|
step.setValue(state["step"])
|
||||||
|
self.addWidget(step, 2, 1)
|
||||||
|
self.addWidget(QtWidgets.QLabel("Step:"), 2, 0)
|
||||||
|
|
||||||
|
randomize = QtWidgets.QCheckBox("Randomize")
|
||||||
|
self.addWidget(randomize, 3, 1)
|
||||||
|
randomize.setChecked(state["randomize"])
|
||||||
|
|
||||||
|
def update_center(value):
|
||||||
|
state["center"] = value*scale
|
||||||
|
|
||||||
|
def update_span(value):
|
||||||
|
state["span"] = value*scale
|
||||||
|
|
||||||
|
def update_step(value):
|
||||||
|
state["step"] = value*scale
|
||||||
|
|
||||||
|
def update_randomize(value):
|
||||||
|
state["randomize"] = value
|
||||||
|
|
||||||
|
center.valueChanged.connect(update_center)
|
||||||
|
span.valueChanged.connect(update_span)
|
||||||
|
step.valueChanged.connect(update_step)
|
||||||
|
randomize.stateChanged.connect(update_randomize)
|
||||||
|
|
||||||
|
|
||||||
class _ExplicitScan(LayoutWidget):
|
class _ExplicitScan(LayoutWidget):
|
||||||
def __init__(self, state):
|
def __init__(self, state):
|
||||||
LayoutWidget.__init__(self)
|
LayoutWidget.__init__(self)
|
||||||
|
@ -307,6 +379,7 @@ class ScanEntry(LayoutWidget):
|
||||||
self.widgets = OrderedDict()
|
self.widgets = OrderedDict()
|
||||||
self.widgets["NoScan"] = _NoScan(procdesc, state["NoScan"])
|
self.widgets["NoScan"] = _NoScan(procdesc, state["NoScan"])
|
||||||
self.widgets["RangeScan"] = _RangeScan(procdesc, state["RangeScan"])
|
self.widgets["RangeScan"] = _RangeScan(procdesc, state["RangeScan"])
|
||||||
|
self.widgets["CenterScan"] = _CenterScan(procdesc, state["CenterScan"])
|
||||||
self.widgets["ExplicitScan"] = _ExplicitScan(state["ExplicitScan"])
|
self.widgets["ExplicitScan"] = _ExplicitScan(state["ExplicitScan"])
|
||||||
for widget in self.widgets.values():
|
for widget in self.widgets.values():
|
||||||
self.stack.addWidget(widget)
|
self.stack.addWidget(widget)
|
||||||
|
@ -314,6 +387,7 @@ class ScanEntry(LayoutWidget):
|
||||||
self.radiobuttons = OrderedDict()
|
self.radiobuttons = OrderedDict()
|
||||||
self.radiobuttons["NoScan"] = QtWidgets.QRadioButton("No scan")
|
self.radiobuttons["NoScan"] = QtWidgets.QRadioButton("No scan")
|
||||||
self.radiobuttons["RangeScan"] = QtWidgets.QRadioButton("Range")
|
self.radiobuttons["RangeScan"] = QtWidgets.QRadioButton("Range")
|
||||||
|
self.radiobuttons["CenterScan"] = QtWidgets.QRadioButton("Center")
|
||||||
self.radiobuttons["ExplicitScan"] = QtWidgets.QRadioButton("Explicit")
|
self.radiobuttons["ExplicitScan"] = QtWidgets.QRadioButton("Explicit")
|
||||||
scan_type = QtWidgets.QButtonGroup()
|
scan_type = QtWidgets.QButtonGroup()
|
||||||
for n, b in enumerate(self.radiobuttons.values()):
|
for n, b in enumerate(self.radiobuttons.values()):
|
||||||
|
@ -343,6 +417,8 @@ class ScanEntry(LayoutWidget):
|
||||||
"NoScan": {"value": 0.0, "repetitions": 1},
|
"NoScan": {"value": 0.0, "repetitions": 1},
|
||||||
"RangeScan": {"start": 0.0, "stop": 100.0*scale, "npoints": 10,
|
"RangeScan": {"start": 0.0, "stop": 100.0*scale, "npoints": 10,
|
||||||
"randomize": False},
|
"randomize": False},
|
||||||
|
"CenterScan": {"center": 0.*scale, "span": 100.*scale,
|
||||||
|
"step": 10.*scale, "randomize": False},
|
||||||
"ExplicitScan": {"sequence": []}
|
"ExplicitScan": {"sequence": []}
|
||||||
}
|
}
|
||||||
if "default" in procdesc:
|
if "default" in procdesc:
|
||||||
|
@ -361,6 +437,9 @@ class ScanEntry(LayoutWidget):
|
||||||
state[ty]["npoints"] = default["npoints"]
|
state[ty]["npoints"] = default["npoints"]
|
||||||
state[ty]["randomize"] = default["randomize"]
|
state[ty]["randomize"] = default["randomize"]
|
||||||
state[ty]["seed"] = default["seed"]
|
state[ty]["seed"] = default["seed"]
|
||||||
|
elif ty == "CenterScan":
|
||||||
|
for key in "center span step randomize seed".split():
|
||||||
|
state[ty][key] = default[key]
|
||||||
elif ty == "ExplicitScan":
|
elif ty == "ExplicitScan":
|
||||||
state[ty]["sequence"] = default["sequence"]
|
state[ty]["sequence"] = default["sequence"]
|
||||||
else:
|
else:
|
||||||
|
|
|
@ -27,7 +27,7 @@ from artiq.language import units
|
||||||
|
|
||||||
|
|
||||||
__all__ = ["ScanObject",
|
__all__ = ["ScanObject",
|
||||||
"NoScan", "RangeScan", "ExplicitScan",
|
"NoScan", "RangeScan", "CenterScan", "ExplicitScan",
|
||||||
"Scannable", "MultiScanManager"]
|
"Scannable", "MultiScanManager"]
|
||||||
|
|
||||||
|
|
||||||
|
@ -93,6 +93,43 @@ class RangeScan(ScanObject):
|
||||||
"seed": self.seed}
|
"seed": self.seed}
|
||||||
|
|
||||||
|
|
||||||
|
class CenterScan(ScanObject):
|
||||||
|
"""A scan object that yields evenly spaced values within a span around a
|
||||||
|
center. If ``step`` is finite, then ``center`` is always included.
|
||||||
|
Values outside ``span`` around center are never included.
|
||||||
|
If ``randomize`` is True the points are randomly ordered."""
|
||||||
|
def __init__(self, center, span, step, randomize=False, seed=None):
|
||||||
|
self.center = center
|
||||||
|
self.span = span
|
||||||
|
self.step = step
|
||||||
|
self.randomize = randomize
|
||||||
|
self.seed = seed
|
||||||
|
|
||||||
|
if step == 0.:
|
||||||
|
self.sequence = []
|
||||||
|
else:
|
||||||
|
n = 1 + int(span/(2.*step))
|
||||||
|
self.sequence = [center + sign*i*step
|
||||||
|
for i in range(n) for sign in [-1, 1]][1:]
|
||||||
|
|
||||||
|
if randomize:
|
||||||
|
rng = random.Random(seed)
|
||||||
|
random.shuffle(self.sequence, rng.random)
|
||||||
|
|
||||||
|
def __iter__(self):
|
||||||
|
return iter(self.sequence)
|
||||||
|
|
||||||
|
def __len__(self):
|
||||||
|
return len(self.sequence)
|
||||||
|
|
||||||
|
def describe(self):
|
||||||
|
return {"ty": "CenterScan",
|
||||||
|
"center": self.center, "step": self.step,
|
||||||
|
"span": self.span,
|
||||||
|
"randomize": self.randomize,
|
||||||
|
"seed": self.seed}
|
||||||
|
|
||||||
|
|
||||||
class ExplicitScan(ScanObject):
|
class ExplicitScan(ScanObject):
|
||||||
"""A scan object that yields values from an explicitly defined sequence."""
|
"""A scan object that yields values from an explicitly defined sequence."""
|
||||||
def __init__(self, sequence):
|
def __init__(self, sequence):
|
||||||
|
@ -111,6 +148,7 @@ class ExplicitScan(ScanObject):
|
||||||
_ty_to_scan = {
|
_ty_to_scan = {
|
||||||
"NoScan": NoScan,
|
"NoScan": NoScan,
|
||||||
"RangeScan": RangeScan,
|
"RangeScan": RangeScan,
|
||||||
|
"CenterScan": CenterScan,
|
||||||
"ExplicitScan": ExplicitScan
|
"ExplicitScan": ExplicitScan
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -104,18 +104,21 @@ class AD9910Exp(EnvExperiment):
|
||||||
self.core.break_realtime()
|
self.core.break_realtime()
|
||||||
self.dev.cpld.init()
|
self.dev.cpld.init()
|
||||||
self.dev.init()
|
self.dev.init()
|
||||||
bins = [0]*8
|
bins1 = [0]*4
|
||||||
self.scan_io_delay(bins)
|
bins2 = [0]*4
|
||||||
self.set_dataset("bins", bins)
|
self.scan_io_delay(bins1, bins2)
|
||||||
self.set_dataset("dly", self.dev.io_update_delay)
|
self.set_dataset("bins1", bins1)
|
||||||
|
self.set_dataset("bins2", bins2)
|
||||||
|
self.set_dataset("dly", self.dev.tune_io_update_delay())
|
||||||
|
|
||||||
@kernel
|
@kernel
|
||||||
def scan_io_delay(self, bins):
|
def scan_io_delay(self, bins1, bins2):
|
||||||
delay(100*us)
|
delay(100*us)
|
||||||
n = 100
|
n = 100
|
||||||
for i in range(n):
|
for i in range(n):
|
||||||
for phase in range(len(bins)):
|
for j in range(len(bins1)):
|
||||||
bins[phase] += self.dev.measure_io_update_alignment(phase)
|
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)
|
delay(10*ms)
|
||||||
|
|
||||||
@kernel
|
@kernel
|
||||||
|
@ -131,6 +134,24 @@ class AD9910Exp(EnvExperiment):
|
||||||
sw_off = (self.dev.cpld.sta_read() >> (self.dev.chip_select - 4)) & 1
|
sw_off = (self.dev.cpld.sta_read() >> (self.dev.chip_select - 4)) & 1
|
||||||
self.set_dataset("sw", (sw_on, sw_off))
|
self.set_dataset("sw", (sw_on, sw_off))
|
||||||
|
|
||||||
|
@kernel
|
||||||
|
def profile_readback(self):
|
||||||
|
self.core.break_realtime()
|
||||||
|
self.dev.cpld.init()
|
||||||
|
self.dev.init()
|
||||||
|
for i in range(8):
|
||||||
|
self.dev.set_mu(ftw=i, profile=i)
|
||||||
|
ftw = [0] * 8
|
||||||
|
for i in range(8):
|
||||||
|
self.dev.cpld.set_profile(i)
|
||||||
|
# If PROFILE is not alligned to SYNC_CLK a multi-bit change
|
||||||
|
# doesn't transfer cleanly. Use IO_UPDATE to load the profile
|
||||||
|
# again.
|
||||||
|
self.dev.cpld.io_update.pulse_mu(8)
|
||||||
|
ftw[i] = self.dev.read32(_AD9910_REG_FTW)
|
||||||
|
delay(100*us)
|
||||||
|
self.set_dataset("ftw", ftw)
|
||||||
|
|
||||||
|
|
||||||
class AD9910Test(ExperimentCase):
|
class AD9910Test(ExperimentCase):
|
||||||
def test_instantiate(self):
|
def test_instantiate(self):
|
||||||
|
@ -174,13 +195,19 @@ class AD9910Test(ExperimentCase):
|
||||||
def test_io_update_delay(self):
|
def test_io_update_delay(self):
|
||||||
self.execute(AD9910Exp, "io_update_delay")
|
self.execute(AD9910Exp, "io_update_delay")
|
||||||
dly = self.dataset_mgr.get("dly")
|
dly = self.dataset_mgr.get("dly")
|
||||||
bins = self.dataset_mgr.get("bins")
|
bins1 = self.dataset_mgr.get("bins1")
|
||||||
print(dly, bins)
|
bins2 = self.dataset_mgr.get("bins2")
|
||||||
n = max(bins)
|
print(dly, bins1, bins2)
|
||||||
# test for 4-periodicity (SYNC_CLK) and maximal contrast
|
n = max(bins2)
|
||||||
for i in range(len(bins)):
|
# no edge at optimal delay
|
||||||
self.assertEqual(abs(bins[i] - bins[(i + 4) % 8]), n)
|
self.assertEqual(bins2[(dly + 1) & 3], 0)
|
||||||
|
# edge at expected position
|
||||||
|
self.assertEqual(bins2[(dly + 3) & 3], n)
|
||||||
|
|
||||||
def test_sw_readback(self):
|
def test_sw_readback(self):
|
||||||
self.execute(AD9910Exp, "sw_readback")
|
self.execute(AD9910Exp, "sw_readback")
|
||||||
self.assertEqual(self.dataset_mgr.get("sw"), (1, 0))
|
self.assertEqual(self.dataset_mgr.get("sw"), (1, 0))
|
||||||
|
|
||||||
|
def test_profile_readback(self):
|
||||||
|
self.execute(AD9910Exp, "profile_readback")
|
||||||
|
self.assertEqual(self.dataset_mgr.get("ftw"), list(range(8)))
|
||||||
|
|
|
@ -98,6 +98,13 @@ class UrukulExp(EnvExperiment):
|
||||||
self.dev.init()
|
self.dev.init()
|
||||||
self.dev.set_sync_div(2)
|
self.dev.set_sync_div(2)
|
||||||
|
|
||||||
|
@kernel
|
||||||
|
def profile(self):
|
||||||
|
self.core.break_realtime()
|
||||||
|
self.dev.init()
|
||||||
|
self.dev.set_profile(7)
|
||||||
|
self.dev.set_profile(0)
|
||||||
|
|
||||||
|
|
||||||
class UrukulTest(ExperimentCase):
|
class UrukulTest(ExperimentCase):
|
||||||
def test_instantiate(self):
|
def test_instantiate(self):
|
||||||
|
@ -147,3 +154,6 @@ class UrukulTest(ExperimentCase):
|
||||||
|
|
||||||
def test_sync(self):
|
def test_sync(self):
|
||||||
self.execute(UrukulExp, "sync")
|
self.execute(UrukulExp, "sync")
|
||||||
|
|
||||||
|
def test_profile(self):
|
||||||
|
self.execute(UrukulExp, "profile")
|
||||||
|
|
|
@ -15,7 +15,7 @@ requirements:
|
||||||
- python >=3.5.3,<3.6
|
- python >=3.5.3,<3.6
|
||||||
- setuptools 33.1.1
|
- setuptools 33.1.1
|
||||||
- migen 0.8 py35_0+git2d62c0c
|
- migen 0.8 py35_0+git2d62c0c
|
||||||
- misoc 0.11 py35_31+git5ce139dd
|
- misoc 0.11 py35_33+git128750aa
|
||||||
- jesd204b 0.10
|
- jesd204b 0.10
|
||||||
- microscope
|
- microscope
|
||||||
- binutils-or1k-linux >=2.27
|
- binutils-or1k-linux >=2.27
|
||||||
|
|
|
@ -46,4 +46,6 @@ Default network ports
|
||||||
+---------------------------------+--------------+
|
+---------------------------------+--------------+
|
||||||
| TOPTICA Laser SDK (out-of-tree) | 3272 |
|
| TOPTICA Laser SDK (out-of-tree) | 3272 |
|
||||||
+---------------------------------+--------------+
|
+---------------------------------+--------------+
|
||||||
|
| HighFinesse (out-of-tree) | 3273 |
|
||||||
|
+---------------------------------+--------------+
|
||||||
|
|
||||||
|
|
|
@ -131,7 +131,8 @@ On Windows, a third-party tool, `Zadig <http://zadig.akeo.ie/>`_, is necessary.
|
||||||
|
|
||||||
1. Make sure the FPGA board's JTAG USB port is connected to your computer.
|
1. Make sure the FPGA board's JTAG USB port is connected to your computer.
|
||||||
2. Activate Options → List All Devices.
|
2. Activate Options → List All Devices.
|
||||||
3. Select the "Digilent Adept USB Device (Interface 0)" device from the drop-down list.
|
3. Select the "Digilent Adept USB Device (Interface 0)" or "FTDI Quad-RS232 HS" (or similar)
|
||||||
|
device from the drop-down list.
|
||||||
4. Select WinUSB from the spinner list.
|
4. Select WinUSB from the spinner list.
|
||||||
5. Click "Install Driver" or "Replace Driver".
|
5. Click "Install Driver" or "Replace Driver".
|
||||||
|
|
||||||
|
|
|
@ -1,14 +1,14 @@
|
||||||
{ stdenv, fetchgit, llvm-or1k, makeWrapper, python3, ncurses, zlib, python3Packages }:
|
{ stdenv, fetchFromGitHub, llvm-or1k, makeWrapper, python3, ncurses, zlib, python3Packages }:
|
||||||
let
|
let
|
||||||
version = "0f4ebae";
|
version = "0f4ebae";
|
||||||
in
|
in
|
||||||
stdenv.mkDerivation rec {
|
stdenv.mkDerivation rec {
|
||||||
name = "llvmlite-${version}";
|
name = "llvmlite-${version}";
|
||||||
src = fetchgit {
|
src = fetchFromGitHub {
|
||||||
url = "https://github.com/m-labs/llvmlite";
|
|
||||||
rev = "401dfb713166bdd2bc0d3ab2b7ebf12e7a434130";
|
rev = "401dfb713166bdd2bc0d3ab2b7ebf12e7a434130";
|
||||||
sha256 = "1ci1pnpspv1pqz712yix1nmplq7568vpsr6gzzl3a33w9s0sw2nq";
|
owner = "m-labs";
|
||||||
leaveDotGit = true;
|
repo = "llvmlite";
|
||||||
|
sha256 = "1hqahd87ihwgjsaxv0y2iywldi1zgyjxjfy3sy3rr1gnwvxb47xw";
|
||||||
};
|
};
|
||||||
|
|
||||||
buildInputs = [ makeWrapper python3 ncurses zlib llvm-or1k python3Packages.setuptools ];
|
buildInputs = [ makeWrapper python3 ncurses zlib llvm-or1k python3Packages.setuptools ];
|
||||||
|
|
Loading…
Reference in New Issue