Verilog UART串口详解(代码及整体思路)

目录

一、uart概述

二、uart数据格式

2.1 uart起始位的意义

2.2 停止位的意义

2.3 UART基本的数据形式

2.4 UART的数据位可变

三、uart的波特率

四、Verilog 模块划分

五、发送模块(TX)

5.1 发送模块状态跳变

5.2 Verilog 设计文件

5.3 仿真文件

5.4 测试结果

5.5 上板测试(tx_text)

六、接收模块(rx)

6.1 接收模块状态跳转

6.2 接收模块  Verilog 设计文件

6.3 仿真文件

6.5 上板测验(加入tx模块的串口回环)

七、uart_ctrl模块(fifo)

7.1 引入fifo作为数据缓冲器的意义

7.2 fifo IP的配置

7.3 uart_ctrl 设计文件

八、顶层整合

8.1 top文件

8.2 整体仿真

8.2.2 仿真波形分析

九、 上板检测


一、uart概述

UART的全称是通用异步收发器(Universal Asynchronous Receiver/Transmitter)

UART是一个通用的接口协议,被广泛的应用在各类MCU和SOC产品上。“不需要额外的时钟线进行数据的同步传输”,只要信号拉低,就可开始传送数据。UART 只需要两条信号线:RXDTXD(一个数据的发送方和一个数据的接收方 ),其中 RXD 是 UART 的接收端TXD 是 UART 的发送端,接收与发送是全双工形式。

它可以和各种标准串行接口,如 RS 232 和 RS 485 等进行全双工异步通信, 具有传输距离远、成本低、可靠性高等优点。一般 UART 由专用芯片如: 8250, 16450 来实现,但专用芯片引脚都较多,内含许多辅助功能,在实际使用时往往只需要用到 UART 的基本功能,使用专用芯片会造成资源浪费和成本提高。

二、uart数据格式

UART的一帧由四部分组成起始位(1bit)数据位(6\7\8bit)校验位(1bit)停止位(1\1.5\2bit),如下图所示,

uart 是将传输数据的每个字符一位接一位地传输。 其中每一位(Bit)的意义如下:

空闲位:高电平,表明当前无传输事务。

起始位:一位低电平信号,标志着数据传输的开始。

数据位:紧接着起始位之后。数据位的个数可以是 6、7、8 等,构 成一个字符。通常采用 ASCII 码。从最低位开始传送,依靠时钟定位。

奇偶校验位:数据位加上这一位后,使得“1”的位数应为偶数(偶校验)或 奇数(奇校验),以此来校验数据传送的正确性。

停止位:一个字符数据的结束标志。可以是 1 位、1.5 位、2 位的高电平。

由于数据是在传输线上定时的,并且每一个设备有其自己的时钟,很可能在通 信中两台设备间出现了小小的不同步。因此停止位不仅仅是表示传输的结束, 并且提供计算机校正时钟同步的机会。停止位的位数越多,不同时钟同步的容 忍程度越大,但是数据传输率同时也越慢。

2.1 uart起始位的意义

因为UART没有控制线,要让接收方知道什么时候开始接收数据,需要一些手段,UART数据的传输中,只有一根线,所以在发送数据之前,先发一位逻辑“0”作为数据发送的起始标志,接收方在空闲时,当检测到有一个低电平,则开始接逐位接收数据。

2.2 停止位的意义

因为通信两个机器之间不可避免的存在时钟偏移,为了消除数据交互之间时钟偏移对数据传输产生的影响,所以在每一次数据发送结束后,要发送一位逻辑“1”作为数据发送的结束标志,接收方在空闲时,当检测到有一个高电平,则结束接收一次数据。

2.3 UART基本的数据形式

  • 默认无传输数据时,为高电平

  • 当信号拉低,传输线上的电平拉低,意味着开始进行数据传输

  • 紧接着起始位的是数据位,它可以是5、6、7或8位

  • UART的“校验位”紧挨着“数据位”,采用奇偶校验方式,根据设置,校验位可以存在也可以不存在

  • UART将停止位作为停止标志,是在数据位(没有校验位)和校验位(有校验位)之后发送1~2位的逻辑“1”高电平。当发送完停止位之后,UART总线进入空闲。

2.4 UART的数据位可变

因为UART是一种低速总线,每多发一位都占用不少的时间(由传输波特率决定),所以可以根据传输数据的特点,采用不同位宽以节约数据传输的时间

三、uart的波特率

波特率等于每秒钟传输的数据位数

常用的波特率有:9600、19200、38400、57600、115200,本工程所用到的是115200。

计算传输1bit需要的时间如下表:假如我们的全局时钟频率为100MHz,波特率设置为9600,那么意味着每秒该UART传输协议可以传输9600bits的数据。传输1比特需时间约为:10^9(ns)/9600=104166(ns)。

四、Verilog 模块划分

