2
0
mirror of https://github.com/m-labs/artiq.git synced 2025-01-10 19:13:34 +08:00
artiq/artiq/gateware/rtio/phy/ttl_serdes_generic.py
Robert Jördens ea79ba4622 ttl_serdes: detect edges on short pulses
Edges on pulses shorter than the RTIO period were missed because the
reference sample and the last sample of the serdes word are the same.

This change enables detection of edges on pulses as short as the
serdes UI (and shorter as long as the pulse still hits a serdes sample
aperture).

In any RTIO period, only the leading event corresponding to the first
edge with slope according to sensitivity is registerd. If the channel is
sensitive to both rising and falling edges and if the pulse is contained
within an RTIO period, or if it is sensitive only to one edge slope and
there are multiple pulses in an RTIO period, only the leading event is
seen. Thus this possibility of lost events is still there. Only the
conditions under which loss occurs are reduced.

In testing with the kasli-ptb6 variant, this also improves resource
usage (a couple hundred LUT) and timing (0.1 ns WNS).
2020-04-13 13:21:03 +02:00

128 lines
4.0 KiB
Python

from migen import *
from migen.genlib.coding import PriorityEncoder
from artiq.gateware.rtio import rtlink
def _mk_edges(w, direction):
l = [(1 << i) - 1 for i in range(w)]
if direction == "rising":
l = [((1 << w) - 1) ^ x for x in l]
elif direction == "falling":
pass
else:
raise ValueError
return l
class _SerdesDriver(Module):
def __init__(self, serdes_o, stb, data, fine_ts, override_en, override_o):
previous_data = Signal()
serdes_width = len(serdes_o)
edges = Array(_mk_edges(serdes_width, "rising"))
edges_n = Array(_mk_edges(serdes_width, "falling"))
self.sync.rio_phy += [
If(stb, previous_data.eq(data)),
If(override_en,
serdes_o.eq(Replicate(override_o, serdes_width))
).Else(
If(stb & ~previous_data & data,
serdes_o.eq(edges[fine_ts]),
).Elif(stb & previous_data & ~data,
serdes_o.eq(edges_n[fine_ts]),
).Else(
serdes_o.eq(Replicate(previous_data, serdes_width)),
)
)
]
class Output(Module):
def __init__(self, serdes):
self.rtlink = rtlink.Interface(
rtlink.OInterface(1, fine_ts_width=log2_int(len(serdes.o))))
self.probes = [serdes.o[-1]]
override_en = Signal()
override_o = Signal()
self.overrides = [override_en, override_o]
# # #
self.submodules += _SerdesDriver(
serdes.o,
self.rtlink.o.stb, self.rtlink.o.data, self.rtlink.o.fine_ts,
override_en, override_o)
class InOut(Module):
def __init__(self, serdes):
serdes_width = len(serdes.o)
assert len(serdes.i) == serdes_width
self.rtlink = rtlink.Interface(
rtlink.OInterface(2, 2, fine_ts_width=log2_int(serdes_width)),
rtlink.IInterface(1, fine_ts_width=log2_int(serdes_width)))
self.probes = [serdes.i[-1], serdes.oe]
override_en = Signal()
override_o = Signal()
override_oe = Signal()
self.overrides = [override_en, override_o, override_oe]
# Output enable, for interfacing to external buffers.
self.oe = Signal()
# input state exposed for edge_counter: latest serdes sample
# support for short pulses will need a more involved solution
self.input_state = Signal()
# # #
# Output
self.submodules += _SerdesDriver(
serdes_o=serdes.o,
stb=self.rtlink.o.stb & (self.rtlink.o.address == 0),
data=self.rtlink.o.data[0],
fine_ts=self.rtlink.o.fine_ts,
override_en=override_en, override_o=override_o)
oe_k = Signal()
self.oe.attr.add("no_retiming")
self.sync.rio_phy += [
If(self.rtlink.o.stb & (self.rtlink.o.address == 1),
oe_k.eq(self.rtlink.o.data[0])),
If(override_en,
self.oe.eq(override_oe)
).Else(
self.oe.eq(oe_k)
)
]
self.comb += serdes.oe.eq(self.oe)
# Input
sensitivity = Signal(2)
sample = Signal()
self.sync.rio += [
sample.eq(0),
If(self.rtlink.o.stb & self.rtlink.o.address[1],
sensitivity.eq(self.rtlink.o.data),
If(self.rtlink.o.address[0], sample.eq(1))
)
]
i = serdes.i[-1]
self.comb += self.input_state.eq(i)
i_d = Signal()
self.sync.rio_phy += [
i_d.eq(i),
self.rtlink.i.data.eq(i),
]
pe = PriorityEncoder(serdes_width)
self.submodules += pe
self.comb += pe.i.eq(
(serdes.i ^ Cat(i_d, serdes.i)) & (
(serdes.i & Replicate(sensitivity[0], serdes_width)) |
(~serdes.i & Replicate(sensitivity[1], serdes_width))))
self.sync.rio_phy += [
self.rtlink.i.fine_ts.eq(pe.o),
self.rtlink.i.stb.eq(sample | ~pe.n),
]