Compare commits
10 Commits
b38a4d6b8c
...
46c84dfc20
Author | SHA1 | Date |
---|---|---|
occheung | 46c84dfc20 | |
occheung | 514b649bfc | |
occheung | 1248a46a54 | |
occheung | b215dd1b57 | |
occheung | e8a178a111 | |
occheung | 8bfd229bbe | |
occheung | 950d9ee8be | |
occheung | ce1f669138 | |
occheung | f55b2593a1 | |
occheung | 24e8e9add9 |
|
@ -4,3 +4,5 @@ test
|
||||||
*.vcd
|
*.vcd
|
||||||
*.cfg
|
*.cfg
|
||||||
*.txt
|
*.txt
|
||||||
|
# Bitstream build directories have _build as their suffixes
|
||||||
|
*_build
|
|
@ -0,0 +1,25 @@
|
||||||
|
from migen import *
|
||||||
|
|
||||||
|
|
||||||
|
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],
|
||||||
|
)
|
17
comm.py
17
comm.py
|
@ -1,8 +1,8 @@
|
||||||
import serial
|
import serial
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main(serial_port):
|
||||||
comm = serial.Serial("/dev/ttyUSB3", 115200)
|
comm = serial.Serial(serial_port, 115200)
|
||||||
# comm.write(b"Hello World!")
|
# comm.write(b"Hello World!")
|
||||||
|
|
||||||
# for _ in range(32):
|
# for _ in range(32):
|
||||||
|
@ -19,4 +19,15 @@ def main():
|
||||||
# print(f'{byte[0]:0>8b}')
|
# print(f'{byte[0]:0>8b}')
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
main()
|
import argparse
|
||||||
|
|
||||||
|
parser = argparse.ArgumentParser()
|
||||||
|
parser.add_argument("platform")
|
||||||
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
port_dict = {
|
||||||
|
"kasli": "/dev/ttyUSB3",
|
||||||
|
"efc": "/dev/ttyACM1",
|
||||||
|
}
|
||||||
|
|
||||||
|
main(port_dict[args.platform])
|
||||||
|
|
19
default.nix
19
default.nix
|
@ -7,14 +7,28 @@ let
|
||||||
src = pkgs.fetchFromGitHub {
|
src = pkgs.fetchFromGitHub {
|
||||||
owner = "m-labs";
|
owner = "m-labs";
|
||||||
repo = "migen";
|
repo = "migen";
|
||||||
rev = "7bc4eb1387b39159a74c1dbd1b820728e0bfbbaa";
|
rev = "ccaee68e14d3636e1d8fb2e0864dd89b1b1f7384";
|
||||||
sha256 = "039jk8y7f0vhr32svg3nd23i88c0bhws8ngxwk9bdznfxvhiy1h6";
|
sha256 = "sha256-oYdeY0MbTReKbAwmSznnqw0wNawdInJoFJVWW3tesFA=";
|
||||||
fetchSubmodules = true;
|
fetchSubmodules = true;
|
||||||
};
|
};
|
||||||
|
|
||||||
propagatedBuildInputs = with pkgs.python3Packages; [ colorama ];
|
propagatedBuildInputs = with pkgs.python3Packages; [ colorama ];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
misoc = pkgs.python3Packages.buildPythonPackage rec {
|
||||||
|
name = "misoc";
|
||||||
|
|
||||||
|
src = pkgs.fetchFromGitHub {
|
||||||
|
owner = "m-labs";
|
||||||
|
repo = "misoc";
|
||||||
|
rev = "0cf0ebb7d4f56cc6d44a3dea3e386efab9d82419";
|
||||||
|
sha256 = "sha256-TI0agjSSMJtH4mgAMpSO128zxcwSo/AjY1B6AW7zBQQ=";
|
||||||
|
fetchSubmodules = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
propagatedBuildInputs = with pkgs.python3Packages; [ jinja2 numpy pyserial asyncserial ] ++ [ migen ];
|
||||||
|
};
|
||||||
|
|
||||||
vivadoDeps = pkgs: with pkgs; [
|
vivadoDeps = pkgs: with pkgs; [
|
||||||
libxcrypt
|
libxcrypt
|
||||||
ncurses5
|
ncurses5
|
||||||
|
@ -47,6 +61,7 @@ in pkgs.mkShell {
|
||||||
name = "UART-Testing";
|
name = "UART-Testing";
|
||||||
buildInputs = [
|
buildInputs = [
|
||||||
migen
|
migen
|
||||||
|
misoc
|
||||||
pkgs.python3Packages.pyserial
|
pkgs.python3Packages.pyserial
|
||||||
vivado
|
vivado
|
||||||
vivadoEnv
|
vivadoEnv
|
||||||
|
|
|
@ -0,0 +1,60 @@
|
||||||
|
import argparse
|
||||||
|
import tempfile
|
||||||
|
import subprocess
|
||||||
|
|
||||||
|
|
||||||
|
def get_load_cmd(platform, variant):
|
||||||
|
return \
|
||||||
|
'''target create xc7.spi0.proxy testee -chain-position xc7.tap
|
||||||
|
flash bank spi0 jtagspi 0 0 0 0 xc7.spi0.proxy 0x2
|
||||||
|
gdb_port disabled
|
||||||
|
tcl_port disabled
|
||||||
|
telnet_port disabled
|
||||||
|
set error_msg "Trying to use configured scan chain anyway"
|
||||||
|
if {[string first $error_msg [capture "init"]] != -1} {
|
||||||
|
puts "Found error and exiting"
|
||||||
|
exit}
|
||||||
|
xadc_report xc7.tap
|
||||||
|
pld load 0 {bscan_spi_xc7a100t.bit}
|
||||||
|
flash probe spi0
|
||||||
|
flash erase_sector spi0 0 42
|
||||||
|
flash write_bank spi0 {''' + "{}".format(get_bitstream_path(platform, variant)) + '''} 0x0
|
||||||
|
flash verify_bank spi0 {''' + "{}".format(get_bitstream_path(platform, variant)) + '''} 0x0
|
||||||
|
xc7_program xc7.tap
|
||||||
|
exit'''
|
||||||
|
|
||||||
|
def get_build_dir(platform, variant):
|
||||||
|
return "{}_{}_build".format(platform, variant)
|
||||||
|
|
||||||
|
def get_bitstream_path(platform, variant):
|
||||||
|
return get_build_dir(platform, variant)+"/top.bit"
|
||||||
|
|
||||||
|
def run(platform, script):
|
||||||
|
# Dump script to a temp file
|
||||||
|
with tempfile.NamedTemporaryFile(mode="w", delete=False) as f:
|
||||||
|
f.write(script)
|
||||||
|
|
||||||
|
board_path = {
|
||||||
|
"kasli": "board/kasli.cfg",
|
||||||
|
"efc": "efc.cfg",
|
||||||
|
}
|
||||||
|
subprocess.run([
|
||||||
|
"openocd", "-f", board_path[platform], "-f", f.name
|
||||||
|
])
|
||||||
|
|
||||||
|
supported_platform = [ "kasli", "efc" ]
|
||||||
|
supported_variants = [ "master", "satellite" ]
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
parser = argparse.ArgumentParser()
|
||||||
|
parser.add_argument("platform")
|
||||||
|
parser.add_argument("variant")
|
||||||
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
if args.platform not in supported_platform:
|
||||||
|
raise ValueError("Unsupported platform")
|
||||||
|
if args.variant not in supported_variants:
|
||||||
|
raise ValueError("Unsupported variant")
|
||||||
|
|
||||||
|
script = get_load_cmd(args.platform, args.variant)
|
||||||
|
run(args.platform, script)
|
39
kasli_crg.py
39
kasli_crg.py
|
@ -3,8 +3,8 @@ from migen.build.platforms.sinara import kasli
|
||||||
from migen.genlib.resetsync import AsyncResetSynchronizer
|
from migen.genlib.resetsync import AsyncResetSynchronizer
|
||||||
|
|
||||||
|
|
||||||
class KasliCRG(Module):
|
class TransceiverCRG(Module):
|
||||||
def __init__(self, platform):
|
def __init__(self, platform, clk125, gte=True):
|
||||||
self.platform = platform
|
self.platform = platform
|
||||||
|
|
||||||
# Generated clock domains
|
# Generated clock domains
|
||||||
|
@ -16,15 +16,38 @@ class KasliCRG(Module):
|
||||||
|
|
||||||
# Configure system clock using GTP ports
|
# Configure system clock using GTP ports
|
||||||
self.sys_clk_freq = 125e6
|
self.sys_clk_freq = 125e6
|
||||||
clk125 = self.platform.request("clk125_gtp")
|
|
||||||
clk125_buf = Signal()
|
clk125_buf = Signal()
|
||||||
clk125_div2 = Signal()
|
clk125_div2 = Signal()
|
||||||
|
|
||||||
self.specials += Instance("IBUFDS_GTE2",
|
if gte:
|
||||||
i_CEB=0,
|
self.specials += Instance("IBUFDS_GTE2",
|
||||||
i_I=clk125.p, i_IB=clk125.n,
|
i_CEB=0,
|
||||||
o_O=clk125_buf,
|
i_I=clk125.p, i_IB=clk125.n,
|
||||||
o_ODIV2=clk125_div2)
|
o_O=clk125_buf,
|
||||||
|
o_ODIV2=clk125_div2)
|
||||||
|
else:
|
||||||
|
self.specials += Instance("IBUFDS",
|
||||||
|
i_I=clk125.p,
|
||||||
|
i_IB=clk125.n,
|
||||||
|
o_O=clk125_buf)
|
||||||
|
|
||||||
|
div2_pll_fb = Signal()
|
||||||
|
inner_pll_locked = Signal()
|
||||||
|
|
||||||
|
self.specials += Instance("PLLE2_BASE",
|
||||||
|
p_CLKIN1_PERIOD=8.0,
|
||||||
|
i_CLKIN1=clk125_buf,
|
||||||
|
|
||||||
|
i_CLKFBIN=div2_pll_fb,
|
||||||
|
o_CLKFBOUT=div2_pll_fb,
|
||||||
|
o_LOCKED=inner_pll_locked,
|
||||||
|
|
||||||
|
# VCO @ 1GHz
|
||||||
|
p_CLKFBOUT_MULT=8, p_DIVCLK_DIVIDE=1,
|
||||||
|
|
||||||
|
# 200MHz for IDELAYCTRL
|
||||||
|
p_CLKOUT0_DIVIDE=16, p_CLKOUT0_PHASE=0.0, o_CLKOUT0=clk125_div2,
|
||||||
|
),
|
||||||
|
|
||||||
# MMCM to generate different frequencies
|
# MMCM to generate different frequencies
|
||||||
mmcm_fb = Signal()
|
mmcm_fb = Signal()
|
||||||
|
|
|
@ -0,0 +1,180 @@
|
||||||
|
from migen import *
|
||||||
|
from sync_serdes import *
|
||||||
|
from migen.genlib.fifo import SyncFIFO
|
||||||
|
from migen.build.platforms.sinara import kasli
|
||||||
|
from migen.genlib.misc import WaitTimer
|
||||||
|
from kasli_crg import TransceiverCRG
|
||||||
|
from eem_helpers import generate_pads
|
||||||
|
from uart import UART
|
||||||
|
from io_loopback import SingleIOLoopback
|
||||||
|
|
||||||
|
|
||||||
|
class SingleSerDesLoopBack(Module):
|
||||||
|
def __init__(self, io_pad, sys_clk_freq, debug):
|
||||||
|
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),
|
||||||
|
]
|
||||||
|
|
||||||
|
self.submodules.tx = SingleLineTX()
|
||||||
|
self.submodules.rx = SingleLineRX()
|
||||||
|
|
||||||
|
# Debugging reader
|
||||||
|
self.submodules.bitslip_reader = BitSlipReader()
|
||||||
|
self.submodules.postslip_reader = BitSlipReader()
|
||||||
|
|
||||||
|
# Alignment modules
|
||||||
|
self.submodules.slave_aligner = SlaveAligner()
|
||||||
|
|
||||||
|
# The actual channel
|
||||||
|
self.submodules.channel = SingleIOLoopback(io_pad)
|
||||||
|
|
||||||
|
# Attach FIFO to UART TX, send rate is too slow w.r.t sysclk
|
||||||
|
self.submodules.tx_fifo = SyncFIFO(8, 64)
|
||||||
|
|
||||||
|
self.comb += [
|
||||||
|
# Repetitively send 0b00100
|
||||||
|
self.tx.txdata.eq(0b00100),
|
||||||
|
|
||||||
|
# Loopback channel
|
||||||
|
self.channel.i.eq(self.tx.ser_out),
|
||||||
|
self.rx.ser_in_no_dly.eq(self.channel.o),
|
||||||
|
self.channel.t.eq(self.tx.t_out),
|
||||||
|
|
||||||
|
# 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),
|
||||||
|
]
|
||||||
|
|
||||||
|
# Debugging logics
|
||||||
|
self.comb += [
|
||||||
|
self.bitslip_reader.loopback_rxdata.eq(self.rx.rxdata),
|
||||||
|
self.postslip_reader.loopback_rxdata.eq(self.rx.rxdata),
|
||||||
|
self.slave_aligner.loopback_rxdata.eq(self.rx.rxdata),
|
||||||
|
|
||||||
|
self.rx.master_bitslip.eq(
|
||||||
|
self.bitslip_reader.bitslip
|
||||||
|
| self.slave_aligner.master_bitslip
|
||||||
|
| self.postslip_reader.bitslip
|
||||||
|
),
|
||||||
|
self.rx.slave_bitslip.eq(
|
||||||
|
self.bitslip_reader.bitslip
|
||||||
|
| self.slave_aligner.master_bitslip
|
||||||
|
| self.postslip_reader.bitslip
|
||||||
|
),
|
||||||
|
]
|
||||||
|
|
||||||
|
bitslip_count = Signal(3)
|
||||||
|
|
||||||
|
fsm = FSM(reset_state="WAIT_DONE")
|
||||||
|
self.submodules += fsm
|
||||||
|
|
||||||
|
fsm.act("WAIT_DONE",
|
||||||
|
self.bitslip_reader.start.eq(1),
|
||||||
|
If(self.bitslip_reader.done,
|
||||||
|
NextValue(bitslip_count, 0),
|
||||||
|
NextState("WRITE_UPPER"),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
fsm.act("WRITE_UPPER",
|
||||||
|
If(bitslip_count == 5,
|
||||||
|
NextState("WAIT_BITSLIP_ALIGNED"),
|
||||||
|
).Elif(self.tx_fifo.writable,
|
||||||
|
self.tx_fifo.we.eq(1),
|
||||||
|
self.tx_fifo.din.eq(self.bitslip_reader.data_result[bitslip_count][8:]),
|
||||||
|
NextState("WRITE_LOWER"),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
fsm.act("WRITE_LOWER",
|
||||||
|
If(self.tx_fifo.writable,
|
||||||
|
self.tx_fifo.we.eq(1),
|
||||||
|
self.tx_fifo.din.eq(self.bitslip_reader.data_result[bitslip_count][:8]),
|
||||||
|
NextValue(bitslip_count, bitslip_count + 1),
|
||||||
|
NextState("WRITE_UPPER"),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
fsm.act("WAIT_BITSLIP_ALIGNED",
|
||||||
|
self.slave_aligner.start.eq(1),
|
||||||
|
If(self.slave_aligner.done,
|
||||||
|
NextState("BARRIER_UPPER"),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
fsm.act("BARRIER_UPPER",
|
||||||
|
If(self.tx_fifo.writable,
|
||||||
|
self.tx_fifo.we.eq(1),
|
||||||
|
self.tx_fifo.din.eq(0xFF),
|
||||||
|
NextState("BARRIER_LOWER"),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
fsm.act("BARRIER_LOWER",
|
||||||
|
If(self.tx_fifo.writable,
|
||||||
|
self.tx_fifo.we.eq(1),
|
||||||
|
self.tx_fifo.din.eq(0xFF),
|
||||||
|
NextState("REWAIT_BITSLIP_READ_DONE"),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
fsm.act("REWAIT_BITSLIP_READ_DONE",
|
||||||
|
self.postslip_reader.start.eq(1),
|
||||||
|
If(self.postslip_reader.done,
|
||||||
|
NextValue(bitslip_count, 0),
|
||||||
|
NextState("REWRITE_UPPER"),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
fsm.act("REWRITE_UPPER",
|
||||||
|
If(bitslip_count == 5,
|
||||||
|
NextState("TERMINATE"),
|
||||||
|
).Elif(self.tx_fifo.writable,
|
||||||
|
self.tx_fifo.we.eq(1),
|
||||||
|
self.tx_fifo.din.eq(self.postslip_reader.data_result[bitslip_count][8:]),
|
||||||
|
NextState("REWRITE_LOWER"),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
fsm.act("REWRITE_LOWER",
|
||||||
|
If(self.tx_fifo.writable,
|
||||||
|
self.tx_fifo.we.eq(1),
|
||||||
|
self.tx_fifo.din.eq(self.postslip_reader.data_result[bitslip_count][:8]),
|
||||||
|
NextValue(bitslip_count, bitslip_count + 1),
|
||||||
|
NextState("REWRITE_UPPER"),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
fsm.act("TERMINATE",
|
||||||
|
NextState("TERMINATE"),
|
||||||
|
)
|
||||||
|
|
||||||
|
self.comb += self.tx.txdata.eq(0b00100)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
platform = kasli.Platform(hw_rev="v2.0")
|
||||||
|
|
||||||
|
# Generate pads for the I/O blocks
|
||||||
|
eem = 3
|
||||||
|
generate_pads(platform, eem)
|
||||||
|
pad = platform.request("dio{}".format(eem), 0)
|
||||||
|
|
||||||
|
crg = TransceiverCRG(platform, platform.request("clk125_gtp"))
|
||||||
|
top = SingleSerDesLoopBack(pad, crg.sys_clk_freq, True)
|
||||||
|
|
||||||
|
# Wire up UART core to the pads
|
||||||
|
uart_pads = platform.request("serial")
|
||||||
|
top.comb += [
|
||||||
|
top.uart_rx.eq(uart_pads.rx),
|
||||||
|
uart_pads.tx.eq(top.uart_tx),
|
||||||
|
]
|
||||||
|
|
||||||
|
top.submodules += crg
|
||||||
|
platform.build(top)
|
|
@ -0,0 +1,123 @@
|
||||||
|
from migen import *
|
||||||
|
from misoc.cores.code_8b10b import SingleEncoder, Decoder
|
||||||
|
|
||||||
|
|
||||||
|
class MultiEncoder(Module):
|
||||||
|
# For 4-channel EEM
|
||||||
|
# Thus, no need for words parameter
|
||||||
|
# It is always a 2-wds -> 4-ch conversion
|
||||||
|
# Implement using crossbar, so we don't need to do clock domain conversion
|
||||||
|
# while maintaining proper disparity
|
||||||
|
def __init__(self, lsb_first=False):
|
||||||
|
WORDS = 2
|
||||||
|
# Keep the link layer interface identical to standard encoders
|
||||||
|
self.d = [Signal(8) for _ in range(WORDS)]
|
||||||
|
self.k = [Signal() for _ in range(WORDS)]
|
||||||
|
|
||||||
|
# Output interface is simplified because we have custom physical layer
|
||||||
|
self.output = [Signal(10) for _ in range(WORDS)]
|
||||||
|
|
||||||
|
# Module start signal
|
||||||
|
self.start = Signal()
|
||||||
|
|
||||||
|
# lsb_first should not be set set
|
||||||
|
# But technically if the same setting is reflected on the decoder
|
||||||
|
# It shouldn't be an issue
|
||||||
|
if lsb_first:
|
||||||
|
raise ValueError("lsb_first must not be set")
|
||||||
|
|
||||||
|
# Phase of the encoder
|
||||||
|
# Alternate crossbar between encoder and SERDES every cycle
|
||||||
|
phase = Signal()
|
||||||
|
|
||||||
|
# Intermediate registers for output and disparity
|
||||||
|
# More significant bits are buffered due to channel geometry
|
||||||
|
# Disparity bit is delayed. The same encoder is shared by 2 SERDES
|
||||||
|
output_bufs = [Signal(5) for _ in range(WORDS)]
|
||||||
|
disp_bufs = [Signal() for _ in range(WORDS)]
|
||||||
|
|
||||||
|
encoders = [SingleEncoder(lsb_first) for _ in range(WORDS)]
|
||||||
|
self.submodules += encoders
|
||||||
|
|
||||||
|
for d, k, output, output_buf, disp_buf, encoder in \
|
||||||
|
zip(self.d, self.k, self.output, output_bufs, disp_bufs, encoders):
|
||||||
|
self.comb += [
|
||||||
|
encoder.d.eq(d),
|
||||||
|
encoder.k.eq(k),
|
||||||
|
encoder.disp_in.eq(disp_buf),
|
||||||
|
# Implementing switching crossbar
|
||||||
|
If(phase,
|
||||||
|
output.eq(Cat(encoder.output[0:5], output_buf))
|
||||||
|
).Else(
|
||||||
|
output.eq(Cat(output_buf, encoder.output[0:5]))
|
||||||
|
),
|
||||||
|
]
|
||||||
|
# Handle intermediate registers
|
||||||
|
self.sync += [
|
||||||
|
disp_buf.eq(encoder.disp_out),
|
||||||
|
output_buf.eq(encoder.output[5:10]),
|
||||||
|
]
|
||||||
|
|
||||||
|
aligned = Signal()
|
||||||
|
|
||||||
|
self.sync += [
|
||||||
|
# Phase switching
|
||||||
|
phase.eq(~phase),
|
||||||
|
If(~aligned & self.start,
|
||||||
|
aligned.eq(1),
|
||||||
|
# Later statements take precedent
|
||||||
|
phase.eq(0),
|
||||||
|
),
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
# Unlike the usual 8b10b decoder, it needs to know which SERDES to decode
|
||||||
|
class CrossbarDecoder(Module):
|
||||||
|
def __init__(self, lsb_first=False):
|
||||||
|
self.raw_input = Signal(10)
|
||||||
|
self.d = Signal(8)
|
||||||
|
self.k = Signal()
|
||||||
|
|
||||||
|
# Notifier signal when group alignmnet is completed
|
||||||
|
self.start = Signal()
|
||||||
|
aligned = Signal()
|
||||||
|
phase = Signal()
|
||||||
|
|
||||||
|
# Intermediate register for input
|
||||||
|
buffer = Signal(5)
|
||||||
|
|
||||||
|
self.submodules.decoder = Decoder(lsb_first)
|
||||||
|
|
||||||
|
# lsb_first should not be set set
|
||||||
|
# But technically if the same setting is reflected on the decoder
|
||||||
|
# It shouldn't be an issue
|
||||||
|
if lsb_first:
|
||||||
|
raise ValueError("lsb_first must not be set")
|
||||||
|
|
||||||
|
# Update phase & synchronous elements
|
||||||
|
self.sync += [
|
||||||
|
phase.eq(~phase),
|
||||||
|
If(~aligned & self.start,
|
||||||
|
aligned.eq(1),
|
||||||
|
phase.eq(0),
|
||||||
|
),
|
||||||
|
If(phase,
|
||||||
|
buffer.eq(self.raw_input[:5]),
|
||||||
|
).Else(
|
||||||
|
buffer.eq(self.raw_input[5:])
|
||||||
|
),
|
||||||
|
]
|
||||||
|
|
||||||
|
# Send appropriate input to decoder
|
||||||
|
self.comb += [
|
||||||
|
If(phase,
|
||||||
|
self.decoder.input.eq(Cat(buffer, self.raw_input[5:])),
|
||||||
|
).Else(
|
||||||
|
self.decoder.input.eq(Cat(buffer, self.raw_input[:5])),
|
||||||
|
),
|
||||||
|
]
|
||||||
|
|
||||||
|
self.comb += [
|
||||||
|
self.d.eq(self.decoder.d),
|
||||||
|
self.k.eq(self.decoder.k),
|
||||||
|
]
|
|
@ -0,0 +1,259 @@
|
||||||
|
from migen import *
|
||||||
|
from sync_serdes import MultiLineRX, MultiLineTX
|
||||||
|
from migen.genlib.fifo import SyncFIFO
|
||||||
|
from migen.build.platforms.sinara import kasli, efc
|
||||||
|
from multi_coders import MultiEncoder, CrossbarDecoder
|
||||||
|
from kasli_crg import TransceiverCRG
|
||||||
|
from eem_helpers import generate_pads
|
||||||
|
from uart import UART
|
||||||
|
from io_loopback import SingleIOLoopback, IOLoopBack
|
||||||
|
|
||||||
|
|
||||||
|
class MultiTransceiverChannel(Module):
|
||||||
|
def __init__(self, io_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),
|
||||||
|
]
|
||||||
|
|
||||||
|
# SERDES impl
|
||||||
|
self.submodules.tx = MultiLineTX()
|
||||||
|
self.submodules.rx = MultiLineRX()
|
||||||
|
|
||||||
|
# 8b10b encoder & decoder
|
||||||
|
self.submodules.encoder = MultiEncoder(lsb_first=False)
|
||||||
|
decoders = [ CrossbarDecoder(lsb_first=False) for _ in range(2) ]
|
||||||
|
self.submodules += decoders
|
||||||
|
|
||||||
|
# The actual channel
|
||||||
|
self.submodules.channel = IOLoopBack(io_pads)
|
||||||
|
|
||||||
|
# FIFO to record transmission received
|
||||||
|
rx_records = SyncFIFO(16, 128)
|
||||||
|
self.submodules += rx_records
|
||||||
|
|
||||||
|
# Attach FIFO to UART TX, send rate is too slow w.r.t sysclk
|
||||||
|
self.submodules.tx_fifo = SyncFIFO(8, 64)
|
||||||
|
|
||||||
|
self.comb += [
|
||||||
|
# Loopback channel
|
||||||
|
self.channel.i.eq(self.tx.ser_out),
|
||||||
|
self.channel.t.eq(self.tx.t_out),
|
||||||
|
self.rx.ser_in_no_dly.eq(self.channel.o),
|
||||||
|
|
||||||
|
# Link decoders
|
||||||
|
decoders[0].raw_input.eq(self.rx.rxdata[:10]),
|
||||||
|
decoders[1].raw_input.eq(self.rx.rxdata[10:]),
|
||||||
|
# Default encoder linkage
|
||||||
|
self.tx.txdata.eq(Cat(self.encoder.output[0], self.encoder.output[1])),
|
||||||
|
|
||||||
|
# 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),
|
||||||
|
|
||||||
|
# Immediate start RX alignment procedure
|
||||||
|
self.rx.start.eq(1),
|
||||||
|
]
|
||||||
|
|
||||||
|
rx_fsm = FSM(reset_state="WAIT_GROUP_ALIGN")
|
||||||
|
self.submodules += rx_fsm
|
||||||
|
|
||||||
|
rx_fsm.act("WAIT_GROUP_ALIGN",
|
||||||
|
If(self.rx.align_done & rx_records.writable,
|
||||||
|
rx_records.din.eq(self.rx.rxdata),
|
||||||
|
rx_records.we.eq(1),
|
||||||
|
),
|
||||||
|
If(self.rx.err,
|
||||||
|
NextState("WRITE_ERR_UPPER"),
|
||||||
|
).Elif(self.rx.rxdata == 0b11111111111111111111,
|
||||||
|
decoders[0].start.eq(1),
|
||||||
|
decoders[1].start.eq(1),
|
||||||
|
NextState("RECORD_TRANSMISSION"),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
rx_fsm.act("WRITE_ERR_UPPER",
|
||||||
|
If(self.tx_fifo.writable,
|
||||||
|
self.tx_fifo.we.eq(1),
|
||||||
|
self.tx_fifo.din.eq(0b01010101),
|
||||||
|
NextState("WRITE_ERR_LOWER"),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
rx_fsm.act("WRITE_ERR_LOWER",
|
||||||
|
If(self.tx_fifo.writable,
|
||||||
|
self.tx_fifo.we.eq(1),
|
||||||
|
self.tx_fifo.din.eq(0b10101010),
|
||||||
|
NextState("TERMINATE"),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
rx_fsm.act("RECORD_TRANSMISSION",
|
||||||
|
If(rx_records.writable,
|
||||||
|
rx_records.din.eq(Cat(decoders[0].d, decoders[1].d)),
|
||||||
|
rx_records.we.eq(1),
|
||||||
|
).Else(
|
||||||
|
NextState("DUMP_TRANSMISSION_UPPER"),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
rx_fsm.act("DUMP_TRANSMISSION_UPPER",
|
||||||
|
If(rx_records.readable,
|
||||||
|
If(self.tx_fifo.writable,
|
||||||
|
self.tx_fifo.we.eq(1),
|
||||||
|
self.tx_fifo.din.eq(rx_records.dout[8:]),
|
||||||
|
NextState("DUMP_TRANSMISSION_LOWER"),
|
||||||
|
)
|
||||||
|
).Else(
|
||||||
|
NextState("TERMINATE"),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
rx_fsm.act("DUMP_TRANSMISSION_LOWER",
|
||||||
|
If(self.tx_fifo.writable,
|
||||||
|
self.tx_fifo.we.eq(1),
|
||||||
|
self.tx_fifo.din.eq(rx_records.dout[:8]),
|
||||||
|
rx_records.re.eq(1),
|
||||||
|
NextState("DUMP_TRANSMISSION_UPPER"),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
rx_fsm.act("TERMINATE",
|
||||||
|
NextState("TERMINATE")
|
||||||
|
)
|
||||||
|
|
||||||
|
tx_fsm = FSM(reset_state="SEND_TRAINING")
|
||||||
|
self.submodules += tx_fsm
|
||||||
|
|
||||||
|
tx_fsm.act("SEND_TRAINING",
|
||||||
|
self.tx.txdata.eq(0b00100001000010000100),
|
||||||
|
If(self.rx.align_done,
|
||||||
|
NextState("SEND_ZERO"),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
send_zero_duration = Signal(2)
|
||||||
|
|
||||||
|
tx_fsm.act("SEND_ZERO",
|
||||||
|
self.tx.txdata.eq(0),
|
||||||
|
If(send_zero_duration == 0b11,
|
||||||
|
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),
|
||||||
|
NextState("WAIT_GROUP_ALIGN"),
|
||||||
|
)
|
||||||
|
|
||||||
|
data = [ Signal(8) for _ in range(2) ]
|
||||||
|
|
||||||
|
tx_fsm.act("WAIT_GROUP_ALIGN",
|
||||||
|
If(self.rx.delay_done,
|
||||||
|
NextValue(data[0], 0x80),
|
||||||
|
NextValue(data[1], 0x7F),
|
||||||
|
NextState("TERMINATE"),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
tx_fsm.act("TERMINATE",
|
||||||
|
self.encoder.d[0].eq(data[0]),
|
||||||
|
self.encoder.d[1].eq(data[1]),
|
||||||
|
self.encoder.k[0].eq(0),
|
||||||
|
self.encoder.k[1].eq(0),
|
||||||
|
NextValue(data[0], data[0] + 1),
|
||||||
|
NextValue(data[1], data[1] - 1),
|
||||||
|
NextState("TERMINATE"),
|
||||||
|
)
|
||||||
|
|
||||||
|
# tx_fsm.act("SEND_ARB_DATA1",
|
||||||
|
# self.encoder.d[0].eq(0xDE),
|
||||||
|
# self.encoder.d[1].eq(0xAD),
|
||||||
|
# self.encoder.k[0].eq(0),
|
||||||
|
# self.encoder.k[1].eq(0),
|
||||||
|
# NextState("SEND_ARB_DATA2"),
|
||||||
|
# )
|
||||||
|
|
||||||
|
# tx_fsm.act("SEND_ARB_DATA2",
|
||||||
|
# self.encoder.d[0].eq(0xBE),
|
||||||
|
# self.encoder.d[1].eq(0xEF),
|
||||||
|
# self.encoder.k[0].eq(0),
|
||||||
|
# self.encoder.k[1].eq(0),
|
||||||
|
# NextState("SEND_ARB_DATA3"),
|
||||||
|
# )
|
||||||
|
|
||||||
|
# tx_fsm.act("SEND_ARB_DATA3",
|
||||||
|
# self.encoder.d[0].eq(0xBA),
|
||||||
|
# self.encoder.d[1].eq(0xD0),
|
||||||
|
# self.encoder.k[0].eq(0),
|
||||||
|
# self.encoder.k[1].eq(0),
|
||||||
|
# NextState("SEND_ARB_DATA4"),
|
||||||
|
# )
|
||||||
|
|
||||||
|
# tx_fsm.act("SEND_ARB_DATA4",
|
||||||
|
# self.encoder.d[0].eq(0xCA),
|
||||||
|
# self.encoder.d[1].eq(0xFE),
|
||||||
|
# self.encoder.k[0].eq(0),
|
||||||
|
# self.encoder.k[1].eq(0),
|
||||||
|
# NextState("TERMINATE"),
|
||||||
|
# )
|
||||||
|
|
||||||
|
# tx_fsm.act("TERMINATE",
|
||||||
|
# self.encoder.d[0].eq(0xAD),
|
||||||
|
# self.encoder.d[1].eq(0xDE),
|
||||||
|
# self.encoder.k[0].eq(0),
|
||||||
|
# self.encoder.k[1].eq(0),
|
||||||
|
# NextState("TERMINATE"),
|
||||||
|
# )
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
import argparse
|
||||||
|
|
||||||
|
parser = argparse.ArgumentParser()
|
||||||
|
parser.add_argument("platform")
|
||||||
|
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]
|
||||||
|
sysclk = platform.request(sysclk_name[args.platform])
|
||||||
|
|
||||||
|
# Generate pads for the I/O blocks
|
||||||
|
# Using EEM1 for both as both EFC and Kasli has EEM1
|
||||||
|
# EEM1 are not interconnected
|
||||||
|
eem = 4
|
||||||
|
generate_pads(platform, eem)
|
||||||
|
pads = [
|
||||||
|
platform.request("dio{}".format(eem), i) for i in range(4)
|
||||||
|
]
|
||||||
|
# pad = platform.request("dio{}".format(eem), 0)
|
||||||
|
|
||||||
|
crg = TransceiverCRG(platform, sysclk)
|
||||||
|
top = MultiTransceiverChannel(pads, crg.sys_clk_freq)
|
||||||
|
|
||||||
|
# Wire up UART core to the pads
|
||||||
|
uart_pads = platform.request("serial")
|
||||||
|
top.comb += [
|
||||||
|
top.uart_rx.eq(uart_pads.rx),
|
||||||
|
uart_pads.tx.eq(top.uart_tx),
|
||||||
|
]
|
||||||
|
|
||||||
|
top.submodules += crg
|
||||||
|
platform.build(top)
|
|
@ -0,0 +1,230 @@
|
||||||
|
from migen import *
|
||||||
|
from sync_serdes import MultiLineRX, MultiLineTX
|
||||||
|
from migen.genlib.fifo import SyncFIFO
|
||||||
|
from migen.build.platforms.sinara import kasli, efc
|
||||||
|
from kasli_crg import TransceiverCRG
|
||||||
|
from eem_helpers import generate_pads
|
||||||
|
from uart import UART
|
||||||
|
from io_loopback import SingleIOLoopback, IOLoopBack
|
||||||
|
|
||||||
|
|
||||||
|
class MultiSerDesLoopBack(Module):
|
||||||
|
def __init__(self, io_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),
|
||||||
|
]
|
||||||
|
|
||||||
|
self.submodules.tx = MultiLineTX()
|
||||||
|
self.submodules.rx = MultiLineRX()
|
||||||
|
|
||||||
|
# The actual channel
|
||||||
|
self.submodules.channel = IOLoopBack(io_pads)
|
||||||
|
|
||||||
|
# Attach FIFO to UART TX, send rate is too slow w.r.t sysclk
|
||||||
|
self.submodules.tx_fifo = SyncFIFO(8, 64)
|
||||||
|
|
||||||
|
self.comb += [
|
||||||
|
# Repetitively send 0b00100
|
||||||
|
# Note: Replicate() doesn't work, 0b00100 is lowered into 3'd4
|
||||||
|
# I need 5'd4 for the replicate operator in Verilog
|
||||||
|
self.tx.txdata.eq(0b00100001000010000100),
|
||||||
|
|
||||||
|
# Loopback channel
|
||||||
|
self.channel.i.eq(self.tx.ser_out),
|
||||||
|
self.channel.t.eq(self.tx.t_out),
|
||||||
|
self.rx.ser_in_no_dly.eq(self.channel.o),
|
||||||
|
|
||||||
|
# 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),
|
||||||
|
|
||||||
|
# Just start RX alignment, no reason to wait
|
||||||
|
self.rx.start.eq(1),
|
||||||
|
]
|
||||||
|
|
||||||
|
self.submodules.rx_fsm = FSM(reset_state="WAIT_GROUP_ALIGN")
|
||||||
|
|
||||||
|
sampled_rxdata = Array(Signal(20) for _ in range(16))
|
||||||
|
sample_idx = Signal(4)
|
||||||
|
|
||||||
|
self.rx_fsm.act("WAIT_GROUP_ALIGN",
|
||||||
|
If(self.rx.err,
|
||||||
|
NextState("WRITE_ERR_UPPER"),
|
||||||
|
).Elif(self.rx.rxdata == 0b11111111111111111111,
|
||||||
|
NextValue(sampled_rxdata[0], self.rx.rxdata),
|
||||||
|
NextValue(sample_idx, 1),
|
||||||
|
NextState("SAMPLE_RXDATA"),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
self.rx_fsm.act("WRITE_ERR_UPPER",
|
||||||
|
If(self.tx_fifo.writable,
|
||||||
|
self.tx_fifo.we.eq(1),
|
||||||
|
self.tx_fifo.din.eq(0b01010101),
|
||||||
|
NextState("WRITE_ERR_LOWER"),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
self.rx_fsm.act("WRITE_ERR_LOWER",
|
||||||
|
If(self.tx_fifo.writable,
|
||||||
|
self.tx_fifo.we.eq(1),
|
||||||
|
self.tx_fifo.din.eq(0b10101010),
|
||||||
|
NextState("TERMINATE"),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
self.rx_fsm.act("SAMPLE_RXDATA",
|
||||||
|
If(sample_idx == 15,
|
||||||
|
NextValue(sample_idx, 0),
|
||||||
|
NextState("WRITE_PATTERN_FIRST_UPPER"),
|
||||||
|
).Else(
|
||||||
|
NextValue(sampled_rxdata[sample_idx], self.rx.rxdata),
|
||||||
|
NextValue(sample_idx, sample_idx + 1),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
self.rx_fsm.act("WRITE_PATTERN_FIRST_UPPER",
|
||||||
|
If(sample_idx == 15,
|
||||||
|
NextState("TERMINATE"),
|
||||||
|
).Elif(self.tx_fifo.writable,
|
||||||
|
self.tx_fifo.we.eq(1),
|
||||||
|
self.tx_fifo.din.eq(sampled_rxdata[sample_idx][8:10]),
|
||||||
|
NextState("WRITE_PATTERN_FIRST_LOWER"),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
self.rx_fsm.act("WRITE_PATTERN_FIRST_LOWER",
|
||||||
|
If(self.tx_fifo.writable,
|
||||||
|
self.tx_fifo.we.eq(1),
|
||||||
|
self.tx_fifo.din.eq(sampled_rxdata[sample_idx][:8]),
|
||||||
|
NextState("WRITE_PATTERN_SECOND_UPPER"),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
self.rx_fsm.act("WRITE_PATTERN_SECOND_UPPER",
|
||||||
|
If(self.tx_fifo.writable,
|
||||||
|
self.tx_fifo.we.eq(1),
|
||||||
|
self.tx_fifo.din.eq(sampled_rxdata[sample_idx][18:20]),
|
||||||
|
NextState("WRITE_PATTERN_SECOND_LOWER"),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
self.rx_fsm.act("WRITE_PATTERN_SECOND_LOWER",
|
||||||
|
If(self.tx_fifo.writable,
|
||||||
|
self.tx_fifo.we.eq(1),
|
||||||
|
self.tx_fifo.din.eq(sampled_rxdata[sample_idx][10:18]),
|
||||||
|
NextValue(sample_idx, sample_idx + 1),
|
||||||
|
NextState("WRITE_PATTERN_FIRST_UPPER"),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
self.rx_fsm.act("TERMINATE",
|
||||||
|
NextState("TERMINATE"),
|
||||||
|
)
|
||||||
|
|
||||||
|
self.submodules.tx_fsm = FSM(reset_state="SEND_TRAINING")
|
||||||
|
|
||||||
|
self.tx_fsm.act("SEND_TRAINING",
|
||||||
|
self.tx.txdata.eq(0b00100001000010000100),
|
||||||
|
If(self.rx.align_done,
|
||||||
|
NextState("SEND_ZERO"),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
send_zero_duration = Signal(2)
|
||||||
|
|
||||||
|
self.tx_fsm.act("SEND_ZERO",
|
||||||
|
self.tx.txdata.eq(0),
|
||||||
|
If(send_zero_duration == 0b11,
|
||||||
|
NextState("SEND_PULSE"),
|
||||||
|
).Else(
|
||||||
|
NextValue(send_zero_duration, send_zero_duration + 1),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
self.tx_fsm.act("SEND_PULSE",
|
||||||
|
self.tx.txdata.eq(0b11111111111111111111),
|
||||||
|
NextState("WAIT_GROUP_ALIGN"),
|
||||||
|
)
|
||||||
|
|
||||||
|
self.tx_fsm.act("WAIT_GROUP_ALIGN",
|
||||||
|
self.tx.txdata.eq(0),
|
||||||
|
If(self.rx.delay_done,
|
||||||
|
NextState("SEND_ARB_DATA1"),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
self.tx_fsm.act("SEND_ARB_DATA1",
|
||||||
|
self.tx.txdata.eq(0b00111001110011100111),
|
||||||
|
NextState("SEND_ARB_DATA2"),
|
||||||
|
)
|
||||||
|
|
||||||
|
self.tx_fsm.act("SEND_ARB_DATA2",
|
||||||
|
self.tx.txdata.eq(0),
|
||||||
|
NextState("SEND_ARB_DATA3"),
|
||||||
|
)
|
||||||
|
|
||||||
|
self.tx_fsm.act("SEND_ARB_DATA3",
|
||||||
|
self.tx.txdata.eq(0xDEADB),
|
||||||
|
NextState("SEND_ARB_DATA4"),
|
||||||
|
)
|
||||||
|
|
||||||
|
self.tx_fsm.act("SEND_ARB_DATA4",
|
||||||
|
self.tx.txdata.eq(0xBCAFE),
|
||||||
|
NextState("TERMINATE"),
|
||||||
|
)
|
||||||
|
|
||||||
|
self.tx_fsm.act("TERMINATE",
|
||||||
|
self.tx.txdata.eq(0),
|
||||||
|
NextState("TERMINATE"),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
import argparse
|
||||||
|
|
||||||
|
parser = argparse.ArgumentParser()
|
||||||
|
parser.add_argument("platform")
|
||||||
|
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]
|
||||||
|
sysclk = platform.request(sysclk_name[args.platform])
|
||||||
|
|
||||||
|
# Generate pads for the I/O blocks
|
||||||
|
# Using EEM1 for both as both EFC and Kasli has EEM1
|
||||||
|
# EEM1 are not interconnected
|
||||||
|
eem = 1
|
||||||
|
generate_pads(platform, eem)
|
||||||
|
pads = [
|
||||||
|
platform.request("dio{}".format(eem), i+4) for i in range(4)
|
||||||
|
]
|
||||||
|
# pad = platform.request("dio{}".format(eem), 0)
|
||||||
|
|
||||||
|
crg = TransceiverCRG(platform, sysclk)
|
||||||
|
top = MultiSerDesLoopBack(pads, crg.sys_clk_freq)
|
||||||
|
|
||||||
|
# Wire up UART core to the pads
|
||||||
|
uart_pads = platform.request("serial")
|
||||||
|
top.comb += [
|
||||||
|
top.uart_rx.eq(uart_pads.rx),
|
||||||
|
uart_pads.tx.eq(top.uart_tx),
|
||||||
|
]
|
||||||
|
|
||||||
|
top.submodules += crg
|
||||||
|
platform.build(top)
|
|
@ -0,0 +1,255 @@
|
||||||
|
from migen import *
|
||||||
|
from migen.genlib.fifo import SyncFIFO
|
||||||
|
from migen.genlib.misc import WaitTimer
|
||||||
|
from migen.build.platforms.sinara import kasli, efc
|
||||||
|
from eem_helpers import generate_pads
|
||||||
|
from serdes_comm import BiDirectionalIO
|
||||||
|
from sync_serdes import SingleLineRX, BitSlipReader, SlaveAligner
|
||||||
|
from uart_log import UARTLogger
|
||||||
|
from kasli_crg import TransceiverCRG
|
||||||
|
|
||||||
|
|
||||||
|
class SingleLineReader(Module):
|
||||||
|
def __init__(self, uart_log, i_pads, o_pads):
|
||||||
|
# UART Logger
|
||||||
|
self.submodules.uart_logger = uart_log
|
||||||
|
|
||||||
|
# Recorder
|
||||||
|
self.submodules.rx_buffer = SyncFIFO(10, 64)
|
||||||
|
|
||||||
|
# PHY Channel
|
||||||
|
self.submodules.channel = BiDirectionalIO(i_pads, o_pads)
|
||||||
|
|
||||||
|
# Single line RX SERDES
|
||||||
|
self.submodules.rx = SingleLineRX()
|
||||||
|
|
||||||
|
# bitslip reader
|
||||||
|
self.submodules.bitslip_reader = BitSlipReader()
|
||||||
|
self.submodules.postslip_reader = BitSlipReader()
|
||||||
|
|
||||||
|
# Alignment modules
|
||||||
|
self.submodules.slave_aligner = SlaveAligner()
|
||||||
|
|
||||||
|
# Connect SERDES to PHY
|
||||||
|
self.comb += [
|
||||||
|
self.rx.ser_in_no_dly.eq(self.channel.o[0]),
|
||||||
|
]
|
||||||
|
|
||||||
|
# Debugging logics
|
||||||
|
self.comb += [
|
||||||
|
self.bitslip_reader.loopback_rxdata.eq(self.rx.rxdata),
|
||||||
|
self.postslip_reader.loopback_rxdata.eq(self.rx.rxdata),
|
||||||
|
self.slave_aligner.loopback_rxdata.eq(self.rx.rxdata),
|
||||||
|
|
||||||
|
self.rx.master_bitslip.eq(
|
||||||
|
self.bitslip_reader.bitslip
|
||||||
|
| self.slave_aligner.master_bitslip
|
||||||
|
| self.postslip_reader.bitslip
|
||||||
|
),
|
||||||
|
self.rx.slave_bitslip.eq(
|
||||||
|
self.bitslip_reader.bitslip
|
||||||
|
| self.slave_aligner.master_bitslip
|
||||||
|
| self.postslip_reader.bitslip
|
||||||
|
),
|
||||||
|
]
|
||||||
|
|
||||||
|
bitslip_count = Signal(3)
|
||||||
|
|
||||||
|
fsm = FSM(reset_state="WAIT_DONE")
|
||||||
|
self.submodules += fsm
|
||||||
|
|
||||||
|
fsm.act("WAIT_DONE",
|
||||||
|
self.bitslip_reader.start.eq(1),
|
||||||
|
If(self.bitslip_reader.done,
|
||||||
|
NextValue(bitslip_count, 0),
|
||||||
|
NextState("WRITE_UPPER"),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
fsm.act("WRITE_UPPER",
|
||||||
|
If(bitslip_count == 5,
|
||||||
|
NextState("WAIT_BITSLIP_ALIGNED"),
|
||||||
|
).Elif(self.uart_logger.tx_fifo.writable,
|
||||||
|
self.uart_logger.tx_fifo.we.eq(1),
|
||||||
|
self.uart_logger.tx_fifo.din.eq(self.bitslip_reader.data_result[bitslip_count][8:]),
|
||||||
|
NextState("WRITE_LOWER"),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
fsm.act("WRITE_LOWER",
|
||||||
|
If(self.uart_logger.tx_fifo.writable,
|
||||||
|
self.uart_logger.tx_fifo.we.eq(1),
|
||||||
|
self.uart_logger.tx_fifo.din.eq(self.bitslip_reader.data_result[bitslip_count][:8]),
|
||||||
|
NextValue(bitslip_count, bitslip_count + 1),
|
||||||
|
NextState("WRITE_UPPER"),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
fsm.act("WAIT_BITSLIP_ALIGNED",
|
||||||
|
self.slave_aligner.start.eq(1),
|
||||||
|
If(self.slave_aligner.done,
|
||||||
|
NextState("BARRIER_UPPER"),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
fsm.act("BARRIER_UPPER",
|
||||||
|
If(self.uart_logger.tx_fifo.writable,
|
||||||
|
self.uart_logger.tx_fifo.we.eq(1),
|
||||||
|
self.uart_logger.tx_fifo.din.eq(0xFF),
|
||||||
|
NextState("BARRIER_LOWER"),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
fsm.act("BARRIER_LOWER",
|
||||||
|
If(self.uart_logger.tx_fifo.writable,
|
||||||
|
self.uart_logger.tx_fifo.we.eq(1),
|
||||||
|
self.uart_logger.tx_fifo.din.eq(0xFF),
|
||||||
|
NextState("REWAIT_BITSLIP_READ_DONE"),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
fsm.act("REWAIT_BITSLIP_READ_DONE",
|
||||||
|
self.postslip_reader.start.eq(1),
|
||||||
|
If(self.postslip_reader.done,
|
||||||
|
NextValue(bitslip_count, 0),
|
||||||
|
NextState("REWRITE_UPPER"),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
fsm.act("REWRITE_UPPER",
|
||||||
|
If(bitslip_count == 5,
|
||||||
|
NextState("TERMINATE"),
|
||||||
|
).Elif(self.uart_logger.tx_fifo.writable,
|
||||||
|
self.uart_logger.tx_fifo.we.eq(1),
|
||||||
|
self.uart_logger.tx_fifo.din.eq(self.postslip_reader.data_result[bitslip_count][8:]),
|
||||||
|
NextState("REWRITE_LOWER"),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
fsm.act("REWRITE_LOWER",
|
||||||
|
If(self.uart_logger.tx_fifo.writable,
|
||||||
|
self.uart_logger.tx_fifo.we.eq(1),
|
||||||
|
self.uart_logger.tx_fifo.din.eq(self.postslip_reader.data_result[bitslip_count][:8]),
|
||||||
|
NextValue(bitslip_count, bitslip_count + 1),
|
||||||
|
NextState("REWRITE_UPPER"),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
fsm.act("TERMINATE",
|
||||||
|
NextState("TERMINATE"),
|
||||||
|
)
|
||||||
|
|
||||||
|
# self.submodules.wait_timer = WaitTimer(127)
|
||||||
|
# shift_count = Signal(5)
|
||||||
|
|
||||||
|
# self.submodules.fsm = FSM(reset_state="WAIT")
|
||||||
|
|
||||||
|
# self.fsm.act("WAIT",
|
||||||
|
# self.wait_timer.wait.eq(1),
|
||||||
|
# If(shift_count == 10,
|
||||||
|
# NextState("DUMP_UPPER"),
|
||||||
|
# ).Elif(self.wait_timer.done,
|
||||||
|
# NextState("MEASURE"),
|
||||||
|
# ),
|
||||||
|
# )
|
||||||
|
|
||||||
|
# self.fsm.act("MEASURE",
|
||||||
|
# If(self.rx_buffer.writable,
|
||||||
|
# self.rx_buffer.we.eq(1),
|
||||||
|
# self.rx_buffer.din.eq(self.rx.rxdata),
|
||||||
|
# NextState("SHIFT1"),
|
||||||
|
# ),
|
||||||
|
# )
|
||||||
|
|
||||||
|
# self.fsm.act("SHIFT1",
|
||||||
|
# self.rx.master_bitslip.eq(1),
|
||||||
|
# self.rx.slave_bitslip.eq(1),
|
||||||
|
# NextState("GAP"),
|
||||||
|
# )
|
||||||
|
|
||||||
|
# self.fsm.act("GAP",
|
||||||
|
# NextState("SHIFT2"),
|
||||||
|
# )
|
||||||
|
|
||||||
|
# self.fsm.act("SHIFT2",
|
||||||
|
# self.rx.master_bitslip.eq(1),
|
||||||
|
# self.rx.slave_bitslip.eq(1),
|
||||||
|
# NextValue(shift_count, shift_count + 1),
|
||||||
|
# NextState("WAIT"),
|
||||||
|
# )
|
||||||
|
|
||||||
|
# self.fsm.act("DUMP_UPPER",
|
||||||
|
# If(self.rx_buffer.readable,
|
||||||
|
# If(self.uart_logger.tx_fifo.writable,
|
||||||
|
# self.uart_logger.tx_fifo.we.eq(1),
|
||||||
|
# self.uart_logger.tx_fifo.din.eq(self.rx_buffer.dout[8:]),
|
||||||
|
# NextState("DUMP_LOWER"),
|
||||||
|
# ),
|
||||||
|
# ).Else(
|
||||||
|
# NextState("TERMINATE"),
|
||||||
|
# )
|
||||||
|
# )
|
||||||
|
|
||||||
|
# self.fsm.act("DUMP_LOWER",
|
||||||
|
# If(self.uart_logger.tx_fifo.writable,
|
||||||
|
# self.uart_logger.tx_fifo.we.eq(1),
|
||||||
|
# self.uart_logger.tx_fifo.din.eq(self.rx_buffer.dout[:8]),
|
||||||
|
# self.rx_buffer.re.eq(1),
|
||||||
|
# NextState("DUMP_UPPER"),
|
||||||
|
# ),
|
||||||
|
# )
|
||||||
|
|
||||||
|
# self.fsm.act("TERMINATE",
|
||||||
|
# NextState("TERMINATE"),
|
||||||
|
# )
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
import argparse
|
||||||
|
import functools
|
||||||
|
import os
|
||||||
|
|
||||||
|
parser = argparse.ArgumentParser()
|
||||||
|
parser.add_argument("platform")
|
||||||
|
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]
|
||||||
|
sysclk = platform.request(sysclk_name[args.platform])
|
||||||
|
|
||||||
|
# Generate pads for the I/O blocks
|
||||||
|
for eem in range(2):
|
||||||
|
generate_pads(platform, eem)
|
||||||
|
data_eem = 0
|
||||||
|
|
||||||
|
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)
|
||||||
|
]
|
||||||
|
|
||||||
|
crg = TransceiverCRG(platform, sysclk)
|
||||||
|
uart_logger = UARTLogger(crg.sys_clk_freq)
|
||||||
|
top = SingleLineReader(uart_logger, i_pads, o_pads)
|
||||||
|
|
||||||
|
# Wire up UART core to the pads
|
||||||
|
uart_pads = platform.request("serial")
|
||||||
|
top.comb += [
|
||||||
|
uart_logger.uart_rx.eq(uart_pads.rx),
|
||||||
|
uart_pads.tx.eq(uart_logger.uart_tx),
|
||||||
|
]
|
||||||
|
|
||||||
|
top.submodules += crg
|
||||||
|
|
||||||
|
output_dir = "{}_{}_build".format(args.platform, "satellite")
|
||||||
|
platform.build(top, build_dir=output_dir)
|
|
@ -0,0 +1,395 @@
|
||||||
|
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)
|
|
@ -0,0 +1,50 @@
|
||||||
|
from migen import *
|
||||||
|
from migen.build.platforms.sinara import kasli, efc
|
||||||
|
from sync_serdes import MultiLineTX
|
||||||
|
from bidirectionalIO import BiDirectionalIO
|
||||||
|
from eem_helpers import generate_pads
|
||||||
|
from kasli_crg import TransceiverCRG
|
||||||
|
|
||||||
|
|
||||||
|
class ShortPulseTX(Module):
|
||||||
|
def __init__(self, i_pads, o_pads):
|
||||||
|
# TX serdes
|
||||||
|
self.submodules.tx = MultiLineTX()
|
||||||
|
# TX PHY
|
||||||
|
self.submodules.channel = BiDirectionalIO(i_pads, o_pads)
|
||||||
|
|
||||||
|
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),
|
||||||
|
|
||||||
|
# Hardwire TX with 1 pulse signal
|
||||||
|
self.tx.txdata.eq(0b00000000000000010000)
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
platform = efc.Platform()
|
||||||
|
|
||||||
|
# Generate pads for the I/O blocks
|
||||||
|
for eem in range(2):
|
||||||
|
generate_pads(platform, eem)
|
||||||
|
data_eem = 0
|
||||||
|
|
||||||
|
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)
|
||||||
|
]
|
||||||
|
|
||||||
|
crg = TransceiverCRG(platform, platform.request("gtp_clk"))
|
||||||
|
top = ShortPulseTX(i_pads, o_pads)
|
||||||
|
|
||||||
|
top.submodules += crg
|
||||||
|
|
||||||
|
output_dir = "{}_{}_build".format("efc", "master")
|
||||||
|
platform.build(top, build_dir=output_dir)
|
|
@ -1,9 +1,9 @@
|
||||||
from migen import *
|
from migen import *
|
||||||
from sync_serdes import *
|
from sync_serdes import *
|
||||||
from migen.genlib.fifo import SyncFIFO
|
from migen.genlib.fifo import SyncFIFO
|
||||||
from migen.build.platforms.sinara import kasli
|
from migen.build.platforms.sinara import kasli, efc
|
||||||
from migen.genlib.misc import WaitTimer
|
from migen.genlib.misc import WaitTimer
|
||||||
from kasli_crg import KasliCRG
|
from kasli_crg import TransceiverCRG
|
||||||
from eem_helpers import generate_pads
|
from eem_helpers import generate_pads
|
||||||
from uart import UART
|
from uart import UART
|
||||||
from io_loopback import SingleIOLoopback
|
from io_loopback import SingleIOLoopback
|
||||||
|
@ -82,11 +82,6 @@ class SingleSerDesLoopBack(Module):
|
||||||
# Debugging logics
|
# Debugging logics
|
||||||
if debug:
|
if debug:
|
||||||
self.comb += [
|
self.comb += [
|
||||||
# Start the reader initially
|
|
||||||
self.bitslip_reader.start.eq(1),
|
|
||||||
self.post_align_reader.start.eq(0),
|
|
||||||
self.phase_reader.start.eq(0),
|
|
||||||
|
|
||||||
self.bitslip_reader.loopback_rxdata.eq(self.rx.rxdata),
|
self.bitslip_reader.loopback_rxdata.eq(self.rx.rxdata),
|
||||||
self.post_align_reader.loopback_rxdata.eq(self.rx.rxdata),
|
self.post_align_reader.loopback_rxdata.eq(self.rx.rxdata),
|
||||||
self.phase_reader.loopback_rxdata.eq(self.rx.rxdata),
|
self.phase_reader.loopback_rxdata.eq(self.rx.rxdata),
|
||||||
|
@ -212,7 +207,9 @@ class SingleSerDesLoopBack(Module):
|
||||||
self.submodules += fsm
|
self.submodules += fsm
|
||||||
|
|
||||||
fsm.act("WAIT_DONE",
|
fsm.act("WAIT_DONE",
|
||||||
|
self.bitslip_reader.start.eq(1),
|
||||||
If(self.bitslip_reader.done,
|
If(self.bitslip_reader.done,
|
||||||
|
NextValue(bitslip_count, 0),
|
||||||
NextState("WRITE_UPPER"),
|
NextState("WRITE_UPPER"),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
@ -229,10 +226,12 @@ class SingleSerDesLoopBack(Module):
|
||||||
)
|
)
|
||||||
|
|
||||||
fsm.act("WRITE_LOWER",
|
fsm.act("WRITE_LOWER",
|
||||||
self.tx_fifo.we.eq(1),
|
If(self.tx_fifo.writable,
|
||||||
self.tx_fifo.din.eq(self.bitslip_reader.data_result[bitslip_count][:8]),
|
self.tx_fifo.we.eq(1),
|
||||||
NextValue(bitslip_count, bitslip_count + 1),
|
self.tx_fifo.din.eq(self.bitslip_reader.data_result[bitslip_count][:8]),
|
||||||
NextState("WRITE_UPPER"),
|
NextValue(bitslip_count, bitslip_count + 1),
|
||||||
|
NextState("WRITE_UPPER"),
|
||||||
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
fsm.act("START_SLAVE_ALIGNER",
|
fsm.act("START_SLAVE_ALIGNER",
|
||||||
|
@ -373,6 +372,12 @@ class SingleSerDesLoopBack(Module):
|
||||||
If(self.tx_fifo.writable,
|
If(self.tx_fifo.writable,
|
||||||
self.tx_fifo.we.eq(1),
|
self.tx_fifo.we.eq(1),
|
||||||
self.tx_fifo.din.eq(self.delay_solver.opt_delay_tap),
|
self.tx_fifo.din.eq(self.delay_solver.opt_delay_tap),
|
||||||
|
NextState("WAIT_OPT_DELAY_ACTIVE"),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
fsm.act("WAIT_OPT_DELAY_ACTIVE",
|
||||||
|
If(self.rx.cnt_out == self.delay_solver.opt_delay_tap,
|
||||||
NextState("RESAMPLE_RXDATA_UPPER"),
|
NextState("RESAMPLE_RXDATA_UPPER"),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
@ -465,18 +470,16 @@ class SingleSerDesLoopBack(Module):
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
platform = kasli.Platform(hw_rev="v2.0")
|
# platform = kasli.Platform(hw_rev="v2.0")
|
||||||
|
platform = efc.Platform()
|
||||||
|
|
||||||
# Generate pads for the I/O blocks
|
# Generate pads for the I/O blocks
|
||||||
eem = 3
|
eem = 1
|
||||||
generate_pads(platform, eem)
|
generate_pads(platform, eem)
|
||||||
# pads = [
|
|
||||||
# platform.request("dio{}".format(eem), i) for i in range(4)
|
|
||||||
# ]
|
|
||||||
pad = platform.request("dio{}".format(eem), 0)
|
pad = platform.request("dio{}".format(eem), 0)
|
||||||
|
|
||||||
crg = KasliCRG(platform)
|
crg = TransceiverCRG(platform, platform.request("gtp_clk"))
|
||||||
top = SingleSerDesLoopBack(pad, crg.sys_clk_freq, False)
|
top = SingleSerDesLoopBack(pad, crg.sys_clk_freq, True)
|
||||||
|
|
||||||
# Wire up UART core to the pads
|
# Wire up UART core to the pads
|
||||||
uart_pads = platform.request("serial")
|
uart_pads = platform.request("serial")
|
||||||
|
|
221
sync_serdes.py
221
sync_serdes.py
|
@ -1,5 +1,6 @@
|
||||||
from migen import *
|
from migen import *
|
||||||
from migen.genlib.misc import WaitTimer
|
from migen.genlib.misc import WaitTimer
|
||||||
|
from migen.genlib.fifo import SyncFIFO
|
||||||
from util import PriorityEncoderMSB
|
from util import PriorityEncoderMSB
|
||||||
|
|
||||||
|
|
||||||
|
@ -36,7 +37,6 @@ class SingleLineRX(Module):
|
||||||
self.ce = Signal()
|
self.ce = Signal()
|
||||||
self.cnt_in = Signal(5)
|
self.cnt_in = Signal(5)
|
||||||
self.cnt_out = Signal(5)
|
self.cnt_out = Signal(5)
|
||||||
self.opt_delay = Signal(5)
|
|
||||||
self.master_bitslip = Signal()
|
self.master_bitslip = Signal()
|
||||||
self.slave_bitslip = Signal()
|
self.slave_bitslip = Signal()
|
||||||
|
|
||||||
|
@ -207,9 +207,8 @@ class SlaveAligner(Module):
|
||||||
self.done = Signal()
|
self.done = Signal()
|
||||||
self.master_bitslip = Signal()
|
self.master_bitslip = Signal()
|
||||||
self.slave_bitslip = Signal()
|
self.slave_bitslip = Signal()
|
||||||
# self.data_result = Array(Signal(10) for _ in range(5))
|
|
||||||
|
|
||||||
self.slip_count = Signal(3)
|
slip_count = Signal(3)
|
||||||
|
|
||||||
check_odd = Signal()
|
check_odd = Signal()
|
||||||
check_even = Signal()
|
check_even = Signal()
|
||||||
|
@ -220,8 +219,6 @@ class SlaveAligner(Module):
|
||||||
fsm.act("WAIT_START",
|
fsm.act("WAIT_START",
|
||||||
If(self.start,
|
If(self.start,
|
||||||
NextState("WAIT_TIMER"),
|
NextState("WAIT_TIMER"),
|
||||||
).Else(
|
|
||||||
NextState("WAIT_START"),
|
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -244,7 +241,7 @@ class SlaveAligner(Module):
|
||||||
NextValue(check_even, self.loopback_rxdata[0]),
|
NextValue(check_even, self.loopback_rxdata[0]),
|
||||||
NextState("CHECK_MASTER_BITSLIP"),
|
NextState("CHECK_MASTER_BITSLIP"),
|
||||||
).Else(
|
).Else(
|
||||||
NextValue(self.slip_count, self.slip_count + 1),
|
NextValue(slip_count, slip_count + 1),
|
||||||
NextState("HIGH_BITSLIP_FIRST"),
|
NextState("HIGH_BITSLIP_FIRST"),
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
@ -266,7 +263,7 @@ class SlaveAligner(Module):
|
||||||
fsm.act("HIGH_BITSLIP_SECOND",
|
fsm.act("HIGH_BITSLIP_SECOND",
|
||||||
self.master_bitslip.eq(1),
|
self.master_bitslip.eq(1),
|
||||||
self.slave_bitslip.eq(1),
|
self.slave_bitslip.eq(1),
|
||||||
If(self.slip_count == 5,
|
If(slip_count == 5,
|
||||||
NextState("SHIFT_WAIT_TIMER"),
|
NextState("SHIFT_WAIT_TIMER"),
|
||||||
).Else(
|
).Else(
|
||||||
NextState("WAIT_TIMER"),
|
NextState("WAIT_TIMER"),
|
||||||
|
@ -314,7 +311,7 @@ class SlaveAligner(Module):
|
||||||
# After eliminating the potentially duplicating pattern,
|
# After eliminating the potentially duplicating pattern,
|
||||||
# Shift the entire output pattern for delay tap optimization
|
# Shift the entire output pattern for delay tap optimization
|
||||||
# Ideally, the optimized first edge would be the middle pair
|
# Ideally, the optimized first edge would be the middle pair
|
||||||
# So, shift it until bit 4/5 is set and bit 6 is not set
|
# So, shift it until bit 3/4 is set but bit 5 is not set
|
||||||
fsm.act("SHIFT_WAIT_TIMER",
|
fsm.act("SHIFT_WAIT_TIMER",
|
||||||
self.stab_timer.wait.eq(1),
|
self.stab_timer.wait.eq(1),
|
||||||
If(self.stab_timer.done,
|
If(self.stab_timer.done,
|
||||||
|
@ -323,7 +320,7 @@ class SlaveAligner(Module):
|
||||||
)
|
)
|
||||||
|
|
||||||
fsm.act("SHIFT_SAMPLE_PATTERN",
|
fsm.act("SHIFT_SAMPLE_PATTERN",
|
||||||
If((self.loopback_rxdata[4:6] != 0) & ~self.loopback_rxdata[6],
|
If((self.loopback_rxdata[3:5] != 0) & ~self.loopback_rxdata[5],
|
||||||
NextState("TERMINATE"),
|
NextState("TERMINATE"),
|
||||||
).Else(
|
).Else(
|
||||||
NextState("SHIFT_HIGH_BITSLIP_FIRST"),
|
NextState("SHIFT_HIGH_BITSLIP_FIRST"),
|
||||||
|
@ -552,7 +549,13 @@ class DelayOptimizer(Module):
|
||||||
fsm.act("LOAD_OPT_DELAY",
|
fsm.act("LOAD_OPT_DELAY",
|
||||||
self.ld.eq(1),
|
self.ld.eq(1),
|
||||||
# The optimal delay tap is prepared in the SAMPLE_PULSE_OUT state
|
# The optimal delay tap is prepared in the SAMPLE_PULSE_OUT state
|
||||||
NextState("TERMINATE"),
|
NextState("WAIT_DELAY_LOAD"),
|
||||||
|
)
|
||||||
|
|
||||||
|
fsm.act("WAIT_DELAY_LOAD",
|
||||||
|
If(self.delay_tap == self.opt_delay_tap,
|
||||||
|
NextState("TERMINATE"),
|
||||||
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
fsm.act("TERMINATE",
|
fsm.act("TERMINATE",
|
||||||
|
@ -560,3 +563,201 @@ class DelayOptimizer(Module):
|
||||||
self.select_odd.eq(self.expected_pulse[0]),
|
self.select_odd.eq(self.expected_pulse[0]),
|
||||||
NextState("TERMINATE"),
|
NextState("TERMINATE"),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class SyncSingleRX(Module):
|
||||||
|
def __init__(self):
|
||||||
|
# Ports
|
||||||
|
# IN: Undelayed serial signal
|
||||||
|
self.ser_in_no_dly = Signal()
|
||||||
|
# IN: Start RX alignment signal
|
||||||
|
self.start = Signal()
|
||||||
|
# OUT: Received data after self-alignment, decimation
|
||||||
|
self.rxdata = Signal(5)
|
||||||
|
# OUT: RXDATA from this channel is self-aligned
|
||||||
|
self.align_done = Signal()
|
||||||
|
|
||||||
|
# Components
|
||||||
|
self.submodules.rx = SingleLineRX()
|
||||||
|
self.submodules.slave_aligner = SlaveAligner()
|
||||||
|
self.submodules.delay_solver = DelayOptimizer()
|
||||||
|
|
||||||
|
# Sample decimation
|
||||||
|
select_odd = Signal()
|
||||||
|
decimated_rxdata = Signal(5)
|
||||||
|
|
||||||
|
# Dataflow
|
||||||
|
self.comb += [
|
||||||
|
# Delay and oversample the original signal
|
||||||
|
self.rx.ser_in_no_dly.eq(self.ser_in_no_dly),
|
||||||
|
|
||||||
|
# Use the deserialized signals for alignment
|
||||||
|
self.slave_aligner.loopback_rxdata.eq(self.rx.rxdata),
|
||||||
|
self.delay_solver.loopback_rxdata.eq(self.rx.rxdata),
|
||||||
|
|
||||||
|
# Decimate the oversampled signals
|
||||||
|
If(select_odd,
|
||||||
|
decimated_rxdata.eq(self.rx.rxdata[1::2]),
|
||||||
|
).Else(
|
||||||
|
decimated_rxdata.eq(self.rx.rxdata[::2]),
|
||||||
|
),
|
||||||
|
|
||||||
|
# Send it to the output
|
||||||
|
self.rxdata.eq(decimated_rxdata),
|
||||||
|
]
|
||||||
|
|
||||||
|
# Control signals
|
||||||
|
self.comb += [
|
||||||
|
# Bitslip alignment
|
||||||
|
self.rx.master_bitslip.eq(self.slave_aligner.master_bitslip),
|
||||||
|
self.rx.slave_bitslip.eq(self.slave_aligner.slave_bitslip),
|
||||||
|
|
||||||
|
# Tap delay optimization
|
||||||
|
self.rx.ce.eq(self.delay_solver.inc_en),
|
||||||
|
self.rx.ld.eq(self.delay_solver.ld),
|
||||||
|
self.rx.cnt_in.eq(self.delay_solver.opt_delay_tap),
|
||||||
|
self.delay_solver.delay_tap.eq(self.rx.cnt_out),
|
||||||
|
]
|
||||||
|
|
||||||
|
self.submodules.fsm = FSM(reset_state="WAIT_SIGNAL")
|
||||||
|
|
||||||
|
self.fsm.act("WAIT_SIGNAL",
|
||||||
|
If((self.rx.rxdata != 0) & self.start,
|
||||||
|
NextState("WAIT_ALIGNER")
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
self.fsm.act("WAIT_ALIGNER",
|
||||||
|
self.slave_aligner.start.eq(1),
|
||||||
|
If(self.slave_aligner.done,
|
||||||
|
NextState("WAIT_DELAY_OPT"),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
self.fsm.act("WAIT_DELAY_OPT",
|
||||||
|
self.delay_solver.start.eq(1),
|
||||||
|
If(self.delay_solver.done,
|
||||||
|
NextValue(select_odd, self.delay_solver.select_odd),
|
||||||
|
NextState("INTRA_ALIGN_DONE"),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
self.fsm.act("INTRA_ALIGN_DONE",
|
||||||
|
self.align_done.eq(1),
|
||||||
|
NextState("INTRA_ALIGN_DONE"),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class MultiLineTX(Module):
|
||||||
|
def __init__(self):
|
||||||
|
# Ports
|
||||||
|
# IN: Unserialized data
|
||||||
|
self.txdata = Signal(20)
|
||||||
|
# OUT: Serialized data
|
||||||
|
self.ser_out = Signal(4)
|
||||||
|
# OUT: 3-state signal output
|
||||||
|
self.t_out = Signal(4)
|
||||||
|
|
||||||
|
for idx in range(4):
|
||||||
|
single_tx = SingleLineTX()
|
||||||
|
|
||||||
|
self.comb += [
|
||||||
|
self.ser_out[idx].eq(single_tx.ser_out),
|
||||||
|
self.t_out[idx].eq(single_tx.t_out),
|
||||||
|
single_tx.txdata.eq(self.txdata[5*idx:5*(idx+1)]),
|
||||||
|
]
|
||||||
|
|
||||||
|
self.submodules += single_tx
|
||||||
|
|
||||||
|
|
||||||
|
class MultiLineRX(Module):
|
||||||
|
def __init__(self):
|
||||||
|
# Ports
|
||||||
|
# IN: Undelayed serial signal
|
||||||
|
self.ser_in_no_dly = Signal(4)
|
||||||
|
# IN: Start alignment process of all channels
|
||||||
|
self.start = Signal()
|
||||||
|
# OUT: Received data after self-alignment, decimation
|
||||||
|
self.rxdata = Signal(20)
|
||||||
|
# OUT: RXDATA from all channels are self-aligned
|
||||||
|
self.align_done = Signal()
|
||||||
|
# OUT: Group delay compensated
|
||||||
|
self.delay_done = Signal()
|
||||||
|
# OUT: Group delay adjustment failed
|
||||||
|
self.err = Signal()
|
||||||
|
|
||||||
|
channel_align_done = Signal(4)
|
||||||
|
self.comb += self.align_done.eq(channel_align_done == 0b1111)
|
||||||
|
|
||||||
|
buffer_outflow = Signal(4)
|
||||||
|
self.comb += buffer_outflow.eq(0b1111)
|
||||||
|
|
||||||
|
for idx in range(4):
|
||||||
|
single_rx = SyncSingleRX()
|
||||||
|
|
||||||
|
self.comb += [
|
||||||
|
single_rx.ser_in_no_dly.eq(self.ser_in_no_dly[idx]),
|
||||||
|
channel_align_done[idx].eq(single_rx.align_done),
|
||||||
|
# Propagate start alignment signal to all channels
|
||||||
|
single_rx.start.eq(self.start),
|
||||||
|
]
|
||||||
|
|
||||||
|
# FIFOs for handling group delay
|
||||||
|
# Signal from each OSERDES group can have a different delay
|
||||||
|
# So, add delay to the groups that receives the pulse early
|
||||||
|
# Maximum delay = 8
|
||||||
|
channel_buffer = SyncFIFO(5, 16)
|
||||||
|
|
||||||
|
self.comb += [
|
||||||
|
# Allow data go through the FIFO unless aligning
|
||||||
|
# Pay the memory delay cost
|
||||||
|
channel_buffer.we.eq(1),
|
||||||
|
channel_buffer.re.eq(buffer_outflow[idx]),
|
||||||
|
|
||||||
|
# Data always flow from individual RX to the rxdata port
|
||||||
|
channel_buffer.din.eq(single_rx.rxdata),
|
||||||
|
self.rxdata[5*idx:5*(idx+1)].eq(channel_buffer.dout),
|
||||||
|
]
|
||||||
|
|
||||||
|
# If at any point the FIFO fills up,
|
||||||
|
# group delay can no longer be determined and compensated
|
||||||
|
self.sync += [
|
||||||
|
If(~channel_buffer.writable,
|
||||||
|
self.err.eq(1),
|
||||||
|
),
|
||||||
|
]
|
||||||
|
|
||||||
|
self.submodules += [ single_rx, channel_buffer ]
|
||||||
|
|
||||||
|
self.submodules.fsm = FSM(reset_state="WAIT_ALIGN_DONE")
|
||||||
|
|
||||||
|
self.fsm.act("WAIT_ALIGN_DONE",
|
||||||
|
If(self.align_done,
|
||||||
|
NextState("WAIT_ZERO"),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
self.fsm.act("WAIT_ZERO",
|
||||||
|
If(self.rxdata == 0,
|
||||||
|
NextState("WAIT_PULSE"),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
self.fsm.act("WAIT_PULSE",
|
||||||
|
# Control outflow until all channels finds the pulse
|
||||||
|
If(self.rxdata == 0b11111111111111111111,
|
||||||
|
buffer_outflow.eq(0b1111),
|
||||||
|
self.delay_done.eq(1),
|
||||||
|
NextState("GROUP_DELAY_DONE"),
|
||||||
|
).Else(
|
||||||
|
buffer_outflow[0].eq(self.rxdata[ 0: 5] == 0),
|
||||||
|
buffer_outflow[1].eq(self.rxdata[ 5:10] == 0),
|
||||||
|
buffer_outflow[2].eq(self.rxdata[10:15] == 0),
|
||||||
|
buffer_outflow[3].eq(self.rxdata[15:20] == 0),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
self.fsm.act("GROUP_DELAY_DONE",
|
||||||
|
self.delay_done.eq(1),
|
||||||
|
NextState("GROUP_DELAY_DONE"),
|
||||||
|
)
|
||||||
|
|
|
@ -0,0 +1,103 @@
|
||||||
|
from migen import *
|
||||||
|
from multi_coders import MultiEncoder, CrossbarDecoder
|
||||||
|
|
||||||
|
|
||||||
|
class IdentityCoders(Module):
|
||||||
|
def __init__(self):
|
||||||
|
self.submodules.encoder = MultiEncoder(lsb_first=False)
|
||||||
|
decoders = [ CrossbarDecoder(lsb_first=False) for _ in range(2) ]
|
||||||
|
self.submodules += decoders
|
||||||
|
|
||||||
|
# Interface fo input/output
|
||||||
|
self.d_in = [ Signal(8) for _ in range(2) ]
|
||||||
|
self.k_in = [ Signal() for _ in range(2) ]
|
||||||
|
self.d_out = [ Signal(8) for _ in range(2) ]
|
||||||
|
self.k_out = [ Signal() for _ in range(2) ]
|
||||||
|
|
||||||
|
# Signal to start both encoders & decoders
|
||||||
|
self.encoder_start = Signal()
|
||||||
|
self.decoder_start = Signal()
|
||||||
|
self.comb += self.encoder.start.eq(self.encoder_start)
|
||||||
|
for decoder in decoders:
|
||||||
|
self.comb += decoder.start.eq(self.decoder_start)
|
||||||
|
|
||||||
|
# Interconnect encoders and decoders
|
||||||
|
for encoder_output, decoder in zip(self.encoder.output, decoders):
|
||||||
|
self.sync += decoder.raw_input.eq(encoder_output)
|
||||||
|
|
||||||
|
for d_in, k_in, encoder_d, encoder_k in \
|
||||||
|
zip(self.d_in, self.k_in, self.encoder.d, self.encoder.k):
|
||||||
|
self.comb += [
|
||||||
|
# Connect symbols to encoder
|
||||||
|
encoder_d.eq(d_in),
|
||||||
|
encoder_k.eq(k_in),
|
||||||
|
]
|
||||||
|
|
||||||
|
# Connect symbols from decoder
|
||||||
|
for d_out, k_out, decoder in zip(self.d_out, self.k_out, decoders):
|
||||||
|
self.comb += [
|
||||||
|
d_out.eq(decoder.d),
|
||||||
|
k_out.eq(decoder.k),
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
import random
|
||||||
|
|
||||||
|
|
||||||
|
def testbench(dut, transmission_delay=1):
|
||||||
|
data_size = 256
|
||||||
|
list_of_data = [
|
||||||
|
(random.randint(0, 0xFF), random.getrandbits(1)) \
|
||||||
|
for _ in range(data_size)
|
||||||
|
]
|
||||||
|
# Control characters
|
||||||
|
controls = [
|
||||||
|
0x1C, 0x3C, 0x5C, 0x7C, 0x9C, 0xBC, 0xDC, 0xFC,
|
||||||
|
0xF7, 0xFB, 0xFD, 0xFE
|
||||||
|
]
|
||||||
|
|
||||||
|
# Correct control symbols
|
||||||
|
for idx, (data, control) in enumerate(list_of_data):
|
||||||
|
if control:
|
||||||
|
list_of_data[idx] = (controls[random.randint(0, len(controls) - 1)], 1)
|
||||||
|
# Decoder, Encoder, and the channel all introduces delay
|
||||||
|
delay_list = [ None ] * 5
|
||||||
|
|
||||||
|
send_list = list_of_data + delay_list
|
||||||
|
recv_list = delay_list + list_of_data
|
||||||
|
|
||||||
|
yield dut.encoder_start.eq(1)
|
||||||
|
# Skip exactly 1 cycle. The channel has 1 cycle delay.
|
||||||
|
yield
|
||||||
|
yield dut.decoder_start.eq(1)
|
||||||
|
|
||||||
|
for _ in range(transmission_delay):
|
||||||
|
yield
|
||||||
|
|
||||||
|
for data_in, data_out in zip(send_list, recv_list):
|
||||||
|
if data_in is not None:
|
||||||
|
d_in, k_in = data_in
|
||||||
|
yield dut.d_in[0].eq(d_in)
|
||||||
|
yield dut.d_in[1].eq(d_in)
|
||||||
|
yield dut.k_in[0].eq(k_in)
|
||||||
|
yield dut.k_in[1].eq(k_in)
|
||||||
|
else:
|
||||||
|
yield dut.d_in[0].eq(0)
|
||||||
|
yield dut.d_in[1].eq(0)
|
||||||
|
yield dut.k_in[0].eq(0)
|
||||||
|
yield dut.k_in[1].eq(0)
|
||||||
|
|
||||||
|
if data_out is not None:
|
||||||
|
d_out, k_out = data_out
|
||||||
|
assert (yield dut.d_out[0]) == d_out
|
||||||
|
assert (yield dut.d_out[1]) == d_out
|
||||||
|
assert (yield dut.k_out[0]) == k_out
|
||||||
|
assert (yield dut.k_out[1]) == k_out
|
||||||
|
yield
|
||||||
|
|
||||||
|
for _ in range(10):
|
||||||
|
yield
|
||||||
|
|
||||||
|
for delay in range(16):
|
||||||
|
dut = IdentityCoders()
|
||||||
|
run_simulation(dut, testbench(dut, delay), vcd_name="coders.vcd")
|
|
@ -0,0 +1,25 @@
|
||||||
|
from migen import *
|
||||||
|
from migen.genlib.fifo import SyncFIFO
|
||||||
|
from uart import UART
|
||||||
|
|
||||||
|
|
||||||
|
class UARTLogger(Module):
|
||||||
|
def __init__(self, sys_clk_freq):
|
||||||
|
# UART PHY interface
|
||||||
|
self.uart_rx = Signal()
|
||||||
|
self.uart_tx = Signal()
|
||||||
|
|
||||||
|
# Attach FIFO to UART TX, send rate is too slow w.r.t sysclk
|
||||||
|
self.submodules.tx_fifo = SyncFIFO(8, 64)
|
||||||
|
|
||||||
|
self.submodules.uart = UART(round((115200/sys_clk_freq)*2**32))
|
||||||
|
self.comb += [
|
||||||
|
# PHY connection
|
||||||
|
self.uart.phy_rx.eq(self.uart_rx),
|
||||||
|
self.uart_tx.eq(self.uart.phy_tx),
|
||||||
|
|
||||||
|
# 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),
|
||||||
|
]
|
Loading…
Reference in New Issue