forked from M-Labs/artiq
Add serial Wishbone bridge
Files copied directly from https://github.com/enjoy-digital/sayma_test @ 9ce2cba87896d056819dc2edc54f0453a86162c3
This commit is contained in:
parent
d6b624dfbe
commit
44dc76e42e
|
@ -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)
|
||||||
|
]
|
|
@ -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)))
|
||||||
|
|
||||||
|
]
|
|
@ -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)
|
|
@ -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)
|
||||||
|
]
|
|
@ -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)))
|
||||||
|
|
||||||
|
]
|
Loading…
Reference in New Issue