miqro: document coredevice driver

This commit is contained in:
Robert Jördens 2022-09-23 12:59:21 +00:00
parent 5cfa8d9a42
commit 513f9f00f3
1 changed files with 97 additions and 9 deletions

View File

@ -1420,15 +1420,28 @@ class Miqro:
@kernel @kernel
def reset(self): def reset(self):
"""Establish no-output profiles and no-output window and execute them.
This establishes the first profile (index 0) on all oscillators as zero
amplitude, creates a trivial window (one sample with zero amplitude,
minimal interpolation), and executes a corresponding pulse.
"""
for osc in range(16): for osc in range(16):
for pro in range(32): self.set_profile_mu(osc, profile=0, ftw=0, asf=0)
self.set_profile_mu(osc, pro, 0, 0, 0)
delay(10*us) delay(10*us)
self.set_window_mu(start=0, iq=[0], rate=1, shift=0, order=0, head=0, tail=0) self.set_window_mu(start=0, iq=[0], order=0)
self.pulse(window=0, profiles=[0]) self.pulse(window=0, profiles=[0])
@kernel @kernel
def set_profile_mu(self, oscillator, profile, ftw, asf, pow=0): def set_profile_mu(self, oscillator, profile, ftw, asf, pow_=0):
"""Store an oscillator profile (machine units).
:param oscillator: Oscillator index (0 to 15)
:param profile: Profile index (0 to 31)
:param ftw: Frequency tuning word (32 bit signed integer on a 250 MHz clock)
:param asf: Amplitude scale factor (16 bit unsigned integer)
:param pow_: Phase offset word (16 bit integer)
"""
if oscillator >= 16: if oscillator >= 16:
raise ValueError("invalid oscillator index") raise ValueError("invalid oscillator index")
if profile >= 32: if profile >= 32:
@ -1438,21 +1451,51 @@ class Miqro:
(oscillator << 6) | (profile << 1)) (oscillator << 6) | (profile << 1))
self.channel.phaser.write32(PHASER_ADDR_MIQRO_MEM_DATA, ftw) self.channel.phaser.write32(PHASER_ADDR_MIQRO_MEM_DATA, ftw)
self.channel.phaser.write32(PHASER_ADDR_MIQRO_MEM_DATA, self.channel.phaser.write32(PHASER_ADDR_MIQRO_MEM_DATA,
(asf & 0xffff) | (pow << 16)) (asf & 0xffff) | (pow_ << 16))
@kernel @kernel
def set_profile(self, oscillator, profile, frequency, amplitude, phase=0.): def set_profile(self, oscillator, profile, frequency, amplitude, phase=0.):
# frequency is interpreted in the Nyquist sense, i.e. aliased """Store an oscillator profile.
:param oscillator: Oscillator index (0 to 15)
:param profile: Profile index (0 to 31)
:param frequency: Frequency in Hz (passband -100 to 100 MHz).
Interpreted in the Nyquist sense, i.e. aliased.
:param amplitude: Amplitude in units of full scale (0. to 1.)
:param phase: Phase in turns. See :class:`Miqro` for a definition of
phase in this context.
:return: The quantized 32 bit frequency tuning word
"""
ftw = int32(round(frequency*((1 << 30)/(62.5*MHz)))) ftw = int32(round(frequency*((1 << 30)/(62.5*MHz))))
asf = int32(round(amplitude*0xffff)) asf = int32(round(amplitude*0xffff))
if asf < 0 or asf > 0xffff: if asf < 0 or asf > 0xffff:
raise ValueError("amplitude out of bounds") raise ValueError("amplitude out of bounds")
pow = int32(round(phase*(1 << 16))) pow_ = int32(round(phase*(1 << 16)))
self.set_profile_mu(oscillator, profile, ftw, asf, pow) self.set_profile_mu(oscillator, profile, ftw, asf, pow_)
return ftw return ftw
@kernel @kernel
def set_window_mu(self, start, iq, rate=1, shift=0, order=3, head=1, tail=1): def set_window_mu(self, start, iq, rate=1, shift=0, order=3, head=1, tail=1):
"""Store a window segment (machine units)
:param start: Window start address (0 to 0x3ff)
:param iq: List of IQ window samples. Each window sample is an integer
containing the signed I part in the 16 LSB and the signed Q part in
the 16 MSB. The maximum window length is 0x3fe. The user must
ensure that this window does not overlap with other windows in the
memory.
:param rate: Interpolation rate change (1 to 1 << 12)
:param shift: Interpolator amplitude gain compensation in powers of 2 (0 to 63)
:param order: Interpolation order from 0 (corresponding to
constant/zero-order-hold/1st order CIC interpolation) to 3 (corresponding
to cubic/Parzen/4th order CIC interpolation)
:param head: Update the interpolator settings and clear its state at the start
of the window. This also implies starting the envelope from zero.
:param tail: Feed zeros into the interpolator after the window samples.
In the absence of further pulses this will return the output envelope
to zero with the chosen interpolation.
:return: Next available window memory address after this segment.
"""
if start >= 1 << 10: if start >= 1 << 10:
raise ValueError("start out of bounds") raise ValueError("start out of bounds")
if len(iq) >= 1 << 10: if len(iq) >= 1 << 10:
@ -1480,6 +1523,24 @@ class Miqro:
@kernel @kernel
def set_window(self, start, iq, period=4*ns, order=3, head=1, tail=1): def set_window(self, start, iq, period=4*ns, order=3, head=1, tail=1):
"""Store a window segment
:param start: Window start address (0 to 0x3ff)
:param iq: List of IQ window samples. Each window sample is a pair of
two float numbers -1 to 1, one for each I and Q in units of full scale.
The maximum window length is 0x3fe. The user must ensure that this window
does not overlap with other windows in the memory.
:param period: Desired window sample period in SI units (4*ns to (4 << 12)*ns).
:param order: Interpolation order from 0 (corresponding to
constant/zero-order-hold/1st order CIC interpolation) to 3 (corresponding
to cubic/Parzen/4th order CIC interpolation)
:param head: Update the interpolator settings and clear its state at the start
of the window. This also implies starting the envelope from zero.
:param tail: Feed zeros into the interpolator after the window samples.
In the absence of further pulses this will return the output envelope
to zero with the chosen interpolation.
:return: Actual sample period in SI units
"""
rate = int32(round(period/(4*ns))) rate = int32(round(period/(4*ns)))
gain = 1. gain = 1.
for _ in range(order): for _ in range(order):
@ -1499,6 +1560,17 @@ class Miqro:
@kernel @kernel
def encode(self, window, profiles, data): def encode(self, window, profiles, data):
"""Encode window and profile selection
:param window: Window start address (0 to 0x3ff)
:param profiles: List of profile indices for the oscillators. Maximum
length 16. Unused oscillators will be set to profile 0.
:param data: List of integers to store the encoded data words into.
Unused entries will remain untouched. Must contain at least three
lements if all oscillators are used and should be initialized to
zeros.
:return: Number of words from `data` used.
"""
if len(profiles) > 16: if len(profiles) > 16:
raise ValueError("too many oscillators") raise ValueError("too many oscillators")
if window > 0x3ff: if window > 0x3ff:
@ -1518,6 +1590,13 @@ class Miqro:
@kernel @kernel
def pulse_mu(self, data): def pulse_mu(self, data):
"""Emit a pulse (encoded)
The pulse fiducial timing resolution is 4 ns.
:param data: List of up to 3 words containing an encoded MIQRO pulse as
returned by :meth:`encode`.
"""
word = len(data) word = len(data)
delay_mu(-8*word) # back shift to align delay_mu(-8*word) # back shift to align
while word > 0: while word > 0:
@ -1528,6 +1607,15 @@ class Miqro:
@kernel @kernel
def pulse(self, window, profiles): def pulse(self, window, profiles):
"""Emit a pulse
This encodes the window and profiles (see :meth:`encode`) and emits them
(see :meth:`pulse_mu`).
:param window: Window start address (0 to 0x3ff)
:param profiles: List of profile indices for the oscillators. Maximum
length 16. Unused oscillators will select profile 0.
"""
data = [0, 0, 0] data = [0, 0, 0]
words = self.encode(window, profiles, data) words = self.encode(window, profiles, data)
self.pulse_mu(data[:words]) self.pulse_mu(data[:words])