From 908e72e65ba42114ac398a0a0710a08d9c6d03d0 Mon Sep 17 00:00:00 2001 From: Florent Kermarrec Date: Wed, 17 Feb 2021 09:06:14 +0100 Subject: [PATCH] cores/uart: rewrite RS232PHYTX/RX (with FSM and comments) and optimize resource usage (~100LCs). --- litex/soc/cores/uart.py | 181 ++++++++++++++++++++++------------------ 1 file changed, 99 insertions(+), 82 deletions(-) diff --git a/litex/soc/cores/uart.py b/litex/soc/cores/uart.py index 5aed29bf7..6e10710a4 100644 --- a/litex/soc/cores/uart.py +++ b/litex/soc/cores/uart.py @@ -31,104 +31,121 @@ class UARTInterface: # RS232 PHY ---------------------------------------------------------------------------------------- -class RS232PHYInterface(UARTInterface): - pass +RS232_IDLE = 1 +RS232_START = 0 +RS232_STOP = 1 -class RS232PHYRX(Module): - def __init__(self, pads, tuning_word): - self.source = stream.Endpoint([("data", 8)]) +class RS232PHYInterface(UARTInterface): pass + + +class RS232ClkPhaseAccum(Module): + def __init__(self, tuning_word, mode="tx"): + assert mode in ["tx", "rx"] + self.enable = Signal() + self.tick = Signal(32) # # # - rx_clken = Signal() - rx_clkphase = Signal(32, reset_less=True) - - rx = Signal() - rx_r = Signal() - rx_reg = Signal(8, reset_less=True) - rx_bitcount = Signal(4, reset_less=True) - rx_busy = Signal() - rx_done = self.source.valid - rx_data = self.source.data - self.specials += MultiReg(pads.rx, rx) - self.sync += [ - rx_done.eq(0), - rx_r.eq(rx), - If(~rx_busy, - If(~rx & rx_r, # look for start bit - rx_busy.eq(1), - rx_bitcount.eq(0), - ) - ).Else( - If(rx_clken, - rx_bitcount.eq(rx_bitcount + 1), - If(rx_bitcount == 0, - If(rx, # verify start bit - rx_busy.eq(0) - ) - ).Elif(rx_bitcount == 9, - rx_busy.eq(0), - If(rx, # verify stop bit - rx_data.eq(rx_reg), - rx_done.eq(1) - ) - ).Else( - rx_reg.eq(Cat(rx_reg[1:], rx)) - ) - ) - ) - ] - self.sync += [ - If(rx_busy, - Cat(rx_clkphase, rx_clken).eq(rx_clkphase + tuning_word) - ).Else( - Cat(rx_clkphase, rx_clken).eq(2**31) - ) - ] + phase = Signal(32) + self.sync += Cat(phase, self.tick).eq(tuning_word if mode == "tx" else 2**31) + self.sync += If(self.enable, Cat(phase, self.tick).eq(phase + tuning_word)) class RS232PHYTX(Module): def __init__(self, pads, tuning_word): - self.sink = stream.Endpoint([("data", 8)]) + self.sink = sink = stream.Endpoint([("data", 8)]) # # # - tx_clken = Signal() - tx_clkphase = Signal(32, reset_less=True) + data = Signal(8, reset_less=True) + count = Signal(4, reset_less=True) - pads.tx.reset = 1 + # Clock Phase Accumulator. + clk_phase_accum = RS232ClkPhaseAccum(tuning_word, mode="tx") + self.submodules += clk_phase_accum - tx_reg = Signal(8, reset_less=True) - tx_bitcount = Signal(4, reset_less=True) - tx_busy = Signal() - self.sync += [ - self.sink.ready.eq(0), - If(self.sink.valid & ~tx_busy & ~self.sink.ready, - tx_reg.eq(self.sink.data), - tx_bitcount.eq(0), - tx_busy.eq(1), - pads.tx.eq(0) - ).Elif(tx_clken & tx_busy, - tx_bitcount.eq(tx_bitcount + 1), - If(tx_bitcount == 8, - pads.tx.eq(1) - ).Elif(tx_bitcount == 9, - pads.tx.eq(1), - tx_busy.eq(0), - self.sink.ready.eq(1), - ).Else( - pads.tx.eq(tx_reg[0]), - tx_reg.eq(Cat(tx_reg[1:], 0)) + + # FSM + self.submodules.fsm = fsm = FSM(reset_state="IDLE") + fsm.act("IDLE", + # Reset Count and set TX to Idle. + NextValue(count, 0), + NextValue(pads.tx, RS232_IDLE), + # Wait for TX data to transmit. + If(sink.valid, + NextValue(pads.tx, RS232_START), + NextValue(data, sink.data), + NextState("RUN") + ) + ) + fsm.act("RUN", + # Enable Clock Phase Accumulator. + clk_phase_accum.enable.eq(1), + # On Clock Phase Accumulator tick: + If(clk_phase_accum.tick, + # Set TX data. + NextValue(pads.tx, data), + # Increment Count. + NextValue(count, count + 1), + # Shift TX data. + NextValue(data, Cat(data[1:], RS232_STOP)), + # When 10-bit have been transmitted... + If(count == (10 - 1), + # Ack sink and return to Idle. + sink.ready.eq(1), + NextState("IDLE") ) ) - ] - self.sync += [ - If(tx_busy, - Cat(tx_clkphase, tx_clken).eq(tx_clkphase + tuning_word) - ).Else( - Cat(tx_clkphase, tx_clken).eq(tuning_word) + ) + + +class RS232PHYRX(Module): + def __init__(self, pads, tuning_word): + self.source = source = stream.Endpoint([("data", 8)]) + + # # # + + data = Signal(8, reset_less=True) + count = Signal(4, reset_less=True) + + # Clock Phase Accumulator. + clk_phase_accum = RS232ClkPhaseAccum(tuning_word, mode="rx") + self.submodules += clk_phase_accum + + # Resynchronize pads.rx and generate delayed version. + rx = Signal() + rx_d = Signal() + self.specials += MultiReg(pads.rx, rx) + self.sync += rx_d.eq(rx) + + # FSM + self.submodules.fsm = fsm = FSM(reset_state="IDLE") + fsm.act("IDLE", + # Reset Count. + NextValue(count, 0), + # Wait for RX Start bit. + If((rx == RS232_START) & (rx_d == RS232_IDLE), + NextState("RUN") ) - ] + ) + fsm.act("RUN", + # Enable Clock Phase Accumulator. + clk_phase_accum.enable.eq(1), + # On Clock Phase Accumulator tick: + If(clk_phase_accum.tick, + # Increment Count. + NextValue(count, count + 1), + # Shift RX data. + NextValue(data, Cat(data[1:], rx)), + # When 10-bit have been received... + If(count == (10 - 1), + # Produce data (but only when RX Stop bit is seen). + source.valid.eq(rx == RS232_STOP), + source.data.eq(data), + NextState("IDLE") + ) + ) + ) class RS232PHY(Module, AutoCSR):