uart 的设计分为两个部分,一部分为发送模块,另一部分为接收模块。程 序设计主要通过状态机实现,系统框架图如下:

  • 全局参数

    parameter                   CHECK_BIT =  "None"      ,    //校验位    “None”无校验 “Odd”奇校验,“Even”偶校验
    parameter                   BPS       =  115200      ,    //系统波特率
    parameter                   CLK       =  50_000_000  ,    //系统时钟频率
    parameter                   DATA_BIT  =  8           ,    //数据位(6、7、8)
    parameter                   STOP_BIT  =  1                //停止位

五、发送模块(TX)

5.1 发送模块状态跳变

IDLE:空闲状态,无数据传输,输出高电平,当tx_data_vld信号到来时跳转到START;

START:起始位,无数据传输,输出低电平为 ,无条件跳转至DATA;

DATA:数据位,进行数据传输,先发送低比特,根据数据输出高低电平,假如有校验位,跳到CHECK,假如数据传输不设校验位,跳转到STOP;

CHECK:奇偶校验位,

STOP:停止位,高电平,共 1 位。 发送模块在上一状态向下一状态切换时,改变电平

5.2 Verilog 设计文件

/**************************************功能介绍***********************************
Date    : 2023年9月18日21:37:53
Author  : linxiaoxiao.
Version : 
Description: uart串口发送模块
*********************************************************************************/
//例化模版
// tx_uart #(
//     .CHECK_BIT ( "None"      ),    //校验位    “None”无校验 “Odd”奇校验,“Even”偶校验
//     .BPS       ( 115200      ),    //系统波特率
//     .CLK       ( 50_000_000  ),    //系统时钟频率
//     .DATA_BIT  ( 8           ),    //数据位(6、7、8)
//     .STOP_BIT  ( 1           )     //停止位
// )tx_uart_inst( 
//     /* input                     */.clk         (),
//     /* input                     */.rst_n       (),
//     /* input                        */.tx_data_vld (),
//     /* input [DATA_BIT - 1:0]    */.tx_data     (),
​
//     /* output                       */.tx_ready    (),
//     /* output    reg                 */.tx          () 
// );       
​
//---------<模块及端口声名>------------------------------------------------------
module tx_uart#(
    parameter                   CHECK_BIT =  "None"      ,    //校验位    “None”无校验 “Odd”奇校验,“Even”偶校验
    parameter                   BPS       =  115200      ,    //系统波特率
    parameter                   CLK       =  50_000_000  ,    //系统时钟频率
    parameter                   DATA_BIT  =  8           ,    //数据位(6、7、8)
    parameter                   STOP_BIT  =  1                //停止位
)( 
    input                       clk         ,
    input                       rst_n       ,
    input                       tx_data_vld ,
    input   [DATA_BIT - 1:0]    tx_data     ,
​
    output                      tx_ready    ,
    output  reg                 tx  
);                               
//---------<参数定义>--------------------------------------------------------- 
​
//波特计数最大值
    localparam          BPS_MAX =   CLK/BPS;
//状态机参数定义
    localparam          IDLE    = 0 ,//
                        START   = 1 ,//
                        DATA    = 2 ,//
                        CHECK   = 3 ,
                        STOP    = 4 ;//
​
//---------<内部信号定义>-----------------------------------------------------
​
    reg     [2:0]           state        ;//现态
        
//状态转移条件      
    wire                    idle2start  ;
    wire                    start2data  ;
    wire                    data2check  ;
    wire                    data2stop   ;
    wire                    check2stop  ;
    wire                    stop2idle   ;
        
    reg   [25:0]            bps_cnt     ;
    wire                    add_bps_cnt ,
                            end_bps_cnt ;
    reg   [3:0]             bit_cnt     ,
                            BIT_MAX     ;
    wire                    add_bit_cnt ,
                            end_bit_cnt ;
​
    reg  [DATA_BIT - 1:0]  tx_data_r    ;
    wire                   check_vld    ;//校验位使能
​
 /****************************************************************
                    数据校验
 ****************************************************************/  
​
//奇校验缩位同或
/*         assign      check_vld = ~^tx_data_r; */
​
//偶校验缩位异或
 /* assign        check_vld = ^tx_data_r; */
 assign     check_vld = (CHECK_BIT == "Odd")?~^tx_data_r:^tx_data_r;
​
/****************************************************************
                    输入数据寄存
****************************************************************/
    always @(posedge clk or negedge rst_n) 
        if(!rst_n)
            tx_data_r <= 'd0; 
        else if(tx_data_vld)
            tx_data_r <= tx_data;
/****************************************************************
                    fsm
****************************************************************/
// 时序逻辑描述状态转移
always @(posedge clk or negedge rst_n)begin 
    if(!rst_n)begin
        state <= IDLE;
    end 
    else case(state)
            IDLE    : if(idle2start) state <= START;  
            START   : if(start2data) state <= DATA ;   
            DATA    : if(data2check) state <= CHECK; 
                else  if(data2stop ) state <= STOP ;   
            CHECK   : if(check2stop) state <= STOP ;    
            STOP    : if(stop2idle ) state <= IDLE ;    
            default : state <= IDLE ;
        endcase
    end
              
    assign    idle2start = state == IDLE  && tx_data_vld;
    assign    start2data = state == START && end_bit_cnt;
    assign    data2check = state == DATA  && end_bit_cnt && CHECK_BIT != "None";
    assign    data2stop  = state == DATA  && end_bit_cnt && CHECK_BIT == "None";
    assign    check2stop = state == CHECK && end_bit_cnt;
    assign    stop2idle  = state == STOP  && end_bit_cnt;    
