1
0
Fork 0

Compare commits

..

35 Commits

Author SHA1 Message Date
morgan 7285479f5b cxp GW: use upconn layout 2024-09-12 13:22:07 +08:00
morgan cb0a0358a3 upconn GW: use pipeline upconn layout var 2024-09-12 13:22:07 +08:00
morgan 790f0196b6 pipeline GW: use a common layout 2024-09-12 13:22:07 +08:00
morgan b1069f524a upconn fw: use cxp_protco and remove unused function 2024-09-12 13:22:07 +08:00
morgan 98095664ce cxp protocol: init
protocol: uncomment error
2024-09-12 13:22:07 +08:00
morgan 12bf931614 dowconn GW: rename csr 2024-09-11 17:47:07 +08:00
morgan b725594a1d cxp GW: wrap gtxs into downconn interface 2024-09-11 17:46:57 +08:00
morgan fe57067f70 upconn fw: rename csr 2024-09-11 17:41:50 +08:00
morgan 8b2181b1a8 cxp GW: rename phy 2024-09-11 17:41:40 +08:00
morgan 08ee4f1cb9 cxp GW: rename to downconn phy 2024-09-11 17:20:37 +08:00
morgan 133802fef2 downconn GW: rename to PHY 2024-09-11 17:20:19 +08:00
morgan 5af2d8c23b pipeline GW: cleanup docs 2024-09-11 16:37:20 +08:00
morgan 581d9ffebb upconn fw: add test packet 2024-09-11 16:06:16 +08:00
morgan 6943a2b17e pipeline GW: add busy csr 2024-09-11 16:05:13 +08:00
morgan 5c253fefb6 upconn fw: add event ack packet 2024-09-11 12:48:06 +08:00
morgan aeabca2182 upconn fw: refactor Control W/R packet 2024-09-11 11:57:21 +08:00
morgan 16ccd7eada upconn fw: add tx test packet csr control 2024-09-11 10:17:26 +08:00
morgan d9888d7647 cxp GW: add test packet to phy 2024-09-11 10:16:47 +08:00
morgan 6b50d83e67 pipeline GW: add test packet 2024-09-11 10:16:35 +08:00
morgan b2ce43155e pipeline GW: clean up variable naming 2024-09-10 16:43:54 +08:00
morgan d81c770e54 upconn GW: clean up 2024-09-10 12:17:51 +08:00
morgan f83afc7195 main: add testing 2024-09-09 10:52:11 +08:00
morgan 38485aec56 cxp: remove unused import 2024-09-09 10:51:27 +08:00
morgan 7dbeefd0a4 upconn fw: add big endian doc 2024-09-09 10:08:26 +08:00
morgan fa674e32f5 upconn GW: rename variable 2024-09-09 10:08:26 +08:00
morgan 9f8f8c1ad0 cxp downconn firmware: GTX setup
testing: add loopmode mode & tsusrclk mmcm drp bitbang
testing: add IDLE word printout
downconn: add QPLL and GTX setup
downconn: add DRP to support all CXP linerate up to 12.5Gbps
2024-09-09 10:08:26 +08:00
morgan 397027876c cxp upconn firmware: low speed serial setup
testing: add trigger & trigger ack CSR to test tranmission prioirty
upconn: add some upconn error for control flow
upconn: add control packet writer
control packet: add u32 & u64 register control
control packet: add CRC calculation & packet type inserter in fw
2024-09-09 10:08:26 +08:00
morgan 8a6e89b2d8 zc706: add CXP_DEMO variant
zc706: add fmc pads
zc706: add constraint to fix comma alignment & setup/hold time issue
2024-09-09 10:08:26 +08:00
morgan f0dda0fcf7 cxp: add upconn interface, downconn PHY & crc
testing: add CSR control for tx trigger & trigger ack
upconn: connect trigger, trigger ack & command_packet to UpConnPHY
downconn: add GTX PHY
2024-09-09 10:08:26 +08:00
morgan 04932d630f cxp pipeline: packet handling pipeline
tx pipeline: add CRC32 inserter
tx pipeline: add start & end of packet code inserter
tx pipeline: add packet wrapper for start & stop packet indication
tx pipeline: add code source for trigger & trigger ack packet
tx pipeline: add packet for trigger & trigger ack
tx pipeline: add tx_command_packet for firmware
tx command packet: add fifo to store control packet
2024-09-09 10:08:26 +08:00
morgan d9fb50c12e cxp upconn gw: add low speed serial PHY
testing: add debug fifo output b4 encoder
cxp upconn: add low speed serial
cxp upconn: add reset, tx_busy, tx_enable
cxp upconn: add clockgen module for 20.83Mbps & 41.66Mbps using counters
cxp upconn: add oserdes using CEInserter
cxp upconn: add packet scheduler with CEInserter
scheduler: send priority packet during word/char boundary
scheduler: send IDLE every 10000 words
scheduler: encode packet before sending to oserdes
2024-09-09 10:08:26 +08:00
morgan fa5ede6174 cxp downconn gw: add gtx up to 12.5Gbps
testing: add txusrclk mmcm & loopback mode
testing: add debug output
downconn: add GTX and QPLL support
downconn: add DRP for GTX and QPLL to support all CXP linerates
GTX: add gtx with mmcm for TXUSRCLK freq requirement
GTX: add loopback mode parameter for testing
GTX: add gtx with 40bits internal width
GTX: use built-in comma aligner
GTX: add comma checker to ensure comma is aligner on highest linerate
GTX: set QPLL as CLK source for GTX
2024-09-09 10:08:26 +08:00
morgan ce0f302b22 fmc: add cxp_4r_fmc adepter io 2024-09-09 10:08:26 +08:00
morgan 3a80f37a2f flake: add CXP_DEMO variant build options 2024-09-09 10:08:26 +08:00
morgan f19c7fa369 flake: update dependencies 2024-09-09 10:08:26 +08:00
10 changed files with 493 additions and 333 deletions

View File

