UART

The UART module allows the PicoNut processor to communicate with the outside world. It is a simple module that can be used to send and receive data between the PicoNut and a user’s computer. The module is based on the SiFive UART (SiFive FE310-G000 Manual), which has been implemented in the PicoNut project.

There are two implementations of the UART module in the PicoNut project. The first is the Wishbone UART, a UART module connected to the Wishbone bus, primarily intended for use on an FPGA. The second implementation is the SimUART, a UART module that can be connected to the CSoftPeripheral interface of the PicoNut processor, designed for use in the simulator.

UART Registers

A list of the registers for the UART module can be found in the documentation (SiFive FE310-G000 Manual) of the SiFive UART module.

Wishbone UART

The following figure provides an overview of the components of the Wishbone UART module. All subsequent diagrams omit the clk and rst signals for clarity.

../_images/wb_uart_overview.drawio.svg

Fig. 15 Overview of the Wishbone UART module.

SC_MODULE(m_wb_uart)

#define

#define *

#define #define * following submodules:

  • baudgen_x16: Baud rate generator for the 16x baud rate #define * - baudgen_rx: Baud rate generator for the receive (rx) baud rate

  • baudgen_tx: Baud rate generator for the transmit (tx) baud rate

  • uart_rx: Receiver module

  • uart_tx: Transmitter module

  • uart_rx_fifo: Receiver FIFO

  • uart_tx_fifo: Transmitter FIFO

Author

Lukas Bauer

It connects the submodules and handles the Wishbone interface. It provides the registers as specified for the SiFive UART. The registers are:

  • txdata: Transmit data register

  • rxdata: Receive data register

  • txctrl: Transmit control register

  • rxctrl: Receive control register

  • ie: Interrupt enable register

  • ip: Interrupt pending register

  • div: Baud rate divisor register

The module provides control functionality for the FIFOs, receiver, and transmitter. It initiates UART transmissions when data is written to the FIFOs, or writes the received data to the rx_fifo. It also controls whether the receiver or transmitter are enabled and at what baud rate they operate. Interrupts are also activated and deactivated by this module. A FIFO state machine handles Wishbone access to both FIFOs to prevent invalid transactions of data to and from the UART module.

There are two configuration options in the config.mk file:

  • CFG_WB_UART_DISABLE_FIFO: This disables the FIFOs of the UART module. In this mode, only the rx and tx data registers are usable, meaning the rxdata_empty and txdata_full flags are set when the registers are in use. This also changes the behavior of the interrupts, where the txctrl_txcnt and rxctrl_rxcnt watermark levels have no effect. The watermark levels are set to 1, and the interrupts are triggered when the txdata and rxdata registers exceed the threshold as described in the SiFive UART specification.

  • CFG_WB_UART_BASE_ADDRESS: This sets the base address of the UART module in the Wishbone bus address space.

Ports:

Parameters:
  • clk[in] clock of the module

  • reset[in] reset of the module

  • stb_i[in] wb strobe

  • cyc_i[in] wb cycle

  • we_i[in] wb write enable

  • sel_i[in] wb byte select

  • ack_o[out] wb acknowledge

  • err_o[out] wb error

  • rty_o[out] wb retry

  • addr_i[in] wb address

  • dat_i[in] wb data in

  • dat_o[out] wb data out

  • rx[in] rx line

  • tx[out] tx line

The Wishbone UART module consists of the following components:

  • Baudgen: Multiple Baudgenerators that divide the system clock to generate the baudrate for the UART module.

  • Majority Filter: The majority filter is used to filter the received data. To ensure that the bit is received correctly.

  • FIFOs: The FIFOs are used to buffer the data that is sent and received by the UART module.

  • uart_rx: The receiver receives data from the outside world.

  • uart_tx: The transmitter sends data to the outside world.

Baudgen

../_images/wb_uart_baudgen.drawio.svg

Fig. 16 Diagramm of the Baudrate Generator.

SC_MODULE(m_baudgen)

clockdivider to generate the baudtick for the UART

This clock divider can be configured by setting a divider value in the 16-bit div register. If the module is enabled, the baud generator counts up until the divider value is reached. Then, the baudtick is set to high, and the counter is reset.

Author

Lukas Bauer

Ports:

