FPGA UART串口设计与上板

目录

基本框架

代码设计

上板验证


基本框架

使用黑金 ARTIX-7 系列FPGA 开发平台(型号:AX7102,数据手册详见官网)实现UART接口RTL代码的设计,然后简单上板测试。可以配置协议数据位宽,校验类型,停止位宽度和波特率。

对于RX接收模块,使用串口调试工具发送数据,FPGA接收数据后,根据低4位数据点亮FPGA上的LED灯。

对于TX发送模块,为了简单测试模块的功能,在复位后简单打拍几下,将测试数据发送到串口调试工具上。

代码设计

代码主要包括三个模块和一个IP核。

IP核为一个分频模块,将FPGA的200M差分信号晶振分频为5M的时钟信号(主要为了后续项目,可以直接使用差分信号或者任意分频频率)。

一个UART发送模块uart_tx.v

module uart_tx#(
    parameter                       DATA_WIDTH  =   4'd8        ,
    parameter                       CHECKSUM    =   2'd2        , // 0:无校验位 1:奇校验 2:偶校验
    parameter                       STOP_WIDTH  =   2'd2        , // 1:1个停止位 2:2个停止位
    parameter                       BAUD        =   11520000    ,
    parameter                       FREQUENCY   =   50000000    )
(   input                           i_clk                       ,
    input                           i_rst                       ,
    input   [DATA_WIDTH - 1 : 0]    i_data                      ,
    input                           i_en                        ,
    output                          o_tx                        ,
    output                          o_valid                     );

/*--------------------------------parameter------------------------------*/
localparam  CLK     =   FREQUENCY/BAUD - 1                      ;

localparam  IDLE    =   8'b0000_0001                            ,
            START   =   8'b0000_0010                            ,
            TX      =   8'b0000_0100                            ,
            CHECK   =   8'b0000_1000                            ,
            STOP    =   8'b0001_0000                            ;

/*----------------------------------wire---------------------------------*/
wire [1 : 0] w_check_mode;

/*----------------------------------reg----------------------------------*/
reg [7 : 0] r_state;
reg [7 : 0] r_state_next;
reg [15: 0] r_clk_cnt;
reg [3 : 0] r_tx_cnt;
reg [1 : 0] r_stop_cnt;
reg         r_tx;
reg [DATA_WIDTH - 1 : 0] r_data;
reg         r_div_clk; // 仿真时看波形的,无实际意义
/*---------------------------------assign--------------------------------*/
assign o_valid = (r_state == IDLE);
assign o_tx = r_tx;

