Design and verification of serial port sending module for FPGA learning

Design and verification of serial port sending module for FPGA learning

1. The purpose of the experiment:

Realize a serial port output, check whether the received data is the data sent by the serial port through the upper computer PC.

2. Introduction to the experiment:

Learn the principle of UART communication and its hardware circuit design, use FPGA to realize the design of the data transmission part of UART communication, and use ISSP tool to carry out board-level verification.

3. Experimental principle

(1) Principle of asynchronous serial communication

Serial communication refers to the use of a transmission line to transfer data one bit in sequence, and each bit of data occupies a fixed length of time.

Asynchronous communication uses one character as the transmission unit. The time interval between two characters in the communication is not fixed, but the time interval between two adjacent bits in the same character is fixed.
Insert picture description here
That is, the time interval between the 10-bit data in the figure below is fixed. This fixed time is called the baud rate, which refers to the number of data bits that can be communicated per second. Typical baud rates are 1200bps, 4800bps, 9600bps, 19200bps, 115200bps, etc. Generally, the baud rate at both ends of the communication should be the same.

Insert picture description here

(2) System framework diagram

For the entire serial port sending module, based on the above principles, understand the following input and output interfaces:

1. First as a sending module, there must be an output sending data port Rx232_Tx;

2. For the completion of a character transmission, it is necessary to tell the upper computer that the character transmission is over and start the next transmission, that is, the transmission end signal mark Tx_Done. When the transmission is completed, a high level of a clock cycle is output.

3. When the serial port module is sending data, there needs to be an output flag serial port status bit uart_state, that is, it is 1 when it is in the sending state, telling the host computer that the module is currently busy.

4. The input must have clock Clk and reset input Rst_n.
Insert picture description here

5. Although there are several commonly used baud rates in the above figure, how to choose the baud rate corresponding to the upper computer to realize the communication between different upper computers, so here needs a baud rate selection input port baud_set[3:0 ].

6. If we want to send a piece of data, we must control the module to send it when the data is needed, and it can’t be sent endlessly, so there must be a sending control signal, that is, sending an enable signal send_en

7. The data to be sent is input data_byte[7:0];

So what kind of logic circuit exists between these input and output ports to send data out?

Data output format

For outputting 1-byte 10-bit data, where the data sending format is known, that is, start bit + 8 data bits + stop bit (the parity bit is not available by default), then a multiplexer is needed to select one of 10 here, so that we can To choose which bit of data to send out.

Insert picture description here
Because RS232 is an asynchronous transceiver, in order to ensure that the sent data is stable when the clock arrives, the input data needs to be registered.

The 8-bit valid data is sent to the 10-choose multiplex through the register, and then the sel selection signal selects which bit of data to output. After the data is selected, it is transmitted to the data_reg register.

So how is the sel selection signal generated in the above figure?

Baud rate clock edge count output 10 select one multiplexer control

First, which data output is selected by the sel selection signal? As can be seen from the figure below, we can count the clock edges of BPS_CLK. Whenever a rising edge comes, the counter bps_cnt is incremented by 1, and then the number of data bits is selected according to the counting result. For example, when the counter counts to 7, the selection signal is Will choose to send BIT[5] data.

Insert picture description here

And this counter can only count up to 11 (one byte is sent). When it counts to 11, it needs to generate a clear clr signal.

This clear clr signal comes from the aforementioned output port Tx_Done. When the end signal flag Tx_Done outputs a high level for a clock cycle, it is determined that a byte has been transmitted.

When the counter bps_cnt_q is equal to 11, the result is true, and it is connected to the clr clear signal line to realize the clear action.

At the same time, the comparison result is transmitted to the Tx_Done signal through the register to tell the upper computer that the character transmission is over.

Insert picture description here
Looking at the figure above, the input BPS_Clk signal of bps_cnt is actually the clock signal corresponding to the baud rate.

Divider counter output baud rate clock

Insert picture description here
Because the time interval between adjacent bits of a character corresponds to the baud rate, and the transmission of each bit is based on the rising edge of bps_clk, we need different bps_clk to correspond to different baud rates (1200bps, 4800bps, 9600bps, 115200bps) ).

The first step is to generate different baud rates. Because the system clock is 50Mhz and the system clock cycle is 20ns, the frequency division counter is still used to achieve different baud rate clocks. The following table shows the counter relationships corresponding to common baud rates. :

Insert picture description here
The above table shows that the so-called baud rate generation is nothing more than using a timer to time the frequency and the corresponding baud rate clock frequency.

For example, when we use 9600bps, we need to generate a clock signal with a frequency of 9600Hz. So how to generate this signal?

Here, we first calculate the period of the 9600Hz clock signal, 1 second is 1000_000_000ns, so the period of the 9600bps baud rate clock is 1000_000_000/9600≈104166.6, that is, a period of the 9600Hz clock signal is 104166.6ns, whenever the timing time When it comes, just generate a high pulse signal with a system clock cycle length. Since the system clock cycle is 20ns (50MHz), only 104166.6/20 system clocks need to be counted to get the timing of 101166.6.

Insert picture description here

Divider counter count enable control

Corresponding to a frequency divider counter, when the system clock is running, when does it start counting even though it can count the signal? When does the counting start to work?

This design uses two alternative-one multiplexers to generate the enable count signal. First of all, there is a priority problem, because when the send_en sends the enable signal to a high level, then our frequency division counter will also start Counted.

However, the send_en signal only maintains a high level for one clock cycle and remains low for the rest of the time. At this time, a two-to-one multiplexer is needed to realize that when the send_en signal is high, output "1" to indicate the start of counting.

How to realize that when send_en is low, the divider counter is still counting? What is the sign of the end of the count?

It can be seen from the above that a Tx_Done flag signal will be generated after the data transmission is over. This signal is generated when the counter bps_cnt is equal to 11. The comparison result with 11 will be connected to the counter bps_cnt to clear the clr signal, then this signal can also be used As another two-to-one multiplexer on the selection side.

Insert picture description here

Therefore, when the send_en signal is low, the second one-of-two multiplexer MUX2_2 will be selected as the output. The selection end of MUX2_2 is the same as clr. Therefore, when the comparison result is false, the output of MUX2_2 is the original input UART_state. That is to keep the UART_state state unchanged, it still means that the divider counter is still counting.

When the comparison result is true, that is, when the data is sent, MUX2_2 will output "0". At this time, UART_state will output a low level, and the enable counting en_cnt will control the divider counter to stop counting.

Find the count value output at different baud rates

How to generate the count value bps_DR[15:0] corresponding to different baud rate clock signals?

Insert picture description here

Here we use the look-up table LUT to realize the baud_set signal to select different baud rates and output different system clock count values.

System frame diagram

Therefore, the basic logic system framework diagram of the serial port sending module can be drawn:
Insert picture description here

4. Code implementation

According to the above-described system framework diagram to realize the corresponding part of the function:

Data output format

Insert picture description here

Baud rate clock edge count output 10 select one multiplexer control

Insert picture description here

Divider counter output baud rate clock

Insert picture description here

Divider counter count enable control

Insert picture description here

Find the count value output at different baud rates

Insert picture description here

Overall code:

//--------------------------------------------------------------------------------------------
//      Component name  : uart_Tx	
//      Author          : 硬件嘟嘟嘟
//      time            : 2020.04.21
//      Description     : 串口发送模块
//      src             : FPGA系统设计与验证实战指南_V1.2
//--------------------------------------------------------------------------------------------

module uart_tx(Clk,Rst_n,send_en,baud_set,data_byte,Rx232_Tx,Tx_Done,uart_state);

		input Clk,Rst_n;
		input send_en;  //发送使能信号输入
		
		input [2:0] baud_set;  //波特率选择输入
		input [7:0] data_byte; //待发送数据
		//
		output Rx232_Tx;    //发送数据输出端口
		output Tx_Done;     //一字节发送结束标志位
		output uart_state;   //发送数据状态
		

		
