Reverse Engineering of UART Protocol: 128 Ways to Real-Time Decode Serial Data with FPGA

1. Experimental Task

  • Task: Based on the STEP-MAX10M08 core board and STEP BaseBoard V4.0 baseboard, complete the design of a serial port monitoring system and observe the debugging results.

  • Requirements: Design a serial port monitoring system to monitor the serial port (UART) receiving data in real-time and display the data on the baseboard’s 8-digit digital tube (limited to digits 0~9).

  • Analysis: Drive the CH340 serial communication module on the baseboard through FPGA programming to receive data from PC (serial port debugging assistant) or other serial devices, process it, and finally display the received data on the baseboard’s digital tube through the 8-digit scanning digital tube module.

2.Experimental Purpose

This experiment mainly studies the working principle, protocol, and related knowledge of the serial port (UART) bus, practices how to use FPGA to drive the CH340 module to achieve serial communication design, while reviewing the instantiation application of the scanning digital tube module from the previous section.

  • Familiarize with the working principle and communication protocol of the serial port (UART) bus

  • Complete the design of the serial communication module based on FPGA.

  • Complete the design and implementation of the serial monitoring system

3. Design Block Diagram

Based on the previous experimental analysis, we can see that this design can be divided into three functional modules:

  • Uart_Bus: UART serial communication design, achieving serial communication data transmission.

  • Decoder: Convert the data received by the UART module into BCD code for display on the digital tube.

  • Segment_scan: Display the data received from the serial port on the scanning digital tube by driving the baseboard.

The top-level module Display_Ctl achieves the overall design of the serial monitoring system by instantiating two sub-modules and connecting the corresponding signals. UART communication is full-duplex, with receiving and sending as two independent designs. This experiment only needs to receive data. There are two key factors in serial communication: transmission format and transmission rate, which can be implemented with two modules:

  • Baud: Control the transmission rate of UART communication data.

  • Uart_Rx: Control the data format of UART communication based on the data transmission rate clock.

Reverse Engineering of UART Protocol: 128 Ways to Real-Time Decode Serial Data with FPGA

Top-down hierarchical design

Reverse Engineering of UART Protocol: 128 Ways to Real-Time Decode Serial Data with FPGA

Module structure design

4.Experimental Principle

4.1 UART Interface Introduction

Reverse Engineering of UART Protocol: 128 Ways to Real-Time Decode Serial Data with FPGA

UART communication interface

In the embedded field, the serial port generally refers to the UART interface, Universal Asynchronous Receiver/Transmitter (UART), which is a general-purpose serial data bus used for asynchronous communication. This bus supports bidirectional communication and can achieve full-duplex transmission and reception.

Reverse Engineering of UART Protocol: 128 Ways to Real-Time Decode Serial Data with FPGA

RS232 serial communication interface

In systems or computers, the serial port generally refers to the RS232 interface, also known as COM port or DB9. Older computers and desktops usually have this interface, which has 9 pins, with the three most important pins being: TXD, RXD, and GND. The basic communication logic is completely consistent with UART. To enhance the anti-interference capability of serial communication, the RS232 serial communication interface defines its own voltage standards, using negative logic levels, defining +5~+12V as low level and -12~-5V as high level, which is equivalent to adding a driver to the original UART communication voltage standard to adjust it to the voltage standard of RS232, with the communication principle as follows:

Reverse Engineering of UART Protocol: 128 Ways to Real-Time Decode Serial Data with FPGA

RS232 serial communication

With the development of technology, the types of communication interfaces are increasing, the solutions are becoming more stable, the costs are decreasing, and the sizes are becoming more compact. The RS232 serial communication interface solution is gradually being abandoned, replaced by various faster, more stable, and more compact interfaces. USB is one of the widely used interfaces. To achieve UART communication, a USB to UART solution has been widely applied, with common USB to UART solutions including CP2102, FT232, CH340, etc.

Reverse Engineering of UART Protocol: 128 Ways to Real-Time Decode Serial Data with FPGA

USB to UART communication

Our STEP BaseBoard V4.0 integrates a UART communication module that uses the CP2102 solution. The FPGA drives the CP2102 through the UART bus to achieve data communication between USB and UART, ultimately achieving data transmission between the FPGA and the computer. The timing of UART communication is as follows.

Reverse Engineering of UART Protocol: 128 Ways to Real-Time Decode Serial Data with FPGA

