396 lines
12 KiB
Python
396 lines
12 KiB
Python
from migen import *
|
|
from sync_serdes import MultiLineRX, MultiLineTX
|
|
from multi_coders import MultiEncoder, CrossbarDecoder
|
|
from migen.build.platforms.sinara import kasli, efc
|
|
from eem_helpers import generate_pads
|
|
from kasli_crg import TransceiverCRG
|
|
from uart import UART
|
|
from migen.genlib.fifo import SyncFIFO
|
|
|
|
|
|
class BiDirectionalIO(Module):
|
|
def __init__(self, i_pads, o_pads):
|
|
self.i = Signal(4)
|
|
self.o = Signal(4)
|
|
self.t = Signal(4)
|
|
|
|
for i in range(4):
|
|
self.specials += Instance("OBUFTDS",
|
|
i_I=self.i[i],
|
|
o_O=o_pads[i].p,
|
|
o_OB=o_pads[i].n,
|
|
# Always chain the 3-states input to serializer
|
|
# Vivado will complain otherwise
|
|
i_T=self.t[i],
|
|
)
|
|
|
|
for i in range(4):
|
|
self.specials += Instance("IBUFDS",
|
|
i_I=i_pads[i].p,
|
|
i_IB=i_pads[i].n,
|
|
o_O=self.o[i],
|
|
)
|
|
|
|
|
|
class ClockOut(Module):
|
|
def __init__(self, clk_out):
|
|
self.clk = Signal()
|
|
# clk_buffer = Signal()
|
|
# clk_se = Signal()
|
|
|
|
# self.specials += Instance("BUFIO",
|
|
# i_I=self.clk,
|
|
# o_O=clk_buffer,
|
|
# )
|
|
|
|
# self.specials += Instance("ODDR",
|
|
# i_C=clk_buffer, i_CE=1, i_D1=0, i_D2=1, o_Q=clk_se),
|
|
|
|
self.specials += Instance("OBUFDS",
|
|
i_I=self.clk,
|
|
o_O=clk_out.p,
|
|
o_OB=clk_out.n,
|
|
)
|
|
|
|
|
|
class Master(Module):
|
|
def __init__(self, i_pads, o_pads):
|
|
self.sysclk = ClockSignal("sys")
|
|
self.submodules.tx = MultiLineTX()
|
|
self.submodules.rx = MultiLineRX()
|
|
self.submodules.channel = BiDirectionalIO(i_pads, o_pads)
|
|
|
|
self.submodules.encoder = MultiEncoder(lsb_first=False)
|
|
decoders = [ CrossbarDecoder(lsb_first=False) for _ in range(2) ]
|
|
self.submodules += decoders
|
|
|
|
self.comb += [
|
|
# Transmitter to SERDES
|
|
self.channel.i.eq(self.tx.ser_out),
|
|
self.channel.t.eq(self.tx.t_out),
|
|
|
|
# SERDES to receiver
|
|
self.rx.ser_in_no_dly.eq(self.channel.o),
|
|
|
|
# Connect encoders & decoders by default
|
|
# Overrule the encoder connection during alignment
|
|
self.tx.txdata.eq(Cat(self.encoder.output[0], self.encoder.output[1])),
|
|
decoders[0].raw_input.eq(self.rx.rxdata[:10]),
|
|
decoders[1].raw_input.eq(self.rx.rxdata[10:]),
|
|
]
|
|
|
|
tx_fsm = FSM(reset_state="SEND_TRAINING")
|
|
self.submodules += tx_fsm
|
|
|
|
tx_fsm.act("SEND_TRAINING",
|
|
self.tx.txdata.eq(0b00100001000010000100),
|
|
# Keep sending the training sequence unless
|
|
# an identifier is received
|
|
NextState("SEND_TRAINING"),
|
|
# If(self.rx.rxdata == 0b11111111111111111111,
|
|
# NextState("WAIT_IDENT_END"),
|
|
# ),
|
|
)
|
|
|
|
tx_fsm.act("WAIT_IDENT_END",
|
|
self.tx.txdata.eq(0),
|
|
If(self.rx.rxdata != 0b11111111111111111111,
|
|
NextState("SEND_ZERO"),
|
|
),
|
|
)
|
|
|
|
send_zero_duration = Signal(3)
|
|
|
|
tx_fsm.act("SEND_ZERO",
|
|
self.tx.txdata.eq(0),
|
|
If(send_zero_duration == 0b111,
|
|
NextState("SEND_PULSE"),
|
|
).Else(
|
|
NextValue(send_zero_duration, send_zero_duration + 1),
|
|
),
|
|
)
|
|
|
|
tx_fsm.act("SEND_PULSE",
|
|
self.tx.txdata.eq(0b11111111111111111111),
|
|
self.encoder.start.eq(1),
|
|
# Slave decoder start will be triggered by this state
|
|
NextState("WAIT_GROUP_ALIGN"),
|
|
)
|
|
|
|
data = [ Signal(8) for _ in range(2) ]
|
|
|
|
tx_fsm.act("WAIT_GROUP_ALIGN",
|
|
# Wait for the identifier from the slave
|
|
# TODO: Align the master receiver after
|
|
If(self.rx.rxdata == 0b11111111111111111111,
|
|
NextValue(data[0], 0x80),
|
|
NextValue(data[1], 0x7F),
|
|
NextState("TERMINATE"),
|
|
),
|
|
)
|
|
|
|
tx_fsm.act("TERMINATE",
|
|
self.encoder.d[0].eq(0x89),
|
|
self.encoder.d[1].eq(0x75),
|
|
NextValue(data[0], data[0] + 1),
|
|
NextValue(data[1], data[1] - 1),
|
|
NextState("TERMINATE"),
|
|
)
|
|
|
|
|
|
class Satellite(Module):
|
|
def __init__(self, i_pads, o_pads, sys_clk_freq):
|
|
self.uart_rx = Signal()
|
|
self.uart_tx = Signal()
|
|
|
|
self.submodules.uart = UART(round((115200/sys_clk_freq)*2**32))
|
|
self.comb += [
|
|
self.uart.phy_rx.eq(self.uart_rx),
|
|
self.uart_tx.eq(self.uart.phy_tx),
|
|
]
|
|
|
|
# Attach FIFO to UART TX, send rate is too slow w.r.t sysclk
|
|
self.submodules.tx_fifo = SyncFIFO(8, 64)
|
|
|
|
self.comb += [
|
|
# UART TX path
|
|
self.uart.tx_data.eq(self.tx_fifo.dout),
|
|
self.uart.tx_stb.eq(self.tx_fifo.readable),
|
|
self.tx_fifo.re.eq(self.uart.tx_ack),
|
|
]
|
|
|
|
self.submodules.tx = MultiLineTX()
|
|
self.submodules.rx = MultiLineRX()
|
|
self.submodules.channel = BiDirectionalIO(i_pads, o_pads)
|
|
|
|
self.submodules.encoder = MultiEncoder(lsb_first=False)
|
|
decoders = [ CrossbarDecoder(lsb_first=False) for _ in range(2) ]
|
|
self.submodules += decoders
|
|
|
|
self.comb += [
|
|
# Transmitter to SERDES
|
|
self.channel.i.eq(self.tx.ser_out),
|
|
self.channel.t.eq(self.tx.t_out),
|
|
|
|
# SERDES to receiver
|
|
self.rx.ser_in_no_dly.eq(self.channel.o),
|
|
|
|
# Immediately start alignment for RX
|
|
self.rx.start.eq(1),
|
|
|
|
# Connect encoders & decoders by default
|
|
# Overrule the encoder connection during alignment
|
|
self.tx.txdata.eq(Cat(self.encoder.output[0], self.encoder.output[1])),
|
|
decoders[0].raw_input.eq(self.rx.rxdata[:10]),
|
|
decoders[1].raw_input.eq(self.rx.rxdata[10:]),
|
|
|
|
# Start decoder after delay_done is set
|
|
decoders[0].start.eq(self.rx.delay_done),
|
|
decoders[1].start.eq(self.rx.delay_done),
|
|
]
|
|
|
|
rx_fsm = FSM(reset_state="WAIT_ALIGN_DELAY")
|
|
self.submodules += rx_fsm
|
|
|
|
log_buffer = SyncFIFO(20, 128)
|
|
self.submodules += log_buffer
|
|
|
|
rx_fsm.act("WAIT_ALIGN_DELAY",
|
|
If(self.rx.align_done & log_buffer.writable,
|
|
log_buffer.we.eq(1),
|
|
log_buffer.din.eq(self.rx.rxdata),
|
|
),
|
|
If(~log_buffer.writable,
|
|
NextState("DUMP_LOG_UPPER"),
|
|
),
|
|
If(self.rx.delay_done,
|
|
NextState("LOG_TRAFFIC"),
|
|
),
|
|
)
|
|
|
|
rx_fsm.act("LOG_TRAFFIC",
|
|
If(log_buffer.writable,
|
|
log_buffer.we.eq(1),
|
|
log_buffer.din[0:8].eq(decoders[0].d),
|
|
log_buffer.din[10:18].eq(decoders[1].d),
|
|
# log_buffer.din.eq(self.rx.rxdata),
|
|
).Else(
|
|
NextState("DUMP_LOG_UPPER"),
|
|
),
|
|
)
|
|
|
|
rx_fsm.act("DUMP_LOG_UPPER",
|
|
If(log_buffer.readable,
|
|
If(self.tx_fifo.writable,
|
|
self.tx_fifo.we.eq(1),
|
|
self.tx_fifo.din.eq(log_buffer.dout[18:20]),
|
|
NextState("DUMP_LOG_LOWER"),
|
|
),
|
|
).Else(
|
|
NextState("TERMINATE")
|
|
),
|
|
)
|
|
|
|
rx_fsm.act("DUMP_LOG_LOWER",
|
|
If(self.tx_fifo.writable,
|
|
self.tx_fifo.we.eq(1),
|
|
self.tx_fifo.din.eq(log_buffer.dout[10:18]),
|
|
# log_buffer.re.eq(1),
|
|
NextState("DUMP_LOG_UPPER_SECOND"),
|
|
),
|
|
)
|
|
|
|
rx_fsm.act("DUMP_LOG_UPPER_SECOND",
|
|
If(self.tx_fifo.writable,
|
|
self.tx_fifo.we.eq(1),
|
|
self.tx_fifo.din.eq(log_buffer.dout[8:10]),
|
|
NextState("DUMP_LOG_LOWER_SECOND"),
|
|
),
|
|
)
|
|
|
|
rx_fsm.act("DUMP_LOG_LOWER_SECOND",
|
|
If(self.tx_fifo.writable,
|
|
self.tx_fifo.we.eq(1),
|
|
self.tx_fifo.din.eq(log_buffer.dout[:8]),
|
|
log_buffer.re.eq(1),
|
|
NextState("DUMP_LOG_UPPER"),
|
|
),
|
|
)
|
|
|
|
rx_fsm.act("TERMINATE",
|
|
NextState("TERMINATE"),
|
|
)
|
|
|
|
tx_fsm = FSM(reset_state="WAIT_RX_INTRA_ALIGN")
|
|
self.submodules += tx_fsm
|
|
|
|
tx_fsm.act("WAIT_RX_INTRA_ALIGN",
|
|
# Note: (Redundant) Only send something when aligned
|
|
self.tx.txdata.eq(0),
|
|
If(self.rx.align_done,
|
|
NextState("SEND_INTRA_ALIGN_IDENT"),
|
|
),
|
|
)
|
|
|
|
# Master receiver is not aligned yet
|
|
# We need to account for the possibility of group delay
|
|
# and SERDES misalignment
|
|
# 4 cycles of full 1s seems sufficiently detectable
|
|
ident_timer = Signal(2)
|
|
|
|
tx_fsm.act("SEND_INTRA_ALIGN_IDENT",
|
|
self.tx.txdata.eq(0b11111111111111111111),
|
|
If(ident_timer == 0b11,
|
|
NextValue(ident_timer, 0),
|
|
NextState("WAIT_RX_GROUP_ALIGN"),
|
|
).Else(
|
|
NextValue(ident_timer, ident_timer + 1)
|
|
),
|
|
)
|
|
|
|
tx_fsm.act("WAIT_RX_GROUP_ALIGN",
|
|
self.tx.txdata.eq(0),
|
|
If(self.rx.delay_done,
|
|
NextState("SEND_RX_DELAY_IDENT"),
|
|
),
|
|
)
|
|
|
|
tx_fsm.act("SEND_RX_DELAY_IDENT",
|
|
self.tx.txdata.eq(0b11111111111111111111),
|
|
If(ident_timer == 0b11,
|
|
NextValue(ident_timer, 0),
|
|
NextState("TERMINATE"),
|
|
).Else(
|
|
NextValue(ident_timer, ident_timer + 1)
|
|
),
|
|
)
|
|
|
|
tx_fsm.act("TERMINATE",
|
|
# TODO: Release the TX serdes to the encoders
|
|
self.tx.txdata.eq(0),
|
|
NextState("TERMINATE"),
|
|
)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
import argparse
|
|
import functools
|
|
import os
|
|
|
|
parser = argparse.ArgumentParser()
|
|
parser.add_argument("platform")
|
|
parser.add_argument("variant")
|
|
args = parser.parse_args()
|
|
|
|
platform_dict = {
|
|
"kasli": kasli.Platform(hw_rev="v2.0"),
|
|
"efc": efc.Platform(),
|
|
}
|
|
|
|
sysclk_name = {
|
|
"kasli": "clk125_gtp",
|
|
"efc": "gtp_clk",
|
|
}
|
|
|
|
platform = platform_dict[args.platform]
|
|
|
|
# Generate pads for the I/O blocks
|
|
for eem in range(2):
|
|
generate_pads(platform, eem)
|
|
data_eem = 0
|
|
clk_eem = 1
|
|
|
|
clk_pad = platform.request("dio{}".format(clk_eem), 0)
|
|
|
|
# Manage clock source
|
|
if args.variant == "master":
|
|
sysclk = platform.request(sysclk_name[args.platform])
|
|
# Fan out clock through EEM1 pair 0
|
|
# clkout = ClockOut(clk_pad)
|
|
elif args.variant == "satellite":
|
|
# Receive clock from EEM1 pair 0
|
|
sysclk = clk_pad
|
|
|
|
if args.variant == "master":
|
|
i_pads = [
|
|
platform.request("dio{}".format(data_eem), i) for i in range(4)
|
|
]
|
|
o_pads = [
|
|
platform.request("dio{}".format(data_eem), i+4) for i in range(4)
|
|
]
|
|
elif args.variant == "satellite":
|
|
i_pads = [
|
|
platform.request("dio{}".format(data_eem), i) for i in range(4)
|
|
]
|
|
o_pads = [
|
|
platform.request("dio{}".format(data_eem), i+4) for i in range(4)
|
|
]
|
|
else:
|
|
raise ValueError("variant {} not implemented".format(args.variant))
|
|
|
|
crg = TransceiverCRG(platform, sysclk, gte=(args.variant == "master"))
|
|
|
|
variant = {
|
|
"master": functools.partial(Master, i_pads, o_pads),
|
|
"satellite": functools.partial(Satellite, i_pads, o_pads, crg.sys_clk_freq),
|
|
}
|
|
module_cls = variant[args.variant]
|
|
|
|
top = module_cls()
|
|
|
|
# Wire up UART core to the pads
|
|
if args.variant == "satellite":
|
|
uart_pads = platform.request("serial")
|
|
top.comb += [
|
|
top.uart_rx.eq(uart_pads.rx),
|
|
uart_pads.tx.eq(top.uart_tx),
|
|
]
|
|
|
|
top.submodules += crg
|
|
# if args.variant == "master":
|
|
# top.submodules += clkout
|
|
# top.comb += clkout.clk.eq(top.sysclk)
|
|
|
|
output_dir = "{}_{}_build".format(args.platform, args.variant)
|
|
platform.build(top, build_dir=output_dir)
|