cores/uart: Cleanup code and add optional automatic TX Flush.
In some SoCs where UART's PHY is managed externally (ex through a Bridge) we don't necessarily want the UART TX to wait for the PHY to be ready (and then stall the CPU) but just want to let the CPU print the UART and will just connect when useful and handle backpressure when connected. This is now possible by calling add_auto_tx_flush method, ex in the SoC: self.uart.add_auto_tx_flush(sys_clk_freq)
This commit is contained in:
parent
f6b2135cc9
commit
ad1fe143cc
|
@ -216,57 +216,85 @@ class UART(Module, AutoCSR, UARTInterface):
|
||||||
rx_fifo_depth = 16,
|
rx_fifo_depth = 16,
|
||||||
rx_fifo_rx_we = False,
|
rx_fifo_rx_we = False,
|
||||||
phy_cd = "sys"):
|
phy_cd = "sys"):
|
||||||
self._rxtx = CSR(8)
|
self._rxtx = CSR(8) # RX/TX Data.
|
||||||
self._txfull = CSRStatus()
|
self._txfull = CSRStatus(description="TX FIFO Full.")
|
||||||
self._rxempty = CSRStatus()
|
self._rxempty = CSRStatus(description="RX FIFO Empty.")
|
||||||
|
|
||||||
self.submodules.ev = EventManager()
|
self.submodules.ev = EventManager()
|
||||||
self.ev.tx = EventSourceProcess(edge="rising")
|
self.ev.tx = EventSourceProcess(edge="rising")
|
||||||
self.ev.rx = EventSourceProcess(edge="rising")
|
self.ev.rx = EventSourceProcess(edge="rising")
|
||||||
self.ev.finalize()
|
self.ev.finalize()
|
||||||
|
|
||||||
self._txempty = CSRStatus()
|
self._txempty = CSRStatus(description="TX FIFO Empty.")
|
||||||
self._rxfull = CSRStatus()
|
self._rxfull = CSRStatus(description="RX FIFO Full.")
|
||||||
|
|
||||||
# # #
|
# # #
|
||||||
|
|
||||||
UARTInterface.__init__(self)
|
UARTInterface.__init__(self)
|
||||||
|
|
||||||
# PHY
|
# PHY
|
||||||
|
# ---
|
||||||
if phy is not None:
|
if phy is not None:
|
||||||
self.comb += [
|
self.comb += phy.source.connect(self.sink)
|
||||||
phy.source.connect(self.sink),
|
self.comb += self.source.connect(phy.sink)
|
||||||
self.source.connect(phy.sink)
|
|
||||||
]
|
|
||||||
|
|
||||||
# TX
|
# TX
|
||||||
tx_fifo = _get_uart_fifo(tx_fifo_depth, source_cd=phy_cd)
|
# --
|
||||||
self.submodules += tx_fifo
|
self.submodules.tx_fifo = tx_fifo = _get_uart_fifo(tx_fifo_depth, source_cd=phy_cd)
|
||||||
|
|
||||||
self.comb += [
|
self.comb += [
|
||||||
|
# CSR --> FIFO.
|
||||||
tx_fifo.sink.valid.eq(self._rxtx.re),
|
tx_fifo.sink.valid.eq(self._rxtx.re),
|
||||||
tx_fifo.sink.data.eq(self._rxtx.r),
|
tx_fifo.sink.data.eq(self._rxtx.r),
|
||||||
|
|
||||||
|
# FIFO --> Source.
|
||||||
|
tx_fifo.source.connect(self.source),
|
||||||
|
|
||||||
|
# CSR Status.
|
||||||
self._txfull.status.eq(~tx_fifo.sink.ready),
|
self._txfull.status.eq(~tx_fifo.sink.ready),
|
||||||
self._txempty.status.eq(~tx_fifo.source.valid),
|
self._txempty.status.eq(~tx_fifo.source.valid),
|
||||||
tx_fifo.source.connect(self.source),
|
|
||||||
# Generate TX IRQ when tx_fifo becomes non-full.
|
# IRQ (When FIFO becomes non-full).
|
||||||
self.ev.tx.trigger.eq(tx_fifo.sink.ready)
|
self.ev.tx.trigger.eq(tx_fifo.sink.ready)
|
||||||
]
|
]
|
||||||
|
|
||||||
# RX
|
# RX
|
||||||
rx_fifo = _get_uart_fifo(rx_fifo_depth, sink_cd=phy_cd)
|
# --
|
||||||
self.submodules += rx_fifo
|
self.submodules.rx_fifo = rx_fifo = _get_uart_fifo(rx_fifo_depth, sink_cd=phy_cd)
|
||||||
|
|
||||||
self.comb += [
|
self.comb += [
|
||||||
|
# Sink --> FIFO.
|
||||||
self.sink.connect(rx_fifo.sink),
|
self.sink.connect(rx_fifo.sink),
|
||||||
self._rxempty.status.eq(~rx_fifo.source.valid),
|
|
||||||
self._rxfull.status.eq(~rx_fifo.sink.ready),
|
# FIFO --> CSR.
|
||||||
self._rxtx.w.eq(rx_fifo.source.data),
|
self._rxtx.w.eq(rx_fifo.source.data),
|
||||||
rx_fifo.source.ready.eq(self.ev.rx.clear | (rx_fifo_rx_we & self._rxtx.we)),
|
rx_fifo.source.ready.eq(self.ev.rx.clear | (rx_fifo_rx_we & self._rxtx.we)),
|
||||||
# Generate RX IRQ when rx_fifo becomes non-empty.
|
|
||||||
|
# Status.
|
||||||
|
self._rxempty.status.eq(~rx_fifo.source.valid),
|
||||||
|
self._rxfull.status.eq(~rx_fifo.sink.ready),
|
||||||
|
|
||||||
|
# IRQ (When FIFO becomes non-empty).
|
||||||
self.ev.rx.trigger.eq(rx_fifo.source.valid)
|
self.ev.rx.trigger.eq(rx_fifo.source.valid)
|
||||||
]
|
]
|
||||||
|
|
||||||
|
def add_auto_tx_flush(self, sys_clk_freq, timeout=1e-2, interval=2):
|
||||||
|
# Add automatic TX flush when ready is not active for a long time (timeout), this can prevent
|
||||||
|
# stalling the UART (and thus CPU) when the PHY is not operational at startup.
|
||||||
|
|
||||||
|
flush_ep = stream.Endpoint([("data", 8)])
|
||||||
|
flush_count = Signal(int(log2(interval)))
|
||||||
|
|
||||||
|
# Insert Flush Endpoint between TX FIFO and Source.
|
||||||
|
self.comb += self.tx_fifo.source.connect(flush_ep)
|
||||||
|
self.comb += flush_ep.connect(self.source)
|
||||||
|
|
||||||
|
# Flush TX FIFO when Source.ready is inactive for timeout (with interval cycles between
|
||||||
|
# each ready).
|
||||||
|
self.submodules.timer = timer = WaitTimer(int(timeout*sys_clk_freq))
|
||||||
|
self.comb += timer.wait.eq(~self.source.ready)
|
||||||
|
self.sync += flush_count.eq(flush_count + 1)
|
||||||
|
self.comb += If(timer.done, flush_ep.ready.eq(flush_count == 0))
|
||||||
|
#self.sync += If(flush_ep.valid & flush_ep.ready, Display("%c", flush_ep.data))
|
||||||
|
|
||||||
# UART Bone ----------------------------------------------------------------------------------------
|
# UART Bone ----------------------------------------------------------------------------------------
|
||||||
|
|
||||||
CMD_WRITE_BURST_INCR = 0x01
|
CMD_WRITE_BURST_INCR = 0x01
|
||||||
|
|
Loading…
Reference in New Issue