UART communication timing

  • Start bit: First, send a logic 0 signal, indicating the start of character transmission.

  • Data bits: Can be 5~8 bits of logic 0 or 1. For example, ASCII code (7 bits), extended BCD code (8 bits).

  • Parity bit: Adding this bit to the data bits makes the number of 1s even (even parity) or odd (odd parity).

  • Stop bit: It is a character data end marker. It can be 1 bit, 1.5 bits, or 2 bits of high level.

  • Idle bit: In a logic 1 state, indicating that there is no data transmission on the current line.

4.2 UART Module Connection

The circuit diagram of the UART communication module based on the CH340 solution on the STEP BaseBoard V4.0 is as follows:

Reverse Engineering of UART Protocol: 128 Ways to Real-Time Decode Serial Data with FPGA

UART communication module circuit

The above diagram is the circuit diagram of the UART communication module based on the CH340 solution, which does not require an external USB communication clock crystal (internally integrated). The CH340 chip TXD and RXD are connected to the FPGA chip RXD and TXD, respectively, and both signals are connected to LED lights, so when UART communication occurs, the corresponding LED lights will flash quickly, serving as the UART communication indicator. The CH340 chip DTR and RTS are connected through two transistors to build a flow control circuit, connecting to the WIFI module ESP8266-12F. When using the UART module to program the ESP8266 module, there is no need to manually enter the firmware programming mode, which will be detailed in subsequent experiments involving WIFI communication; this can be ignored here.

4.3 UART Driver Implementation

Reverse Engineering of UART Protocol: 128 Ways to Real-Time Decode Serial Data with FPGA

Reverse Engineering of UART Protocol: 128 Ways to Real-Time Decode Serial Data with FPGA

SPI bus connection and timing

Reverse Engineering of UART Protocol: 128 Ways to Real-Time Decode Serial Data with FPGA

Reverse Engineering of UART Protocol: 128 Ways to Real-Time Decode Serial Data with FPGA

I2C bus connection and timing

SPI, I2C, UART bus comparison table:

SPI bus

I2C bus

UART bus

SS

SCL

TXD

SCK

SDA

RXD

MOSI/MISO

For the SPI bus, both parties complete the bus data sampling triggered by the rising or falling edge of SCK when the bus is enabled, allowing both parties to accurately receive the data sent by the other. For the I2C bus, the receiving party completes the bus data sampling triggered by the high level of SCL. In summary, SCK in the SPI bus and SCL in the I2C bus serve as the clock in communication, with the receiver sampling data based on the corresponding clock state, ultimately ensuring normal communication.

For the UART bus, TXD and RXD are used for sending and receiving data, respectively, equivalent to two independently working single-wire buses. Without the cooperation of a clock line, how does the receiving end obtain the data transmitted by the sending end? There is indeed a method: both parties need to agree on the UART bus data transmission’s communication rate and timing format.

Communication rate:

The data transmission speed of UART is described by the baud rate, which is the number of data bits received or sent by UART per second. For example, a baud rate of 9600 means sending or receiving 9600 bits of data per second, which means the sender needs to maintain the corresponding time for each data bit sent, calculated as follows:

  • 1s / 9600 = 1000000us / 9600 = 104.17us

The hardware on the small foot uses a 12MHz clock crystal. If using a 12MHz clock signal as the system clock and using a counter delay to complete UART communication data sampling, the counter delay count terminal value is calculated as follows:

  • 12M / 9600 = 1250

Since the baud rate is agreed upon in the protocol, to ensure the universality and flexibility of the protocol, the baud rate parameter has fixed options and cannot be set arbitrarily (if both parties of the UART communication are programmed by themselves, they can define the baud rate they need according to their requirements, except in this case). There are many baud rate parameter options, and you can open the serial port debugging assistant tool to find the baud rate configuration list. The commonly used baud rate values are as follows:

Common baud rates for UART:

1200

4800

9600

38400

115200

Timing format:

Regarding the timing format, it was briefly mentioned in the previous section on the UART interface introduction. The timing during communication is sequentially: start bit, data bits, parity bit, stop bit, idle bit, where the data bits can be 5~8 bits. In this design, we use 8 bits of data, and the parity bit can be omitted. The final determined timing format is as follows:

Reverse Engineering of UART Protocol: 128 Ways to Real-Time Decode Serial Data with FPGA

This experiment UART communication timing

The previously mentioned communication rate and timing format are actually two important parameters in UART communication. The data to be transmitted is output according to the timing format of UART based on the clock of the communication rate, which can be achieved in the following three steps:

1) Combine the data to be sent with the start bit and stop bit to form a 10-bit wide data

2) Use a counter to count delays to generate the timing pulse required for the corresponding baud rate

3) Output the data serially according to the timing (start bit — bit0~bit7 — stop bit)