@ -11,11 +11,11 @@
"src-pythonparser": "src-pythonparser" "src-pythonparser": "src-pythonparser"
}, },
"locked": { "locked": {
"lastModified": 1724210813, "lastModified": 1725373154,
"narHash": "sha256-OqQdE2lC0jKNS2fFq0Fda1nBpyT8ijmSXqdkO8xeOJ8=", "narHash": "sha256-fq9EW9fDWrV0v1vNj7ZqDNpNYx8+OxoFdPwpvkPf67g=",
"ref": "refs/heads/master", "ref": "refs/heads/master",
"rev": "61e96b37f9c4345e2d7bf71d47ba0b5e947de83e", "rev": "0c1ffa9f4f6a3e7864459923ec4b9cc45f16327a",
"revCount": 8985, "revCount": 9005,
"type": "git", "type": "git",
"url": "https://github.com/m-labs/artiq.git" "url": "https://github.com/m-labs/artiq.git"
}, },
@ -102,11 +102,11 @@
}, },
"nixpkgs": { "nixpkgs": {
"locked": { "locked": {
"lastModified": 1723362943, "lastModified": 1724224976,
"narHash": "sha256-dFZRVSgmJkyM0bkPpaYRtG/kRMRTorUIDj8BxoOt1T4=", "narHash": "sha256-Z/ELQhrSd7bMzTO8r7NZgi9g5emh+aRKoCdaAv5fiO0=",
"owner": "NixOS", "owner": "NixOS",
"repo": "nixpkgs", "repo": "nixpkgs",
"rev": "a58bc8ad779655e790115244571758e8de055e3d", "rev": "c374d94f1536013ca8e92341b540eba4c22f9c62",
"type": "github" "type": "github"
}, },
"original": { "original": {
@ -169,11 +169,11 @@
"src-migen": { "src-migen": {
"flake": false, "flake": false,
"locked": { "locked": {
"lastModified": 1721561053, "lastModified": 1724304798,
"narHash": "sha256-z3LRhNmKZrjr6rFD0yxtccSa/SWvFIYmb+G/D5d2Jd8=", "narHash": "sha256-tQ02N0eXY5W/Z7CrOy3Cu4WjDZDQWb8hYlzsFzr3Mus=",
"owner": "m-labs", "owner": "m-labs",
"repo": "migen", "repo": "migen",
"rev": "9279e8623f8433bc4f23ac51e5e2331bfe544417", "rev": "832a7240ba32af9cbd4fdd519ddcb4f912534726",
"type": "github" "type": "github"
}, },
"original": { "original": {

View File

@ -2,7 +2,7 @@ from migen import *
from misoc.interconnect.csr import * from misoc.interconnect.csr import *
from misoc.interconnect import stream from misoc.interconnect import stream
from cxp_downconn import CXP_DownConn from cxp_downconn import CXP_DownConn_PHY
from cxp_upconn import CXP_UpConn_PHY from cxp_upconn import CXP_UpConn_PHY
from cxp_pipeline import * from cxp_pipeline import *
@ -10,11 +10,20 @@ class CXP(Module, AutoCSR):
def __init__(self, refclk, downconn_pads, upconn_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.upconn = UpConn_Interface(upconn_pads, sys_clk_freq, debug_sma, pmod_pads) self.submodules.upconn = UpConn_Interface(upconn_pads, sys_clk_freq, debug_sma, pmod_pads)
self.submodules.downconn = CXP_DownConn(refclk, downconn_pads, sys_clk_freq, debug_sma, pmod_pads) self.submodules.downconn = DownConn_Interface(refclk, downconn_pads, sys_clk_freq, debug_sma, pmod_pads)
# TODO: support the option high speed upconn # TODO: support the option high speed upconn
# TODO: add link layer # TODO: add link layer
class DownConn_Interface(Module, AutoCSR):
def __init__(self, refclk, downconn_pads, sys_clk_freq, debug_sma, pmod_pads):
# # #
self.submodules.phy = phy = CXP_DownConn_PHY(refclk, downconn_pads, sys_clk_freq, debug_sma, pmod_pads)
self.gtxs = phy.gtxs
class UpConn_Interface(Module, AutoCSR): class UpConn_Interface(Module, AutoCSR):
def __init__(self, upconn_pads, sys_clk_freq, debug_sma, pmod_pads): def __init__(self, upconn_pads, sys_clk_freq, debug_sma, pmod_pads):
@ -23,24 +32,24 @@ class UpConn_Interface(Module, AutoCSR):
self.tx_enable = CSRStorage() self.tx_enable = CSRStorage()
self.tx_busy = CSRStatus() self.tx_busy = CSRStatus()
self.tx_testmode_en = CSRStorage()
# # # # # #
layout = [("data", 8), ("k", 1)] self.submodules.phy = phy = CXP_UpConn_PHY(upconn_pads, sys_clk_freq, debug_sma, pmod_pads)
self.submodules.upconn_phy = upconn_phy = CXP_UpConn_PHY(upconn_pads, sys_clk_freq, debug_sma, pmod_pads, layout)
self.sync += [ self.sync += [
upconn_phy.bitrate2x_enable.eq(self.bitrate2x_enable.storage), phy.bitrate2x_enable.eq(self.bitrate2x_enable.storage),
upconn_phy.tx_enable.eq(self.tx_enable.storage), phy.tx_enable.eq(self.tx_enable.storage),
upconn_phy.clk_reset.eq(self.clk_reset.re), phy.clk_reset.eq(self.clk_reset.re),
self.tx_busy.status.eq(upconn_phy.tx_busy), self.tx_busy.status.eq(phy.tx_busy),
] ]
# Packet FIFOs with transmission priority # Packet FIFOs with transmission priority
# 0: Trigger packet # 0: Trigger packet
self.submodules.trig = trig = TX_Trigger(layout) self.submodules.trig = trig = TX_Trigger()
self.comb += trig.source.connect(upconn_phy.sinks[0]) self.comb += trig.source.connect(phy.sinks[0])
# DEBUG: INPUT # DEBUG: INPUT
self.trig_stb = CSR() self.trig_stb = CSR()
@ -55,8 +64,8 @@ class UpConn_Interface(Module, AutoCSR):
# 1: IO acknowledgment for trigger packet # 1: IO acknowledgment for trigger packet
self.submodules.trig_ack = trig_ack = Trigger_ACK(layout) self.submodules.trig_ack = trig_ack = Trigger_ACK()
self.comb += trig_ack.source.connect(upconn_phy.sinks[1]) self.comb += trig_ack.source.connect(phy.sinks[1])
# DEBUG: INPUT # DEBUG: INPUT
self.ack = CSR() self.ack = CSR()
@ -65,5 +74,16 @@ class UpConn_Interface(Module, AutoCSR):
# 2: All other packets # 2: All other packets
# Control is not timing dependent, all the link layer is done in firmware # Control is not timing dependent, all the link layer is done in firmware
self.submodules.command = command = TX_Command_Packet(layout) self.submodules.command = command = TX_Command_Packet()
self.comb += command.source.connect(upconn_phy.sinks[2]) self.submodules.testseq = testseq = TX_Test_Packet()
self.submodules.mux = mux = stream.Multiplexer(upconn_layout, 2)
self.comb += [
command.source.connect(mux.sink0),
testseq.source.connect(mux.sink1),
mux.sel.eq(self.tx_testmode_en.storage),
mux.source.connect(phy.sinks[2])
]

View File

@ -10,7 +10,7 @@ from artiq.gateware.drtio.transceiver.gtx_7series_init import *
from functools import reduce from functools import reduce
from operator import add from operator import add
class CXP_DownConn(Module, AutoCSR): class CXP_DownConn_PHY(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) nconn = len(pads)
self.rx_start_init = CSRStorage() self.rx_start_init = CSRStorage()

View File

@ -3,6 +3,11 @@ from misoc.interconnect.csr import *
from misoc.interconnect import stream from misoc.interconnect import stream
from misoc.cores.liteeth_mini.mac.crc import LiteEthMACCRCEngine, LiteEthMACCRCChecker from misoc.cores.liteeth_mini.mac.crc import LiteEthMACCRCEngine, LiteEthMACCRCChecker
upconn_dw = 8
upconn_layout = [("data", upconn_dw), ("k", upconn_dw//8)]
def K(x, y): def K(x, y):
return ((y << 5) | x) return ((y << 5) | x)
@ -52,11 +57,11 @@ class Code_Source(Module):
class Code_Inserter(Module): class Code_Inserter(Module):
def __init__(self, layout, insert_infront=True, counts=4): def __init__(self, layout, insert_infront=True, counts=4):
self.sink = sink = stream.Endpoint(layout) self.sink = stream.Endpoint(layout)
self.source = source = stream.Endpoint(layout) self.source = stream.Endpoint(layout)
self.data = Signal.like(sink.data) self.data = Signal.like(self.sink.data)
self.k = Signal.like(sink.k) self.k = Signal.like(self.sink.k)
# # # # # #
assert counts > 0 assert counts > 0
@ -77,61 +82,61 @@ class Code_Inserter(Module):
if insert_infront: if insert_infront:
fsm.act("IDLE", fsm.act("IDLE",
sink.ack.eq(1), self.sink.ack.eq(1),
clr_cnt.eq(1), clr_cnt.eq(1),
If(sink.stb, If(self.sink.stb,
sink.ack.eq(0), self.sink.ack.eq(0),
NextState("INSERT"), NextState("INSERT"),
) )
) )
fsm.act("INSERT", fsm.act("INSERT",
sink.ack.eq(0), self.sink.ack.eq(0),
source.stb.eq(1), self.source.stb.eq(1),
source.data.eq(self.data), self.source.data.eq(self.data),
source.k.eq(self.k), self.source.k.eq(self.k),
If(cnt == counts - 1, If(cnt == counts - 1,
If(source.ack, NextState("COPY")) If(self.source.ack, NextState("COPY"))
).Else( ).Else(
inc_cnt.eq(source.ack) inc_cnt.eq(self.source.ack)
) )
) )
fsm.act("COPY", fsm.act("COPY",
sink.connect(source), self.sink.connect(self.source),
If(sink.stb & sink.eop & source.ack, If(self.sink.stb & self.sink.eop & self.source.ack,
NextState("IDLE"), NextState("IDLE"),
) )
) )
else: else:
fsm.act("IDLE", fsm.act("IDLE",
sink.ack.eq(1), self.sink.ack.eq(1),
clr_cnt.eq(1), clr_cnt.eq(1),
If(sink.stb, If(self.sink.stb,
sink.ack.eq(0), self.sink.ack.eq(0),
NextState("COPY"), NextState("COPY"),
) )
) )
fsm.act("COPY", fsm.act("COPY",
sink.connect(source), self.sink.connect(self.source),
source.eop.eq(0), self.source.eop.eq(0),
If(sink.stb & sink.eop & source.ack, If(self.sink.stb & self.sink.eop & self.source.ack,
NextState("INSERT"), NextState("INSERT"),
) )
) )
fsm.act("INSERT", fsm.act("INSERT",
sink.ack.eq(0), self.sink.ack.eq(0),
source.stb.eq(1), self.source.stb.eq(1),
source.data.eq(self.data), self.source.data.eq(self.data),
source.k.eq(self.k), self.source.k.eq(self.k),
If(cnt == counts - 1, If(cnt == counts - 1,
source.eop.eq(1), self.source.eop.eq(1),
If(source.ack, NextState("IDLE")) If(self.source.ack, NextState("IDLE"))
).Else( ).Else(
inc_cnt.eq(source.ack) inc_cnt.eq(self.source.ack)
), ),
) )
@ -184,22 +189,22 @@ class CXPCRC32Checker(LiteEthMACCRCChecker):
LiteEthMACCRCChecker.__init__(self, CXPCRC32, layout) LiteEthMACCRCChecker.__init__(self, CXPCRC32, layout)
class TX_Trigger(Module, AutoCSR): class TX_Trigger(Module, AutoCSR):
def __init__(self, layout): def __init__(self):
self.trig_stb = Signal() self.trig_stb = Signal()
self.delay = Signal(8) self.delay = Signal(upconn_dw)
self.linktrig_mode = Signal(max=4) self.linktrig_mode = Signal(max=4)
# # # # # #
self.submodules.code_src = code_src = Code_Source(layout, counts=3) self.submodules.code_src = code_src = Code_Source(upconn_layout, counts=3)
self.comb += [ self.comb += [
code_src.stb.eq(self.trig_stb), code_src.stb.eq(self.trig_stb),
code_src.data.eq(self.delay), code_src.data.eq(self.delay),
code_src.k.eq(0) code_src.k.eq(0)
] ]
self.submodules.inserter_once = inserter_once = Code_Inserter(layout, counts=1) self.submodules.inserter_once = inserter_once = Code_Inserter(upconn_layout, counts=1)
self.submodules.inserter_twice = inserter_twice = Code_Inserter(layout, counts=2) self.submodules.inserter_twice = inserter_twice = Code_Inserter(upconn_layout, counts=2)
self.comb += [ self.comb += [
inserter_once.k.eq(1), inserter_once.k.eq(1),
inserter_twice.k.eq(1), inserter_twice.k.eq(1),
@ -220,15 +225,15 @@ class TX_Trigger(Module, AutoCSR):
self.source = tx_pipeline[-1].source self.source = tx_pipeline[-1].source
class Trigger_ACK(Module): class Trigger_ACK(Module):
def __init__(self, layout): def __init__(self):
self.ack = Signal() self.ack = Signal()
# # # # # #
# Section 9.3.2 (CXP-001-2021) # Section 9.3.2 (CXP-001-2021)
# Send 4x K28.6 and 4x 0x01 as trigger packet ack # Send 4x K28.6 and 4x 0x01 as trigger packet ack
self.submodules.code_src = code_src = Code_Source(layout) self.submodules.code_src = code_src = Code_Source(upconn_layout)
self.submodules.k_code_inserter = k_code_inserter = Code_Inserter(layout) self.submodules.k_code_inserter = k_code_inserter = Code_Inserter(upconn_layout)
self.comb += [ self.comb += [
code_src.stb.eq(self.ack), code_src.stb.eq(self.ack),
code_src.data.eq(0x01), code_src.data.eq(0x01),
@ -242,20 +247,17 @@ class Trigger_ACK(Module):
self.source = k_code_inserter.source self.source = k_code_inserter.source
class TX_Command_Packet(Module, AutoCSR): class TX_Command_Packet(Module, AutoCSR):
def __init__(self, layout): def __init__(self):
self.len = CSRStorage(6) self.len = CSRStorage(6)
self.data = CSR(8) self.data = CSR(upconn_dw)
self.writeable = CSRStatus() self.writeable = CSRStatus()
# # # # # #
# TODO: use RAM instead of FIFO ?
# Section 12.1.2 (CXP-001-2021) # Section 12.1.2 (CXP-001-2021)
# Max control packet size is 128 bytes # Max control packet size is 128 bytes
self.submodules.fifo = fifo = stream.SyncFIFO(upconn_layout, 128)
# NOTE: The firmware will lock up if there is not enough space for the packet self.submodules.pak_wrp = pak_wrp = Packet_Wrapper(upconn_layout)
self.submodules.fifo = fifo = stream.SyncFIFO(layout, 128)
self.submodules.pak_wrp = pak_wrp = Packet_Wrapper(layout)
self.source = pak_wrp.source self.source = pak_wrp.source
self.comb += fifo.source.connect(pak_wrp.sink) self.comb += fifo.source.connect(pak_wrp.sink)
@ -278,3 +280,69 @@ class TX_Command_Packet(Module, AutoCSR):
), ),
) )
] ]
class TX_Test_Packet(Module, AutoCSR):
def __init__(self):
self.stb = CSR()
self.busy = CSRStatus()
# # #
testdata_src = stream.Endpoint(upconn_layout)
# Section 9.9.2 (CXP-001-2021)
# 0x00, 0x01 ... 0xFF need to be send 16 times
# cnt[8:12] is used to count up 16 times while cnt[:8] is the data
cnt = Signal(max=0x1000)
clr_cnt = Signal()
inc_cnt = Signal()
self.sync += [
If(clr_cnt,
cnt.eq(cnt.reset),
).Elif(inc_cnt,
cnt.eq(cnt + 1),
),
]
self.submodules.fsm = fsm = FSM(reset_state="IDLE")
fsm.act("IDLE",
clr_cnt.eq(1),
If(self.stb.re,
NextState("WRITE")
)
)
fsm.act("WRITE",
testdata_src.stb.eq(1),
testdata_src.data.eq(cnt[:8]),
testdata_src.k.eq(0),
If(cnt == 0xFFF,
testdata_src.eop.eq(1),
If(testdata_src.ack, NextState("IDLE"))
).Else(
inc_cnt.eq(testdata_src.ack)
)
)
self.submodules.pak_type_inserter = pak_type_inserter = Code_Inserter(upconn_layout)
self.submodules.pak_wrp = pak_wrp = Packet_Wrapper(upconn_layout)
self.comb += [
pak_type_inserter.data.eq(0x04),
pak_type_inserter.k.eq(0x04),
testdata_src.connect(pak_type_inserter.sink),
pak_type_inserter.source.connect(pak_wrp.sink),
]
self.source = pak_wrp.source
self.sync += \
If(self.stb.re,
self.busy.status.eq(1),
).Elif(self.source.eop & self.source.ack,
self.busy.status.eq(0)
)

View File

@ -7,6 +7,8 @@ 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 *
from cxp_pipeline import upconn_layout
IDLE_CHARS = Array([ IDLE_CHARS = Array([
#[char, k] #[char, k]
[0xBC, 1], #K28.5 [0xBC, 1], #K28.5
@ -93,23 +95,27 @@ class SERDES_10bits(Module):
] ]
@ResetInserter() @ResetInserter()
@CEInserter() class Transmit_Scheduler(Module):
class Packets_Scheduler(Module): def __init__(self, interface, debug_buf):
def __init__(self, tx_fifos, debug_buf):
self.tx_enable = Signal() self.tx_enable = Signal()
self.oe = Signal() self.oe = Signal()
self.ce = Signal()
# # # # # #
self.submodules.startup_fsm = startup_fsm = FSM(reset_state="WAIT_TX_ENABLE") self.submodules.startup_fsm = startup_fsm = CEInserter()(FSM(reset_state="WAIT_TX_ENABLE"))
self.submodules.encoder = encoder = SingleEncoder(True) self.submodules.encoder = encoder = CEInserter()(SingleEncoder(True))
self.comb += [
startup_fsm.ce.eq(self.ce),
encoder.ce.eq(self.ce),
]
tx_charcount = Signal(max=4) tx_charcount = Signal(max=4)
tx_wordcount = Signal(max=10000) tx_wordcount = Signal(max=10000)
idling = Signal() idling = Signal()
priorities = Signal.like(tx_fifos.pe.o) priorities = Signal.like(interface.pe.o)
# DEBUG: # DEBUG:
self.idling = Signal() self.idling = Signal()
@ -143,20 +149,25 @@ class Packets_Scheduler(Module):
) )
) )
# hold ack for only one sys clk cycle to prevent data loss
for ack in interface.sink_ack:
self.sync += ack.eq(0)
self.sync += [ self.sync += [
If(self.oe, debug_buf.sink_stb.eq(0),
If(self.oe & self.ce,
encoder.disp_in.eq(encoder.disp_out), encoder.disp_in.eq(encoder.disp_out),
If((~tx_fifos.pe.n) & (tx_fifos.pe.o == 0), If((~interface.pe.n) & (interface.pe.o == 0),
# trigger packets are inserted at char boundary and don't contribute to word count # trigger packets are inserted at char boundary and don't contribute to word count
tx_fifos.source_ack[0].eq(1), interface.sink_ack[0].eq(1),
encoder.d.eq(tx_fifos.source_data[0]), encoder.d.eq(interface.sink_data[0]),
encoder.k.eq(tx_fifos.source_k[0]), encoder.k.eq(interface.sink_k[0]),
# DEBUG: # DEBUG:
If(debug_buf.sink_ack, If(debug_buf.sink_ack,
debug_buf.sink_stb.eq(1), debug_buf.sink_stb.eq(1),
debug_buf.sink_data.eq(tx_fifos.source_data[0]), debug_buf.sink_data.eq(interface.sink_data[0]),
debug_buf.sink_k.eq(tx_fifos.source_k[0]), debug_buf.sink_k.eq(interface.sink_k[0]),
) )
).Else( ).Else(
If(tx_charcount == 3, If(tx_charcount == 3,
@ -164,20 +175,20 @@ class Packets_Scheduler(Module):
# Section 9.2.4 (CXP-001-2021) # Section 9.2.4 (CXP-001-2021)
# other priorities packets are inserted at word boundary # other priorities packets are inserted at word boundary
If((~tx_fifos.pe.n) & (tx_wordcount != 9999), If((~interface.pe.n) & (tx_wordcount != 9999),
idling.eq(0), idling.eq(0),
priorities.eq(tx_fifos.pe.o), priorities.eq(interface.pe.o),
tx_wordcount.eq(tx_wordcount + 1), tx_wordcount.eq(tx_wordcount + 1),
tx_fifos.source_ack[tx_fifos.pe.o].eq(1), interface.sink_ack[interface.pe.o].eq(1),
encoder.d.eq(tx_fifos.source_data[tx_fifos.pe.o]), encoder.d.eq(interface.sink_data[interface.pe.o]),
encoder.k.eq(tx_fifos.source_k[tx_fifos.pe.o]), encoder.k.eq(interface.sink_k[interface.pe.o]),
# DEBUG: # DEBUG:
If(debug_buf.sink_ack, If(debug_buf.sink_ack,
debug_buf.sink_stb.eq(1), debug_buf.sink_stb.eq(1),
debug_buf.sink_data.eq(tx_fifos.source_data[tx_fifos.pe.o]), debug_buf.sink_data.eq(interface.sink_data[interface.pe.o]),
debug_buf.sink_k.eq(tx_fifos.source_k[tx_fifos.pe.o]), debug_buf.sink_k.eq(interface.sink_k[interface.pe.o]),
) )
).Else( ).Else(
# Section 9.2.5.1 (CXP-001-2021) # Section 9.2.5.1 (CXP-001-2021)
@ -198,15 +209,16 @@ class Packets_Scheduler(Module):
).Else( ).Else(
tx_charcount.eq(tx_charcount + 1), tx_charcount.eq(tx_charcount + 1),
If(~idling, If(~idling,
tx_fifos.source_ack[priorities].eq(1), tx_wordcount.eq(tx_wordcount + 1),
encoder.d.eq(tx_fifos.source_data[priorities]), interface.sink_ack[priorities].eq(1),
encoder.k.eq(tx_fifos.source_k[priorities]), encoder.d.eq(interface.sink_data[priorities]),
encoder.k.eq(interface.sink_k[priorities]),
# DEBUG: # DEBUG:
If(debug_buf.sink_ack, If(debug_buf.sink_ack,
debug_buf.sink_stb.eq(1), debug_buf.sink_stb.eq(1),
debug_buf.sink_data.eq(tx_fifos.source_data[priorities]), debug_buf.sink_data.eq(interface.sink_data[priorities]),
debug_buf.sink_k.eq(tx_fifos.source_k[priorities]), debug_buf.sink_k.eq(interface.sink_k[priorities]),
) )
).Else( ).Else(
encoder.d.eq(IDLE_CHARS[tx_charcount + 1][0]), encoder.d.eq(IDLE_CHARS[tx_charcount + 1][0]),
@ -226,11 +238,10 @@ class Packets_Scheduler(Module):
class PHY_Interface(Module): class PHY_Interface(Module):
def __init__(self, layout, nsink): def __init__(self, layout, nsink):
sink_stb = Signal(nsink)
self.source_stb = Signal(nsink) self.sink_ack = Array(Signal() for _ in range(nsink))
self.source_ack = Array(Signal() for _ in range(nsink)) self.sink_data = Array(Signal(8) for _ in range(nsink))
self.source_data = Array(Signal(8) for _ in range(nsink)) self.sink_k = Array(Signal() for _ in range(nsink))
self.source_k = Array(Signal() for _ in range(nsink))
# # # # # #
@ -239,28 +250,19 @@ class PHY_Interface(Module):
sink = stream.Endpoint(layout) sink = stream.Endpoint(layout)
self.sinks += [sink] self.sinks += [sink]
self.sync += [ self.comb += [
If(self.source_ack[i], sink.ack.eq(self.sink_ack[i]),
# reset ack after asserted sink_stb[i].eq(sink.stb),
# since upconn clk run much slower, the ack will be high for longer than expected which will result in data loss self.sink_data[i].eq(sink.data),
self.source_ack[i].eq(0), self.sink_k[i].eq(sink.k),
sink.ack.eq(1),
).Else(
sink.ack.eq(0),
),
self.source_stb[i].eq(sink.stb),
self.source_data[i].eq(sink.data),
self.source_k[i].eq(sink.k),
] ]
# FIFOs transmission priority # FIFOs transmission priority
self.submodules.pe = PriorityEncoder(nsink) self.submodules.pe = PriorityEncoder(nsink)
self.comb += self.pe.i.eq(self.source_stb) self.comb += self.pe.i.eq(sink_stb)
class Debug_buffer(Module,AutoCSR): class Debug_buffer(Module,AutoCSR):
def __init__(self, layout): def __init__(self, layout):
self.sink_stb = Signal() self.sink_stb = Signal()
self.sink_ack = Signal() self.sink_ack = Signal()
self.sink_data = Signal(8) self.sink_data = Signal(8)
@ -271,19 +273,10 @@ class Debug_buffer(Module,AutoCSR):
self.submodules.buf_out = buf_out = stream.SyncFIFO(layout, 128) self.submodules.buf_out = buf_out = stream.SyncFIFO(layout, 128)
self.sync += [ self.sync += [
If(self.sink_stb, buf_out.sink.stb.eq(self.sink_stb),
# reset ack after asserted
# since upconn clk run much slower, the stb will be high for longer than expected which will result in multiple data entry
self.sink_stb.eq(0),
buf_out.sink.stb.eq(1),
).Else(
buf_out.sink.stb.eq(0),
),
self.sink_ack.eq(buf_out.sink.ack), self.sink_ack.eq(buf_out.sink.ack),
buf_out.sink.data.eq(self.sink_data), buf_out.sink.data.eq(self.sink_data),
buf_out.sink.k.eq(self.sink_k), buf_out.sink.k.eq(self.sink_k),
] ]
self.inc = CSR() self.inc = CSR()
@ -301,7 +294,7 @@ class Debug_buffer(Module,AutoCSR):
class CXP_UpConn_PHY(Module, AutoCSR): class CXP_UpConn_PHY(Module, AutoCSR):
def __init__(self, pad, sys_clk_freq, debug_sma, pmod_pads, layout, nsink=3): def __init__(self, pad, sys_clk_freq, debug_sma, pmod_pads, nsink=3):
self.bitrate2x_enable = Signal() self.bitrate2x_enable = Signal()
self.clk_reset = Signal() self.clk_reset = Signal()
@ -311,18 +304,18 @@ class CXP_UpConn_PHY(Module, AutoCSR):
# # # # # #
self.submodules.cg = cg = UpConn_ClockGen(sys_clk_freq) self.submodules.cg = cg = UpConn_ClockGen(sys_clk_freq)
self.submodules.interface = interface = PHY_Interface(layout, nsink) self.submodules.interface = interface = PHY_Interface(upconn_layout, nsink)
self.sinks = interface.sinks self.sinks = interface.sinks
# DEBUG: # DEBUG:
self.submodules.debug_buf = debug_buf = Debug_buffer(layout) self.submodules.debug_buf = debug_buf = Debug_buffer(upconn_layout)
self.submodules.scheduler = scheduler = Packets_Scheduler(interface, debug_buf) self.submodules.scheduler = scheduler = Transmit_Scheduler(interface, debug_buf)
self.submodules.serdes = serdes = SERDES_10bits(pad) self.submodules.serdes = serdes = SERDES_10bits(pad)
self.comb += [ self.comb += [
self.tx_busy.eq(interface.source_stb != 0), self.tx_busy.eq(~interface.pe.n),
cg.reset.eq(self.clk_reset), cg.reset.eq(self.clk_reset),
cg.freq2x_enable.eq(self.bitrate2x_enable), cg.freq2x_enable.eq(self.bitrate2x_enable),
@ -355,8 +348,8 @@ class CXP_UpConn_PHY(Module, AutoCSR):
] ]
self.specials += [ self.specials += [
# # debug sma # # debug sma
# Instance("OBUF", i_I=cg.clk, o_O=debug_sma.p_tx), Instance("OBUF", i_I=serdes.o, o_O=debug_sma.p_tx),
# Instance("OBUF", i_I=cg.clk_10x, o_O=debug_sma.n_rx), Instance("OBUF", i_I=cg.clk_10x, o_O=debug_sma.n_rx),

View File

@ -26,25 +26,25 @@ pub fn loopback_testing(timer: &mut GlobalTimer, speed: CXP_SPEED) {
timer.delay_us(50_000); timer.delay_us(50_000);
info!( info!(
"tx_phaligndone = {} | rx_phaligndone = {}", "tx_phaligndone = {} | rx_phaligndone = {}",
csr::cxp::downconn_txinit_phaligndone_read(), csr::cxp::downconn_phy_txinit_phaligndone_read(),
csr::cxp::downconn_rxinit_phaligndone_read(), csr::cxp::downconn_phy_rxinit_phaligndone_read(),
); );
// enable txdata tranmission thought MGTXTXP, required by PMA loopback // enable txdata tranmission thought MGTXTXP, required by PMA loopback
csr::cxp::downconn_txenable_write(1); csr::cxp::downconn_phy_txenable_write(1);
info!("waiting for rx to align..."); info!("waiting for rx to align...");
while csr::cxp::downconn_rx_ready_read() != 1 {} while csr::cxp::downconn_phy_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_phy_rxdata_0_read();
// let data1 = csr::cxp::downconn_rxdata_1_read(); // let data1 = csr::cxp::downconn_phy_rxdata_1_read();
// let data2 = csr::cxp::downconn_rxdata_2_read(); // let data2 = csr::cxp::downconn_phy_rxdata_2_read();
// let data3 = csr::cxp::downconn_rxdata_3_read(); // let data3 = csr::cxp::downconn_phy_rxdata_3_read();
// let rxready = csr::cxp::downconn_rx_ready_read(); // let rxready = csr::cxp::downconn_phy_rx_ready_read();
// timer.delay_us(100); // timer.delay_us(100);
// if data0 == 0b0101111100 || data0 == 0b1010000011 { // if data0 == 0b0101111100 || data0 == 0b1010000011 {
// println!( // println!(
@ -72,24 +72,24 @@ pub fn loopback_testing(timer: &mut GlobalTimer, speed: CXP_SPEED) {
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_phy_rxdata_0_read();
// let data1 = csr::cxp::downconn_rxdata_1_read(); // let data1 = csr::cxp::downconn_phy_rxdata_1_read();
// let data2 = csr::cxp::downconn_rxdata_2_read(); // let data2 = csr::cxp::downconn_phy_rxdata_2_read();
// let data3 = csr::cxp::downconn_rxdata_3_read(); // let data3 = csr::cxp::downconn_phy_rxdata_3_read();
// println!( // println!(
// "0b{:010b} {:010b} {:010b} {:010b}", // "0b{:010b} {:010b} {:010b} {:010b}",
// data0, data1, data2, data3 // data0, data1, data2, data3
// ); // );
// NOTE:decode data // NOTE:decode data
// let data0_k = csr::cxp::downconn_decoded_k_0_read(); // let data0_k = csr::cxp::downconn_phy_decoded_k_0_read();
// let data1_k = csr::cxp::downconn_decoded_k_1_read(); // let data1_k = csr::cxp::downconn_phy_decoded_k_1_read();
// let data2_k = csr::cxp::downconn_decoded_k_2_read(); // let data2_k = csr::cxp::downconn_phy_decoded_k_2_read();
// let data3_k = csr::cxp::downconn_decoded_k_3_read(); // let data3_k = csr::cxp::downconn_phy_decoded_k_3_read();
let data0_decoded = csr::cxp::downconn_decoded_data_0_read(); let data0_decoded = csr::cxp::downconn_phy_decoded_data_0_read();
let data1_decoded = csr::cxp::downconn_decoded_data_1_read(); let data1_decoded = csr::cxp::downconn_phy_decoded_data_1_read();
let data2_decoded = csr::cxp::downconn_decoded_data_2_read(); let data2_decoded = csr::cxp::downconn_phy_decoded_data_2_read();
let data3_decoded = csr::cxp::downconn_decoded_data_3_read(); let data3_decoded = csr::cxp::downconn_phy_decoded_data_3_read();
println!( println!(
"{:#04x} {:#04x} {:#04x} {:#04x}", "{:#04x} {:#04x} {:#04x} {:#04x}",
data0_decoded, data1_decoded, data2_decoded, data3_decoded, data0_decoded, data1_decoded, data2_decoded, data3_decoded,
@ -108,24 +108,24 @@ pub fn loopback_testing(timer: &mut GlobalTimer, speed: CXP_SPEED) {
pub fn setup(timer: &mut GlobalTimer) { pub fn setup(timer: &mut GlobalTimer) {
unsafe { unsafe {
info!("turning on pmc loopback mode..."); info!("turning on pmc loopback mode...");
csr::cxp::downconn_loopback_mode_write(0b010); // Near-End PMA Loopback csr::cxp::downconn_phy_loopback_mode_write(0b010); // Near-End PMA Loopback
// QPLL setup // QPLL setup
csr::cxp::downconn_qpll_reset_write(1); csr::cxp::downconn_phy_qpll_reset_write(1);
info!("waiting for QPLL/CPLL to lock..."); info!("waiting for QPLL/CPLL to lock...");
while csr::cxp::downconn_qpll_locked_read() != 1 {} while csr::cxp::downconn_phy_qpll_locked_read() != 1 {}
info!("QPLL locked"); info!("QPLL locked");
// tx/rx setup // tx/rx setup
csr::cxp::downconn_tx_start_init_write(1); csr::cxp::downconn_phy_tx_start_init_write(1);
csr::cxp::downconn_rx_start_init_write(1); csr::cxp::downconn_phy_rx_start_init_write(1);
info!("waiting for tx & rx setup..."); info!("waiting for tx & rx setup...");
timer.delay_us(50_000); timer.delay_us(50_000);
info!( info!(
"tx_phaligndone = {} | rx_phaligndone = {}", "tx_phaligndone = {} | rx_phaligndone = {}",
csr::cxp::downconn_txinit_phaligndone_read(), csr::cxp::downconn_phy_txinit_phaligndone_read(),
csr::cxp::downconn_rxinit_phaligndone_read(), csr::cxp::downconn_phy_rxinit_phaligndone_read(),
); );
} }
@ -153,15 +153,15 @@ pub mod cxp_gtx {
change_cdr_cfg(speed); change_cdr_cfg(speed);
unsafe { unsafe {
csr::cxp::downconn_qpll_reset_write(1); csr::cxp::downconn_phy_qpll_reset_write(1);
info!("waiting for QPLL/CPLL to lock..."); info!("waiting for QPLL/CPLL to lock...");
while csr::cxp::downconn_qpll_locked_read() != 1 {} while csr::cxp::downconn_phy_qpll_locked_read() != 1 {}
info!("QPLL locked"); info!("QPLL locked");
} }
unsafe { unsafe {
csr::cxp::downconn_tx_restart_write(1); csr::cxp::downconn_phy_tx_restart_write(1);
csr::cxp::downconn_rx_restart_write(1); csr::cxp::downconn_phy_rx_restart_write(1);
} }
} }
@ -245,19 +245,19 @@ pub mod cxp_gtx {
fn gtx_read(address: u16) -> u16 { fn gtx_read(address: u16) -> u16 {
// DEBUG: // DEBUG:
unsafe { unsafe {
csr::cxp::downconn_gtx_daddr_write(address); csr::cxp::downconn_phy_gtx_daddr_write(address);
csr::cxp::downconn_gtx_dread_write(1); csr::cxp::downconn_phy_gtx_dread_write(1);
while csr::cxp::downconn_gtx_dready_read() != 1 {} while csr::cxp::downconn_phy_gtx_dready_read() != 1 {}
csr::cxp::downconn_gtx_dout_read() csr::cxp::downconn_phy_gtx_dout_read()
} }
} }
fn gtx_write(address: u16, value: u16) { fn gtx_write(address: u16, value: u16) {
unsafe { unsafe {
csr::cxp::downconn_gtx_daddr_write(address); csr::cxp::downconn_phy_gtx_daddr_write(address);
csr::cxp::downconn_gtx_din_write(value); csr::cxp::downconn_phy_gtx_din_write(value);
csr::cxp::downconn_gtx_din_stb_write(1); csr::cxp::downconn_phy_gtx_din_stb_write(1);
while csr::cxp::downconn_gtx_dready_read() != 1 {} while csr::cxp::downconn_phy_gtx_dready_read() != 1 {}
} }
} }
@ -265,19 +265,19 @@ pub mod cxp_gtx {
fn qpll_read(address: u8) -> u16 { fn qpll_read(address: u8) -> u16 {
// DEBUG: // DEBUG:
unsafe { unsafe {
csr::cxp::downconn_qpll_daddr_write(address); csr::cxp::downconn_phy_qpll_daddr_write(address);
csr::cxp::downconn_qpll_dread_write(1); csr::cxp::downconn_phy_qpll_dread_write(1);
while csr::cxp::downconn_qpll_dready_read() != 1 {} while csr::cxp::downconn_phy_qpll_dready_read() != 1 {}
csr::cxp::downconn_qpll_dout_read() csr::cxp::downconn_phy_qpll_dout_read()
} }
} }
fn qpll_write(address: u8, value: u16) { fn qpll_write(address: u8, value: u16) {
unsafe { unsafe {
csr::cxp::downconn_qpll_daddr_write(address); csr::cxp::downconn_phy_qpll_daddr_write(address);
csr::cxp::downconn_qpll_din_write(value); csr::cxp::downconn_phy_qpll_din_write(value);
csr::cxp::downconn_qpll_din_stb_write(1); csr::cxp::downconn_phy_qpll_din_stb_write(1);
while csr::cxp::downconn_qpll_dready_read() != 1 {} while csr::cxp::downconn_phy_qpll_dready_read() != 1 {}
} }
} }
} }
@ -301,43 +301,43 @@ pub mod txusrclk {
fn one_clock_cycle() { fn one_clock_cycle() {
unsafe { unsafe {
csr::cxp::downconn_pll_dclk_write(1); csr::cxp::downconn_phy_pll_dclk_write(1);
csr::cxp::downconn_pll_dclk_write(0); csr::cxp::downconn_phy_pll_dclk_write(0);
} }
} }
fn set_addr(address: u8) { fn set_addr(address: u8) {
unsafe { unsafe {
csr::cxp::downconn_pll_daddr_write(address); csr::cxp::downconn_phy_pll_daddr_write(address);
} }
} }
fn set_data(value: u16) { fn set_data(value: u16) {
unsafe { unsafe {
csr::cxp::downconn_pll_din_write(value); csr::cxp::downconn_phy_pll_din_write(value);
} }
} }
fn set_enable(en: bool) { fn set_enable(en: bool) {
unsafe { unsafe {
let val = if en { 1 } else { 0 }; let val = if en { 1 } else { 0 };
csr::cxp::downconn_pll_den_write(val); csr::cxp::downconn_phy_pll_den_write(val);
} }
} }
fn set_write_enable(en: bool) { fn set_write_enable(en: bool) {
unsafe { unsafe {
let val = if en { 1 } else { 0 }; let val = if en { 1 } else { 0 };
csr::cxp::downconn_pll_dwen_write(val); csr::cxp::downconn_phy_pll_dwen_write(val);
} }
} }
fn get_data() -> u16 { fn get_data() -> u16 {
unsafe { csr::cxp::downconn_pll_dout_read() } unsafe { csr::cxp::downconn_phy_pll_dout_read() }
} }
fn drp_ready() -> bool { fn drp_ready() -> bool {
unsafe { csr::cxp::downconn_pll_dready_read() == 1 } unsafe { csr::cxp::downconn_phy_pll_dready_read() == 1 }
} }
#[allow(dead_code)] #[allow(dead_code)]
@ -374,7 +374,7 @@ pub mod txusrclk {
fn reset(rst: bool) { fn reset(rst: bool) {
unsafe { unsafe {
let val = if rst { 1 } else { 0 }; let val = if rst { 1 } else { 0 };
csr::cxp::downconn_txpll_reset_write(val) csr::cxp::downconn_phy_txpll_reset_write(val)
} }
} }
@ -411,7 +411,7 @@ pub mod txusrclk {
// wait for the pll to lock // wait for the pll to lock
timer.delay_us(100); timer.delay_us(100);
let locked = unsafe { csr::cxp::downconn_txpll_locked_read() == 1 }; let locked = unsafe { csr::cxp::downconn_phy_txpll_locked_read() == 1 };
info!("txusrclk locked = {}", locked); info!("txusrclk locked = {}", locked);
} }
} }
@ -540,3 +540,5 @@ pub mod txusrclk {
} }
} }
} }
// TODO: add recv like in drtioaux

View File

@ -0,0 +1,193 @@
use core_io::{Error as IoError, Write};
use crc::crc32;
use embedded_hal::prelude::_embedded_hal_blocking_delay_DelayUs;
use io::Cursor;
use libboard_zynq::{println, timer::GlobalTimer};
use crate::pl::csr;
const MAX_PACKET: usize = 128;
const DATA_MAXSIZE: usize = /*max size*/MAX_PACKET - /*Tag*/4 - /*Op code & length*/4 - /*addr*/4 - /*CRC*/4 ;
#[derive(Debug)]
pub enum Error {
BufferError,
LinkDown,
}
impl From<IoError> for Error {
fn from(_: IoError) -> Error {
Error::BufferError
}
}
pub enum Packet {
CtrlRead {
addr: u32,
length: u8,
},
CtrlWrite {
addr: u32,
length: u8,
data: [u8; DATA_MAXSIZE],
}, // max register size is 8 bytes
CtrlReadWithTag {
tag: u8,
addr: u32,
length: u8,
},
CtrlWriteWithTag {
tag: u8,
addr: u32,
length: u8,
data: [u8; DATA_MAXSIZE],
}, // max register size is 8 bytes
EventAck {
packet_tag: u8,
},
TestPacket,
}
impl Packet {
pub fn write_to(&self, writer: &mut Cursor<&mut [u8]>) -> Result<(), Error> {
// CoaXpress use big endian
match *self {
Packet::CtrlRead { addr, length } => {
writer.write(&[0x02; 4])?;
writer.write(&[0x00, 0x00, 0x00, length])?;
writer.write(&addr.to_be_bytes())?;
}
Packet::CtrlWrite { addr, length, data } => {
writer.write(&[0x02; 4])?;
writer.write(&[0x01, 0x00, 0x00, length])?;
writer.write(&addr.to_be_bytes())?;
writer.write(&data[0..length as usize])?;
}
Packet::CtrlReadWithTag { tag, addr, length } => {
writer.write(&[0x05; 4])?;
writer.write(&[tag; 4])?;
writer.write(&[0x00, 0x00, 0x00, length])?;
writer.write(&addr.to_be_bytes())?;
}
Packet::CtrlWriteWithTag {
tag,
addr,
length,
data,
} => {
writer.write(&[0x05; 4])?;
writer.write(&[tag; 4])?;
writer.write(&[0x01, 0x00, 0x00, length])?;
writer.write(&addr.to_be_bytes())?;
writer.write(&data[0..length as usize])?;
}
Packet::EventAck { packet_tag } => {
writer.write(&[0x08; 4])?;
writer.write(&[packet_tag; 4])?;
}
_ => {}
}
// Section 9.2.2.2 (CXP-001-2021)
// Only Control packet need CRC32 appended in the end of the packet
// CoaXpress use the polynomial of IEEE-802.3 (Ethernet) CRC but the checksum calculation is different
// Also, the calculation does not include the first 4 bytes of packet_type
match *self {
Packet::CtrlRead { .. }
| Packet::CtrlWrite { .. }
| Packet::CtrlReadWithTag { .. }
| Packet::CtrlWriteWithTag { .. } => {
let checksum = crc32::checksum_ieee(&writer.get_ref()[4..writer.position()]);
writer.write(&(!checksum).to_le_bytes())?;
}
_ => {}
}
Ok(())
}
}
pub fn send(packet: &Packet) -> Result<(), Error> {
if unsafe { csr::cxp::upconn_tx_enable_read() } == 0 {
Err(Error::LinkDown)?
}
match *packet {
Packet::TestPacket => send_test_packet(),
_ => send_data_packet(packet),
}
}
fn send_data_packet(packet: &Packet) -> Result<(), Error> {
let mut buffer: [u8; MAX_PACKET] = [0; MAX_PACKET];
let mut writer = Cursor::new(&mut buffer[..]);
packet.write_to(&mut writer)?;
unsafe {
let len = writer.position();
csr::cxp::upconn_command_len_write(len as u8);
for data in writer.get_ref()[..len].iter() {
while csr::cxp::upconn_command_writeable_read() == 0 {}
csr::cxp::upconn_command_data_write(*data);
}
}
Ok(())
}
fn send_test_packet() -> Result<(), Error> {
unsafe {
while csr::cxp::upconn_tx_busy_read() == 1 {}
csr::cxp::upconn_tx_testmode_en_write(1);
// timer.delay_us(2);
csr::cxp::upconn_testseq_stb_write(1);
while csr::cxp::upconn_testseq_busy_read() == 1 {}
csr::cxp::upconn_tx_testmode_en_write(0);
}
Ok(())
}
pub fn write_u32(addr: u32, data: u32) -> Result<(), Error> {
let mut data_slice: [u8; DATA_MAXSIZE] = [0; DATA_MAXSIZE];
data_slice[..4].clone_from_slice(&data.to_be_bytes());
send(&Packet::CtrlWrite {
addr,
length: 4,
data: data_slice,
})?;
Ok(())
}
pub fn read_u32(addr: u32) -> Result<(), Error> {
send(&Packet::CtrlRead { addr, length: 4 })?;
Ok(())
}
pub fn write_u64(addr: u32, data: u64) -> Result<(), Error> {
let mut data_slice: [u8; DATA_MAXSIZE] = [0; DATA_MAXSIZE];
data_slice[..8].clone_from_slice(&data.to_be_bytes());
send(&Packet::CtrlWrite {
addr,
length: 8,
data: data_slice,
})?;
Ok(())
}
// DEBUG: use only
pub fn print_packet(pak: &[u8]) {
println!("pak = [");
for i in 0..(pak.len() / 4) {
println!(
"{:#03} {:#04X} {:#04X} {:#04X} {:#04X},",
i + 1,
pak[i * 4],
pak[i * 4 + 1],
pak[i * 4 + 2],
pak[i * 4 + 3]
)
}
println!("]");
println!("============================================");
}

View File

@ -4,22 +4,11 @@ use embedded_hal::prelude::_embedded_hal_blocking_delay_DelayUs;
use io::Cursor; use io::Cursor;
use libboard_zynq::{println, timer::GlobalTimer}; use libboard_zynq::{println, timer::GlobalTimer};
pub use crate::cxp_proto;
use crate::pl::csr; use crate::pl::csr;
#[derive(Debug)]
pub enum Error {
BufferError,
LinkDown,
}
impl From<IoError> for Error {
fn from(_: IoError) -> Error {
Error::BufferError
}
}
pub fn tx_test(timer: &mut GlobalTimer) { pub fn tx_test(timer: &mut GlobalTimer) {
const LEN: usize = 4 * 20; const LEN: usize = 4 * 50;
let mut pak_arr: [u8; LEN] = [0; LEN]; let mut pak_arr: [u8; LEN] = [0; LEN];
unsafe { unsafe {
@ -27,154 +16,36 @@ pub fn tx_test(timer: &mut GlobalTimer) {
// csr::cxp::upconn_bitrate2x_enable_write(1); // csr::cxp::upconn_bitrate2x_enable_write(1);
csr::cxp::upconn_clk_reset_write(0); csr::cxp::upconn_clk_reset_write(0);
send(&Packet::ControlU32Reg(Command::Read { addr: 0x00 })).expect("Cannot send CoaXpress packet"); // read_u32(0x00).expect("Cannot Write CoaXpress Register");
csr::cxp::upconn_tx_enable_write(1); csr::cxp::upconn_tx_enable_write(1);
timer.delay_us(2); // send one word
cxp_proto::send(&cxp_proto::Packet::EventAck { packet_tag: 0x04 }).expect("Cannot send CoaXpress packet");
// cxp_proto::send(&cxp_proto::Packet::TestPacket).expect("Cannot send CoaXpress packet");
timer.delay_us(2); // timer.delay_us(2);
// DEBUG: Trigger packet // DEBUG: Trigger packet
let linktrig_mode: u8 = 0x01; // let linktrig_mode: u8 = 0x01;
csr::cxp::upconn_trig_delay_write(0x05); // csr::cxp::upconn_trig_delay_write(0x05);
csr::cxp::upconn_linktrigger_write(linktrig_mode); // csr::cxp::upconn_linktrigger_write(linktrig_mode);
csr::cxp::upconn_trig_stb_write(1); // send trig // csr::cxp::upconn_trig_stb_write(1); // send trig
// DEBUG: Trigger ACK packet // DEBUG: Trigger ACK packet
// csr::cxp::upconn_ack_write(1); // csr::cxp::upconn_ack_write(1);
timer.delay_us(20); timer.delay_us(20);
csr::cxp::upconn_tx_enable_write(0); csr::cxp::upconn_tx_enable_write(0);
// Collect data // Collect data
let mut i: usize = 0; let mut i: usize = 0;
while csr::cxp::upconn_upconn_phy_debug_buf_dout_valid_read() == 1 { while csr::cxp::upconn_phy_debug_buf_dout_valid_read() == 1 {
pak_arr[i] = csr::cxp::upconn_upconn_phy_debug_buf_dout_pak_read(); pak_arr[i] = csr::cxp::upconn_phy_debug_buf_dout_pak_read();
// println!("received {:#04X}", pak_arr[i]); // println!("received {:#04X}", pak_arr[i]);
csr::cxp::upconn_upconn_phy_debug_buf_inc_write(1); csr::cxp::upconn_phy_debug_buf_inc_write(1);
i += 1; i += 1;
if i == LEN { if i == LEN {
break; break;
} }
} }
print_packet(&pak_arr); cxp_proto::print_packet(&pak_arr);
} }
} }
pub enum Command<T> {
Read { addr: u32 },
Write { addr: u32, data: T },
ReadWithTag { addr: u32, tag: u8 },
WriteWithTag { addr: u32, data: T, tag: u8 },
}
pub enum Packet {
ControlU32Reg(Command<u32>),
ControlU64Reg(Command<u64>),
}
impl Packet {
pub fn write_to<W>(&self, writer: &mut W) -> Result<(), Error>
where W: Write {
match self {
Packet::ControlU32Reg(cmd) => match cmd {
Command::Read { addr } => {
writer.write(&[0x02; 4])?;
writer.write(&[0x00, 0x00, 0x00, 0x04])?;
writer.write(&addr.to_be_bytes())?;
}
Command::Write { addr, data } => {
writer.write(&[0x02; 4])?;
writer.write(&[0x01, 0x00, 0x00, 0x04])?;
writer.write(&addr.to_be_bytes())?;
writer.write(&data.to_be_bytes())?;
}
Command::ReadWithTag { addr, tag } => {
writer.write(&[0x05; 4])?;
writer.write(&[*tag; 4])?;
writer.write(&[0x00, 0x00, 0x00, 0x04])?;
writer.write(&addr.to_be_bytes())?;
}
Command::WriteWithTag { addr, data, tag } => {
writer.write(&[0x05; 4])?;
writer.write(&[*tag; 4])?;
writer.write(&[0x01, 0x00, 0x00, 0x04])?;
writer.write(&addr.to_be_bytes())?;
writer.write(&data.to_be_bytes())?;
}
},
Packet::ControlU64Reg(cmd) => match cmd {
Command::Read { addr } => {
writer.write(&[0x02; 4])?;
writer.write(&[0x00, 0x00, 0x00, 0x08])?;
writer.write(&addr.to_be_bytes())?;
}
Command::Write { addr, data } => {
writer.write(&[0x02; 4])?;
writer.write(&[0x01, 0x00, 0x00, 0x08])?;
writer.write(&addr.to_be_bytes())?;
writer.write(&data.to_be_bytes())?;
}
Command::ReadWithTag { addr, tag } => {
writer.write(&[0x05; 4])?;
writer.write(&[*tag; 4])?;
writer.write(&[0x00, 0x00, 0x00, 0x08])?;
writer.write(&addr.to_be_bytes())?;
}
Command::WriteWithTag { addr, data, tag } => {
writer.write(&[0x05; 4])?;
writer.write(&[*tag; 4])?;
writer.write(&[0x01, 0x00, 0x00, 0x08])?;
writer.write(&addr.to_be_bytes())?;
writer.write(&data.to_be_bytes())?;
}
},
}
Ok(())
}
}
pub fn send(packet: &Packet) -> Result<(), Error> {
// DEBUG: remove the comment out section
// if unsafe { csr::cxp::upconn_tx_enable_read() } == 0 {
// Err(Error::LinkDown)?
// }
const LEN: usize = 4 * 20;
let mut buffer: [u8; LEN] = [0; LEN];
let mut writer = Cursor::new(&mut buffer[..]);
packet.write_to(&mut writer)?;
// Section 9.2.2.2 (CXP-001-2021)
// CoaXpress use the polynomial of IEEE-802.3 (Ethernet) CRC but the checksum calculation is different
// Also, the calculation does not include the first 4 bytes of packet_type
let checksum = crc32::checksum_ieee(&writer.get_ref()[4..writer.position()]);
writer.write(&(!checksum).to_le_bytes())?;
unsafe {
let len = writer.position();
csr::cxp::upconn_command_len_write(len as u8);
for data in writer.get_ref()[..len].iter() {
while csr::cxp::upconn_command_writeable_read() == 0 {}
csr::cxp::upconn_command_data_write(*data);
}
}
Ok(())
}
fn print_packet(pak: &[u8]) {
println!("pak = [");
for i in 0..(pak.len() / 4) {
println!(
"{:#03} {:#04X} {:#04X} {:#04X} {:#04X},",
i + 1,
pak[i * 4],
pak[i * 4 + 1],
pak[i * 4 + 2],
pak[i * 4 + 3]
)
}
println!("]");
println!("============================================");
}

View File

@ -47,6 +47,8 @@ pub mod cxp_downconn;
#[cfg(has_cxp)] #[cfg(has_cxp)]
pub mod cxp_upconn; pub mod cxp_upconn;
pub mod cxp_proto;
pub fn identifier_read(buf: &mut [u8]) -> &str { pub fn identifier_read(buf: &mut [u8]) -> &str {
unsafe { unsafe {
pl::csr::identifier::address_write(0); pl::csr::identifier::address_write(0);

View File

@ -150,5 +150,16 @@ pub fn main_core0() {
task::spawn(ksupport::report_async_rtio_errors()); task::spawn(ksupport::report_async_rtio_errors());
use libboard_artiq::{cxp_downconn, cxp_upconn};
cxp_downconn::setup(&mut timer);
// cxp_downconn::loopback_testing(&mut timer, cxp_downconn::CXP_SPEED::CXP_1);
// cxp_downconn::loopback_testing(&mut timer, cxp_downconn::CXP_SPEED::CXP_2);
// cxp_downconn::loopback_testing(&mut timer, cxp_downconn::CXP_SPEED::CXP_3);
// cxp_downconn::loopback_testing(&mut timer, cxp_downconn::CXP_SPEED::CXP_5);
// cxp_downconn::loopback_testing(&mut timer, cxp_downconn::CXP_SPEED::CXP_6);
// cxp_downconn::loopback_testing(&mut timer, cxp_downconn::CXP_SPEED::CXP_10);
// cxp_downconn::loopback_testing(&mut timer, cxp_downconn::CXP_SPEED::CXP_12);
cxp_upconn::tx_test(&mut timer);
comms::main(timer, cfg); comms::main(timer, cfg);
} }