Parameters:
  • clk[in] clock of the module

  • reset[in] reset of the module

  • en_i[in] enable for baudgen module

  • clear_i[in] clearing the baudgen (synchronous reset)

  • div_i[in] <16> divider value for baudgen

  • baudtick_o[out] generated baudtick

Majority Filter

../_images/wb_uart_majr_filter.drawio.svg

Fig. 17 Diagramm of the Majority Filter.

SC_MODULE(m_majority_filter)

majority filter to filter a input signal

This module allows checking if a signal remains stable for a certain amount of time. If the module is enabled, the input signal is checked every clock cycle. When the signal is low, the counter is incremented. Once the threshold is reached, the output signal is set to low. Otherwise, the output signal remains high.

Author

Lukas Bauer

Ports:

Parameters:
  • clk[in] clock of the module

  • reset[in] reset of the module

  • filter_i[in] Signal to be filtered

  • capture_i[in] Enable Signal to capture the filter signal

  • clear_i[in] Clear the filter signal

  • filter_o[out] Filtered Signal

FIFOs

../_images/wb_uart_fifo.drawio.svg

Fig. 18 Diagramm of the FIFOs.

SC_MODULE(m_uart_fifo)

first in first out buffer for UART

This module is a simple first-in, first-out (FIFO) buffer for the UART RX and TX. If the read and write pointers are equal, the buffer is empty. If the write pointer and the addresses are as far apart as possible, the buffer is full. The usage signal is a counter for the number of elements in the buffer. The clear signal resets the read and write pointers, as well as the usage counter. If write is set and the buffer is not full, the data is written to the buffer. If read is set and the buffer is not empty, the data is read from the buffer. The width of the data can be set using the UART_FIFO_WIDTH parameter. The size of the buffer can be set using the UART_FIFO_SIZE_2E parameter, which is the log2 of the size of the buffer. The buffer is implemented as a circular buffer.

Author

Lukas Bauer

Ports:

Parameters:
  • clk[in] clock of the module

  • reset[in] reset of the module

  • clear_i[in] clear the content of the FIFO

  • write_i[in] write enable for the FIFO

  • read_i[in] read enable for the FIFO

  • data_i[in] <8> data to be written to the FIFO

  • data_o[out] <8> data to be read from the FIFO

  • full_o[out] FIFO is full

  • empty_o[out] FIFO is empty

  • usage_o[out] <4> usage of the FIFO

uart_rx

../_images/wb_uart_rx.drawio.svg

Fig. 19 Diagramm of the Receiver.

SC_MODULE(m_uart_rx)

Implementation of the UART receiver.

The UART receiver is a state machine that waits for the start bit of a UART frame. It contains the following submodule:

  • Majority Filter Once the receiver detects the start bit, it starts the baudtick generation and waits for the next baudtick to sample the RX line. The received bits are stored in a shift register. They are then filtered by a majority filter to reduce noise. When the stop bit is received, the data is stored in the data register, and the rx_finished signal is set. The number of stop bits can be configured to 1 or 2.

Author

Lukas Bauer

Ports:

Parameters:
  • clk[in] clock of the module

  • reset[in] reset of the module

  • baudtick_i[in] baudtick input

  • baudtick_x16_i[in] baudtick x16 input

  • stopbit_8_9_i[in] unset for 1 stop bit, set for 2 stop bits

  • rx_i[in] UART rx line

  • data_o[out] <16> Data Output

  • rx_finished_o[out] RX finished output

  • baudtick_disable_o[out] disable the baudtick generation if needed

uart_tx

../_images/wb_uart_tx.drawio.svg

Fig. 20 Diagramm of the Transmitter.

SC_MODULE(m_uart_tx)

Implementation of the UART transmitter.

This module is used to transmit data over the UART interface. The data is provided to the module as an 8-bit word, and the module will transmit the data over the tx_o line. Transmission is initiated by setting the tx_start_i signal. The module will then transmit the data and set the tx_finish_o signal when the transmission is complete. The module also handles the stop bits. If the stopbit_cnt_i signal is set, the module will transmit 2 stop bits. If the signal is unset, the module will transmit 1 stop bit.

Author

Lukas Bauer

Ports:

Parameters:
  • clk[in] clock of the module

  • reset[in] reset of the module

  • baudtick_i[in] baudtick input

  • tx_start_i[in] start signal for transmission

  • stopbit_cnt_i[in] unset: 1 stopbit, set: 2 stopbits

  • tx_data_i[in] <8> 8-bit word to transmit

  • tx_finish_o[out] transmission finished signal

  • tx_o[out] tx line signal