For example, the timing of sending 8’h73 and 8’h5a through UART, with the red arrows indicating the baud rate corresponding to the timing points.

Reverse Engineering of UART Protocol: 128 Ways to Real-Time Decode Serial Data with FPGA

UART sending data example

For sending data via UART, the baud rate timing is generated by itself, and the data is actively sent, making the logic relatively simple. However, when UART receives data, since it is uncertain when the other party will send data, it needs to continuously detect the RX signal. When data transmission is detected, sampling can be performed based on the agreed baud rate timing, which can be achieved in the following three steps:

1) Detect the falling edge of the UART RX signal (self-locking, complete reception before unlocking to continue detection)

2) During the reception sampling, the sampling point should be at the middle value of the counter

3) Reassemble the sampled data into 8-bit data according to the timing requirements of UART timing

For example, when the UART RX end receives data 8’h73 and 8’h5a, the red arrows indicate the points where data transmission is detected, and the green arrows indicate the corresponding sampling timing points (the sampling point in the middle of the data is the most stable).

Reverse Engineering of UART Protocol: 128 Ways to Real-Time Decode Serial Data with FPGA

UART receiving data example

Through the above theory, we understand the entire process of sending and receiving data via UART. In both processes, we need the baud rate timing, so we can design a timing module Baud, allowing both sending and receiving to instantiate the timing module to generate the corresponding baud rate timing signal.

Baud module design implementation:

The port program implementation of the Baud module is as follows:

module Baud #(parameter              BPS_PARA = 1250 //12MHz clock 1250 corresponds to 9600 baud rate)(input                   clk,        //system clockinput                   rst_n,      //system reset, active lowinput                   bps_en,     //receive or send clock enableoutput  reg             bps_clk     //receive or send clock output);

Design a counter to generate the corresponding baud rate timing signal. Since UART may receive data at any time, the timing module must always be on standby, keeping the counter reset, and outputting the timing signal accurately when needed.

The counter design program implementation is as follows:

//Counter counts to meet baud rate clock requirementsalways @ (posedge clk or negedge rst_n) begin    if(!rst_n)         cnt <= 1'b0;    else if((cnt >= BPS_PARA-1)||(!bps_en)) // bps_en=0时,计数器复位        cnt <= 1'b0;                            else // bps_en=1时,计数器工作,周期为BPS_PARA个系统时钟周期        cnt <= cnt + 1'b1;end

When bps_en (active high) is enabled, the counter’s counting period is determined by the parameter BPS_PARA. From the previous data reception timing section, we learned that when detecting the falling edge from RX, the counter starts working, and it takes half a timing pulse to reach the data sampling point. For data transmission, it is only necessary to ensure that the time between two adjacent timing points is one counter cycle, so we can generate a pulse signal as the timing signal when the counter reaches the middle value.

The timing signal generation program implementation is as follows:

//Generate the corresponding baud rate clock timing, the receiving module will use this timing for UART data receptionalways @ (posedge clk or negedge rst_n) begin    if(!rst_n)         bps_clk <= 1'b0;    else if(cnt == (BPS_PARA>>1)) //Middle value data stable, make sampling point        bps_clk <= 1'b1;        else         bps_clk <= 1'b0;end

Sending module design implementation:

The preceding circuit transmits the data to be sent through tx_data_valid and tx_data_in. When tx_data_valid has a pulse signal, tx_data_in is valid data, which is combined with the start bit and stop bit and assigned to tx_data_r, while controlling the timing enable signal to enable and self-lock, then wait until the transmission of 10-bit data is completed before releasing the enable.

The data sending control program implementation is as follows:

output  reg             bps_en,         //Sending clock enableinput                   bps_clk,        //Sending clock inputinput                   tx_data_valid,  //Sending data valid pulseinput           [7:0]   tx_data_in,     //Data to be sentoutput  reg             uart_tx         //UART sending outputreg             [9:0]   tx_data_r;    //Data combined with start and stop bits//According to the completion of receiving data, drive sending data operationalways @ (posedge clk or negedge rst_n) begin    if(!rst_n) begin        bps_en <= 1'b0;        tx_data_r <= 10'd0;    end else if(tx_data_valid && (!bps_en))begin            bps_en <= 1'b1;     //When data needs to be sent, enable the timing enable signal        tx_data_r <= {1'b1,tx_data_in,1'b0};        end else if(num==4'd10) begin           bps_en <= 1'b0;     //One UART transmission requires 10 clock signals, then ends    endend

UART data sending timing program implementation is as follows:

