diff --git a/artiq/gateware/serwb/etherbone.py b/artiq/gateware/serwb/etherbone.py new file mode 100644 index 000000000..8337b4389 --- /dev/null +++ b/artiq/gateware/serwb/etherbone.py @@ -0,0 +1,722 @@ +""" +Etherbone + +CERN's Etherbone protocol is initially used to run a Wishbone bus over an +ethernet network. This re-implementation is meant to be run over serdes +and introduces some limitations: +- no probing (pf/pr) +- no address spaces (rca/bca/wca/wff) +- 32bits data and address +- 1 record per frame +""" + +from migen import * + +from misoc.interconnect import stream +from misoc.interconnect import wishbone + +from amc_rtm_link.packet import * + + +class Packetizer(Module): + def __init__(self, sink_description, source_description, header): + self.sink = sink = stream.Endpoint(sink_description) + self.source = source = stream.Endpoint(source_description) + self.header = Signal(header.length*8) + + # # # + + dw = len(self.sink.data) + + header_reg = Signal(header.length*8, reset_less=True) + header_words = (header.length*8)//dw + load = Signal() + shift = Signal() + counter = Signal(max=max(header_words, 2)) + counter_reset = Signal() + counter_ce = Signal() + self.sync += \ + If(counter_reset, + counter.eq(0) + ).Elif(counter_ce, + counter.eq(counter + 1) + ) + + self.comb += header.encode(sink, self.header) + if header_words == 1: + self.sync += [ + If(load, + header_reg.eq(self.header) + ) + ] + else: + self.sync += [ + If(load, + header_reg.eq(self.header) + ).Elif(shift, + header_reg.eq(Cat(header_reg[dw:], Signal(dw))) + ) + ] + + fsm = FSM(reset_state="IDLE") + self.submodules += fsm + + if header_words == 1: + idle_next_state = "COPY" + else: + idle_next_state = "SEND_HEADER" + + fsm.act("IDLE", + sink.ack.eq(1), + counter_reset.eq(1), + If(sink.stb, + sink.ack.eq(0), + source.stb.eq(1), + source.eop.eq(0), + source.data.eq(self.header[:dw]), + If(source.stb & source.ack, + load.eq(1), + NextState(idle_next_state) + ) + ) + ) + if header_words != 1: + fsm.act("SEND_HEADER", + source.stb.eq(1), + source.eop.eq(0), + source.data.eq(header_reg[dw:2*dw]), + If(source.stb & source.ack, + shift.eq(1), + counter_ce.eq(1), + If(counter == header_words-2, + NextState("COPY") + ) + ) + ) + if hasattr(sink, "error"): + self.comb += source.error.eq(sink.error) + fsm.act("COPY", + source.stb.eq(sink.stb), + source.eop.eq(sink.eop), + source.data.eq(sink.data), + If(source.stb & source.ack, + sink.ack.eq(1), + If(source.eop, + NextState("IDLE") + ) + ) + ) + + +class Depacketizer(Module): + def __init__(self, sink_description, source_description, header): + self.sink = sink = stream.Endpoint(sink_description) + self.source = source = stream.Endpoint(source_description) + self.header = Signal(header.length*8) + + # # # + + dw = len(sink.data) + + header_reg = Signal(header.length*8, reset_less=True) + header_words = (header.length*8)//dw + + shift = Signal() + counter = Signal(max=max(header_words, 2)) + counter_reset = Signal() + counter_ce = Signal() + self.sync += \ + If(counter_reset, + counter.eq(0) + ).Elif(counter_ce, + counter.eq(counter + 1) + ) + + if header_words == 1: + self.sync += \ + If(shift, + header_reg.eq(sink.data) + ) + else: + self.sync += \ + If(shift, + header_reg.eq(Cat(header_reg[dw:], sink.data)) + ) + self.comb += self.header.eq(header_reg) + + fsm = FSM(reset_state="IDLE") + self.submodules += fsm + + if header_words == 1: + idle_next_state = "COPY" + else: + idle_next_state = "RECEIVE_HEADER" + + fsm.act("IDLE", + sink.ack.eq(1), + counter_reset.eq(1), + If(sink.stb, + shift.eq(1), + NextState(idle_next_state) + ) + ) + if header_words != 1: + fsm.act("RECEIVE_HEADER", + sink.ack.eq(1), + If(sink.stb, + counter_ce.eq(1), + shift.eq(1), + If(counter == header_words-2, + NextState("COPY") + ) + ) + ) + no_payload = Signal() + self.sync += \ + If(fsm.before_entering("COPY"), + no_payload.eq(sink.eop) + ) + + if hasattr(sink, "error"): + self.comb += source.error.eq(sink.error) + self.comb += [ + source.eop.eq(sink.eop | no_payload), + source.data.eq(sink.data), + header.decode(self.header, source) + ] + fsm.act("COPY", + sink.ack.eq(source.ack), + source.stb.eq(sink.stb | no_payload), + If(source.stb & source.ack & source.eop, + NextState("IDLE") + ) + ) + + +etherbone_magic = 0x4e6f +etherbone_version = 1 +etherbone_packet_header_length = 8 +etherbone_packet_header_fields = { + "magic": HeaderField(0, 0, 16), + + "version": HeaderField(2, 4, 4), + "nr": HeaderField(2, 2, 1), + "pr": HeaderField(2, 1, 1), # unused + "pf": HeaderField(2, 0, 1), # unused + + "addr_size": HeaderField(3, 4, 4), # static + "port_size": HeaderField(3, 0, 4) # static +} +etherbone_packet_header = Header(etherbone_packet_header_fields, + etherbone_packet_header_length, + swap_field_bytes=True) + +etherbone_record_header_length = 4 +etherbone_record_header_fields = { + "bca": HeaderField(0, 0, 1), # unused + "rca": HeaderField(0, 1, 1), # unused + "rff": HeaderField(0, 2, 1), # unused + "cyc": HeaderField(0, 4, 1), # unused + "wca": HeaderField(0, 5, 1), # unused + "wff": HeaderField(0, 6, 1), # unused + + "byte_enable": HeaderField(1, 0, 8), + + "wcount": HeaderField(2, 0, 8), + + "rcount": HeaderField(3, 0, 8) +} +etherbone_record_header = Header(etherbone_record_header_fields, + etherbone_record_header_length, + swap_field_bytes=True) + +def _remove_from_layout(layout, *args): + r = [] + for f in layout: + remove = False + for arg in args: + if f[0] == arg: + remove = True + if not remove: + r.append(f) + return r + +def etherbone_packet_description(dw): + layout = etherbone_packet_header.get_layout() + layout += [("data", dw)] + return stream.EndpointDescription(layout) + +def etherbone_packet_user_description(dw): + layout = etherbone_packet_header.get_layout() + layout = _remove_from_layout(layout, + "magic", + "portsize", + "addrsize", + "version") + layout += user_description(dw).payload_layout + return stream.EndpointDescription(layout) + +def etherbone_record_description(dw): + layout = etherbone_record_header.get_layout() + layout += [("data", dw)] + return stream.EndpointDescription(layout) + +def etherbone_mmap_description(dw): + layout = [ + ("we", 1), + ("count", 8), + ("base_addr", 32), + ("be", dw//8), + ("addr", 32), + ("data", dw) + ] + return stream.EndpointDescription(layout) + + +# etherbone packet + +class EtherbonePacketPacketizer(Packetizer): + def __init__(self): + Packetizer.__init__(self, + etherbone_packet_description(32), + user_description(32), + etherbone_packet_header) + + +class EtherbonePacketTX(Module): + def __init__(self): + self.sink = sink = stream.Endpoint(etherbone_packet_user_description(32)) + self.source = source = stream.Endpoint(user_description(32)) + + # # # + + self.submodules.packetizer = packetizer = EtherbonePacketPacketizer() + self.comb += [ + packetizer.sink.stb.eq(sink.stb), + packetizer.sink.eop.eq(sink.eop), + sink.ack.eq(packetizer.sink.ack), + + packetizer.sink.magic.eq(etherbone_magic), + packetizer.sink.port_size.eq(32//8), + packetizer.sink.addr_size.eq(32//8), + packetizer.sink.nr.eq(sink.nr), + packetizer.sink.version.eq(etherbone_version), + + packetizer.sink.data.eq(sink.data) + ] + self.submodules.fsm = fsm = FSM(reset_state="IDLE") + fsm.act("IDLE", + packetizer.source.ack.eq(1), + If(packetizer.source.stb, + packetizer.source.ack.eq(0), + NextState("SEND") + ) + ) + fsm.act("SEND", + packetizer.source.connect(source), + source.length.eq(sink.length + etherbone_packet_header.length), + If(source.stb & source.eop & source.ack, + NextState("IDLE") + ) + ) + + +class EtherbonePacketDepacketizer(Depacketizer): + def __init__(self): + Depacketizer.__init__(self, + user_description(32), + etherbone_packet_description(32), + etherbone_packet_header) + + +class EtherbonePacketRX(Module): + def __init__(self): + self.sink = sink = stream.Endpoint(user_description(32)) + self.source = source = stream.Endpoint(etherbone_packet_user_description(32)) + + # # # + + self.submodules.depacketizer = depacketizer = EtherbonePacketDepacketizer() + self.comb += sink.connect(depacketizer.sink) + + self.submodules.fsm = fsm = FSM(reset_state="IDLE") + fsm.act("IDLE", + depacketizer.source.ack.eq(1), + If(depacketizer.source.stb, + depacketizer.source.ack.eq(0), + NextState("CHECK") + ) + ) + stb = Signal() + self.sync += stb.eq( + depacketizer.source.stb & + (depacketizer.source.magic == etherbone_magic) + ) + fsm.act("CHECK", + If(stb, + NextState("PRESENT") + ).Else( + NextState("DROP") + ) + ) + self.comb += [ + source.eop.eq(depacketizer.source.eop), + + source.nr.eq(depacketizer.source.nr), + + source.data.eq(depacketizer.source.data), + + source.length.eq(sink.length - etherbone_packet_header.length) + ] + fsm.act("PRESENT", + source.stb.eq(depacketizer.source.stb), + depacketizer.source.ack.eq(source.ack), + If(source.stb & source.eop & source.ack, + NextState("IDLE") + ) + ) + fsm.act("DROP", + depacketizer.source.ack.eq(1), + If(depacketizer.source.stb & + depacketizer.source.eop & + depacketizer.source.ack, + NextState("IDLE") + ) + ) + + +class EtherbonePacket(Module): + def __init__(self, port_sink, port_source): + self.submodules.tx = tx = EtherbonePacketTX() + self.submodules.rx = rx = EtherbonePacketRX() + self.comb += [ + tx.source.connect(port_sink), + port_source.connect(rx.sink) + ] + self.sink, self.source = self.tx.sink, self.rx.source + +# etherbone record + +class EtherboneRecordPacketizer(Packetizer): + def __init__(self): + Packetizer.__init__(self, + etherbone_record_description(32), + etherbone_packet_user_description(32), + etherbone_record_header) + + +class EtherboneRecordDepacketizer(Depacketizer): + def __init__(self): + Depacketizer.__init__(self, + etherbone_packet_user_description(32), + etherbone_record_description(32), + etherbone_record_header) + + +class EtherboneRecordReceiver(Module): + def __init__(self, buffer_depth=256): + self.sink = sink = stream.Endpoint(etherbone_record_description(32)) + self.source = source = stream.Endpoint(etherbone_mmap_description(32)) + + # # # + + fifo = stream.SyncFIFO(etherbone_record_description(32), buffer_depth, + buffered=True) + self.submodules += fifo + self.comb += sink.connect(fifo.sink) + + base_addr = Signal(32) + base_addr_update = Signal() + self.sync += If(base_addr_update, base_addr.eq(fifo.source.data)) + + counter = Signal(max=512) + counter_reset = Signal() + counter_ce = Signal() + self.sync += \ + If(counter_reset, + counter.eq(0) + ).Elif(counter_ce, + counter.eq(counter + 1) + ) + + self.submodules.fsm = fsm = FSM(reset_state="IDLE") + fsm.act("IDLE", + fifo.source.ack.eq(1), + counter_reset.eq(1), + If(fifo.source.stb, + base_addr_update.eq(1), + If(fifo.source.wcount, + NextState("RECEIVE_WRITES") + ).Elif(fifo.source.rcount, + NextState("RECEIVE_READS") + + ) + ) + ) + fsm.act("RECEIVE_WRITES", + source.stb.eq(fifo.source.stb), + source.eop.eq(counter == fifo.source.wcount-1), + source.count.eq(fifo.source.wcount), + source.be.eq(fifo.source.byte_enable), + source.addr.eq(base_addr[2:] + counter), + source.we.eq(1), + source.data.eq(fifo.source.data), + fifo.source.ack.eq(source.ack), + If(source.stb & source.ack, + counter_ce.eq(1), + If(source.eop, + If(fifo.source.rcount, + NextState("RECEIVE_BASE_RET_ADDR") + ).Else( + NextState("IDLE") + ) + ) + ) + ) + fsm.act("RECEIVE_BASE_RET_ADDR", + counter_reset.eq(1), + If(fifo.source.stb, + base_addr_update.eq(1), + NextState("RECEIVE_READS") + ) + ) + fsm.act("RECEIVE_READS", + source.stb.eq(fifo.source.stb), + source.eop.eq(counter == fifo.source.rcount-1), + source.count.eq(fifo.source.rcount), + source.base_addr.eq(base_addr), + source.addr.eq(fifo.source.data[2:]), + fifo.source.ack.eq(source.ack), + If(source.stb & source.ack, + counter_ce.eq(1), + If(source.eop, + NextState("IDLE") + ) + ) + ) + + +class EtherboneRecordSender(Module): + def __init__(self, buffer_depth=256): + self.sink = sink = stream.Endpoint(etherbone_mmap_description(32)) + self.source = source = stream.Endpoint(etherbone_record_description(32)) + + # # # + + pbuffer = stream.SyncFIFO(etherbone_mmap_description(32), buffer_depth) + self.submodules += pbuffer + self.comb += sink.connect(pbuffer.sink) + + self.submodules.fsm = fsm = FSM(reset_state="IDLE") + fsm.act("IDLE", + pbuffer.source.ack.eq(1), + If(pbuffer.source.stb, + pbuffer.source.ack.eq(0), + NextState("SEND_BASE_ADDRESS") + ) + ) + self.comb += [ + source.byte_enable.eq(pbuffer.source.be), + If(pbuffer.source.we, + source.wcount.eq(pbuffer.source.count) + ).Else( + source.rcount.eq(pbuffer.source.count) + ) + ] + + fsm.act("SEND_BASE_ADDRESS", + source.stb.eq(pbuffer.source.stb), + source.eop.eq(0), + source.data.eq(pbuffer.source.base_addr), + If(source.ack, + NextState("SEND_DATA") + ) + ) + fsm.act("SEND_DATA", + source.stb.eq(pbuffer.source.stb), + source.eop.eq(pbuffer.source.eop), + source.data.eq(pbuffer.source.data), + If(source.stb & source.ack, + pbuffer.source.ack.eq(1), + If(source.eop, + NextState("IDLE") + ) + ) + ) + + +class EtherboneRecord(Module): + def __init__(self): + self.sink = sink = stream.Endpoint(etherbone_packet_user_description(32)) + self.source = source = stream.Endpoint(etherbone_packet_user_description(32)) + + # # # + + # receive record, decode it and generate mmap stream + self.submodules.depacketizer = depacketizer = EtherboneRecordDepacketizer() + self.submodules.receiver = receiver = EtherboneRecordReceiver() + self.comb += [ + sink.connect(depacketizer.sink), + depacketizer.source.connect(receiver.sink) + ] + + # receive mmap stream, encode it and send records + self.submodules.sender = sender = EtherboneRecordSender() + self.submodules.packetizer = packetizer = EtherboneRecordPacketizer() + self.comb += [ + sender.source.connect(packetizer.sink), + packetizer.source.connect(source), + source.length.eq(etherbone_record_header.length + + (sender.source.wcount != 0)*4 + sender.source.wcount*4 + + (sender.source.rcount != 0)*4 + sender.source.rcount*4) + ] + + +# etherbone wishbone + +class EtherboneWishboneMaster(Module): + def __init__(self): + self.sink = sink = stream.Endpoint(etherbone_mmap_description(32)) + self.source = source = stream.Endpoint(etherbone_mmap_description(32)) + self.bus = bus = wishbone.Interface() + + # # # + + data = Signal(32) + data_update = Signal() + self.sync += If(data_update, data.eq(bus.dat_r)) + + self.submodules.fsm = fsm = FSM(reset_state="IDLE") + fsm.act("IDLE", + sink.ack.eq(1), + If(sink.stb, + sink.ack.eq(0), + If(sink.we, + NextState("WRITE_DATA") + ).Else( + NextState("READ_DATA") + ) + ) + ) + fsm.act("WRITE_DATA", + bus.adr.eq(sink.addr), + bus.dat_w.eq(sink.data), + bus.sel.eq(sink.be), + bus.stb.eq(sink.stb), + bus.we.eq(1), + bus.cyc.eq(1), + If(bus.stb & bus.ack, + sink.ack.eq(1), + If(sink.eop, + NextState("IDLE") + ) + ) + ) + fsm.act("READ_DATA", + bus.adr.eq(sink.addr), + bus.sel.eq(sink.be), + bus.stb.eq(sink.stb), + bus.cyc.eq(1), + If(bus.stb & bus.ack, + data_update.eq(1), + NextState("SEND_DATA") + ) + ) + fsm.act("SEND_DATA", + source.stb.eq(sink.stb), + source.eop.eq(sink.eop), + source.base_addr.eq(sink.base_addr), + source.addr.eq(sink.addr), + source.count.eq(sink.count), + source.be.eq(sink.be), + source.we.eq(1), + source.data.eq(data), + If(source.stb & source.ack, + sink.ack.eq(1), + If(source.eop, + NextState("IDLE") + ).Else( + NextState("READ_DATA") + ) + ) + ) + + +class EtherboneWishboneSlave(Module): + def __init__(self): + self.bus = bus = wishbone.Interface() + self.sink = sink = stream.Endpoint(etherbone_mmap_description(32)) + self.source = source = stream.Endpoint(etherbone_mmap_description(32)) + + # # # + + self.submodules.fsm = fsm = FSM(reset_state="IDLE") + fsm.act("IDLE", + sink.ack.eq(1), + If(bus.stb & bus.cyc, + If(bus.we, + NextState("SEND_WRITE") + ).Else( + NextState("SEND_READ") + ) + ) + ) + fsm.act("SEND_WRITE", + source.stb.eq(1), + source.eop.eq(1), + source.base_addr[2:].eq(bus.adr), + source.count.eq(1), + source.be.eq(bus.sel), + source.we.eq(1), + source.data.eq(bus.dat_w), + If(source.stb & source.ack, + bus.ack.eq(1), + NextState("IDLE") + ) + ) + fsm.act("SEND_READ", + source.stb.eq(1), + source.eop.eq(1), + source.base_addr.eq(0), + source.count.eq(1), + source.be.eq(bus.sel), + source.we.eq(0), + source.data[2:].eq(bus.adr), + If(source.stb & source.ack, + NextState("WAIT_READ") + ) + ) + fsm.act("WAIT_READ", + sink.ack.eq(1), + If(sink.stb & sink.we, + bus.ack.eq(1), + bus.dat_r.eq(sink.data), + NextState("IDLE") + ) + ) + + +# etherbone + +class Etherbone(Module): + def __init__(self, mode="master"): + self.sink = sink = stream.Endpoint(user_description(32)) + self.source = source = stream.Endpoint(user_description(32)) + + # # # + + self.submodules.packet = EtherbonePacket(source, sink) + self.submodules.record = EtherboneRecord() + if mode == "master": + self.submodules.wishbone = EtherboneWishboneMaster() + elif mode == "slave": + self.submodules.wishbone = EtherboneWishboneSlave() + else: + raise ValueError + + self.comb += [ + self.packet.source.connect(self.record.sink), + self.record.source.connect(self.packet.sink), + self.record.receiver.source.connect(self.wishbone.sink), + self.wishbone.source.connect(self.record.sender.sink) + ] diff --git a/artiq/gateware/serwb/kusphy.py b/artiq/gateware/serwb/kusphy.py new file mode 100644 index 000000000..ed3716491 --- /dev/null +++ b/artiq/gateware/serwb/kusphy.py @@ -0,0 +1,321 @@ +from migen import * +from migen.genlib.resetsync import AsyncResetSynchronizer +from migen.genlib.cdc import MultiReg, PulseSynchronizer, Gearbox +from migen.genlib.misc import BitSlip + +from misoc.cores.code_8b10b import Encoder, Decoder + +from amc_rtm_link.phy import PhaseDetector + + +class KUSSerdesPLL(Module): + def __init__(self, refclk_freq, linerate, vco_div=1): + assert refclk_freq == 125e6 + assert linerate == 1.25e9 + + self.lock = Signal() + self.refclk = Signal() + self.serdes_clk = Signal() + self.serdes_20x_clk = Signal() + self.serdes_5x_clk = Signal() + + # # # + + #---------------------- + # refclk: 125MHz + # vco: 1250MHz + #---------------------- + # serdes: 31.25MHz + # serdes_20x: 625MHz + # serdes_5x: 156.25MHz + #---------------------- + self.linerate = linerate + + pll_locked = Signal() + pll_fb = Signal() + pll_serdes_clk = Signal() + pll_serdes_20x_clk = Signal() + pll_serdes_5x_clk = Signal() + self.specials += [ + Instance("PLLE2_BASE", + p_STARTUP_WAIT="FALSE", o_LOCKED=pll_locked, + + # VCO @ 1.25GHz / vco_div + p_REF_JITTER1=0.01, p_CLKIN1_PERIOD=8.0, + p_CLKFBOUT_MULT=10, p_DIVCLK_DIVIDE=vco_div, + i_CLKIN1=self.refclk, i_CLKFBIN=pll_fb, + o_CLKFBOUT=pll_fb, + + # 31.25MHz: serdes + p_CLKOUT0_DIVIDE=40//vco_div, p_CLKOUT0_PHASE=0.0, + o_CLKOUT0=pll_serdes_clk, + + # 625MHz: serdes_20x + p_CLKOUT1_DIVIDE=2//vco_div, p_CLKOUT1_PHASE=0.0, + o_CLKOUT1=pll_serdes_20x_clk, + + # 156.25MHz: serdes_5x + p_CLKOUT2_DIVIDE=8//vco_div, p_CLKOUT2_PHASE=0.0, + o_CLKOUT2=pll_serdes_5x_clk + ), + Instance("BUFG", i_I=pll_serdes_clk, o_O=self.serdes_clk), + Instance("BUFG", i_I=pll_serdes_20x_clk, o_O=self.serdes_20x_clk), + Instance("BUFG", i_I=pll_serdes_5x_clk, o_O=self.serdes_5x_clk) + ] + self.specials += MultiReg(pll_locked, self.lock) + + +class KUSSerdes(Module): + def __init__(self, pll, pads, mode="master"): + self.tx_data = Signal(32) + self.rx_data = 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_ce = Signal() + self.rx_delay_en_vtc = Signal() + + # # # + + self.submodules.encoder = ClockDomainsRenamer("serdes")( + Encoder(4, True)) + self.decoders = [ClockDomainsRenamer("serdes")( + Decoder(True)) for _ in range(4)] + self.submodules += self.decoders + + # clocking: + + # In master mode: + # - linerate/10 pll refclk provided by user + # - linerate/10 slave refclk generated on clk_pads + # In Slave mode: + # - linerate/10 pll refclk provided by clk_pads + self.clock_domains.cd_serdes = ClockDomain() + self.clock_domains.cd_serdes_5x = ClockDomain() + self.clock_domains.cd_serdes_20x = ClockDomain(reset_less=True) + self.comb += [ + self.cd_serdes.clk.eq(pll.serdes_clk), + self.cd_serdes_5x.clk.eq(pll.serdes_5x_clk), + self.cd_serdes_20x.clk.eq(pll.serdes_20x_clk) + ] + self.specials += AsyncResetSynchronizer(self.cd_serdes, ~pll.lock) + self.comb += self.cd_serdes_5x.rst.eq(self.cd_serdes.rst) + + # control/status cdc + tx_idle = Signal() + tx_comma = Signal() + rx_idle = Signal() + rx_comma = Signal() + rx_bitslip_value = Signal(6) + rx_delay_rst = Signal() + rx_delay_inc = Signal() + rx_delay_en_vtc = Signal() + rx_delay_ce = Signal() + self.specials += [ + MultiReg(self.tx_idle, tx_idle, "serdes"), + MultiReg(self.tx_comma, tx_comma, "serdes"), + MultiReg(rx_idle, self.rx_idle, "sys"), + MultiReg(rx_comma, self.rx_comma, "sys"), + MultiReg(self.rx_bitslip_value, rx_bitslip_value, "serdes"), + MultiReg(self.rx_delay_inc, rx_delay_inc, "serdes_5x"), + MultiReg(self.rx_delay_en_vtc, rx_delay_en_vtc, "serdes_5x") + ] + self.submodules.do_rx_delay_rst = PulseSynchronizer("sys", "serdes_5x") + self.comb += [ + rx_delay_rst.eq(self.do_rx_delay_rst.o), + self.do_rx_delay_rst.i.eq(self.rx_delay_rst) + ] + self.submodules.do_rx_delay_ce = PulseSynchronizer("sys", "serdes_5x") + self.comb += [ + rx_delay_ce.eq(self.do_rx_delay_ce.o), + self.do_rx_delay_ce.i.eq(self.rx_delay_ce) + ] + + # tx clock (linerate/10) + if mode == "master": + self.submodules.tx_clk_gearbox = Gearbox(40, "serdes", 8, "serdes_5x") + self.comb += self.tx_clk_gearbox.i.eq((0b1111100000 << 30) | + (0b1111100000 << 20) | + (0b1111100000 << 10) | + (0b1111100000 << 0)) + 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("serdes"), + i_CLK=ClockSignal("serdes_20x"), i_CLKDIV=ClockSignal("serdes_5x"), + i_D=self.tx_clk_gearbox.o + ), + Instance("OBUFDS", + i_I=clk_o, + o_O=pads.clk_p, + o_OB=pads.clk_n + ) + ] + + # tx datapath + # tx_data -> encoders -> gearbox -> serdes + self.submodules.tx_gearbox = Gearbox(40, "serdes", 8, "serdes_5x") + self.comb += [ + If(tx_comma, + self.encoder.k[0].eq(1), + self.encoder.d[0].eq(0xbc) + ).Else( + self.encoder.d[0].eq(self.tx_data[0:8]), + self.encoder.d[1].eq(self.tx_data[8:16]), + self.encoder.d[2].eq(self.tx_data[16:24]), + self.encoder.d[3].eq(self.tx_data[24:32]) + ) + ] + self.sync.serdes += \ + If(tx_idle, + self.tx_gearbox.i.eq(0) + ).Else( + self.tx_gearbox.i.eq(Cat(*[self.encoder.output[i] for i in range(4)])) + ) + + 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("serdes"), + i_CLK=ClockSignal("serdes_20x"), i_CLKDIV=ClockSignal("serdes_5x"), + i_D=self.tx_gearbox.o + ), + 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 += pll.refclk.eq(clk_i_bufg) + + # rx datapath + # serdes -> gearbox -> bitslip -> decoders -> rx_data + self.submodules.rx_gearbox = Gearbox(8, "serdes_5x", 40, "serdes") + self.submodules.rx_bitslip = ClockDomainsRenamer("serdes")(BitSlip(40)) + + self.submodules.phase_detector = ClockDomainsRenamer("serdes_5x")(PhaseDetector()) + + # 2 serdes for phase detection: 1 master (used for data) / 1 slave + serdes_m_i_nodelay = Signal() + serdes_s_i_nodelay = Signal() + self.specials += [ + Instance("IBUFDS_DIFF_OUT", + i_I=pads.rx_p, + i_IB=pads.rx_n, + o_O=serdes_m_i_nodelay, + o_OB=serdes_s_i_nodelay + ) + ] + + serdes_m_i_delayed = Signal() + serdes_m_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("serdes_5x"), + i_RST=rx_delay_rst, i_LOAD=0, + i_INC=rx_delay_inc, i_EN_VTC=rx_delay_en_vtc, + i_CE=rx_delay_ce, + + i_IDATAIN=serdes_m_i_nodelay, o_DATAOUT=serdes_m_i_delayed + ), + Instance("ISERDESE3", + p_DATA_WIDTH=8, + + i_D=serdes_m_i_delayed, + i_RST=ResetSignal("serdes"), + i_FIFO_RD_CLK=0, i_FIFO_RD_EN=0, + i_CLK=ClockSignal("serdes_20x"), i_CLK_B=~ClockSignal("serdes_20x"), + i_CLKDIV=ClockSignal("serdes_5x"), + o_Q=serdes_m_q + ) + ] + self.comb += self.phase_detector.mdata.eq(serdes_m_q) + + serdes_s_i_delayed = Signal() + serdes_s_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, + # Note: can't use TIME mode since not reloading DELAY_VALUE on rst... + # Got answer from Xilinx, need testing: + # https://forums.xilinx.com/xlnx/board/crawl_message?board.id=ultrascale&message.id=4699 + p_DELAY_FORMAT="COUNT", p_DELAY_SRC="IDATAIN", + p_DELAY_TYPE="VARIABLE", p_DELAY_VALUE=50, # 1/4 bit period (ambient temp) + + i_CLK=ClockSignal("serdes_5x"), + i_RST=rx_delay_rst, i_LOAD=0, + i_INC=rx_delay_inc, i_EN_VTC=rx_delay_en_vtc, + i_CE=rx_delay_ce, + + i_IDATAIN=serdes_s_i_nodelay, o_DATAOUT=serdes_s_i_delayed + ), + Instance("ISERDESE3", + p_DATA_WIDTH=8, + + i_D=serdes_s_i_delayed, + i_RST=ResetSignal("serdes"), + i_FIFO_RD_CLK=0, i_FIFO_RD_EN=0, + i_CLK=ClockSignal("serdes_20x"), i_CLK_B=~ClockSignal("serdes_20x"), + i_CLKDIV=ClockSignal("serdes_5x"), + o_Q=serdes_s_q + ) + ] + self.comb += self.phase_detector.sdata.eq(~serdes_s_q) + + self.comb += [ + self.rx_gearbox.i.eq(serdes_m_q), + self.rx_bitslip.value.eq(rx_bitslip_value), + self.rx_bitslip.i.eq(self.rx_gearbox.o), + self.decoders[0].input.eq(self.rx_bitslip.o[0:10]), + self.decoders[1].input.eq(self.rx_bitslip.o[10:20]), + self.decoders[2].input.eq(self.rx_bitslip.o[20:30]), + self.decoders[3].input.eq(self.rx_bitslip.o[30:40]), + self.rx_data.eq(Cat(*[self.decoders[i].d for i in range(4)])), + rx_idle.eq(self.rx_bitslip.o == 0), + rx_comma.eq(((self.decoders[0].d == 0xbc) & (self.decoders[0].k == 1)) & + ((self.decoders[1].d == 0x00) & (self.decoders[1].k == 0)) & + ((self.decoders[2].d == 0x00) & (self.decoders[2].k == 0)) & + ((self.decoders[3].d == 0x00) & (self.decoders[3].k == 0))) + + ] diff --git a/artiq/gateware/serwb/packet.py b/artiq/gateware/serwb/packet.py new file mode 100644 index 000000000..a1d2b00fb --- /dev/null +++ b/artiq/gateware/serwb/packet.py @@ -0,0 +1,175 @@ +from math import ceil +from copy import copy +from collections import OrderedDict + +from migen import * +from migen.genlib.misc import WaitTimer + +from misoc.interconnect import stream +from misoc.interconnect.stream import EndpointDescription + + +def reverse_bytes(signal): + n = ceil(len(signal)/8) + return Cat(iter([signal[i*8:(i+1)*8] for i in reversed(range(n))])) + + +class HeaderField: + def __init__(self, byte, offset, width): + self.byte = byte + self.offset = offset + self.width = width + + +class Header: + def __init__(self, fields, length, swap_field_bytes=True): + self.fields = fields + self.length = length + self.swap_field_bytes = swap_field_bytes + + def get_layout(self): + layout = [] + for k, v in sorted(self.fields.items()): + layout.append((k, v.width)) + return layout + + def get_field(self, obj, name, width): + if "_lsb" in name: + field = getattr(obj, name.replace("_lsb", ""))[:width] + elif "_msb" in name: + field = getattr(obj, name.replace("_msb", ""))[width:2*width] + else: + field = getattr(obj, name) + if len(field) != width: + raise ValueError("Width mismatch on " + name + " field") + return field + + def encode(self, obj, signal): + r = [] + for k, v in sorted(self.fields.items()): + start = v.byte*8 + v.offset + end = start + v.width + field = self.get_field(obj, k, v.width) + if self.swap_field_bytes: + field = reverse_bytes(field) + r.append(signal[start:end].eq(field)) + return r + + def decode(self, signal, obj): + r = [] + for k, v in sorted(self.fields.items()): + start = v.byte*8 + v.offset + end = start + v.width + field = self.get_field(obj, k, v.width) + if self.swap_field_bytes: + r.append(field.eq(reverse_bytes(signal[start:end]))) + else: + r.append(field.eq(signal[start:end])) + return r + +def phy_description(dw): + layout = [("data", dw)] + return stream.EndpointDescription(layout) + + +def user_description(dw): + layout = [ + ("data", 32), + ("length", 32) + ] + return stream.EndpointDescription(layout) + + +class Packetizer(Module): + def __init__(self): + self.sink = sink = stream.Endpoint(user_description(32)) + self.source = source = stream.Endpoint(phy_description(32)) + + # # # + + # Packet description + # - preamble : 4 bytes + # - length : 4 bytes + # - payload + + fsm = FSM(reset_state="IDLE") + self.submodules += fsm + + fsm.act("IDLE", + If(sink.stb, + NextState("INSERT_PREAMBLE") + ) + ) + fsm.act("INSERT_PREAMBLE", + source.stb.eq(1), + source.data.eq(0x5aa55aa5), + If(source.ack, + NextState("INSERT_LENGTH") + ) + ) + fsm.act("INSERT_LENGTH", + source.stb.eq(1), + source.data.eq(sink.length), + If(source.ack, + NextState("COPY") + ) + ) + fsm.act("COPY", + source.stb.eq(sink.stb), + source.data.eq(sink.data), + sink.ack.eq(source.ack), + If(source.ack & sink.eop, + NextState("IDLE") + ) + ) + + +class Depacketizer(Module): + def __init__(self, clk_freq, timeout=10): + self.sink = sink = stream.Endpoint(phy_description(32)) + self.source = source = stream.Endpoint(user_description(32)) + + # # # + + # Packet description + # - preamble : 4 bytes + # - length : 4 bytes + # - payload + + fsm = FSM(reset_state="IDLE") + self.submodules += fsm + + self.submodules.timer = WaitTimer(clk_freq*timeout) + self.comb += self.timer.wait.eq(~fsm.ongoing("IDLE")) + + fsm.act("IDLE", + sink.ack.eq(1), + If(sink.stb & (sink.data == 0x5aa55aa5), + NextState("RECEIVE_LENGTH") + ) + ) + fsm.act("RECEIVE_LENGTH", + sink.ack.eq(1), + If(sink.stb, + NextValue(source.length, sink.data), + NextState("COPY") + ) + ) + eop = Signal() + cnt = Signal(32) + fsm.act("COPY", + source.stb.eq(sink.stb), + source.eop.eq(eop), + source.data.eq(sink.data), + sink.ack.eq(source.ack), + If((source.stb & source.ack & eop) | self.timer.done, + NextState("IDLE") + ) + ) + self.sync += \ + If(fsm.ongoing("IDLE"), + cnt.eq(0) + ).Elif(source.stb & source.ack, + cnt.eq(cnt + 1) + ) + self.comb += eop.eq(cnt == source.length[2:] - 1) diff --git a/artiq/gateware/serwb/phy.py b/artiq/gateware/serwb/phy.py new file mode 100644 index 000000000..39640208d --- /dev/null +++ b/artiq/gateware/serwb/phy.py @@ -0,0 +1,345 @@ +from migen import * +from migen.genlib.cdc import MultiReg, PulseSynchronizer +from migen.genlib.misc import WaitTimer + +from misoc.interconnect.csr import * + + +class PhaseDetector(Module, AutoCSR): + def __init__(self, nbits=8): + self.mdata = Signal(8) + self.sdata = Signal(8) + + self.reset = Signal() + self.too_early = Signal() + self.too_late = Signal() + + # # # + + # Ideal sampling (middle of the eye): + # _____ _____ _____ + # | |_____| |_____| |_____| data + # + + + + + + master sampling + # - - - - - - slave sampling (90°/bit period) + # Since taps are fixed length delays, this ideal case is not possible + # and we will fall in the 2 following possible cases: + # + # 1) too late sampling (idelay needs to be decremented): + # _____ _____ _____ + # | |_____| |_____| |_____| data + # + + + + + + master sampling + # - - - - - - slave sampling (90°/bit period) + # On mdata transition, mdata != sdata + # + # + # 2) too early sampling (idelay needs to be incremented): + # _____ _____ _____ + # | |_____| |_____| |_____| data + # + + + + + + master sampling + # - - - - - - slave sampling (90°/bit period) + # On mdata transition, mdata == sdata + + transition = Signal() + inc = Signal() + dec = Signal() + + # Find transition + mdata_d = Signal(8) + self.sync.serdes_5x += mdata_d.eq(self.mdata) + self.comb += transition.eq(mdata_d != self.mdata) + + + # Find what to do + self.comb += [ + inc.eq(transition & (self.mdata == self.sdata)), + dec.eq(transition & (self.mdata != self.sdata)) + ] + + # Error accumulator + lateness = Signal(nbits, reset=2**(nbits - 1)) + too_late = Signal() + too_early = Signal() + reset_lateness = Signal() + self.comb += [ + too_late.eq(lateness == (2**nbits - 1)), + too_early.eq(lateness == 0) + ] + self.sync.serdes_5x += [ + If(reset_lateness, + lateness.eq(2**(nbits - 1)) + ).Elif(~too_late & ~too_early, + If(inc, lateness.eq(lateness - 1)), + If(dec, lateness.eq(lateness + 1)) + ) + ] + + # control / status cdc + self.specials += [ + MultiReg(too_early, self.too_early), + MultiReg(too_late, self.too_late) + ] + self.submodules.do_reset_lateness = PulseSynchronizer("sys", "serdes_5x") + self.comb += [ + reset_lateness.eq(self.do_reset_lateness.o), + self.do_reset_lateness.i.eq(self.reset) + ] + + +# Master <--> Slave synchronization: +# 1) Master sends idle pattern (zeroes) to reset Slave. +# 2) Master sends K28.5 commas to allow Slave to calibrate, Slave sends idle pattern. +# 3) Slave sends K28.5 commas to allow Master to calibrate, Master sends K28.5 commas. +# 4) Master stops sending K28.5 commas. +# 5) Slave stops sending K25.5 commas. +# 6) Link is ready. + +class SerdesMasterInit(Module): + def __init__(self, serdes, taps): + self.reset = Signal() + self.error = Signal() + self.ready = Signal() + + # # # + + self.delay = delay = Signal(max=taps) + self.delay_found = delay_found = Signal() + self.bitslip = bitslip = Signal(max=40) + self.bitslip_found = bitslip_found = Signal() + + timer = WaitTimer(8192) + self.submodules += timer + + self.submodules.fsm = fsm = ResetInserter()(FSM(reset_state="IDLE")) + self.comb += self.fsm.reset.eq(self.reset) + + phase_detector_too_early_last = Signal() + + fsm.act("IDLE", + NextValue(delay_found, 0), + NextValue(delay, 0), + serdes.rx_delay_rst.eq(1), + NextValue(bitslip_found, 0), + NextValue(bitslip, 0), + NextValue(phase_detector_too_early_last, 0), + NextState("RESET_SLAVE"), + serdes.tx_idle.eq(1) + ) + fsm.act("RESET_SLAVE", + timer.wait.eq(1), + If(timer.done, + timer.wait.eq(0), + NextState("SEND_PATTERN") + ), + serdes.tx_idle.eq(1) + ) + fsm.act("SEND_PATTERN", + If(~serdes.rx_idle, + NextState("WAIT_STABLE") + ), + serdes.tx_comma.eq(1) + ) + fsm.act("WAIT_STABLE", + timer.wait.eq(1), + If(timer.done, + timer.wait.eq(0), + serdes.phase_detector.reset.eq(1), + If(~delay_found, + NextState("CHECK_PHASE") + ).Else( + NextState("CHECK_PATTERN") + ), + ), + serdes.tx_comma.eq(1) + ) + fsm.act("CHECK_PHASE", + # Since we are always incrementing delay, + # ideal sampling is found when phase detector + # transitions from too_early to too_late + If(serdes.phase_detector.too_late & + phase_detector_too_early_last, + NextValue(delay_found, 1), + NextState("CHECK_PATTERN") + ).Elif(serdes.phase_detector.too_late | + serdes.phase_detector.too_early, + NextValue(phase_detector_too_early_last, + serdes.phase_detector.too_early), + NextState("INC_DELAY") + ), + serdes.tx_comma.eq(1) + ) + fsm.act("INC_DELAY", + If(delay == (taps - 1), + NextState("ERROR") + ).Else( + NextValue(delay, delay + 1), + serdes.rx_delay_inc.eq(1), + serdes.rx_delay_ce.eq(1), + NextState("WAIT_STABLE") + ), + serdes.tx_comma.eq(1) + ) + fsm.act("CHECK_PATTERN", + If(serdes.rx_comma, + timer.wait.eq(1), + If(timer.done, + NextValue(bitslip_found, 1), + NextState("READY") + ) + ).Else( + NextState("INC_BITSLIP") + ), + serdes.tx_comma.eq(1) + ) + self.comb += serdes.rx_bitslip_value.eq(bitslip) + fsm.act("INC_BITSLIP", + If(bitslip == (40 - 1), + NextState("ERROR") + ).Else( + NextValue(bitslip, bitslip + 1), + NextState("WAIT_STABLE") + ), + serdes.tx_comma.eq(1) + ) + fsm.act("READY", + self.ready.eq(1) + ) + fsm.act("ERROR", + self.error.eq(1) + ) + + +class SerdesSlaveInit(Module, AutoCSR): + def __init__(self, serdes, taps): + self.reset = Signal() + self.ready = Signal() + self.error = Signal() + + # # # + + self.delay = delay = Signal(max=taps) + self.delay_found = delay_found = Signal() + self.bitslip = bitslip = Signal(max=40) + self.bitslip_found = bitslip_found = Signal() + + timer = WaitTimer(1024) + self.submodules += timer + + self.comb += self.reset.eq(serdes.rx_idle) + + self.submodules.fsm = fsm = ResetInserter()(FSM(reset_state="IDLE")) + + phase_detector_too_early_last = Signal() + + fsm.act("IDLE", + NextValue(delay_found, 0), + NextValue(delay, 0), + serdes.rx_delay_rst.eq(1), + NextValue(bitslip_found, 0), + NextValue(bitslip, 0), + NextValue(phase_detector_too_early_last, 0), + NextState("WAIT_STABLE"), + serdes.tx_idle.eq(1) + ) + fsm.act("WAIT_STABLE", + timer.wait.eq(1), + If(timer.done, + timer.wait.eq(0), + serdes.phase_detector.reset.eq(1), + If(~delay_found, + NextState("CHECK_PHASE") + ).Else( + NextState("CHECK_PATTERN") + ), + ), + serdes.tx_idle.eq(1) + ) + fsm.act("CHECK_PHASE", + # Since we are always incrementing delay, + # ideal sampling is found when phase detector + # transitions from too_early to too_late + If(serdes.phase_detector.too_late & + phase_detector_too_early_last, + NextValue(delay_found, 1), + NextState("CHECK_PATTERN") + ).Elif(serdes.phase_detector.too_late | + serdes.phase_detector.too_early, + NextValue(phase_detector_too_early_last, + serdes.phase_detector.too_early), + NextState("INC_DELAY") + ), + serdes.tx_idle.eq(1) + ) + fsm.act("INC_DELAY", + If(delay == (taps - 1), + NextState("ERROR") + ).Else( + NextValue(delay, delay + 1), + serdes.rx_delay_inc.eq(1), + serdes.rx_delay_ce.eq(1), + NextState("WAIT_STABLE") + ), + serdes.tx_idle.eq(1) + ) + fsm.act("CHECK_PATTERN", + If(serdes.rx_comma, + timer.wait.eq(1), + If(timer.done, + NextValue(bitslip_found, 1), + NextState("SEND_PATTERN") + ) + ).Else( + NextState("INC_BITSLIP") + ), + serdes.tx_idle.eq(1) + ) + self.comb += serdes.rx_bitslip_value.eq(bitslip) + fsm.act("INC_BITSLIP", + If(bitslip == (40 - 1), + NextState("ERROR") + ).Else( + NextValue(bitslip, bitslip + 1), + NextState("WAIT_STABLE") + ), + serdes.tx_idle.eq(1) + ) + fsm.act("SEND_PATTERN", + timer.wait.eq(1), + If(timer.done, + If(~serdes.rx_comma, + NextState("READY") + ) + ), + serdes.tx_comma.eq(1) + ) + fsm.act("READY", + self.ready.eq(1) + ) + fsm.act("ERROR", + self.error.eq(1) + ) + + +class SerdesControl(Module, AutoCSR): + def __init__(self, init, mode="master"): + if mode == "master": + self.reset = CSR() + self.ready = CSRStatus() + self.error = CSRStatus() + + self.delay = CSRStatus(9) + self.delay_found = CSRStatus() + self.bitslip = CSRStatus(6) + self.bitslip_found = CSRStatus() + + # # # + + if mode == "master": + self.comb += init.reset.eq(self.reset.re) + self.comb += [ + self.ready.status.eq(init.ready), + self.error.status.eq(init.error), + self.delay_found.status.eq(init.delay_found), + self.delay.status.eq(init.delay), + self.bitslip_found.status.eq(init.bitslip_found), + self.bitslip.status.eq(init.bitslip) + ] diff --git a/artiq/gateware/serwb/s7phy.py b/artiq/gateware/serwb/s7phy.py new file mode 100644 index 000000000..861c38d05 --- /dev/null +++ b/artiq/gateware/serwb/s7phy.py @@ -0,0 +1,325 @@ +from migen import * +from migen.genlib.resetsync import AsyncResetSynchronizer +from migen.genlib.cdc import MultiReg, Gearbox +from migen.genlib.misc import BitSlip + +from misoc.cores.code_8b10b import Encoder, Decoder + +from amc_rtm_link.phy import PhaseDetector + + +class S7SerdesPLL(Module): + def __init__(self, refclk_freq, linerate, vco_div=1): + assert refclk_freq == 125e6 + assert linerate == 1.25e9 + + self.lock = Signal() + self.refclk = Signal() + self.serdes_clk = Signal() + self.serdes_20x_clk = Signal() + self.serdes_5x_clk = Signal() + + # # # + + #---------------------- + # refclk: 125MHz + # vco: 1250MHz + #---------------------- + # serdes: 31.25MHz + # serdes_20x: 625MHz + # serdes_5x: 156.25MHz + #---------------------- + self.linerate = linerate + + pll_locked = Signal() + pll_fb = Signal() + pll_serdes_clk = Signal() + pll_serdes_20x_clk = Signal() + pll_serdes_5x_clk = Signal() + self.specials += [ + Instance("PLLE2_BASE", + p_STARTUP_WAIT="FALSE", o_LOCKED=pll_locked, + + # VCO @ 1.25GHz / vco_div + p_REF_JITTER1=0.01, p_CLKIN1_PERIOD=8.0, + p_CLKFBOUT_MULT=10, p_DIVCLK_DIVIDE=vco_div, + i_CLKIN1=self.refclk, i_CLKFBIN=pll_fb, + o_CLKFBOUT=pll_fb, + + # 31.25MHz: serdes + p_CLKOUT0_DIVIDE=40//vco_div, p_CLKOUT0_PHASE=0.0, + o_CLKOUT0=pll_serdes_clk, + + # 625MHz: serdes_20x + p_CLKOUT1_DIVIDE=2//vco_div, p_CLKOUT1_PHASE=0.0, + o_CLKOUT1=pll_serdes_20x_clk, + + # 156.25MHz: serdes_5x + p_CLKOUT2_DIVIDE=8//vco_div, p_CLKOUT2_PHASE=0.0, + o_CLKOUT2=pll_serdes_5x_clk + ), + Instance("BUFG", i_I=pll_serdes_clk, o_O=self.serdes_clk), + Instance("BUFG", i_I=pll_serdes_20x_clk, o_O=self.serdes_20x_clk), + Instance("BUFG", i_I=pll_serdes_5x_clk, o_O=self.serdes_5x_clk) + ] + self.specials += MultiReg(pll_locked, self.lock) + + +class S7Serdes(Module): + def __init__(self, pll, pads, mode="master"): + self.tx_data = Signal(32) + self.rx_data = 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_ce = Signal() + + # # # + + self.submodules.encoder = ClockDomainsRenamer("serdes")( + Encoder(4, True)) + self.decoders = [ClockDomainsRenamer("serdes")( + Decoder(True)) for _ in range(4)] + self.submodules += self.decoders + + # clocking: + + # In master mode: + # - linerate/10 pll refclk provided by user + # - linerate/10 slave refclk generated on clk_pads + # In Slave mode: + # - linerate/10 pll refclk provided by clk_pads + self.clock_domains.cd_serdes = ClockDomain() + self.clock_domains.cd_serdes_5x = ClockDomain() + self.clock_domains.cd_serdes_20x = ClockDomain(reset_less=True) + self.comb += [ + self.cd_serdes.clk.eq(pll.serdes_clk), + self.cd_serdes_5x.clk.eq(pll.serdes_5x_clk), + self.cd_serdes_20x.clk.eq(pll.serdes_20x_clk) + ] + self.specials += AsyncResetSynchronizer(self.cd_serdes, ~pll.lock) + self.comb += self.cd_serdes_5x.rst.eq(self.cd_serdes.rst) + + # control/status cdc + tx_idle = Signal() + tx_comma = Signal() + rx_idle = Signal() + rx_comma = Signal() + rx_bitslip_value = Signal(6) + self.specials += [ + MultiReg(self.tx_idle, tx_idle, "serdes"), + MultiReg(self.tx_comma, tx_comma, "serdes"), + MultiReg(rx_idle, self.rx_idle, "sys"), + MultiReg(rx_comma, self.rx_comma, "sys") + ] + self.specials += MultiReg(self.rx_bitslip_value, rx_bitslip_value, "serdes"), + + # tx clock (linerate/10) + if mode == "master": + self.submodules.tx_clk_gearbox = Gearbox(40, "serdes", 8, "serdes_5x") + self.comb += self.tx_clk_gearbox.i.eq((0b1111100000 << 30) | + (0b1111100000 << 20) | + (0b1111100000 << 10) | + (0b1111100000 << 0)) + 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("serdes"), + i_CLK=ClockSignal("serdes_20x"), i_CLKDIV=ClockSignal("serdes_5x"), + i_D1=self.tx_clk_gearbox.o[0], i_D2=self.tx_clk_gearbox.o[1], + i_D3=self.tx_clk_gearbox.o[2], i_D4=self.tx_clk_gearbox.o[3], + i_D5=self.tx_clk_gearbox.o[4], i_D6=self.tx_clk_gearbox.o[5], + i_D7=self.tx_clk_gearbox.o[6], i_D8=self.tx_clk_gearbox.o[7] + ), + Instance("OBUFDS", + i_I=clk_o, + o_O=pads.clk_p, + o_OB=pads.clk_n + ) + ] + + # tx datapath + # tx_data -> encoders -> gearbox -> serdes + self.submodules.tx_gearbox = Gearbox(40, "serdes", 8, "serdes_5x") + self.comb += [ + If(tx_comma, + self.encoder.k[0].eq(1), + self.encoder.d[0].eq(0xbc) + ).Else( + self.encoder.d[0].eq(self.tx_data[0:8]), + self.encoder.d[1].eq(self.tx_data[8:16]), + self.encoder.d[2].eq(self.tx_data[16:24]), + self.encoder.d[3].eq(self.tx_data[24:32]) + ) + ] + self.sync.serdes += \ + If(tx_idle, + self.tx_gearbox.i.eq(0) + ).Else( + self.tx_gearbox.i.eq(Cat(*[self.encoder.output[i] for i in range(4)])) + ) + + 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("serdes"), + i_CLK=ClockSignal("serdes_20x"), i_CLKDIV=ClockSignal("serdes_5x"), + i_D1=self.tx_gearbox.o[0], i_D2=self.tx_gearbox.o[1], + i_D3=self.tx_gearbox.o[2], i_D4=self.tx_gearbox.o[3], + i_D5=self.tx_gearbox.o[4], i_D6=self.tx_gearbox.o[5], + i_D7=self.tx_gearbox.o[6], i_D8=self.tx_gearbox.o[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 += pll.refclk.eq(clk_i_bufg) + + # rx datapath + # serdes -> gearbox -> bitslip -> decoders -> rx_data + self.submodules.rx_gearbox = Gearbox(8, "serdes_5x", 40, "serdes") + self.submodules.rx_bitslip = ClockDomainsRenamer("serdes")(BitSlip(40)) + + self.submodules.phase_detector = ClockDomainsRenamer("serdes_5x")(PhaseDetector()) + + # 2 serdes for phase detection: 1 master (used for data) / 1 slave + serdes_m_i_nodelay = Signal() + serdes_s_i_nodelay = Signal() + self.specials += [ + Instance("IBUFDS_DIFF_OUT", + i_I=pads.rx_p, + i_IB=pads.rx_n, + o_O=serdes_m_i_nodelay, + o_OB=serdes_s_i_nodelay + ) + ] + + serdes_m_i_delayed = Signal() + serdes_m_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_ce, + i_LDPIPEEN=0, i_INC=self.rx_delay_inc, + + i_IDATAIN=serdes_m_i_nodelay, o_DATAOUT=serdes_m_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_m_i_delayed, + i_CE1=1, + i_RST=ResetSignal("serdes"), + i_CLK=ClockSignal("serdes_20x"), i_CLKB=~ClockSignal("serdes_20x"), + i_CLKDIV=ClockSignal("serdes_5x"), + i_BITSLIP=0, + o_Q8=serdes_m_q[0], o_Q7=serdes_m_q[1], + o_Q6=serdes_m_q[2], o_Q5=serdes_m_q[3], + o_Q4=serdes_m_q[4], o_Q3=serdes_m_q[5], + o_Q2=serdes_m_q[6], o_Q1=serdes_m_q[7] + ) + ] + self.comb += self.phase_detector.mdata.eq(serdes_m_q) + + serdes_s_i_delayed = Signal() + serdes_s_q = Signal(8) + serdes_s_idelay_value = int(1/(4*pll.linerate)/78e-12) # 1/4 bit period + assert serdes_s_idelay_value < 32 + 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=serdes_s_idelay_value, + + i_C=ClockSignal(), + i_LD=self.rx_delay_rst, + i_CE=self.rx_delay_ce, + i_LDPIPEEN=0, i_INC=self.rx_delay_inc, + + i_IDATAIN=serdes_s_i_nodelay, o_DATAOUT=serdes_s_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_s_i_delayed, + i_CE1=1, + i_RST=ResetSignal("serdes"), + i_CLK=ClockSignal("serdes_20x"), i_CLKB=~ClockSignal("serdes_20x"), + i_CLKDIV=ClockSignal("serdes_5x"), + i_BITSLIP=0, + o_Q8=serdes_s_q[0], o_Q7=serdes_s_q[1], + o_Q6=serdes_s_q[2], o_Q5=serdes_s_q[3], + o_Q4=serdes_s_q[4], o_Q3=serdes_s_q[5], + o_Q2=serdes_s_q[6], o_Q1=serdes_s_q[7] + ) + ] + self.comb += self.phase_detector.sdata.eq(~serdes_s_q) + + self.comb += [ + self.rx_gearbox.i.eq(serdes_m_q), + self.rx_bitslip.value.eq(rx_bitslip_value), + self.rx_bitslip.i.eq(self.rx_gearbox.o), + self.decoders[0].input.eq(self.rx_bitslip.o[0:10]), + self.decoders[1].input.eq(self.rx_bitslip.o[10:20]), + self.decoders[2].input.eq(self.rx_bitslip.o[20:30]), + self.decoders[3].input.eq(self.rx_bitslip.o[30:40]), + self.rx_data.eq(Cat(*[self.decoders[i].d for i in range(4)])), + rx_idle.eq(self.rx_bitslip.o == 0), + rx_comma.eq(((self.decoders[0].d == 0xbc) & (self.decoders[0].k == 1)) & + ((self.decoders[1].d == 0x00) & (self.decoders[1].k == 0)) & + ((self.decoders[2].d == 0x00) & (self.decoders[2].k == 0)) & + ((self.decoders[3].d == 0x00) & (self.decoders[3].k == 0))) + + ]