​
/****************************************************************
                bps_cnt                    
****************************************************************/
    always @(posedge clk or negedge rst_n)begin 
       if(!rst_n)begin
            bps_cnt <= 'd0;
        end 
        else if(add_bps_cnt)begin 
            if(end_bps_cnt)begin 
                bps_cnt <= 'd0;
            end
            else begin 
                bps_cnt <= bps_cnt + 1'b1;
            end 
        end
    end 
    
    assign add_bps_cnt = state != IDLE;
    assign end_bps_cnt = add_bps_cnt && bps_cnt == BPS_MAX - 1;
​
/****************************************************************
                    bit_cnt
****************************************************************/      
    always @(posedge clk or negedge rst_n)begin 
       if(!rst_n)begin
            bit_cnt <= 'd0;
        end 
        else if(add_bit_cnt)begin 
            if(end_bit_cnt)begin 
                bit_cnt <= 'd0;
            end
            else begin 
                bit_cnt <= bit_cnt + 1'b1;
            end 
        end
    end 
    
    assign add_bit_cnt = end_bps_cnt;
    assign end_bit_cnt = add_bit_cnt && bit_cnt == BIT_MAX - 1;
//计数器复用
    always @(*)begin 
       case (state)
        START  : BIT_MAX = 1;
        DATA   : BIT_MAX = DATA_BIT;
        CHECK  : BIT_MAX = 1;
        STOP   : BIT_MAX = STOP_BIT;
        default: BIT_MAX = 1;
       endcase
    end
​
/****************************************************************
                    tx时序
****************************************************************/
    always @(*)begin 
        case (state)
            IDLE    :   tx = 1;
            START   :   tx = 0;
            DATA    :   if(tx_data_r[bit_cnt])  tx= 1;
                        else tx = 0;
            CHECK   :   tx = check_vld;
            STOP    :   tx = 1;
            default :   tx = 1;
        endcase
    end
​
    assign      tx_ready = state == IDLE;
    
endmodule

5.3 仿真文件

