The module "uart"
a VHDL implementation of the universal asynchronous receiver/transmitter protocol

The VHDL module "uart" (see symbol) transfers data by the universal asynchronous receiver/transmitter protocol.
It has a clock divider which is able to divide the incoming clock by non integer numbers to support each wished baudrate.
The module "uart" works full synchronous, this means all flipflops are clocked by the incoming clock signal.

The module is configurable by generics in order

The module "uart" was developed with HDL-SCHEM-Editor.

Ports:

Port name Direction Description
res_i input asynchronous reset input, 1-active
clk_i input clock input
clock_divisor_i(g_divisor_width-1:0) input Determines the baudrate, must fulfill: clock_divisor_i>2
The resulting baudrate (for transmit and receive) is determined by: (frequency at clk_i)/clock_divisor_i
If the resulting baudrate is not close enough to the wished baudrate,
the inputs fraction_nominator_i/denominator_i can be used.
Must be stable during sending and receiving.
fraction_nominator_i(g_fraction_width-1:0) input If fraction_nominator_i is different from 0, then the resulting baudrate is determined by:
(frequency at clk_i)/(clock_divisor_i + fraction_nominator_i/fraction_denominator_i)
Must be stable during sending and receiving.
fraction_denominator_i(g_fraction_width-1:0) input If fraction_nominator_i is different from 0, then the resulting baudrate is determined by:
(frequency at clk_i)/(clock_divisor_i + fraction_nominator_i/fraction_denominator_i)
Must be stable during sending and receiving.
data_i(g_data_width-1:0) input Data to send
Must be stable during sending.
send_i input An 1-active impulse of 1 clock cycle width starts transmitting.
tx_o output uart transmit output.
Has value 1 at idle.
ready_send_o output Changes from 1 to 0 after send_i=1.
Changes from 0 to 1 after the last stopbit has been sent.
rx_i input uart receive input.
Expects 1 at idle.
ready_receive_o output Is a 1-active impulse of one clock period.
Gets active in the middle of the first stop bit.
parity_err_o output Signals a parity error of the received data.
Gets valid at the rising edge of ready_receive_o
Changes to 0, when the next start bit is detected.
data_o(g_data_width-1:0) output Received data.
Gets valid in the middle of the last data bit (this means: before ready_receive_o=1).
Stable until the middle of the first data bit of the next frame.
Shall be sampled with ready_receive_o.

Generics:

Generic name Minimum Value Maximum Value Description
g_divisor_width 2 n/a Number of bits of clock_divisor_i
g_fraction_width 2 n/a Number of bits of fraction_nominator_i and fraction_denominator_i
g_data_width 1 n/a Number of bits of data_i, data_o and in the uart frame
g_has_parity false true If "true", then the uart frame will have a parity bit.
g_odd_parity false true When "true", then the number of ones together in databits and paritybit must be odd, else even.
g_number_of_stopbits 1 g_data_width This is the number of stop bits which are inserted at transmit.
g_number_of_sync_flipflops 0 n/a This is the number of flipflops which are used to synchronize rx_i.

The module ""uart"" is a hierarchical module, which is built by 4 submodules.

Submodule name Functionality
uart_clock_divider

This submodule is a clock divider. It is used twice:
One instance for the uart_send submodule and a second one for the uart_receive submodule.
Both instances are configured by their generic g_create_clock_enable to create a clock-enable signal instead of 50:50 duty cycle clock signal.

The submodule divides the incoming uart clock signal clk_i by the value provided at the uart input clock_divisor_i.
Depending on the frequency of clk_i and the wished baudrate, the division by clock_divisor_i may not create a send/receive clock-enable with a frequency exact enough.

In this case the values at fraction_nominator_i and fraction_denominator_i can be used to create a send/receive clock-enable with the exact frequency by dividing in this way:

(frequency at clk_i)/(clock_divisor_i + fraction_nominator_i/fraction_denominator_i)

Of course the clock enable moments (or clock edges) of the divided clock are in this case not active at the exact moments where they should be, but jitter around these exact moments in a way that the difference to the exact moment is kept as small as possible.

uart_send

This submodule sends the data at input data_i to the output tx_o.

It starts transmitting as soon as an impulse is applied to the send_i input.
It enables and disables its uart_clock_divider as required.
It adds a parity bit if configured by the generics g_has_parity and g_odd_parity.
It adds as many stop bits as configured by the generic g_number_of_stopbits.

uart_rx_sync

This submodule synchronises the incoming uart receive signal rx_i with the clock signal clk_i.

The number of flipflops used to avoid metastability must be configured by the generic g_number_of_sync_flipflops.

uart_receive

This submodule converts the synchronised uart receive signal into a data word at output data_o.

It samples the data at the centre of each data bit.
This centre is determined once by counting from the falling edge of the start bit and is maintained throughout the data frame.
The submodule signals the end of reception at output ready_receive_o and the result of the parity check at output parity_err_o.

symbol symbol symbol symbol symbol symbol

Source code for HDL-SCHEM-Editor and HDL-FSM-Editor for module "uart" and its testbenches (Number of downloads = 31 ).
With these files the schematics and the state-diagram can be loaded into HDL-SCHEM-Editor or HDL-FSM-Editor and can be easily read and modified:

All module VHDL-files of the module "uart" (Number of downloads = 29 ).
These files were generated by HDL-SCHEM-Editor and HDL-FSM-Editor:

All testbench VHDL-files of the 2 testbenches of the module "uart" (Number of downloads = 31 ).
These files were generated by HDL-SCHEM-Editor and HDL-FSM-Editor:

Relocation hints:

You should extract all archives into a folder named "uart".

Then you should load the toplevel (probably the testbench) into HDL-SCHEM-Editor.
When you navigate through the design hierarchy by a double click at each symbol,
HDL-SCHEM-Editor will find the submodules on your disk and ask if it can replace
the original path to the submodule by the new one at your disk.
After storing the changed modules the relocation of the source files is ready
(instead you could replace "M:/gesicherte Daten/Programmieren/VHDL/uart" in all
"hdl_editor_designs/*.hse" source files by your path to this directory with your editor).

Now you can navigate through the design by HDL-SCHEM-Editor and generate HDL by HDL-SCHEM-Editor for
all modules except uart_send and uart_receive, for which the HDL must be generated by HDL-FSM-Editor.
Of course there is only need for generating HDL, if you change something at the modules, because you can find the HDL in VHDL_designs.zip and VHDL_testbenches.zip.

If you want to simulate or modify the modules by HDL-SCHEM-Editor you also must adapt the information in the Control-tab of the toplevel you want to work on.
There you must define a "Compile through hierarchy command", an "Edit command", the path to your HDL-FSM-Editor and a "Working directory".

Change log:

Version 1.0 (24.02.2025):

If you detect any bugs or have any questions,
please send a mail to "matthias.schweikart@gmx.de".