mirror of https://github.com/m-labs/artiq.git
sawg: clean up Config
* unify I and Q data limiters. there is no conceivable way why they would be different. * reorder clr bits to be like consistent * move the sat add limiter to before the hbf again
This commit is contained in:
parent
f4c6879c76
commit
4b3aad2563
|
@ -10,12 +10,10 @@ _SAWG_DIV = 0
|
||||||
_SAWG_CLR = 1
|
_SAWG_CLR = 1
|
||||||
_SAWG_IQ_EN = 2
|
_SAWG_IQ_EN = 2
|
||||||
# _SAWF_PAD = 3 # reserved
|
# _SAWF_PAD = 3 # reserved
|
||||||
_SAWG_DUC_I_MIN = 4
|
_SAWG_OUT_MIN = 4
|
||||||
_SAWG_DUC_I_MAX = 5
|
_SAWG_OUT_MAX = 5
|
||||||
_SAWG_DUC_Q_MIN = 6
|
_SAWG_DUC_MIN = 6
|
||||||
_SAWG_DUC_Q_MAX = 7
|
_SAWG_DUC_MAX = 7
|
||||||
_SAWG_OUT_MIN = 8
|
|
||||||
_SAWG_OUT_MAX = 9
|
|
||||||
|
|
||||||
|
|
||||||
class Config:
|
class Config:
|
||||||
|
@ -92,8 +90,8 @@ class Config:
|
||||||
:param clr2: Auto-clear phase accumulator of the ``phase2``/
|
:param clr2: Auto-clear phase accumulator of the ``phase2``/
|
||||||
``frequency2`` DDS. Default: ``True``
|
``frequency2`` DDS. Default: ``True``
|
||||||
"""
|
"""
|
||||||
rtio_output(now_mu(), self.channel, _SAWG_CLR, clr1 |
|
rtio_output(now_mu(), self.channel, _SAWG_CLR, clr0 |
|
||||||
(clr2 << 1) | (clr0 << 2))
|
(clr1 << 1) | (clr2 << 2))
|
||||||
|
|
||||||
@kernel
|
@kernel
|
||||||
def set_iq_en(self, i_enable: TInt32, q_enable: TInt32):
|
def set_iq_en(self, i_enable: TInt32, q_enable: TInt32):
|
||||||
|
@ -122,48 +120,38 @@ class Config:
|
||||||
(q_enable << 1))
|
(q_enable << 1))
|
||||||
|
|
||||||
@kernel
|
@kernel
|
||||||
def set_duc_i_max_mu(self, limit: TInt32):
|
def set_duc_max_mu(self, limit: TInt32):
|
||||||
"""Set the digital up-converter (DUC) I data summing junction upper
|
"""Set the digital up-converter (DUC) I and Q data summing junctions
|
||||||
limit. In machine units.
|
upper limit. In machine units.
|
||||||
|
|
||||||
The default limits are chosen to reach maximum and minimum DAC output
|
The default limits are chosen to reach maximum and minimum DAC output
|
||||||
amplitude.
|
amplitude.
|
||||||
|
|
||||||
For a description of the limiter functions in normalized units see:
|
For a description of the limiter functions in normalized units see:
|
||||||
|
|
||||||
.. seealso:: :meth:`set_duc_i_max`
|
.. seealso:: :meth:`set_duc_max`
|
||||||
"""
|
"""
|
||||||
rtio_output(now_mu(), self.channel, _SAWG_DUC_I_MAX, limit)
|
rtio_output(now_mu(), self.channel, _SAWG_DUC_MAX, limit)
|
||||||
|
|
||||||
@kernel
|
@kernel
|
||||||
def set_duc_i_min_mu(self, limit: TInt32):
|
def set_duc_min_mu(self, limit: TInt32):
|
||||||
""".. seealso:: :meth:`set_duc_i_max_mu`"""
|
""".. seealso:: :meth:`set_duc_max_mu`"""
|
||||||
rtio_output(now_mu(), self.channel, _SAWG_DUC_I_MIN, limit)
|
rtio_output(now_mu(), self.channel, _SAWG_DUC_MIN, limit)
|
||||||
|
|
||||||
@kernel
|
|
||||||
def set_duc_q_max_mu(self, limit: TInt32):
|
|
||||||
""".. seealso:: :meth:`set_duc_i_max_mu`"""
|
|
||||||
rtio_output(now_mu(), self.channel, _SAWG_DUC_Q_MAX, limit)
|
|
||||||
|
|
||||||
@kernel
|
|
||||||
def set_duc_q_min_mu(self, limit: TInt32):
|
|
||||||
""".. seealso:: :meth:`set_duc_i_max_mu`"""
|
|
||||||
rtio_output(now_mu(), self.channel, _SAWG_DUC_Q_MIN, limit)
|
|
||||||
|
|
||||||
@kernel
|
@kernel
|
||||||
def set_out_max_mu(self, limit: TInt32):
|
def set_out_max_mu(self, limit: TInt32):
|
||||||
""".. seealso:: :meth:`set_duc_i_max_mu`"""
|
""".. seealso:: :meth:`set_duc_max_mu`"""
|
||||||
rtio_output(now_mu(), self.channel, _SAWG_OUT_MAX, limit)
|
rtio_output(now_mu(), self.channel, _SAWG_OUT_MAX, limit)
|
||||||
|
|
||||||
@kernel
|
@kernel
|
||||||
def set_out_min_mu(self, limit: TInt32):
|
def set_out_min_mu(self, limit: TInt32):
|
||||||
""".. seealso:: :meth:`set_duc_i_max_mu`"""
|
""".. seealso:: :meth:`set_duc_max_mu`"""
|
||||||
rtio_output(now_mu(), self.channel, _SAWG_OUT_MIN, limit)
|
rtio_output(now_mu(), self.channel, _SAWG_OUT_MIN, limit)
|
||||||
|
|
||||||
@kernel
|
@kernel
|
||||||
def set_duc_i_max(self, limit: TFloat):
|
def set_duc_max(self, limit: TFloat):
|
||||||
"""Set the digital up-converter (DUC) I data summing junction upper
|
"""Set the digital up-converter (DUC) I and Q data summing junctions
|
||||||
limit.
|
upper limit.
|
||||||
|
|
||||||
Each of the three summing junctions has a saturating adder with
|
Each of the three summing junctions has a saturating adder with
|
||||||
configurable upper and lower limits. The three summing junctions are:
|
configurable upper and lower limits. The three summing junctions are:
|
||||||
|
@ -171,7 +159,8 @@ class Config:
|
||||||
* At the in-phase input to the ``phase0``/``frequency0`` fast DUC,
|
* At the in-phase input to the ``phase0``/``frequency0`` fast DUC,
|
||||||
after the anti-aliasing FIR filter.
|
after the anti-aliasing FIR filter.
|
||||||
* At the quadrature input to the ``phase0``/``frequency0``
|
* At the quadrature input to the ``phase0``/``frequency0``
|
||||||
fast DUC, after the anti-aliasing FIR filter.
|
fast DUC, after the anti-aliasing FIR filter. The in-phase and
|
||||||
|
quadrature data paths both use the same limits.
|
||||||
* Before the DAC, where the following three data streams
|
* Before the DAC, where the following three data streams
|
||||||
are added together:
|
are added together:
|
||||||
|
|
||||||
|
@ -190,42 +179,28 @@ class Config:
|
||||||
``[-1, 1]``.
|
``[-1, 1]``.
|
||||||
|
|
||||||
.. seealso::
|
.. seealso::
|
||||||
* :meth:`set_duc_i_max`: Upper limit of the in-phase input to
|
* :meth:`set_duc_max`: Upper limit of the in-phase and quadrature
|
||||||
the DUC.
|
inputs to the DUC.
|
||||||
* :meth:`set_duc_i_min`: Lower limit of the in-phase input to
|
* :meth:`set_duc_min`: Lower limit of the in-phase and quadrature
|
||||||
the DUC.
|
inputs to the DUC.
|
||||||
* :meth:`set_duc_q_max`: Upper limit of the quadrature input to
|
|
||||||
the DUC.
|
|
||||||
* :meth:`set_duc_q_min`: Lower limit of the quadrature input to
|
|
||||||
the DUC.
|
|
||||||
* :meth:`set_out_max`: Upper limit of the DAC output.
|
* :meth:`set_out_max`: Upper limit of the DAC output.
|
||||||
* :meth:`set_out_min`: Lower limit of the DAC output.
|
* :meth:`set_out_min`: Lower limit of the DAC output.
|
||||||
"""
|
"""
|
||||||
self.set_duc_i_max_mu(int32(round(limit*self._duc_scale)))
|
self.set_duc_max_mu(int32(round(limit*self._duc_scale)))
|
||||||
|
|
||||||
@kernel
|
@kernel
|
||||||
def set_duc_i_min(self, limit: TFloat):
|
def set_duc_min(self, limit: TFloat):
|
||||||
""".. seealso:: :meth:`set_duc_i_max`"""
|
""".. seealso:: :meth:`set_duc_max`"""
|
||||||
self.set_duc_i_min_mu(int32(round(limit*self._duc_scale)))
|
self.set_duc_min_mu(int32(round(limit*self._duc_scale)))
|
||||||
|
|
||||||
@kernel
|
|
||||||
def set_duc_q_max(self, limit: TFloat):
|
|
||||||
""".. seealso:: :meth:`set_duc_i_max`"""
|
|
||||||
self.set_duc_q_max_mu(int32(round(limit*self._duc_scale)))
|
|
||||||
|
|
||||||
@kernel
|
|
||||||
def set_duc_q_min(self, limit: TFloat):
|
|
||||||
""".. seealso:: :meth:`set_duc_i_max`"""
|
|
||||||
self.set_duc_q_min_mu(int32(round(limit*self._duc_scale)))
|
|
||||||
|
|
||||||
@kernel
|
@kernel
|
||||||
def set_out_max(self, limit: TFloat):
|
def set_out_max(self, limit: TFloat):
|
||||||
""".. seealso:: :meth:`set_duc_i_max`"""
|
""".. seealso:: :meth:`set_duc_max`"""
|
||||||
self.set_out_max_mu(int32(round(limit*self._out_scale)))
|
self.set_out_max_mu(int32(round(limit*self._out_scale)))
|
||||||
|
|
||||||
@kernel
|
@kernel
|
||||||
def set_out_min(self, limit: TFloat):
|
def set_out_min(self, limit: TFloat):
|
||||||
""".. seealso:: :meth:`set_duc_i_max`"""
|
""".. seealso:: :meth:`set_duc_max`"""
|
||||||
self.set_out_min_mu(int32(round(limit*self._out_scale)))
|
self.set_out_min_mu(int32(round(limit*self._out_scale)))
|
||||||
|
|
||||||
|
|
||||||
|
@ -306,6 +281,7 @@ class SAWG:
|
||||||
width = 16
|
width = 16
|
||||||
time_width = 16
|
time_width = 16
|
||||||
cordic_gain = 1.646760258057163 # Cordic(width=16, guard=None).gain
|
cordic_gain = 1.646760258057163 # Cordic(width=16, guard=None).gain
|
||||||
|
cordic_gain *= 1.01 # leave a bit of headroom
|
||||||
self.config = Config(channel_base, self.core, cordic_gain)
|
self.config = Config(channel_base, self.core, cordic_gain)
|
||||||
self.offset = Spline(width, time_width, channel_base + 1,
|
self.offset = Spline(width, time_width, channel_base + 1,
|
||||||
self.core, 2.)
|
self.core, 2.)
|
||||||
|
@ -335,7 +311,7 @@ class SAWG:
|
||||||
settings.
|
settings.
|
||||||
|
|
||||||
This method advances the timeline by the time required to perform all
|
This method advances the timeline by the time required to perform all
|
||||||
eight writes to the configuration channel.
|
six writes to the configuration channel.
|
||||||
"""
|
"""
|
||||||
self.frequency0.set_mu(0)
|
self.frequency0.set_mu(0)
|
||||||
self.frequency1.set_mu(0)
|
self.frequency1.set_mu(0)
|
||||||
|
@ -350,13 +326,9 @@ class SAWG:
|
||||||
delay_mu(self.config._rtio_interval)
|
delay_mu(self.config._rtio_interval)
|
||||||
self.config.set_iq_en(1, 0)
|
self.config.set_iq_en(1, 0)
|
||||||
delay_mu(self.config._rtio_interval)
|
delay_mu(self.config._rtio_interval)
|
||||||
self.config.set_duc_i_min(-1.)
|
self.config.set_duc_min(-1.)
|
||||||
delay_mu(self.config._rtio_interval)
|
delay_mu(self.config._rtio_interval)
|
||||||
self.config.set_duc_i_max(1.)
|
self.config.set_duc_max(1.)
|
||||||
delay_mu(self.config._rtio_interval)
|
|
||||||
self.config.set_duc_q_min(-1.)
|
|
||||||
delay_mu(self.config._rtio_interval)
|
|
||||||
self.config.set_duc_q_max(1.)
|
|
||||||
delay_mu(self.config._rtio_interval)
|
delay_mu(self.config._rtio_interval)
|
||||||
self.config.set_out_min(-1.)
|
self.config.set_out_min(-1.)
|
||||||
delay_mu(self.config._rtio_interval)
|
delay_mu(self.config._rtio_interval)
|
||||||
|
|
|
@ -83,35 +83,43 @@ class SplineParallelDDS(SplineParallelDUC):
|
||||||
|
|
||||||
|
|
||||||
class Config(Module):
|
class Config(Module):
|
||||||
def __init__(self, width):
|
def __init__(self, width, cordic_gain):
|
||||||
self.clr = Signal(3, reset=0b111)
|
self.clr = Signal(3, reset=0b111)
|
||||||
self.iq_en = Signal(2, reset=0b01)
|
self.iq_en = Signal(2, reset=0b01)
|
||||||
self.limits = [[Signal((width, True), reset=-(1 << width - 1)),
|
self.limits = [[Signal((width, True)), Signal((width, True))]
|
||||||
Signal((width, True), reset=(1 << width - 1) - 1)]
|
for i in range(2)]
|
||||||
for i in range(3)]
|
limit = 1 << width - 1
|
||||||
self.clipped = [Signal(2) for i in range(3)] # TODO
|
self.limits[0][0].reset = -limit
|
||||||
self.i = Endpoint([("addr", bits_for(4 + 2*len(self.limits))),
|
self.limits[0][1].reset = limit - 1
|
||||||
("data", 16)])
|
self.limits[1][0].reset = int(-limit/cordic_gain)
|
||||||
|
self.limits[1][1].reset = int((limit - 1)/cordic_gain)
|
||||||
|
# TODO make persistent, add read-out/notification/clear
|
||||||
|
self.clipped = [Signal(2) for i in range(2)]
|
||||||
|
self.i = Endpoint([("addr", bits_for(4 + 2*len(self.limits) - 1)),
|
||||||
|
("data", width)])
|
||||||
|
assert len(self.i.addr) == 3
|
||||||
self.ce = Signal()
|
self.ce = Signal()
|
||||||
|
|
||||||
###
|
###
|
||||||
|
|
||||||
div = Signal(16, reset=0)
|
div = Signal(16, reset=0)
|
||||||
n = Signal.like(div)
|
n = Signal.like(div)
|
||||||
pad = Signal()
|
|
||||||
|
|
||||||
reg = Array([Cat(div, n), self.clr, self.iq_en, pad] +
|
self.comb += self.ce.eq(n == 0)
|
||||||
sum(self.limits, []))
|
|
||||||
|
|
||||||
self.comb += [
|
|
||||||
self.i.ack.eq(1),
|
|
||||||
self.ce.eq(n == 0),
|
|
||||||
]
|
|
||||||
self.sync += [
|
self.sync += [
|
||||||
n.eq(n - 1),
|
n.eq(n - 1),
|
||||||
If(self.ce,
|
If(self.ce,
|
||||||
n.eq(div),
|
n.eq(div),
|
||||||
),
|
)
|
||||||
|
]
|
||||||
|
|
||||||
|
pad = Signal()
|
||||||
|
|
||||||
|
reg = Array(sum(self.limits,
|
||||||
|
[Cat(div, n), self.clr, self.iq_en, pad]))
|
||||||
|
|
||||||
|
self.comb += self.i.ack.eq(1)
|
||||||
|
self.sync += [
|
||||||
If(self.i.stb,
|
If(self.i.stb,
|
||||||
reg[self.i.addr].eq(self.i.data),
|
reg[self.i.addr].eq(self.i.data),
|
||||||
),
|
),
|
||||||
|
@ -131,26 +139,26 @@ class Channel(Module, SatAddMixin):
|
||||||
coeff = halfgen4_cascade(parallelism, width=.4, order=8)
|
coeff = halfgen4_cascade(parallelism, width=.4, order=8)
|
||||||
hbf = [ParallelHBFUpsampler(coeff, width=width + 1) for i in range(2)]
|
hbf = [ParallelHBFUpsampler(coeff, width=width + 1) for i in range(2)]
|
||||||
self.submodules.b = b = SplineParallelDUC(
|
self.submodules.b = b = SplineParallelDUC(
|
||||||
widths._replace(a=len(hbf[0].o[0]), f=widths.f - width), orders,
|
widths._replace(a=width + 1, f=widths.f - width), orders,
|
||||||
parallelism=parallelism)
|
parallelism=parallelism)
|
||||||
cfg = Config(width)
|
cfg = Config(width, b.gain)
|
||||||
u = Spline(width=widths.a, order=orders.a)
|
u = Spline(width=widths.a, order=orders.a)
|
||||||
self.submodules += cfg, u, hbf
|
self.submodules += cfg, u, hbf
|
||||||
self.u = u.tri(widths.t)
|
self.u = u.tri(widths.t)
|
||||||
self.i = [cfg.i, self.u, a1.a, a1.f, a1.p, a2.a, a2.f, a2.p, b.f, b.p]
|
self.i = [cfg.i, self.u, a1.a, a1.f, a1.p, a2.a, a2.f, a2.p, b.f, b.p]
|
||||||
self.i_names = "cfg u a1 f1 p1 a2 f2 p2 f0 p0".split()
|
self.i_names = "cfg u a1 f1 p1 a2 f2 p2 f0 p0".split()
|
||||||
self.i_named = dict(zip(self.i_names, self.i))
|
self.i_named = dict(zip(self.i_names, self.i))
|
||||||
self.y_in = [Signal((width, True)) for i in range(parallelism)]
|
self.y_in = [Signal.like(b.yo[0]) for i in range(parallelism)]
|
||||||
self.o = [Signal((width, True)) for i in range(parallelism)]
|
self.o = [Signal((width, True)) for i in range(parallelism)]
|
||||||
self.widths = widths
|
self.widths = widths
|
||||||
self.orders = orders
|
self.orders = orders
|
||||||
self.parallelism = parallelism
|
self.parallelism = parallelism
|
||||||
self.cordic_gain = a1.gain*b.gain
|
self.cordic_gain = a2.gain*b.gain
|
||||||
|
|
||||||
self.u.latency += 1
|
self.u.latency += 1
|
||||||
b.p.latency += 2
|
b.p.latency += 2
|
||||||
b.f.latency += 2
|
b.f.latency += 2
|
||||||
a_latency_delta = hbf[0].latency + b.latency + 3
|
a_latency_delta = hbf[0].latency + b.latency + 2
|
||||||
for a in a1, a2:
|
for a in a1, a2:
|
||||||
a.a.latency += a_latency_delta
|
a.a.latency += a_latency_delta
|
||||||
a.p.latency += a_latency_delta
|
a.p.latency += a_latency_delta
|
||||||
|
@ -169,25 +177,18 @@ class Channel(Module, SatAddMixin):
|
||||||
a2.ce.eq(cfg.ce),
|
a2.ce.eq(cfg.ce),
|
||||||
b.ce.eq(cfg.ce),
|
b.ce.eq(cfg.ce),
|
||||||
u.o.ack.eq(cfg.ce),
|
u.o.ack.eq(cfg.ce),
|
||||||
Cat(a1.clr, a2.clr, b.clr).eq(cfg.clr),
|
Cat(b.clr, a1.clr, a2.clr).eq(cfg.clr),
|
||||||
|
]
|
||||||
|
self.sync += [
|
||||||
|
hbf[0].i.eq(self.sat_add(a1.xo[0], a2.xo[0],
|
||||||
|
limits=cfg.limits[1], clipped=cfg.clipped[1])),
|
||||||
|
hbf[1].i.eq(self.sat_add(a1.yo[0], a2.yo[0],
|
||||||
|
limits=cfg.limits[1], clipped=None)), # ignore Q clip data
|
||||||
]
|
]
|
||||||
for i in range(parallelism):
|
for i in range(parallelism):
|
||||||
self.sync += [
|
self.comb += [
|
||||||
b.xi[i].eq(self.sat_add(hbf[0].o[i],
|
b.xi[i].eq(hbf[0].o[i]),
|
||||||
limits=cfg.limits[0],
|
b.yi[i].eq(hbf[1].o[i]),
|
||||||
clipped=cfg.clipped[0])),
|
|
||||||
b.yi[i].eq(self.sat_add(hbf[1].o[i],
|
|
||||||
limits=cfg.limits[1],
|
|
||||||
clipped=cfg.clipped[1])),
|
|
||||||
]
|
|
||||||
for i in range(2):
|
|
||||||
for j in range(2):
|
|
||||||
# correct pre-DUC limiter by cordic gain
|
|
||||||
v = cfg.limits[i][j].reset.value
|
|
||||||
cfg.limits[i][j].reset.value = int(v / b.gain)
|
|
||||||
self.sync += [
|
|
||||||
hbf[0].i.eq(a1.xo[0] + a2.xo[0]),
|
|
||||||
hbf[1].i.eq(a1.yo[0] + a2.yo[0])
|
|
||||||
]
|
]
|
||||||
# wire up outputs and q_{i,o} exchange
|
# wire up outputs and q_{i,o} exchange
|
||||||
for o, x, y in zip(self.o, b.xo, self.y_in):
|
for o, x, y in zip(self.o, b.xo, self.y_in):
|
||||||
|
@ -196,8 +197,8 @@ class Channel(Module, SatAddMixin):
|
||||||
u.o.a0[-len(o):],
|
u.o.a0[-len(o):],
|
||||||
Mux(cfg.iq_en[0], x, 0),
|
Mux(cfg.iq_en[0], x, 0),
|
||||||
Mux(cfg.iq_en[1], y, 0),
|
Mux(cfg.iq_en[1], y, 0),
|
||||||
limits=cfg.limits[2],
|
limits=cfg.limits[0],
|
||||||
clipped=cfg.clipped[2])),
|
clipped=cfg.clipped[0])),
|
||||||
]
|
]
|
||||||
|
|
||||||
def connect_y(self, buddy):
|
def connect_y(self, buddy):
|
||||||
|
|
Loading…
Reference in New Issue