mirror of
https://github.com/m-labs/artiq.git
synced 2025-01-10 19:13:34 +08:00
Robert Jördens
ea79ba4622
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).
128 lines
4.0 KiB
Python
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),
|
|
]
|