Designing a UART in MyHDL and test it in an FPGA
The universal asynchronous receiver/transmitter (UART) is an old friend to embedded systems engineers. It's probably the first communications protocol that we learn in college. In this article, we will design our very own UART using MyHDL.
MyHDL is a free, open-source Python library developed by Jan Decaluwe. Its goal is to be a powerful hardware description language. The idea is to apply the new concepts that have appeared in the software industry to hardware design, such as test-driven development, functional tests, and high-level abstraction. The system also generates synthesizable VHDL and Verilog code from the MyHDL design. The idea is to verify everything in Python and then press the "Go" button to generate a VHDL or Verilog representation that can be synthesized and loaded into an FPGA.
High-level view of the UART
As we know, to have working UART communication, we need the receiver and the transmitter to agree on a baud rate, the number of data bits, the number of stop bits, and other details. In our example, we are going to implement a UART with 8 data bits, 2 stop bits, and a baud rate of 115,200 Hz. (You can customize these values as you wish.)
We are going to have three main modules as follows:
- baudrate_gen: This module generates the baud rate "ticks" at the right time.
- serial_tx: This module is our transmitter; it's going to send data out over the TX line. Its input is the data to be sent (one byte) and a start that initiates the process.
- serial_rx: This module is our receiver; it's going to receive data in over the RX line. Its output is the data received (one byte) and a data_rdy signal that indicates the data is ready to be consumed.
The baudrate_gen module
This is our simplest module. Based on the input clock, an output enable tick is generated. On my board, I have a 50MHz clock, and I need a baud rate of 115,200. This means we have 50,000,000/115,200, which give us 434.027. Let's round this value to 434, which means we have to generate a tick every 434 clock pulses. We will also generate a half baud rate tick; for this we have to count 434/2 = 217 pulses before enabling this signal.
The MyHDL code is very similar to VHDL or Verilog. First we define our module, which is called baudrate_gen, and then we declare everything that is either an input or an output. In our module we have the following signals as inputs:
- sysclk: Our clock
- reset_n: Our reset signal
- baud_rate_i: The number we have to count to generate our baud rate (this is going to be automatically transformed to a constant)
Meanwhile, as outputs, we are going to have the baud_rate_tick_o and half_baud_rate_tick_o signals as previously discussed.
The keyword Signal acts like a VHDL Signal; intbv is a type created in MyHDL. This is short for INT BIT VECTOR. The goal is to avoid confusion between Boolean, signed, and unsigned data. The first parameter is the initialization, and then comes the min and max range.
The @always_seq is a MyHDL decorator. It's analogous to a VHDL process or a Verilog @always. There are several decorators in MyHDL; this one creates a synchronous process with a clock and a reset. The .next is a function that indicates that the value is going to be updated in the next delta cycle, which is again analogous to processes in VHDL and Verilog. It's necessary to return our decorators at the end of the process.
To read more of this external content and to leave a comment, go to "The serial_tx mode."