Skip to content

Commit

Permalink
cores/uart: rewrite RS232PHYTX/RX (with FSM and comments) and optimiz…
Browse files Browse the repository at this point in the history
…e resource usage (~100LCs).
  • Loading branch information
enjoy-digital committed Feb 17, 2021
1 parent 7c7f540 commit 908e72e
Showing 1 changed file with 99 additions and 82 deletions.
181 changes: 99 additions & 82 deletions litex/soc/cores/uart.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,104 +31,121 @@ def __init__(self):

# RS232 PHY ----------------------------------------------------------------------------------------

class RS232PHYInterface(UARTInterface):
pass
RS232_IDLE = 1
RS232_START = 0
RS232_STOP = 1

class RS232PHYRX(Module):
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)

# # #

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.source = stream.Endpoint([("data", 8)])
self.sink = sink = stream.Endpoint([("data", 8)])

# # #

rx_clken = Signal()
rx_clkphase = Signal(32, reset_less=True)
data = Signal(8, reset_less=True)
count = Signal(4, 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))
)
)
# Clock Phase Accumulator.
clk_phase_accum = RS232ClkPhaseAccum(tuning_word, mode="tx")
self.submodules += clk_phase_accum


# 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")
)
]
self.sync += [
If(rx_busy,
Cat(rx_clkphase, rx_clken).eq(rx_clkphase + tuning_word)
).Else(
Cat(rx_clkphase, rx_clken).eq(2**31)
)
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")
)
)
]
)


class RS232PHYTX(Module):
class RS232PHYRX(Module):
def __init__(self, pads, tuning_word):
self.sink = stream.Endpoint([("data", 8)])
self.source = source = stream.Endpoint([("data", 8)])

# # #

tx_clken = Signal()
tx_clkphase = Signal(32, reset_less=True)

pads.tx.reset = 1

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))
)
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")
)
]
self.sync += [
If(tx_busy,
Cat(tx_clkphase, tx_clken).eq(tx_clkphase + tuning_word)
).Else(
Cat(tx_clkphase, tx_clken).eq(tuning_word)
)
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):
Expand Down

0 comments on commit 908e72e

Please sign in to comment.