`timescale 1ns/1ns
    
module tx_tb();
​
//激励信号定义 
    reg             tb_clk      ;
    reg             tb_rst_n    ;
    reg     [7:0]   tx_data     ;
    reg             tx_data_vld ;
​
//输出信号定义     
    wire            tx          ;
   
​
//时钟周期参数定义  
    parameter       CLOCK_CYCLE = 20;   
​
//模块例化
 tx_uart #(
     .CHECK_BIT ( "None"      ),    //校验位    “None”无校验 “Odd”奇校验,“Even”偶校验
     .BPS       ( 115200      ),    //系统波特率
     .CLK       ( 50_000_000  ),    //系统时钟频率
     .DATA_BIT  ( 8           ),    //数据位(6、7、8)
     .STOP_BIT  ( 1           )     //停止位
 )tx_uart_inst( 
     /* input                       */.clk         (tb_clk      ),
     /* input                       */.rst_n       (tb_rst_n    ),
     /* input                       */.tx_data_vld (tx_data_vld ),
     /* input   [DATA_BIT - 1:0]    */.tx_data     (tx_data     ),
     /* output                      */.tx_ready    (tx_ready    ),
     /* output  reg                 */.tx          (tx          ) 
 ); 
​
//产生时钟
    initial         tb_clk = 1'b0;
    always #(CLOCK_CYCLE/2) tb_clk = ~tb_clk;
​
//复位
    initial  begin 
        tb_rst_n = 1'b1;
        #(CLOCK_CYCLE*2);
        tb_rst_n = 1'b0;
        #(CLOCK_CYCLE*20);
        tb_rst_n = 1'b1;
    end
integer i;
//描述输入
    initial begin 
​
        #(CLOCK_CYCLE*200);
        #1;
        tx_data     = 0;
        tx_data_vld = 0;
        #(CLOCK_CYCLE*4);
    for ( i= 0;i<10 ;i=i+1 ) begin
        #(CLOCK_CYCLE*20);
        tx_data_vld = 1;
​
        tx_data = {$random}%256;
        #20;
        tx_data_vld = 0;
        wait(tx_uart_inst.stop2idle == 1);
    end   
        
        #100800;
        $stop;
    end
endmodule 

5.4 测试结果

模拟多次数据传输:

放大:

5.5 上板测试(tx_text)

这里引入一个按键消抖模块作为接收使能,检验代码如下:

/**************************************功能介绍***********************************
Date    : 
Author  : linxiaoxiao.
Version : 
Description: 测试tx模块
*********************************************************************************/
    
//---------<模块及端口声名>------------------------------------------------------
module tx_text( 
    input               clk     ,
    input               rst_n   ,
    input               key_in  ,
     
    output              tx
);                               
    
//---------<内部信号定义>-----------------------------------------------------
​
    wire                key_down;
    
//模块例化
    tx_uart #(
    .CHECK_BIT ( "None"      ),    //校验位    “None”无校验 “Odd”奇校验,“Even”偶校验
    .BPS       ( 115200      ),    //系统波特率
    .CLK       ( 50_000_000  ),    //系统时钟频率
    .DATA_BIT  ( 8           ),    //数据位(6、7、8)
    .STOP_BIT  ( 1           )     //停止位
)tx_uart_inst( 
    /* input                        */.clk         ( clk            ),
    /* input                        */.rst_n       ( rst_n          ),
    /* input                        */.tx_data_vld ( key_down       ),
    /* input    [DATA_BIT - 1:0]    */.tx_data     ( 8'hac          ),
​
    /* output                       */.tx_ready    ( tx_ready       ),
    /* output   reg                 */.tx          ( tx             ) 
);
    
​
fsm_key_filter  #( .WIDTH(1))  fsm_key_filter_inst ( 
   /* input                    */.clk      ( clk      ),
   /* input                    */.rst_n    ( rst_n    ),
   /* input        [WIDTH-1:0] */.key_in   ( key_in   ),
   /* output  reg   [WIDTH-1:0]*/.key_down ( key_down )     
);      
    
    
endmodule

配置引脚

#引脚参数导入脚本
#时钟、复位
set_location_assignment PIN_E1 -to clk
set_location_assignment PIN_E15 -to rst_n
​
#UART
#set_location_assignment PIN_M2 -to uart_rx
set_location_assignment PIN_G1 -to tx

串口窗口 可设置奇偶校验

六、接收模块(rx)

6.1 接收模块状态跳转

IDLE:空闲状态,无数据传输,输出高电平,当tx_data_vld信号到来时跳转到START;  

START:起始位,无数据传输,输出低电平为 ,延时结束后至DATA; 

DATA:数据位,进行数据传输,先发送低比特,根据数据输出高低电平,假如有校验位,跳到CHECK,假如数据传输不设校验位,跳转到STOP;  

CHECK:奇偶校验位

STOP:停止位,高电平,共 1 位。  发送模块在上一状态向下一状态切换时,改变电平

6.2 接收模块  Verilog 设计文件

/**************************************功能介绍***********************************
Date	: 2023年9月18日22:37:53
Author	: linxiaoxiao.
Version	: 
Description: uart串口接收模块
*********************************************************************************/
//例化模版
// rx_uart #(
//     .CHECK_BIT ( "None"      ),    //校验位    “None”无校验 “Odd”奇校验,“Even”偶校验
//     .BPS       ( 115200      ),    //系统波特率
//     .CLK       ( 50_000_000  ),    //系统时钟频率
//     .DATA_BIT  ( 8           ),    //数据位(6、7、8)
//     .STOP_BIT  ( 1           )     //停止位
// )rx_uart_inst( 
//  /* input				        */ .clk		    (),
//  /* input				        */ .rst_n	    (),
//  /* input	reg                 */ .rx          (),	
//  /* output                      */  .rx_data_vld (),
//  /* output	[DATA_BIT - 1:0]    */ .rx_data     (),
//  /* output                     */  .rx_ready    ()
// // );		

//---------<模块及端口声名>------------------------------------------------------
module rx_uart#(
    parameter                   CHECK_BIT =  "None"      ,    //校验位    “None”无校验 “Odd”奇校验,“Even”偶校验
    parameter                   BPS       =  115200      ,    //系统波特率
    parameter                   CLK       =  50_000_000  ,    //系统时钟频率
    parameter                   DATA_BIT  =  8           ,    //数据位(6、7、8)
    parameter                   STOP_BIT  =  1                //停止位
)( 
    input				        clk		    ,
    input				        rst_n	    ,
    input	                    rx          ,
    output                      rx_data_vld ,	
    output	[DATA_BIT - 1:0]    rx_data     ,
    output                      rx_ready    
   
);								 
//---------<参数定义>--------------------------------------------------------- 

//波特计数最大值
    localparam          BPS_MAX =   CLK/BPS;
//状态机参数定义
    localparam          IDLE    = 0 ,//空闲
                        START   = 1 ,//起始位
                        DATA    = 2 ,//数据位
                        CHECK   = 3 ,//奇偶校验位 
                        STOP    = 4 ;//停止位

//---------<内部信号定义>-----------------------------------------------------

    reg 	[2:0]	        state        ;//现态
        
//状态转移条件      
    wire                    idle2start  ;
    wire                    start2data  ;
    wire                    data2check  ;
    wire                    data2stop   ;
    wire                    check2stop  ;
    wire                    stop2idle   ;
        
    reg	  [25:0]	        bps_cnt	    ;
    wire  			        add_bps_cnt ,
                            end_bps_cnt	;
    reg	  [3:0]	            bit_cnt	   	,
                            BIT_MAX     ;
    wire  			        add_bit_cnt ,
                            end_bit_cnt	;

    reg   [DATA_BIT - 1:0]  temp_data   ;//输入数据零时缓存

    reg                     check_reg   ;//校验寄存
    wire                    check_vld   ;//校验位使能

    reg                     rx_r0       ,//输入数据同步寄存
                            rx_r1       ;
    wire                    flag_n      ;//下降沿监测 确定rx采样时序
    
/****************************************************************
                    输入数据寄存(同步打拍)
****************************************************************/
    always @(posedge clk or negedge rst_n)begin 
        if(!rst_n)begin
            rx_r0 <= 0;
            rx_r1 <= 0;
        end 
        else begin 
           rx_r0 <= rx;
           rx_r1 <= rx_r0;
        end 
    end
//下降沿标志
    assign  flag_n = !rx_r0 && rx_r1;
/****************************************************************
                    数据接收逻辑
****************************************************************/
    always @(posedge clk or negedge rst_n)  
        if(!rst_n) 
            temp_data <= 'd0;
        else if(state == DATA && bps_cnt == BPS_MAX >> 1) 
            temp_data[bit_cnt] <= rx_r0;

/****************************************************************
                   接收数据校验
****************************************************************/  
    always @(posedge clk or negedge rst_n) 
        if(!rst_n)
            check_reg <= 'd0;
        else if(state == CHECK && bps_cnt == BPS_MAX >> 1)
            check_reg <= rx_r0;
//校验位选择
    assign  check_vld = (CHECK_BIT == "Odd")?~^temp_data : ^temp_data;

/****************************************************************
                    fsm
****************************************************************/
// 时序逻辑描述状态转移
always @(posedge clk or negedge rst_n)begin 
    if(!rst_n)begin
        state <= IDLE;
    end 
    else case(state)
            IDLE    : if(idle2start) state <= START;  
            START   : if(start2data) state <= DATA ;   
            DATA    : if(data2check) state <= CHECK; 
                else  if(data2stop ) state <= STOP ;   
            CHECK   : if(check2stop) state <= STOP ;    
            STOP    : if(stop2idle ) state <= IDLE ;    
            default : state <= IDLE ;
        endcase
    end
              
    assign    idle2start = state == IDLE  && flag_n     ;
    assign    start2data = state == START && end_bit_cnt;
    assign    data2check = state == DATA  && end_bit_cnt && CHECK_BIT != "None";
    assign    data2stop  = state == DATA  && end_bit_cnt && CHECK_BIT == "None";
    assign    check2stop = state == CHECK && end_bit_cnt;
    assign    stop2idle  = state == STOP  && end_bit_cnt;    

/****************************************************************
                bps_cnt                    
****************************************************************/
    always @(posedge clk or negedge rst_n)begin 
       if(!rst_n)begin
            bps_cnt <= 'd0;
        end 
        else if(add_bps_cnt)begin 
            if(end_bps_cnt)begin 
                bps_cnt <= 'd0;
            end
            else begin 
                bps_cnt <= bps_cnt + 1'b1;
            end 
        end
    end 
    
    assign add_bps_cnt = state != IDLE;
    assign end_bps_cnt = add_bps_cnt && bps_cnt == BPS_MAX - 1;

/****************************************************************
                    bit_cnt
****************************************************************/      
    always @(posedge clk or negedge rst_n)begin 
       if(!rst_n)begin
            bit_cnt <= 'd0;
        end 
        else if(add_bit_cnt)begin 
            if(end_bit_cnt)begin 
                bit_cnt <= 'd0;
            end
            else begin 
                bit_cnt <= bit_cnt + 1'b1;
            end 
        end
    end 
    
    assign add_bit_cnt = end_bps_cnt;
    assign end_bit_cnt = add_bit_cnt && bit_cnt == BIT_MAX - 1;
//计数器复用
    always @(*)begin 
       case (state)
        START  : BIT_MAX = 1;
        DATA   : BIT_MAX = DATA_BIT;
        CHECK  : BIT_MAX = 1;
        STOP   : BIT_MAX = STOP_BIT;
        default: BIT_MAX = 1;
       endcase
    end

/****************************************************************
                    输出逻辑           
****************************************************************/

    assign      rx_ready     = state == IDLE;
    assign      rx_data_vld  = (CHECK_BIT == "None")? stop2idle
                             : (stop2idle && (check_vld == check_reg))? 1
                             : 0;

    assign rx_data =(state == STOP && bit_cnt == STOP_BIT - 1) ? temp_data : 'b0 ;
    
endmodule

6.3 仿真文件

两种模拟输入方式,都在下面体现了:

`timescale 1ns/1ns
    
