[原创] 【Perf-V评测】之UART

eew_3sqZMg   2021-2-4 21:50 楼主

UART对工程师来说实在是太重要了,现在大大小小各种模块都用串口来调试,串口也是最方便省事的一种接口了。今天就来用澎峰的板子调试一下串口通讯。手册资料里给的串口是基于软核CPU之上C语言开发的,这次的测试打算基于FPGA裸板,用verilog来测试。

UART通讯协议的基本原理如下:

image.png

此次使用串口转USB cp2102模块,该模块是CMOS电平,输入高最小值是2.0V,输入低最大值是0.8V,因此FPGA引脚选择LVCMOS3.3v电平与其连接。将该模块的TX同开发板的D0号引脚rx连接,将RX同开发板的D1号引脚tx连接,接地同开发板的地线连接。

image.png

代码共分三大模块,借鉴黑金开发板代码,做了适量修改,包括发送数据模块uart_tx,接收数据模块rx_uart,顶层模块uart_top,顶层模块实现对发送接收模块的实例化与调用。

传输数据格式包括1位起始位,8位数据位,1位停止位,无校验位。传输波特率位115200.

外部时钟输入为50Mhz,依然采用系统时钟输入引脚n14,原代码中的差分时钟改成了单时钟输入。发送、接收模块都采用状态机实现。

接收模块的代码如下:

