1
0
Fork 0

Compare commits

...

30 Commits

Author SHA1 Message Date
morgan f2d8ffbe05 cxp: cleanup 2024-08-29 13:37:35 +08:00
morgan 98bb49e1b5 cxp upconn: rename modules & cleanup 2024-08-29 11:57:40 +08:00
morgan d88af397ec cxp upconn fw: remove unused csr 2024-08-28 16:58:23 +08:00
morgan 468f9d7185 cxp: add upconn packet handler proto 2024-08-28 16:58:11 +08:00
morgan 08cade7f16 cxp upconn: clenaup & add 41.66Mbps 2024-08-28 16:52:48 +08:00
morgan f5eb196726 cxp upconn fw: correct p3 word 2024-08-28 12:39:51 +08:00
morgan d55bd81137 cxp upconn: rewrite oserdes using CEInserter
IDLE word: fix p3 to D21.5
CLKGEN: missing 24ns
2024-08-28 12:39:51 +08:00
morgan 4170e63a6e cxp downconn: disable debug pad 2024-08-28 10:19:02 +08:00
morgan a39c939a68 cxp upconn: separate 9bits data to data & k code 2024-08-27 16:02:29 +08:00
morgan 97bf7e72b7 cxp upconn: update doc 2024-08-27 12:23:52 +08:00
morgan 868ac41060 cxp: add linklayer upconn proto 2024-08-27 11:15:09 +08:00
morgan e78d0f4083 cxp upconn:clean up and remove csr 2024-08-27 11:12:49 +08:00
morgan bc0d45cd82 zc706: add upconn pads 2024-08-27 10:56:10 +08:00
morgan 1331281e5c cxp: add upconn pads 2024-08-27 10:56:10 +08:00
morgan 75407b2ff4 cxp upconn: add upconn pads & cleanup
cxp upconn: cleanup
2024-08-27 10:56:10 +08:00
morgan 0cf8cd42d5 cxp 4r fmc: update LVCMOS 2.5V to 3.3V 2024-08-27 10:44:45 +08:00
morgan b8aea61fd8 cxp upconn: send IDLE every 10000 words & cleanup 2024-08-26 16:44:45 +08:00
morgan 2aa194390f cxp upconn: refactor to use one encoder 2024-08-26 16:28:49 +08:00
morgan 94d569502b cxp up conn fw: update test case 2024-08-26 11:57:57 +08:00
morgan 679b430d74 cxp downconn: cleanup debug csr 2024-08-26 10:48:07 +08:00
morgan 7e6d4e186f cxp downconn fw: replace tx/rx rate with DRP 2024-08-26 10:47:44 +08:00
morgan 7631907d76 cxp downconn: remove tx/rx rate 2024-08-26 10:47:44 +08:00
morgan 95297157c4 zc706: constraint the CXP CLKs & add docs 2024-08-26 10:47:39 +08:00
morgan f34f500ed8 cxp downconn: refactor to allow gtx extensions 2024-08-22 16:33:51 +08:00
morgan 2dade34119 cxp downconn: refactor QPLl DRP into the module 2024-08-22 15:43:22 +08:00
morgan 1460f5b94b cxp downconn fw: fix warning and cleanup 2024-08-22 13:34:35 +08:00
morgan 66dee0b812 cxp downconn: update to accept a list of gtx pins 2024-08-22 12:50:17 +08:00
morgan 3102dd8a52 zc706: use 4.0 period constraint to fix s/h issue 2024-08-22 12:49:37 +08:00
morgan 1ddd6f4a4a zc706: use all CXP HS pads 2024-08-22 12:49:10 +08:00
morgan 6f6e580a92 cxp fmc: fix typo 2024-08-22 10:51:07 +08:00
7 changed files with 575 additions and 529 deletions

View File

@ -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 UpConn_Packets(Module, AutoCSR):
def __init__(self, upconn_pads, sys_clk_freq, debug_sma, pmod_pads, fifos_depth=64):
# increment after ack
# for CXP 2.0 or latest, command packet need to includet tags
# section 9.6.1.2 (CXP-001-2021)
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.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): class CXP_CRC(Module, AutoCSR):
width = 32
polynom = 0x04C11DB7
seed = 2**width-1
def __init__(self, data_width): 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.d = Signal(data_width)
self.stb = Signal() self.stb = Signal()
self.reset = Signal() self.reset = Signal()
self.val = Signal(self.width, reset=self.seed) self.val = Signal(crc_width, reset=seed)
self.data = CSR(data_width)
self.en = CSR()
self.value = CSRStatus(self.width)
self.processed = CSRStatus(self.width)
# # # # # #
self.submodules.engine = LiteEthMACCRCEngine(data_width, self.width, self.polynom) 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),

View File

@ -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")),

View File

@ -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")
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
# NOTE: No need to connect cxp_gtx_tx, we don't use tx anyway (just for loopback)
# 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,

View File

@ -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, self.sync += [
NextValue(self.tx_idle.word_idx, 0), If(self.oe,
NextValue(tx_wordcount, 0), # send LSB first
NextValue(tx_bitcount, 0),
NextState("LOAD_CHAR")
)
)
self.fsm.act("LOAD_CHAR",
NextValue(idling, 1),
NextValue(self.tx_idle.source_ack, 1),
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),
) )
] ]
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 # 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() 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),
)
),
]

View File

@ -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

View File

@ -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 {}
} }
} }
} }

View File

@ -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!("-------------------------------------");