//数据输入寄存,输出格式及输出寄存模块

//8位有效数据寄存
reg [7:0] r_data_byte;
always@(posedge Clk,negedge Rst_n)
			if(!Rst_n)
					r_data_byte <= 8'b0;
			else if(send_en)
					r_data_byte <= data_byte;
			else
					r_data_byte <= r_data_byte;

//10选1多路器模块
reg [3:0] bps_cnt_q;
reg Rx232_Tx;
localparam START_BIT = 1'b0,
           STOP_BIT  = 1'b1;
always@(posedge Clk,negedge Rst_n) 
		if(!Rst_n)
			Rx232_Tx = 1'b1;
		else begin 
			case(bps_cnt_q)
				0 : Rx232_Tx <= 1'b1;
				1 : Rx232_Tx <= START_BIT;
				2 : Rx232_Tx <= r_data_byte[0];
				3 : Rx232_Tx <= r_data_byte[1];
				4 : Rx232_Tx <= r_data_byte[2];
				5 : Rx232_Tx <= r_data_byte[3];
				6 : Rx232_Tx <= r_data_byte[4];
				7 : Rx232_Tx <= r_data_byte[5];
				8 : Rx232_Tx <= r_data_byte[6];
				9 : Rx232_Tx <= r_data_byte[7];
				10 : Rx232_Tx <= STOP_BIT;
				default : Rx232_Tx = 1'b1;
			endcase
		end
		
		

//波特率时钟沿计数输出10选一多路器控制
//10选1多路器控制信号产生
reg bps_clk;
always@(posedge Clk,negedge Rst_n)
			if(!Rst_n)
					bps_cnt_q <= 4'd0;
			else if(bps_clk)
					bps_cnt_q <= bps_cnt_q + 1'b1;
			else if(bps_cnt_q == 4'd11)
					bps_cnt_q <= 4'd0;
			else 
					bps_cnt_q <= bps_cnt_q;

//发送数据完成标志位
reg Tx_Done;
always@(posedge Clk,negedge Rst_n)
		if(!Rst_n)
			Tx_Done <= 1'b0;
		else if(bps_cnt_q == 4'd11)
			Tx_Done <= 1'b0;
		else 
			Tx_Done <= 1'b1;

//分频计数器输出波特率时钟

//分频计数
reg [15:0] bps_DR;
reg [15:0] div_cnt;
reg uart_state ;//en_cnt信号由uart_cnt寄存器寄存
always@(posedge Clk,negedge Rst_n)
		if(!Rst_n)
				div_cnt <= 16'd0;
		else if(uart_state)begin
			if(div_cnt == bps_DR)
				div_cnt <= 16'd0;
		else 
				div_cnt <= div_cnt + 1'b1;
		end
		else
		div_cnt <= 16'd0;
//输出波特率时钟信号模块
always@(posedge Clk,negedge Rst_n)
		if(!Rst_n)
				bps_clk <= 1'b0;
		else if(div_cnt == 16'd1)
				bps_clk <= 1'b1;
		else 
				bps_clk <= 1'b0;

//分频计数器计数使能控制
always@(posedge Clk ,negedge Rst_n)
		if(!Rst_n)
				uart_state <= 1'b0;
		else if(send_en)
				uart_state <= 1'b1;
		else if(bps_cnt_q == 4'd11)
				uart_state <= 1'b0;
		else 
				uart_state <= uart_state;

//不同波特率查找计数值输出
always@(posedge Clk,negedge Rst_n)
if(!Rst_n)
bps_DR <= 16'd5207;
else begin
			case (baud_set)
				0:bps_DR <= 16'd10416;
				1:bps_DR <= 16'd5207;
				2:bps_DR <= 16'd2603;
				3:bps_DR <= 16'd1301;
				4:bps_DR <= 16'd433;
				default : bps_DR <= 16'd5207;
			endcase
		end
endmodule

Guess you like

Origin blog.csdn.net/sinat_41653350/article/details/105663810