module rx_tb ();

//激励信号定义 
    reg				clk  	    ;
    reg				rst_n	    ;
    reg		        /* tx_data_vld	, */rx_din;
/*     reg  [7:0]      tx_data     ; */
/*     wire            tx   ; */

//输出信号定义	 
    wire	    	/* tx_ready      , */rx_data_vld ;




//时钟周期参数定义	
    parameter		CLOCK_CYCLE = 20;  


//模块例化
//  tx_uart #(
//      .CHECK_BIT ( "None"      ),    //校验位    “None”无校验 “Odd”奇校验,“Even”偶校验
//      .BPS       ( 115200      ),    //系统波特率
//      .CLK       ( 50_000_000  ),    //系统时钟频率
//      .DATA_BIT  ( 8           ),    //数据位(6、7、8)
//      .STOP_BIT  ( 1           )     //停止位
//  )tx_uart_inst( 
//      /* input				        */.clk		   (clk		    ),
//      /* input				        */.rst_n	   (rst_n	    ),
//      /* input                       */.tx_data_vld (tx_data_vld ),
//      /* input	[DATA_BIT - 1:0]    */.tx_data     (tx_data     ),
//      /* output                      */.tx_ready    (tx_ready    ),
//      /* output	reg                 */.tx	       (tx	        ) 
//  );	
					

