LiteX – UART hub

Language & Programming

LiteX – UART hub

Following up on some blinking with the Mimas Spartan 6, we will send some UART data via the FPGA. The code can be found on github.com/dayjaby/litex-mimas. In theory it is possible to have dozens of UART channels on a single FPGA! To be able to talk to the internal bus system, called Wishbone, we will use a second serial UART channel which connects to a WishboneUARTBridge. We use the following PINs for the UART channels:

_io = [
    ("clk100", 0, Pins("P126"), IOStandard("LVCMOS33")),

    # for WishboneUARTBridge
    ("serial", 0,
        Subsignal("tx", Pins("P104"), IOStandard("LVCMOS33"), # GPIO-N21
                  Misc("SLEW=FAST")),
        Subsignal("rx", Pins("P101"), IOStandard("LVCMOS33"),  # GPIO-N20
                  Misc("SLEW=FAST"))),
    ("uart0", 0,
        Subsignal("tx", Pins("P100"), IOStandard("LVCMOS33"), # GPIO-P21
                  Misc("SLEW=FAST")),
        Subsignal("rx", Pins("P102"), IOStandard("LVCMOS33"),  # GPIO-P20
                  Misc("SLEW=FAST"))),
]

LiteX provides a SoCMini class, which contains functions to add a wishbone master…

        serial = platform.request("serial", 0)
        self.submodules.serial_bridge = UARTWishboneBridge(serial, sys_clk_freq)
        self.add_wb_master(self.serial_bridge.wishbone)

… create UART modules …

    self.submodules.uart0 = ResetInserter()(UART(
        phy=UARTPHY(platform.request("uart0", 0), sys_clk_freq, baudrate=9600),
        tx_fifo_depth=16,
        rx_fifo_depth=16,
        phy_cd="sys"))
    self.add_constant("UART_POLLING")

… and make the CSR (Control and Status Register) for submodules available via wishbone:

        self.add_csr("uart0")

From the host computer, we can connect to the WishboneUARTBridge with LiteX’s RemoteClient and do modifications to the exposed CSRs:

from litex import RemoteClient

wb = RemoteClient()
wb.open()

class UART:
    def __init__(self, regs, name):
        self._txfull = getattr(regs, name + "_txfull")
        self._rxempty = getattr(regs, name + "_rxempty")
        self._rxtx = getattr(regs, name + "_rxtx")

    def txfull(self):
        return bool(self._txfull.read())

    def rxempty(self):
        return bool(self._rxempty.read())

    def write(self, c):
        self._rxtx.write(c)

    def read(self):
        return self._rxtx.read()

uart = UART(wb.regs, name="uart0")
for i in range(10):
    if not uart.txfull():
        uart.write(0x76)

wb.close()

Now we build, flash, connect to it and run the test script:

$ python3 uart_hub.py
$ python3 mimasconfig.py /dev/ttyACM0 build/gateware/top.bin
$ litex_server --uart --uart-port=/dev/ttyUSB0
in a new terminal:
$ python3 uart_hub_test.py

The result is what we expected! The interruptions between the single bytes are caused by the WishboneUARTBridge, which could be optimized probably.

Leave a Reply

Your email address will not be published. Required fields are marked *