forked from M-Labs/artiq-zynq
cxp GW: use memory buffer tx command
cxp GW: add 32bit test packet cxp GW: add 32bit trig ack to pipeline cxp GW: restore rx loopback test cxp GW: add idle word inserter cxp GW: add phy to pipeline cxp GW: add trig to pipeline cxp GW: add tx docs
This commit is contained in:
parent
7cafe6a293
commit
7eeceb1dfa
|
@ -5,7 +5,6 @@ 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 *
|
||||||
|
|
||||||
buffer_depth = 128
|
|
||||||
|
|
||||||
@FullMemoryWE()
|
@FullMemoryWE()
|
||||||
class CXP(Module, AutoCSR):
|
class CXP(Module, AutoCSR):
|
||||||
|
@ -15,74 +14,17 @@ class CXP(Module, AutoCSR):
|
||||||
self.submodules.downconn = DownConn_Interface(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
|
||||||
|
|
||||||
self.submodules.transmitter = Transmitter()
|
|
||||||
|
|
||||||
# TODO: add link layer
|
# TODO: add link layer
|
||||||
|
|
||||||
def get_tx_port(self):
|
def get_tx_port(self):
|
||||||
return self.transmitter.mem.get_port(write_capable=True)
|
return self.upconn.command.mem.get_port(write_capable=True)
|
||||||
|
|
||||||
|
def get_loopback_tx_port(self):
|
||||||
|
return self.downconn.command.mem.get_port(write_capable=True)
|
||||||
|
|
||||||
def get_mem_size(self):
|
def get_mem_size(self):
|
||||||
return buffer_depth*downconn_dw
|
return buffer_depth*downconn_dw
|
||||||
|
|
||||||
@FullMemoryWE()
|
|
||||||
class Transmitter(Module, AutoCSR):
|
|
||||||
def __init__(self):
|
|
||||||
self.cxp_tx_word_len = CSRStorage(bits_for(buffer_depth))
|
|
||||||
self.cxp_tx = CSR()
|
|
||||||
|
|
||||||
# # #
|
|
||||||
|
|
||||||
self.specials.mem = mem = Memory(downconn_dw, buffer_depth)
|
|
||||||
self.specials.mem_port = mem_port = mem.get_port()
|
|
||||||
self.source = stream.Endpoint(downconn_layout)
|
|
||||||
|
|
||||||
|
|
||||||
tx_done = Signal()
|
|
||||||
addr_next = Signal(bits_for(buffer_depth))
|
|
||||||
addr = Signal.like(addr_next)
|
|
||||||
addr_rst = Signal()
|
|
||||||
addr_inc = Signal()
|
|
||||||
|
|
||||||
# increment addr in the same cycle the moment addr_inc is rise
|
|
||||||
# since memory takes one cycle to shift to the correct addr
|
|
||||||
self.sync += [
|
|
||||||
addr.eq(addr_next),
|
|
||||||
If(self.cxp_tx.re, self.cxp_tx.w.eq(1)),
|
|
||||||
If(tx_done, self.cxp_tx.w.eq(0)),
|
|
||||||
]
|
|
||||||
|
|
||||||
self.comb += [
|
|
||||||
addr_next.eq(addr),
|
|
||||||
If(addr_rst,
|
|
||||||
addr_next.eq(addr_next.reset),
|
|
||||||
).Elif(addr_inc,
|
|
||||||
addr_next.eq(addr + 1),
|
|
||||||
),
|
|
||||||
mem_port.adr.eq(addr_next),
|
|
||||||
self.source.data.eq(mem_port.dat_r)
|
|
||||||
]
|
|
||||||
|
|
||||||
self.submodules.fsm = fsm = FSM(reset_state="IDLE")
|
|
||||||
|
|
||||||
fsm.act("IDLE",
|
|
||||||
addr_rst.eq(1),
|
|
||||||
If(self.cxp_tx.re, NextState("TRANSMIT"))
|
|
||||||
)
|
|
||||||
fsm.act("TRANSMIT",
|
|
||||||
self.source.stb.eq(1),
|
|
||||||
If(self.source.ack,
|
|
||||||
addr_inc.eq(1),
|
|
||||||
),
|
|
||||||
If(addr_next == self.cxp_tx_word_len.storage,
|
|
||||||
tx_done.eq(1),
|
|
||||||
NextState("IDLE")
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
self.submodules.debug_out = debug_out = RX_Debug_Buffer()
|
|
||||||
self.comb += self.source.connect(debug_out.sink)
|
|
||||||
|
|
||||||
class DownConn_Interface(Module, AutoCSR):
|
class DownConn_Interface(Module, AutoCSR):
|
||||||
def __init__(self, refclk, downconn_pads, sys_clk_freq, debug_sma, pmod_pads):
|
def __init__(self, refclk, downconn_pads, sys_clk_freq, debug_sma, pmod_pads):
|
||||||
# # #
|
# # #
|
||||||
|
@ -91,24 +33,23 @@ class DownConn_Interface(Module, AutoCSR):
|
||||||
self.gtxs = phy.gtxs
|
self.gtxs = phy.gtxs
|
||||||
|
|
||||||
# DEBUG: TX pipeline
|
# DEBUG: TX pipeline
|
||||||
self.submodules.debug_src = debug_src = TX_Command_Packet()
|
self.submodules.command = command = TX_Command_Packet()
|
||||||
self.submodules.trig_ack = trig_ack = Trigger_ACK()
|
|
||||||
self.submodules.testseq = testseq = TX_Test_Packet()
|
self.submodules.testseq = testseq = TX_Test_Packet()
|
||||||
self.submodules.mux = mux = stream.Multiplexer(upconn_layout, 3)
|
self.submodules.mux = mux = stream.Multiplexer(word_layout, 2)
|
||||||
self.submodules.conv = conv = stream.StrideConverter(upconn_layout, downconn_layout)
|
self.submodules.pak_wrp = pak_wrp = Packet_Wrapper()
|
||||||
|
self.submodules.trig_ack = trig_ack = Trigger_ACK_Inserter()
|
||||||
|
|
||||||
self.ack = CSR()
|
self.ack = CSR()
|
||||||
self.mux_sel = CSRStorage(4)
|
self.mux_sel = CSRStorage()
|
||||||
self.sync += trig_ack.ack.eq(self.ack.re),
|
|
||||||
|
|
||||||
|
self.sync += trig_ack.stb.eq(self.ack.re),
|
||||||
self.comb += [
|
self.comb += [
|
||||||
debug_src.source.connect(mux.sink0),
|
command.source.connect(mux.sink0),
|
||||||
trig_ack.source.connect(mux.sink1),
|
testseq.source.connect(mux.sink1),
|
||||||
testseq.source.connect(mux.sink2),
|
mux.sel.eq(self.mux_sel.storage),
|
||||||
mux.sel.eq(self.mux_sel.storage)
|
|
||||||
]
|
]
|
||||||
|
|
||||||
tx_pipeline = [mux , conv, phy.sinks[0]]
|
tx_pipeline = [mux , pak_wrp, trig_ack, phy.sinks[0]]
|
||||||
for s, d in zip(tx_pipeline, tx_pipeline[1:]):
|
for s, d in zip(tx_pipeline, tx_pipeline[1:]):
|
||||||
self.comb += s.source.connect(d.sink)
|
self.comb += s.source.connect(d.sink)
|
||||||
|
|
||||||
|
@ -162,6 +103,8 @@ class UpConn_Interface(Module, AutoCSR):
|
||||||
self.clk_reset = CSRStorage(reset=1)
|
self.clk_reset = CSRStorage(reset=1)
|
||||||
self.bitrate2x_enable = CSRStorage()
|
self.bitrate2x_enable = CSRStorage()
|
||||||
self.tx_enable = CSRStorage()
|
self.tx_enable = CSRStorage()
|
||||||
|
|
||||||
|
# TODO: add busy condition
|
||||||
self.tx_busy = CSRStatus()
|
self.tx_busy = CSRStatus()
|
||||||
|
|
||||||
self.tx_testmode_en = CSRStorage()
|
self.tx_testmode_en = CSRStorage()
|
||||||
|
@ -174,57 +117,87 @@ class UpConn_Interface(Module, AutoCSR):
|
||||||
phy.bitrate2x_enable.eq(self.bitrate2x_enable.storage),
|
phy.bitrate2x_enable.eq(self.bitrate2x_enable.storage),
|
||||||
phy.tx_enable.eq(self.tx_enable.storage),
|
phy.tx_enable.eq(self.tx_enable.storage),
|
||||||
phy.clk_reset.eq(self.clk_reset.re),
|
phy.clk_reset.eq(self.clk_reset.re),
|
||||||
self.tx_busy.status.eq(phy.tx_busy),
|
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
# TODO: rewrite the transmite path into pipeline
|
# Transmission Pipeline
|
||||||
#
|
#
|
||||||
# test pak ----+
|
# test pak ----+
|
||||||
# from gw | 32 32 8
|
# from gw | 32 32 8
|
||||||
# |---/---> mux -----> trig ack -----> idle word ---/--> conv ---/---> trig -----> PHY
|
# |---/---> mux -----> packet -----> idle word -----> trigger ack ---/--> conv ---/---> trigger -----> PHY
|
||||||
# | inserter inserter inserter
|
# | wrapper inserter inserter inserter
|
||||||
# data pak ----+
|
# data pak ----+
|
||||||
# from fw
|
# from fw
|
||||||
|
#
|
||||||
|
# Equivalent transmission priority:
|
||||||
|
# trigger > trigger ack > idle > test/data packet
|
||||||
|
# To maintain the trigger performance, idle word should not be inserted into trigger or trigger ack.
|
||||||
|
#
|
||||||
|
# In low speed serial, the higher priority packet can be inserted in two types of boundary
|
||||||
|
# Insertion @ char boundary: Trigger packets
|
||||||
|
# Insertion @ word boundary: Trigger ack & IDLE packets
|
||||||
|
# The 32 bit part of the pipeline handles the word boundary insertion while the 8 bit part handles the char boundary insertion
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# Packet FIFOs with transmission priority
|
# Packet FIFOs with transmission priority
|
||||||
# 0: Trigger packet
|
# 0: Trigger packet
|
||||||
self.submodules.trig = trig = TX_Trigger()
|
self.submodules.trig = trig = TX_Trigger()
|
||||||
self.comb += trig.source.connect(phy.sinks[0])
|
|
||||||
|
|
||||||
# DEBUG: INPUT
|
# # DEBUG: INPUT
|
||||||
self.trig_stb = CSR()
|
self.trig_stb = CSR()
|
||||||
self.trig_delay = CSRStorage(8)
|
self.trig_delay = CSRStorage(8)
|
||||||
self.linktrigger = CSRStorage(2)
|
self.linktrigger = CSRStorage(2)
|
||||||
|
|
||||||
self.sync += [
|
self.sync += [
|
||||||
trig.trig_stb.eq(self.trig_stb.re),
|
trig.stb.eq(self.trig_stb.re),
|
||||||
trig.delay.eq(self.trig_delay.storage),
|
trig.delay.eq(self.trig_delay.storage),
|
||||||
trig.linktrig_mode.eq(self.linktrigger.storage),
|
trig.linktrig_mode.eq(self.linktrigger.storage),
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
# 1: IO acknowledgment for trigger packet
|
# 1: IO acknowledgment for trigger packet
|
||||||
self.submodules.trig_ack = trig_ack = Trigger_ACK()
|
self.submodules.trig_ack = trig_ack = Trigger_ACK_Inserter()
|
||||||
self.comb += trig_ack.source.connect(phy.sinks[1])
|
|
||||||
|
|
||||||
# DEBUG: INPUT
|
# DEBUG: INPUT
|
||||||
self.ack = CSR()
|
self.ack = CSR()
|
||||||
self.sync += trig_ack.ack.eq(self.ack.re),
|
self.sync += trig_ack.stb.eq(self.ack.re),
|
||||||
|
|
||||||
|
|
||||||
# 2: All other packets
|
# 2: All other packets (data & test packet)
|
||||||
# Control is not timing dependent, all the link layer is done in firmware
|
# Control is not timing dependent, all the data packets are handled in firmware
|
||||||
|
|
||||||
self.submodules.command = command = TX_Command_Packet()
|
self.submodules.command = command = TX_Command_Packet()
|
||||||
self.submodules.testseq = testseq = TX_Test_Packet()
|
self.submodules.testseq = testseq = TX_Test_Packet()
|
||||||
|
self.submodules.mux = mux = stream.Multiplexer(word_layout, 2)
|
||||||
|
|
||||||
self.submodules.mux = mux = stream.Multiplexer(upconn_layout, 2)
|
|
||||||
|
|
||||||
self.comb += [
|
self.comb += [
|
||||||
command.source.connect(mux.sink0),
|
command.source.connect(mux.sink0),
|
||||||
testseq.source.connect(mux.sink1),
|
testseq.source.connect(mux.sink1),
|
||||||
mux.sel.eq(self.tx_testmode_en.storage),
|
mux.sel.eq(self.tx_testmode_en.storage),
|
||||||
|
|
||||||
mux.source.connect(phy.sinks[2])
|
|
||||||
]
|
]
|
||||||
|
|
||||||
|
self.submodules.pak_wrp = pak_wrp = Packet_Wrapper()
|
||||||
|
|
||||||
|
# IDLE Word
|
||||||
|
self.submodules.idle = idle = Idle_Word_Inserter()
|
||||||
|
|
||||||
|
# Section 9.2.5.1 (CXP-001-2021)
|
||||||
|
# IDLE should be transmitter every 10000 words
|
||||||
|
cnt = Signal(max=10000)
|
||||||
|
|
||||||
|
self.sync += [
|
||||||
|
idle.stb.eq(0),
|
||||||
|
If((~idle.sink.stb) | (cnt == 9999),
|
||||||
|
idle.stb.eq(1),
|
||||||
|
cnt.eq(cnt.reset),
|
||||||
|
).Else(
|
||||||
|
cnt.eq(cnt + 1),
|
||||||
|
),
|
||||||
|
]
|
||||||
|
|
||||||
|
self.submodules.converter = converter = stream.StrideConverter(word_layout, char_layout)
|
||||||
|
|
||||||
|
tx_pipeline = [mux, pak_wrp, idle, trig_ack, converter, trig, phy]
|
||||||
|
for s, d in zip(tx_pipeline, tx_pipeline[1:]):
|
||||||
|
self.comb += s.source.connect(d.sink)
|
||||||
|
|
Loading…
Reference in New Issue