基于FPGA的UART回环设计(1)

UART回环设计系统框图

首先介绍一下接下来几篇博客是设计一个串口循环的小项目,串口的电路与详细的协议这里不再介绍。详细绝大多数同学都明白。本次使用的硬件环境与软件环境如下:

FPGA开发板:米联客MA7035FA 100T版本
软件环境:vivado2019.1

我们要养好良好的FPGA设计习惯,首先是进行模块化设计画出实验的框图,然后对实验框图中的每个小模块进行时序设计。只有养成了良好的设计习惯才可能在复杂项目中不至于茫然无措,本次实验的实验框图如下:
在这里插入图片描述
通过观察实验框图可以看出此次项目需要设计两个模块,分别是uart_rx和uart_tx模块。这篇文章我们主要设计uart_rx模块,设计的详细过程如下。

uart_rx模块的时序图

因为FPGA程序特别考验逻辑性,没有好的逻辑几乎寸步难行,所以一个很好的方法是先把模块的时序图画出来。所以,uart_rx模块的时序图如下:
在这里插入图片描述
从时序图中可以看出,我们采用的主要是线性序列机的设计方法,有关线性序列机的概念我们这里不多赘述。

uart_rx模块的代码实现

相信有了上述的时序图,各位同学们就很容易可以设计出来相应的代码了。这里废话不多说,直接附上我写的代码,大家对应时序图和代码可以很容易看懂:

`timescale 1ns / 1ps
// *********************************************************************************
// Project Name : OSXXXX
// Author       : zhangningning
// Email        : [email protected]
// Website      : 
// Module Name  : uart_tx.v
// Create Time  : 2020-01-04 13:01:46
// Editor       : sublime text3, tab size (4)
// CopyRight(c) : All Rights Reserved
//
// *********************************************************************************
// Modification History:
// Date             By              Version                 Change Description
// -----------------------------------------------------------------------
// XXXX       zhangningning          1.0                        Original
//  
// *********************************************************************************
   

module uart_rx(
    input                   sclk            ,
    input                   rst_n           ,
    input                   rx              ,
    output  reg             po_flag         ,
    output  reg     [ 7:0]  po_data                
);
 
//========================================================================================\
//**************Define Parameter and  Internal Signals**********************************
//========================================================================================/
parameter   BAUD_NUM        =       433     ;       

reg                         rx1             ;
reg                         rx2             ;
reg                         rx2_reg         ;
reg                         rx_flag         ;
reg                 [14:0]  cnt_baud        ;
reg                 [ 3:0]  bit_cnt         ;
reg                         bit_flag        ;


 
//========================================================================================\
//**************     Main      Code        **********************************
//========================================================================================/
always @(posedge sclk) 
    rx1             <=          rx;

always @(posedge sclk)
    rx2             <=          rx1;
    
always @(posedge sclk)
    rx2_reg         <=          rx2; 
    
always @(posedge sclk or posedge rst_n)
    if(rst_n == 1'b0)
        rx_flag         <=          1'b0;
    else if(bit_cnt == 4'd8 && bit_flag == 1'b1)
        rx_flag         <=          1'b0;    
    else if(rx2 == 1'b0 && rx2_reg == 1'b1)
        rx_flag         <=          1'b1;
    else
        rx_flag         <=          rx_flag;

always @(posedge sclk or negedge rst_n)
    if(rst_n == 1'b0)
        cnt_baud        <=          'd0;
    else if(rx_flag == 1'b0 || cnt_baud == BAUD_NUM)
        cnt_baud        <=          'd0;
    else if(rx_flag == 1'b1) 
        cnt_baud        <=          cnt_baud + 1'b1;
    else
        cnt_baud        <=          cnt_baud;
        
always @(posedge sclk or negedge rst_n)
    if(rst_n == 1'b0)
        bit_flag        <=          1'b0;
    else if(cnt_baud == BAUD_NUM/2)
        bit_flag        <=          1'b1;
    else
        bit_flag        <=          1'b0;

always @(posedge sclk or negedge rst_n)
    if(rst_n == 1'b0)
        bit_cnt         <=          4'd0;
    else if(bit_cnt == 4'd8 && bit_flag == 1'b1)
        bit_cnt         <=          4'd0;
    else if(bit_flag)
        bit_cnt         <=          bit_cnt + 1'b1;
    else
        bit_cnt         <=          bit_cnt;

always @(posedge sclk or negedge rst_n)
    if(rst_n == 1'b0)
        po_data         <=          8'd0;
    else if(bit_flag == 1'b1) 
        po_data         <=          {rx2,po_data[7:1]};
    else
        po_data         <=          po_data;

always @(posedge sclk or negedge rst_n)
    if(rst_n == 1'b0)
        po_flag         <=          1'b0;
    else if(bit_cnt == 4'd8 && bit_flag == 1'b1) 
        po_flag         <=          1'b1;
    else
        po_flag         <=          1'b0;    
    

endmodule

强烈建议大家使用我上述的代码风格,对应时序图看简洁明了。

uart_rx测试模块的代码设计

如果只有上面的模块代码,对于一些刚入门的同学来说不容易观察也不容易调试,所以我将程序的测试代码附上。同学们联系两个程序,可以很容易的学会uart_rx模块的设计方法。

`timescale 1ns / 1ps
`define     CLOCK   20
// *********************************************************************************
// Project Name : OSXXXX
// Author       : zhangningning
// Email        : [email protected]
// Website      : 
// Module Name  : uart_tx_tb.v
// Create Time  : 2020-01-04 13:35:06
// Editor       : sublime text3, tab size (4)
// CopyRight(c) : All Rights Reserved
//
// *********************************************************************************
// Modification History:
// Date             By              Version                 Change Description
// -----------------------------------------------------------------------
// XXXX       zhangningning          1.0                        Original
//  
// *********************************************************************************

module uart_rx_tb();

reg                     sclk            ;
reg                     rst_n           ;
reg                     rx              ;
wire                    po_flag         ;
wire         [ 7:0]     po_data         ;

initial begin
    sclk        <=          1'b0;
    rst_n       =           1'b0;
    rx          =           1'b1;
    #(100*`CLOCK)
    rst_n       =           1'b1;
    data_send(0);
    data_send(1);
    data_send(2);
    data_send(3);
    data_send(4);
    data_send(5);

    data_send(10);
    data_send(11);
    data_send(12);
    data_send(13);
    data_send(14);
    data_send(15);

    data_send(100);
    data_send(101);
    data_send(102);
    data_send(103);
    data_send(104);
    data_send(105);
end
always  #(`CLOCK/2)         sclk        <=          ~sclk;

task  data_send ;
    input       [ 7:0]          data        ;
    integer                     i           ;           
    begin
        rx          <=          1'b0        ;
        #(433*`CLOCK);         
        for(i = 0;i <= 7;i = i+1)begin               
            rx      <=          data[i];
            #(433*`CLOCK); 
        end
         rx          <=          1'b1        ;
        #(433*`CLOCK); 
    end
            


endtask

uart_rx uart_rx_inst(
    .sclk               (sclk               ),
    .rst_n              (rst_n              ),
    .rx                 (rx                 ),
    .po_flag            (po_flag            ),
    .po_data            (po_data            )       
);

endmodule

测试模块使用了一个较常用的语法,即task语句。相信大家从上面的程序中可以学到task的使用规则。

结束语

对文章有什么看法或者需要更近一步交流的同学,可以加入下面的群:
在这里插入图片描述

发布了14 篇原创文章 · 获赞 4 · 访问量 597

猜你喜欢

转载自blog.csdn.net/zhangningning1996/article/details/103836599