rx_uart #(
    .CHECK_BIT ( "None"      ),    //校验位    “None”无校验 “Odd”奇校验,“Even”偶校验
    .BPS       ( 115200      ),    //系统波特率
    .CLK       ( 50_000_000  ),    //系统时钟频率
    .DATA_BIT  ( 8           ),    //数据位(6、7、8)
    .STOP_BIT  ( 1           )     //停止位
)rx_uart_inst( 
 /* input				        */ .clk		    ( clk	      ),
 /* input				        */ .rst_n	    ( rst_n       ),
 /* input	reg                 */ .rx          ( rx_din      ),	
 /* output                      */ .rx_data_vld ( rx_data_vld ),
 /* output	[DATA_BIT - 1:0]    */ .rx_data     ( rx_data     ),
 /* output                     */  .rx_ready    ( rx_ready    )
);	


//产生时钟
    initial 		clk = 1'b0;
    always #(CLOCK_CYCLE/2) clk = ~clk;

//产生激励
    initial  begin 
        rst_n = 1'b1;
        #(CLOCK_CYCLE*2);
        rst_n = 1'b0;
        #(CLOCK_CYCLE*20);
        rst_n = 1'b1;
    end

    initial begin 
        repeat(5)begin
		rx_din = 1'b1;
		#(20*100)
		rx_din = 1'b0;
		#(434*20)
		repeat(8)begin
		rx_din = {$random};
		#(434*20);
		end
        rx_din = 1'b1;
		wait(rx_data_vld == 1);
	end
	#10000;
	$stop;

        // tx_data   = 0;
        // tx_data_vld = 0;
        // #(CLOCK_CYCLE*6);
        
        // tx_data_vld = 1;
        // repeat(8) begin
        //     tx_data = {$random};#20;
        //     tx_data_vld = 0;
        //     #200;
        //     tx_data_vld = 1;
        //     wait(tx_ready == 1);
        //     #20;
        // end
        // #20000000;
        // $stop;
    end

endmodule 

6.5 上板测验(加入tx模块的串口回环)

代码:

/**************************************功能介绍***********************************
Date	: 
Author	: linxiaoxiao.
Version	: 
Description:  串口回环(未加fifo)
*********************************************************************************/ 
//---------<模块及端口声名>------------------------------------------------------
module top( 
    input				clk		,
    input				rst_n	,
    input               rx      ,
     
    output		        tx
);								 
  
//---------<内部信号定义>-----------------------------------------------------

    wire       [7:0]    rx_data    ;
    wire                tx_ready   ;
    wire                rx_data_vld;

//模块例化
    tx_uart #(
    .CHECK_BIT ( "None"      ),    //校验位    “None”无校验 “Odd”奇校验,“Even”偶校验
    .BPS       ( 115200      ),    //系统波特率
    .CLK       ( 50_000_000  ),    //系统时钟频率
    .DATA_BIT  ( 8           ),    //数据位(6、7、8)
    .STOP_BIT  ( 1           )     //停止位
)tx_uart_inst( 
    /* input				        */.clk		   ( clk		    ),
    /* input				        */.rst_n	   ( rst_n	        ),
    /* input                        */.tx_data_vld ( rx_data_vld && tx_ready ),
    /* input	[DATA_BIT - 1:0]    */.tx_data     ( rx_data        ),

    /* output                       */.tx_ready    ( tx_ready       ),
    /* output	reg                 */.tx	       ( tx	            ) 
);
    
rx_uart #(
    .CHECK_BIT ( "None"      ),    //校验位    “None”无校验 “Odd”奇校验,“Even”偶校验
    .BPS       ( 115200      ),    //系统波特率
    .CLK       ( 50_000_000  ),    //系统时钟频率
    .DATA_BIT  ( 8           ),    //数据位(6、7、8)
    .STOP_BIT  ( 1           )     //停止位
)rx_uart_inst( 
 /* input				        */ .clk		    ( clk	      ),
 /* input				        */ .rst_n	    ( rst_n       ),
 /* input	reg                 */ .rx          ( rx          ),
 /* output                      */ .rx_data_vld ( rx_data_vld ),
 /* output	[DATA_BIT - 1:0]    */ .rx_data     ( rx_data     ),
 /* output                     */  .rx_ready    ( rx_ready    )
);	
   
