• 16字节协议的串口通信


    1.协议要求

    协议为帧传输,一共16字节。主要是2字节的固定帧头 EB 90,2字节的帧计数(用来计数发出的帧),10字节的数据和2字节的校验位

    在这里插入图片描述

    帧头:2字节,固定值 8’HEB、8’H90

    帧计数:2字节,用来说明发出去帧是第几帧

    数据:10字节,用于发送数据

    校验位:2字节,和校验,将数据位累计后取反

    2.架构设计

    2.1整体架构设计

    本架构基于黑金开发版Ax7101,开发工具vivado2022.3,整个协议的架构图设计如下:

    在这里插入图片描述

    整个架构的功能是实现帧的接收和帧的发送,该架构可以分成两个部分:解帧使用组帧发送的两个部分。

    数据流向解析

    • 组帧发送部分

      晶振时钟(200Mhz)和板上复位信号先流入锁相环PLL,得到的是50Mhz的时钟(因为200Mzh时钟经PLL分频不能一次性分频到波特率时钟如9600,因此需要再次分频)。

      50Mhz的时钟先流入分频模块clk_div将分频得到波特率率时钟的16倍时钟(如9600*16,这里之所以不用波特率时钟,是因为要在单字节接收模块uart_rx用过采样,为了节省资源所有的时钟都用这个波特时钟的16时钟,本文称之为over_clk)

      经过分频后的over_clk 流入所有模块作为输入时钟,同时pll_lock信号作为复位信号sys_rst_n.(这里用plll_lock作为复位信号是因为,在pll_lock为低电平的时候,输出时钟是不稳定的,是有毛刺的。而当时pll_lock输入为高电平的时候才是输出的稳定时钟over_clk)

      接着来到了主要功能实现模块,首先是用户会给你一个数据,让你把这个数据发出去,这里假设给你一帧数据(10字节),如果10字节每个字节是一样的,如都是8’H01。那么就是用uart_tx发送10次单字节模块就好了。然后,用户为了保证传输安全,他给你规定了一个协议(2byte帧头+2byte帧计数+10byte数据+2byte校验)。因此,你拿到数据,首先应该做的是先封装成一帧,然后再去想怎么将这一帧发出去的问题。

      这就有了组帧模块Frame,通过Frame模块将用户输入的10字节数据封装成一帧,为了隔离组帧模块和单字节发送模块Tx_Drive,所以在这两个模块之间加了异步FIFO(FIFO_Frame),同时也是为了平衡组帧和发送这一帧的速率。因为组帧模块组帧完成后,输入的FIFO_Frame的是连续的16个字节,而发送模块是一字节一字节的发送的,两模块之间的速度存在差异。

      而组帧模块Frame所做的就是将组帧好的16字节数据送入FIFO_Frame,然后继续组帧,继续送入…就这样一直工作。而Tx_Drive模块的功能是先从FIFO_Frame取出1字节,然后将1字节的数据通过串口tx发送出去。然后再到FIFO_Frame里面去取,再发…就这样就这实现了整个组帧发送部分。

    • 解帧使用部分

      首先输入端口是由tx发出的二进制数据流,所以先需要Rx_Drive模块将这个数据转换成16字节的8位数据,而在收到16字节之后,这16字节中只有10字节的数据,而用户需要只是这10字节的数据,而不是带帧格式的整个帧,所以需要解帧模块Unframe模块将16字节的模块中10字节数据提取出来。而中间的流动过程跟组帧模块相反,但思想几乎一模一样,唯一区别就是解帧模块Unframe要在Rx_Drive一字节一字节存入的FIFO_Unframe中一次性取出16字节,然后从这16字节中把数据取出来。

    2.1.1组帧发送

    主要构成:Frame(组帧模块)+FIFO_Frame(同步FIFO)+Tx_Drive(串口发送模块)

    **主要功能:**将用户给的数据,先组帧,然后通过单字节串口将数据一字节一字节的方式发出去。

    2.1.2解帧使用

    主要构成:Unframe(解帧模块)+FIFO_Unframe(同步FIFO)+Rx_Drive(串口发送模块)

    主要功能:将收到数据流解析成16字节,然后解帧模块再将其中的数据取出来,供用户使用。

    2.2 定时发送架构设计

    定时发送模块是属于上面整体架构的组帧发送部分,因为组帧发送需要出入10字节的数据,才能将数据组帧,然后发送出去,所以需要一个定时产生数据的模块。

    在这里插入图片描述

    3.模块代码分析

    本章叙述流程,将会先从从定时发送发送多字节模块(即多了定时发送的组帧发送模块)展开叙述每个模块的具体细节和实现思路,然后将再将定时发送tx端产生的数据流,流入解帧接收部分,从而一点点随着数据的流动,逐渐由表及里,剖析代码。

    3.1 定时模块 Timeout_Send

    定时发送模块Timeout_Send为顶层模块,该模块是下面两个功能的封装:

    (1)定时

    (2)产生一帧要发送的数据(10字节)

    参数控制

    • P_SYS_CLK :输入时钟,默认是9600*16
    • P_SEND_BYTE_LENGTH:发送一帧长度,自动计算需要产生多少字节数据
    • P_TIMEOUT_PRIOD:发送间隔,默认5ms发送一帧

    框图

    在这里插入图片描述

    参考代码

    `timescale 1ns / 1ps
    // 定时发送模块,到时间后输出高电平
    
    module Timeout_Send
    #(
        parameter P_SYS_CLK             = 153_600   ,
        parameter P_SEND_BYTE_LENGTH    = 16       ,
        parameter P_TIMEOUT_PRIOD       = 500        //定时发送1帧数据,单位毫秒,默认5ms
    )
    (
        input           i_clk       ,             //时钟为50Mhz
        input           i_rst_n     ,             //工作使能信号
        input           i_full      ,
    
        output  [7:0]   o_data      ,             //输出的数据
        output          o_valid                   //输出数据的有效信号
    );
    
    wire                work_en;
    
    Set_Timeout
    #(
        .P_SYS_CLK      (P_SYS_CLK      )   ,
        .P_TIMEOUT_PRIOD(P_TIMEOUT_PRIOD)       //定时发送1帧数据,单位毫秒,默认50ms
    )
    Set_Timeout_U0
    (
        .i_clk          (i_clk          )   ,   //时钟为50Mhz
        .i_rst_n        (i_rst_n        )   ,
    
        .o_timeout      (work_en        )       //时间到了就拉高一个周期
    );
    
    Product_Data
    #(
        .P_SEND_BYTE_LENGTH (P_SEND_BYTE_LENGTH)
    )
    Product_Data_U0
    (
        .i_clk          (i_clk          )   ,
        .i_rst_n        (i_rst_n        )   ,
        .i_work_en      (work_en        )   ,
        .i_full         (i_full         )   ,
    
        .o_data         (o_data         )   ,
        .o_valid        (o_valid        )  
    );
    endmodule
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    3.1.1 定时模块Set_Timeout

    主要功能:设置的定时时间到了,就拉高一个工作使能信号o_timeout,默认5ms拉高信号。

    参数控制

    • P_SYS_CLK:输入时钟,默认分频时钟的16倍,每个周期是10^6/(9600*16)=6.51us
    • P_TIMEOUT_PRIOD:发送间隔,定时发送1帧数据,单位毫秒,默认5ms

    参考代码:

    `timescale 1ns / 1ns
    module Set_Timeout
    #(
        parameter P_SYS_CLK         = 153_600,  //分频时钟的16倍,每个周期是10^6/(9600*16)=6.51us
        parameter P_TIMEOUT_PRIOD   = 500       //定时发送1帧数据,单位毫秒,默认5ms
    )
    (
        input   i_clk       ,                   //时钟为50Mhz
        input   i_rst_n     ,
    
        output  o_timeout                       //时间到了就拉高一个周期
    );
    
    /***************parameter*************/
    localparam      P_CNT_MAX       = P_TIMEOUT_PRIOD*P_SYS_CLK/1000_000; 
    
    reg             ro_timeout              ;
    reg     [31:0]  r_cnt                   ;
    
    assign          o_timeout = ro_timeout  ;
    /***************always****************/
    always @(posedge i_clk, negedge i_rst_n) begin
        if(~i_rst_n)
            r_cnt <= 32'd0;
        else if(r_cnt == P_CNT_MAX - 1)
            r_cnt <= 32'd0;
        else
            r_cnt <= r_cnt + 1;
    end
    
    always @(posedge i_clk, negedge i_rst_n) begin
        if(~i_rst_n)
            ro_timeout <= 1'b0;
        else if(r_cnt == P_CNT_MAX - 1)
            ro_timeout <= ro_timeout + 1;
        else
            ro_timeout <= ro_timeout;
    end
    endmodule
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    3.1.2 数据产生模块Product_Data

    主要功能:收到拉高的工作使能信号,就输出一帧数据,默认50ms输出一帧数据。

    参数控制

    • P_SEND_BYTE_LENGTH:一帧要发送的字节的长度,自动除去帧头、帧计数和帧校验,发需要发的字节长度。列如16字节:16-2-2-2

    参考代码:

    `timescale 1ns / 1ps
    
    //产生模拟数据模块:当时工作使能信号为高,同时有握手信号的才输出数据
    module Product_Data
    #(
        parameter P_SEND_BYTE_LENGTH = 16      
    )
    (
        input           i_clk       ,
        input           i_rst_n     ,
        input           i_work_en   ,
        input           i_full      ,
    
        output  [7:0]   o_data      ,
        output          o_valid
    );
    
    /***************function**************/
    /***************parameter*************/
    localparam P_FRAME_DATA_LENGTH = P_SEND_BYTE_LENGTH -2-2-2;//一帧数据的长度
    /***************port******************/             
    /***************mechine***************/
    /***************reg*******************/
    reg   [7:0]         ri_data     ;
    reg                 ri_valid    ;
    reg   [9:0]         valid_cnt   ;
    reg                 send_en     ;
    reg   [7:0]         r_byte_cnt  ;   //输出字节计数器,用来约束只发一帧数据
    reg                 is_finish   ;   //完整输出一帧的后拉高信号,不在产生数据
    /***************wire******************/
    wire                wo_rd_en    ;
    /***************component*************/
    localparam          P_VALID_CNT = 10 ;
    /***************assign****************/
    assign              wo_rd_en = ~i_full  ;
    assign              o_data   = ri_data  ;
    assign              o_valid  = ri_valid ;
    /***************always****************/
    always @(posedge i_clk,negedge i_rst_n) begin   //限制发送一帧数据长度计数器
        if(~i_rst_n)
            r_byte_cnt <= 'd0;
        else if(r_byte_cnt == P_FRAME_DATA_LENGTH)
            r_byte_cnt <= 'd0;
        else if(ri_valid && ~is_finish)
            r_byte_cnt <= r_byte_cnt + 1;
        else
            r_byte_cnt <= r_byte_cnt;
    end
    always @(posedge i_clk ,negedge i_rst_n) begin
        if(~i_rst_n)
            is_finish <= 'd0;
        else if(~i_work_en)
            is_finish <= 'd0;
        else if(r_byte_cnt == P_FRAME_DATA_LENGTH)
            is_finish <= 'd1;
        else
            is_finish <= is_finish;
    end
    always@(posedge i_clk,negedge i_rst_n)begin //发送使能,当拉高的时候,处于发数据阶段
        if(~i_rst_n)
            send_en <= 'd0;
        else if(r_byte_cnt == P_FRAME_DATA_LENGTH && valid_cnt == P_VALID_CNT-1)
            send_en <= 'd0;
        else if(valid_cnt == P_VALID_CNT-1)
            send_en <= 'd0;
        else if(wo_rd_en && i_work_en && is_finish==0)
            send_en <= 'd1;
        else
            send_en <= send_en;
    end
    always @(posedge i_clk,negedge i_rst_n) begin
        if(~i_rst_n)
            valid_cnt <= 'd0;
        else if(valid_cnt == P_VALID_CNT)
            valid_cnt <= 'd0;
        else if(wo_rd_en  && i_work_en && ~is_finish)
            valid_cnt <= valid_cnt+1;
        else
            valid_cnt <= 'd0;
    end
    always @(posedge i_clk,negedge i_rst_n) begin
        if(~i_rst_n)
            ri_data <= 'd0;
        else if(~wo_rd_en)
            ri_data <= 'd0;
        else if(~send_en && i_work_en)
            ri_data <= ri_data + 1;
        else
            ri_data <= ri_data;
    end
    always @(posedge i_clk,negedge i_rst_n) begin
        if(~i_rst_n)
            ri_valid <= 'd0;
        else if(valid_cnt == P_VALID_CNT-1 && ~is_finish)
            ri_valid <= 1'b1;
        else
            ri_valid <= 'd0;
    end
    endmodule
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    3.2组帧发送
    3.2.1组帧模块Frame

    主要功能:将用户输入的数据封装成帧发送出去

    参数控制

    • P_CRC_POLY:CRC校验的多项式,默认使用和校验,要用需要解开注释部分。默认CRC-16模型
    • P_FIRST_MARK:帧头第一位,默认为8’HEB。
    • P_SECOND_MARK:帧头第二位,默认为8’H90。
    • P_SEND_BYTE_LENGTH:待发送的一帧长度,默认16字节。

    框图:

    在这里插入图片描述

    参考代码

    `timescale 100ps / 1ps
    /*计算机校验位,组帧发出去*/
    
    module Frame
    #(
        parameter       P_CRC_POLY          = 16'H8005  ,    //CRC校验中的多项式
        parameter       P_FIRST_MARK        = 8'HEB     ,
        parameter       P_SECOND_MARK       = 8'H90     ,
        parameter       P_SEND_BYTE_LENGTH  = 16
    
    )
    (
        input           i_clk       ,
        input           i_rst_n     ,
        input   [7:0]   i_data      ,
        input           i_valid     ,
        input           i_full      ,   //写入的FIFO是否满
        input           i_empty     ,   //被读入的FIFO是否空,当其拉高的时候读使能不在有效
    
        output  [7:0]   o_data      ,
        output          o_rd_en     ,             //读使能信号,当fifo非空且需要读数据的拉高
        output          o_valid
    );
    /***************function**************/
        function    automatic [15:0]  get_CRC;	//生成CRC校验的函数
        input   [15:0]      i_CRC_reg;
        input   [7:0]       i_f_poly;
        integer i;
        begin
            for(i = 0;i<8;i = i+1)begin
                i_CRC_reg = i_CRC_reg >> 1;
                if(i_CRC_reg[0] == 1)
                     i_CRC_reg = i_CRC_reg ^ {1'b1,i_f_poly};
            end
            get_CRC = i_CRC_reg;
        end
    endfunction
    
        function automatic [15:0] get_Sum;	//累加函数,用于生成累加校验checksum
        input   [15:0]      i_sum_reg;
        input   [7:0]       i_sum_item;
        get_Sum  = i_sum_reg + i_sum_item;
    endfunction
    
    /***************parameter*************/
    localparam          P_VALID_CNT = 3;
    /***************port******************/             
    /***************mechine***************/
    /***************reg*******************/
    reg     [7:0]       ro_data             ;
    reg                 ro_valid            ;
    reg                 ro_valid_1d         ;
    reg     [15:0]      r_CRC_Reg           ;       //原始数据左移之后的16位数据
    reg     [4:0]       r_byte_cnt          ;       //接收字节计数器
    reg     [79:0]      r_recive_container  ;
    reg     [4:0]       r_send_byte_cnt     ;       //发送字节计数器
    reg                 r_send_lock         ;       //接收的时候为低电平,高电平为发送状态  
    reg                 ro_rd_en            ;
    reg     [1:0]       r_valid_cnt         ;
    reg     [15:0]      r_frame_cnt         ;       //帧计数器
    /***************wire******************/
    /***************component*************/
    /***************assign****************/
    assign              o_data  = ro_data       ;
    assign              o_valid = ro_valid_1d   ;
    assign              o_rd_en = ro_rd_en      ;
    /***************always****************/
    
    always @(posedge i_clk,negedge i_rst_n) begin
        if(~i_rst_n)
            r_byte_cnt <= 'd0;
        else if(r_byte_cnt == (P_SEND_BYTE_LENGTH - 4-2))      //去除帧头标志位和校验位
            r_byte_cnt <= 'd0;
        else if(i_valid)
            r_byte_cnt <= r_byte_cnt + 1;
        else
            r_byte_cnt <= r_byte_cnt;
    end
    
    always @(posedge i_clk,negedge i_rst_n) begin           //1代表空闲
        if(~i_rst_n)
            ro_rd_en <= 'd1;
        else if(r_byte_cnt == (P_SEND_BYTE_LENGTH - 4-2))     //提前1位拉高,11位的时候拉高,然后valid慢数据1位,所以要提前两位
            ro_rd_en <= 'd0;
        else if(r_send_byte_cnt == P_SEND_BYTE_LENGTH && r_valid_cnt == 0 && ~i_empty)
            ro_rd_en <= 'd1;
        else if(r_byte_cnt <(P_SEND_BYTE_LENGTH - 4-2)&&r_send_byte_cnt == 0 && ~i_empty)
            ro_rd_en <= 'd1;
        else 
            ro_rd_en <= ro_rd_en;
    end
    
    always @(posedge i_clk,negedge i_rst_n) begin           //接收的时候为低电平,高电平为发送状态  
        if(~i_rst_n)
            r_send_lock <= 1'b0;
        else if(r_byte_cnt == (P_SEND_BYTE_LENGTH - 4-2) && ~i_full)     //当接受完12为数据,就开始准备发送
            r_send_lock <= 1'b1;
        else if(r_send_byte_cnt == P_SEND_BYTE_LENGTH && r_valid_cnt == 0)      //当发送完,变成接收状态
            r_send_lock <= 1'b0;
        else
            r_send_lock <= r_send_lock;
    end
    
    always @(posedge i_clk,negedge i_rst_n) begin
        if(~i_rst_n)
            // r_CRC_Reg <= 16'HFFFF;						//CRC校验的初始化
            r_CRC_Reg <= 'd0;
        else if(r_send_byte_cnt == P_SEND_BYTE_LENGTH  && r_valid_cnt == 0)
            // r_CRC_Reg <= 16'HFFFF;						//CRC校验的归零
            r_CRC_Reg <= 'd0;
       // else if(i_valid) begin                           	//CRC校验的写法:每收到一字节就先把包括这字节及其以前的所有字节全都计算出来,收完就计算完;
       		//r_CRC_Reg <= i_data^r_CRC_Reg[7:0];
        	//r_CRC_Reg <= get_CRC(r_CRC_Reg,P_CRC_POLY);  
       // end
        else if(i_valid) begin                              //checksum校验的写法:先把12收完再计算出校验位
            r_CRC_Reg <= get_Sum(r_CRC_Reg,i_data);
        end
        else if(r_byte_cnt == (P_SEND_BYTE_LENGTH - 4-2))   //加上帧计数一起算
            r_CRC_Reg <= ~r_CRC_Reg;
        else
            r_CRC_Reg <= r_CRC_Reg;
    end
    
    always @(posedge i_clk, negedge i_rst_n) begin
        if(~i_rst_n)
            r_valid_cnt <= 'd0;
        else if(r_valid_cnt == P_VALID_CNT)
            r_valid_cnt <= 'd0;
        else if(r_send_lock)
            r_valid_cnt <= r_valid_cnt + 1;
        else
            r_valid_cnt <= 'd0;
    end
    
    always@(posedge i_clk, negedge i_rst_n)begin
        if(~i_rst_n)
            r_send_byte_cnt <= 'd0;
        else if(r_send_byte_cnt == P_SEND_BYTE_LENGTH && r_valid_cnt == 0)
            r_send_byte_cnt <= 'd0;
        else if(r_send_lock && r_valid_cnt == 0)
            r_send_byte_cnt <= r_send_byte_cnt+1;
        else
            r_send_byte_cnt <= r_send_byte_cnt;
    end
    
    always @(posedge i_clk, negedge i_rst_n) begin
        if(~i_rst_n)
            ro_valid <= 1'b0;
        else if(r_send_lock && r_send_byte_cnt <=P_SEND_BYTE_LENGTH && r_valid_cnt == P_VALID_CNT)
            ro_valid <= 1'b1;
        else
            ro_valid <= 1'b0;
    end
    
    always@(posedge i_clk,negedge i_rst_n)begin
        if(~i_rst_n)
            ro_valid_1d <= 1'b0;
        else 
            ro_valid_1d <= ro_valid;
    end
    
    always @(posedge i_clk, negedge i_rst_n) begin
        if(~i_rst_n)
            r_recive_container <= 'd0;
        else if(r_send_byte_cnt == P_SEND_BYTE_LENGTH && r_valid_cnt == 0)
            r_recive_container <= 'd0;
        else if(i_valid)
            r_recive_container <= {i_data,r_recive_container[79:8]};
        else if(r_valid_cnt == 2 && r_send_byte_cnt > 4 && r_send_byte_cnt < P_SEND_BYTE_LENGTH-1)
            r_recive_container <= r_recive_container>>8;
        else
            r_recive_container <= r_recive_container;
    end
    
    always @(posedge i_clk, negedge i_rst_n) begin
        if(~i_rst_n)
            ro_data <= 'd0;
        else if(r_send_byte_cnt == 0 && r_valid_cnt == 1)
            ro_data <= 'd0;
        else if( r_send_lock && ~i_full && r_send_byte_cnt==1)
            ro_data <= 8'HEB;
        else if( r_send_lock && ~i_full && r_send_byte_cnt==2)
            ro_data <= 8'H90;
        else if( r_send_lock && ~i_full && r_send_byte_cnt==3)
            ro_data <= r_frame_cnt[7:0];
        else if( r_send_lock && ~i_full && r_send_byte_cnt==4)
            ro_data <= r_frame_cnt[15:8];
        else if( r_valid_cnt == 1 && r_send_byte_cnt > 4 && r_send_byte_cnt < P_SEND_BYTE_LENGTH-1) //接收完12字节数据,同时算出CRC校验码/累加和码
            ro_data <= r_recive_container[7:0];
        else if(r_valid_cnt == 1 && r_send_byte_cnt == P_SEND_BYTE_LENGTH - 1)
            ro_data <= r_CRC_Reg[7:0];
        else if(r_valid_cnt == 1 && r_send_byte_cnt== P_SEND_BYTE_LENGTH)
            ro_data <= r_CRC_Reg[15:8];
        else
            ro_data <= ro_data;
    end
    
    //帧计数器的赋值
    always @(posedge i_clk, negedge i_rst_n) begin
        if(~i_rst_n)
            r_frame_cnt <= 'd0;
        else if(r_byte_cnt == (P_SEND_BYTE_LENGTH - 4-2)) //提前1时钟周期,让最后一个数据接收完的时候,把
            r_frame_cnt <= r_frame_cnt + 1;
        else
            r_frame_cnt <= r_frame_cnt;
    end
    endmodule
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    • 154
    • 155
    • 156
    • 157
    • 158
    • 159
    • 160
    • 161
    • 162
    • 163
    • 164
    • 165
    • 166
    • 167
    • 168
    • 169
    • 170
    • 171
    • 172
    • 173
    • 174
    • 175
    • 176
    • 177
    • 178
    • 179
    • 180
    • 181
    • 182
    • 183
    • 184
    • 185
    • 186
    • 187
    • 188
    • 189
    • 190
    • 191
    • 192
    • 193
    • 194
    • 195
    • 196
    • 197
    • 198
    • 199
    • 200
    • 201
    • 202
    • 203
    • 204
    • 205
    • 206
    • 207
    3.2.2 单字节发送的顶层模块Tx_Drive

    主要功能:用来控制底层模块uart_tx模块

    参数控制

    • P_SYS_CLOCK:输入时钟,默认50Mhz.
    • P_UART_BAUDRATE:波特率,默认9600
    • P_UART_STOP_WIDTH:停止位长度,默认为1
    • P_UART_PARITY_WIDTH:1代表奇校验,2代表偶校验,0代表无校验,默认奇校验
    • P_OVER_SAMPLE:过采样倍数,默认16倍

    框图:

    在这里插入图片描述

    参考代码

    `timescale 1ns / 1ps
    
    module Tx_Drive
    #(
        parameter               P_SYS_CLOCK           =   50_000_000    ,   //系统时钟可配置,默认50MHZ
        parameter               P_UART_BAUDRATE       =   256000        ,   //波特率可配置,默认9600
        parameter               P_UART_STOP_WIDTH     =   1             ,   //停止位宽度,默认1位
        parameter               P_UART_PARITY_WIDTH   =   1             ,   //校验位,默认0,0不校验,1奇校验,2偶校验
        parameter               P_OVER_SAMPLE         =   16                //接收模块的过采样次数
    )
    (
        input                   i_sys_clk                               ,
        input                   i_rst_n                                 ,
        input                   i_valid                                 ,
        input   [7:0]           i_data                                  ,
        input                   i_empty                                 ,
    
        output                  o_tx                                    ,
        output                  o_tx_ready
    
    );
    
    localparam                  BAUD_CNT = P_SYS_CLOCK / P_UART_BAUDRATE;
    
    wire                        wo_tx                   ;
    wire                        w_uart_tx_ready         ;
    reg                         w_tx_drive_ready        ;
    
    assign                      o_tx_ready = w_uart_tx_ready && ~i_empty && w_tx_drive_ready;
    assign                      o_tx = wo_tx;
    wire                        o_tx_end;
    
    uart_tx
    #(
        .P_SYS_CLOCK            (P_SYS_CLOCK        )   ,   //系统时钟可配置,默认50MHZ
        .P_UART_BAUDRATE        (P_UART_BAUDRATE    )   ,   //波特率可配置,默认9600
        .P_UART_DATA_WIDTH      (8                  )   ,   //发送数据位可配置,默认8位
        .P_UART_STOP_WIDTH      (P_UART_STOP_WIDTH  )   ,   //停止位宽度,默认1位
        .P_UART_PARITY_WIDTH    (P_UART_PARITY_WIDTH)       //校验位宽度,0为无校验,1为奇校验,2位偶校验
    
    )
    uart_tx_u0
    (
        .i_clk                  (i_sys_clk          )   ,
        .i_rst                  (i_rst_n            )   ,
        .i_user_tx_data         (i_data             )   ,   //用户待发送的数据
        .i_user_tx_data_valid   (i_valid            )   ,   //数据发送的有效信号
    
        .o_uart_tx              (wo_tx              )   ,
        .o_user_tx_ready        (w_uart_tx_ready    )   ,   //数据准备信号,当有效信号和准备信号同时为高的时候,发送的数据才是有效的
        .o_user_tx_end          (o_tx_end           )
    );
    
    reg                         o_tx_end_1d;
    reg         [3:0]           work_cnt;
    localparam                  P_CNT_MAX = 3;
    
    always @(posedge i_sys_clk,negedge i_rst_n) begin
        if(~i_rst_n)
            o_tx_end_1d <= 'd0;
        else
            o_tx_end_1d <= o_tx_end;
    end
    
    always @(posedge i_sys_clk,negedge i_rst_n) begin
        if(~i_rst_n)
            work_cnt <= 'd0;
        else if(~w_tx_drive_ready&& ~i_empty&&work_cnt
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    3.2.3 单字节发送模块uart_tx

    主要功能:输入一个5/6/7/8位数据,将这个数据转换成一个串行数据发送出去。

    参数控制:

    • P_SYS_CLOCK:输入时钟,默认50Mhz。
    • P_UART_BAUDRATE:波特率,默认9600。
    • P_UART_DATA_WIDTH:输入数据宽度,默认发送8bit数据。
    • P_UART_STOP_WIDTH:停止位宽度,默认为1.
    • P_UART_PARITY_WIDTH:校验位,默认无校验0,1奇校验,2偶校验。
    • P_OVER_SAMPLE:过采样倍数,默认16倍采样。

    状态图

    在这里插入图片描述

    状态图解释:当模块处于空闲的时候是P_IDLE状态,当输入端数据有限信号拉高的同时本模块空闲的时候(即o_tx_ready为1),此时开始进入发送数据的起始位状态P_START,即bit计数器1。当bit计数器大于1的同时小于等于P_UART_DATA_WIDTH的时候,处于发送数据状态P_DATA。当发送完数据之后,判断是否需要发送校验位,当P_UART_PARITY_WIDTH大于0的时候,就进入校验状态,发送校验位;如果P_UART_PARITY_WIDTH等于0,就直接进入停止状态。最后都经过1bit的时间同时本模块空闲的时候,进入空闲状态;如果经过1bit之后,发现本模块还是处于忙碌状态(即o_tx_ready0)就说要发下一个数据,直接进入起始状态。

    参考代码:

    `timescale 1ns / 1ns
    
    module uart_tx
    #(
        parameter                       P_SYS_CLOCK           =   50_000_000    ,   //系统时钟可配置,默认50MHZ
        parameter                       P_UART_BAUDRATE       =   9600          ,   //波特率可配置,默认9600
        parameter                       P_UART_DATA_WIDTH     =   8             ,   //发送数据位可配置,默认8位
        parameter                       P_UART_STOP_WIDTH     =   1             ,   //停止位宽度,默认1位
        parameter                       P_UART_PARITY_WIDTH   =   0             ,   //校验位宽度,0为无校验,1为奇校验,2位偶校验
        parameter                       P_OVER_SAMPLE         =   16                //接收模块的过采样次数
    
    )
    (
        input                           i_clk                                   ,   //输入的时钟是波特率的16倍时钟,所以发出数据要持续16个时钟周期
        input                           i_rst                                   ,
        output                          o_uart_tx                               ,
    
        input   [P_UART_DATA_WIDTH-1:0] i_user_tx_data                          ,   //用户待发送的数据+校验位+停止位
        input                           i_user_tx_data_valid                    ,   //数据发送有效信号
        output                          o_user_tx_ready                         ,   //数据准备信号,当有效信号和准备信号同时为高的时候,发送的数据才是有效的
        output                          o_user_tx_end
    );
    
    
    
    /****************************状态机写法*********************************************/
    /***************function**************/
    /***************parameter*************/
    localparam                          P_IDLE    =   4'd0                      ,   //状态机的定义
                                        P_START   =   4'd1                      ,
                                        P_DATA    =   4'd2                      ,
                                        P_PARITY  =   4'd3                      ,
                                        P_STOP    =   4'd4                      ;
    /***************port******************/             
    /***************mechine***************/
    /***************reg*******************/
    reg         [3:0]                   ct,nt                                   ;   //状态机的现态和次态
    reg                                 ro_uart_tx                              ;
    reg                                 ro_user_tx_ready                        ;   
    reg         [15:0]                  r_bit_cnt                               ;
    reg         [7:0]                   r_clk_cnt                               ;   //计数到16为一个波特率的时钟周期
    reg         [P_UART_DATA_WIDTH-1:0] r_tx_data                               ;   //移位寄存器
    reg                                 r_parity                                ;
    /***************wire******************/
    wire                                w_tx_active                             ;
    reg                                 o_tx_end                                ;
    reg                                 o_tx_end_1d                             ;
    /***************component*************/
    /***************assign****************/
    assign                              o_uart_tx       = ro_uart_tx            ;
    assign                              o_user_tx_ready = ro_user_tx_ready      ;
    assign                              w_tx_active     = i_user_tx_data_valid & ro_user_tx_ready;  //两个同是为高表示可以开始发送数据
    assign                              o_user_tx_end   = o_tx_end_1d           ;
    /***************always****************/
    always @(posedge i_clk,negedge i_rst) begin     //当激活信号开始的时候,就开始产生技术使能,让时钟计数16个周期为一个波特时钟周期
        if(~i_rst)
            r_clk_cnt <= 'd0;
        else if(r_clk_cnt == P_OVER_SAMPLE-1)
            r_clk_cnt <= 'd0;
        else if(~ro_user_tx_ready)
            r_clk_cnt <= r_clk_cnt + 1;
        else
            r_clk_cnt <= 'd0;
    
    end
    
    /***************状态机****************/
    always @(posedge i_clk, negedge i_rst) begin            //状态机第一段:同步时序描述状态转移
        if(!i_rst)
            ct = P_IDLE;
        else
            ct = nt;
    end
    
    always@(*) begin                                        //状态机第二段:组合逻辑判断状态转移条件,描述状态转移规律
        case (ct)
            P_IDLE: begin
                if(w_tx_active)
                    nt = P_START;
                else
                    nt = P_IDLE;
            end
            P_START: begin
                if(r_clk_cnt == P_OVER_SAMPLE-1)   
                    nt = P_DATA;
                else
                    nt = P_START;
            end
            P_DATA: begin
                if(r_bit_cnt ==  P_UART_DATA_WIDTH && r_clk_cnt == P_OVER_SAMPLE-1 &&P_UART_PARITY_WIDTH > 0)
                    nt = P_PARITY;
                else if(r_bit_cnt == P_UART_DATA_WIDTH && r_clk_cnt == P_OVER_SAMPLE-1 && P_UART_PARITY_WIDTH == 0)
                    nt = P_STOP;
                else
                    nt = P_DATA;
            end
            P_PARITY: begin
                if(r_bit_cnt >= (P_UART_DATA_WIDTH +1) && P_UART_PARITY_WIDTH>0 &&r_clk_cnt == P_OVER_SAMPLE-1)
                    nt = P_STOP;
                else
                    nt = P_PARITY;
            end
            P_STOP: begin
                if(r_bit_cnt == P_UART_DATA_WIDTH + 1 + P_UART_STOP_WIDTH && P_UART_PARITY_WIDTH>0 && r_clk_cnt == P_OVER_SAMPLE-1)
                    nt = P_IDLE;
                else if(r_bit_cnt == P_UART_DATA_WIDTH + P_UART_STOP_WIDTH && !P_UART_PARITY_WIDTH && r_clk_cnt == P_OVER_SAMPLE-1)
                    nt = P_IDLE;
                else
                    nt = P_STOP;
            end
            default: nt = P_IDLE;
        endcase
    end
    
    
    always @(posedge i_clk, negedge i_rst) begin
        if(~i_rst)
            ro_user_tx_ready <= 'd1;
        else if(w_tx_active)
            ro_user_tx_ready <= 'd0;
        else if(r_bit_cnt == P_UART_DATA_WIDTH+P_UART_STOP_WIDTH && !P_UART_PARITY_WIDTH && r_clk_cnt == P_OVER_SAMPLE-1)              //发送完数据拉高,表示tx模块已经空闲了
            ro_user_tx_ready <= 'd1;
        else if(r_bit_cnt == P_UART_DATA_WIDTH+1+P_UART_STOP_WIDTH && P_UART_PARITY_WIDTH>0 && r_clk_cnt == P_OVER_SAMPLE-1)              //发送完数据拉高,表示tx模块已经空闲了
            ro_user_tx_ready <= 'd1;
        else
            ro_user_tx_ready <= ro_user_tx_ready;
    end
    
    always @(posedge i_clk, negedge i_rst) begin
        if(!i_rst)
            r_bit_cnt <= 'd0;
        else if(r_bit_cnt == P_UART_DATA_WIDTH+P_UART_STOP_WIDTH && P_UART_PARITY_WIDTH==0 && r_clk_cnt == P_OVER_SAMPLE-1)
            r_bit_cnt <= 'd0;
        else if(r_bit_cnt == P_UART_DATA_WIDTH+1+P_UART_STOP_WIDTH && P_UART_PARITY_WIDTH>0 && r_clk_cnt == P_OVER_SAMPLE-1)
            r_bit_cnt <= 'd0;
        else if(!ro_user_tx_ready&& r_clk_cnt == P_OVER_SAMPLE-1)
            r_bit_cnt <= r_bit_cnt + 1;
        else
            r_bit_cnt <= r_bit_cnt;
    end
    
    always @(posedge i_clk, negedge i_rst) begin
        if(!i_rst)
            r_tx_data <= 'd0;
        else if(w_tx_active)
            r_tx_data <= i_user_tx_data;
        else if(r_bit_cnt>0&&r_bit_cnt<=P_UART_DATA_WIDTH && r_clk_cnt == P_OVER_SAMPLE-2)
            r_tx_data <= r_tx_data >> 1;
        else
            r_tx_data <= r_tx_data;
    end
    
    always @(posedge  i_clk,negedge i_rst) begin
        if(~i_rst)
            r_parity <= 1'b0;
        else if(ct == P_STOP)
            r_parity <= 1'b0;
        else if(ct == P_DATA && r_clk_cnt == P_OVER_SAMPLE-1)
            r_parity <= r_parity ^ r_tx_data[0];
        else
            r_parity <= r_parity;
    end
    
    always @(posedge i_clk, negedge i_rst) begin            //状态机第三段:时序逻辑描述输出
        if(!i_rst)
            ro_uart_tx <= 1'b1;
        else case(ct)
            P_IDLE  :   ro_uart_tx <= 1'b1;
            P_START :   ro_uart_tx <= 1'b0;
            P_DATA  :   ro_uart_tx <= r_tx_data[0];
            P_PARITY:   ro_uart_tx <= P_UART_PARITY_WIDTH==1?~r_parity:r_parity;
            P_STOP  :   ro_uart_tx <= 1'b1;
            default :   ro_uart_tx <= 1'b1;
        endcase
    end
    
    always @(posedge i_clk, negedge i_rst) begin        //结束标志,为高的时候代表空闲
        if(!i_rst)
            o_tx_end <= 1'b1;
        else if(ct == P_START)
            o_tx_end <= 1'b0;
        else if(ct == P_STOP && r_clk_cnt == P_OVER_SAMPLE-2)
            o_tx_end <= 1'b1;
        else
            o_tx_end <= o_tx_end;
    end
    always @(posedge i_clk, negedge i_rst) begin
        if(!i_rst)
            o_tx_end_1d <= 1'b0;
        else
            o_tx_end_1d <= o_tx_end;
    end
    endmodule
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    • 154
    • 155
    • 156
    • 157
    • 158
    • 159
    • 160
    • 161
    • 162
    • 163
    • 164
    • 165
    • 166
    • 167
    • 168
    • 169
    • 170
    • 171
    • 172
    • 173
    • 174
    • 175
    • 176
    • 177
    • 178
    • 179
    • 180
    • 181
    • 182
    • 183
    • 184
    • 185
    • 186
    • 187
    • 188
    • 189
    • 190
    • 191
    • 192
    • 193
    3.3 解帧使用
    2.3.1 单字节接收模块顶层Rx_Drive

    主要功能:控制单字节接收模块。

    参数控制

    • P_SYS_CLOCK:输入时钟,默认50Mhz。
    • P_UART_BAUDRATE:波特率,默认9600。
    • P_UART_STOP_WIDTH:停止位宽度,默认为1.
    • P_UART_PARITY_WIDTH:校验位,默认无校验0,1奇校验,2偶校验。
    • P_OVER_SAMPLE:过采样倍数,默认16倍采样。

    参考代码

    `timescale 1ns / 1ns
    
    module Rx_Drive#(
        parameter               P_SYS_CLOCK           =   50_000_000    ,   //系统时钟可配置,默认50MHZ
        parameter               P_UART_BAUDRATE       =   9600          ,   //波特率可配置,默认9600
        parameter               P_UART_STOP_WIDTH     =   1             ,   //停止位宽度,默认1位
        parameter               P_UART_PARITY_WIDTH   =   1             ,   //校验位,默认0,0不校验,1奇校验,2偶校验
        parameter               P_OVER_SAMPLE         =   16                //过采样的时钟的倍率
    )
    (
        input                   i_clk                                   ,   //输入时钟乃过采样时钟
        input                   i_rst_n                                 ,
        input                   i_full                                  ,   //判断待写入的FIFO是否已满
        input                   i_rx                                    ,   
    
        output                  o_rx_valid                              ,
        output      [7:0]       o_rx_data
    );
    
    
    //接收数据模块
    uart_rx
    #(
        .P_SYS_CLOCK            (P_SYS_CLOCK        )    ,   //系统时钟可配置,默认50MHZ
        .P_UART_BAUDRATE        (P_UART_BAUDRATE    )    ,   //波特率可配置,默认9600
        .P_UART_STOP_WIDTH      (P_UART_STOP_WIDTH  )    ,   //停止位宽度,默认1位
        .P_UART_PARITY_WIDTH    (P_UART_PARITY_WIDTH)    ,   //校验位,默认0,
        .P_OVER_SAMPLE          (P_OVER_SAMPLE      )        //过采样的时钟的倍率
    )
    uart_rx_u0 
    (
        .i_over_clk             (i_clk              )    ,   //过采样时钟
        .i_rst_n                (i_rst_n            )    ,
        .i_rx                   (i_rx               )    ,   //判断待写入的FIFO是否已满
        /***********************/
        .sys_clk                (sys_clk            )   ,
        /***********************/
        .o_rx_valid             (o_rx_valid         )    ,
        .o_rx_data              (o_rx_data          )
    );
    endmodule
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    3.3.2 单字节接收模块uart_rx

    主要功能:将输入的5/6/7/8并行数据,串行的发送出去

    参数控制

    • P_SYS_CLOCK:输入时钟,默认50Mhz。
    • P_UART_BAUDRATE:波特率,默认9600。
    • P_UART_DATA_WIDTH:输入数据宽度,默认发送8bit数据。
    • P_UART_STOP_WIDTH:停止位宽度,默认为1.
    • P_UART_PARITY_WIDTH:校验位,默认无校验0,1奇校验,2偶校验。
    • P_OVER_SAMPLE:过采样倍数,默认16倍采样。

    状态图

    在这里插入图片描述

    状态图解释:开始的时候处于空闲状态P_IDLE,当检测起始位,进入起始位状态P_START,这个状态持续到起始位结束,即bit==0。当bit>0且小于等于P_UART_DATA_WIDTH时候,进入接受数据状态P_DATA,接收完了。根据P_UART_PARITY_WIDTH是否大于0,判断是否进入校验状态P_PARITY。如果P_UART_PARITY_WIDTH大于0,就进入校验状态P_PARITY,然后经过1bit时间,就进入停止状态,即对应停止位P_STOP.否则就直接进入停止状态,然后经过P_UART_STOP_WIDTH个bit时间长度进入P_IDLE.之所以没有直接接入起始位,是因为在发送端设计的时候会一定有空闲状态产生,具体可以看发送端握手信号o_tx_ready。

    参考代码:两种实现方式,计数器和状态机,都能用,用的时候只需要注释另一个。

    `timescale 1ns / 1ps
    /*本模块采用的全在中间采样,包括起始位的判断也是*/
    
    module uart_rx
    #(
        parameter               P_SYS_CLOCK             =   50_000_000  ,   //系统时钟可配置,默认50MHZ
        parameter               P_UART_BAUDRATE         =   9600        ,   //波特率可配置,默认9600
        parameter               P_UART_PARITY_WIDTH     =   1           ,   //校验位,默认0,
        parameter               P_UART_STOP_WIDTH       =   1           ,   //停止位宽度,默认1位
        parameter               P_OVER_SAMPLE           =   16              //过采样的时钟的倍率
    )   
    (
        input                   i_over_clk                              ,   //过采样时钟
        input                   i_rst_n                                 ,
        input                   i_rx                                    ,   //判断待写入的FIFO是否已满
        output                  o_rx_valid                              ,
        output      [7:0]       o_rx_data
    );
    
    /****************************计数器写法*********************************************/
    localparam                  P_OVER_CHECK_MAX = P_OVER_SAMPLE/2; //控制采样到什么位置停,范围在P_OVER_SAMPL/2-P_OVER_SAMPLE
    /***************function**************/
    /***************parameter*************/
    /***************port******************/
    /***************mechine***************/
    /***************reg*******************/
    reg         [2:0]               r_rx_reg        ;   //延迟打拍
    reg                             ri_rx_1d        ;   //打拍
    reg                             ri_rx_2d        ;   //打拍
    reg                             r_over_en       ;
    reg         [7:0]               r_over_cnt      ;   //过采样计数器
    reg                             r_start_flag    ;   //检测是否是起始位的信号
    reg                             r_bit_en        ;       
    reg         [7:0]               r_bit_cnt       ;
    reg                             r_get_flag      ;   //数据的过采样点
    reg         [7:0]               r_data_container;   //装数据的移位寄存器
    reg                             r_check         ;   //生成奇偶校验
    
    reg         [7:0]               ro_rx_data      ;
    reg                             ro_rx_valid     ;
    
    /***************wire******************/
    /***************component*************/
    /***************assign****************/
    assign                          o_rx_valid = ro_rx_valid;
    assign                          o_rx_data = ro_rx_data  ;
    /***************always****************/
    always @(posedge i_over_clk,negedge i_rst_n) begin  //打拍
        if(!i_rst_n) begin
            r_rx_reg <= 3'b111;
            ri_rx_1d <= 'd0;
            ri_rx_2d <= 'd0;
        end
        else begin
            r_rx_reg <= {r_rx_reg[1:0],i_rx};
            ri_rx_1d <= r_rx_reg[2];
            ri_rx_2d <= ri_rx_1d;
        end
    end
    
    always @(posedge i_over_clk,negedge i_rst_n) begin
        if(!i_rst_n)
            r_over_en <= 'd0;
        else if(r_bit_cnt == 8+1+P_UART_STOP_WIDTH && r_over_cnt == P_OVER_SAMPLE-1 && P_UART_PARITY_WIDTH>0)
            r_over_en <= 'd0;
        else if(r_bit_cnt == 8+P_UART_STOP_WIDTH && r_over_cnt == P_OVER_SAMPLE-1 && P_UART_PARITY_WIDTH==0)
            r_over_en <= 'd0;
        else if(r_bit_cnt == 0 && r_over_cnt == P_OVER_CHECK_MAX+1&& r_bit_en==0)   //异常归零
            r_over_en <= 'd0;
        else if(r_rx_reg[2]==0 && ri_rx_1d)
            r_over_en <= 'd1;
        else
            r_over_en <= r_over_en;
    end
    
    always @(posedge i_over_clk,negedge i_rst_n) begin
        if(!i_rst_n)
            r_over_cnt <= 'd0;
        else if(r_over_cnt == P_OVER_SAMPLE -1)
            r_over_cnt <= 'd0;
        else if(r_bit_cnt == 0 && r_over_cnt == P_OVER_CHECK_MAX+1&& r_bit_en==0)   //异常归零
            r_over_cnt <= 'd0;
        else if(r_over_en)
            r_over_cnt <= r_over_cnt + 1;
        else
            r_over_cnt <= r_over_cnt;
    end
    
    
    always @(posedge i_over_clk,negedge i_rst_n) begin
        if(!i_rst_n)
            r_start_flag <= 'd0;
        else if(r_bit_cnt==0 && r_over_cnt == 8'd7 && ri_rx_1d==0 && r_bit_en==0)
            r_start_flag <= 'd1;
        else
            r_start_flag <= 'd0;
    end
    
    
    always @(posedge i_over_clk,negedge i_rst_n) begin
        if(!i_rst_n)
            r_bit_en <= 'd0;
        else if(r_bit_cnt == 8+1+P_UART_STOP_WIDTH && r_over_cnt == P_OVER_SAMPLE-1 && P_UART_PARITY_WIDTH>0)
            r_bit_en <= 'd0;
        else if(r_bit_cnt == 8+P_UART_STOP_WIDTH && r_over_cnt == P_OVER_SAMPLE-1 && P_UART_PARITY_WIDTH==0)
            r_bit_en <= 'd0;
        else if(r_start_flag)
            r_bit_en <= 'd1;
        else
            r_bit_en <= r_bit_en;
    end
        
    always @(posedge i_over_clk,negedge i_rst_n) begin
        if(!i_rst_n)
            r_bit_cnt <= 'd0;
        else if(r_bit_cnt == 8+1+P_UART_STOP_WIDTH && P_UART_STOP_WIDTH>0 && r_over_cnt == P_OVER_SAMPLE-1)
            r_bit_cnt <= 'd0;
        else if(r_bit_cnt == 8+P_UART_STOP_WIDTH && P_UART_STOP_WIDTH==0 && r_over_cnt == P_OVER_SAMPLE-1)
            r_bit_cnt <= 'd0;
        else if(r_bit_en && r_over_cnt == P_OVER_SAMPLE-1)
            r_bit_cnt <= r_bit_cnt + 1;
        else
            r_bit_cnt <= r_bit_cnt;
    end
    
    always @(posedge i_over_clk,negedge i_rst_n) begin
        if(!i_rst_n)
            r_get_flag <= 'd0;
        else if(r_bit_cnt >8'd0 && r_bit_cnt <8'd9 && r_over_cnt == P_OVER_CHECK_MAX-1)
            r_get_flag <= 'd1;
        else
            r_get_flag <= 'd0;
    end
    
    always @(posedge i_over_clk,negedge i_rst_n) begin
        if(!i_rst_n)
            r_data_container <= 'd0;
        else if(r_bit_cnt == 8+1+P_UART_STOP_WIDTH && P_UART_STOP_WIDTH>0 && r_over_cnt == P_OVER_SAMPLE-1)
            r_data_container <= 'd0;
        else if(r_bit_cnt == 8+P_UART_STOP_WIDTH && P_UART_STOP_WIDTH==0 && r_over_cnt == P_OVER_SAMPLE-1)
            r_data_container <= 'd0;
        else if(r_get_flag)
            r_data_container <= {ri_rx_2d,r_data_container[7:1]};
        else
            r_data_container <= r_data_container;
    
    end
    
    always @(posedge i_over_clk,negedge i_rst_n) begin
        if(!i_rst_n)
            r_check <= 'd0;
         else if(r_bit_cnt == 8+1+P_UART_STOP_WIDTH && P_UART_STOP_WIDTH>0 && r_over_cnt == P_OVER_SAMPLE-1)
            r_check <= 'd0;
        else if(r_bit_cnt == 8+P_UART_STOP_WIDTH && P_UART_STOP_WIDTH==0 && r_over_cnt == P_OVER_SAMPLE-1)
            r_check <= 'd0;
        else if(r_get_flag)
            r_check <= ri_rx_2d^r_check;
        else if(r_bit_cnt == 8'd9 && P_UART_PARITY_WIDTH == 1&& r_over_cnt == 0)  //在数据位后面时候就生成校验值,偶校验直接是异或值
            r_check <= !r_check;
        else
            r_check <= r_check;
    end
    
    always @(posedge i_over_clk,negedge i_rst_n) begin
        if(!i_rst_n)
            ro_rx_data <= 'd0;
        else if(r_bit_cnt == 9)    //直接输出
            ro_rx_data <= r_data_container;
        else
            ro_rx_data <= ro_rx_data;
    
    end
    
    always @(posedge i_over_clk,negedge i_rst_n) begin
        if(!i_rst_n)
            ro_rx_valid <= 'd0;
        else if(r_bit_cnt == 8'd9 && P_UART_PARITY_WIDTH>0 && r_over_cnt == 1)
            ro_rx_valid <= r_check == ri_rx_2d?1:0;
        else if(r_bit_cnt == 8'd9 && P_UART_PARITY_WIDTH == 0 && r_over_cnt == 1)
            ro_rx_valid <= 'd1; 
        else
            ro_rx_valid <= 'd0;
    end
    
    /****************************状态机写法*********************************************/
    // /***************function**************/
    // localparam                  P_OVER_CHECK_MAX = P_OVER_SAMPLE/2; //控制采样到什么位置停,范围在P_OVER_SAMPL/2-P_OVER_SAMPLE
    
    // /***************parameter*************/
    // localparam                  P_IDLE   = 4'd0 ,
    //                             P_START  = 4'd1 ,
    //                             P_DATA   = 4'd2 ,
    //                             P_PARITY = 4'd3 ,
    //                             P_STOP   = 4'd4 ,
    //                             P_ERROR  = 4'd5 ;
    
    
    // /***************port******************/          
    // /***************mechine***************/
    // /***************reg*******************/
    // reg                         ro_rx_valid     ;   //输出数据有效信号的寄存器
    // reg         [7:0]           ro_rx_data      ;   //输出数据
    
    // reg                         ri_rx_d1        ;   //消除亚稳态
    // reg                         ri_rx_d2        ;
    // reg                         ri_rx_d3        ;
    
    // reg                         ri_rx_1d        ;   //将输入数据延迟一拍
    // reg                         ri_rx_2d        ;   //将输入数据再延迟一拍
    // reg                         r_cnt_en        ;   //开始计数的使能信号
    // reg         [7:0]           r_over_cnt      ;   //超采样计数器
    // reg                         r_start_flag    ;   //在第一个低电平的中点点,采样,从而判断起始位
    // reg                         r_bit_en        ;   //当检测起始信号之后,就开始拉高bit计数器的使能
    // reg         [7:0]           r_bit_cnt       ;   //bit计数器
    // reg                         r_get_flag      ;   //采样数据的采样信号
    // reg                         r_check         ;   //进行奇偶校验的
    // reg         [7:0]           r_data_container;   //数据移位寄存器
    
    // reg         [3:0]           ct,nt       ;   //保存状态的状态机
    
    // /***************wire******************/
    // /***************component*************/
    // /***************assign****************/
    // assign                      o_rx_valid = ro_rx_valid;
    // assign                      o_rx_data  = ro_rx_data ;
    // /***************always****************/
    // always @(posedge i_over_clk,negedge i_rst_n) begin  //延迟信号
    //     if(!i_rst_n) begin
    //         ri_rx_1d <= 'd0;
    //         ri_rx_2d <= 'd0;
    
    //         ri_rx_d1 <= 'd0;
    //         ri_rx_d2 <= 'd0;
    //         ri_rx_d3 <= 'd0;
    //     end
    //     else begin
    //         ri_rx_1d <= ri_rx_d3;
    //         ri_rx_2d <= ri_rx_1d;
    
    //         ri_rx_d1 <= i_rx;
    //         ri_rx_d2 <= ri_rx_d1;
    //         ri_rx_d3 <= ri_rx_d2;
    //     end
    // end
    
    // always @(posedge i_over_clk,negedge i_rst_n) begin  //当检测到下降沿的时候开始计数的使能信号
    //     if(!i_rst_n)
    //         r_cnt_en <= 'd0;
    //     else if(ct == P_STOP && r_over_cnt == P_OVER_SAMPLE-1)   //当停止状态的时候归零
    //         r_cnt_en <= 'd0;
    //     else if(ri_rx_1d && !ri_rx_d3)
    //         r_cnt_en <= 'd1;
    //     else
    //         r_cnt_en <= r_cnt_en;
    
    // end
    // always @(posedge i_over_clk,negedge i_rst_n) begin  //超采样时钟
    //     if(!i_rst_n)
    //         r_over_cnt <= 'd0;
    //     else if(r_over_cnt == P_OVER_SAMPLE-1)  //正常归零
    //         r_over_cnt <= 'd0;
    //     else if(ct == P_IDLE && r_over_cnt == P_OVER_CHECK_MAX +2 && !r_bit_cnt )   //异常归零,即开始计数后,在1bit的时间内发现这个低电平不是起始信号
    //         r_over_cnt <= 'd0;
    //     else if(r_cnt_en)
    //         r_over_cnt <= r_over_cnt + 8'd1;
    //     else
    //         r_over_cnt <= r_over_cnt;
    // end
    
    // always @(posedge i_over_clk,negedge i_rst_n) begin  //检测是否是起始位的信号
    //     if(!i_rst_n)
    //         r_start_flag <= 'd0;
    //     else if(ct == P_IDLE && r_over_cnt == P_OVER_CHECK_MAX-1 && ri_rx_1d == 0)   //在计数到中点(7)的时候,就进行检查是否是起始位
    //         r_start_flag <= 'd1;
    //     else
    //         r_start_flag <= 'd0;
    // end
    
    // always @(posedge i_over_clk,negedge i_rst_n) begin  //开始比特计数器的使能信号
    //     if(!i_rst_n)
    //         r_bit_en <= 'd0;
    //     else if(ct == P_STOP && r_over_cnt == P_OVER_SAMPLE-1)
    //         r_bit_en <= 'd0;
    //     else if(r_start_flag)
    //         r_bit_en <= 'd1;
    //     else
    //         r_bit_en <= r_bit_en;
    // end
    // always @(posedge i_over_clk,negedge i_rst_n) begin  //比特计数器
    //     if(!i_rst_n)
    //         r_bit_cnt <= 'd0;
    //     else if(ct == P_STOP && r_over_cnt == P_OVER_SAMPLE-1)  //无校验和校验正确的时,归零
    //         r_bit_cnt <= 'd0;
    //     else if(ct == P_ERROR && r_over_cnt == P_OVER_SAMPLE-1)  //校验错误时,也归零
    //         r_bit_cnt <= 'd0;
    //     else if(r_bit_en && r_over_cnt == P_OVER_SAMPLE-1)
    //         r_bit_cnt <= r_bit_cnt + 8'd1;
    //     else
    //         r_bit_cnt <= r_bit_cnt;
    // end
    
    // always @(posedge i_over_clk,negedge i_rst_n) begin  //中间采样信号,应该在中点的前一时刻
    //     if(!i_rst_n)
    //         r_get_flag <= 'd0;
    //     else if(ct == P_DATA && r_over_cnt == P_OVER_CHECK_MAX-2)
    //         r_get_flag <= 'd1;
    //     else
    //         r_get_flag <= 'd0;
    // end
    
    // always @(posedge i_over_clk,negedge i_rst_n) begin  //累计异或,从而算出校验位,跟传输校验位相比
    //     if(!i_rst_n)
    //         r_check <= 'd0;
    //     else if(ct == P_STOP || ct == P_ERROR)
    //         r_check <= 'd0;
    //     else if(r_get_flag)
    //         r_check <= r_check^ri_rx_2d;
    //     else
    //         r_check <= r_check;
    // end
    // always @(posedge i_over_clk,negedge i_rst_n) begin  //采样8位数据
    //     if(!i_rst_n)
    //         r_data_container <= 'd0;
    //     else if(ct == P_STOP && r_over_cnt == P_OVER_SAMPLE-1)
    //         r_data_container <= 'd0;
    //     else if(ct == P_DATA && r_get_flag)
    //         r_data_container <= {ri_rx_2d,r_data_container[7:1]};
    //     else
    //         r_data_container <= r_data_container;
    // end
    
    
    // //状态机第一段:时序逻辑描述现态和次态转移
    // always @(posedge i_over_clk,negedge i_rst_n) begin
    //     if(!i_rst_n)
    //         ct = P_IDLE;
    //     else
    //         ct = nt;
    // end
    // //状态机第二段:组合逻辑描述状态转移规律
    // always@(*)begin
    //     case (ct)
    //         P_IDLE:begin
    //             if(r_start_flag)
    //                 nt = P_START;
    //             else
    //                 nt = P_IDLE;
    //         end
    //         P_START:begin
    //             if(r_bit_cnt>0 && r_bit_cnt < 8'd9) //数据位八位
    //                 nt = P_DATA;
    //             else
    //                 nt = P_START;
    //         end
    //         P_DATA:begin
    //             if(r_bit_cnt > 8'd8 && P_UART_PARITY_WIDTH == 0)   //无校验位
    //                 nt = P_STOP;
    //             else if(r_bit_cnt > 8'd8 && P_UART_PARITY_WIDTH > 0)   //无校验位
    //                 nt = P_PARITY;
    //             else
    //                 nt = P_DATA;
    //         end
    //         P_PARITY:begin
    //             if((r_bit_cnt > 8'd9 && ~r_check == ri_rx_2d && P_UART_PARITY_WIDTH == 1)||(r_bit_cnt > 8'd9 && r_check == ri_rx_2d && P_UART_PARITY_WIDTH == 2))
    //                 nt = P_STOP;
    //             else if((r_bit_cnt > 8'd9 && ~r_check != ri_rx_2d && P_UART_PARITY_WIDTH == 1)||(r_bit_cnt > 8'd9 && r_check != ri_rx_2d && P_UART_PARITY_WIDTH == 2))
    //                 nt = P_ERROR;
    //             else
    //                 nt = P_PARITY;
    //         end
    //         P_STOP:begin
    //             if(P_UART_PARITY_WIDTH>0 && r_bit_cnt == 8+1+P_UART_STOP_WIDTH && r_over_cnt == P_OVER_SAMPLE-1)    //有校验位的情况
    //                 nt = P_IDLE;
    //             else if(P_UART_PARITY_WIDTH==0 && r_bit_cnt == 8+P_UART_STOP_WIDTH && r_over_cnt == P_OVER_SAMPLE-1)    //无校验位的情况
    //                 nt = P_IDLE;
    //             else
    //                 nt = P_STOP;
    //         end
    //         P_ERROR:begin
    //             if(r_bit_cnt == 8+1+P_UART_STOP_WIDTH && r_over_cnt == P_OVER_SAMPLE-1) //进入错误状态,肯定有校验位
    //                 nt = P_IDLE;
    //             else
    //                 nt = P_STOP;
    //         end
    //         default:nt = P_IDLE; 
    //     endcase
    // end
    // //状态机第三段:时序逻辑描述输出
    // always @(posedge i_over_clk,negedge i_rst_n) begin  //输出数据
    //     if(!i_rst_n)
    //         ro_rx_data <= 'd0;
    //     else if(ct == P_PARITY && P_UART_PARITY_WIDTH >0)   //有校验
    //         ro_rx_data <= r_data_container;
    //     else if(ct == P_STOP && P_UART_PARITY_WIDTH == 0)   //无校验
    //         ro_rx_data <= r_data_container;
    //     else
    //         ro_rx_data <= ro_rx_data;
    // end
    // always @(posedge i_over_clk,negedge i_rst_n) begin  //输出数据的有效信号,只拉高一个时钟周期
    //     if(!i_rst_n)
    //         ro_rx_valid <= 'd0;
    //     else if(ct == P_PARITY && ~r_check == ri_rx_2d && P_UART_PARITY_WIDTH == 1 && r_over_cnt == P_OVER_CHECK_MAX-1) //奇校验
    //         ro_rx_valid <= 'd1;
    //     else if(ct == P_PARITY && r_check == ri_rx_2d && P_UART_PARITY_WIDTH == 2 && r_over_cnt == P_OVER_CHECK_MAX-1)  //偶校验
    //         ro_rx_valid <= 'd1;
    //     else if(ct == P_STOP && P_UART_PARITY_WIDTH == 0 && r_over_cnt == P_OVER_CHECK_MAX-1)   //无校验
    //         ro_rx_valid <= 'd1;
    //     else
    //         ro_rx_valid <= 'd0;
    // end
    endmodule
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    • 154
    • 155
    • 156
    • 157
    • 158
    • 159
    • 160
    • 161
    • 162
    • 163
    • 164
    • 165
    • 166
    • 167
    • 168
    • 169
    • 170
    • 171
    • 172
    • 173
    • 174
    • 175
    • 176
    • 177
    • 178
    • 179
    • 180
    • 181
    • 182
    • 183
    • 184
    • 185
    • 186
    • 187
    • 188
    • 189
    • 190
    • 191
    • 192
    • 193
    • 194
    • 195
    • 196
    • 197
    • 198
    • 199
    • 200
    • 201
    • 202
    • 203
    • 204
    • 205
    • 206
    • 207
    • 208
    • 209
    • 210
    • 211
    • 212
    • 213
    • 214
    • 215
    • 216
    • 217
    • 218
    • 219
    • 220
    • 221
    • 222
    • 223
    • 224
    • 225
    • 226
    • 227
    • 228
    • 229
    • 230
    • 231
    • 232
    • 233
    • 234
    • 235
    • 236
    • 237
    • 238
    • 239
    • 240
    • 241
    • 242
    • 243
    • 244
    • 245
    • 246
    • 247
    • 248
    • 249
    • 250
    • 251
    • 252
    • 253
    • 254
    • 255
    • 256
    • 257
    • 258
    • 259
    • 260
    • 261
    • 262
    • 263
    • 264
    • 265
    • 266
    • 267
    • 268
    • 269
    • 270
    • 271
    • 272
    • 273
    • 274
    • 275
    • 276
    • 277
    • 278
    • 279
    • 280
    • 281
    • 282
    • 283
    • 284
    • 285
    • 286
    • 287
    • 288
    • 289
    • 290
    • 291
    • 292
    • 293
    • 294
    • 295
    • 296
    • 297
    • 298
    • 299
    • 300
    • 301
    • 302
    • 303
    • 304
    • 305
    • 306
    • 307
    • 308
    • 309
    • 310
    • 311
    • 312
    • 313
    • 314
    • 315
    • 316
    • 317
    • 318
    • 319
    • 320
    • 321
    • 322
    • 323
    • 324
    • 325
    • 326
    • 327
    • 328
    • 329
    • 330
    • 331
    • 332
    • 333
    • 334
    • 335
    • 336
    • 337
    • 338
    • 339
    • 340
    • 341
    • 342
    • 343
    • 344
    • 345
    • 346
    • 347
    • 348
    • 349
    • 350
    • 351
    • 352
    • 353
    • 354
    • 355
    • 356
    • 357
    • 358
    • 359
    • 360
    • 361
    • 362
    • 363
    • 364
    • 365
    • 366
    • 367
    • 368
    • 369
    • 370
    • 371
    • 372
    • 373
    • 374
    • 375
    • 376
    • 377
    • 378
    • 379
    • 380
    • 381
    • 382
    • 383
    • 384
    • 385
    • 386
    • 387
    • 388
    • 389
    • 390
    • 391
    • 392
    • 393
    • 394
    • 395
    • 396
    • 397
    • 398
    • 399
    • 400
    • 401
    • 402
    • 403
    • 404
    • 405
    • 406
    • 407
    • 408
    • 409
    • 410
    • 411
    • 412
    3.3.3 解帧模块Unframe

    主要功能:从某个容器读到帧头标志位8’HEB开始,连续读P_SEND_BYTE_LENGTH个字节,然后从中读出10个字节的数据

    参数控制

    • P_CRC_POLY:CRC校验多项式,默认CRC-16模型
    • P_FIRST_MARK:帧头第一个标志位
    • P_SECOND_MARK:帧头第二标志位
    • P_SEND_BYTE_LENGTH:发送字节

    参考代码

    `timescale 1ns / 1ns
    
    module Unframe
    #(
        parameter       P_CRC_POLY          = 16'H8005  ,   //CRC校验中的多项式
        parameter       P_FIRST_MARK        = 8'HEB     ,
        parameter       P_SECOND_MARK       = 8'H90     ,
        parameter       P_SEND_BYTE_LENGTH  = 16
    )
    (
        input           i_clk   ,
        input           i_rst_n ,
        input           i_full  ,   //判断解帧后的FIFO是否满
        input           i_empty ,   //在读入的时候,判断FIFO是否为空
        input   [7:0]   i_data  ,   //输入数据跟数据有效信号持续时间是不同的,默认是数据之间是有间隔的
        input           i_valid ,
        
        output  [7:0]   o_data  ,   
        output          o_valid ,
        output          o_rd_en
    );
    
    /***************function**************/
    /***************parameter*************/
    /***************port******************/
    /***************mechine***************/
    /***************reg*******************/
    reg [7:0]           ro_data             ;
    reg                 ro_valid            ;
    reg                 ri_valid_1d         ;   //将有效信号延后一时钟周期,以便后面采集数据的时候只采集一次
    reg                 ro_rd_en            ;
    reg [7:0]           ri_data_1d          ;   //将数据打一拍,便于采集数据
    reg [1:0]           r_is_mark_cnt       ;   //用累加计数的方式判断是否是帧头,帧头的第一二字节都符合才开始解帧,不是帧头则继续读,直到读出的是帧头为止
    reg [1:0]           r_is_mark_cnt_1d    ;   //延迟一排用来判断上升沿
    reg [7:0]           r_byte_cnt          ;   //字节计数器
    reg                 r_unframe_en        ;   //解帧的使能,只有拉高的时候才开始解帧,即帧头匹配成功的时候开始解帧
    reg [15:0]          r_frame_counter     ;   //帧计数计数器
    reg [15:0]          r_frame_container   ;   //从数据流上取出帧计数
    reg                 r_frame_is_True     ;   //检验帧计数是否正确的标志信号
    reg [79:0]          r_frame_data        ;   //用来装十字节数据的寄存器
    reg [15:0]          r_checksum_container;   //装接收帧的两字节的寄存器
    reg [15:0]          r_generate_checksum ;   //根据读的数据,诸位累加
    reg                 r_check_checksum    ;   //检查校验和,正确的时候拉高电平
    reg                 r_recive_end_flag   ;   //接受完1帧数据的标志信号
    reg [7:0]           r_send_byte_bit     ;   //当解帧正确的时候,将收到的10自己饿饿发出去
    reg [3:0]           r_valid_cnt         ;   //用来延长数据的持续时间,缩短有效信号的时间
    /***************wire******************/
    /***************component*************/
    /***************assign****************/
    assign              o_data  = ro_data   ;
    assign              o_valid = ro_valid  ;
    assign              o_rd_en = ro_rd_en  ;   //控制解帧模块的读使能信号,每拉高电平,读出数据
    
    /***************always****************/
    
    always@(posedge i_clk,negedge i_rst_n)
        if(~i_rst_n)
            ri_data_1d <= 'd0;
        else
            ri_data_1d <= i_data;
    
    always@(posedge i_clk,negedge i_rst_n)
        if(~i_rst_n)
            ro_rd_en <= 'd1;
        else if(r_byte_cnt == P_SEND_BYTE_LENGTH || i_empty)
            ro_rd_en <= 'd0;
        else if(r_send_byte_bit == P_SEND_BYTE_LENGTH - 6)                        //使能信号会在下一周期拉低,因此多持续了一个时钟周期,因此每次读出的是2字节的帧头
            ro_rd_en <= 'd1;
        else if(r_byte_cnt < P_SEND_BYTE_LENGTH && ~i_empty && r_send_byte_bit == 0)                        //FIFO中有数据的时候,拉高使能
            ro_rd_en <= 'd1;
        else
            ro_rd_en <= ro_rd_en;
    
    always@(posedge i_clk,negedge i_rst_n)
        if(~i_rst_n)
            r_byte_cnt <= 'd0;
        else if(r_byte_cnt == P_SEND_BYTE_LENGTH)
            r_byte_cnt <= 'd0;
        else if(r_byte_cnt == 1 && ri_data_1d != P_FIRST_MARK)
            r_byte_cnt <= 'd0;
        else if(r_byte_cnt == 2 && ri_data_1d != P_SECOND_MARK)
            r_byte_cnt <= 'd0;
        else if(i_valid)
            r_byte_cnt <= r_byte_cnt + 1;
        else
            r_byte_cnt <= r_byte_cnt;
    
    always@(posedge i_clk,negedge i_rst_n)      //检测是否符合帧头,符合枕头第一字节就加一,符合帧头第二字节就加二,只有连续符合两个帧头才开始解帧,即当计数到二时候才开始解帧
        if(~i_rst_n)
            r_is_mark_cnt <= 'd0;
        else if(r_send_byte_bit == P_SEND_BYTE_LENGTH - 6)
            r_is_mark_cnt <= 'd0;
        else if(r_byte_cnt == 0 &&i_valid)begin
            if(i_data == P_FIRST_MARK)
                r_is_mark_cnt <= r_is_mark_cnt + 1;
            else
                r_is_mark_cnt <= r_is_mark_cnt;
        end
        else if(r_byte_cnt == 1 && i_valid)begin
            if(i_data == P_SECOND_MARK)
                r_is_mark_cnt <= r_is_mark_cnt + 1;
            else
                r_is_mark_cnt <= r_is_mark_cnt;
        end
        else
            r_is_mark_cnt <= r_is_mark_cnt;
    
    
    always @(posedge i_clk,negedge i_rst_n) begin
        if(~i_rst_n)
            r_is_mark_cnt_1d <= 'd0;
        else
            r_is_mark_cnt_1d <= r_is_mark_cnt;
    end
    
    always @(posedge i_clk,negedge i_rst_n) begin
        if(~i_rst_n)
            r_unframe_en <= 'd0;
        else if(r_is_mark_cnt == 2)
            r_unframe_en <= 'd1;
        else if(r_byte_cnt == P_SEND_BYTE_LENGTH)       //解帧结束的时候拉低
            r_unframe_en <= 'd0;
        else
            r_unframe_en <= r_unframe_en;
    end
    
    
    always @(posedge i_clk,negedge i_rst_n) begin       //本地计数接收到的帧,然后与每帧的帧计数进行匹配
        if(~i_rst_n)
            r_frame_counter <= 'd0;
        else if(r_is_mark_cnt == 2 && r_is_mark_cnt_1d == 1)
            r_frame_counter <= r_frame_counter+1;
        else
            r_frame_counter <= r_frame_counter;
    end
    
    always @(posedge i_clk,negedge i_rst_n) begin       //获取收到的帧的帧计数
        if(~i_rst_n)
            r_frame_container <= 'd0;
        else if(r_byte_cnt == P_SEND_BYTE_LENGTH)
            r_frame_container <= 'd0;
        else if(r_byte_cnt == 3 && r_unframe_en)
            r_frame_container[7:0] <= ri_data_1d;
        else if(r_byte_cnt == 4 && r_unframe_en)
            r_frame_container[15:8] <= ri_data_1d;
        else
            r_frame_container <= r_frame_container;
    end
    
    
    always @(posedge i_clk,negedge i_rst_n) begin   //与本地应该受到的帧计数进行比较,在收到第一字节数据的的同时,进行帧匹配
        if(~i_rst_n)
            r_frame_is_True <= 'd0;
        else if(r_send_byte_bit == P_SEND_BYTE_LENGTH -6)
            r_frame_is_True <= 'd0;
        else if(r_byte_cnt == 4 && i_valid)
            r_frame_is_True <= r_frame_counter == r_frame_container?1:0;
        else
            r_frame_is_True <= r_frame_is_True;
    end
    
    always @(posedge i_clk,negedge i_rst_n) begin
        if(~i_rst_n)
            ri_valid_1d <= 'd0;
        else
            ri_valid_1d <= i_valid;
    end
    
    
    always @(posedge i_clk,negedge i_rst_n) begin   //把10个字节存着,等验证成功,就把10字节写入FIFO
        if(~i_rst_n)
            r_frame_data <= 'd0;
        else if(r_send_byte_bit == P_SEND_BYTE_LENGTH -6)
            r_frame_data <= 'd0;
        else if(r_byte_cnt > 4 && r_byte_cnt <= 14 && r_frame_is_True && r_unframe_en&&ri_valid_1d)
            r_frame_data <= {ri_data_1d,r_frame_data[79:8]};
        else if(r_valid_cnt == 3)       //在等于2的时候,已经把低八位取出去了,所以要右移8位
            r_frame_data <= r_frame_data>>8;
        else
            r_frame_data <= r_frame_data;
    end
    
    
    always @(posedge i_clk,negedge i_rst_n) begin   //读数据,进行累加,生成累加校验
        if(~i_rst_n)
            r_generate_checksum <= 'd0;
        else if(r_byte_cnt == 4)                    //接受下一帧的时候才归零
            r_generate_checksum <= 'd0;
        else if(r_byte_cnt > 4 && r_byte_cnt <= 14 && r_frame_is_True && r_unframe_en&&ri_valid_1d)
            r_generate_checksum <= r_generate_checksum + ri_data_1d;
        else if(r_byte_cnt == P_SEND_BYTE_LENGTH)
            r_generate_checksum <= ~r_generate_checksum;
        else
            r_generate_checksum <= r_generate_checksum;
    end
    
    always @(posedge i_clk,negedge i_rst_n) begin       //获取收到的帧的checksum校验和
        if(~i_rst_n)
            r_checksum_container <= 'd0;
        else if(r_send_byte_bit == P_SEND_BYTE_LENGTH -6)
            r_checksum_container <= 'd0;
        else if(r_byte_cnt == P_SEND_BYTE_LENGTH - 1 && r_frame_is_True &&ri_valid_1d)
            r_checksum_container[7:0] <= ri_data_1d;
        else if(r_byte_cnt == P_SEND_BYTE_LENGTH && r_frame_is_True && ri_valid_1d)
            r_checksum_container[15:8] <= ri_data_1d;
        else
            r_checksum_container <= r_checksum_container;
    end
    
    always @(posedge i_clk,negedge i_rst_n) begin   //接收完就拉高一个周期,其他时候都是低电平
        if(~i_rst_n)
            r_recive_end_flag <= 'd0;
        else if(r_byte_cnt == P_SEND_BYTE_LENGTH) 
            r_recive_end_flag <= 'd1;
        else
            r_recive_end_flag <= 'd0;
    end
    
    always @(posedge i_clk,negedge i_rst_n) begin   //进行和校验,校验成功拉高电平,进行发送
        if(~i_rst_n)
            r_check_checksum <= 'd0;
        else if(r_send_byte_bit == P_SEND_BYTE_LENGTH - 6) 
            r_check_checksum <= 'd0;
        else if(r_recive_end_flag) 
            r_check_checksum <= r_checksum_container == r_generate_checksum?1:0;
        else
            r_check_checksum <= r_check_checksum;
    end
    
    always @(posedge i_clk,negedge i_rst_n) begin   //发送时候的字节计数器
        if(~i_rst_n)
            r_send_byte_bit <= 'd0;
        else if(r_send_byte_bit == P_SEND_BYTE_LENGTH - 6)
            r_send_byte_bit <= 'd0;
        else if(ro_valid)
            r_send_byte_bit <= r_send_byte_bit + 1;
        else
            r_send_byte_bit <= r_send_byte_bit;
    end
    
    always @(posedge i_clk,negedge i_rst_n) begin
        if(~i_rst_n)
            ro_data <= 'd0;
        else if(r_send_byte_bit == P_SEND_BYTE_LENGTH - 6)
            ro_data <= 'd0;
        else if(r_valid_cnt == 2)
            ro_data <= r_frame_data[7:0];
        else
            ro_data <= ro_data;
        
    end
       
    always @(posedge i_clk,negedge i_rst_n) begin   //数据有效值的间隔周期计数器
        if(~i_rst_n)
            r_valid_cnt <= 'd0;
        else if(r_valid_cnt == 3)
            r_valid_cnt <= 'd0;
        else if(r_check_checksum)
            r_valid_cnt <= r_valid_cnt+1;
        else
            r_valid_cnt <= 'd0;
    end
    
    always @(posedge i_clk,negedge i_rst_n) begin
        if(~i_rst_n)
            ro_valid <= 'd0;
        else if(r_valid_cnt == 3 && ~i_full && r_check_checksum) //当full拉高的时候,就说明已满,写使能不再有效
            ro_valid <= 'd1;
        else
            ro_valid <= 'd0;
    end
    endmodul
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    • 154
    • 155
    • 156
    • 157
    • 158
    • 159
    • 160
    • 161
    • 162
    • 163
    • 164
    • 165
    • 166
    • 167
    • 168
    • 169
    • 170
    • 171
    • 172
    • 173
    • 174
    • 175
    • 176
    • 177
    • 178
    • 179
    • 180
    • 181
    • 182
    • 183
    • 184
    • 185
    • 186
    • 187
    • 188
    • 189
    • 190
    • 191
    • 192
    • 193
    • 194
    • 195
    • 196
    • 197
    • 198
    • 199
    • 200
    • 201
    • 202
    • 203
    • 204
    • 205
    • 206
    • 207
    • 208
    • 209
    • 210
    • 211
    • 212
    • 213
    • 214
    • 215
    • 216
    • 217
    • 218
    • 219
    • 220
    • 221
    • 222
    • 223
    • 224
    • 225
    • 226
    • 227
    • 228
    • 229
    • 230
    • 231
    • 232
    • 233
    • 234
    • 235
    • 236
    • 237
    • 238
    • 239
    • 240
    • 241
    • 242
    • 243
    • 244
    • 245
    • 246
    • 247
    • 248
    • 249
    • 250
    • 251
    • 252
    • 253
    • 254
    • 255
    • 256
    • 257
    • 258
    • 259
    • 260
    • 261
    • 262
    • 263
    • 264
    • 265
    • 266
    • 267
    • 268
    • 269
    • 270
    • 271
    • 272
    3.4 顶层模块Multi_Uart

    主要功能:顶层主要是用来隔离外界,控制各个底层模块,只暴露rx和tx管脚

    框图:

    在这里插入图片描述

    参数控制

    • P_SEND_BYTE_LENGTH:发送的字节数,默认16字节
    • P_FIRST_MARK:帧头第一个标志位,默认8’HEB
    • P_SECOND_MARK:帧头第二个标志位,默认8’H90
    • P_OVER_SAMPLE:过采样次数,默认16
    • P_SYS_CLOCK:输入时钟,即PLL后的输入时钟,默认50Mhz
    • P_UART_BAUDRATE:波特率,默认9600.
    • P_UART_STOP_WIDTH:停止位宽度,默认1位
    • P_UART_PARITY_WIDTH:校验位,1代表奇校验,2代表偶检验,0代表无校验,默认奇校验
    • P_CRC_POLY:CRC校验多项式,默认CRC-16模型

    参考代码

    `timescale 1ns / 1ns
    
    module Multi_Uart
    #(
        parameter               P_SEND_BYTE_LENGTH    =     16          ,   //发送的字节数
        parameter               P_FIRST_MARK          =     8'HEB       ,   //帧头标志位
        parameter               P_SECOND_MARK         =     8'H90       ,   //帧头标志位
        parameter               P_OVER_SAMPLE         =     16          ,   //接收模块的过采样次数
        parameter               P_SYS_CLOCK           =     50_000_000  ,   //系统时钟可配置,默认50MHZ
        parameter               P_UART_BAUDRATE       =     9600        ,   //波特率可配置,默认9600
        parameter               P_UART_STOP_WIDTH     =     1           ,   //停止位宽度,默认1位
        parameter               P_UART_PARITY_WIDTH   =     1           ,   //校验位宽度,0为无校验,1为奇校验,2位偶校验
        parameter               P_CRC_POLY            =     16'H8005        //CRC校验中的多项式,执行的CRC-8/MAXIM标准
    )
    (
        input                   i_sys_clk_p                             ,
        input                   i_sys_clk_n                             ,
        input                   i_sys_rst_n                             ,
        input                   i_rx                                    ,
        output                  o_tx        
    );
    
    localparam                  P_BAUD_CNT = (P_SYS_CLOCK/P_UART_BAUDRATE)/P_OVER_SAMPLE;
    
    wire                        w_clk_50MHZ         ;   //pll后输出的50Mhz时钟
    wire                        w_pll_locked        ;   //锁相环的上锁信号
    wire                        w_sys_rst_n         ;   //PLL lock信号产生的复位信号
    wire                        wo_tx               ;
    wire                        w_over_clk          ;   //波特率的P_OVER_SAMPLE倍的时钟
    
    assign                      w_sys_rst_n = w_pll_locked;
    assign                      o_tx = wo_tx;
    
    wire                        w_pll_rst_p         ;
    assign                      w_pll_rst_p = ~i_sys_rst_n;
    
    System_Pll System_Pll_U1
       (
        .clk_in1_p              (i_sys_clk_p        )   ,   // input clk_in1_p
        .clk_in1_n              (i_sys_clk_n        )   ,   // input clk_in1_n
        .reset                  (w_pll_rst_p        )   ,   // input reset
        .sys_clk                (w_clk_50MHZ        )   ,   // output sys_clk
        .locked                 (w_pll_locked       )      // output locked
    );
    
    
    //分频模块
    clk_div#(
        .P_CLK_DIV_CNT          (P_BAUD_CNT        )       //默认4分频,最大为65535
    )
    clk_div_u0
    (
        .i_clk                  (w_clk_50MHZ        )   ,
        .i_rst_n                (w_sys_rst_n        )   ,
    
        .o_clk_div              (w_over_clk         )       //9.6K级别的频率
    );
    
    wire                        w_fifo_Unframe_full     ;   //判断即将写入的FIFO是否已满
    wire    [7:0]               w_rx_o_data             ;
    wire                        w_rx_o_valid            ;
    
    
    //接收模块
    Rx_Drive#(
        .P_SYS_CLOCK            (P_SYS_CLOCK        )    ,   //系统时钟可配置,默认50MHZ
        .P_UART_BAUDRATE        (P_UART_BAUDRATE    )    ,   //波特率可配置,默认9600
        .P_UART_STOP_WIDTH      (P_UART_STOP_WIDTH  )    ,   //停止位宽度,默认1位
        .P_UART_PARITY_WIDTH    (P_UART_PARITY_WIDTH)    ,   //校验位,默认0,0不校验,1奇校验,2偶校验
        .P_OVER_SAMPLE          (P_OVER_SAMPLE      )        //过采样的时钟的倍率
    )
    Rx_Drive_U0
    (
        .i_clk                  (w_over_clk         )   ,
        .i_rst_n                (w_sys_rst_n        )   ,
        .i_full                 (w_fifo_Unframe_full)   ,   //判断待写入的FIFO是否已满
        .i_rx                   (i_rx               )   ,   //目前在拼回环用的是输出tx接入输入rx,后面应该就接rx输入
    
        .o_rx_data              (w_rx_o_data        )   ,
        .o_rx_valid             (w_rx_o_valid       )   
    );
    
    wire    [7:0]               w_fifo_Unframe_data     ;   //FIFO输出的数据
    wire                        w_fifo_Unframe_valid    ;   //FIFO输出的数据有效信号
    wire                        w_fifo_Unframe_empty    ;   //读之前判断FIFO是否为空,当为空的时候,高电平,读使能不再有效
    wire                        w_fifo_rst_p            ;   //fifo复位
    
    wire                        w_Unframe_rd_en         ;   //组帧的读使能信号
    assign                      w_fifo_rst_p = ~w_sys_rst_n;
    
    
    //异步FIFO,主要用在解帧前的数据缓存
    FIFO_UnFrame_32_8 FIFO_UnFrame_32_8_U0 (
        .clk                    (w_over_clk         )   ,   // input wire clk
        .srst                   (w_fifo_rst_p       )   ,   // input wire srst
        .din                    (w_rx_o_data        )   ,   // input wire [7 : 0] din
        .wr_en                  (w_rx_o_valid       )   ,   // input wire wr_en
        .full                   (w_fifo_Unframe_full)   ,   // output wire full
    
        .rd_en                  (w_Unframe_rd_en    )   ,   // input wire rd_en
        .empty                  (w_fifo_Unframe_empty)  ,   // output wire empty
        .dout                   (w_fifo_Unframe_data)   ,   // output wire [7 : 0] dout
        .valid                  (w_fifo_Unframe_valid)      // output wire valid
    );
    
    wire        [7:0]           w_fifo_data_i_data      ;   //输入fifo-data的数据
    wire                        w_fifo_data_i_valid     ;   //输入输入fifo-data的有效信号
    wire                        w_fifo_data_o_full      ;   //判断fifo-data是否写满,写满了就拉高
    
    //解帧模块
    Unframe
    #(
        .P_CRC_POLY             (P_CRC_POLY         )   ,   //CRC校验中的多项式
        .P_FIRST_MARK           (P_FIRST_MARK       )   ,
        .P_SECOND_MARK          (P_SECOND_MARK      )   ,
        .P_SEND_BYTE_LENGTH     (P_SEND_BYTE_LENGTH )    
    )
    Unframe_U0
    (
        .i_clk                  (w_over_clk         )   ,
        .i_rst_n                (w_sys_rst_n        )   ,
        .i_full                 (w_fifo_data_o_full )   ,   //判断解帧后的FIFO是否满
        .i_empty                (w_fifo_Unframe_empty)   ,   //在读入的时候,判断FIFO是否为空
        .i_data                 (w_fifo_Unframe_data)   ,
        .i_valid                (w_fifo_Unframe_valid)   ,
    
        .o_data                 (w_fifo_data_i_data )   ,
        .o_valid                (w_fifo_data_i_valid)   ,
        .o_rd_en                (w_Unframe_rd_en    )   
    );
    
    
    wire        [7:0]           w_frame_o_data          ;   //组帧模块输出的数据
    wire                        w_frame_o_valid         ;   //组帧模块输出的数据有效信号
    wire                        w_frame_fifo_o_full     ;   //组帧后,判断即将写入的FIFO是否已满
    wire                        w_fifo_data_rst_p       ;   //FIFo的复位位高电平复位
    wire                        w_fifo_data_o_rd_en     ;   //组帧模块输出的读使能信号
    wire                        w_fifo_frame_empty      ;   //组帧模块在读之前,判断fifo-data是否为空
    wire        [7:0]           w_frame_i_data          ;   //输入组帧模块的数据
    wire                        w_frame_i_valid         ;   //输入组帧模块的数据有效信号
    
    assign                      w_fifo_data_rst_p = ~w_sys_rst_n;
    
    //同步fifo:接收数据的fifo-data
    FIFO_Data_32_8 FIFO_Data_32_8_U0 (
        .clk                    (w_over_clk         )   ,   // input wire clk
        .srst                   (w_fifo_data_rst_p  )   ,   // input wire srst
        .din                    (w_fifo_data_i_data )   ,   // input wire [7 : 0] din
        .wr_en                  (w_fifo_data_i_valid)   ,   // input wire wr_en
    
        .rd_en                  (w_fifo_data_o_rd_en)   ,   // input wire rd_en
        .dout                   (w_frame_i_data     )   ,   // output wire [7 : 0] dout
        .full                   (w_fifo_data_o_full )   ,   // output wire full
        .empty                  (w_fifo_frame_empty )   ,   // output wire empty
        .valid                  (w_frame_i_valid    )       // output wire valid
    );
    
    
    //组帧模块:将2byte标志位+ 2byte帧计数+10byte数据+2byte和校验位
    Frame
    #(
        .P_CRC_POLY             (P_CRC_POLY         )   ,   
        .P_FIRST_MARK           (P_FIRST_MARK       )   ,
        .P_SECOND_MARK          (P_SECOND_MARK      )   ,
        .P_SEND_BYTE_LENGTH     (P_SEND_BYTE_LENGTH )   
    )Frame_U0
    (
        .i_clk                  (w_over_clk         )   ,
        .i_rst_n                (w_sys_rst_n        )   ,
        .i_data                 (w_frame_i_data     )   ,
        .i_valid                (w_frame_i_valid    )   ,
        .i_full                 (w_frame_fifo_o_full)   ,   //写满信号,当为高电平时,写使能不再有效
        .i_empty                (w_fifo_frame_empty )   ,   //读空信号,当为高电平时,读使能不再有效
    
        .o_rd_en                (w_fifo_data_o_rd_en)   ,  //读使能信号,当fifo非空且需要读数据的拉高
        .o_data                 (w_frame_o_data     )   ,
        .o_valid                (w_frame_o_valid    )
    );
    
    
    wire        [7:0]           w_fifo_tx_i_data        ;   //发送模块输出的待发送八位数据    
    wire                        w_fifo_tx_i_valid       ;
    wire                        w_fifo_tx_o_empty       ;
    wire                        w_fifo_tx_i_rd_en       ;
    
    wire                        w_fifo_frame_rst_p      ;
    assign                      w_fifo_frame_rst_p = !w_sys_rst_n;
    
    
    //同步FIFO,主要用于缓存 组帧模块和字节发送模块的数据流
    FIFO_Frame_32_8  FIFO_Frame_32_8_U0(
        .clk                    (w_over_clk         )   ,  // input wire clk
        .srst                   (w_fifo_frame_rst_p )   ,  // input wire srst
        .din                    (w_frame_o_data     )   ,  // input wire [7 : 0] din
        .wr_en                  (w_frame_o_valid    )   ,  // input wire wr_en
        .rd_en                  (w_fifo_tx_i_rd_en  )   ,  // input wire rd_en
        .dout                   (w_fifo_tx_i_data   )   ,  // output wire [7 : 0] dout
        .valid                  (w_fifo_tx_i_valid  )   ,  // output wire valid
        .full                   (w_frame_fifo_o_full)   ,  // output wire full
        .empty                  (w_fifo_tx_o_empty  )      // output wire empty
    );
    
    
    //单字节发送驱动模块
    Tx_Drive
    #(
        .P_SYS_CLOCK            (P_SYS_CLOCK        )   ,   //系统时钟可配置,默认50MHZ
        .P_UART_BAUDRATE        (P_UART_BAUDRATE    )   ,   //波特率可配置,默认9600
        .P_UART_STOP_WIDTH      (P_UART_STOP_WIDTH  )   ,   //停止位宽度,默认1位
        .P_UART_PARITY_WIDTH    (P_UART_PARITY_WIDTH)   ,   //校验位,默认0,0不校验,1奇校验,2偶校验
        .P_OVER_SAMPLE          (P_OVER_SAMPLE      )
    )
    Tx_Drive_U0
    (
        .i_sys_clk              (w_over_clk         )   ,
        .i_rst_n                (w_sys_rst_n        )   ,
        .i_valid                (w_fifo_tx_i_valid  )   ,
        .i_data                 (w_fifo_tx_i_data   )   ,
        .i_empty                (w_fifo_tx_o_empty  )   ,
        .o_tx                   (wo_tx              )   ,                               
        .o_tx_ready             (w_fifo_tx_i_rd_en  )       //发送模块空闲信号,当为高电平时,代表空闲
    
    );
    endmodule
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    • 154
    • 155
    • 156
    • 157
    • 158
    • 159
    • 160
    • 161
    • 162
    • 163
    • 164
    • 165
    • 166
    • 167
    • 168
    • 169
    • 170
    • 171
    • 172
    • 173
    • 174
    • 175
    • 176
    • 177
    • 178
    • 179
    • 180
    • 181
    • 182
    • 183
    • 184
    • 185
    • 186
    • 187
    • 188
    • 189
    • 190
    • 191
    • 192
    • 193
    • 194
    • 195
    • 196
    • 197
    • 198
    • 199
    • 200
    • 201
    • 202
    • 203
    • 204
    • 205
    • 206
    • 207
    • 208
    • 209
    • 210
    • 211
    • 212
    • 213
    • 214
    • 215
    • 216
    • 217
    • 218
    • 219
    • 220
    • 221
    • 222
    • 223
    • 224
    • 225
    3.5 其他模块
    3.5.1 分频模块CLK_DIV

    主要功能:输出分频后的时钟

    程序代码:

    `timescale 1ns / 1ps
    
    module clk_div#(
        parameter   P_CLK_DIV_CNT = 4  //默认4分频,最大为65535
    )(
        input   wire    i_clk       ,
        input   wire    i_rst_n     ,
        output  wire    o_clk_div
    );
    
    /***************reg*******************/
    reg                 ro_clk_div          ;   //输出时钟
    reg         [15:0]  r_cnt               ;   //分频计数器
    /***************wire******************/
    /***************component*************/
    /***************assign****************/
    assign  o_clk_div = ro_clk_div  ;
    /***************always****************/
    
    //计数器
    always @(posedge i_clk, negedge i_rst_n) begin
        if(~i_rst_n)
            r_cnt   <=  'd0;
        else if(r_cnt == (P_CLK_DIV_CNT >> 1)-1)
            r_cnt   <=  'd0;
        else
            r_cnt   <=  r_cnt + 1;
    end
    
    //输出分频
    always @(posedge i_clk, negedge i_rst_n) begin
        if(~i_rst_n)
            ro_clk_div <= 1'd0;
        else if(r_cnt == (P_CLK_DIV_CNT >> 1)-1)
            ro_clk_div <= ~ro_clk_div;
        else
             ro_clk_div <=  ro_clk_div;
    end
    endmodule
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
  • 相关阅读:
    26. 删除有序数组中的重复项
    “暗蚊”黑产团伙通过国内下载站传播Mac远控木马攻击活动分析
    MySQL 教程 2.4
    React中的key有什么作用?
    TypeScript 实用工具类型
    css_css3新特性
    方便面行业调研:预计2028年将达到436亿美元
    企业怎样做好工厂生产人员管理?
    springboot的@ConditionalOnBean注解
    python LeetCode 刷题记录 83
  • 原文地址:https://blog.csdn.net/qq_45801179/article/details/132701676