From ad1fe143cc32017589976b0934ffe73e696af9e4 Mon Sep 17 00:00:00 2001 From: Florent Kermarrec Date: Fri, 28 May 2021 11:01:08 +0200 Subject: [PATCH] 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) --- litex/soc/cores/uart.py | 68 +++++++++++++++++++++++++++++------------ 1 file changed, 48 insertions(+), 20 deletions(-) diff --git a/litex/soc/cores/uart.py b/litex/soc/cores/uart.py index 48b333ae4..a54680462 100644 --- a/litex/soc/cores/uart.py +++ b/litex/soc/cores/uart.py @@ -216,57 +216,85 @@ class UART(Module, AutoCSR, UARTInterface): rx_fifo_depth = 16, rx_fifo_rx_we = False, phy_cd = "sys"): - self._rxtx = CSR(8) - self._txfull = CSRStatus() - self._rxempty = CSRStatus() + self._rxtx = CSR(8) # RX/TX Data. + self._txfull = CSRStatus(description="TX FIFO Full.") + self._rxempty = CSRStatus(description="RX FIFO Empty.") self.submodules.ev = EventManager() self.ev.tx = EventSourceProcess(edge="rising") self.ev.rx = EventSourceProcess(edge="rising") self.ev.finalize() - self._txempty = CSRStatus() - self._rxfull = CSRStatus() + self._txempty = CSRStatus(description="TX FIFO Empty.") + self._rxfull = CSRStatus(description="RX FIFO Full.") # # # UARTInterface.__init__(self) # PHY + # --- if phy is not None: - self.comb += [ - phy.source.connect(self.sink), - self.source.connect(phy.sink) - ] + self.comb += phy.source.connect(self.sink) + self.comb += self.source.connect(phy.sink) # 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 += [ + # CSR --> FIFO. tx_fifo.sink.valid.eq(self._rxtx.re), 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._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) ] # 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 += [ + # Sink --> FIFO. 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), 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) ] + 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 ---------------------------------------------------------------------------------------- CMD_WRITE_BURST_INCR = 0x01