endmodule

七、uart_ctrl模块(fifo)

7.1 引入fifo作为数据缓冲器的意义

在串口通信中,采用缓冲器FIFO的主要意义是解决数据传输中的**时序差异**问题,并提供数据的缓冲和流控能力。具体而言,它有以下几个作用:

1. 时序匹配:串口通信中发送方和接收方的时钟频率可能存在差异,使用FIFO可以将数据以一定的速率存储在缓冲区中,从而实现发送方和接收方之间的时序匹配,避免数据的丢失和错误。

2. 数据缓冲:在数据传输中,当发送方产生数据的速率快于接收方处理数据的速率时,FIFO可以暂时存储多出来的数据,保证数据不会丢失。

3. 流控控制:当接收方处理能力不足以处理发送方的数据时,FIFO可以用于流控,即发送方通过监测FIFO的状态来控制数据的发送速率,从而避免数据的丢失。

4. 缓存传输:一些应用场景中,需要在某个特定时刻对数据进行处理,而不是即时处理。FIFO可以将接收到的数据缓存在缓冲区中,待到特定时刻再进行处理。

总之,**FPGA串口数据缓冲器FIFO在数据传输中起到了时序匹配、数据缓冲、流控控制和缓存传输的重要作用,提高了数据传输的可靠性和灵活性。**

7.2 fifo IP的配置

7.3 uart_ctrl 设计文件

/**************************************功能介绍***********************************
Date	: 2023年9月21日21:48:59
Author	: linxiaoxiao.
Version	: 
Description: uart_ctrl控制模块——fifo
*********************************************************************************/
//---------<模块及端口声名>------------------------------------------------------
module uart_ctrl( 
    input				clk		    ,
    input				rst_n	    ,
    
    input		[7:0]	rx_data	    ,
    input		    	rx_data_vld	,  
    input               tx_ready    ,
    output	    [7:0]	tx_data	    ,
    output	        	tx_data_vld	   
);								   
//---------<内部信号定义>-----------------------------------------------------

    wire            fifo_empty  ;
    wire            fifo_full   ;

    fifo	fifo_inst (
	        .aclr   ( ~rst_n        ),
	        .clock  ( clk           ),
	        .data   ( rx_data       ),
	        .rdreq  ( ~fifo_empty && tx_ready  ),
	        .wrreq  ( rx_data_vld && ~fifo_full ),
	        .empty  ( fifo_empty    ),
	        .full   ( fifo_full     ),
	        .q      ( tx_data       ),
	        .usedw  (               )
	);
        
    assign          tx_data_vld = tx_ready && ~fifo_empty ;

endmodule

八、顶层整合

8.1 top文件

/**************************************功能介绍***********************************
Date	: 
Author	: linxiaoxiao.
Version	: 
Description:  
*********************************************************************************/
    
//---------<模块及端口声名>------------------------------------------------------
module top( 
    input				clk		,
    input				rst_n	,
    input               rx      ,   
    output		        tx
);								 
//---------<内部信号定义>-----------------------------------------------------

    wire       [7:0]    tx_data    , rx_data  ;
    wire                tx_ready   , rx_ready ;
    wire                tx_data_vld,rx_data_vld;

//模块例化
    tx_uart #(
    .CHECK_BIT ( "None"      ),    //校验位    “None”无校验 “Odd”奇校验,“Even”偶校验
    .BPS       ( 115200      ),    //系统波特率
    .CLK       ( 50_000_000  ),    //系统时钟频率
    .DATA_BIT  ( 8           ),    //数据位(6、7、8)
    .STOP_BIT  ( 1           )     //停止位
)tx_uart_inst( 
    /* input				        */.clk		   ( clk		    ),
    /* input				        */.rst_n	   ( rst_n	        ),
    /* input                        */.tx_data_vld ( tx_data_vld    ),
    /* input	[DATA_BIT - 1:0]    */.tx_data     ( tx_data        ),

    /* output                       */.tx_ready    ( tx_ready       ),
    /* output	reg                 */.tx	       ( tx	            ) 
);
    
uart_ctrl uart_ctrl_inst( 
    /* input				 */.clk		    ( clk		    ),
    /* input				 */.rst_n	    ( rst_n	        ),
    /* input		[7:0]	 */.rx_data	    ( rx_data	    ),
    /* input		    	 */.rx_data_vld ( rx_data_vld   ),  
    /* input                 */.tx_ready    ( tx_ready      ),
    /* output	    [7:0]	 */.tx_data	    ( tx_data	    ),
    /* output	        	 */.tx_data_vld ( tx_data_vld   )   
);				

