Merge branch 'master' into new

This commit is contained in:
Sebastien Bourdeauducq 2018-11-16 15:20:32 +08:00
commit 1b841805f6
12 changed files with 276 additions and 69 deletions

View File

@ -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")

View File

@ -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)

View File

@ -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);
} }

View File

@ -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()):

View File

@ -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:

View File

@ -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
} }

View File

@ -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)))

View File

@ -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")

View File

@ -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

View File

@ -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 |
+---------------------------------+--------------+

View File

@ -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".

View File

@ -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 ];