//When in working state, send data according to the sending clock timingalways @ (posedge clk or negedge rst_n) begin    if(!rst_n) begin        num <= 1'b0;        uart_tx <= 1'b1;    end else if(bps_en) begin        if(bps_clk) begin                  //Send data according to timing            num <= num + 1'b1;            uart_tx <= tx_data_r[num];   //Send low bits first, then high bits        end else if(num>=4'd10)             num <= 4'd0;        endend

Instantiate and connect the timing module Baud and sending module Uart_tx to complete the design of the sending function, as shown below:

Reverse Engineering of UART Protocol: 128 Ways to Real-Time Decode Serial Data with FPGA

UART sending function design implementation

The receiving module Uart_Rx design implementation:

First, multi-stage buffering is performed on the RX signal to eliminate metastability while detecting the falling edge, with the program implementation as follows:

input                   uart_rx,        //UART receive inputreg uart_rx0,uart_rx1,uart_rx2; //Multi-stage delay latch to eliminate metastabilityalways @ (posedge clk) begin    uart_rx0 <= uart_rx;    uart_rx1 <= uart_rx0;    uart_rx2 <= uart_rx1;end//Detect the falling edge of the UART receive input signalwire    neg_uart_rx = uart_rx2 & ~uart_rx1;

When a falling edge is detected on RX, enable the timing enable signal, self-lock until the reception operation is completed, and then reset the timing enable signal. The program implementation is as follows:

//Control of the receive clock enable signalalways @ (posedge clk or negedge rst_n) begin    if(!rst_n)        bps_en <= 1'b0;    else if(neg_uart_rx && (!bps_en)) //When transmission is detected, enable the timing enable signal        bps_en <= 1'b1;         else if(num==4'd9)              //Complete UART reception operation, reset the timing enable signal        bps_en <= 1'b0;         end

Complete the data sampling of the UART bus based on the timing signal to obtain 8-bit valid data, with the program implementation as follows:

reg             [7:0]   rx_data;//When in working state, sample data according to the receive clock timingalways @ (posedge clk or negedge rst_n) begin    if(!rst_n) begin        num <= 4'd0;        rx_data <= 8'd0;    end else if(bps_en) begin           if(bps_clk) begin                       num <= num + 1'b1;            if(num<=4'd8) rx_data[num-1] <= uart_rx1; //Send low bits first, then high bits        end else if(num == 4'd9) begin              num <= 4'd0;                        end    end else begin        num <= 4'd0;    endend

When the UART reception operation is completed, output the obtained 8-bit valid data to the subsequent circuit, with the program implementation as follows:

//Output the received data while controlling the output valid signal to generate a pulsealways @ (posedge clk or negedge rst_n) begin    if(!rst_n) begin        rx_data_out <= 8'd0;        rx_data_valid <= 1'b0;    end else if(num == 4'd9) begin          rx_data_out <= rx_data;        rx_data_valid <= 1'b1;    end else begin        rx_data_out <= rx_data_out;        rx_data_valid <= 1'b0;    endend

Finally, instantiate and connect the timing module Baud and receiving module Uart_rx to complete the design of the receiving function, as shown below:

Reverse Engineering of UART Protocol: 128 Ways to Real-Time Decode Serial Data with FPGA

UART receiving function design implementation

The entire UART driver design consists of two independent functional combinations: the sending function part and the receiving function part. The overall design block diagram of UART functionality is as follows:

Reverse Engineering of UART Protocol: 128 Ways to Real-Time Decode Serial Data with FPGA

When we need to send data via UART, we only need to instantiate the sending function part design. When we need to receive data via UART, we only need to instantiate the receiving function part design. For example, in this design, the FPGA drives the UART module to receive data sent from the computer’s serial port debugging assistant, so we only need to instantiate the receiving function part design.

4.4 Overall System Implementation

Having just learned about the UART communication module, this design only needs to use the receiving function part design. Each communication will receive an 8-bit data. How to display the corresponding data on the digital tube? First, I need to understand the relationship between the 8-bit data received by UART and the numbers to be displayed.

Reverse Engineering of UART Protocol: 128 Ways to Real-Time Decode Serial Data with FPGA

Serial port debugging assistant interface

The above image shows the interface of the computer’s serial port debugging assistant. When we connect the hardware, select the corresponding port in the serial port settings, and configure the baud rate, data bits, parity bits, stop bits, flow control, etc. as shown in the image, click to start establishing a connection. Next, we can input the data to be sent in the serial port sending window, click send, and observe the changes on the baseboard’s digital tube. Re-enter the number, click send, and observe the changes on the baseboard’s digital tube again.

Reverse Engineering of UART Protocol: 128 Ways to Real-Time Decode Serial Data with FPGA

Leave a Comment