diff --git a/artiq/gateware/rtio/phy/ttl_serdes_7series.py b/artiq/gateware/rtio/phy/ttl_serdes_7series.py index 57f3f4272..ccb32ab5e 100644 --- a/artiq/gateware/rtio/phy/ttl_serdes_7series.py +++ b/artiq/gateware/rtio/phy/ttl_serdes_7series.py @@ -1,58 +1,70 @@ from migen.fhdl.std import * + from artiq.gateware.rtio.phy import ttl_serdes_generic -class OSerdese2(Module): +class _OSERDESE2_8X(Module): def __init__(self, pad): - self.o = o = Signal(8) - self.oe = oe = Signal() - self.t = t = Signal() + self.o = Signal(8) + self.t_in = Signal() + self.t_out = Signal() + # # # + + o = self.o self.specials += Instance("OSERDESE2", p_DATA_RATE_OQ="DDR", p_DATA_RATE_TQ="DDR", p_DATA_WIDTH=8, - p_TRISTATE_WIDTH=1, o_OQ=pad, o_TQ=t, + p_TRISTATE_WIDTH=1, + o_OQ=pad, o_TQ=self.t_out, i_CLK=ClockSignal("rtiox4"), i_CLKDIV=ClockSignal("rio_phy"), i_D1=o[0], i_D2=o[1], i_D3=o[2], i_D4=o[3], i_D5=o[4], i_D6=o[5], i_D7=o[6], i_D8=o[7], - i_TCE=1, i_OCE=1, i_RST=ResetSignal(), - i_T1=~oe) + i_TCE=1, i_OCE=1, i_RST=0, + i_T1=self.t_in) -class IOSerdese2(Module): +class _IOSERDESE2_8X(Module): def __init__(self, pad): - ts = TSTriple() - self.o = o = Signal(8) - self.oe = oe = Signal() - self.i = i = Signal(8) - self.specials += ts.get_tristate(pad) + self.o = Signal(8) + self.i = Signal(8) + self.oe = Signal() + # # # + + pad_i = Signal() + pad_o = Signal() + i = self.i self.specials += Instance("ISERDESE2", p_DATA_RATE="DDR", p_DATA_WIDTH=8, p_INTERFACE_TYPE="NETWORKING", p_NUM_CE=1, o_Q1=i[7], o_Q2=i[6], o_Q3=i[5], o_Q4=i[4], o_Q5=i[3], o_Q6=i[2], o_Q7=i[1], o_Q8=i[0], - i_D=ts.i, i_CLK=ClockSignal("rtiox4"), - i_CE1=1, i_RST=ResetSignal(), + i_D=pad_i, + i_CLK=ClockSignal("rtiox4"), + i_CLKB=~ClockSignal("rtiox4"), + i_CE1=1, i_RST=0, i_CLKDIV=ClockSignal("rio_phy")) - - oserdes = OSerdese2(ts.o) + oserdes = _OSERDESE2_8X(pad_o) self.submodules += oserdes - + self.specials += Instance("IOBUF", + i_I=pad_o, o_O=pad_i, i_T=oserdes.t_out, + io_IO=pad) self.comb += [ - ts.oe.eq(~oserdes.t), - oserdes.o.eq(o), - oserdes.oe.eq(oe) + oserdes.t_in.eq(~self.oe), + oserdes.o.eq(self.o) ] -class Output(Module): +class Output_8X(ttl_serdes_generic.Output): def __init__(self, pad): - serdes = OSerdese2(pad) - self.submodules += ttl_serdes_generic.Output(serdes, fine_ts_width=3) + serdes = _OSERDESE2_8X(pad) + self.submodules += serdes + ttl_serdes_generic.Output.__init__(self, serdes) -class Inout(Module): +class Inout_8X(ttl_serdes_generic.Inout): def __init__(self, pad): - serdes = IOSerdese2(pad) - self.submodules += ttl_serdes_generic.InOut(serdes, fine_ts_width=3) + serdes = _IOSERDESE2_8X(pad) + self.submodules += serdes + ttl_serdes_generic.Inout.__init__(self, serdes) diff --git a/artiq/gateware/rtio/phy/ttl_serdes_generic.py b/artiq/gateware/rtio/phy/ttl_serdes_generic.py index 56e17c7e1..2ba010226 100644 --- a/artiq/gateware/rtio/phy/ttl_serdes_generic.py +++ b/artiq/gateware/rtio/phy/ttl_serdes_generic.py @@ -1,304 +1,273 @@ from migen.fhdl.std import * -from artiq.gateware.rtio import rtlink from migen.genlib.coding import PriorityEncoder +from artiq.gateware.rtio import rtlink -class Output(Module): - def __init__(self, serdes, fine_ts_width=0): - self.rtlink = rtlink.Interface(rtlink.OInterface(1, fine_ts_width= - fine_ts_width)) - serdes_width = 2**fine_ts_width - o = Signal() - previous_o = Signal() - override_en = Signal() - override_o = Signal() - io_o = Signal() - self.overrides = [override_en, override_o] +def _mk_edges(w, direction): + l = [(1 << i) - 1 for i in range(w)] + if direction == "rising": + l = [2**w - 1 ^ x for x in l] + elif direction == "falling": + pass + else: + raise ValueError + return l - io = serdes - self.submodules += io - - if fine_ts_width > 0: - timestamp = Signal(fine_ts_width) - - # dout - edges = Array([0xff ^ ((1 << i) - 1) for i in range(serdes_width)]) - edge_out = Signal(serdes_width) - edge_out_n = Signal(serdes_width) - rise_out = Signal() - fall_out = Signal() - self.comb += [ - timestamp.eq(self.rtlink.o.fine_ts), - edge_out.eq(edges[timestamp]), - edge_out_n.eq(~edge_out), - rise_out.eq(~previous_o & o), - fall_out.eq(previous_o & ~o), - If(override_en, - io.o.eq(override_o) - ).Else( - If(rise_out, - io.o.eq(edge_out), - ).Elif(fall_out, - io.o.eq(edge_out_n), - ).Else( - io.o.eq(Replicate(o, serdes_width)), - ) - ) - ] - else: - self.comb += [ - If(override_en, - io_o.eq(override_o) - ).Else( - io_o.eq(o) - ) - ] - - self.comb += [ - io.o.eq(io_o), - ] +class _SerdesDriver(Module): + def __init__(self, serdes_o, stb, data, fine_ts, override_en, override_o): + previous_data = Signal() + serdes_width = flen(serdes_o) + edges = Array(_mk_edges(serdes_width, "rising")) + edges_n = Array(_mk_edges(serdes_width, "falling")) self.sync.rio_phy += [ - If(self.rtlink.o.stb, - o.eq(self.rtlink.o.data), - ), - previous_o.eq(o), - ] - - -class Inout(Module): - def __init__(self, serdes, fine_ts_width=0): - self.rtlink = rtlink.Interface( - rtlink.OInterface(2, 2, fine_ts_width=fine_ts_width), - rtlink.IInterface(1, fine_ts_width=fine_ts_width)) - self.probes = [] - - serdes_width = 2**fine_ts_width - self.io = io = serdes - self.submodules += io - io_o = Signal(serdes_width) - io_i = Signal(serdes_width) - o = Signal() - rising = Signal() - falling = Signal() - i0 = Signal() - self.oe = oe = Signal() - override_en = Signal() - override_o = Signal() - override_oe = Signal() - self.sensitivity = Signal(2) - self.overrides = [override_en, override_o, override_oe] - previous_o = Signal() - - if fine_ts_width > 0: - - # Input - self.submodules.pe = pe = PriorityEncoder(serdes_width) - - self.sync.rio_phy += i0.eq(io_i[-1]) - - self.comb += [ - io_i.eq(io.i), - rising.eq(~i0 & io_i[-1]), - falling.eq(i0 & ~io_i[-1]), - pe.i.eq(io_i ^ Replicate(falling, serdes_width)), - self.rtlink.i.data.eq(io_i[-1]), - self.rtlink.i.fine_ts.eq(pe.o), - ] - - # Output - timestamp = Signal(fine_ts_width) - edges = Array([0xff ^ ((1 << i) - 1) for i in range(serdes_width)]) - edge_out = Signal(serdes_width) - edge_out_n = Signal(serdes_width) - rise_out = Signal() - fall_out = Signal() - - self.comb += [ - timestamp.eq(self.rtlink.o.fine_ts), - edge_out.eq(edges[timestamp]), - edge_out_n.eq(~edge_out), - rise_out.eq(~previous_o & o), - fall_out.eq(previous_o & ~o), - If(override_en, - io_o.eq(override_o), - ).Else( - If(rise_out, - io_o.eq(edge_out), - ).Elif(fall_out, - io_o.eq(edge_out_n), - ).Else( - io_o.eq(Replicate(o, serdes_width)), - ) - ) - ] - else: - self.comb += [ - io_i.eq(io.i), - rising.eq(~i0 & io_i), - falling.eq(i0 & ~io_i), - If(override_en, - io_o.eq(override_o) - ).Else( - io_o.eq(o), - ), - self.rtlink.i.data.eq(io_i), - ] - - self.comb += [ - io.oe.eq(oe), - io.o.eq(io_o), - self.rtlink.i.stb.eq( - (self.sensitivity[0] & rising) | - (self.sensitivity[1] & falling) - ), - ] - - self.sync.rio_phy += [ - If(self.rtlink.o.stb, - If(self.rtlink.o.address == 0, o.eq(self.rtlink.o.data[0])), - If(self.rtlink.o.address == 1, oe.eq(self.rtlink.o.data[0])), - ), + If(stb, previous_data.eq(data)), If(override_en, - oe.eq(override_oe) - ), - previous_o.eq(o), - ] - - self.sync.rio += [ - If(self.rtlink.o.stb & (self.rtlink.o.address == 2), - self.sensitivity.eq(self.rtlink.o.data) + 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 FakeSerdes(Module): + +class Output(Module): + def __init__(self, serdes): + self.rtlink = rtlink.Interface( + rtlink.OInterface(1, fine_ts_width=log2_int(flen(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 = flen(serdes.o) + assert flen(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 + 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.sync.rio_phy += [ + If(self.rtlink.o.stb & (self.rtlink.o.address == 1), + oe_k.eq(self.rtlink.o.data[0])), + If(override_en, + serdes.oe.eq(override_oe) + ).Else( + serdes.oe.eq(oe_k) + ) + ] + + # Input + sensitivity = Signal(2) + self.sync.rio += If(self.rtlink.o.stb & (self.rtlink.o.address == 2), + sensitivity.eq(self.rtlink.o.data)) + + i = serdes.i[-1] + i_d = Signal() + self.sync.rio_phy += [ + i_d.eq(i), + self.rtlink.i.stb.eq( + (sensitivity[0] & ( i & ~i_d)) | + (sensitivity[1] & (~i & i_d)) + ), + self.rtlink.i.data.eq(i), + ] + + pe = PriorityEncoder(serdes_width) + self.submodules += pe + self.comb += pe.i.eq(serdes.i ^ Replicate(i_d, serdes_width)) + self.sync.rio_phy += self.rtlink.i.fine_ts.eq(pe.o) + + +class _FakeSerdes(Module): def __init__(self): - self.o = o = Signal(8) - self.oe = oe = Signal(8) + self.o = Signal(8) + self.i = Signal(8) + self.oe = Signal() -class FakeIOSerdes(Module): +class _OutputTB(Module): def __init__(self): - self.o = o = Signal(8) - self.oe = oe = Signal(8) - self.i = i = Signal(8) - - -class OutputTB(Module): - def __init__(self): - serdes = FakeSerdes() - self.o = RenameClockDomains(Output(serdes, fine_ts_width=3), - {"rio_phy": "sys"}) - self.submodules += self.o + serdes = _FakeSerdes() + self.submodules.dut = RenameClockDomains(Output(serdes), + {"rio_phy": "sys"}) def gen_simulation(self, selfp): - + selfp.dut.rtlink.o.data = 1 + selfp.dut.rtlink.o.fine_ts = 1 + selfp.dut.rtlink.o.stb = 1 yield - selfp.o.rtlink.o.data = 1 - selfp.o.rtlink.o.fine_ts = 1 - selfp.o.rtlink.o.stb = 1 + selfp.dut.rtlink.o.stb = 0 yield - selfp.o.rtlink.o.stb = 0 + selfp.dut.rtlink.o.data = 0 + selfp.dut.rtlink.o.fine_ts = 2 + selfp.dut.rtlink.o.stb = 1 yield - selfp.o.rtlink.o.data = 0 - selfp.o.rtlink.o.fine_ts = 2 - selfp.o.rtlink.o.stb = 1 yield - selfp.o.rtlink.o.data = 1 - selfp.o.rtlink.o.fine_ts = 7 - yield - - while True: + selfp.dut.rtlink.o.data = 1 + selfp.dut.rtlink.o.fine_ts = 7 + selfp.dut.rtlink.o.stb = 1 + for _ in range(6): + # note that stb stays active; output should not change yield -class InoutTB(Module): +class _InoutTB(Module): def __init__(self): - ioserdes = FakeIOSerdes() - self.io = RenameClockDomains(Inout(ioserdes, fine_ts_width=3), - {"rio_phy": "sys"}) - self.submodules += self.io + self.serdes = _FakeSerdes() + self.submodules.dut = RenameClockDomains(Inout(self.serdes), + {"rio_phy": "sys", + "rio": "sys"}) def check_input(self, selfp, stb, fine_ts=None): - if stb != selfp.io.rtlink.i.stb: + if stb != selfp.dut.rtlink.i.stb: print("KO rtlink.i.stb should be {} but is {}" - .format(stb, selfp.io.rtlink.i.stb)) - elif fine_ts is not None and fine_ts != selfp.io.rtlink.i.fine_ts: + .format(stb, selfp.dut.rtlink.i.stb)) + elif fine_ts is not None and fine_ts != selfp.dut.rtlink.i.fine_ts: print("KO rtlink.i.fine_ts should be {} but is {}" - .format(fine_ts, selfp.io.rtlink.i.fine_ts)) + .format(fine_ts, selfp.dut.rtlink.i.fine_ts)) else: print("OK") def check_output(self, selfp, data): - if selfp.io.io.o != data: - print("KO io.o should be {} but is {}".format(data, selfp.io.io.o)) + if selfp.serdes.o != data: + print("KO io.o should be {} but is {}".format(data, selfp.serdes.o)) else: print("OK") def check_output_enable(self, selfp, oe): - if selfp.io.io.oe != oe: - print("KO io.oe should be {} but is {}".format(oe, selfp.io.io.oe)) + if selfp.serdes.oe != oe: + print("KO io.oe should be {} but is {}".format(oe, selfp.serdes.oe)) else: print("OK") def gen_simulation(self, selfp): - selfp.io.sensitivity = 0b11 # rising + falling + selfp.dut.rtlink.o.address = 2 + selfp.dut.rtlink.o.data = 0b11 + selfp.dut.rtlink.o.stb = 1 # set sensitivity to rising + falling + yield + selfp.dut.rtlink.o.stb = 0 + self.check_output_enable(selfp, 0) yield - selfp.io.io.i = 0b11111110 # rising edge at fine_ts = 1 + + selfp.serdes.i = 0b11111110 # rising edge at fine_ts = 1 + yield + selfp.serdes.i = 0b11111111 yield self.check_input(selfp, stb=1, fine_ts=1) - selfp.io.io.i = 0b01111111 # falling edge at fine_ts = 7 + + selfp.serdes.i = 0b01111111 # falling edge at fine_ts = 7 + yield + selfp.serdes.i = 0b00000000 yield self.check_input(selfp, stb=1, fine_ts=7) - selfp.io.io.i = 0b11000000 # rising edge at fine_ts = 6 + + selfp.serdes.i = 0b11000000 # rising edge at fine_ts = 6 + yield + selfp.serdes.i = 0b11111111 yield self.check_input(selfp, stb=1, fine_ts=6) - selfp.io.sensitivity = 0b01 # rising - selfp.io.io.i = 0b00001111 # falling edge at fine_ts = 4 + + selfp.dut.rtlink.o.address = 2 + selfp.dut.rtlink.o.data = 0b11 + selfp.dut.rtlink.o.stb = 1 # set sensitivity to rising only + yield + selfp.dut.rtlink.o.stb = 0 + yield + + selfp.serdes.i = 0b00001111 # falling edge at fine_ts = 4 yield self.check_input(selfp, stb=0) # no strobe, sensitivity is rising edge - selfp.io.io.i = 0b11110000 # rising edge at fine_ts = 4 + + selfp.serdes.i = 0b11110000 # rising edge at fine_ts = 4 yield self.check_input(selfp, stb=1, fine_ts=4) - selfp.io.rtlink.o.address = 1 - selfp.io.rtlink.o.data = 1 - selfp.io.rtlink.o.stb = 1 # set Output Enable to 1 + + selfp.dut.rtlink.o.address = 1 + selfp.dut.rtlink.o.data = 1 + selfp.dut.rtlink.o.stb = 1 # set Output Enable to 1 + yield + selfp.dut.rtlink.o.stb = 0 yield - selfp.io.rtlink.o.address = 0 - selfp.io.rtlink.o.data = 1 - selfp.io.rtlink.o.fine_ts = 3 # rising edge at fine_ts = 3 yield self.check_output_enable(selfp, 1) + + selfp.dut.rtlink.o.address = 0 + selfp.dut.rtlink.o.data = 1 + selfp.dut.rtlink.o.fine_ts = 3 + selfp.dut.rtlink.o.stb = 1 # rising edge at fine_ts = 3 + yield + selfp.dut.rtlink.o.stb = 0 yield - selfp.io.rtlink.o.data = 0 - selfp.io.rtlink.o.fine_ts = 0 # falling edge at fine_ts = 0 self.check_output(selfp, data=0b11111000) + yield self.check_output(selfp, data=0xFF) # stays at 1 + + selfp.dut.rtlink.o.data = 0 + selfp.dut.rtlink.o.fine_ts = 0 + selfp.dut.rtlink.o.stb = 1 # falling edge at fine_ts = 0 yield - selfp.io.rtlink.o.data = 1 - selfp.io.rtlink.o.fine_ts = 7 - self.check_output(selfp, data=0) + selfp.dut.rtlink.o.stb = 0 yield self.check_output(selfp, data=0) + + yield + self.check_output(selfp, data=0) + + selfp.dut.rtlink.o.data = 1 + selfp.dut.rtlink.o.fine_ts = 7 + selfp.dut.rtlink.o.stb = 1 # rising edge at fine_ts = 7 + yield + selfp.dut.rtlink.o.stb = 0 yield self.check_output(selfp, data=0b10000000) - while True: - yield if __name__ == "__main__": import sys from migen.sim.generic import Simulator, TopLevel - from migen.sim import icarus - if len(sys.argv) <= 1: - print("You should run this script with either InoutTB() or OutputTB() " - "arg") + if len(sys.argv) != 2: + print("Incorrect command line") sys.exit(1) - with Simulator(eval(sys.argv[1]), TopLevel("top.vcd", clk_period=int(1/0.125)), - icarus.Runner(keep_files=False,)) as s: - s.run(200) + cls = { + "output": _OutputTB, + "inout": _InoutTB + }[sys.argv[1]] + + with Simulator(cls(), TopLevel("top.vcd", clk_period=int(1/0.125))) as s: + s.run()