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:
Robert Jördens 2017-06-21 14:30:44 +02:00
parent f4c6879c76
commit 4b3aad2563
2 changed files with 76 additions and 103 deletions

View File

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

View File

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