`timescale 1ns / 1ps
/*
模块功能简介
接收来自rx_pin 的 uart 数据v
将接收到的数据完成后,将我们的数据的rx_data_valid 置位有效

# 然后将收到的数据 通过rx_data 发送出去


*/


module uart_rx

#( 
    parameter clk_fre = 50,
    parameter baud_rate = 115200
)
(

    input clk,
    input rst_n,
    
    input rx_pin,
    
        
    input rx_data_ready,
    
    output reg rx_data_valid,
    output reg [7:0] rx_data
);
//calculates the clock cycle for baud rate 
localparam cycle = clk_fre * 1000000/baud_rate;
parameter rx_idle=3'b000;
parameter rx_start=3'b001;
parameter rx_rcv_byte=3'b010;
parameter rx_stop=3'b011;
parameter rx_data_state =3'b100;

reg [2:0]rx_state=0;
reg [2:0]next_stae;
reg rx_d0;
reg rx_d1;

wire rx_negedge;
reg [7:0] rx_bits=0;
reg [15:0] cycle_cnt=0;
reg [2:0] bit_cnt=0;

assign rx_negedge = rx_d1 && ~rx_d0;


// This part is for detecting the start of the translation f
always @(posedge clk or negedge rst_n)
begin
    if (rst_n ==1'b0)
        begin
            rx_d0 <=0;
            rx_d1 <=0;
        
        end
    else 
    begin
        rx_d0 <= rx_pin;
        rx_d1 <= rx_d0; 
    end
end


always@(posedge clk or negedge rst_n)
begin
    if (rst_n == 1'b0)
        begin
            rx_state <= rx_idle;
            rx_data_valid <= 1'b0;
            
        end
    else 
        begin
            case(rx_state)
            rx_idle:
                begin
                    if(rx_negedge == 1'b1)
                        rx_state <= rx_start;
                    else
                        rx_state <= rx_idle;
                 end
            rx_start:
                begin
                    if(cycle_cnt == cycle -1)
                        begin
                            rx_state <= rx_rcv_byte;
                            cycle_cnt <= 16'd0;
                        end
                    else
                        begin
                            rx_state <= rx_start;
                            cycle_cnt <= cycle_cnt + 16'd1;
                        end
                end
            rx_rcv_byte:
                begin
                    if(cycle_cnt == cycle - 1)
                    //分成两种情况,达到8个bit/没到8个字节
                    begin
                        if(bit_cnt == 3'd7)
                            begin
                                rx_state <= rx_stop;
                                bit_cnt <= 3'd0;
                                cycle_cnt <= 16'd0;
                            end
                        else
                            begin
                                bit_cnt <= bit_cnt + 3'd1;
                                cycle_cnt <= 16'd0;
                            end
                        
                    end
                    else if (cycle_cnt == cycle/2 -1)
                        begin
                            rx_bits[bit_cnt] <= rx_pin;
                            cycle_cnt <= cycle_cnt + 16'd1;
                        end
                    else
                        cycle_cnt <= cycle_cnt + 16'd1;
                        
                end
            rx_stop:
                begin
                
                    if(cycle_cnt == cycle/2 -1)
                        begin
                            rx_state <= rx_data_state;      
                            rx_data <= rx_bits;//latch received data
                            cycle_cnt <= 16'd0;    
                            rx_data_valid <=1'b1;
                            
                        end
                    else
                        begin   
                            rx_state <= rx_stop;
                            rx_data_valid <=1'b0;
                            cycle_cnt <= cycle_cnt + 16'd1;
                        end
                end
            rx_data_state:
                begin                   
                    
                    if(rx_data_ready) // 低电平时候,就一直处在则个等待数据接收的状态
                        begin
                            rx_state <= rx_idle;
                            rx_data_valid <=1'b0;
                        end
                    else
                        begin                   
                            rx_data_valid <=1'b1;
                            rx_state <= rx_data_state;
                        end
                end
          default:
                rx_state <= rx_idle;
        endcase
    end
end

endmodule

发送模块的代码如下:

module uart_tx

#( 
    parameter clk_fre = 50,
    parameter baud_rate = 115200
)
(

    input clk,
    input rst_n,
    input [7:0]tx_data,
    input tx_data_valid,
    
    
    output tx_pin,
    output reg tx_data_ready
    
);
//calculates the clock cycle for baud rate 
localparam cycle = clk_fre * 1000000/baud_rate;
parameter s_idle=3'b000;
parameter s_start=3'b001;
parameter s_send_byte=3'b010;
parameter s_stop=3'b011;

reg [2:0]state=0;
reg [2:0]next_state;

reg [15:0] cycle_cnt=0;
reg [2:0] bit_cnt=0;
reg [7:0] tx_data_latch;
reg tx_reg=1;
assign tx_pin = tx_reg;


always@(posedge clk or negedge rst_n)
begin
    if (rst_n == 1'b0)
        begin
            state <= s_idle;
        end
    else 
        begin
            case(state)
            s_idle:
                begin
                    if(tx_data_valid == 1'b1)
                        begin
                            state <= s_start;
                            tx_data_latch <= tx_data;
                            tx_data_ready <= 1'b0;
                        end
                    else
                        begin
                            tx_reg <= 1'b1;
                            state <=s_idle;
                            tx_data_ready <= 1'b1;
                        end
                 end
            s_start:
                begin
                    if(cycle_cnt == cycle -1)
                        begin
                            state <= s_send_byte;
                            cycle_cnt <= 16'd0;
                        end
                    else
                        begin
                            cycle_cnt <= cycle_cnt + 16'd1;
                            tx_reg <= 1'b0;
                            state <= s_start;
                            
                        end
                end
            s_send_byte:
                begin
                    if(cycle_cnt == cycle - 1)
                        //分成两种情况,达到8个bit/没到8个字节
                        begin
                            if(bit_cnt == 3'd7)
                                begin
                                    state <= s_stop;
                                    bit_cnt <= 3'd0;
                                    cycle_cnt <= 16'd0;
                                end
                            else
                                begin
                                    bit_cnt <= bit_cnt + 3'd1;
                                    cycle_cnt <= 16'd0;
                                end
                            
                        end
                    else
                        begin
                            cycle_cnt <= cycle_cnt + 16'd1;
                            tx_reg = tx_data_latch[bit_cnt];
                        end
                end
            s_stop:
                begin
                    if(cycle_cnt == cycle -1)
                        begin
                            tx_data_ready <= 1'b1;   
                            state <= s_idle;      
                            cycle_cnt <= 16'd0;    
                        end
                    else
                        begin   
                            tx_reg <= 1'b1;
                            cycle_cnt <= cycle_cnt + 16'd1;
                        end
                end
          default:
               state <= s_idle;
        endcase
    end
end

endmodule

顶层模块的代码:

`timescale 1ns / 1ps
//
// Company: 
// Engineer: 
// 
// Create Date: 2020/07/31 12:20:32
// Design Name: 
// Module Name: uart_top
// Project Name: 
// Target Devices: 
// Tool Versions: 
// Description: 
// 
// Dependencies: 
// 
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
// 
//


module uart_top
(
input                          sys_clk,// sys_clk_p,         //system clock positive
//input                           sys_clk_n,         //system clock negative 
input                           rst_n,             //reset ,low active
input                           uart_rx,           //fpga receive data
output                          uart_tx            //fpga send data
);
parameter                       clk_fre = 50;    //Mhz
localparam                       IDLE =  0;
localparam                       SEND =  1;         //send HELLO ALINX\r\n
localparam                       WAIT =  2;         //wait 1 second and send uart received data
reg[7:0]                         tx_data;          //sending data
reg[7:0]                         tx_str;
reg                              tx_data_valid;    //sending data valid
wire                             tx_data_ready;    //singal for sending data
reg[7:0]                         tx_cnt=0; 
wire[7:0]                        rx_data;          //receiving data
wire                             rx_data_valid;    // receiving data valid
wire                             rx_data_ready;    // singal for receiving data
reg[31:0]                        wait_cnt=0;
reg[3:0]                         state=0;          
wire                             sys_clk;          //single end clock
/*************************************************************************
generate single end clock
**************************************************************************/
/*IBUFDS sys_clk_ibufgds   
(
.O                      (sys_clk                  ),
.I                      (sys_clk_p                ),
.IB                     (sys_clk_n                )
);*/
assign rx_data_ready = 1'b1;//always can receive data,
                            //if HELLO ALINX\r\n is being sent, the received data is discarded
