FPGA study notes
1. UART serial port protocol and serial port receiver design
1 原理图
2 Verilog 代码
3 Modelsim仿真
4. FPGA板级验证
1.1 Serial port protocol receiver design
- Goal: FPGA receives data from other devices via UART.
- Experimental phenomenon: In Quartusz II, call the in system sources and probes
editor tool to view the data received by the UART receiving module. The data is sent by the PC. - Knowledge points:
- The principle of uart's communication protocol and the realization of data acceptance in an industrial environment
- Use of debugging tools in system sources and probes editor (ISSP)
1. Schematic
- Module composition:
- Input signal synchronization module
- Start data detection module
- Baud rate generation module
- Data receiving process
(1). Sequence diagram of data collection in the sending end laboratory
- Laboratory environment: sample the midpoint of a data
(2). Timing diagram of industrial data collection at the sender
- Industrial environment: The electromagnetic interference in the industrial environment is serious, the noise signal is easy to cause errors, and the data sampled only once is not accurate.
- Method: Take the results of the middle 6 times of each bit of data sampling, and determine the final result according to the number of occurrences of the level (0,1) of the 6 results.
2. Verilog code
//----top---------------------------------------
module uart_rx_r0
(
input clk,
input rst_n,
input rs232_rx,
input [2:0] baud_set,
output reg rx_done,
// output rx_state,
output reg [7:0] data_byte
);
reg rs232_rx_reg1;
reg rs232_rx_reg2;
reg rs232_rx_syn1;
reg rs232_rx_syn2;
reg uart_state;
reg [8:0] div_cnt;
reg [8:0] cnt_max;//div_cnt max value
reg baud_clk;
reg [7:0] baud_cnt;
reg [3:0] reg_data_byte [7:0];
reg [2:0] START;
reg [2:0] STOP;
// wire pos_edge;
wire neg_edge;
//----asynch rs232_rx--> synch-----------
always@(posedge clk or negedge rst_n)
if(!rst_n)begin
rs232_rx_syn1 <= 0;
rs232_rx_syn2 <= 0;
end else begin
rs232_rx_syn1 <= rs232_rx;
rs232_rx_syn2 <= rs232_rx_syn1;
end
//----start bit detect------------------
always@(posedge clk or negedge rst_n)
if(!rst_n)begin
rs232_rx_reg1 <= 0;
rs232_rx_reg2 <= 0;
end else begin
rs232_rx_reg1 <= rs232_rx_syn2;
rs232_rx_reg2 <= rs232_rx_reg1;
end
// assign pos_edge = (rs232_rx_reg1 & !rs232_rx_reg2);
assign neg_edge = (!rs232_rx_reg1 & rs232_rx_reg2);
//------baud_set------------------------
always@(*)
case(baud_set)
3'd0: cnt_max <= 9'd325;
3'd1: cnt_max <= 9'd163;
3'd2: cnt_max <= 9'd81;
3'd3: cnt_max <= 9'd54;
3'd4: cnt_max <= 9'd27;
default: cnt_max <= 9'd325;
endcase
//----uart_state-------------------------
always@(posedge clk or negedge rst_n)
if(!rst_n)
uart_state <= 0;
else if(neg_edge)
uart_state <= 1;
else if(rx_done || (baud_cnt==12 && (START > 2)))
uart_state <= 0;
else
uart_state <= uart_state;
//----frequence div-->baud_clk-----------
always@(posedge clk or negedge rst_n)
if(!rst_n)
div_cnt <= 0;
else if(uart_state)begin
if(div_cnt == cnt_max)
div_cnt <= 0;
else
div_cnt <= div_cnt + 9'd1;
end else
div_cnt <= 0;
always@(posedge clk or negedge rst_n)
if(!rst_n)
baud_clk <= 1'd0;
else if(div_cnt == 9'd1)
baud_clk <= 1'd1;
else
baud_clk <= 1'd0;
//----baud_cnt--------------------------
always@(posedge clk or negedge rst_n)
if(!rst_n)
baud_cnt <= 0;
else if(rx_done || (baud_cnt==12 && (START > 2)))
baud_cnt <= 0;
else if(baud_clk)
baud_cnt <= baud_cnt + 8'd1;
else
baud_cnt <= baud_cnt;
//----rx_done----------------------------
always@(posedge clk or negedge rst_n)
if(!rst_n)
rx_done <= 1'b0;
else if(baud_cnt == 8'd159)
rx_done <= 1'b1;
else
rx_done <= 1'b0;
//----data_byte--------------------------
always@(posedge clk or negedge rst_n) // asynchronos transimiter, reg_data_byte is used to keep input data stable
if(!rst_n)
data_byte <= 8'd0;
else if(baud_cnt == 8'd159)begin
data_byte[0] <= reg_data_byte[0][2];
data_byte[1] <= reg_data_byte[1][2];
data_byte[2] <= reg_data_byte[2][2];
data_byte[3] <= reg_data_byte[3][2];
data_byte[4] <= reg_data_byte[4][2];
data_byte[5] <= reg_data_byte[5][2];
data_byte[6] <= reg_data_byte[6][2];
data_byte[7] <= reg_data_byte[7][2];
end
//----reg_data_byte----------------------
always@(posedge clk or negedge rst_n)begin
if(!rst_n)begin
START <= 3'd0;
reg_data_byte[0] <= 3'd0;
reg_data_byte[1] <= 3'd0;
reg_data_byte[2] <= 3'd0;
reg_data_byte[3] <= 3'd0;
reg_data_byte[4] <= 3'd0;
reg_data_byte[5] <= 3'd0;
reg_data_byte[6] <= 3'd0;
reg_data_byte[7] <= 3'd0;
STOP <= 3'd0;
end
else if(baud_clk)begin
case(baud_cnt) //0:initial baud_cnt=0 --> after sending data, rs232_tx always be 0;
0: begin
START <= 3'd0;
reg_data_byte[0] <= 3'd0;
reg_data_byte[1] <= 3'd0;
reg_data_byte[2] <= 3'd0;
reg_data_byte[3] <= 3'd0;
reg_data_byte[4] <= 3'd0;
reg_data_byte[5] <= 3'd0;
reg_data_byte[6] <= 3'd0;
reg_data_byte[7] <= 3'd0;
STOP <= 3'd0;
end
6,7,8,9,10,11: START <= START + rs232_rx_reg2;
22,23,24,25,26,27: reg_data_byte[0] <= reg_data_byte[0] + rs232_rx_reg2;
38,39,40,41,42,43: reg_data_byte[1] <= reg_data_byte[1] + rs232_rx_reg2;
54,55,56,57,58,59: reg_data_byte[2] <= reg_data_byte[2] + rs232_rx_reg2;
70,71,72,73,74,75: reg_data_byte[3] <= reg_data_byte[3] + rs232_rx_reg2;
86,87,88,89,90,91: reg_data_byte[4] <= reg_data_byte[4] + rs232_rx_reg2;
102,103,104,105,106,107: reg_data_byte[5] <= reg_data_byte[5] + rs232_rx_reg2;
118,119,120,121,122,123: reg_data_byte[6] <= reg_data_byte[6] + rs232_rx_reg2;
130,131,132,133,134,135: reg_data_byte[7] <= reg_data_byte[7] + rs232_rx_reg2;
148,149,150,151,152,153: STOP <= STOP + rs232_rx_reg2;
default begin
START <= START;
reg_data_byte[0] <= reg_data_byte[0];
reg_data_byte[1] <= reg_data_byte[1];
reg_data_byte[2] <= reg_data_byte[2];
reg_data_byte[3] <= reg_data_byte[3];
reg_data_byte[4] <= reg_data_byte[4];
reg_data_byte[5] <= reg_data_byte[5];
reg_data_byte[6] <= reg_data_byte[6];
reg_data_byte[7] <= reg_data_byte[7];
STOP <= STOP;
end
endcase
end
end
endmodule
//------------------------------------------------
//----testbench-----------------------------------
`timescale 1ns/1ns
`define clk_period 20
module uart_rx_r0_tb;
reg clk;
reg rst_n;
reg [7:0] data_byte_tx;
wire rx_done;
reg send_en;
reg [2:0] baud_set;
wire [7:0] data_byte_rx;
wire rs232_rx;
wire tx_done;
//wire uart_state;
wire led;
uart_rx_r0 uut_rx(
.clk(clk),
.rst_n(rst_n),
.rs232_rx(rs232_rx),
.baud_set(baud_set),
.rx_done(rx_done),
.data_byte(data_byte_rx)
);
uart_tx_r0 uut_tx(
.clk(clk),
.rst_n(rst_n),
.send_en(send_en),
.baud_set(baud_set),
.tx_done(tx_done),
.data_byte(data_byte_tx),
.uart_state(led),
.rs232_tx(rs232_rx)
);
initial begin
clk = 1;
rst_n = 0;
send_en = 0;
baud_set = 3'd4;
#(`clk_period*20+1);
rst_n = 1;
#(`clk_period*50);
data_byte_tx = 8'haa;//8'b1010_1010
send_en = 1;
#`clk_period;
send_en = 0;
@(posedge tx_done)
#(`clk_period*5000);
data_byte_tx = 8'h55;//0101_0101
send_en = 1;
#`clk_period;
send_en = 0;
@(posedge tx_done)
#(`clk_period*5000);
$stop;
end
always begin #(`clk_period/2) clk = ~clk;end
endmodule
3. Modelsim simulation
4. FPGA board-level verification
- The serial port assistant sends data, ISSP software receives the data in real time.
----The learning content comes from the video of Xiaomei Ge FPGA
[Note]: Personal study notes, if there are any mistakes, please feel free to enlighten me. This is polite~~~