forked from M-Labs/artiq-zynq
Compare commits
30 Commits
f746f66a3a
...
f2d8ffbe05
Author | SHA1 | Date |
---|---|---|
morgan | f2d8ffbe05 | |
morgan | 98bb49e1b5 | |
morgan | d88af397ec | |
morgan | 468f9d7185 | |
morgan | 08cade7f16 | |
morgan | f5eb196726 | |
morgan | d55bd81137 | |
morgan | 4170e63a6e | |
morgan | a39c939a68 | |
morgan | 97bf7e72b7 | |
morgan | 868ac41060 | |
morgan | e78d0f4083 | |
morgan | bc0d45cd82 | |
morgan | 1331281e5c | |
morgan | 75407b2ff4 | |
morgan | 0cf8cd42d5 | |
morgan | b8aea61fd8 | |
morgan | 2aa194390f | |
morgan | 94d569502b | |
morgan | 679b430d74 | |
morgan | 7e6d4e186f | |
morgan | 7631907d76 | |
morgan | 95297157c4 | |
morgan | f34f500ed8 | |
morgan | 2dade34119 | |
morgan | 1460f5b94b | |
morgan | 66dee0b812 | |
morgan | 3102dd8a52 | |
morgan | 1ddd6f4a4a | |
morgan | 6f6e580a92 |
|
@ -1,40 +1,101 @@
|
||||||
from migen import *
|
from migen import *
|
||||||
from misoc.interconnect.csr import *
|
from misoc.interconnect.csr import *
|
||||||
|
from misoc.interconnect import stream
|
||||||
from misoc.cores.liteeth_mini.mac.crc import LiteEthMACCRCEngine
|
from misoc.cores.liteeth_mini.mac.crc import LiteEthMACCRCEngine
|
||||||
|
|
||||||
from cxp_downconn import CXP_DownConn
|
from cxp_downconn import CXP_DownConn
|
||||||
from cxp_upconn import CXP_UpConn
|
from cxp_upconn import CXP_UpConn
|
||||||
|
|
||||||
class CXP(Module, AutoCSR):
|
class CXP(Module, AutoCSR):
|
||||||
def __init__(self, refclk, pads, sys_clk_freq, debug_sma, pmod_pads):
|
def __init__(self, refclk, downconn_pads, upconn_pads, sys_clk_freq, debug_sma, pmod_pads):
|
||||||
self.submodules.crc = CXP_CRC(8)
|
self.submodules.crc = CXP_CRC(8)
|
||||||
# FIFOs with transmission priority
|
|
||||||
# 0: Trigger packet
|
|
||||||
# 1: IO acknowledgment for trigger packet
|
|
||||||
# 2: All other packets
|
|
||||||
self.submodules.upconn = CXP_UpConn(debug_sma, sys_clk_freq, pmod_pads)
|
|
||||||
|
|
||||||
self.submodules.downconn = CXP_DownConn(refclk, pads, sys_clk_freq, debug_sma, pmod_pads)
|
self.submodules.upconn = UpConn_Packets(upconn_pads, sys_clk_freq, debug_sma, pmod_pads)
|
||||||
|
|
||||||
|
self.submodules.downconn = CXP_DownConn(refclk, downconn_pads, sys_clk_freq, debug_sma, pmod_pads)
|
||||||
|
# TODO: support the option high speed upconn
|
||||||
|
|
||||||
|
# TODO: add link layer
|
||||||
|
|
||||||
|
|
||||||
class CXP_CRC(Module, AutoCSR):
|
class UpConn_Packets(Module, AutoCSR):
|
||||||
width = 32
|
def __init__(self, upconn_pads, sys_clk_freq, debug_sma, pmod_pads, fifos_depth=64):
|
||||||
polynom = 0x04C11DB7
|
|
||||||
seed = 2**width-1
|
|
||||||
def __init__(self, data_width):
|
|
||||||
self.d = Signal(data_width)
|
|
||||||
self.stb = Signal()
|
|
||||||
self.reset = Signal()
|
|
||||||
self.val = Signal(self.width, reset=self.seed)
|
|
||||||
|
|
||||||
self.data = CSR(data_width)
|
# increment after ack
|
||||||
self.en = CSR()
|
# for CXP 2.0 or latest, command packet need to includet tags
|
||||||
self.value = CSRStatus(self.width)
|
# section 9.6.1.2 (CXP-001-2021)
|
||||||
self.processed = CSRStatus(self.width)
|
self.tag_counts = Signal(max=0xFF)
|
||||||
|
self.use_tag = Signal()
|
||||||
|
|
||||||
|
self.bitrate2x_enable = CSRStorage()
|
||||||
|
self.tx_enable = CSRStorage()
|
||||||
|
self.encoded_data = CSRStatus(10)
|
||||||
|
|
||||||
# # #
|
# # #
|
||||||
|
|
||||||
self.submodules.engine = LiteEthMACCRCEngine(data_width, self.width, self.polynom)
|
self.submodules.upconn = upconn = CXP_UpConn(upconn_pads, sys_clk_freq, debug_sma, pmod_pads, fifos_depth)
|
||||||
|
|
||||||
|
self.comb += [
|
||||||
|
upconn.bitrate2x_enable.eq(self.bitrate2x_enable.storage),
|
||||||
|
upconn.tx_enable.eq(self.tx_enable.storage),
|
||||||
|
]
|
||||||
|
self.sync += [
|
||||||
|
self.encoded_data.status.eq(upconn.scheduler.encoder.output),
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
# Packet FIFOs with transmission priority
|
||||||
|
# 0: Trigger packet
|
||||||
|
self.symbol0 = CSR(9)
|
||||||
|
self.sync += [
|
||||||
|
upconn.tx_fifos.sink_stb[0].eq(self.symbol0.re),
|
||||||
|
upconn.tx_fifos.sink_data[0].eq(self.symbol0.r[:8]),
|
||||||
|
upconn.tx_fifos.sink_k[0].eq(self.symbol0.r[8]),
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
# 1: IO acknowledgment for trigger packet
|
||||||
|
self.symbol1 = CSR(9)
|
||||||
|
self.sync += [
|
||||||
|
upconn.tx_fifos.sink_stb[1].eq(self.symbol1.re),
|
||||||
|
upconn.tx_fifos.sink_data[1].eq(self.symbol1.r[:8]),
|
||||||
|
upconn.tx_fifos.sink_k[1].eq(self.symbol1.r[8]),
|
||||||
|
]
|
||||||
|
|
||||||
|
# 2: All other packets
|
||||||
|
# Control is not timing dependent, all the link layer is done in firmware
|
||||||
|
|
||||||
|
# Table 54 (CXP-001-2021)
|
||||||
|
# Largest CXP register is 8 byte
|
||||||
|
|
||||||
|
self.symbol2 = CSR(9)
|
||||||
|
self.sync += [
|
||||||
|
upconn.tx_fifos.sink_stb[2].eq(self.symbol2.re),
|
||||||
|
upconn.tx_fifos.sink_data[2].eq(self.symbol2.r[:8]),
|
||||||
|
upconn.tx_fifos.sink_k[2].eq(self.symbol2.r[8]),
|
||||||
|
]
|
||||||
|
|
||||||
|
class CXP_Packet(Module):
|
||||||
|
def __init__(self, max_packet_length):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class CXP_CRC(Module, AutoCSR):
|
||||||
|
def __init__(self, data_width):
|
||||||
|
# Section 9.2.2.2 (CXP-001-2021)
|
||||||
|
crc_width = 32
|
||||||
|
polynom = 0x04C11DB7
|
||||||
|
seed = 2**crc_width-1
|
||||||
|
|
||||||
|
self.d = Signal(data_width)
|
||||||
|
self.stb = Signal()
|
||||||
|
self.reset = Signal()
|
||||||
|
self.val = Signal(crc_width, reset=seed)
|
||||||
|
|
||||||
|
|
||||||
|
# # #
|
||||||
|
|
||||||
|
self.submodules.engine = LiteEthMACCRCEngine(data_width, crc_width, polynom)
|
||||||
|
|
||||||
self.sync += [
|
self.sync += [
|
||||||
self.val.eq(self.engine.next),
|
self.val.eq(self.engine.next),
|
||||||
|
@ -42,7 +103,8 @@ class CXP_CRC(Module, AutoCSR):
|
||||||
self.engine.data.eq(self.d),
|
self.engine.data.eq(self.d),
|
||||||
|
|
||||||
If(self.reset,
|
If(self.reset,
|
||||||
self.engine.last.eq(self.seed),
|
# because the seed is non zero, even if the data is 0x00, the engine output will be change :<
|
||||||
|
self.engine.last.eq(seed),
|
||||||
# clear reset bit
|
# clear reset bit
|
||||||
self.reset.eq(0),
|
self.reset.eq(0),
|
||||||
).Else(
|
).Else(
|
||||||
|
@ -64,6 +126,12 @@ class CXP_CRC(Module, AutoCSR):
|
||||||
p1.eq(self.engine.next[16:24][::-1]),
|
p1.eq(self.engine.next[16:24][::-1]),
|
||||||
p0.eq(self.engine.next[24:32][::-1]),
|
p0.eq(self.engine.next[24:32][::-1]),
|
||||||
]
|
]
|
||||||
|
|
||||||
|
self.data = CSR(data_width)
|
||||||
|
self.en = CSR()
|
||||||
|
self.value = CSRStatus(crc_width)
|
||||||
|
self.processed = CSRStatus(crc_width)
|
||||||
|
|
||||||
self.sync += [
|
self.sync += [
|
||||||
self.d.eq(self.data.r),
|
self.d.eq(self.data.r),
|
||||||
self.stb.eq(self.data.re),
|
self.stb.eq(self.data.re),
|
||||||
|
|
|
@ -19,7 +19,7 @@ fmc_adapter_io = [
|
||||||
Subsignal("txp", Pins("HPC:DP2_C2M_P")),
|
Subsignal("txp", Pins("HPC:DP2_C2M_P")),
|
||||||
Subsignal("txn", Pins("HPC:DP2_C2M_N")),
|
Subsignal("txn", Pins("HPC:DP2_C2M_N")),
|
||||||
Subsignal("rxp", Pins("HPC:DP2_M2C_P")),
|
Subsignal("rxp", Pins("HPC:DP2_M2C_P")),
|
||||||
Subsignal("rxn", Pins("HPC:DP2_M2C_n")),
|
Subsignal("rxn", Pins("HPC:DP2_M2C_N")),
|
||||||
),
|
),
|
||||||
("CXP_HS", 3,
|
("CXP_HS", 3,
|
||||||
Subsignal("txp", Pins("HPC:DP3_C2M_P")),
|
Subsignal("txp", Pins("HPC:DP3_C2M_P")),
|
||||||
|
@ -29,51 +29,51 @@ fmc_adapter_io = [
|
||||||
),
|
),
|
||||||
|
|
||||||
# CoaXPress low speed link
|
# CoaXPress low speed link
|
||||||
("CXP_LS", 0, Pins("HPC:LA00_CC_P"), IOStandard("LVCMOS25")),
|
("CXP_LS", 0, Pins("HPC:LA00_CC_P"), IOStandard("LVCMOS33")),
|
||||||
("CXP_LS", 1, Pins("HPC:LA01_CC_N"), IOStandard("LVCMOS25")),
|
("CXP_LS", 1, Pins("HPC:LA01_CC_N"), IOStandard("LVCMOS33")),
|
||||||
("CXP_LS", 2, Pins("HPC:LA01_CC_P"), IOStandard("LVCMOS25")),
|
("CXP_LS", 2, Pins("HPC:LA01_CC_P"), IOStandard("LVCMOS33")),
|
||||||
("CXP_LS", 3, Pins("HPC:LA02_N"), IOStandard("LVCMOS25")),
|
("CXP_LS", 3, Pins("HPC:LA02_N"), IOStandard("LVCMOS33")),
|
||||||
|
|
||||||
# CoaXPress green and red LED
|
# CoaXPress green and red LED
|
||||||
("CXP_LED", 0,
|
("CXP_LED", 0,
|
||||||
Subsignal("green", Pins("HPC:LA11_P"), IOStandard("LVCMOS25")),
|
Subsignal("green", Pins("HPC:LA11_P"), IOStandard("LVCMOS33")),
|
||||||
Subsignal("red", Pins("HPC:LA11_N"), IOStandard("LVCMOS25")),
|
Subsignal("red", Pins("HPC:LA11_N"), IOStandard("LVCMOS33")),
|
||||||
),
|
),
|
||||||
("CXP_LED", 1,
|
("CXP_LED", 1,
|
||||||
Subsignal("green", Pins("HPC:LA12_P"), IOStandard("LVCMOS25")),
|
Subsignal("green", Pins("HPC:LA12_P"), IOStandard("LVCMOS33")),
|
||||||
Subsignal("red", Pins("HPC:LA12_N"), IOStandard("LVCMOS25")),
|
Subsignal("red", Pins("HPC:LA12_N"), IOStandard("LVCMOS33")),
|
||||||
),
|
),
|
||||||
("CXP_LED", 2,
|
("CXP_LED", 2,
|
||||||
Subsignal("green", Pins("HPC:LA13_P"), IOStandard("LVCMOS25")),
|
Subsignal("green", Pins("HPC:LA13_P"), IOStandard("LVCMOS33")),
|
||||||
Subsignal("red", Pins("HPC:LA13_N"), IOStandard("LVCMOS25")),
|
Subsignal("red", Pins("HPC:LA13_N"), IOStandard("LVCMOS33")),
|
||||||
),
|
),
|
||||||
("CXP_LED", 3,
|
("CXP_LED", 3,
|
||||||
Subsignal("green", Pins("HPC:LA14_P"), IOStandard("LVCMOS25")),
|
Subsignal("green", Pins("HPC:LA14_P"), IOStandard("LVCMOS33")),
|
||||||
Subsignal("red", Pins("HPC:LA14_N"), IOStandard("LVCMOS25")),
|
Subsignal("red", Pins("HPC:LA14_N"), IOStandard("LVCMOS33")),
|
||||||
),
|
),
|
||||||
|
|
||||||
# Power over CoaXPress
|
# Power over CoaXPress
|
||||||
("PoCXP", 0,
|
("PoCXP", 0,
|
||||||
Subsignal("enable", Pins("HPC:LA21_N"), IOStandard("LVCMOS25")),
|
Subsignal("enable", Pins("HPC:LA21_N"), IOStandard("LVCMOS33")),
|
||||||
Subsignal("alert", Pins("HPC:LA18_CC_P"), IOStandard("LVCMOS25")),
|
Subsignal("alert", Pins("HPC:LA18_CC_P"), IOStandard("LVCMOS33")),
|
||||||
),
|
),
|
||||||
("PoCXP", 1,
|
("PoCXP", 1,
|
||||||
Subsignal("enable", Pins("HPC:LA21_P"), IOStandard("LVCMOS25")),
|
Subsignal("enable", Pins("HPC:LA21_P"), IOStandard("LVCMOS33")),
|
||||||
Subsignal("alert", Pins("HPC:LA19_N"), IOStandard("LVCMOS25")),
|
Subsignal("alert", Pins("HPC:LA19_N"), IOStandard("LVCMOS33")),
|
||||||
),
|
),
|
||||||
("PoCXP", 2,
|
("PoCXP", 2,
|
||||||
Subsignal("enable", Pins("HPC:LA22_N"), IOStandard("LVCMOS25")),
|
Subsignal("enable", Pins("HPC:LA22_N"), IOStandard("LVCMOS33")),
|
||||||
Subsignal("alert", Pins("HPC:LA19_P"), IOStandard("LVCMOS25")),
|
Subsignal("alert", Pins("HPC:LA19_P"), IOStandard("LVCMOS33")),
|
||||||
),
|
),
|
||||||
("PoCXP", 3,
|
("PoCXP", 3,
|
||||||
Subsignal("enable", Pins("HPC:LA22_P"), IOStandard("LVCMOS25")),
|
Subsignal("enable", Pins("HPC:LA22_P"), IOStandard("LVCMOS33")),
|
||||||
Subsignal("alert", Pins("HPC:LA20_N"), IOStandard("LVCMOS25")),
|
Subsignal("alert", Pins("HPC:LA20_N"), IOStandard("LVCMOS33")),
|
||||||
),
|
),
|
||||||
|
|
||||||
("i2c_fmc", 0,
|
("i2c_fmc", 0,
|
||||||
Subsignal("scl", Pins("HPC:IIC_SCL")),
|
Subsignal("scl", Pins("HPC:IIC_SCL")),
|
||||||
Subsignal("sda", Pins("HPC:IIC_SDA")),
|
Subsignal("sda", Pins("HPC:IIC_SDA")),
|
||||||
IOStandard("LVCMOS25")
|
IOStandard("LVCMOS33")
|
||||||
),
|
),
|
||||||
|
|
||||||
("3V3", 0, Pins("HPC:PG_M2C")),
|
("3V3", 0, Pins("HPC:PG_M2C")),
|
||||||
|
|
|
@ -12,6 +12,7 @@ from operator import add
|
||||||
|
|
||||||
class CXP_DownConn(Module, AutoCSR):
|
class CXP_DownConn(Module, AutoCSR):
|
||||||
def __init__(self, refclk, pads, sys_clk_freq, debug_sma, pmod_pads):
|
def __init__(self, refclk, pads, sys_clk_freq, debug_sma, pmod_pads):
|
||||||
|
nconn = len(pads)
|
||||||
self.rx_start_init = CSRStorage()
|
self.rx_start_init = CSRStorage()
|
||||||
self.rx_restart = CSR()
|
self.rx_restart = CSR()
|
||||||
|
|
||||||
|
@ -19,45 +20,38 @@ class CXP_DownConn(Module, AutoCSR):
|
||||||
self.tx_restart = CSR()
|
self.tx_restart = CSR()
|
||||||
self.txenable = CSRStorage()
|
self.txenable = CSRStorage()
|
||||||
|
|
||||||
self.txinit_phaligndone = CSRStatus()
|
self.rx_ready = CSRStatus(nconn)
|
||||||
self.rxinit_phaligndone = CSRStatus()
|
|
||||||
self.rx_ready = CSRStatus()
|
|
||||||
|
|
||||||
self.qpll_reset = CSR()
|
self.qpll_reset = CSR()
|
||||||
self.qpll_locked = CSRStatus()
|
self.qpll_locked = CSRStatus()
|
||||||
|
|
||||||
|
self.gtxs = []
|
||||||
# # #
|
# # #
|
||||||
|
|
||||||
self.submodules.qpll = qpll = QPLL(refclk, sys_clk_freq)
|
self.submodules.qpll = qpll = QPLL(refclk, sys_clk_freq)
|
||||||
|
self.sync += [
|
||||||
|
qpll.reset.eq(self.qpll_reset.re),
|
||||||
|
self.qpll_locked.status.eq(qpll.lock),
|
||||||
|
]
|
||||||
|
|
||||||
# single & master tx_mode can lock with rx in loopback
|
|
||||||
self.submodules.gtx = gtx = GTX(self.qpll, pads, sys_clk_freq, tx_mode="single", rx_mode="single")
|
|
||||||
|
|
||||||
# NOTE: No need to connect cxp_gtx_tx, we don't use tx anyway (just for loopback)
|
|
||||||
|
for i in range(nconn):
|
||||||
|
if i != 0:
|
||||||
|
break
|
||||||
|
gtx = GTX(self.qpll, pads[i], sys_clk_freq, tx_mode="single", rx_mode="single")
|
||||||
|
self.gtxs.append(gtx)
|
||||||
|
setattr(self.submodules, "gtx"+str(i), gtx)
|
||||||
|
|
||||||
|
# TODO: add extension gtx connections
|
||||||
|
# TODO: add connection interface
|
||||||
|
|
||||||
|
|
||||||
# TODO: Connect slave cxp_gtx_rx clock tgt
|
# TODO: Connect slave cxp_gtx_rx clock tgt
|
||||||
# checkout channel interfaces & drtio_gtx
|
# checkout channel interfaces & drtio_gtx
|
||||||
# checkout GTPTXPhaseAlignement for inspiration
|
# GTPTXPhaseAlignement for inspiration
|
||||||
|
|
||||||
self.sync += [
|
# Connect all GTX connections' DRP
|
||||||
# PLL
|
|
||||||
qpll.reset.eq(self.qpll_reset.re),
|
|
||||||
self.qpll_locked.status.eq(qpll.lock),
|
|
||||||
# GTX
|
|
||||||
self.txinit_phaligndone.status.eq(gtx.tx_init.Xxphaligndone),
|
|
||||||
self.rxinit_phaligndone.status.eq(gtx.rx_init.Xxphaligndone),
|
|
||||||
self.rx_ready.status.eq(gtx.rx_ready),
|
|
||||||
|
|
||||||
gtx.txenable.eq(self.txenable.storage[0]),
|
|
||||||
gtx.tx_restart.eq(self.tx_restart.re),
|
|
||||||
gtx.rx_restart.eq(self.rx_restart.re),
|
|
||||||
gtx.tx_init.clk_path_ready.eq(self.tx_start_init.storage),
|
|
||||||
gtx.rx_init.clk_path_ready.eq(self.rx_start_init.storage),
|
|
||||||
]
|
|
||||||
|
|
||||||
# GTX Channels DRP
|
|
||||||
self.tx_div = CSRStorage(3)
|
|
||||||
self.rx_div = CSRStorage(3)
|
|
||||||
|
|
||||||
self.gtx_daddr = CSRStorage(9)
|
self.gtx_daddr = CSRStorage(9)
|
||||||
self.gtx_dread = CSR()
|
self.gtx_dread = CSR()
|
||||||
|
@ -67,63 +61,46 @@ class CXP_DownConn(Module, AutoCSR):
|
||||||
self.gtx_dout = CSRStatus(16)
|
self.gtx_dout = CSRStatus(16)
|
||||||
self.gtx_dready = CSR()
|
self.gtx_dready = CSR()
|
||||||
|
|
||||||
self.comb += gtx.dclk.eq(ClockSignal("sys"))
|
for gtx in self.gtxs:
|
||||||
self.sync += [
|
self.sync += [
|
||||||
gtx.tx_rate.eq(self.tx_div.storage),
|
|
||||||
gtx.rx_rate.eq(self.rx_div.storage),
|
|
||||||
|
|
||||||
gtx.den.eq(0),
|
gtx.txenable.eq(self.txenable.storage[0]),
|
||||||
gtx.dwen.eq(0),
|
gtx.tx_restart.eq(self.tx_restart.re),
|
||||||
If(self.gtx_dread.re,
|
gtx.rx_restart.eq(self.rx_restart.re),
|
||||||
gtx.den.eq(1),
|
gtx.tx_init.clk_path_ready.eq(self.tx_start_init.storage),
|
||||||
gtx.daddr.eq(self.gtx_daddr.storage),
|
gtx.rx_init.clk_path_ready.eq(self.rx_start_init.storage),
|
||||||
).Elif(self.gtx_din_stb.re,
|
]
|
||||||
gtx.den.eq(1),
|
|
||||||
gtx.dwen.eq(1),
|
|
||||||
gtx.daddr.eq(self.gtx_daddr.storage),
|
|
||||||
gtx.din.eq(self.gtx_din.storage),
|
|
||||||
),
|
|
||||||
If(gtx.dready,
|
|
||||||
self.gtx_dready.w.eq(1),
|
|
||||||
self.gtx_dout.status.eq(gtx.dout),
|
|
||||||
),
|
|
||||||
If(self.gtx_dready.re,
|
|
||||||
self.gtx_dready.w.eq(0),
|
|
||||||
),
|
|
||||||
]
|
|
||||||
|
|
||||||
# QPLL DRP
|
self.comb += gtx.dclk.eq(ClockSignal("sys"))
|
||||||
|
self.sync += [
|
||||||
|
gtx.den.eq(0),
|
||||||
|
gtx.dwen.eq(0),
|
||||||
|
If(self.gtx_dread.re,
|
||||||
|
gtx.den.eq(1),
|
||||||
|
gtx.daddr.eq(self.gtx_daddr.storage),
|
||||||
|
).Elif(self.gtx_din_stb.re,
|
||||||
|
gtx.den.eq(1),
|
||||||
|
gtx.dwen.eq(1),
|
||||||
|
gtx.daddr.eq(self.gtx_daddr.storage),
|
||||||
|
gtx.din.eq(self.gtx_din.storage),
|
||||||
|
),
|
||||||
|
]
|
||||||
|
|
||||||
self.qpll_daddr = CSRStorage(8)
|
# TODO: deal with 4 GTX instance of outpus
|
||||||
self.qpll_dread = CSR()
|
for n, gtx in enumerate(self.gtxs):
|
||||||
self.qpll_din_stb = CSR()
|
self.sync += [
|
||||||
self.qpll_din = CSRStorage(16)
|
self.rx_ready.status[n].eq(gtx.rx_ready),
|
||||||
|
If(gtx.dready,
|
||||||
|
self.gtx_dready.w.eq(1),
|
||||||
|
self.gtx_dout.status.eq(gtx.dout),
|
||||||
|
),
|
||||||
|
If(self.gtx_dready.re,
|
||||||
|
self.gtx_dready.w.eq(0),
|
||||||
|
),
|
||||||
|
]
|
||||||
|
|
||||||
self.qpll_dout = CSRStatus(16)
|
|
||||||
self.qpll_dready = CSR()
|
|
||||||
|
|
||||||
self.comb += qpll.dclk.eq(ClockSignal("sys"))
|
|
||||||
self.sync += [
|
|
||||||
qpll.den.eq(0),
|
|
||||||
qpll.dwen.eq(0),
|
|
||||||
|
|
||||||
If(self.qpll_dread.re,
|
|
||||||
qpll.den.eq(1),
|
|
||||||
qpll.daddr.eq(self.qpll_daddr.storage),
|
|
||||||
).Elif(self.qpll_din_stb.re,
|
|
||||||
qpll.den.eq(1),
|
|
||||||
qpll.dwen.eq(1),
|
|
||||||
qpll.daddr.eq(self.qpll_daddr.storage),
|
|
||||||
qpll.din.eq(self.qpll_din.storage),
|
|
||||||
),
|
|
||||||
If(qpll.dready,
|
|
||||||
self.qpll_dready.w.eq(1),
|
|
||||||
self.qpll_dout.status.eq(qpll.dout),
|
|
||||||
),
|
|
||||||
If(self.qpll_dready.re,
|
|
||||||
self.qpll_dready.w.eq(0),
|
|
||||||
),
|
|
||||||
]
|
|
||||||
|
|
||||||
# DEBUG: txusrclk PLL DRG
|
# DEBUG: txusrclk PLL DRG
|
||||||
|
|
||||||
|
@ -138,106 +115,102 @@ class CXP_DownConn(Module, AutoCSR):
|
||||||
self.pll_dout = CSRStatus(16)
|
self.pll_dout = CSRStatus(16)
|
||||||
self.pll_dready = CSRStatus()
|
self.pll_dready = CSRStatus()
|
||||||
|
|
||||||
self.comb += [
|
self.txinit_phaligndone = CSRStatus()
|
||||||
gtx.txpll_reset.eq(self.txpll_reset.storage),
|
self.rxinit_phaligndone = CSRStatus()
|
||||||
gtx.pll_daddr.eq(self.pll_daddr.storage),
|
|
||||||
gtx.pll_dclk.eq(self.pll_dclk.storage),
|
|
||||||
gtx.pll_den.eq(self.pll_den.storage),
|
|
||||||
gtx.pll_din.eq(self.pll_din.storage),
|
|
||||||
gtx.pll_dwen.eq(self.pll_dwen.storage),
|
|
||||||
|
|
||||||
self.txpll_locked.status.eq(gtx.txpll_locked),
|
for n, gtx in enumerate(self.gtxs):
|
||||||
self.pll_dout.status.eq(gtx.pll_dout),
|
self.comb += [
|
||||||
self.pll_dready.status.eq(gtx.pll_dready),
|
gtx.txpll_reset.eq(self.txpll_reset.storage),
|
||||||
]
|
gtx.pll_daddr.eq(self.pll_daddr.storage),
|
||||||
|
gtx.pll_dclk.eq(self.pll_dclk.storage),
|
||||||
|
gtx.pll_den.eq(self.pll_den.storage),
|
||||||
|
gtx.pll_din.eq(self.pll_din.storage),
|
||||||
|
gtx.pll_dwen.eq(self.pll_dwen.storage),
|
||||||
|
|
||||||
# DEBUG:loopback
|
self.txinit_phaligndone.status.eq(gtx.tx_init.Xxphaligndone),
|
||||||
self.loopback_mode = CSRStorage(3)
|
self.rxinit_phaligndone.status.eq(gtx.rx_init.Xxphaligndone), self.txpll_locked.status.eq(gtx.txpll_locked),
|
||||||
self.comb += gtx.loopback_mode.eq(self.loopback_mode.storage)
|
self.pll_dout.status.eq(gtx.pll_dout),
|
||||||
|
self.pll_dready.status.eq(gtx.pll_dready),
|
||||||
|
]
|
||||||
|
|
||||||
# DEBUG: IO SMA & PMOD
|
# DEBUG:loopback
|
||||||
self.specials += [
|
self.loopback_mode = CSRStorage(3)
|
||||||
Instance("OBUF", i_I=gtx.cd_cxp_gtx_rx.clk, o_O=debug_sma.p_tx),
|
self.comb += gtx.loopback_mode.eq(self.loopback_mode.storage)
|
||||||
Instance("OBUF", i_I=gtx.cd_cxp_gtx_tx.clk, o_O=debug_sma.n_rx),
|
|
||||||
|
|
||||||
# pmod 0-7 pin
|
# DEBUG: IO SMA & PMOD
|
||||||
Instance("OBUF", i_I=gtx.comma_checker.comma_aligned, o_O=pmod_pads[0]),
|
if n == 0:
|
||||||
Instance("OBUF", i_I=gtx.comma_checker.comma_det, o_O=pmod_pads[1]),
|
self.specials += [
|
||||||
Instance("OBUF", i_I=gtx.comma_checker.restart_sys, o_O=pmod_pads[2]),
|
# Instance("OBUF", i_I=gtx.cd_cxp_gtx_rx.clk, o_O=debug_sma.p_tx),
|
||||||
Instance("OBUF", i_I=gtx.comma_checker.aligner_en, o_O=pmod_pads[3]),
|
# Instance("OBUF", i_I=gtx.cd_cxp_gtx_tx.clk, o_O=debug_sma.n_rx),
|
||||||
Instance("OBUF", i_I=gtx.comma_checker.check_reset, o_O=pmod_pads[4]),
|
|
||||||
Instance("OBUF", i_I=gtx.comma_checker.has_comma, o_O=pmod_pads[5]),
|
|
||||||
Instance("OBUF", i_I=gtx.comma_checker.has_error, o_O=pmod_pads[6]),
|
|
||||||
Instance("OBUF", i_I=gtx.comma_checker.ready_sys, o_O=pmod_pads[7]),
|
|
||||||
|
|
||||||
# Instance("OBUF", i_I=gtx.dclk, o_O=pmod_pads[0]),
|
# # pmod 0-7 pin
|
||||||
# Instance("OBUF", i_I=gtx.den, o_O=pmod_pads[1]),
|
# Instance("OBUF", i_I=gtx.comma_checker.comma_aligned, o_O=pmod_pads[0]),
|
||||||
# Instance("OBUF", i_I=gtx.dwen, o_O=pmod_pads[2]),
|
# Instance("OBUF", i_I=gtx.comma_checker.comma_det, o_O=pmod_pads[1]),
|
||||||
# Instance("OBUF", i_I=gtx.dready, o_O=pmod_pads[3]),
|
# Instance("OBUF", i_I=gtx.comma_checker.restart_sys, o_O=pmod_pads[2]),
|
||||||
]
|
# Instance("OBUF", i_I=gtx.comma_checker.aligner_en, o_O=pmod_pads[3]),
|
||||||
|
# Instance("OBUF", i_I=gtx.comma_checker.check_reset, o_O=pmod_pads[4]),
|
||||||
|
# Instance("OBUF", i_I=gtx.comma_checker.has_comma, o_O=pmod_pads[5]),
|
||||||
|
# Instance("OBUF", i_I=gtx.comma_checker.has_error, o_O=pmod_pads[6]),
|
||||||
|
# Instance("OBUF", i_I=gtx.comma_checker.ready_sys, o_O=pmod_pads[7]),
|
||||||
|
|
||||||
# DEBUG: datain
|
# Instance("OBUF", i_I=gtx.dclk, o_O=pmod_pads[0]),
|
||||||
|
# Instance("OBUF", i_I=gtx.den, o_O=pmod_pads[1]),
|
||||||
|
# Instance("OBUF", i_I=gtx.dwen, o_O=pmod_pads[2]),
|
||||||
|
# Instance("OBUF", i_I=gtx.dready, o_O=pmod_pads[3]),
|
||||||
|
]
|
||||||
|
|
||||||
|
# DEBUG: datain
|
||||||
|
|
||||||
|
|
||||||
self.sync.cxp_gtx_tx += [
|
self.sync.cxp_gtx_tx += [
|
||||||
self.gtx.encoder.d[0].eq(0xBC),
|
gtx.encoder.d[0].eq(0xBC),
|
||||||
self.gtx.encoder.k[0].eq(1),
|
gtx.encoder.k[0].eq(1),
|
||||||
self.gtx.encoder.d[1].eq(0x3C),
|
gtx.encoder.d[1].eq(0x3C),
|
||||||
self.gtx.encoder.k[1].eq(1),
|
gtx.encoder.k[1].eq(1),
|
||||||
self.gtx.encoder.d[2].eq(0x3C),
|
gtx.encoder.d[2].eq(0x3C),
|
||||||
self.gtx.encoder.k[2].eq(1),
|
gtx.encoder.k[2].eq(1),
|
||||||
self.gtx.encoder.d[3].eq(0xB5),
|
gtx.encoder.d[3].eq(0xB5),
|
||||||
self.gtx.encoder.k[3].eq(0),
|
gtx.encoder.k[3].eq(0),
|
||||||
]
|
]
|
||||||
|
|
||||||
self.rxdata_0 = CSRStatus(10)
|
for i in range(4):
|
||||||
self.rxdata_1 = CSRStatus(10)
|
gtx.decoders[i].input.attr.add("no_retiming")
|
||||||
self.rxdata_2 = CSRStatus(10)
|
gtx.decoders[i].d.attr.add("no_retiming")
|
||||||
self.rxdata_3 = CSRStatus(10)
|
gtx.decoders[i].k.attr.add("no_retiming")
|
||||||
self.decoded_data_0 = CSRStatus(8)
|
|
||||||
self.decoded_data_1 = CSRStatus(8)
|
|
||||||
self.decoded_data_2 = CSRStatus(8)
|
|
||||||
self.decoded_data_3 = CSRStatus(8)
|
|
||||||
self.decoded_k_0 = CSRStatus()
|
|
||||||
self.decoded_k_1 = CSRStatus()
|
|
||||||
self.decoded_k_2 = CSRStatus()
|
|
||||||
self.decoded_k_3 = CSRStatus()
|
|
||||||
|
|
||||||
self.sync.cxp_gtx_rx += [
|
rxdata_name = "rxdata_" + str(i)
|
||||||
self.rxdata_0.status.eq(self.gtx.decoders[0].input),
|
rxdata_csr = CSRStatus(10, name=rxdata_name)
|
||||||
self.decoded_data_0.status.eq(self.gtx.decoders[0].d),
|
setattr(self, rxdata_name, rxdata_csr)
|
||||||
self.decoded_k_0.status.eq(self.gtx.decoders[0].k),
|
|
||||||
|
|
||||||
self.rxdata_1.status.eq(self.gtx.decoders[1].input),
|
decoded_name = "decoded_data_" + str(i)
|
||||||
self.decoded_data_1.status.eq(self.gtx.decoders[1].d),
|
decoded_csr = CSRStatus(8, name=decoded_name)
|
||||||
self.decoded_k_1.status.eq(self.gtx.decoders[1].k),
|
setattr(self, decoded_name, decoded_csr)
|
||||||
|
|
||||||
self.rxdata_2.status.eq(self.gtx.decoders[2].input),
|
k_name = "rxdata_" + str(i)
|
||||||
self.decoded_data_2.status.eq(self.gtx.decoders[2].d),
|
k_csr = CSRStatus(1, name=k_name)
|
||||||
self.decoded_k_2.status.eq(self.gtx.decoders[2].k),
|
setattr(self, k_name, k_csr)
|
||||||
|
|
||||||
self.rxdata_3.status.eq(self.gtx.decoders[3].input),
|
self.sync.cxp_gtx_rx += [
|
||||||
self.decoded_data_3.status.eq(self.gtx.decoders[3].d),
|
rxdata_csr.status.eq(gtx.decoders[i].input),
|
||||||
self.decoded_k_3.status.eq(self.gtx.decoders[3].k),
|
decoded_csr.status.eq(gtx.decoders[i].d),
|
||||||
]
|
k_csr.status.eq(gtx.decoders[i].k),
|
||||||
|
]
|
||||||
|
|
||||||
|
class QPLL(Module, AutoCSR):
|
||||||
class QPLL(Module):
|
|
||||||
def __init__(self, refclk, sys_clk_freq):
|
def __init__(self, refclk, sys_clk_freq):
|
||||||
self.clk = Signal()
|
self.clk = Signal()
|
||||||
self.refclk = Signal()
|
self.refclk = Signal()
|
||||||
self.lock = Signal()
|
self.lock = Signal()
|
||||||
self.reset = Signal()
|
self.reset = Signal()
|
||||||
|
|
||||||
# Dynamic Reconfiguration Ports
|
self.daddr = CSRStorage(8)
|
||||||
self.daddr = Signal(8)
|
self.dread = CSR()
|
||||||
self.dclk = Signal()
|
self.din_stb = CSR()
|
||||||
self.den = Signal()
|
self.din = CSRStorage(16)
|
||||||
self.dwen = Signal()
|
|
||||||
self.din = Signal(16)
|
self.dout = CSRStatus(16)
|
||||||
|
self.dready = CSR()
|
||||||
|
|
||||||
self.dout = Signal(16)
|
|
||||||
self.dready = Signal()
|
|
||||||
# # #
|
# # #
|
||||||
|
|
||||||
# VCO @ 10GHz, linerate = 1.25Gbps
|
# VCO @ 10GHz, linerate = 1.25Gbps
|
||||||
|
@ -251,6 +224,7 @@ class QPLL(Module):
|
||||||
fbdiv_real = 80
|
fbdiv_real = 80
|
||||||
self.tx_usrclk_freq = (sys_clk_freq*fbdiv_real/self.Xxout_div)/40
|
self.tx_usrclk_freq = (sys_clk_freq*fbdiv_real/self.Xxout_div)/40
|
||||||
|
|
||||||
|
dready = Signal()
|
||||||
self.specials += [
|
self.specials += [
|
||||||
Instance("GTXE2_COMMON",
|
Instance("GTXE2_COMMON",
|
||||||
i_QPLLREFCLKSEL=0b001,
|
i_QPLLREFCLKSEL=0b001,
|
||||||
|
@ -293,16 +267,25 @@ class QPLL(Module):
|
||||||
i_QPLLRSVD2=0b11111,
|
i_QPLLRSVD2=0b11111,
|
||||||
|
|
||||||
# Dynamic Reconfiguration Ports
|
# Dynamic Reconfiguration Ports
|
||||||
i_DRPADDR=self.daddr,
|
i_DRPADDR=self.daddr.storage,
|
||||||
i_DRPCLK=self.dclk,
|
i_DRPCLK=ClockSignal("sys"),
|
||||||
i_DRPEN=self.den,
|
i_DRPEN=(self.dread.re | self.din_stb.re),
|
||||||
i_DRPWE=self.dwen,
|
i_DRPWE=self.din_stb.re,
|
||||||
i_DRPDI=self.din,
|
i_DRPDI=self.din.storage,
|
||||||
o_DRPDO=self.dout,
|
o_DRPDO=self.dout.status,
|
||||||
o_DRPRDY=self.dready,
|
o_DRPRDY=dready,
|
||||||
)
|
)
|
||||||
]
|
]
|
||||||
|
|
||||||
|
self.sync += [
|
||||||
|
If(dready,
|
||||||
|
self.dready.w.eq(1),
|
||||||
|
),
|
||||||
|
If(self.dready.re,
|
||||||
|
self.dready.w.eq(0),
|
||||||
|
),
|
||||||
|
]
|
||||||
|
|
||||||
# Warning: Xilinx transceivers are LSB first, and comma needs to be flipped
|
# Warning: Xilinx transceivers are LSB first, and comma needs to be flipped
|
||||||
# compared to the usual 8b10b binary representation.
|
# compared to the usual 8b10b binary representation.
|
||||||
class Comma_Checker(Module):
|
class Comma_Checker(Module):
|
||||||
|
@ -404,8 +387,6 @@ class Comma_Checker(Module):
|
||||||
|
|
||||||
rxfsm.act("WAIT_COMMA",
|
rxfsm.act("WAIT_COMMA",
|
||||||
If(self.comma_det,
|
If(self.comma_det,
|
||||||
# # start aligner early, so word aligned will fall
|
|
||||||
# self.aligner_en_rxclk.eq(1),
|
|
||||||
NextState("ALIGNING"),
|
NextState("ALIGNING"),
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
@ -476,9 +457,6 @@ class GTX(Module):
|
||||||
self.txenable = Signal()
|
self.txenable = Signal()
|
||||||
self.rx_ready = Signal()
|
self.rx_ready = Signal()
|
||||||
|
|
||||||
self.tx_rate = Signal(3)
|
|
||||||
self.rx_rate = Signal(3)
|
|
||||||
|
|
||||||
# Dynamic Reconfiguration Ports
|
# Dynamic Reconfiguration Ports
|
||||||
self.daddr = Signal(9)
|
self.daddr = Signal(9)
|
||||||
self.dclk = Signal()
|
self.dclk = Signal()
|
||||||
|
@ -539,10 +517,6 @@ class GTX(Module):
|
||||||
p_PD_TRANS_TIME_TO_P2=0x64,
|
p_PD_TRANS_TIME_TO_P2=0x64,
|
||||||
i_CPLLPD=1,
|
i_CPLLPD=1,
|
||||||
|
|
||||||
# Dynamic Tx/Rx divider
|
|
||||||
i_TXRATE=self.tx_rate,
|
|
||||||
i_RXRATE=self.rx_rate,
|
|
||||||
|
|
||||||
# QPLL
|
# QPLL
|
||||||
i_QPLLCLK=qpll.clk,
|
i_QPLLCLK=qpll.clk,
|
||||||
i_QPLLREFCLK=qpll.refclk,
|
i_QPLLREFCLK=qpll.refclk,
|
||||||
|
|
|
@ -1,279 +1,281 @@
|
||||||
|
from math import ceil
|
||||||
|
|
||||||
from migen import *
|
from migen import *
|
||||||
from migen.genlib.resetsync import AsyncResetSynchronizer
|
|
||||||
from migen.genlib.coding import PriorityEncoder
|
from migen.genlib.coding import PriorityEncoder
|
||||||
|
|
||||||
from misoc.cores.code_8b10b import SingleEncoder
|
from misoc.cores.code_8b10b import SingleEncoder
|
||||||
from misoc.interconnect import stream
|
from misoc.interconnect import stream
|
||||||
from misoc.interconnect.csr import *
|
from misoc.interconnect.csr import *
|
||||||
|
|
||||||
|
IDLE_CHARS = Array([
|
||||||
|
#[char, k]
|
||||||
|
[0xBC, 1], #K28.5
|
||||||
|
[0x3C, 1], #K28.1
|
||||||
|
[0x3C, 1], #K28.1
|
||||||
|
[0xB5, 0], #D21.5
|
||||||
|
])
|
||||||
|
|
||||||
class CXP_UpConn(Module, AutoCSR):
|
class UpConn_ClockGen(Module):
|
||||||
nfifos = 3
|
def __init__(self, sys_clk_freq):
|
||||||
def __init__(self, pads, sys_clk_freq, pmod, fifo_depth=32):
|
self.clk = Signal()
|
||||||
self.clock_domains.cd_cxp_upconn = ClockDomain()
|
self.clk_10x = Signal() # 20.83MHz 48ns or 41.66MHz 24ns
|
||||||
self.clk_reset = CSRStorage(reset=1)
|
|
||||||
self.bitrate2x_enable = CSRStorage()
|
self.freq2x_enable = Signal()
|
||||||
self.tx_enable = CSRStorage()
|
# # #
|
||||||
|
|
||||||
|
period = 1e9/sys_clk_freq
|
||||||
|
max_count = ceil(48/period)
|
||||||
|
counter = Signal(max=max_count, reset=max_count-1)
|
||||||
|
|
||||||
|
clk_div = Signal(max=10, reset=9)
|
||||||
|
|
||||||
|
self.sync += [
|
||||||
|
self.clk.eq(0),
|
||||||
|
self.clk_10x.eq(0),
|
||||||
|
|
||||||
|
If(counter == 0,
|
||||||
|
self.clk_10x.eq(1),
|
||||||
|
If(self.freq2x_enable,
|
||||||
|
counter.eq(int(max_count/2)-1),
|
||||||
|
).Else(
|
||||||
|
counter.eq(counter.reset),
|
||||||
|
),
|
||||||
|
).Else(
|
||||||
|
counter.eq(counter-1),
|
||||||
|
),
|
||||||
|
|
||||||
|
If(counter == 0,
|
||||||
|
If(clk_div == 0,
|
||||||
|
self.clk.eq(1),
|
||||||
|
clk_div.eq(clk_div.reset),
|
||||||
|
).Else(
|
||||||
|
clk_div.eq(clk_div-1),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
]
|
||||||
|
|
||||||
|
class SERDES_10bits(Module):
|
||||||
|
def __init__(self, pad):
|
||||||
|
self.oe = Signal()
|
||||||
|
self.d = Signal(10)
|
||||||
|
|
||||||
# # #
|
# # #
|
||||||
|
|
||||||
pll_locked = Signal()
|
|
||||||
pll_fb_clk = Signal()
|
|
||||||
pll_cxpclk = Signal()
|
|
||||||
pll_cxpclk2x = Signal()
|
|
||||||
|
|
||||||
self.specials += [
|
|
||||||
Instance("PLLE2_ADV",
|
|
||||||
p_BANDWIDTH="HIGH",
|
|
||||||
o_LOCKED=pll_locked,
|
|
||||||
i_RST=ResetSignal("sys"),
|
|
||||||
|
|
||||||
p_CLKIN1_PERIOD=1e9/sys_clk_freq, # ns
|
|
||||||
i_CLKIN1=ClockSignal("sys"),
|
|
||||||
|
|
||||||
# VCO @ 1.25GHz
|
|
||||||
p_CLKFBOUT_MULT=1.25e9/sys_clk_freq, p_DIVCLK_DIVIDE=1,
|
|
||||||
i_CLKFBIN=pll_fb_clk, o_CLKFBOUT=pll_fb_clk,
|
|
||||||
|
|
||||||
# 20.83MHz (48ns)
|
|
||||||
p_CLKOUT0_DIVIDE=60, p_CLKOUT0_PHASE=0.0, o_CLKOUT0=pll_cxpclk,
|
|
||||||
|
|
||||||
# 41.66MHz (24ns) for downconnection over 6.25Gpbs
|
|
||||||
p_CLKOUT1_DIVIDE=30, p_CLKOUT1_PHASE=0.0, o_CLKOUT1=pll_cxpclk2x,
|
|
||||||
),
|
|
||||||
Instance("BUFGMUX",
|
|
||||||
i_I0=pll_cxpclk,
|
|
||||||
i_I1=pll_cxpclk2x,
|
|
||||||
i_S=self.bitrate2x_enable.storage,
|
|
||||||
o_O=self.cd_cxp_upconn.clk
|
|
||||||
),
|
|
||||||
AsyncResetSynchronizer(self.cd_cxp_upconn, ~pll_locked | self.clk_reset.storage)
|
|
||||||
]
|
|
||||||
|
|
||||||
self.submodules.fsm = ClockDomainsRenamer("cxp_upconn")(FSM(reset_state="WAIT_TX_ENABLE"))
|
|
||||||
self.submodules.tx_fifos = TxFIFOs(self.nfifos, fifo_depth)
|
|
||||||
self.submodules.tx_idle = TxIdle()
|
|
||||||
|
|
||||||
o = Signal()
|
o = Signal()
|
||||||
tx_en = Signal()
|
|
||||||
tx_bitcount = Signal(max=10)
|
tx_bitcount = Signal(max=10)
|
||||||
tx_wordcount = Signal(max=4)
|
|
||||||
tx_reg = Signal(10)
|
tx_reg = Signal(10)
|
||||||
|
|
||||||
disp = Signal()
|
# DEBUG:
|
||||||
priorities = Signal(max=self.nfifos)
|
self.o = Signal()
|
||||||
idling = Signal()
|
self.comb += self.o.eq(o)
|
||||||
|
|
||||||
# startup sequence
|
self.specials += Instance("OBUF", i_I=o, o_O=pad),
|
||||||
self.fsm.act("WAIT_TX_ENABLE",
|
|
||||||
If(self.tx_enable.storage,
|
|
||||||
NextValue(self.tx_idle.word_idx, 0),
|
|
||||||
NextValue(tx_wordcount, 0),
|
|
||||||
NextValue(tx_bitcount, 0),
|
|
||||||
NextState("LOAD_CHAR")
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
self.fsm.act("LOAD_CHAR",
|
self.sync += [
|
||||||
NextValue(idling, 1),
|
If(self.oe,
|
||||||
NextValue(self.tx_idle.source_ack, 1),
|
# send LSB first
|
||||||
NextValue(tx_reg, self.tx_idle.source_data),
|
|
||||||
NextValue(disp, self.tx_idle.disp_out),
|
|
||||||
NextState("START_TX")
|
|
||||||
)
|
|
||||||
|
|
||||||
self.fsm.act("START_TX",
|
|
||||||
tx_en.eq(1),
|
|
||||||
If((~self.tx_enable.storage) & (tx_wordcount == 3),
|
|
||||||
NextState("WAIT_TX_ENABLE")
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
self.sync.cxp_upconn += [
|
|
||||||
self.tx_fifos.disp_in.eq(disp),
|
|
||||||
self.tx_idle.disp_in.eq(disp),
|
|
||||||
|
|
||||||
If(tx_en,
|
|
||||||
o.eq(tx_reg[0]),
|
o.eq(tx_reg[0]),
|
||||||
tx_reg.eq(Cat(tx_reg[1:], 0)),
|
tx_reg.eq(Cat(tx_reg[1:], 0)),
|
||||||
tx_bitcount.eq(tx_bitcount + 1),
|
tx_bitcount.eq(tx_bitcount + 1),
|
||||||
|
|
||||||
|
|
||||||
# char boundary
|
|
||||||
If(tx_bitcount == 9,
|
If(tx_bitcount == 9,
|
||||||
tx_bitcount.eq(0),
|
tx_bitcount.eq(0),
|
||||||
If((~self.tx_fifos.pe.n) & (self.tx_fifos.pe.o == 0),
|
tx_reg.eq(self.d),
|
||||||
# trigger packets are inserted at char boundary and don't contribute to word count
|
),
|
||||||
tx_reg.eq(self.tx_fifos.source_data[0]),
|
|
||||||
self.tx_fifos.source_ack[0].eq(1),
|
|
||||||
disp.eq(self.tx_fifos.disp_out[0]),
|
|
||||||
).Else(
|
|
||||||
# word boundary
|
|
||||||
If(tx_wordcount == 3,
|
|
||||||
tx_wordcount.eq(0),
|
|
||||||
If(~self.tx_fifos.pe.n,
|
|
||||||
# priority lv 1 & 2 packets are inserted at word boundary
|
|
||||||
idling.eq(0),
|
|
||||||
priorities.eq(self.tx_fifos.pe.o),
|
|
||||||
self.tx_fifos.source_ack[self.tx_fifos.pe.o].eq(1),
|
|
||||||
tx_reg.eq(self.tx_fifos.source_data[self.tx_fifos.pe.o]),
|
|
||||||
disp.eq(self.tx_fifos.disp_out[self.tx_fifos.pe.o]),
|
|
||||||
).Else(
|
|
||||||
idling.eq(1),
|
|
||||||
self.tx_idle.source_ack.eq(1),
|
|
||||||
tx_reg.eq(self.tx_idle.source_data),
|
|
||||||
disp.eq(self.tx_idle.disp_out),
|
|
||||||
)
|
|
||||||
).Else(
|
|
||||||
tx_wordcount.eq(tx_wordcount + 1),
|
|
||||||
If(~idling,
|
|
||||||
self.tx_fifos.source_ack[priorities].eq(1),
|
|
||||||
tx_reg.eq(self.tx_fifos.source_data[priorities]),
|
|
||||||
disp.eq(self.tx_fifos.disp_out[priorities]),
|
|
||||||
).Else(
|
|
||||||
self.tx_idle.source_ack.eq(1),
|
|
||||||
tx_reg.eq(self.tx_idle.source_data),
|
|
||||||
disp.eq(self.tx_idle.disp_out),
|
|
||||||
)
|
|
||||||
),
|
|
||||||
)
|
|
||||||
)
|
|
||||||
).Else(
|
).Else(
|
||||||
o.eq(0)
|
o.eq(0),
|
||||||
)
|
tx_bitcount.eq(0),
|
||||||
]
|
|
||||||
# DEBUG: remove pads
|
|
||||||
self.encoded_data = CSRStatus(10)
|
|
||||||
self.sync.cxp_upconn +=[
|
|
||||||
If(tx_bitcount == 0,
|
|
||||||
self.encoded_data.status.eq(tx_reg),
|
|
||||||
)
|
)
|
||||||
]
|
]
|
||||||
|
|
||||||
ninth_bit = Signal()
|
class Packets_Scheduler(Module):
|
||||||
|
def __init__(self, tx_fifos):
|
||||||
|
self.tx_enable = Signal()
|
||||||
|
|
||||||
|
self.oe = Signal()
|
||||||
|
|
||||||
|
# # #
|
||||||
|
|
||||||
|
self.submodules.startup_fsm = startup_fsm = FSM(reset_state="WAIT_TX_ENABLE")
|
||||||
|
self.submodules.encoder = encoder = SingleEncoder(True)
|
||||||
|
|
||||||
|
tx_charcount = Signal(max=4)
|
||||||
|
tx_wordcount = Signal(max=10000)
|
||||||
|
|
||||||
|
idling = Signal()
|
||||||
|
priorities = Signal.like(tx_fifos.pe.o)
|
||||||
|
|
||||||
|
# DEBUG:
|
||||||
|
self.idling = Signal()
|
||||||
|
self.tx_charcount = Signal(max=4)
|
||||||
|
self.comb += [
|
||||||
|
self.idling.eq(idling),
|
||||||
|
self.tx_charcount.eq(tx_charcount),
|
||||||
|
]
|
||||||
|
|
||||||
|
startup_fsm.act("WAIT_TX_ENABLE",
|
||||||
|
If(self.tx_enable,
|
||||||
|
NextValue(idling, 1),
|
||||||
|
NextValue(tx_charcount, 0),
|
||||||
|
NextValue(encoder.d, IDLE_CHARS[0][0]),
|
||||||
|
NextValue(encoder.k, IDLE_CHARS[0][1]),
|
||||||
|
NextState("START_TX"),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
startup_fsm.act("START_TX",
|
||||||
|
self.oe.eq(1),
|
||||||
|
If((~self.tx_enable) & (tx_charcount == 3),
|
||||||
|
NextState("WAIT_TX_ENABLE")
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
self.sync += [
|
||||||
|
If(self.oe,
|
||||||
|
encoder.disp_in.eq(encoder.disp_out),
|
||||||
|
If((~tx_fifos.pe.n) & (tx_fifos.pe.o == 0),
|
||||||
|
# trigger packets are inserted at char boundary and don't contribute to word count
|
||||||
|
tx_fifos.source_ack[0].eq(1),
|
||||||
|
encoder.d.eq(tx_fifos.source_data[0]),
|
||||||
|
encoder.k.eq(tx_fifos.source_k[0]),
|
||||||
|
).Else(
|
||||||
|
If(tx_charcount == 3,
|
||||||
|
tx_charcount.eq(0),
|
||||||
|
|
||||||
|
# Section 9.2.4 (CXP-001-2021)
|
||||||
|
# other priorities packets are inserted at word boundary
|
||||||
|
If((~tx_fifos.pe.n) & (tx_wordcount != 9999),
|
||||||
|
idling.eq(0),
|
||||||
|
priorities.eq(tx_fifos.pe.o),
|
||||||
|
tx_wordcount.eq(tx_wordcount + 1),
|
||||||
|
|
||||||
|
tx_fifos.source_ack[tx_fifos.pe.o].eq(1),
|
||||||
|
encoder.d.eq(tx_fifos.source_data[tx_fifos.pe.o]),
|
||||||
|
encoder.k.eq(tx_fifos.source_k[tx_fifos.pe.o]),
|
||||||
|
).Else(
|
||||||
|
# Section 9.2.5.1 (CXP-001-2021)
|
||||||
|
# IDLE word shall be transmitted at least once every 10,000 words, but should not be inserted into trigger packet
|
||||||
|
idling.eq(1),
|
||||||
|
tx_wordcount.eq(0),
|
||||||
|
|
||||||
|
encoder.d.eq(IDLE_CHARS[0][0]),
|
||||||
|
encoder.k.eq(IDLE_CHARS[0][1]),
|
||||||
|
)
|
||||||
|
).Else(
|
||||||
|
tx_charcount.eq(tx_charcount + 1),
|
||||||
|
If(~idling,
|
||||||
|
tx_fifos.source_ack[priorities].eq(1),
|
||||||
|
encoder.d.eq(tx_fifos.source_data[priorities]),
|
||||||
|
encoder.k.eq(tx_fifos.source_k[priorities]),
|
||||||
|
).Else(
|
||||||
|
encoder.d.eq(IDLE_CHARS[tx_charcount + 1][0]),
|
||||||
|
encoder.k.eq(IDLE_CHARS[tx_charcount + 1][1]),
|
||||||
|
)
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
]
|
||||||
|
|
||||||
|
class TxFIFOs(Module):
|
||||||
|
def __init__(self, nfifos, fifo_depth):
|
||||||
|
|
||||||
|
self.sink_full = Signal(nfifos)
|
||||||
|
self.sink_stb = Signal(nfifos)
|
||||||
|
self.sink_data = [Signal(8) for _ in range(nfifos)]
|
||||||
|
self.sink_k = [Signal() for _ in range(nfifos)]
|
||||||
|
|
||||||
|
self.source_ack = Array(Signal() for _ in range(nfifos))
|
||||||
|
self.source_data = Array(Signal(8) for _ in range(nfifos))
|
||||||
|
self.source_k = Array(Signal() for _ in range(nfifos))
|
||||||
|
|
||||||
|
# # #
|
||||||
|
|
||||||
|
not_empty_reg = Signal(nfifos)
|
||||||
|
|
||||||
|
for i in range(nfifos):
|
||||||
|
fifo = stream.SyncFIFO([("data", 8), ("k", 1)], fifo_depth)
|
||||||
|
|
||||||
|
setattr(self.submodules, "tx_fifo" + str(i), fifo)
|
||||||
|
|
||||||
|
self.sync += [
|
||||||
|
fifo.sink.stb.eq(self.sink_stb[i]),
|
||||||
|
self.sink_full[i].eq(fifo.sink.ack),
|
||||||
|
fifo.sink.data.eq(self.sink_data[i]),
|
||||||
|
fifo.sink.k.eq(self.sink_k[i]),
|
||||||
|
|
||||||
|
If(self.source_ack[i],
|
||||||
|
# reset ack after asserted
|
||||||
|
self.source_ack[i].eq(0),
|
||||||
|
fifo.source.ack.eq(1),
|
||||||
|
).Else(
|
||||||
|
fifo.source.ack.eq(0),
|
||||||
|
),
|
||||||
|
|
||||||
|
not_empty_reg[i].eq(fifo.source.stb),
|
||||||
|
self.source_data[i].eq(fifo.source.data),
|
||||||
|
self.source_k[i].eq(fifo.source.k),
|
||||||
|
]
|
||||||
|
|
||||||
|
# FIFOs transmission priority
|
||||||
|
self.submodules.pe = PriorityEncoder(nfifos)
|
||||||
|
self.comb += self.pe.i.eq(not_empty_reg)
|
||||||
|
|
||||||
|
class CXP_UpConn(Module):
|
||||||
|
def __init__(self, pad, sys_clk_freq, debug_sma, pmod_pads, fifo_depth, nfifos=3):
|
||||||
|
self.bitrate2x_enable = Signal()
|
||||||
|
self.tx_enable = Signal()
|
||||||
|
|
||||||
|
# # #
|
||||||
|
|
||||||
|
self.submodules.cg = cg = UpConn_ClockGen(sys_clk_freq)
|
||||||
|
self.submodules.tx_fifos = tx_fifos = TxFIFOs(nfifos, fifo_depth)
|
||||||
|
|
||||||
|
self.submodules.scheduler = scheduler = CEInserter()(Packets_Scheduler(tx_fifos))
|
||||||
|
self.submodules.serdes = serdes = CEInserter()(SERDES_10bits(pad))
|
||||||
|
|
||||||
|
self.comb += [
|
||||||
|
cg.freq2x_enable.eq(self.bitrate2x_enable),
|
||||||
|
|
||||||
|
scheduler.ce.eq(cg.clk),
|
||||||
|
scheduler.tx_enable.eq(self.tx_enable),
|
||||||
|
|
||||||
|
serdes.ce.eq(cg.clk_10x),
|
||||||
|
serdes.d.eq(scheduler.encoder.output),
|
||||||
|
serdes.oe.eq(scheduler.oe),
|
||||||
|
]
|
||||||
|
|
||||||
|
# DEBUG: remove pads
|
||||||
|
|
||||||
|
prioity_0 = Signal()
|
||||||
word_bound = Signal()
|
word_bound = Signal()
|
||||||
|
|
||||||
p0 = Signal()
|
p0 = Signal()
|
||||||
p3 = Signal()
|
p3 = Signal()
|
||||||
self.comb += [
|
self.comb += [
|
||||||
ninth_bit.eq(tx_bitcount == 9),
|
prioity_0.eq((~tx_fifos.pe.n) & (tx_fifos.pe.o == 0)),
|
||||||
word_bound.eq(tx_wordcount == 3),
|
word_bound.eq(scheduler.tx_charcount == 3),
|
||||||
p0.eq(self.tx_idle.word_idx == 0),
|
|
||||||
p3.eq(self.tx_idle.word_idx == 3),
|
# because of clk delay
|
||||||
|
p0.eq(scheduler.tx_charcount == 2),
|
||||||
|
p3.eq(scheduler.tx_charcount == 1),
|
||||||
]
|
]
|
||||||
self.specials += [
|
self.specials += [
|
||||||
# # debug sma
|
# # debug sma
|
||||||
# Instance("OBUF", i_I=o, o_O=pads.p_tx),
|
Instance("OBUF", i_I=cg.clk, o_O=debug_sma.p_tx),
|
||||||
# Instance("OBUF", i_I=self.cd_cxp_upconn.clk, o_O=pads.n_rx),
|
Instance("OBUF", i_I=cg.clk_10x, o_O=debug_sma.n_rx),
|
||||||
|
|
||||||
# # pmod 0-7 pin
|
# # pmod 0-7 pin
|
||||||
# Instance("OBUF", i_I=o, o_O=pmod[0]),
|
Instance("OBUF", i_I=serdes.o, o_O=pmod_pads[0]),
|
||||||
# Instance("OBUF", i_I=self.cd_cxp_upconn.clk, o_O=pmod[1]),
|
Instance("OBUF", i_I=cg.clk_10x, o_O=pmod_pads[1]),
|
||||||
# Instance("OBUF", i_I=~self.tx_fifos.pe.n, o_O=pmod[2]),
|
Instance("OBUF", i_I=~tx_fifos.pe.n, o_O=pmod_pads[2]),
|
||||||
# Instance("OBUF", i_I=ninth_bit, o_O=pmod[3]),
|
Instance("OBUF", i_I=prioity_0, o_O=pmod_pads[3]),
|
||||||
# Instance("OBUF", i_I=word_bound, o_O=pmod[4]),
|
Instance("OBUF", i_I=word_bound, o_O=pmod_pads[4]),
|
||||||
# Instance("OBUF", i_I=idling, o_O=pmod[5]),
|
Instance("OBUF", i_I=scheduler.idling, o_O=pmod_pads[5]),
|
||||||
# # Instance("OBUF", i_I=self.tx_fifos.source_ack[0], o_O=pmod[6]),
|
# Instance("OBUF", i_I=tx_fifos.source_ack[0], o_O=pmod[6]),
|
||||||
# # Instance("OBUF", i_I=self.tx_fifos.source_ack[2], o_O=pmod[6]),
|
# Instance("OBUF", i_I=tx_fifos.source_ack[2], o_O=pmod[6]),
|
||||||
# # Instance("OBUF", i_I=self.tx_fifos.source_ack[1], o_O=pmod[7]),
|
# Instance("OBUF", i_I=tx_fifos.source_ack[1], o_O=pmod[7]),
|
||||||
# Instance("OBUF", i_I=p0, o_O=pmod[6]),
|
Instance("OBUF", i_I=p0, o_O=pmod_pads[6]),
|
||||||
# Instance("OBUF", i_I=p3, o_O=pmod[7]),
|
Instance("OBUF", i_I=p3, o_O=pmod_pads[7]),
|
||||||
]
|
|
||||||
self.symbol0 = CSR(9)
|
|
||||||
self.symbol1 = CSR(9)
|
|
||||||
self.symbol2 = CSR(9)
|
|
||||||
|
|
||||||
self.sync += [
|
|
||||||
self.tx_fifos.sink_stb[0].eq(self.symbol0.re),
|
|
||||||
self.tx_fifos.sink_data[0].eq(self.symbol0.r),
|
|
||||||
self.tx_fifos.sink_stb[1].eq(self.symbol1.re),
|
|
||||||
self.tx_fifos.sink_data[1].eq(self.symbol1.r),
|
|
||||||
self.tx_fifos.sink_stb[2].eq(self.symbol2.re),
|
|
||||||
self.tx_fifos.sink_data[2].eq(self.symbol2.r),
|
|
||||||
]
|
]
|
||||||
|
|
||||||
class TxFIFOs(Module):
|
|
||||||
def __init__(self, nfifos, fifo_depth):
|
|
||||||
self.disp_in = Signal()
|
|
||||||
self.disp_out = Array(Signal() for _ in range(nfifos))
|
|
||||||
|
|
||||||
self.sink_stb = Signal(nfifos)
|
|
||||||
self.sink_ack = Signal(nfifos)
|
|
||||||
self.sink_data = [Signal(9) for _ in range(nfifos)]
|
|
||||||
|
|
||||||
self.source_ack = Array(Signal() for _ in range(nfifos))
|
|
||||||
self.source_data = Array(Signal(10) for _ in range(nfifos))
|
|
||||||
|
|
||||||
# # #
|
|
||||||
|
|
||||||
source_stb = Signal(nfifos)
|
|
||||||
|
|
||||||
for i in range(nfifos):
|
|
||||||
cdr = ClockDomainsRenamer({"write": "sys", "read": "cxp_upconn"})
|
|
||||||
fifo = cdr(stream.AsyncFIFO([("data", 9)], fifo_depth))
|
|
||||||
encoder = ClockDomainsRenamer("cxp_upconn")(SingleEncoder(True))
|
|
||||||
setattr(self.submodules, "tx_fifo" + str(i), fifo)
|
|
||||||
setattr(self.submodules, "tx_encoder" + str(i), encoder)
|
|
||||||
self.sync += [
|
|
||||||
fifo.sink.stb.eq(self.sink_stb[i]),
|
|
||||||
self.sink_ack[i].eq(fifo.sink.ack),
|
|
||||||
fifo.sink.data.eq(self.sink_data[i]),
|
|
||||||
]
|
|
||||||
self.sync.cxp_upconn += [
|
|
||||||
encoder.d.eq(fifo.source.data[:8]),
|
|
||||||
encoder.k.eq(fifo.source.data[8]),
|
|
||||||
encoder.disp_in.eq(self.disp_in),
|
|
||||||
self.disp_out[i].eq(encoder.disp_out),
|
|
||||||
|
|
||||||
source_stb[i].eq(fifo.source.stb),
|
|
||||||
fifo.source.ack.eq(self.source_ack[i]),
|
|
||||||
self.source_data[i].eq(encoder.output),
|
|
||||||
# reset ack after asserted
|
|
||||||
If(self.source_ack[i], self.source_ack[i].eq(0)),
|
|
||||||
]
|
|
||||||
|
|
||||||
# FIFOs transmission priority
|
|
||||||
self.submodules.pe = PriorityEncoder(nfifos)
|
|
||||||
self.comb += self.pe.i.eq(source_stb)
|
|
||||||
|
|
||||||
class TxIdle(Module):
|
|
||||||
def __init__(self):
|
|
||||||
self.disp_in = Signal()
|
|
||||||
self.disp_out = Signal()
|
|
||||||
|
|
||||||
self.word_idx = Signal(max=4)
|
|
||||||
self.source_ack = Signal()
|
|
||||||
self.source_data = Signal(10)
|
|
||||||
|
|
||||||
# # #
|
|
||||||
|
|
||||||
# CXP 2.1 section 9.2.5
|
|
||||||
IDLE_CHARS = Array([
|
|
||||||
#[char, k]
|
|
||||||
[0b10111100, 1], #K28.5
|
|
||||||
[0b00111100, 1], #K28.1
|
|
||||||
[0b00111100, 1], #K28.1
|
|
||||||
[0b10111100, 0], #D28.5
|
|
||||||
])
|
|
||||||
|
|
||||||
encoder = ClockDomainsRenamer("cxp_upconn")(SingleEncoder(True))
|
|
||||||
self.submodules += encoder
|
|
||||||
|
|
||||||
self.sync.cxp_upconn += [
|
|
||||||
encoder.d.eq(IDLE_CHARS[self.word_idx][0]),
|
|
||||||
encoder.k.eq(IDLE_CHARS[self.word_idx][1]),
|
|
||||||
encoder.disp_in.eq(self.disp_in),
|
|
||||||
self.disp_out.eq(encoder.disp_out),
|
|
||||||
self.source_data.eq(encoder.output),
|
|
||||||
|
|
||||||
If(self.source_ack,
|
|
||||||
# reset after asserted
|
|
||||||
self.source_ack.eq(0),
|
|
||||||
|
|
||||||
If(self.word_idx != 3,
|
|
||||||
self.word_idx.eq(self.word_idx + 1),
|
|
||||||
).Else(
|
|
||||||
self.word_idx.eq(0),
|
|
||||||
)
|
|
||||||
),
|
|
||||||
]
|
|
||||||
|
|
|
@ -686,9 +686,12 @@ class CXP_FMC():
|
||||||
|
|
||||||
clk_freq = 125e6
|
clk_freq = 125e6
|
||||||
|
|
||||||
|
gtx_pads = [platform.request("CXP_HS", i) for i in range(4)]
|
||||||
|
|
||||||
self.submodules.cxp = cxp.CXP(
|
self.submodules.cxp = cxp.CXP(
|
||||||
refclk=self.cdr_clk,
|
refclk=self.cdr_clk,
|
||||||
pads=platform.request("CXP_HS", 0),
|
downconn_pads=gtx_pads,
|
||||||
|
upconn_pads=platform.request("CXP_LS", 0),
|
||||||
sys_clk_freq=clk_freq,
|
sys_clk_freq=clk_freq,
|
||||||
debug_sma=platform.request("user_sma_clock_33"),
|
debug_sma=platform.request("user_sma_clock_33"),
|
||||||
pmod_pads = pmod_pads
|
pmod_pads = pmod_pads
|
||||||
|
@ -696,9 +699,13 @@ class CXP_FMC():
|
||||||
self.csr_devices.append("cxp")
|
self.csr_devices.append("cxp")
|
||||||
|
|
||||||
# max freq of cxp_gtx_rx = linerate/internal_datawidth = 12.5Gbps/40 = 312.5MHz
|
# max freq of cxp_gtx_rx = linerate/internal_datawidth = 12.5Gbps/40 = 312.5MHz
|
||||||
platform.add_period_constraint(self.cxp.downconn.gtx.cd_cxp_gtx_tx.clk, 3.2)
|
# zc706 use speed grade 2 which only support up to 10.3125Gbps (4ns)
|
||||||
platform.add_period_constraint(self.cxp.downconn.gtx.cd_cxp_gtx_rx.clk, 3.2)
|
# pushing to 12.5Gbps (3.2ns) will result in Pulse width violation but setup/hold times are met
|
||||||
platform.add_false_path_constraints(self.cxp.downconn.gtx.cd_cxp_gtx_tx.clk, self.cxp.downconn.gtx.cd_cxp_gtx_rx.clk)
|
for gtx in self.cxp.downconn.gtxs:
|
||||||
|
platform.add_period_constraint(gtx.cd_cxp_gtx_tx.clk, 3.2)
|
||||||
|
platform.add_period_constraint(gtx.cd_cxp_gtx_rx.clk, 3.2)
|
||||||
|
# constraint the CLK path
|
||||||
|
platform.add_false_path_constraints(self.sys_crg.cd_sys.clk, gtx.cd_cxp_gtx_tx.clk, gtx.cd_cxp_gtx_rx.clk)
|
||||||
|
|
||||||
rtio_channels = []
|
rtio_channels = []
|
||||||
# FIXME remove this placeholder RTIO channel
|
# FIXME remove this placeholder RTIO channel
|
||||||
|
|
|
@ -5,11 +5,6 @@ use log::info;
|
||||||
// use log::info;
|
// use log::info;
|
||||||
use crate::pl::csr;
|
use crate::pl::csr;
|
||||||
|
|
||||||
pub struct CXP_DownConn_Settings {
|
|
||||||
pub rxdiv: u8,
|
|
||||||
pub qpll_fbdiv: u8,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug)]
|
#[derive(Clone, Copy, Debug)]
|
||||||
#[allow(non_camel_case_types)]
|
#[allow(non_camel_case_types)]
|
||||||
pub enum CXP_SPEED {
|
pub enum CXP_SPEED {
|
||||||
|
@ -24,7 +19,7 @@ pub enum CXP_SPEED {
|
||||||
|
|
||||||
pub fn loopback_testing(timer: &mut GlobalTimer, speed: CXP_SPEED) {
|
pub fn loopback_testing(timer: &mut GlobalTimer, speed: CXP_SPEED) {
|
||||||
println!("==============================================================================");
|
println!("==============================================================================");
|
||||||
CXP_GTX::change_linerate(timer, speed);
|
cxp_gtx::change_linerate(timer, speed);
|
||||||
|
|
||||||
unsafe {
|
unsafe {
|
||||||
info!("waiting for tx&rx setup...");
|
info!("waiting for tx&rx setup...");
|
||||||
|
@ -42,8 +37,8 @@ pub fn loopback_testing(timer: &mut GlobalTimer, speed: CXP_SPEED) {
|
||||||
while csr::cxp::downconn_rx_ready_read() != 1 {}
|
while csr::cxp::downconn_rx_ready_read() != 1 {}
|
||||||
info!("rx ready!");
|
info!("rx ready!");
|
||||||
|
|
||||||
// loop {
|
loop {
|
||||||
for _ in 0..20 {
|
// for _ in 0..20 {
|
||||||
// NOTE: raw bits
|
// NOTE: raw bits
|
||||||
// let data0 = csr::cxp::downconn_rxdata_0_read();
|
// let data0 = csr::cxp::downconn_rxdata_0_read();
|
||||||
// let data1 = csr::cxp::downconn_rxdata_1_read();
|
// let data1 = csr::cxp::downconn_rxdata_1_read();
|
||||||
|
@ -75,7 +70,7 @@ pub fn loopback_testing(timer: &mut GlobalTimer, speed: CXP_SPEED) {
|
||||||
// timer.delay_us(1_000_000);
|
// timer.delay_us(1_000_000);
|
||||||
// }
|
// }
|
||||||
|
|
||||||
// timer.delay_us(1_000_000);
|
timer.delay_us(1_000_000);
|
||||||
// NOTE: raw bits
|
// NOTE: raw bits
|
||||||
// let data0 = csr::cxp::downconn_rxdata_0_read();
|
// let data0 = csr::cxp::downconn_rxdata_0_read();
|
||||||
// let data1 = csr::cxp::downconn_rxdata_1_read();
|
// let data1 = csr::cxp::downconn_rxdata_1_read();
|
||||||
|
@ -134,13 +129,13 @@ pub fn setup(timer: &mut GlobalTimer) {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
CXP_GTX::change_linerate(timer, CXP_SPEED::CXP_1);
|
cxp_gtx::change_linerate(timer, CXP_SPEED::CXP_1);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub mod CXP_GTX {
|
pub mod cxp_gtx {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
struct RX_CDR_CFG {
|
struct CdrConfig {
|
||||||
pub cfg_reg0: u16, //0x0A8
|
pub cfg_reg0: u16, //0x0A8
|
||||||
pub cfg_reg1: u16, //0x0A9
|
pub cfg_reg1: u16, //0x0A9
|
||||||
pub cfg_reg2: u16, //0x0AA
|
pub cfg_reg2: u16, //0x0AA
|
||||||
|
@ -177,28 +172,29 @@ pub mod CXP_GTX {
|
||||||
CXP_SPEED::CXP_3 | CXP_SPEED::CXP_6 | CXP_SPEED::CXP_12 => 0x0170, // FB_Divider = 100
|
CXP_SPEED::CXP_3 | CXP_SPEED::CXP_6 | CXP_SPEED::CXP_12 => 0x0170, // FB_Divider = 100
|
||||||
};
|
};
|
||||||
|
|
||||||
println!("0x36 = {:#018b}", qpll_read(0x36));
|
println!("0x36 = {:#06x}", qpll_read(0x36));
|
||||||
qpll_write(0x36, qpll_div_reg);
|
qpll_write(0x36, qpll_div_reg);
|
||||||
println!("0x36 = {:#018b}", qpll_read(0x36));
|
println!("0x36 = {:#06x}", qpll_read(0x36));
|
||||||
|
|
||||||
let rxout_div = match speed {
|
// DEBUG: remove txoutdiv
|
||||||
CXP_SPEED::CXP_1 => 0b100, // 8
|
let txrxout_div = match speed {
|
||||||
CXP_SPEED::CXP_2 | CXP_SPEED::CXP_3 => 0b011, // 4
|
CXP_SPEED::CXP_1 => 0x33, // 8
|
||||||
CXP_SPEED::CXP_5 | CXP_SPEED::CXP_6 => 0b010, // 2
|
CXP_SPEED::CXP_2 | CXP_SPEED::CXP_3 => 0x22, // 4
|
||||||
CXP_SPEED::CXP_10 | CXP_SPEED::CXP_12 => 0b001, // 1
|
CXP_SPEED::CXP_5 | CXP_SPEED::CXP_6 => 0x11, // 2
|
||||||
|
CXP_SPEED::CXP_10 | CXP_SPEED::CXP_12 => 0x00, // 1
|
||||||
};
|
};
|
||||||
|
|
||||||
unsafe {
|
// OUT_DIV
|
||||||
csr::cxp::downconn_rx_div_write(rxout_div);
|
println!("0x88 = {:#06x}", gtx_read(0x88));
|
||||||
csr::cxp::downconn_tx_div_write(rxout_div);
|
gtx_write(0x88, txrxout_div);
|
||||||
}
|
println!("0x88 = {:#06x}", gtx_read(0x88));
|
||||||
}
|
}
|
||||||
|
|
||||||
fn change_cdr_cfg(speed: CXP_SPEED) {
|
fn change_cdr_cfg(speed: CXP_SPEED) {
|
||||||
let cdr_cfg = match speed {
|
let cdr_cfg = match speed {
|
||||||
// rxout_div = 8
|
// rxout_div = 8
|
||||||
CXP_SPEED::CXP_1 => {
|
CXP_SPEED::CXP_1 => {
|
||||||
RX_CDR_CFG {
|
CdrConfig {
|
||||||
cfg_reg0: 0x0020, //0x0A8
|
cfg_reg0: 0x0020, //0x0A8
|
||||||
cfg_reg1: 0x1008, //0x0A9
|
cfg_reg1: 0x1008, //0x0A9
|
||||||
cfg_reg2: 0x23FF, //0x0AA
|
cfg_reg2: 0x23FF, //0x0AA
|
||||||
|
@ -208,7 +204,7 @@ pub mod CXP_GTX {
|
||||||
}
|
}
|
||||||
// rxout_div = 4
|
// rxout_div = 4
|
||||||
CXP_SPEED::CXP_2 | CXP_SPEED::CXP_5 => {
|
CXP_SPEED::CXP_2 | CXP_SPEED::CXP_5 => {
|
||||||
RX_CDR_CFG {
|
CdrConfig {
|
||||||
cfg_reg0: 0x0020, //0x0A8
|
cfg_reg0: 0x0020, //0x0A8
|
||||||
cfg_reg1: 0x1010, //0x0A9
|
cfg_reg1: 0x1010, //0x0A9
|
||||||
cfg_reg2: 0x23FF, //0x0AA
|
cfg_reg2: 0x23FF, //0x0AA
|
||||||
|
@ -218,7 +214,7 @@ pub mod CXP_GTX {
|
||||||
}
|
}
|
||||||
// rxout_div = 2
|
// rxout_div = 2
|
||||||
CXP_SPEED::CXP_3 | CXP_SPEED::CXP_6 => {
|
CXP_SPEED::CXP_3 | CXP_SPEED::CXP_6 => {
|
||||||
RX_CDR_CFG {
|
CdrConfig {
|
||||||
cfg_reg0: 0x0020, //0x0A8
|
cfg_reg0: 0x0020, //0x0A8
|
||||||
cfg_reg1: 0x1020, //0x0A9
|
cfg_reg1: 0x1020, //0x0A9
|
||||||
cfg_reg2: 0x23FF, //0x0AA
|
cfg_reg2: 0x23FF, //0x0AA
|
||||||
|
@ -226,19 +222,9 @@ pub mod CXP_GTX {
|
||||||
cfg_reg4: 0x0003, //0x0AC
|
cfg_reg4: 0x0003, //0x0AC
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// // Divided by 1
|
|
||||||
// CXP_SPEED::CXP_6 => {
|
|
||||||
// RX_CDR_CFG {
|
|
||||||
// cfg_reg0: 0x0020, //0x0A8
|
|
||||||
// cfg_reg1: 0x1040, //0x0A9
|
|
||||||
// cfg_reg2: 0x23FF, //0x0AA
|
|
||||||
// cfg_reg3: 0x0000, //0x0AB
|
|
||||||
// cfg_reg4: 0x0003, //0x0AC
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// rxout_div = 1
|
// rxout_div = 1
|
||||||
CXP_SPEED::CXP_10 | CXP_SPEED::CXP_12 => {
|
CXP_SPEED::CXP_10 | CXP_SPEED::CXP_12 => {
|
||||||
RX_CDR_CFG {
|
CdrConfig {
|
||||||
cfg_reg0: 0x0020, //0x0A8
|
cfg_reg0: 0x0020, //0x0A8
|
||||||
cfg_reg1: 0x1040, //0x0A9
|
cfg_reg1: 0x1040, //0x0A9
|
||||||
cfg_reg2: 0x23FF, //0x0AA
|
cfg_reg2: 0x23FF, //0x0AA
|
||||||
|
@ -255,31 +241,33 @@ pub mod CXP_GTX {
|
||||||
gtx_write(0x0AC, cdr_cfg.cfg_reg4);
|
gtx_write(0x0AC, cdr_cfg.cfg_reg4);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
fn gtx_read(address: u16) -> u16 {
|
fn gtx_read(address: u16) -> u16 {
|
||||||
// DEBUG: DRPCLK need to be on for a few cycle before accessing other DRP ports
|
// DEBUG:
|
||||||
unsafe {
|
unsafe {
|
||||||
csr::cxp::downconn_gtx_daddr_write(address);
|
csr::cxp::downconn_gtx_daddr_write(address);
|
||||||
csr::cxp::downconn_gtx_dread_write(1);
|
csr::cxp::downconn_gtx_dread_write(1);
|
||||||
while (csr::cxp::downconn_gtx_dready_read() != 1) {}
|
while csr::cxp::downconn_gtx_dready_read() != 1 {}
|
||||||
csr::cxp::downconn_gtx_dout_read()
|
csr::cxp::downconn_gtx_dout_read()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn gtx_write(address: u16, value: u16) {
|
fn gtx_write(address: u16, value: u16) {
|
||||||
// DEBUG: DRPCLK need to be on for a few cycle before accessing other DRP ports
|
|
||||||
unsafe {
|
unsafe {
|
||||||
csr::cxp::downconn_gtx_daddr_write(address);
|
csr::cxp::downconn_gtx_daddr_write(address);
|
||||||
csr::cxp::downconn_gtx_din_write(value);
|
csr::cxp::downconn_gtx_din_write(value);
|
||||||
csr::cxp::downconn_gtx_din_stb_write(1);
|
csr::cxp::downconn_gtx_din_stb_write(1);
|
||||||
while (csr::cxp::downconn_gtx_dready_read() != 1) {}
|
while csr::cxp::downconn_gtx_dready_read() != 1 {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
fn qpll_read(address: u8) -> u16 {
|
fn qpll_read(address: u8) -> u16 {
|
||||||
|
// DEBUG:
|
||||||
unsafe {
|
unsafe {
|
||||||
csr::cxp::downconn_qpll_daddr_write(address);
|
csr::cxp::downconn_qpll_daddr_write(address);
|
||||||
csr::cxp::downconn_qpll_dread_write(1);
|
csr::cxp::downconn_qpll_dread_write(1);
|
||||||
while (csr::cxp::downconn_qpll_dready_read() != 1) {}
|
while csr::cxp::downconn_qpll_dready_read() != 1 {}
|
||||||
csr::cxp::downconn_qpll_dout_read()
|
csr::cxp::downconn_qpll_dout_read()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -289,7 +277,7 @@ pub mod CXP_GTX {
|
||||||
csr::cxp::downconn_qpll_daddr_write(address);
|
csr::cxp::downconn_qpll_daddr_write(address);
|
||||||
csr::cxp::downconn_qpll_din_write(value);
|
csr::cxp::downconn_qpll_din_write(value);
|
||||||
csr::cxp::downconn_qpll_din_stb_write(1);
|
csr::cxp::downconn_qpll_din_stb_write(1);
|
||||||
while (csr::cxp::downconn_qpll_dready_read() != 1) {}
|
while csr::cxp::downconn_qpll_dready_read() != 1 {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,9 +17,10 @@ pub fn crc_test() {
|
||||||
csr::cxp::crc_data_write(*a);
|
csr::cxp::crc_data_write(*a);
|
||||||
crc = csr::cxp::crc_value_read();
|
crc = csr::cxp::crc_value_read();
|
||||||
println!("input = {:#04x}", *a);
|
println!("input = {:#04x}", *a);
|
||||||
println!("CRC NOT(val.reverse) = {:#010x}", !crc.reverse_bits());
|
// println!("CRC NOT(val.reverse) = {:#010x}", !crc.reverse_bits());
|
||||||
// since the input bit are reversed when entering the crc engine, the output char need to be reversed to cancel out on the receiver side
|
// since the input bit are reversed when entering the crc engine, the output char need to be reversed to cancel out on the receiver side
|
||||||
println!("CRC CXP = {:#010x}", crc);
|
// println!("CRC CXP = {:#010x}", crc);
|
||||||
|
println!("CRC processed = {:#010x}", csr::cxp::crc_processed_read())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -28,52 +29,58 @@ pub fn tx_test(timer: &mut GlobalTimer) {
|
||||||
// the 8bit shift is k symbol
|
// the 8bit shift is k symbol
|
||||||
// const K28_1: u16 = 0x3C | (1 << 8);
|
// const K28_1: u16 = 0x3C | (1 << 8);
|
||||||
// const K28_5: u16 = 0xBC | (1 << 8);
|
// const K28_5: u16 = 0xBC | (1 << 8);
|
||||||
const D31_1: u16 = 0x3F;
|
|
||||||
const D01_1: u16 = 0x21;
|
const D01_1: u16 = 0x21;
|
||||||
|
const D31_1: u16 = 0x3F;
|
||||||
|
|
||||||
const LEN: usize = 100;
|
const LEN: usize = 200;
|
||||||
let mut arr: [u16; LEN] = [0; LEN];
|
let mut arr: [u16; LEN] = [0; LEN];
|
||||||
|
|
||||||
unsafe {
|
unsafe {
|
||||||
csr::cxp::upconn_clk_reset_write(1);
|
csr::cxp::upconn_bitrate2x_enable_write(1);
|
||||||
// csr::cxp::upconn_bitrate2x_enable_write(1);
|
|
||||||
csr::cxp::upconn_clk_reset_write(0);
|
|
||||||
loop {
|
loop {
|
||||||
// TODO: verify the char & word boundary thingy
|
// TODO: verify the char & word boundary thingy
|
||||||
for _ in 0..8 {
|
for _ in 0..12 {
|
||||||
csr::cxp::upconn_symbol1_write(D01_1);
|
|
||||||
}
|
|
||||||
|
|
||||||
for _ in 0..4 {
|
|
||||||
csr::cxp::upconn_symbol2_write(D31_1);
|
csr::cxp::upconn_symbol2_write(D31_1);
|
||||||
}
|
}
|
||||||
|
|
||||||
timer.delay_us(1);
|
|
||||||
csr::cxp::upconn_tx_enable_write(1);
|
csr::cxp::upconn_tx_enable_write(1);
|
||||||
|
timer.delay_us(1);
|
||||||
|
|
||||||
|
for _ in 0..3 {
|
||||||
|
csr::cxp::upconn_symbol0_write(D01_1);
|
||||||
|
}
|
||||||
|
|
||||||
for i in 0..LEN {
|
for i in 0..LEN {
|
||||||
arr[i] = get_encoded();
|
arr[i] = get_encoded();
|
||||||
}
|
}
|
||||||
|
let mut last_encoded: u16 = 0;
|
||||||
for i in 0..LEN {
|
for i in 0..LEN {
|
||||||
match arr[i] {
|
if last_encoded != arr[i] {
|
||||||
0b1010111001 | 0b0101001001 => {
|
match arr[i] {
|
||||||
println!("encoded = {:#012b} D31.1", arr[i])
|
0b1010111001 | 0b0101001001 => {
|
||||||
}
|
println!("D31.1")
|
||||||
0b0111011001 | 0b1000101001 => {
|
}
|
||||||
println!("encoded = {:#012b} D01.1", arr[i])
|
0b0111011001 | 0b1000101001 => {
|
||||||
}
|
println!("D01.1")
|
||||||
0b0011111010 | 0b1100000101 => {
|
}
|
||||||
println!("encoded = {:#012b} K28.5 start idling....", arr[i])
|
0b1101010010 | 0b0010101101 => {
|
||||||
}
|
println!("D04.4")
|
||||||
0b0011111001 | 0b1100000110 => {
|
}
|
||||||
println!("encoded = {:#012b} K28.1 idling...", arr[i])
|
0b0011111010 | 0b1100000101 => {
|
||||||
}
|
println!("K28.5 start idling....")
|
||||||
0b0011101010 => {
|
}
|
||||||
println!("encoded = {:#012b} D28.5 END idle", arr[i])
|
0b0011111001 | 0b1100000110 => {
|
||||||
}
|
println!("K28.1 idling...")
|
||||||
_ => {
|
}
|
||||||
println!("encoded = {:#012b}", arr[i])
|
0b1010101010 => {
|
||||||
|
println!("D21.5 END idle")
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
println!("encoded = {:#012b}", arr[i])
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
last_encoded = arr[i]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
println!("-------------------------------------");
|
println!("-------------------------------------");
|
||||||
|
|
Loading…
Reference in New Issue