/*************************************************************************
1 second sends a packet HELLO ALINX\r\n , FPGA has been receiving state
****************************************************************************/
always@(posedge sys_clk or negedge rst_n)
begin
    if(rst_n == 1'b0)
    begin
        wait_cnt <= 32'd0;
        tx_data <= 8'd0;
        state <= IDLE;
        tx_cnt <= 8'd0;
        tx_data_valid <= 1'b0;
    end
    else
    case(state)
        IDLE:
            state <= SEND;
        SEND:
        begin
            wait_cnt <= 32'd0;
            tx_data <= tx_str;

            if(tx_data_valid == 1'b1 && tx_data_ready == 1'b1 && tx_cnt < 8'd12)//Send 12 bytes data
            begin
                tx_cnt <= tx_cnt + 8'd1; //Send data counter
            end
            else if(tx_data_valid && tx_data_ready)//last byte sent is complete
            begin
                tx_cnt <= 8'd0;
                tx_data_valid <= 1'b0;
                state <= WAIT;
            end
            else if(~tx_data_valid)
            begin
                tx_data_valid <= 1'b1;
            end
        end
        WAIT:
        begin
            wait_cnt <= wait_cnt + 32'd1;
            // 等待1s 数据的时候
            if(rx_data_valid == 1'b1) // 如果接收到数据
            begin
                tx_data_valid <= 1'b1; //使得发送数据使能,
                tx_data <= rx_data;   // send uart received data
            end
            else if(tx_data_valid && tx_data_ready)
            begin
                tx_data_valid <= 1'b0;
            end
            else if(wait_cnt >= clk_fre * 1000000) // wait for 1 second
                state <= SEND;
        end
        default:
            state <= IDLE;
    endcase
end
/*************************************************************************
combinational logic  Send "HELLO PERF-\r\n"
****************************************************************************/
always@(*)
begin
    case(tx_cnt)
        8'd0 :  tx_str <= "H";
        8'd1 :  tx_str <= "E";
        8'd2 :  tx_str <= "L";
        8'd3 :  tx_str <= "L";
        8'd4 :  tx_str <= "O";
        8'd5 :  tx_str <= " ";
        8'd6 :  tx_str <= "P";
        8'd7 :  tx_str <= "E";
        8'd8 :  tx_str <= "R";
        8'd9 :  tx_str <= "F";
        8'd10:  tx_str <= "-";
        8'd11:  tx_str <= "\r";
        8'd12:  tx_str <= "\n";
        default:tx_str <= 8'd0;
    endcase
end
/***************************************************************************
calling uart_tx module and uart_rx module
****************************************************************************/
uart_rx#
(
.clk_fre(clk_fre),
.baud_rate(115200)
) uart_rx_inst
(
.clk                        (sys_clk                  ),
.rst_n                      (rst_n                    ),
.rx_data                    (rx_data                  ),
.rx_data_valid              (rx_data_valid            ),
.rx_data_ready              (rx_data_ready            ),
.rx_pin                     (uart_rx                  )
);

uart_tx#
(
.clk_fre(clk_fre),
.baud_rate(115200)
) uart_tx_inst
(
.clk                        (sys_clk                  ),
.rst_n                      (rst_n                    ),
.tx_data                    (tx_data                  ),
.tx_data_valid              (tx_data_valid            ),
.tx_data_ready              (tx_data_ready            ),
.tx_pin                     (uart_tx                  )
);

endmodule
引脚约束代码:

set_property IOSTANDARD LVCMOS33 [get_ports rst_n]
set_property IOSTANDARD LVCMOS33 [get_ports uart_rx]
set_property IOSTANDARD LVCMOS33 [get_ports uart_tx]
set_property PACKAGE_PIN M15 [get_ports rst_n]
set_property PACKAGE_PIN N6 [get_ports uart_rx]
set_property PACKAGE_PIN M6 [get_ports uart_tx]
set_property DRIVE 12 [get_ports uart_tx]

 

image.png

image.png image.png

本帖最后由 eew_3sqZMg 于 2021-2-4 21:50 编辑
  • image.png

回复评论 (3)

谢谢分享

 

点赞  2021-2-5 10:14

感谢分享,只是代码有些长哈。打包一下更好呢!

点赞  2021-2-5 18:35

看不懂,fpga没学精通。。。

默认摸鱼,再摸鱼。2022、9、28
点赞  2021-2-5 21:47
电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 京公网安备 11010802033920号
    写回复