forked from M-Labs/artiq
commit
591507a7c0
|
@ -3,7 +3,7 @@ streaming DAC.
|
||||||
"""
|
"""
|
||||||
from numpy import int32
|
from numpy import int32
|
||||||
|
|
||||||
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,
|
from artiq.coredevice.rtio import (rtio_output, rtio_output_wide,
|
||||||
rtio_input_data)
|
rtio_input_data)
|
||||||
from artiq.language.units import us
|
from artiq.language.units import us
|
||||||
|
@ -191,3 +191,82 @@ class Fastino:
|
||||||
green LED.
|
green LED.
|
||||||
"""
|
"""
|
||||||
self.write(0x23, leds)
|
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 CIC 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.
|
||||||
|
|
||||||
|
This method approximates the desired interpolation rate using a 10 bit
|
||||||
|
floating point representation (6 bit mantissa, 4 bit exponent) and
|
||||||
|
then determines an 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 (but more
|
||||||
|
than 0.5 gain).
|
||||||
|
|
||||||
|
The overall gain including gain compensation is
|
||||||
|
`actual_rate**order/2**ceil(log2(actual_rate**order))`
|
||||||
|
where `order = 3`.
|
||||||
|
|
||||||
|
Returns the actual interpolation rate.
|
||||||
|
"""
|
||||||
|
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.
|
||||||
|
|
||||||
|
Each Fastino channel includes a fourth order (cubic) CIC interpolator with
|
||||||
|
variable rate change and variable output gain compensation (see
|
||||||
|
:meth:`stage_cic`).
|
||||||
|
|
||||||
|
Channels using non-unity interpolation rate should have
|
||||||
|
continous DAC updates enabled (see :meth:`set_continuous`) unless
|
||||||
|
their output is supposed to be constant.
|
||||||
|
|
||||||
|
This method resets and settles the affected interpolators. There will be
|
||||||
|
no output updates for the next `order = 3` input samples.
|
||||||
|
Affected channels will only accept one input sample per input sample
|
||||||
|
period. This method synchronizes the input sample period to the current
|
||||||
|
frame on the affected channels.
|
||||||
|
|
||||||
|
If application of new interpolator settings results in a change of the
|
||||||
|
overall gain, there will be a corresponding output step.
|
||||||
|
"""
|
||||||
|
self.write(0x27, channel_mask)
|
||||||
|
|
|
@ -27,16 +27,16 @@ class Fastino(Module):
|
||||||
|
|
||||||
# dac data words
|
# dac data words
|
||||||
dacs = [Signal(16) for i in range(32)]
|
dacs = [Signal(16) for i in range(32)]
|
||||||
|
|
||||||
header = Record([
|
header = Record([
|
||||||
("cfg", 4),
|
("cfg", 4),
|
||||||
("leds", 8),
|
("leds", 8),
|
||||||
("reserved", 8),
|
("typ", 1),
|
||||||
|
("reserved", 7),
|
||||||
("addr", 4),
|
("addr", 4),
|
||||||
("enable", len(dacs)),
|
("enable", len(dacs)),
|
||||||
])
|
])
|
||||||
body = Cat(header.raw_bits(), dacs)
|
assert len(Cat(header.raw_bits(), dacs)) == len(self.serializer.payload)
|
||||||
assert len(body) == len(self.serializer.payload)
|
|
||||||
self.comb += self.serializer.payload.eq(body)
|
|
||||||
|
|
||||||
# # #
|
# # #
|
||||||
|
|
||||||
|
@ -62,38 +62,61 @@ class Fastino(Module):
|
||||||
# address space is sparse.
|
# address space is sparse.
|
||||||
|
|
||||||
hold = Signal.like(header.enable)
|
hold = Signal.like(header.enable)
|
||||||
|
continuous = Signal.like(header.enable)
|
||||||
|
cic_config = Signal(16)
|
||||||
|
|
||||||
read_regs = Array([Signal.like(self.serializer.readback)
|
read_regs = Array([Signal.like(self.serializer.readback)
|
||||||
for _ in range(1 << len(header.addr))])
|
for _ in range(1 << len(header.addr))])
|
||||||
|
|
||||||
cases = {
|
cases = {
|
||||||
# update
|
# update
|
||||||
0x20: header.enable.eq(header.enable | self.rtlink.o.data),
|
0x20: [
|
||||||
|
header.enable.eq(self.rtlink.o.data),
|
||||||
|
header.typ.eq(0),
|
||||||
|
],
|
||||||
# hold
|
# hold
|
||||||
0x21: hold.eq(self.rtlink.o.data),
|
0x21: hold.eq(self.rtlink.o.data),
|
||||||
# cfg
|
# cfg
|
||||||
0x22: header.cfg.eq(self.rtlink.o.data),
|
0x22: header.cfg.eq(self.rtlink.o.data),
|
||||||
# leds
|
# leds
|
||||||
0x23: header.leds.eq(self.rtlink.o.data),
|
0x23: header.leds.eq(self.rtlink.o.data),
|
||||||
# reserved
|
# reserved bits
|
||||||
0x24: header.reserved.eq(self.rtlink.o.data),
|
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):
|
for i in range(0, len(dacs), width):
|
||||||
cases[i] = [
|
cases[i] = [
|
||||||
Cat(dacs[i:i + width]).eq(self.rtlink.o.data),
|
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),
|
header.enable[i + j].eq(1),
|
||||||
) for j in range(width)]
|
) 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 += [
|
self.sync.rio_phy += [
|
||||||
If(self.serializer.stb,
|
If(self.serializer.stb,
|
||||||
header.enable.eq(0),
|
header.typ.eq(0),
|
||||||
|
header.enable.eq(continuous),
|
||||||
read_regs[header.addr].eq(self.serializer.readback),
|
read_regs[header.addr].eq(self.serializer.readback),
|
||||||
header.addr.eq(header.addr + 1),
|
header.addr.eq(header.addr + 1),
|
||||||
),
|
),
|
||||||
If(self.rtlink.o.stb & ~self.rtlink.o.address[-1],
|
If(self.rtlink.o.stb,
|
||||||
Case(self.rtlink.o.address[:-1], cases),
|
Case(self.rtlink.o.address, cases),
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue