Пример #1
0
def test_upscaler():
    i = axi.Interface()
    dw = i.data_width
    dut = stream.Converter(8, dw)
    source, sink = dut.source, dut.sink
    write = partial(write_data, sink)
    read = partial(read_data, source)

    def testbench_upscaler():
        def push():
            yield from write(0x11)
            yield from write(0x22)
            yield from write(0x33)
            yield from write(0x44)
            yield from write(0x55)
            yield from write(0x66)
            yield from write(0x77)
            yield from write(0x88)
            yield from write(0x99, eop=1)

        def pull():
            yield source.ack.eq(1)
            assert (yield from read()) == 0x44332211
            yield
            assert (yield from read()) == 0x88776655
            yield
            assert (yield from read()) & 0xff == 0x99

        return [
            push(),
            pull(),
        ]

    run_simulation(dut, testbench_upscaler())
Пример #2
0
    def __init__(self, kcsrs, rtio_counter, membus, fifo_depth=128):
        # shutdown procedure: set enable to 0, wait until busy=0
        self.enable = CSRStorage()
        self.busy = CSRStatus()

        self.submodules.message_encoder = MessageEncoder(
            kcsrs, rtio_counter, self.enable.storage)
        self.submodules.fifo = stream.SyncFIFO([("data", message_len)],
                                               fifo_depth, True)
        self.submodules.converter = stream.Converter(
            message_len,
            len(membus.dat_w),
            reverse=True,
            report_valid_token_count=True)
        self.submodules.dma = DMAWriter(membus)

        enable_r = Signal()
        self.sync += [
            enable_r.eq(self.enable.storage),
            If(self.enable.storage & ~enable_r, self.busy.status.eq(1)),
            If(self.dma.sink.stb & self.dma.sink.ack & self.dma.sink.eop,
               self.busy.status.eq(0))
        ]

        self.comb += [
            self.message_encoder.source.connect(self.fifo.sink),
            self.fifo.source.connect(self.converter.sink),
            self.converter.source.connect(self.dma.sink)
        ]
Пример #3
0
    def __init__(self, pads, mode="master"):
        self.refclk = Signal()

        # # #

        # In Master mode, generate the linerate/10 clock. Slave will re-multiply it.
        if mode == "master":
            converter = stream.Converter(40, 8)
            self.submodules += converter
            self.comb += [
                converter.sink.stb.eq(1),
                converter.source.ack.eq(1),
                converter.sink.data.eq(
                    Replicate(Signal(10, reset=0b1111100000), 4)),
            ]
            self.specials += [
                Instance("OSERDESE3",
                         p_DATA_WIDTH=8,
                         p_INIT=0,
                         p_IS_CLK_INVERTED=0,
                         p_IS_CLKDIV_INVERTED=0,
                         p_IS_RST_INVERTED=0,
                         o_OQ=self.refclk,
                         i_RST=ResetSignal("sys"),
                         i_CLK=ClockSignal("sys4x"),
                         i_CLKDIV=ClockSignal("sys"),
                         i_D=converter.source.data),
                DifferentialOutput(self.refclk, pads.clk_p, pads.clk_n)
            ]

        # In Slave mode, multiply the clock provided by Master with a PLL/MMCM
        elif mode == "slave":
            self.specials += DifferentialInput(pads.clk_p, pads.clk_n,
                                               self.refclk)
Пример #4
0
    def __init__(self, pads, mode="master"):
        # Control
        self.idle = idle = Signal()
        self.comma = comma = Signal()

        # Datapath
        self.ce = ce = Signal()
        self.k = k = Signal(4)
        self.d = d = Signal(32)

        # # #

        # 8b10b encoder
        self.submodules.encoder = encoder = CEInserter()(Encoder(4, True))
        self.comb += encoder.ce.eq(ce)

        # 40 --> 8 converter
        converter = stream.Converter(40, 8)
        self.submodules += converter
        self.comb += [
            converter.sink.stb.eq(1),
            converter.source.ack.eq(1),
            # Enable pipeline when converter accepts the 40 bits
            ce.eq(converter.sink.ack),
            # If not idle, connect encoder to converter
            If(~idle,
                converter.sink.data.eq(Cat(*[encoder.output[i] for i in range(4)]))
            ),
            # If comma, send K28.5
            If(comma,
                encoder.k[0].eq(1),
                encoder.d[0].eq(K(28,5)),
            # Else connect TX to encoder
            ).Else(
                encoder.k[0].eq(k[0]),
                encoder.k[1].eq(k[1]),
                encoder.k[2].eq(k[2]),
                encoder.k[3].eq(k[3]),
                encoder.d[0].eq(d[0:8]),
                encoder.d[1].eq(d[8:16]),
                encoder.d[2].eq(d[16:24]),
                encoder.d[3].eq(d[24:32])
            )
        ]

        # Data output (DDR with sys4x)
        data = Signal()
        self.specials += [
              Instance("OSERDESE3",
                p_DATA_WIDTH=8, p_INIT=0,
                p_IS_CLK_INVERTED=0, p_IS_CLKDIV_INVERTED=0, p_IS_RST_INVERTED=0,

                o_OQ=data,
                i_RST=ResetSignal("sys"),
                i_CLK=ClockSignal("sys4x"), i_CLKDIV=ClockSignal("sys"),
                i_D=converter.source.data
            ),
            DifferentialOutput(data, pads.tx_p, pads.tx_n)
        ]
Пример #5
0
    def __init__(self, pads, mode="master"):
        # Control
        self.idle = idle = Signal()
        self.comma = comma = Signal()

        # Datapath
        self.ce = ce = Signal()
        self.k = k = Signal(4)
        self.d = d = Signal(32)

        # # #

        # 8b10b encoder
        self.submodules.encoder = encoder = CEInserter()(Encoder(4, True))
        self.comb += encoder.ce.eq(ce)

        # 40 --> 1 converter
        converter = stream.Converter(40, 1)
        self.submodules += converter
        self.comb += [
            converter.sink.stb.eq(1),
            converter.source.ack.eq(1),
            # Enable pipeline when converter accepts the 40 bits
            ce.eq(converter.sink.ack),
            # If not idle, connect encoder to converter
            If(~idle,
                converter.sink.data.eq(Cat(*[encoder.output[i] for i in range(4)]))
            ),
            # If comma, send K28.5
            If(comma,
                encoder.k[0].eq(1),
                encoder.d[0].eq(K(28,5)),
            # Else connect TX to encoder
            ).Else(
                encoder.k[0].eq(k[0]),
                encoder.k[1].eq(k[1]),
                encoder.k[2].eq(k[2]),
                encoder.k[3].eq(k[3]),
                encoder.d[0].eq(d[0:8]),
                encoder.d[1].eq(d[8:16]),
                encoder.d[2].eq(d[16:24]),
                encoder.d[3].eq(d[24:32])
            )
        ]

        # Data output (on rising edge of sys_clk)
        data = Signal()
        self.sync += data.eq(converter.source.data)
        self.specials += DifferentialOutput(data, pads.tx_p, pads.tx_n)
Пример #6
0
    def __init__(self, pads):
        self.source = source = stream.Endpoint(eth_phy_layout(8))

        # # #

        converter = stream.Converter(4, 8)
        converter = ResetInserter()(converter)
        self.submodules += converter

        self.sync += [
            converter.reset.eq(~pads.dv),
            converter.sink.stb.eq(1),
            converter.sink.data.eq(pads.rx_data)
        ]
        self.comb += [converter.sink.eop.eq(~pads.dv)]
        self.comb += converter.source.connect(source)
Пример #7
0
    def __init__(self, pads):
        self.sink = sink = stream.Endpoint(eth_phy_layout(8))

        # # #

        if hasattr(pads, "tx_er"):
            self.sync += pads.tx_er.eq(0)
        converter = stream.Converter(8, 4)
        self.submodules += converter
        self.comb += [
            converter.sink.stb.eq(sink.stb),
            converter.sink.data.eq(sink.data),
            sink.ack.eq(converter.sink.ack),
            converter.source.ack.eq(1)
        ]
        self.sync += [
            pads.tx_en.eq(converter.source.stb),
            pads.tx_data.eq(converter.source.data)
        ]
Пример #8
0
    def __init__(self, pads, mode="master"):
        self.refclk = Signal()

        # # #

        # In Master mode, generate the linerate/10 clock. Slave will re-multiply it.
        if mode == "master":
            converter = stream.Converter(40, 8)
            self.submodules += converter
            self.comb += [
                converter.sink.stb.eq(1),
                converter.source.ack.eq(1),
                converter.sink.data.eq(
                    Replicate(Signal(10, reset=0b1111100000), 4)),
            ]
            self.specials += [
                Instance("OSERDESE2",
                         p_DATA_WIDTH=8,
                         p_TRISTATE_WIDTH=1,
                         p_DATA_RATE_OQ="DDR",
                         p_DATA_RATE_TQ="BUF",
                         p_SERDES_MODE="MASTER",
                         o_OQ=self.refclk,
                         i_OCE=1,
                         i_RST=ResetSignal("sys"),
                         i_CLK=ClockSignal("sys4x"),
                         i_CLKDIV=ClockSignal("sys"),
                         i_D1=converter.source.data[0],
                         i_D2=converter.source.data[1],
                         i_D3=converter.source.data[2],
                         i_D4=converter.source.data[3],
                         i_D5=converter.source.data[4],
                         i_D6=converter.source.data[5],
                         i_D7=converter.source.data[6],
                         i_D8=converter.source.data[7]),
                DifferentialOutput(self.refclk, pads.clk_p, pads.clk_n)
            ]

        # In Slave mode, multiply the clock provided by Master with a PLL/MMCM
        elif mode == "slave":
            self.specials += DifferentialInput(pads.clk_p, pads.clk_n,
                                               self.refclk)
Пример #9
0
def test_downscaler():
    i = axi.Interface()
    dw = i.data_width
    dut = stream.Converter(dw, 8)
    source, sink = dut.source, dut.sink
    write = partial(write_data, sink)
    read = partial(read_data, source)

    def testbench_downscaler():
        def push():
            yield from write(0x44332211)
            yield from write(0x88776655, eop=True)

        def pull():
            yield source.ack.eq(1)
            assert (yield from read()) == 0x11
            yield
            assert (yield from read()) == 0x22
            yield
            assert (yield from read()) == 0x33
            yield
            assert (yield from read()) == 0x44
            yield
            assert (yield from read()) == 0x55
            yield
            assert (yield from read()) == 0x66
            yield
            assert (yield from read()) == 0x77
            yield
            assert (yield from read()) == 0x88
            assert (yield source.eop) == 1

        return [
            push(),
            pull(),
        ]

    run_simulation(dut,
                   testbench_downscaler(),
                   vcd_name=file_tmp_folder("test_downscaler.vcd"))
Пример #10
0
def test_reader():
    i = axi.Interface()
    dw = i.data_width
    dut = Reader(i)
    dut.submodules.downscaler = stream.Converter(dw, 8)
    dut.comb += dut.source.connect(dut.downscaler.sink)

    source, sink = dut.downscaler.source, dut.sink
    request = partial(request_addr, sink)
    read = partial(read_data, source)

    def testbench_reader():
        def push_addr():
            yield from request(0x11223344, eop=True)

        def pull_data():
            yield source.ack.eq(1)
            assert (yield from read()) == 0x04
            yield
            assert (yield from read()) == 0x03
            yield
            assert (yield from read()) == 0x02
            yield
            assert (yield from read()) == 0x01
            assert (yield source.eop) == 1

        def ar_and_r_channel():
            assert (yield from i.read_ar()).addr == 0x11223344
            yield from i.write_r(0x55, 0x01020304, last=1)

        return [
            push_addr(),
            pull_data(),
            ar_and_r_channel(),
        ]

    run_simulation(dut,
                   testbench_reader(),
                   vcd_name=file_tmp_folder("test_reader.vcd"))
Пример #11
0
    def __init__(self, bus, nbits_source=None, fifo_depth=None):
        ar, r = operator.attrgetter("ar", "r")(bus)
        dw = bus.data_width
        alignment_bits = bits_for(dw // 8) - 1
        if nbits_source:
            if nbits_source % 8:
                raise ValueError("nbits_source must be a multiple of 8")
            if nbits_source > dw:
                raise ValueError("nbits_source must be <= bus.data_width")
        nbits_source = nbits_source or dw
        counter_bits = bits_for(
            (2**len(bus.ar.addr) - 1) // (nbits_source // 8))
        self.sink = stream.Endpoint(
            pipe(rec_layout(ar, {"addr"}),
                 juxt([
                     identity,
                     R.always([("n", counter_bits)]),
                 ]), concat, list))
        self.source = stream.Endpoint([("data", nbits_source)])

        ###

        sink_consume = Signal()
        self.comb += sink_consume.eq(self.sink.stb & self.sink.ack)
        remaining = Countdown(counter_bits)
        self.submodules += remaining
        self.comb += [
            remaining.count_w.eq(self.sink.n),
            remaining.we.eq(sink_consume),
            remaining.ce.eq(self.source.stb & self.source.ack),
        ]
        eop_consumed = Signal()
        self.sync += [
            If(
                sink_consume,
                eop_consumed.eq(0),
            ).Elif(self.source.stb & self.source.ack & self.source.eop,
                   eop_consumed.eq(1))
        ]
        converter = stream.Converter(dw, nbits_source)
        self.submodules += converter
        self.comb += [
            converter.source.ack.eq(self.source.ack | eop_consumed),
            self.source.stb.eq(converter.source.stb & ~eop_consumed),
            self.source.eop.eq(remaining.done),
            self.source.data.eq(converter.source.data),
        ]
        fifo_depth = min(fifo_depth or BURST_LENGTH, BURST_LENGTH)
        if fifo_depth % (dw // 8):
            raise ValueError("fifo_depth shall be a multiple of wordsize")
        rfifo = stream.SyncFIFO(rec_layout(r, {"data"}), depth=fifo_depth)
        self.submodules += rfifo
        self.comb += rfifo.source.connect(converter.sink)

        burst_done = Signal()
        self.sync += burst_done.eq(r.valid & r.ready & r.last)
        # ar channel
        ar_acked = Signal(reset=1)
        sink_acked = Signal()
        self.sync += [
            If(sink_consume, sink_acked.eq(1)).Elif(remaining.done,
                                                    sink_acked.eq(0))
        ]

        self.sync += [
            If(
                sink_consume,
                ar_acked.eq(0),
                ar.addr[alignment_bits:].eq(self.sink.addr[alignment_bits:]),
            ).Else(
                If(burst_done & ~remaining.done, ar_acked.eq(0),
                   ar.addr.eq(ar.addr + fifo_depth * dw // 8)),
                If(ar.valid & ar.ready, ar_acked.eq(1))),
        ]
        self.comb += [
            self.sink.ack.eq(~sink_acked & remaining.done),
            ar.len.eq(fifo_depth - 1),
            ar.size.eq(burst_size(dw // 8)),
            ar.burst.eq(Burst.incr.value),
            # ensure FIFO is clear to not stall the bus
            ar.valid.eq(~ar_acked & ~rfifo.source.stb)
        ]
        # r channel
        self.comb += [
            remaining.ce.eq(rfifo.sink.stb & rfifo.sink.ack),
            rfifo.sink.data.eq(r.data),
            rfifo.sink.stb.eq(r.valid),
            r.ready.eq(rfifo.sink.ack),
        ]
Пример #12
0
    def __init__(self, pads, mode="master"):
        # Control
        self.delay_rst = Signal()
        self.delay_inc = Signal()
        self.bitslip_value = bitslip_value = Signal(6)

        # Status
        self.idle = idle = Signal()
        self.comma = comma = Signal()

        # Datapath
        self.ce = ce = Signal()
        self.k = k = Signal(4)
        self.d = d = Signal(32)

        # # #

        # Data input (DDR with sys4x)
        data_nodelay = Signal()
        data_delayed = Signal()
        data_deserialized = Signal(8)
        self.specials += [
            DifferentialInput(pads.rx_p, pads.rx_n, data_nodelay),
            Instance("IDELAYE2",
                     p_DELAY_SRC="IDATAIN",
                     p_SIGNAL_PATTERN="DATA",
                     p_CINVCTRL_SEL="FALSE",
                     p_HIGH_PERFORMANCE_MODE="TRUE",
                     p_REFCLK_FREQUENCY=200.0,
                     p_PIPE_SEL="FALSE",
                     p_IDELAY_TYPE="VARIABLE",
                     p_IDELAY_VALUE=0,
                     i_C=ClockSignal(),
                     i_LD=self.delay_rst,
                     i_CE=self.delay_inc,
                     i_LDPIPEEN=0,
                     i_INC=1,
                     i_IDATAIN=data_nodelay,
                     o_DATAOUT=data_delayed),
            Instance("ISERDESE2",
                     p_DATA_WIDTH=8,
                     p_DATA_RATE="DDR",
                     p_SERDES_MODE="MASTER",
                     p_INTERFACE_TYPE="NETWORKING",
                     p_NUM_CE=1,
                     p_IOBDELAY="IFD",
                     i_DDLY=data_delayed,
                     i_CE1=1,
                     i_RST=ResetSignal("sys"),
                     i_CLK=ClockSignal("sys4x"),
                     i_CLKB=~ClockSignal("sys4x"),
                     i_CLKDIV=ClockSignal("sys"),
                     i_BITSLIP=0,
                     o_Q8=data_deserialized[0],
                     o_Q7=data_deserialized[1],
                     o_Q6=data_deserialized[2],
                     o_Q5=data_deserialized[3],
                     o_Q4=data_deserialized[4],
                     o_Q3=data_deserialized[5],
                     o_Q2=data_deserialized[6],
                     o_Q1=data_deserialized[7])
        ]

        # 8 --> 40 converter and bitslip
        converter = stream.Converter(8, 40)
        self.submodules += converter
        bitslip = CEInserter()(BitSlip(40))
        self.submodules += bitslip
        self.comb += [
            converter.sink.stb.eq(1),
            converter.source.ack.eq(1),
            # Enable pipeline when converter outputs the 40 bits
            ce.eq(converter.source.stb),
            # Connect input data to converter
            converter.sink.data.eq(data_deserialized),
            # Connect converter to bitslip
            bitslip.ce.eq(ce),
            bitslip.value.eq(bitslip_value),
            bitslip.i.eq(converter.source.data)
        ]

        # 8b10b decoder
        self.submodules.decoders = decoders = [
            CEInserter()(Decoder(True)) for _ in range(4)
        ]
        self.comb += [decoders[i].ce.eq(ce) for i in range(4)]
        self.comb += [
            # Connect bitslip to decoder
            decoders[0].input.eq(bitslip.o[0:10]),
            decoders[1].input.eq(bitslip.o[10:20]),
            decoders[2].input.eq(bitslip.o[20:30]),
            decoders[3].input.eq(bitslip.o[30:40]),
            # Connect decoder to output
            self.k.eq(Cat(*[decoders[i].k for i in range(4)])),
            self.d.eq(Cat(*[decoders[i].d for i in range(4)])),
        ]

        # Status
        idle_timer = WaitTimer(256)
        self.submodules += idle_timer
        self.comb += [
            idle_timer.wait.eq(1),
            self.idle.eq(idle_timer.done
                         & ((bitslip.o == 0) | (bitslip.o == (2**40 - 1)))),
            self.comma.eq((decoders[0].k == 1) & (decoders[0].d == K(28, 5))
                          & (decoders[1].k == 0) & (decoders[1].d == 0)
                          & (decoders[2].k == 0) & (decoders[2].d == 0)
                          & (decoders[3].k == 0) & (decoders[3].d == 0))
        ]
Пример #13
0
    def __init__(self, pads, mode="master"):
        if mode == "slave":
            self.refclk = Signal()

        self.tx_ce = Signal()
        self.tx_k = Signal(4)
        self.tx_d = Signal(32)

        self.rx_ce = Signal()
        self.rx_k = Signal(4)
        self.rx_d = Signal(32)

        self.tx_idle = Signal()
        self.tx_comma = Signal()
        self.rx_idle = Signal()
        self.rx_comma = Signal()

        self.rx_bitslip_value = Signal(6)
        self.rx_delay_rst = Signal()
        self.rx_delay_inc = Signal()
        self.rx_delay_en_vtc = Signal()

        # # #

        self.submodules.encoder = encoder = CEInserter()(Encoder(4, True))
        self.comb += encoder.ce.eq(self.tx_ce)
        self.submodules.decoders = decoders = [
            CEInserter()(Decoder(True)) for _ in range(4)
        ]
        self.comb += [decoders[i].ce.eq(self.rx_ce) for i in range(4)]

        # clocking:

        # In master mode:
        # - linerate/10 refclk generated on clk_pads
        # In Slave mode:
        # - linerate/10 refclk provided by clk_pads

        # tx clock (linerate/10)
        if mode == "master":
            clk_converter = stream.Converter(40, 8)
            self.submodules += clk_converter
            self.comb += [
                clk_converter.sink.stb.eq(1),
                clk_converter.sink.data.eq(
                    Replicate(Signal(10, reset=0b1111100000), 4)),
                clk_converter.source.ack.eq(1)
            ]
            clk_o = Signal()
            self.specials += [
                Instance("OSERDESE3",
                         p_DATA_WIDTH=8,
                         p_INIT=0,
                         p_IS_CLK_INVERTED=0,
                         p_IS_CLKDIV_INVERTED=0,
                         p_IS_RST_INVERTED=0,
                         o_OQ=clk_o,
                         i_RST=ResetSignal("sys"),
                         i_CLK=ClockSignal("sys4x"),
                         i_CLKDIV=ClockSignal("sys"),
                         i_D=clk_converter.source.data),
                Instance("OBUFDS", i_I=clk_o, o_O=pads.clk_p, o_OB=pads.clk_n)
            ]

        # tx datapath
        # tx_data -> encoders -> converter -> serdes
        self.submodules.tx_converter = tx_converter = stream.Converter(40, 8)
        self.comb += [
            tx_converter.sink.stb.eq(1),
            self.tx_ce.eq(tx_converter.sink.ack),
            tx_converter.source.ack.eq(1),
            If(self.tx_idle, tx_converter.sink.data.eq(0)).Else(
                tx_converter.sink.data.eq(
                    Cat(*[encoder.output[i] for i in range(4)]))),
            If(
                self.tx_comma,
                encoder.k[0].eq(1),
                encoder.d[0].eq(K(28, 5)),
            ).Else(encoder.k[0].eq(self.tx_k[0]), encoder.k[1].eq(
                self.tx_k[1]), encoder.k[2].eq(self.tx_k[2]),
                   encoder.k[3].eq(self.tx_k[3]),
                   encoder.d[0].eq(self.tx_d[0:8]),
                   encoder.d[1].eq(self.tx_d[8:16]),
                   encoder.d[2].eq(self.tx_d[16:24]),
                   encoder.d[3].eq(self.tx_d[24:32]))
        ]

        serdes_o = Signal()
        self.specials += [
            Instance("OSERDESE3",
                     p_DATA_WIDTH=8,
                     p_INIT=0,
                     p_IS_CLK_INVERTED=0,
                     p_IS_CLKDIV_INVERTED=0,
                     p_IS_RST_INVERTED=0,
                     o_OQ=serdes_o,
                     i_RST=ResetSignal("sys"),
                     i_CLK=ClockSignal("sys4x"),
                     i_CLKDIV=ClockSignal("sys"),
                     i_D=tx_converter.source.data),
            Instance("OBUFDS", i_I=serdes_o, o_O=pads.tx_p, o_OB=pads.tx_n)
        ]

        # rx clock
        use_bufr = True
        if mode == "slave":
            clk_i = Signal()
            clk_i_bufg = Signal()
            self.specials += [
                Instance("IBUFDS", i_I=pads.clk_p, i_IB=pads.clk_n, o_O=clk_i)
            ]
            if use_bufr:
                clk_i_bufr = Signal()
                self.specials += [
                    Instance("BUFR", i_I=clk_i, o_O=clk_i_bufr),
                    Instance("BUFG", i_I=clk_i_bufr, o_O=clk_i_bufg)
                ]
            else:
                self.specials += Instance("BUFG", i_I=clk_i, o_O=clk_i_bufg)
            self.comb += self.refclk.eq(clk_i_bufg)

        # rx datapath
        # serdes -> converter -> bitslip -> decoders -> rx_data
        self.submodules.rx_converter = rx_converter = stream.Converter(8, 40)
        self.comb += [
            self.rx_ce.eq(rx_converter.source.stb),
            rx_converter.source.ack.eq(1)
        ]
        self.submodules.rx_bitslip = rx_bitslip = CEInserter()(BitSlip(40))
        self.comb += rx_bitslip.ce.eq(self.rx_ce)

        serdes_i_nodelay = Signal()
        self.specials += [
            Instance("IBUFDS_DIFF_OUT",
                     i_I=pads.rx_p,
                     i_IB=pads.rx_n,
                     o_O=serdes_i_nodelay)
        ]

        serdes_i_delayed = Signal()
        serdes_q = Signal(8)
        self.specials += [
            Instance("IDELAYE3",
                     p_CASCADE="NONE",
                     p_UPDATE_MODE="ASYNC",
                     p_REFCLK_FREQUENCY=200.0,
                     p_IS_CLK_INVERTED=0,
                     p_IS_RST_INVERTED=0,
                     p_DELAY_FORMAT="COUNT",
                     p_DELAY_SRC="IDATAIN",
                     p_DELAY_TYPE="VARIABLE",
                     p_DELAY_VALUE=0,
                     i_CLK=ClockSignal("sys"),
                     i_RST=self.rx_delay_rst,
                     i_LOAD=0,
                     i_INC=1,
                     i_EN_VTC=self.rx_delay_en_vtc,
                     i_CE=self.rx_delay_inc,
                     i_IDATAIN=serdes_i_nodelay,
                     o_DATAOUT=serdes_i_delayed),
            Instance(
                "ISERDESE3",
                p_IS_CLK_INVERTED=0,
                p_IS_CLK_B_INVERTED=1,
                p_DATA_WIDTH=8,
                i_D=serdes_i_delayed,
                i_RST=ResetSignal("sys"),
                i_FIFO_RD_CLK=0,
                i_FIFO_RD_EN=0,
                i_CLK=ClockSignal("sys4x"),
                i_CLK_B=ClockSignal("sys4x"),  # locally inverted
                i_CLKDIV=ClockSignal("sys"),
                o_Q=serdes_q)
        ]

        self.comb += [
            rx_converter.sink.stb.eq(1),
            rx_converter.sink.data.eq(serdes_q),
            rx_bitslip.value.eq(self.rx_bitslip_value),
            rx_bitslip.i.eq(rx_converter.source.data),
            decoders[0].input.eq(rx_bitslip.o[0:10]),
            decoders[1].input.eq(rx_bitslip.o[10:20]),
            decoders[2].input.eq(rx_bitslip.o[20:30]),
            decoders[3].input.eq(rx_bitslip.o[30:40]),
            self.rx_k.eq(Cat(*[decoders[i].k for i in range(4)])),
            self.rx_d.eq(Cat(*[decoders[i].d for i in range(4)])),
            self.rx_comma.eq((decoders[0].k == 1) & (decoders[0].d == K(28, 5))
                             & (decoders[1].k == 0) & (decoders[1].d == 0)
                             & (decoders[2].k == 0) & (decoders[2].d == 0)
                             & (decoders[3].k == 0) & (decoders[3].d == 0))
        ]

        idle_timer = WaitTimer(32)
        self.submodules += idle_timer
        self.comb += idle_timer.wait.eq(1)
        self.sync += self.rx_idle.eq(idle_timer.done & (rx_bitslip.o == 0))
Пример #14
0
    def __init__(self, pads, mode="master"):
        # Control
        self.delay_rst = Signal()
        self.delay_inc = Signal()
        self.bitslip_value = bitslip_value = Signal(6)

        # Status
        self.idle = idle = Signal()
        self.comma = comma = Signal()

        # Datapath
        self.ce = ce = Signal()
        self.k = k = Signal(4)
        self.d = d = Signal(32)

        # # #

        # Data input (DDR with sys4x)
        data_nodelay = Signal()
        data_delayed = Signal()
        data_deserialized = Signal(8)
        self.specials += [
            DifferentialInput(pads.rx_p, pads.rx_n, data_nodelay),
            Instance("IDELAYE3",
                p_CASCADE="NONE", p_UPDATE_MODE="ASYNC", p_REFCLK_FREQUENCY=200.0,
                p_IS_CLK_INVERTED=0, p_IS_RST_INVERTED=0,
                p_DELAY_FORMAT="COUNT", p_DELAY_SRC="IDATAIN",
                p_DELAY_TYPE="VARIABLE", p_DELAY_VALUE=0,

                i_CLK=ClockSignal("sys"),
                i_RST=self.delay_rst, i_LOAD=0,
                i_INC=1, i_EN_VTC=0,
                i_CE=self.delay_inc,

                i_IDATAIN=data_nodelay, o_DATAOUT=data_delayed
            ),
            Instance("ISERDESE3",
                p_IS_CLK_INVERTED=0,
                p_IS_CLK_B_INVERTED=1,
                p_DATA_WIDTH=8,

                i_D=data_delayed,
                i_RST=ResetSignal("sys"),
                i_FIFO_RD_CLK=0, i_FIFO_RD_EN=0,
                i_CLK=ClockSignal("sys4x"),
                i_CLK_B=ClockSignal("sys4x"), # locally inverted
                i_CLKDIV=ClockSignal("sys"),
                o_Q=data_deserialized
            )
        ]

        # 8 --> 40 converter and bitslip
        converter = stream.Converter(8, 40)
        self.submodules += converter
        bitslip = CEInserter()(BitSlip(40))
        self.submodules += bitslip
        self.comb += [
            converter.sink.stb.eq(1),
            converter.source.ack.eq(1),
            # Enable pipeline when converter outputs the 40 bits
            ce.eq(converter.source.stb),
            # Connect input data to converter
            converter.sink.data.eq(data_deserialized),
            # Connect converter to bitslip
            bitslip.ce.eq(ce),
            bitslip.value.eq(bitslip_value),
            bitslip.i.eq(converter.source.data)
        ]

        # 8b10b decoder
        self.submodules.decoders = decoders = [CEInserter()(Decoder(True)) for _ in range(4)]
        self.comb += [decoders[i].ce.eq(ce) for i in range(4)]
        self.comb += [
            # Connect bitslip to decoder
            decoders[0].input.eq(bitslip.o[0:10]),
            decoders[1].input.eq(bitslip.o[10:20]),
            decoders[2].input.eq(bitslip.o[20:30]),
            decoders[3].input.eq(bitslip.o[30:40]),
            # Connect decoder to output
            self.k.eq(Cat(*[decoders[i].k for i in range(4)])),
            self.d.eq(Cat(*[decoders[i].d for i in range(4)])),
        ]

        # Status
        idle_timer = WaitTimer(256)
        self.submodules += idle_timer
        self.comb += [
            idle_timer.wait.eq(1),
            self.idle.eq(idle_timer.done &
                 ((bitslip.o == 0) | (bitslip.o == (2**40-1)))),
            self.comma.eq(
                (decoders[0].k == 1) & (decoders[0].d == K(28,5)) &
                (decoders[1].k == 0) & (decoders[1].d == 0) &
                (decoders[2].k == 0) & (decoders[2].d == 0) &
                (decoders[3].k == 0) & (decoders[3].d == 0))
        ]
Пример #15
0
    def __init__(self, pads, mode="master"):
        # Control
        self.bitslip_value = bitslip_value = Signal(6)

        # Status
        self.idle = idle = Signal()
        self.comma = comma = Signal()

        # Datapath
        self.ce = ce = Signal()
        self.k = k = Signal(4)
        self.d = d = Signal(32)

        # # #

        # Input data (on rising edge of sys_clk)
        data = Signal()
        data_d = Signal()
        self.specials += DifferentialInput(pads.rx_p, pads.rx_n, data)
        self.sync += data_d.eq(data)

        # 1 --> 40 converter and bitslip
        converter = stream.Converter(1, 40)
        self.submodules += converter
        bitslip = CEInserter()(BitSlip(40))
        self.submodules += bitslip
        self.comb += [
            converter.sink.stb.eq(1),
            converter.source.ack.eq(1),
            # Enable pipeline when converter outputs the 40 bits
            ce.eq(converter.source.stb),
            # Connect input data to converter
            converter.sink.data.eq(data),
            # Connect converter to bitslip
            bitslip.ce.eq(ce),
            bitslip.value.eq(bitslip_value),
            bitslip.i.eq(converter.source.data)
        ]

        # 8b10b decoder
        self.submodules.decoders = decoders = [CEInserter()(Decoder(True)) for _ in range(4)]
        self.comb += [decoders[i].ce.eq(ce) for i in range(4)]
        self.comb += [
            # Connect bitslip to decoder
            decoders[0].input.eq(bitslip.o[0:10]),
            decoders[1].input.eq(bitslip.o[10:20]),
            decoders[2].input.eq(bitslip.o[20:30]),
            decoders[3].input.eq(bitslip.o[30:40]),
            # Connect decoder to output
            self.k.eq(Cat(*[decoders[i].k for i in range(4)])),
            self.d.eq(Cat(*[decoders[i].d for i in range(4)])),
        ]

        # Status
        idle_timer = WaitTimer(256)
        self.submodules += idle_timer
        self.comb += [
            idle_timer.wait.eq(1),
            self.idle.eq(idle_timer.done &
                 ((bitslip.o == 0) | (bitslip.o == (2**40-1)))),
            self.comma.eq(
                (decoders[0].k == 1) & (decoders[0].d == K(28,5)) &
                (decoders[1].k == 0) & (decoders[1].d == 0) &
                (decoders[2].k == 0) & (decoders[2].d == 0) &
                (decoders[3].k == 0) & (decoders[3].d == 0))
        ]
Пример #16
0
    def __init__(self, link_layer, min_mem_dw):
        self.aux_rx_length = CSRStatus(bits_for(max_packet))
        self.aux_rx_present = CSR()
        self.aux_rx_error = CSR()

        ll_dw = len(link_layer.rx_aux_data)
        mem_dw = max(min_mem_dw, ll_dw)
        self.specials.mem = Memory(mem_dw, max_packet//(mem_dw//8))

        converter = ClockDomainsRenamer("rtio_rx")(
            stream.Converter(ll_dw, mem_dw))
        self.submodules += converter

        # when continuously drained, the Converter accepts data continuously
        frame_r = Signal()
        self.sync.rtio_rx += [
            If(link_layer.rx_aux_stb,
                frame_r.eq(link_layer.rx_aux_frame),
                converter.sink.data.eq(link_layer.rx_aux_data)
            )
        ]
        self.comb += [
            converter.sink.stb.eq(link_layer.rx_aux_stb & frame_r),
            converter.sink.eop.eq(converter.sink.stb & ~link_layer.rx_aux_frame)
        ]

        mem_port = self.mem.get_port(write_capable=True, clock_domain="rtio_rx")
        self.specials += mem_port

        frame_counter_nbits = bits_for(max_packet) - log2_int(mem_dw//8)
        frame_counter = Signal(frame_counter_nbits)
        self.comb += [
            mem_port.adr.eq(frame_counter),
            mem_port.dat_w.eq(converter.source.data),
            converter.source.ack.eq(1)
        ]

        frame_counter.attr.add("no_retiming")
        frame_counter_sys = Signal(frame_counter_nbits)
        self.specials += MultiReg(frame_counter, frame_counter_sys)
        self.comb += self.aux_rx_length.status.eq(frame_counter_sys << log2_int(mem_dw//8))

        signal_frame = PulseSynchronizer("rtio_rx", "sys")
        frame_ack = PulseSynchronizer("sys", "rtio_rx")
        signal_error = PulseSynchronizer("rtio_rx", "sys")
        self.submodules += signal_frame, frame_ack, signal_error
        self.sync += [
            If(self.aux_rx_present.re, self.aux_rx_present.w.eq(0)),
            If(signal_frame.o, self.aux_rx_present.w.eq(1)),
            If(self.aux_rx_error.re, self.aux_rx_error.w.eq(0)),
            If(signal_error.o, self.aux_rx_error.w.eq(1))
        ]
        self.comb += frame_ack.i.eq(self.aux_rx_present.re)

        fsm = ClockDomainsRenamer("rtio_rx")(FSM(reset_state="IDLE"))
        self.submodules += fsm

        sop = Signal(reset=1)
        self.sync.rtio_rx += \
            If(converter.source.stb,
                If(converter.source.eop,
                    sop.eq(1)
                ).Else(
                    sop.eq(0)
                )
            )

        fsm.act("IDLE",
            If(converter.source.stb & sop,
                NextValue(frame_counter, frame_counter + 1),
                mem_port.we.eq(1),
                If(converter.source.eop,
                    NextState("SIGNAL_FRAME")
                ).Else(
                    NextState("FRAME")
                )
            ).Else(
                NextValue(frame_counter, 0)
            )
        )
        fsm.act("FRAME",
            If(converter.source.stb,
                NextValue(frame_counter, frame_counter + 1),
                mem_port.we.eq(1),
                If(frame_counter == max_packet,
                    mem_port.we.eq(0),
                    signal_error.i.eq(1),
                    NextState("IDLE")  # discard the rest of the frame
                ),
                If(converter.source.eop,
                    NextState("SIGNAL_FRAME")
                )
            )
        )
        fsm.act("SIGNAL_FRAME",
            signal_frame.i.eq(1),
            NextState("WAIT_ACK"),
            If(converter.source.stb, signal_error.i.eq(1))
        )
        fsm.act("WAIT_ACK",
            If(frame_ack.o,
                NextValue(frame_counter, 0), 
                NextState("IDLE")
            ),
            If(converter.source.stb, signal_error.i.eq(1))
        )
Пример #17
0
    def __init__(self, link_layer, min_mem_dw):
        ll_dw = len(link_layer.tx_aux_data)
        mem_dw = max(min_mem_dw, ll_dw)

        self.aux_tx_length = CSRStorage(bits_for(max_packet),
                                        alignment_bits=log2_int(mem_dw//8))
        self.aux_tx = CSR()
        self.specials.mem = Memory(mem_dw, max_packet//(mem_dw//8))

        converter = ClockDomainsRenamer("rtio")(
            stream.Converter(mem_dw, ll_dw))
        self.submodules += converter

        # when continuously fed, the Converter outputs data continuously
        self.comb += [
            converter.source.ack.eq(link_layer.tx_aux_ack),
            link_layer.tx_aux_frame.eq(converter.source.stb),
            link_layer.tx_aux_data.eq(converter.source.data)
        ]

        seen_eop_rst = Signal()
        frame_r = Signal()
        seen_eop = Signal()
        self.sync.rtio += [
            If(link_layer.tx_aux_ack,
                frame_r.eq(link_layer.tx_aux_frame),
                If(frame_r & ~link_layer.tx_aux_frame, seen_eop.eq(1))
            ),
            If(seen_eop_rst, seen_eop.eq(0))
        ]

        mem_port = self.mem.get_port(clock_domain="rtio")
        self.specials += mem_port

        self.aux_tx_length.storage.attr.add("no_retiming")
        tx_length = Signal(bits_for(max_packet))
        self.specials += MultiReg(self.aux_tx_length.storage, tx_length, "rtio")

        frame_counter_nbits = bits_for(max_packet) - log2_int(mem_dw//8)
        frame_counter = Signal(frame_counter_nbits)
        frame_counter_next = Signal(frame_counter_nbits)
        frame_counter_ce = Signal()
        frame_counter_rst = Signal()
        self.comb += [
            frame_counter_next.eq(frame_counter),
            If(frame_counter_rst,
                frame_counter_next.eq(0)
            ).Elif(frame_counter_ce,
                frame_counter_next.eq(frame_counter + 1)
            ),
            mem_port.adr.eq(frame_counter_next),
            converter.sink.data.eq(mem_port.dat_r)
        ]
        self.sync.rtio += frame_counter.eq(frame_counter_next)

        start_tx = PulseSynchronizer("sys", "rtio")
        tx_done = PulseSynchronizer("rtio", "sys")
        self.submodules += start_tx, tx_done
        self.comb += start_tx.i.eq(self.aux_tx.re)
        self.sync += [
            If(tx_done.o, self.aux_tx.w.eq(0)),
            If(self.aux_tx.re, self.aux_tx.w.eq(1))
        ]

        fsm = ClockDomainsRenamer("rtio")(FSM(reset_state="IDLE"))
        self.submodules += fsm

        fsm.act("IDLE",
            frame_counter_rst.eq(1),
            seen_eop_rst.eq(1),
            If(start_tx.o, NextState("TRANSMIT"))
        )
        fsm.act("TRANSMIT",
            converter.sink.stb.eq(1),
            If(converter.sink.ack,
                frame_counter_ce.eq(1)
            ),
            If(frame_counter_next == tx_length, NextState("WAIT_INTERFRAME"))
        )
        fsm.act("WAIT_INTERFRAME",
            If(seen_eop,
                tx_done.i.eq(1),
                NextState("IDLE")
            )
        )
Пример #18
0
    def __init__(self, pads, mode="master"):
        if mode == "slave":
            self.refclk = Signal()

        self.tx_ce = Signal()
        self.tx_k = Signal(4)
        self.tx_d = Signal(32)

        self.rx_ce = Signal()
        self.rx_k = Signal(4)
        self.rx_d = Signal(32)

        self.tx_idle = Signal()
        self.tx_comma = Signal()
        self.rx_idle = Signal()
        self.rx_comma = Signal()

        self.rx_bitslip_value = Signal(6)
        self.rx_delay_rst = Signal()
        self.rx_delay_inc = Signal()

        # # #

        self.submodules.encoder = encoder = CEInserter()(Encoder(4, True))
        self.comb += encoder.ce.eq(self.tx_ce)
        self.submodules.decoders = decoders = [
            CEInserter()(Decoder(True)) for _ in range(4)
        ]
        self.comb += [decoders[i].ce.eq(self.rx_ce) for i in range(4)]

        # clocking:

        # In Master mode:
        # - linerate/10 refclk is generated on clk_pads
        # In Slave mode:
        # - linerate/10 refclk is provided by clk_pads

        # tx clock (linerate/10)
        if mode == "master":
            clk_converter = stream.Converter(40, 8)
            self.submodules += clk_converter
            self.comb += [
                clk_converter.sink.stb.eq(1),
                clk_converter.sink.data.eq(
                    Replicate(Signal(10, reset=0b1111100000), 4)),
                clk_converter.source.ack.eq(1)
            ]
            clk_o = Signal()
            self.specials += [
                Instance("OSERDESE2",
                         p_DATA_WIDTH=8,
                         p_TRISTATE_WIDTH=1,
                         p_DATA_RATE_OQ="DDR",
                         p_DATA_RATE_TQ="BUF",
                         p_SERDES_MODE="MASTER",
                         o_OQ=clk_o,
                         i_OCE=1,
                         i_RST=ResetSignal("sys"),
                         i_CLK=ClockSignal("sys4x"),
                         i_CLKDIV=ClockSignal("sys"),
                         i_D1=clk_converter.source.data[0],
                         i_D2=clk_converter.source.data[1],
                         i_D3=clk_converter.source.data[2],
                         i_D4=clk_converter.source.data[3],
                         i_D5=clk_converter.source.data[4],
                         i_D6=clk_converter.source.data[5],
                         i_D7=clk_converter.source.data[6],
                         i_D8=clk_converter.source.data[7]),
                Instance("OBUFDS", i_I=clk_o, o_O=pads.clk_p, o_OB=pads.clk_n)
            ]

        # tx datapath
        # tx_data -> encoders -> converter -> serdes
        self.submodules.tx_converter = tx_converter = stream.Converter(40, 8)
        self.comb += [
            tx_converter.sink.stb.eq(1),
            self.tx_ce.eq(tx_converter.sink.ack),
            tx_converter.source.ack.eq(1),
            If(self.tx_idle, tx_converter.sink.data.eq(0)).Else(
                tx_converter.sink.data.eq(
                    Cat(*[encoder.output[i] for i in range(4)]))),
            If(
                self.tx_comma,
                encoder.k[0].eq(1),
                encoder.d[0].eq(K(28, 5)),
            ).Else(encoder.k[0].eq(self.tx_k[0]), encoder.k[1].eq(
                self.tx_k[1]), encoder.k[2].eq(self.tx_k[2]),
                   encoder.k[3].eq(self.tx_k[3]),
                   encoder.d[0].eq(self.tx_d[0:8]),
                   encoder.d[1].eq(self.tx_d[8:16]),
                   encoder.d[2].eq(self.tx_d[16:24]),
                   encoder.d[3].eq(self.tx_d[24:32]))
        ]

        serdes_o = Signal()
        self.specials += [
            Instance("OSERDESE2",
                     p_DATA_WIDTH=8,
                     p_TRISTATE_WIDTH=1,
                     p_DATA_RATE_OQ="DDR",
                     p_DATA_RATE_TQ="BUF",
                     p_SERDES_MODE="MASTER",
                     o_OQ=serdes_o,
                     i_OCE=1,
                     i_RST=ResetSignal("sys"),
                     i_CLK=ClockSignal("sys4x"),
                     i_CLKDIV=ClockSignal("sys"),
                     i_D1=tx_converter.source.data[0],
                     i_D2=tx_converter.source.data[1],
                     i_D3=tx_converter.source.data[2],
                     i_D4=tx_converter.source.data[3],
                     i_D5=tx_converter.source.data[4],
                     i_D6=tx_converter.source.data[5],
                     i_D7=tx_converter.source.data[6],
                     i_D8=tx_converter.source.data[7]),
            Instance("OBUFDS", i_I=serdes_o, o_O=pads.tx_p, o_OB=pads.tx_n)
        ]

        # rx clock
        use_bufr = True
        if mode == "slave":
            clk_i = Signal()
            clk_i_bufg = Signal()
            self.specials += [
                Instance("IBUFDS", i_I=pads.clk_p, i_IB=pads.clk_n, o_O=clk_i)
            ]
            if use_bufr:
                clk_i_bufr = Signal()
                self.specials += [
                    Instance("BUFR", i_I=clk_i, o_O=clk_i_bufr),
                    Instance("BUFG", i_I=clk_i_bufr, o_O=clk_i_bufg)
                ]
            else:
                self.specials += Instance("BUFG", i_I=clk_i, o_O=clk_i_bufg)
            self.comb += self.refclk.eq(clk_i_bufg)

        # rx datapath
        # serdes -> converter -> bitslip -> decoders -> rx_data
        self.submodules.rx_converter = rx_converter = stream.Converter(8, 40)
        self.comb += [
            self.rx_ce.eq(rx_converter.source.stb),
            rx_converter.source.ack.eq(1)
        ]
        self.submodules.rx_bitslip = rx_bitslip = CEInserter()(BitSlip(40))
        self.comb += rx_bitslip.ce.eq(self.rx_ce)

        serdes_i_nodelay = Signal()
        self.specials += [
            Instance("IBUFDS_DIFF_OUT",
                     i_I=pads.rx_p,
                     i_IB=pads.rx_n,
                     o_O=serdes_i_nodelay)
        ]

        serdes_i_delayed = Signal()
        serdes_q = Signal(8)
        self.specials += [
            Instance("IDELAYE2",
                     p_DELAY_SRC="IDATAIN",
                     p_SIGNAL_PATTERN="DATA",
                     p_CINVCTRL_SEL="FALSE",
                     p_HIGH_PERFORMANCE_MODE="TRUE",
                     p_REFCLK_FREQUENCY=200.0,
                     p_PIPE_SEL="FALSE",
                     p_IDELAY_TYPE="VARIABLE",
                     p_IDELAY_VALUE=0,
                     i_C=ClockSignal(),
                     i_LD=self.rx_delay_rst,
                     i_CE=self.rx_delay_inc,
                     i_LDPIPEEN=0,
                     i_INC=1,
                     i_IDATAIN=serdes_i_nodelay,
                     o_DATAOUT=serdes_i_delayed),
            Instance("ISERDESE2",
                     p_DATA_WIDTH=8,
                     p_DATA_RATE="DDR",
                     p_SERDES_MODE="MASTER",
                     p_INTERFACE_TYPE="NETWORKING",
                     p_NUM_CE=1,
                     p_IOBDELAY="IFD",
                     i_DDLY=serdes_i_delayed,
                     i_CE1=1,
                     i_RST=ResetSignal("sys"),
                     i_CLK=ClockSignal("sys4x"),
                     i_CLKB=~ClockSignal("sys4x"),
                     i_CLKDIV=ClockSignal("sys"),
                     i_BITSLIP=0,
                     o_Q8=serdes_q[0],
                     o_Q7=serdes_q[1],
                     o_Q6=serdes_q[2],
                     o_Q5=serdes_q[3],
                     o_Q4=serdes_q[4],
                     o_Q3=serdes_q[5],
                     o_Q2=serdes_q[6],
                     o_Q1=serdes_q[7])
        ]

        self.comb += [
            rx_converter.sink.stb.eq(1),
            rx_converter.sink.data.eq(serdes_q),
            rx_bitslip.value.eq(self.rx_bitslip_value),
            rx_bitslip.i.eq(rx_converter.source.data),
            decoders[0].input.eq(rx_bitslip.o[0:10]),
            decoders[1].input.eq(rx_bitslip.o[10:20]),
            decoders[2].input.eq(rx_bitslip.o[20:30]),
            decoders[3].input.eq(rx_bitslip.o[30:40]),
            self.rx_k.eq(Cat(*[decoders[i].k for i in range(4)])),
            self.rx_d.eq(Cat(*[decoders[i].d for i in range(4)])),
            self.rx_comma.eq((decoders[0].k == 1) & (decoders[0].d == K(28, 5))
                             & (decoders[1].k == 0) & (decoders[1].d == 0)
                             & (decoders[2].k == 0) & (decoders[2].d == 0)
                             & (decoders[3].k == 0) & (decoders[3].d == 0))
        ]

        idle_timer = WaitTimer(32)
        self.submodules += idle_timer
        self.comb += idle_timer.wait.eq(1)
        self.sync += self.rx_idle.eq(idle_timer.done & (rx_bitslip.o == 0))