rx_uart #(
    .CHECK_BIT ( "None"      ),    //校验位    “None”无校验 “Odd”奇校验,“Even”偶校验
    .BPS       ( 115200      ),    //系统波特率
    .CLK       ( 50_000_000  ),    //系统时钟频率
    .DATA_BIT  ( 8           ),    //数据位(6、7、8)
    .STOP_BIT  ( 1           )     //停止位
)rx_uart_inst( 
 /* input				        */ .clk		    ( clk	      ),
 /* input				        */ .rst_n	    ( rst_n       ),
 /* input	reg                 */ .rx          ( rx          ),
 /* output                      */ .rx_data_vld ( rx_data_vld ),
 /* output	[DATA_BIT - 1:0]    */ .rx_data     ( rx_data     ),
 /* output                     */  .rx_ready    ( rx_ready    )
);	
endmodule

8.2 整体仿真

8.2.1 仿真文件

`timescale 1ns/1ns
    
module tb_top ();

//激励信号定义 
    reg				clk  	    ;
    reg				rst_n	    ;
    reg		        rx_din      ;

//时钟周期参数定义	
    parameter		CLOCK_CYCLE = 20;  
					

top top_inst( 
    /* input	      */.clk    ( clk    ),
    /* input	      */.rst_n	( rst_n  ),
    /* input          */.rx     ( rx_din ),
    /* output		  */.tx     ( tx     )
);								 

//产生时钟
    initial 		clk = 1'b0;
    always #(CLOCK_CYCLE/2) clk = ~clk;

//产生激励
    initial  begin 
        rst_n = 1'b1;
        #(CLOCK_CYCLE*2);
        rst_n = 1'b0;
        #(CLOCK_CYCLE*20);
        rst_n = 1'b1;
    end

    initial begin 
        repeat(5)begin
		rx_din = 1'b1;
		#(20*100)
		rx_din = 1'b0;
		#(434*20)
		repeat(8)begin
		rx_din = {$random};
		#(434*20);
		end
        rx_din = 1'b1;
		wait(top_inst.rx_uart_inst.rx_data_vld == 1);
	end
	#10000;
	$stop;
    end

endmodule 
8.2.2 仿真波形分析

九、 上板检测

  • 25
    点赞
  • 108
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
UART(通用异步收发传输器)是一种常用的串行通信协议,它使用了异步传输方式,可以用于电脑与其他设备之间的数据传输。在实现UART硬件时,可以使用Verilog进行设计和描述。下面是一个简单的UART通信模块的Verilog代码示例: ```verilog module uart ( input clk, input rst, input [7:0] data_in, input tx_en, output reg [7:0] data_out, output reg rx_dv, output reg rx_err ); // 定义状态机状态 parameter IDLE = 2'b00; parameter START = 2'b01; parameter DATA = 2'b10; parameter STOP = 2'b11; // 定义状态机输入和输出 reg [1:0] state_reg, state_next; reg [7:0] data_in_reg; reg tx_en_reg; reg [3:0] count_reg; reg [7:0] data_out_reg; reg rx_dv_reg, rx_err_reg; // 定义状态机逻辑 always @(posedge clk, posedge rst) begin if (rst) begin state_reg <= IDLE; data_in_reg <= 8'd0; tx_en_reg <= 1'b0; count_reg <= 4'd0; data_out_reg <= 8'd0; rx_dv_reg <= 1'b0; rx_err_reg <= 1'b0; end else begin state_reg <= state_next; data_in_reg <= data_in; tx_en_reg <= tx_en; count_reg <= (count_reg == 4'd15) ? 4'd0 : count_reg + 1; data_out_reg <= data_out; rx_dv_reg <= rx_dv; rx_err_reg <= rx_err; end end // 定义状态机状态转移 always @(*) begin state_next = state_reg; case (state_reg) IDLE: begin if (!tx_en_reg) begin state_next = IDLE; end else begin state_next = START; count_reg <= 4'd0; end end START: begin if (count_reg == 4'd8) begin state_next = DATA; count_reg <= 4'd0; end else begin state_next = START; count_reg <= count_reg + 1; end end DATA: begin if (count_reg == 4'd8) begin state_next = STOP; count_reg <= 4'd0; end else begin state_next = DATA; count_reg <= count_reg + 1; end end STOP: begin state_next = IDLE; end endcase end // 定义rx_dv输出 always @(posedge clk) begin if (state_reg == STOP) begin rx_dv <= 1'b1; end else begin rx_dv <= 1'b0; end end // 定义rx_err输出 always @(posedge clk) begin if (state_reg == STOP && data_in_reg != data_out_reg) begin rx_err <= 1'b1; end else begin rx_err <= 1'b0; end end // 定义data_out输出 always @(posedge clk) begin if (state_reg == DATA) begin data_out <= data_in_reg; end else if (state_reg == STOP) begin data_out <= 8'd0; end else begin data_out <= data_out_reg; end end endmodule ``` 上述代码示例中定义了一个UART通信模块,其中包括了发送和接收数据的状态机逻辑。使用Verilog可以实现这个通信模块的设计和描述,从而实现UART串行通信协议的硬件实现。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值