System for hardware test

To test the Wishbone UART module on hardware, a system has been created. This system contains a test module that can configure the UART module and echo the received data back. The system is synthesizable for the ULX3S board.

It contains two SystemC modules:

  • uart_wb_uart: Contains the test hardware for the UART module.

  • top: Contains a wrapper for synthesis

uart_test

SC_MODULE(m_uart_test)

a hardware module to test the wb_uart

This module initializes the wb_uart module via its Wishbone interface by setting the baud rate and enabling RX and TX. It then periodically checks the rxdata register to see if the RX FIFO is no longer empty. If that is the case, it saves the received data in a register and sends it to the txdata register to be sent back. This effectively echoes the received data back to the sender to check the basic functionality of the wb_uart module in hardware.

Author

Lukas Bauer

Ports:

Parameters:
  • clk[in] the clock signal of the module

  • reset[in] the reset signal of the module

  • rx[in] the receive signal of the uart

  • tx[out] the transmit signal of the uart

top

SC_MODULE(m_top)

wrapper for synthesis

This module is just the top level wrapper for synthesis

Author

Lukas Bauer

Ports:

Parameters:
  • clk_25[in] the clock signal comming form the board

  • reset[in] the reset signal of the module

  • rx[in] the receive signal of the uart

  • tx[out] the transmit signal of the uart

SimUART

The SimUART module implements a UART module that can be connected to the CSoftPeripheral interface of the PicoNut processor, which is described in Chapter Software Library Documentation.

The following figure provides an overview of the components of the SimUART module.

../_images/CSoftUart.drawio.svg

Fig. 21 Overview of the SimUART module.

group c_soft_uart

soft peripheral implementation of the sifive UART for simulation

This module is used to simulate the UART peripheral of the SiFive core. The module is implemented as a soft peripheral and can be used in the simulation environment. The module has the same registers as the SiFive UART. They are:

  • txdata: 32-bit register for the transmit data

  • rxdata: 32-bit register for the receive data

  • txctrl: 32-bit register for the transmit control

  • rxctrl: 32-bit register for the receive control

  • ie: 32-bit register for the interrupt enable

  • ip: 32-bit register for the interrupt pending

  • div: 32-bit register for the baud rate divider

Author

Lukas Bauer

The module has a 32-bit memory interface and can be accessed by the soft peripheral interface.

Note: At the moment, the module only implements transmit functionality, meaning it can only display data on stdout, but cannot receive data on stdin.

Note: The module is not synthesizable and is only used for simulation purposes.

Note: The module does not simulate the baud rate because it is not needed for the simulation, meaning the div register has no effect on the module.

CSoftPeripheral Interface

The following methods are those implemented from the CSoftPeripheral interface. To enable the UART module to be connected to the CSoftPeripheral interface, the following methods must be implemented:

inline c_soft_uart::c_soft_uart(uint64_t size, uint64_t base_address)

Construct a new Soft Uart object.

Parameters:
  • size – address space of the peripheral

  • base_address – base address of the peripheral in address space of the simulation

inline const char *c_soft_uart::get_info() override
inline uint8_t c_soft_uart::read8(uint64_t adr) override
inline void c_soft_uart::write8(uint64_t adr, uint8_t data) override
inline void c_soft_uart::write32(uint64_t adr, uint32_t data) override
inline uint32_t c_soft_uart::read32(uint64_t adr) override
inline bool c_soft_uart::is_addressed(uint64_t adr) override

UART Emulation

The following methods are implemented to emulate the functionality of the SiFive UART module. They handle data transmission, the FIFO, and interrupt generation.

void c_soft_uart::handel_transmit()

handels the transmit of the uart

Handles the transmission of the UART by checking if txen is enabled, and then sending the data from the tx_fifo if the UART is enabled and the tx_fifo is not empty. The data is sent to stdout.

void c_soft_uart::update_txfull()

updates the txfull flag in the txdata register

if the fifo is not full its set to false else to true

void c_soft_uart::handel_interrupt()

sets the values in the ip register according to the conditions

Checks if the interrupts are enabled and then sets the interrupt flag if the cnt register has the correct value.

void c_soft_uart::update_uart()

runs handel_interrupt and handel_transmit to update the uart