assign w_check_mode = CHECKSUM;
/*---------------------------------always--------------------------------*/
always @(posedge i_clk or posedge i_rst) begin
    if (i_rst) begin
        r_data <= {DATA_WIDTH{1'b0}};
    end
    else if (i_en && (r_state == IDLE)) begin
        r_data <= i_data;
    end
end

always @(posedge i_clk or posedge i_rst) begin
    if (i_rst) begin
        r_state <= IDLE;
    end
    else begin
        r_state <= r_state_next;
    end
end

always @(*) begin
    case (r_state)
        IDLE    :begin
            if (i_en) begin
                r_state_next = START;
            end
            else begin
                r_state_next = r_state;
            end
        end
        START   :begin
            if (r_clk_cnt == CLK) begin
                r_state_next = TX;
            end
            else begin
                r_state_next = r_state;
            end
        end
        TX      :begin
            if ((r_clk_cnt == CLK) && (r_tx_cnt == (DATA_WIDTH - 1))) begin
                if (CHECKSUM == 2'd0) begin
                    r_state_next = STOP;
                end
                else begin
                    r_state_next = CHECK;
                end
            end
            else begin
                r_state_next = r_state;
            end
        end
        CHECK   :begin
            if (r_clk_cnt == CLK) begin
                r_state_next = STOP;
            end
            else begin
                r_state_next = r_state;
            end
        end
        STOP    :begin
            if ((r_clk_cnt == CLK) && (r_stop_cnt == (STOP_WIDTH - 1))) begin
                r_state_next = IDLE;
            end
            else begin
                r_state_next = r_state;
            end
        end
        default :begin
            r_state_next = IDLE;
        end
    endcase
end

always @(*) begin
    case (r_state)
        IDLE    :begin
            r_tx = 1'b1;
        end
        START   :begin
            r_tx = 1'b0;
        end
        TX      :begin
            r_tx = r_data[r_tx_cnt];
        end
        CHECK   :begin
            if (w_check_mode == 2'd1) begin // 奇校验
                r_tx = ~(^r_data);
            end
            else begin
                r_tx = ^r_data;
            end
        end
        STOP    :begin
            r_tx = 1;
        end
        default :begin
            r_tx = 1;
        end
    endcase
end

always @(posedge i_clk or posedge i_rst) begin
    if (i_rst) begin
        r_clk_cnt <= 16'b0;
    end
    else if (r_clk_cnt == CLK) begin
        r_clk_cnt <= 16'b0;
    end
    else if (r_state != IDLE) begin
        r_clk_cnt <= r_clk_cnt + 1;
    end
    else begin
        r_clk_cnt <= 16'b0;
    end
end

always @(posedge i_clk or posedge i_rst) begin
    if (i_rst) begin
        r_div_clk <= 1'b0;
    end
    else if (r_clk_cnt == CLK) begin
        r_div_clk <= ~r_div_clk;
    end
end

always @(posedge i_clk or posedge i_rst) begin
    if (i_rst) begin
        r_tx_cnt <= 4'b0;
    end
    else if ((r_state == TX) && (r_clk_cnt == CLK)) begin
        if (r_tx_cnt == (DATA_WIDTH - 1)) begin
            r_tx_cnt <= 4'b0;
        end
        else begin
            r_tx_cnt <= r_tx_cnt + 1;
        end
    end
    else if (r_state != TX) begin
        r_tx_cnt <= 4'b0;
    end
end

always @(posedge i_clk or posedge i_rst) begin
    if (i_rst) begin
        r_stop_cnt <= 2'b0;
    end
    else if ((r_state == STOP) && (r_clk_cnt == CLK)) begin
        if (r_stop_cnt == STOP_WIDTH - 1) begin
            r_stop_cnt <= 2'b0;
        end
        else begin
        r_stop_cnt <= r_stop_cnt + 1;
        end
    end
    else if (r_state != STOP) begin
        r_stop_cnt <= 2'b0;
    end
end

/*----------------------------------inst---------------------------------*/

endmodule

一个UART接收模块uart_rx.v

module uart_rx#(    
    parameter                       DATA_WIDTH  =   4'd8        ,
    parameter                       CHECKSUM    =   2'd0        , // 0:无校验位 1:奇校验 2:偶校验
    parameter                       STOP_WIDTH  =   2'd2        , // 1:1个停止位 2:2个停止位
    parameter                       BAUD        =   115_200     ,
    parameter                       FREQUENCY   =   5_000_000   )
(   input                           i_clk                       ,
    input                           i_rst                       ,
    input                           i_data                      ,
    output  [DATA_WIDTH - 1 : 0]    o_rx                        ,
    output                          o_done                      ,
    output                          o_error                     );

/*--------------------------------parameter------------------------------*/
localparam  CLK         =   FREQUENCY/BAUD - 1                  ;
localparam  CLK_SAMPLE  =   FREQUENCY/(2*BAUD) - 1              ;

localparam  IDLE        =   8'b0000_0001                        ,
            START       =   8'b0000_0010                        ,
            RX          =   8'b0000_0100                        ,
            CHECK       =   8'b0000_1000                        ,
            STOP        =   8'b0001_0000                        ;
/*----------------------------------wire---------------------------------*/
wire w_data_negedge;
wire [1 : 0] w_checksum_mode;
wire w_sample_en;
/*----------------------------------reg----------------------------------*/
reg [DATA_WIDTH - 1 : 0] r_rx;
reg [7 : 0] r_state;
reg [7 : 0] r_state_next;

reg r_data_d1; // 判断下降沿

reg r_check;
reg r_error;

reg [15: 0] r_clk_cnt;
reg r_div_clk;
reg [3 : 0] r_rx_cnt;

/*---------------------------------assign--------------------------------*/
assign o_rx = r_rx;
assign o_done = (r_state == STOP);
assign o_error = r_error;

assign w_data_negedge = (r_state == IDLE) ? ((~i_data) & r_data_d1) : 1'b0;
assign w_checksum_mode = CHECKSUM;
assign w_sample_en = ((r_state == RX) || (r_state == CHECK)) ? (r_clk_cnt == CLK_SAMPLE) : 1'b0;
/*---------------------------------always--------------------------------*/
always @(posedge i_clk or posedge i_rst) begin
    if (i_rst) begin
        r_state <= IDLE;
    end
    else begin
        r_state <= r_state_next;
    end
end

always @(*) begin
    case (r_state)
        IDLE    :begin
            if (w_data_negedge) begin
                r_state_next = START;
            end
            else begin
                r_state_next = r_state;
            end
        end
        START   :begin
            if (r_clk_cnt == CLK) begin
                r_state_next = RX;
            end
            else begin
                r_state_next = r_state;
            end
        end
        RX      :begin
            if ((r_clk_cnt == CLK) && (r_rx_cnt == (DATA_WIDTH - 1))) begin
                if (w_checksum_mode == 2'd0) begin
                    r_state_next = STOP;
                end
                else begin
                    r_state_next = CHECK;
                end
            end
            else begin
                r_state_next = r_state;
            end
        end
        CHECK   :begin
            if (r_clk_cnt == CLK) begin
                r_state_next = STOP;
            end
            else begin
                r_state_next = r_state;
            end
        end
        STOP    :begin
            if (r_clk_cnt == CLK) begin
                r_state_next = IDLE;
            end
            else begin
                r_state_next = r_state;
            end
        end
        default :begin
            r_state_next = IDLE;
        end
    endcase
end

always @(posedge i_clk) begin
    case (r_state)
        IDLE    :begin
            r_rx <= {DATA_WIDTH{1'b0}};
        end
        START   :begin
            r_rx <= r_rx;
        end
        RX      :begin
            if (w_sample_en) begin
                r_rx[r_rx_cnt] <= i_data;
            end
        end
        CHECK   :begin
            r_rx <= r_rx;
        end
        STOP    :begin
             r_rx <= r_rx;
        end
        default :begin
            r_rx <= {DATA_WIDTH{1'b0}};        
        end
    endcase
end

always @(posedge i_clk or posedge i_rst) begin
    if (i_rst) begin
        r_clk_cnt <= 16'b0;
    end
    else if (r_clk_cnt == CLK) begin
        r_clk_cnt <= 16'b0;
    end
    else if (r_state != IDLE) begin
        r_clk_cnt <= r_clk_cnt + 1;
    end
    else begin
        r_clk_cnt <= 16'b0;
    end
end

always @(posedge i_clk or posedge i_rst) begin
    if (i_rst) begin
        r_check <= 1'b0;
    end
    else if ((r_state == CHECK) && w_sample_en && (w_checksum_mode != 2'd0)) begin
        r_check <= i_data;
    end
end

always @(posedge i_clk or posedge i_rst) begin
    if (i_rst) begin
        r_div_clk <= 1'b0;
    end
    else if (r_clk_cnt == CLK) begin
        r_div_clk <= ~r_div_clk;
    end
end

always @(posedge i_clk or posedge i_rst) begin
    if (i_rst) begin
        r_rx_cnt <= 4'b0;
    end
    else if ((r_state == RX) && (r_clk_cnt == CLK)) begin
        if (r_rx_cnt == (DATA_WIDTH - 1)) begin
            r_rx_cnt <= 4'b0;
        end
        else begin
            r_rx_cnt <= r_rx_cnt + 1;
        end
    end
    else if (r_state != RX) begin
        r_rx_cnt <= 4'b0;
    end
end

always @(posedge i_clk or posedge i_rst) begin
    if (i_rst) begin
        r_data_d1 <= 1'b0;
    end
    else begin
        r_data_d1 <= i_data;
    end
end

always @(*) begin
    if (r_state == STOP) begin
        if (w_checksum_mode == 2'd0) begin
            r_error = 1'b0;
        end
        else if (w_checksum_mode == 2'd1) begin
            if (r_check != (^r_rx)) begin
                r_error = 1'b0;
            end
            else begin
                r_error = 1'b1;
            end
        end
        else if (w_checksum_mode == 2'd2) begin
            if (r_check == (^r_rx)) begin
                r_error = 1'b0;
            end
            else begin
                r_error = 1'b1;
            end
        end
    end
    else begin
        r_error = 1'b0;
    end
end
/*----------------------------------inst---------------------------------*/

endmodule

最后是顶层模块top.v

在顶层模块里进行例化和参数配置。同时进行异步复位同步释放和发送模块的简单测试。

module top(
    input                       i_sys_clk_p     , // 200M
    input                       i_sys_clk_n     , // 200M
    input                       i_sys_rstn      ,
    input                       i_data_uart     , // uart_rx
    output                      o_uart_tx       ,
    output                      o_LED0          ,
    output                      o_LED1          ,
    output                      o_LED2          ,
    output                      o_LED3          );


/*--------------------------------parameter------------------------------*/
parameter                       DATA_WIDTH  =   4'd8        ;
parameter                       CHECKSUM    =   2'd0        ;
parameter                       STOP_WIDTH  =   2'd2        ;
parameter                       BAUD        =   115200      ;
parameter                       FREQUENCY   =   5_000_000   ;
/*----------------------------------wire---------------------------------*/
wire w_clk_5M; // 5M
wire w_rst_syn;
wire w_uart_rx_done;
wire w_uart_done_posedge; // rx_done信号上升沿
wire [DATA_WIDTH - 1:0] w_uart_data;
wire w_uart_error;
wire w_uart_tx_valid;
/*----------------------------------reg----------------------------------*/
reg [7:0] r_cnt;
reg r_rstn_d1;
reg r_rstn_syn; // 异步复位同步释放
reg [DATA_WIDTH - 1:0] r_uart_data;
reg r_uart_done_d1;
reg [DATA_WIDTH - 1:0] r_uart_tx;
reg r_uart_tx_en;
/*---------------------------------assign--------------------------------*/
assign w_rst_syn = ~r_rstn_syn; // 高电平复位
assign o_LED0 = r_uart_data[0];
assign o_LED1 = r_uart_data[1];
assign o_LED2 = r_uart_data[2];
assign o_LED3 = r_uart_data[3];
assign w_uart_done_posedge = w_uart_rx_done & (~r_uart_done_d1);
/*---------------------------------always--------------------------------*/
always @(posedge w_clk_5M or negedge r_rstn_syn) begin
    if (!r_rstn_syn) begin
        r_cnt <= 8'b0;
        r_uart_tx <= {DATA_WIDTH{1'b1}};
        r_uart_tx_en <= 1'b0;
    end
    else if ((r_cnt == 8'd100) && w_uart_tx_valid) begin
        r_uart_tx_en <= 1'b1;
    end
    else if (r_cnt <= 8'd200) begin
        r_cnt <= r_cnt + 1;
        r_uart_tx_en <= 1'b0;
    end
end

always @(posedge w_clk_5M or negedge r_rstn_syn) begin
    if (!r_rstn_syn) begin
        r_uart_done_d1 <= 1'b0;
    end
    else begin
        r_uart_done_d1 <= w_uart_rx_done;
    end
end

always @(posedge w_clk_5M or negedge r_rstn_syn) begin
    if (!r_rstn_syn) begin
        r_uart_data <= {DATA_WIDTH{1'b1}};
    end
    else if (w_uart_error) begin
        r_uart_data <= {DATA_WIDTH{1'b1}};
    end
    else if (w_uart_done_posedge) begin
        r_uart_data <= w_uart_data;
    end
end

always @(posedge w_clk_5M or negedge i_sys_rstn) begin
    if (!i_sys_rstn) begin
        r_rstn_d1 <= 1'b0;
        r_rstn_syn <= 1'b0;
    end
    else begin
        r_rstn_d1 <= i_sys_rstn;
        r_rstn_syn <= r_rstn_d1;
    end
end
/*----------------------------------inst---------------------------------*/
uart_tx #(
    .DATA_WIDTH (DATA_WIDTH         ),
    .CHECKSUM   (CHECKSUM           ),
    .STOP_WIDTH (STOP_WIDTH         ),
    .BAUD       (BAUD               ),
    .FREQUENCY  (FREQUENCY          ))
    uart_tx_inst(   
    .i_clk      (w_clk_5M           ),
    .i_rst      (w_rst_syn          ),
    .i_data     (r_uart_tx          ),
    .i_en       (r_uart_tx_en       ),
    .o_tx       (o_uart_tx          ),
    .o_valid    (w_uart_tx_valid    ));


uart_rx #(
    .DATA_WIDTH (DATA_WIDTH         ),
    .CHECKSUM   (CHECKSUM           ),
    .STOP_WIDTH (STOP_WIDTH         ),
    .BAUD       (BAUD               ),
    .FREQUENCY  (FREQUENCY          ))
    uart_rx_inst(   
    .i_clk      (w_clk_5M           ),
    .i_rst      (w_rst_syn          ),
    .i_data     (i_data_uart        ),
    .o_rx       (w_uart_data        ),
    .o_done     (w_uart_rx_done     ),
    .o_error    (w_uart_error       ));

clk_wiz_0 clock_div(    
    .clk_in1_n  (i_sys_clk_n        ),               
    .clk_in1_p  (i_sys_clk_p        ),               
    .resetn     (i_sys_rstn         ),           
    .clk_out1   (w_clk_5M           ));

endmodule

上板验证

首先进行管脚的约束,具体内容参考官方手册。

这里遇到一个问题,对于差分信号我们只需要约束p管脚就行,好像会自动约束n管脚。

set_property PACKAGE_PIN T6 [get_ports i_sys_rstn]
set_property IOSTANDARD LVCMOS15 [get_ports i_sys_rstn]

set_property PACKAGE_PIN R4 [get_ports i_sys_clk_p]
set_property IOSTANDARD DIFF_SSTL15 [get_ports i_sys_clk_p]

set_property PACKAGE_PIN Y12 [get_ports i_data_uart]
set_property IOSTANDARD LVCMOS33 [get_ports i_data_uart]

set_property PACKAGE_PIN C17 [get_ports o_LED0]
set_property IOSTANDARD LVCMOS33 [get_ports o_LED0]
set_property PACKAGE_PIN D17 [get_ports o_LED1]
set_property IOSTANDARD LVCMOS33 [get_ports o_LED1]
set_property PACKAGE_PIN V20 [get_ports o_LED2]
set_property IOSTANDARD LVCMOS33 [get_ports o_LED2]
set_property PACKAGE_PIN U20 [get_ports o_LED3]
set_property IOSTANDARD LVCMOS33 [get_ports o_LED3]

set_property PACKAGE_PIN Y11 [get_ports o_uart_tx]
set_property IOSTANDARD LVCMOS33 [get_ports o_uart_tx]

最后将比特流进行烧录,使用micro USB连接uart端口,在Microsoft Store下载了一个串口调试助手,然后需要去下载一个驱动,就可以进行数据的收发。

在收发的过程中,将数据类型选为16进制显示,比如发送一个8位aa,LED0和LED2会点亮。按一下复位键,PC就会接收到FPGA发送的ff数据。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值