fastino: add interpolator support

This commit is contained in:
Robert Jördens 2021-10-07 13:47:08 +00:00
parent 1ce505c547
commit 3f6bf33298
2 changed files with 99 additions and 11 deletions

View File

@ -2,7 +2,7 @@
streaming DAC.
"""
from artiq.language.core import kernel, portable, delay
from artiq.language.core import kernel, portable, delay, delay_mu
from artiq.coredevice.rtio import (rtio_output, rtio_output_wide,
rtio_input_data)
from artiq.language.units import us
@ -190,3 +190,68 @@ class Fastino:
green LED.
"""
self.write(0x23, leds)
@kernel
def set_continuous(self, channel_mask):
"""Enable continuous DAC updates on channels regardless of new data
being submitted.
"""
self.write(0x25, channel_mask)
@kernel
def stage_cic_mu(self, rate_mantissa, rate_exponent, gain_exponent):
"""Stage machine unit interpolator configuration.
"""
if rate_mantissa < 0 or rate_mantissa >= 1 << 6:
raise ValueError("rate_mantissa out of bounds")
if rate_exponent < 0 or rate_exponent >= 1 << 4:
raise ValueError("rate_exponent out of bounds")
if gain_exponent < 0 or gain_exponent >= 1 << 6:
raise ValueError("gain_exponent out of bounds")
config = rate_mantissa | (rate_exponent << 6) | (gain_exponent << 10)
self.write(0x26, config)
@kernel
def stage_cic(self, rate) -> TInt32:
"""Compute and stage interpolator configuration.
Approximates rate using 6+4 bit floating point representation,
approximates optimal interpolation gain compensation exponent to avoid
clipping. Gains for rates that are powers of two are accurately
compensated. Other rates lead to overall less than unity gain.
Returns the actual interpolation rate.
The actual overall interpolation gain including gain compensation is
`actual_rate**order/2**ceil(log2(actual_rate**order))`
where `order = 3`.
"""
if rate <= 0 or rate > 1 << 16:
raise ValueError("rate out of bounds")
rate_mantissa = rate
rate_exponent = 0
while rate_mantissa > 1 << 6:
rate_exponent += 1
rate_mantissa >>= 1
order = 3
gain = 1
for i in range(order):
gain *= rate_mantissa
gain_exponent = 0
while gain > 1 << gain_exponent:
gain_exponent += 1
gain_exponent += order*rate_exponent
assert gain_exponent <= order*16
self.stage_cic_mu(rate_mantissa - 1, rate_exponent, gain_exponent)
return rate_mantissa << rate_exponent
@kernel
def apply_cic(self, channel_mask):
"""Apply the staged interpolator configuration on the specified channels.
Channels using non-unity interpolation rate and variable data should have
continous DAC updates enabled (see :meth:`set_continuous`).
This resets and settles the interpolators. There will be no output
updates for the next `order = 3` input samples.
"""
self.write(0x27, channel_mask)

View File

@ -27,16 +27,16 @@ class Fastino(Module):
# dac data words
dacs = [Signal(16) for i in range(32)]
header = Record([
("cfg", 4),
("leds", 8),
("reserved", 8),
("typ", 1),
("reserved", 7),
("addr", 4),
("enable", len(dacs)),
])
body = Cat(header.raw_bits(), dacs)
assert len(body) == len(self.serializer.payload)
self.comb += self.serializer.payload.eq(body)
assert len(Cat(header.raw_bits(), dacs)) == len(self.serializer.payload)
# # #
@ -62,38 +62,61 @@ class Fastino(Module):
# address space is sparse.
hold = Signal.like(header.enable)
continuous = Signal.like(header.enable)
cic_config = Signal(16)
read_regs = Array([Signal.like(self.serializer.readback)
for _ in range(1 << len(header.addr))])
cases = {
# update
0x20: header.enable.eq(header.enable | self.rtlink.o.data),
0x20: [
header.enable.eq(self.rtlink.o.data),
header.typ.eq(0),
],
# hold
0x21: hold.eq(self.rtlink.o.data),
# cfg
0x22: header.cfg.eq(self.rtlink.o.data),
# leds
0x23: header.leds.eq(self.rtlink.o.data),
# reserved
# reserved bits
0x24: header.reserved.eq(self.rtlink.o.data),
# force continuous DAC updates
0x25: continuous.eq(self.rtlink.o.data),
# interpolator configuration stage
0x26: cic_config.eq(self.rtlink.o.data),
# interpolator update flags
0x27: [
header.enable.eq(self.rtlink.o.data),
header.typ.eq(1),
],
}
for i in range(0, len(dacs), width):
cases[i] = [
Cat(dacs[i:i + width]).eq(self.rtlink.o.data),
[If(~hold[i + j],
[If(~hold[i + j] & (header.typ == 0),
header.enable[i + j].eq(1),
) for j in range(width)]
]
self.comb += [
If(header.typ == 0,
self.serializer.payload.eq(Cat(header.raw_bits(), dacs)),
).Else(
self.serializer.payload.eq(Cat(header.raw_bits(), Replicate(cic_config, len(dacs)))),
),
]
self.sync.rio_phy += [
If(self.serializer.stb,
header.enable.eq(0),
header.typ.eq(0),
header.enable.eq(continuous),
read_regs[header.addr].eq(self.serializer.readback),
header.addr.eq(header.addr + 1),
),
If(self.rtlink.o.stb & ~self.rtlink.o.address[-1],
Case(self.rtlink.o.address[:-1], cases),
If(self.rtlink.o.stb,
Case(self.rtlink.o.address, cases),
),
]