基于verilog的UART串行总线协议模块设计(含原理、源码、AXI封装、C驱动文件)

文章目录

  • 一、UART简介
  • 二、UART通信的特点
  • 三、UART传输速率
  • 四、UART数据帧格式
  • 五、UART模块设计
    • 5.1 串行发送模块
    • 5.2 串行接收模块
    • 5.3 顶层设计
  • 六、AXI接口封装
  • 七、C函数设计


本文涉及的所有代码仅用于学习交流,不得用于其他用途

一、UART简介

  UART即通用异步收发传输器(Universal Asynchronous Receiver/Transmitter),两个UART设备在进行通信时,发送数据的设备将并行的数据转化为一位一位的串行数据,并按照给定的时钟频率将串行数据依次打到TX通信线上,接收方检测RX上的电平,按照给定的时钟频率读取RX线上电平,并将采样到的串行数据还原成并行数据;
基于verilog的UART串行总线协议模块设计(含原理、源码、AXI封装、C驱动文件)_第1张图片

二、UART通信的特点

  ·异步:设备之间没有统一的时钟线连接
  ·串行:数据以固定的速率一位一位进行发送
  ·全双工:UART设备能够同时进行数据接收和发送
  ·逻辑电平:RS232(3V~25V为高电平 -3V或-25V为低电平)
   TTL(5V或3.3V为高电平 0V为低电平)
  首先UART是一个异步通信,即通信设备之间是不需要时钟线的,这不同于IIC与SPI等串行通信,UART设备通信双方均在自己的时钟系统下工作;
  全双工也很好理解,这里发送线与接收线是独立的分开的,且在每个UART设备的UART模块中,都会设计串口接收模块与发送模块,两者可以同时工作;
  UART通信使用的逻辑电平有很多,在单片机系统中长用TTL电平进行UART传输,除此之外还有RS232,RS485,但这些都不是一种协议,而是一种电平逻辑规范,我们从高电平的识别上来看,TTL允许电压波动的范围较小,因此它也是非常易受到干扰的;相比之下RS232的电压范围相对宽些;RS485使用的是差分传输,因此抗干扰能力更强;
综上,三种逻辑电平规范在传输距离上:TTL   (本次设计将基于TTL电平)


三、UART传输速率

  波特率:描述UART设备传输数据快慢的单位,以每秒位数(bps)表示。
  *UART对波特率的要求比较严格,因为其是异步通信,要求通信双方的波特率相差不能超过10%。
  UART常用波特率:9600、115200、192000、230400等
  假如选择的波特率为9600b/s,则其1s内能够传输9600bit,即9600/8 = 1200字节每秒;
波特率是否越大越好?
  在平时的设备通信中,往往会觉得串口传输速率较慢,因此会把波特率提高来加快传输速率,但波特率也不是越大越好,而是越适合越好;
  举个简单的例子,在单片机中UART模块后面往往会跟一个FIFO来做数据缓冲,因为单片机读取FIFO的速度有限,如果读FIFO速率小于UART向FIFO的写速率,且FIFO深度有限的情况下,则很容易造成FIFO满,满状态的FIFO不允许数据写入,此时就会造成丢包;
  此时有以下几种方法解决,1.增加FIFO深度(参考FIFO文章)2.降低波特率使得读速率大于写速率。3.如果是突发传输,则要根据UART的接收频率、单片机的读出速率来合理计算FIFO深度,使得数据不会溢出(参考FIFO文章);


四、UART数据帧格式

空闲:UART数据线保持高电平。
起始位:传输线TXD由高电平转换为低电平。(必须有)
数据位:5-8位长度数据位,通常从最低位开始发送。(通常为8位)(必须有)
奇偶校验位:判断数据传输过程中是否有数据位发生错误变化。(可以无)
停止位:在数据包发送结束后,发送数据线保持1~2位高电平。(必须有)

基于verilog的UART串行总线协议模块设计(含原理、源码、AXI封装、C驱动文件)_第2张图片
  UART在空闲时,会保持TX与RX线为高电平;
  当一个数据帧进行发送时,首先发送端(TX)会将高电平拉低(发送一个0bit)示意通信的开始;另一接收端在检测到TX线被拉低后,则开始接收数据;
  接下来发送端会将8位数据按照从低位到高位的顺序依次将其打到TX线上,接收方则按照对应波特率读取RX线电平;
  如果通信双方设置了需要校验,则在8位数据发送结束后,会附加1bit的奇偶校验位;(数据量少的通信中不需要)
  在通信需要结束时,发送端会将TX线拉高,接收方检测到这个高电平则结束这次通信;(停止位可以是1,1.5,2个,一般来说是1个)
例:通过UART发送数据0x33(不带校验位,且1个停止位)
在这里插入图片描述对第三节中的字节传输速率做出修正:
  如果是不带校验位,且只有一个停止位,则传输一个字节数据需要10bit,据此得到传输字节的真正速率:
  9600/10 = 960字节每秒


五、UART模块设计

  设计UART模块实际上是设计UART 发送模块与UART 接收模块;
  在设计UART模块过程中需重点关注:
    波特率如何产生?
    串并转换、并串转换如何进行?
    起始位、停止位如何检测?
    rx信号如何采样?
  波特率的产生:
  波特率实际上就是一个分频器,其分频系数为fsys_clk/Baunds;
  fsys_clk为UART模块所接的时钟源,Baunds则是所要得到的波特率;
例: fsys_clk = 50_000_000 Hz, Baunds = 115200,则分频系数Div0 = fsys_clk/Baunds = 50_000_000 / 115200 = 434
串并转换、并串转换:
  并串转换应用于发送模块,其本质是一个移位寄存器,驱动时钟为波特率分频器输出的时钟,输入信号为并行8位数据,输出为串行数据;首先对并行的8位数据进行重建,得到数据帧;
  如0x33则重建后的十位数据为:{1‘b1,0x33,1’b0},最低位的1’b0为起始位,最高位的1’b1为停止位;
  串并转换应用于接收模块,其本质上也是一个移位寄存器,只不过其输出为8位并行数据,输入为串行数据;
起始位、停止位检测:
  起始位的检测可以通过检测高电平到低电平的下降沿来实现;
  停止位可以在接收满8位数据后,在第9位数据上进行检测;
在这里插入图片描述rx信号采样:
  接收信号首先需要进行同步处理,因为rx信号来自另一个时钟域;
  其次同步后的信号需要采样其中间位置,因为此时的电平是最稳定的;
在这里插入图片描述


5.1 串行发送模块

综上,发送模块功能可划分为:
基于verilog的UART串行总线协议模块设计(含原理、源码、AXI封装、C驱动文件)_第3张图片

并串转换器在检测到有效的Valid信号后将tx_data包装成一帧数据{1‘b1,tx_data,1’b0}
同时发送状态机检测到valid信号后,进入到发送状态,此时波特率分频器开始计数,控制比特计数器增,并串转换模块则将对应比特位的数据串行发送出去。

源码:

`timescale 1ns / 1ps
//
// Company: Goodwayhouse
// Engineer: Ge Wen Jie
// 
// Create Date: 2022/09/11 11:23:52
// Design Name: 
// Module Name: uart_dtx
// Project Name: 
// Target Devices: 
// Tool Versions: 
// Description: 
// 
// Dependencies: 
// 
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
// 
//
module uart_dtx#(
        parameter integer   UART_TX_REF_CLK_FRE = 50_000_000,
        parameter integer   UART_TX_BAUNDS_RATE = 115200

    )(
        input  wire               uart_ref_clk,//uart tx module reference clock
        input  wire               uart_tx_nrst,//uart tx module reset signal
        input  wire  [31:0]    uart_baunds_div,//baunds rate division
        input  wire  [7:0]        uart_tx_data,//uart tx 8bits data input    
        input  wire          uart_tx_data_qvld,//send data valid signal
        output wire             uart_tx_finish,//uart send data finish flag
        output wire               uart_tx_busy,//uart tx module busy flag
        output wire               uart_tx_dout //serial data output
    );

    //uart tx module baunds rater counter max, start data out, end data out definitions
    localparam    integer        UART_BAUNDS_COUNTER_MAX = UART_TX_REF_CLK_FRE/UART_TX_BAUNDS_RATE;
    localparam                   UART_TX_START_DOUT =  1'b0;
    localparam                   UART_TX_END_DOUT = 1'b1;

    //uart tx send state definition
    localparam    UART_IDLE0  = 3'b000;
    // localparam    UART_IDLE1  = 3'b001;
    localparam    UART_TXDATE = 3'b010;
    // localparam    UART_TXFINISH = 3'b100;

    //inner signals definitions
    reg                                          tx_out;
    reg                                          tx_finish;
    reg                                          tx_busy;
    reg [3:0]                                    tx_bit_cnt;
    reg [31:0]                                   tx_baunds_cnt;
    reg [2:0]                                    uart_tx_state;
    reg [2:0]                                    uart_tx_next_state;
    reg [9:0]                                    uart_tx_buffer;
    reg                                          uart_wr_en;
    //inner signal connect
    assign uart_tx_finish = tx_finish;
    assign uart_tx_busy = tx_busy;
    assign uart_tx_dout = tx_out;

    always@(posedge uart_ref_clk)
    begin
        if(~uart_tx_nrst)
        begin
            uart_tx_state <= UART_IDLE0;
        end
        else begin
            uart_tx_state <= uart_tx_next_state;
        end
    end

    always@(*)
    begin
        case(uart_tx_state)
            UART_IDLE0://waiting a valid data input
            begin 
                if(uart_tx_data_qvld) 
                begin
                    uart_tx_next_state <= UART_TXDATE;
                end
                else
                begin
                    uart_tx_next_state <= UART_IDLE0;
                end
            end
            UART_TXDATE://waiting tx module send data finish
            begin
                if((tx_finish == 1'b1) && (tx_busy == 1'b0))
                begin
                    uart_tx_next_state <= UART_IDLE0;
                end
                else begin
                    uart_tx_next_state <= UART_TXDATE;
                end
            end
            default:uart_tx_next_state <= UART_IDLE0;
        endcase
    end

    always@(posedge uart_ref_clk)
    begin
        if(~uart_tx_nrst)
        begin
            tx_busy <= 'b0;
            tx_finish <= 'b1;
            tx_out <= 1'b1;
        end
        else begin
            if((tx_finish == 1'b1)&&(tx_busy == 1'b0)&&(uart_tx_data_qvld))//sample date when tx finish and tx is not busy
            begin
                uart_tx_buffer <= {UART_TX_END_DOUT,uart_tx_data,UART_TX_START_DOUT};
                tx_finish <= 1'b0;
                tx_busy <= 1'b1;
                tx_out <= 1'b1;
            end
            else if((tx_finish == 1'b0)&&(tx_busy == 1'b1)&&(uart_tx_state == UART_TXDATE))
            begin
                if(uart_wr_en==1'b1)
                begin
                    if(tx_bit_cnt != 4'd10)begin tx_out <= uart_tx_buffer[0];uart_tx_buffer <= uart_tx_buffer >> 1;  end
                    else begin tx_out <= UART_TX_END_DOUT;tx_finish <= 1'b1;tx_busy <= 1'b0; end
                end
                else begin tx_busy <= tx_busy;tx_finish <= tx_finish;tx_out <= tx_out; end
            end
        end
    end

    always@(posedge uart_ref_clk)
    begin
        if(~uart_tx_nrst)
        begin
           tx_baunds_cnt <= 'b0;
           tx_bit_cnt <= 'b0;
           uart_wr_en <= 1'b0;
        end
        else begin
            if(uart_tx_state == UART_IDLE0)
            begin
                 tx_baunds_cnt <= 'b0;
                 tx_bit_cnt <= 'b0;
                 uart_wr_en <= 1'b0;
            end
            else if(uart_tx_state == UART_TXDATE)
            begin
                if(tx_baunds_cnt != uart_baunds_div - 1)
                begin
                        uart_wr_en <= 1'b0;
                        tx_bit_cnt <= tx_bit_cnt;
                        tx_baunds_cnt <= tx_baunds_cnt + 1;
                end
                else begin
                    uart_wr_en <= 1'b1;
                    tx_baunds_cnt <= 'b0;
                    tx_bit_cnt <= tx_bit_cnt + 1; 
                end
            end
        end
    end
endmodule

5.2 串行接收模块

串行模块的功能可划分为下图所示;
基于verilog的UART串行总线协议模块设计(含原理、源码、AXI封装、C驱动文件)_第4张图片
  首先边沿检测电路对rx进行检测,如果检测到下降沿,则开始一次Uart接收,接收状态机检测到边沿检测电路发出的有效信号,则进入接收状态;
  在接收状态下,波特率分频器开始工作,这里需要注意,为了能够在rx每比特的中间时刻采用,在第0bit时进行一次f(clk) / Baunds Rate / 2分频,并忽略第一次接收的数据;
比特计数器在检测到接收满8bit后,控制并串模块进行一次高电平停止位的检测,若成功则将并行数据输出,接收状态机回到空闲状态;
  源码:

`timescale 1ns / 1ps
//
// Company: Goodwayhouse
// Engineer: Ge Wen Jie
// 
// Create Date: 2022/09/15 16:52:19
// Design Name: 
// Module Name: uart_drx
// Project Name: 
// Target Devices: 
// Tool Versions: 
// Description: 
// 
// Dependencies: 
// 
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
// 
//
module uart_drx#(
        parameter integer   UART_RX_REF_CLK_FRE = 50_000_000,
        parameter integer   UART_RX_BAUNDS_RATE = 115200

    )(
        input  wire               uart_ref_clk,//uart rx module reference clock
        input  wire               uart_rx_nrst,//uart rx module reset signal
        input  wire  [31:0]    uart_baunds_div,//baunds rate division
        input  wire                uart_rx_din, //serial data input          
        output wire          uart_rx_data_qvld,//receive data valid signal
        output wire             uart_rx_finish,//uart send data finish flag
        output wire               uart_rx_busy,//uart rx module busy flag
        output wire  [7:0]        uart_rx_data //uart rx 8bits data input  
    );

    localparam    IDLE0 = 2'b00;
    localparam    UART_RECV = 2'b01;

    localparam    integer        UART_BAUNDS_COUNTER_MAX = UART_RX_REF_CLK_FRE/UART_RX_BAUNDS_RATE;
    localparam                   UART_TX_START_DOUT =  1'b0;
    localparam                   UART_TX_END_DOUT = 1'b1;

    //inner signals definitions
    reg             rx_finish;
    reg             rx_busy;
    reg  [7:0]      rx_data;
    reg             rx_data_qvld;
    reg  [7:0]      rx_data_buffer;//receive data buffer
    reg  [3:0]      bit_cnt;//receive bit counter
    reg  [1:0]      uart_recv_state;
    reg  [1:0]      uart_recv_next_state;
    reg [31:0]      rx_baunds_cnt;
    reg             uart_rd_en;

    //inner signals connect
    assign uart_rx_busy = rx_busy;
    assign uart_rx_data = rx_data;
    assign uart_rx_finish = rx_finish;
    assign  uart_rx_data_qvld = rx_data_qvld;

    //uart send start check
    wire  uart_send_start;
    reg uart_rx_din_dff;
    assign uart_send_start = uart_rx_din_dff & ~uart_rx_din;
    always @(posedge uart_ref_clk) begin
        if (~uart_rx_nrst) begin
            // reset
            uart_rx_din_dff <= 1'b0;
        end
        else
        begin
            uart_rx_din_dff <= uart_rx_din;
        end
    end

    always@(posedge uart_ref_clk)
    begin
        if (~uart_rx_nrst) begin
            // reset
            uart_recv_state <= IDLE0;
        end
        else
        begin
                uart_recv_state <= uart_recv_next_state;
        end
    end

    always@(*)
    begin
        case(uart_recv_state)
            IDLE0:begin//check if UART send start
                if(uart_send_start == 1'b1) begin
                    uart_recv_next_state <= UART_RECV;
                end
                else begin
                    uart_recv_next_state <= IDLE0;
                end
            end
            UART_RECV:begin //receiver data from master
                if((rx_busy == 1'b0) && (rx_finish == 1'b1))begin
                    uart_recv_next_state <= IDLE0;
                end
                else begin
                    uart_recv_next_state <= UART_RECV;
                end
            end
            default:uart_recv_next_state <= IDLE0;
        endcase
    end

    always@(posedge uart_ref_clk)
    begin
        if(~uart_rx_nrst)begin
            rx_data <= 'b0;
            rx_data_buffer <= 'b0;
            rx_busy <= 1'b0;
            rx_finish <= 1'b1;
            rx_data_qvld <= 1'b0;
        end
        else begin
            if((rx_busy == 1'b0) && (rx_finish == 1'b1))
            begin
                rx_data_qvld <= 1'b0;
                if(uart_send_start == 1'b1)
                begin
                    rx_data_buffer <= 'b0;rx_busy <= 1'b1; rx_finish <= 1'b0;
                end
            end
            else if((rx_busy == 1'b1) && (rx_finish == 1'b0) && (uart_recv_state == UART_RECV) && (uart_rd_en == 1'b1))
            begin
                if((bit_cnt != 4'd0)&&(bit_cnt != 4'd10))
                begin
                    rx_data_buffer <= {uart_rx_din,rx_data_buffer[7:1]};
                end
                // rx_data_buffer <= {uart_rx_din,rx_data_buffer[7:1]};
                else if ((bit_cnt == 4'd10)&&(uart_rx_din == 1'b1)) begin
                    rx_busy <= 1'b0;
                    rx_finish <= 1'b1;
                    rx_data_qvld <= 1'b1;
                    rx_data <= rx_data_buffer;
                    rx_data_buffer <= 'b0;
                end
            end
        end
    end

    always@(posedge uart_ref_clk)
    begin
        if(~uart_rx_nrst)
        begin
           rx_baunds_cnt <= 'b0;
           bit_cnt <= 'b0;
           uart_rd_en <= 1'b0;
        end
        else begin
            if(uart_recv_state == IDLE0)
            begin
                 rx_baunds_cnt <= 'b0;
                 bit_cnt <= 'b0;
                 uart_rd_en <= 1'b0;
            end
            else if(uart_recv_state == UART_RECV)
            begin
                if(bit_cnt == 1'b0)
                begin
                    if(rx_baunds_cnt != (uart_baunds_div>>1) - 1)
                    begin
                        uart_rd_en <= 1'b0;
                        bit_cnt <= bit_cnt;
                        rx_baunds_cnt <= rx_baunds_cnt + 1;
                    end
                    else
                    begin
                        uart_rd_en <= 1'b0;
                        bit_cnt <= bit_cnt + 1;
                        rx_baunds_cnt <= 'b0;
                    end
                end
                else
                begin
                    if(rx_baunds_cnt != uart_baunds_div - 1)
                    begin
                            uart_rd_en <= 1'b0;
                            bit_cnt <= bit_cnt;
                            rx_baunds_cnt <= rx_baunds_cnt + 1;
                    end
                    else begin
                        uart_rd_en <= 1'b1;
                        rx_baunds_cnt <= 'b0;
                        bit_cnt <= bit_cnt + 1; 
                    end
                end
            end
        end
    end

endmodule

5.3 顶层设计

在顶层的设计中,我们需要做:
  Tx模块并行输入端的缓冲(FIFO)
  Rx模块并行输出端的缓冲(FIFO)
  rx信号的跨时钟域处理;
  不同于我们之前说的FIFO,本次设计中的FIFO主要是为了解决高速到低速通信的匹配问题,因此不需要使用到异步FIFO;
  对于rx信号的跨时钟域处理,使用典型的2级触发器;
注:这里的parameter实际上只用到了FIFO_DATA_DEPTH和FIFO_DATA_WIDTH,是第一版设计中保留的;这版中波特率的设置通过uart_baunds_div寄存器直接进行分频
顶层源码:

`timescale 1ns / 1ps
//
// Company:  Goodwayhouse
// Engineer: Ge Wen Jie
// 
// Create Date: 2022/09/11 15:36:29
// Design Name: 
// Module Name: uart_dtxrx_top
// Project Name: 
// Target Devices: 
// Tool Versions: 
// Description: 
// 
// Dependencies: 
// 
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
// 
//
module uart_dtxrx_top#(
        parameter integer UART_REF_CLK_FRE = 50_000_000,
        parameter integer UART_BAUNDS_RATE = 115200,
        parameter integer FIFO_DATA_DEPTH = 8,
        parameter integer FIFO_DATA_WIDTH = 8

    )(
        input  wire                     uart_ref_clk,//uart tx module reference clock
        input  wire                        uart_nrst,//uart tx module reset signal
        input  wire   [7:0]             uart_tx_data,//uart tx 8bits data input    
        input  wire                uart_tx_data_qvld,//send data valid signal
        input  wire                  uart_rx_data_rd,//send data valid signal
        //uart inner signals 
        input  wire   [31:0]         uart_baunds_div,//baunds rate division
        output wire                   uart_tx_finish,//uart send data finish flag
        output wire                     uart_tx_busy,//uart tx module busy flag
        output wire                uart_rx_data_qvld,//receive data valid signal
        output wire                   uart_rx_finish,//uart send data finish flag
        output wire                     uart_rx_busy,//uart rx module busy flag
        output wire  [7:0]              uart_rx_data,//uart rx 8bits data input  
        //FIFO status signals
        output wire                     tx_fifo_full,//tx data fifo full signal
        output wire                    tx_fifo_empty,//tx data fifo empty signal
        output wire                     rx_fifo_full,//rx data fifo full signal
        output wire                    rx_fifo_empty,//rx data fifo empty signal
        //uart tx/rx port
        output wire                     uart_tx_dout,//serial data output
        input  wire                      uart_rx_din,//serial data output
        //interrupt signals output
        input  wire                uart_tx_interrupt_en,//uart tx event finish interrupt enable
        input  wire                uart_rx_interrupt_en,//uart rx event finish interrupt enable
        output wire                uart_tx_interrupt,//uart tx event finish interrupt
        output wire                uart_rx_interrupt //uart rx event finish interrupt
    );

    //*************************************FIFO ADDRESS WIDTH definition start**********************************//
    localparam FIFO_ADDR_WIDTH = $clog2(FIFO_DATA_DEPTH);
    //*************************************FIFO ADDRESS WIDTH definition start**********************************//
    
    wire rx_dff0,rx_dff1;

   dff#(
        .DFF_LEVEL(1),
        .DATA_WIDTH(1)
    )rx_din_dff0(
        .clk(uart_ref_clk),
        .din(uart_rx_din),
        .dout(rx_dff0),
        .nrst(uart_nrst)
    );

    dff#(
        .DFF_LEVEL(1),
        .DATA_WIDTH(1)
    )rx_din_dff1(
        .clk(uart_ref_clk),
        .din(rx_dff0),
        .dout(rx_dff1),
        .nrst(uart_nrst)
    );

    reg           clk_tx_data_rd;
    wire  [7:0]   tx_data_fifo2uart;
    wire  [7:0]   rx_data_uart2fifo;
    reg           tx_data_fifo2uart_qvld;
    reg   [1:0]   uart_tx_state;
    wire          rx_fifo_wr_clk;
    wire           rx_fifo_wr_clk_dff;
    
    assign   rx_fifo_wr_clk = uart_rx_data_qvld&(~rx_fifo_full);
    assign   uart_tx_interrupt = uart_tx_finish & uart_tx_interrupt_en;
    assign   uart_rx_interrupt = uart_rx_data_qvld & uart_rx_interrupt_en;


     dff#(
        .DFF_LEVEL(1),
        .DATA_WIDTH(1)
    )rx_wr_clk_dff2(
        .clk(uart_ref_clk),
        .din(rx_fifo_wr_clk),
        .dout(rx_fifo_wr_clk_dff),
        .nrst(uart_nrst)
    );

   always@(posedge uart_ref_clk, negedge uart_nrst)
   begin
    if(~uart_nrst)
    begin
        clk_tx_data_rd <= 1'b0;
        tx_data_fifo2uart_qvld <= 1'b0;
        uart_tx_state   <= 4'd0;
    end
    else
    begin
            case(uart_tx_state)
                4'd0: 
                begin
                    tx_data_fifo2uart_qvld <= 1'b0;
                    if(tx_fifo_empty)
                    begin
                        uart_tx_state <= 4'd0;
                    end
                    else
                    begin
                        clk_tx_data_rd <= 1'b1;
                        uart_tx_state <= 4'd1;
                    end
               end
               4'd1:
               begin
                    clk_tx_data_rd <= 1'b0;
                    tx_data_fifo2uart_qvld <= 1'b1;
                    if(~uart_tx_busy)
                        uart_tx_state <= uart_tx_state;
                    else
                        uart_tx_state <= 4'd2;
               end 
               4'd2:
               begin
                     tx_data_fifo2uart_qvld <= 1'b0;
                    if(uart_tx_busy)
                        uart_tx_state <= uart_tx_state;
                    else
                        uart_tx_state <= 4'd0;
               end
            endcase
    end
  end

    //tx data from FIFO 
FIFO#(
    .DATA_WIDTH (FIFO_DATA_WIDTH),
    .DATA_DEPTH (FIFO_DATA_DEPTH),
    .ADDR_WIDTH (FIFO_ADDR_WIDTH)
)FIFO_tx_inist0
(
      . clk_wr(uart_tx_data_qvld),
      . clk_rd(clk_tx_data_rd),
      . wr_en(1),
      . rd_en(1),
      . fifo_en(1),
      . fifo_rst(~uart_nrst),
      . wr_data(uart_tx_data),
      . rd_data(tx_data_fifo2uart),
      . full(tx_fifo_full),
      . empty(tx_fifo_empty)
    );

    //rx data from FIFO 
FIFO#(
    .DATA_WIDTH (FIFO_DATA_WIDTH),
    .DATA_DEPTH (FIFO_DATA_DEPTH),
    .ADDR_WIDTH (FIFO_ADDR_WIDTH)
)FIFO_rx_inist0
(
      . clk_wr(rx_fifo_wr_clk_dff),
      . clk_rd(uart_rx_data_rd),
      . wr_en(1),
      . rd_en(1),
      . fifo_en(1),
      . fifo_rst(~uart_nrst),
      . wr_data(rx_data_uart2fifo),
      . rd_data(uart_rx_data),
      . full(rx_fifo_full),
      . empty(rx_fifo_empty)
    );

uart_dtx#(
       .UART_TX_REF_CLK_FRE(UART_REF_CLK_FRE),
       .UART_TX_BAUNDS_RATE(UART_BAUNDS_RATE)
    )uart_dtx_inist0(
       . uart_ref_clk(uart_ref_clk),//uart tx module reference clock
       . uart_tx_nrst(uart_nrst),//uart tx module reset signal
       . uart_baunds_div(uart_baunds_div),
       . uart_tx_data(tx_data_fifo2uart),//uart tx 8bits data input    
       . uart_tx_data_qvld(tx_data_fifo2uart_qvld),//send data valid signal
       . uart_tx_finish(uart_tx_finish),//uart send data finish flag
       . uart_tx_busy(uart_tx_busy),//uart tx module busy flag
       . uart_tx_dout(uart_tx_dout) //serial data output
    );

uart_drx#(
        . UART_RX_REF_CLK_FRE(UART_REF_CLK_FRE),
        . UART_RX_BAUNDS_RATE(UART_BAUNDS_RATE)

    )uart_drx_inist0(
        . uart_ref_clk(uart_ref_clk),//uart rx module reference clock
        . uart_rx_nrst(uart_nrst),//uart rx module reset signal
        . uart_baunds_div(uart_baunds_div),
        . uart_rx_din(rx_dff1), //serial data input          
        . uart_rx_data_qvld(uart_rx_data_qvld),//receive data valid signal
        . uart_rx_finish(uart_rx_finish),//uart send data finish flag
        . uart_rx_busy(uart_rx_busy),//uart rx module busy flag
        . uart_rx_data(rx_data_uart2fifo) //uart rx 8bits data input  
    );
endmodule

简单同步FIFO源码

module FIFO#(
    parameter integer DATA_WIDTH = 32,
    parameter integer DATA_DEPTH = 64,
    parameter integer ADDR_WIDTH = 5
)
(
       input           clk_wr,
       input           clk_rd,
       input           wr_en,
       input           rd_en,
       input         fifo_en,
       input        fifo_rst,
       input  [DATA_WIDTH-1:0]  wr_data,//写数据输入
       output [DATA_WIDTH-1:0]  rd_data,//读数据输出
       output           full,//fifo满信号
       output          empty, //fifo 空信号
       output     fifo_wr_en,
       output     fifo_rd_en,    
       output      fifo_busy,
       output [ADDR_WIDTH-1:0]   wr_add,
       output [ADDR_WIDTH-1:0]   rd_add
    );
    
   wire [ADDR_WIDTH-1:0] wr_addr,rd_addr;//送入RAM的读写地址
   reg [ADDR_WIDTH:0] wr_addr_a,rd_addr_a;//扩位后的写地址与读地址
   
   wire fifo_wr,fifo_rd;//fifo读写控制
   reg [7:0] fifo_data_in;
   reg [3:0] rd_add0;
   
   wire [ADDR_WIDTH:0] rd_addr_a1;
   
   assign fifo_busy = clk_rd;
   
   assign wr_add = wr_addr;
   assign rd_add = rd_addr;
   
   assign wr_addr = wr_addr_a[ADDR_WIDTH-1:0];
   assign rd_addr = rd_addr_a[ADDR_WIDTH-1:0];
   
   assign fifo_wr = (wr_en)&(!full);//写使能且非满
   assign fifo_rd = (rd_en)&(!empty);//读使能且非空
   
   assign fifo_wr_en = fifo_wr;//写使能且非满
   assign fifo_rd_en = fifo_rd;//读使能且非空
   
   
   //空、满信号判断
   assign empty = ((rd_addr_a == wr_addr_a)||(wr_addr_a==0)&(rd_addr_a==0)) ? 1:0;
   assign  full = ((rd_addr_a[ADDR_WIDTH] != wr_addr_a[ADDR_WIDTH])&&(rd_addr_a[ADDR_WIDTH-1:0] == wr_addr_a[ADDR_WIDTH-1:0])) ? 1:0;
   
   
   always @ (posedge clk_wr , posedge fifo_rst)//fifo写
   begin
        if(fifo_rst)
        begin
            wr_addr_a <= 5'd0;//第一个不写
        end
        else
        begin
            if(fifo_en & wr_en & !full)
                    wr_addr_a <=  wr_addr_a + 5'd1;
            else
                    wr_addr_a <= wr_addr_a;
        end
   end
   
    always @ (posedge clk_rd , posedge fifo_rst)//fifo读
   begin
        if(fifo_rst)
        begin
            rd_addr_a <= 5'd0;
        end
        else
        begin
            if(fifo_en & rd_en & !empty )
                    rd_addr_a <=  rd_addr_a + 5'd1;
            else
                     rd_addr_a <= rd_addr_a;
        end
   end
    RAM_LUT#
(
    .DATA_WIDTH(DATA_WIDTH),
    .DATA_DEPTH(DATA_DEPTH),
    .ADDR_WIDTH(ADDR_WIDTH)
)
RAM_LUT_inist0
(
    .fifo_rst(fifo_rst),
    .  data_in(wr_data),
    .   addr_a(wr_addr),
    .    clk_a(clk_wr),
    .      ena(fifo_wr),
    
    . data_out(rd_data),
    .   addr_b(rd_addr),
    .    clk_b(clk_rd),
    .     enb(fifo_rd)
);
endmodule

module RAM_LUT#
(
    parameter integer DATA_WIDTH = 32,
    parameter integer ADDR_WIDTH = 4,
    parameter integer DATA_DEPTH = 16
)
(
    input                         fifo_rst,
    input  [DATA_WIDTH - 1:0]      data_in,
    input  [ADDR_WIDTH - 1:0]       addr_a,
    input                            clk_a,
    input                              ena,
    
    output reg [DATA_WIDTH - 1:0] data_out,
    input  [ADDR_WIDTH - 1:0]       addr_b,
    input                            clk_b,
    input                             enb
);

    reg [31:0] ram[DATA_DEPTH:0];
    
    integer i;
    always@(posedge clk_a,posedge fifo_rst)
    begin
        if(fifo_rst)
            for(i=0;i<DATA_DEPTH;i=i+1) ram[i] <= 'd0;
        else if(ena)
            ram[addr_a] <= data_in;
    end
    
    always@(posedge clk_b,posedge fifo_rst)
    begin
        if(fifo_rst)
            data_out <= 'd0;
        else if(enb)
        begin
            data_out <= ram[addr_b];
        end
    end

endmodule

多级同步器源码:

`timescale 1ns / 1ps
//
// Company: Goodwayhouse
// Engineer: Ge Wen Jie
// 
// Create Date: 2022/08/08 21:33:25
// Design Name: 
// Module Name: dff
// Project Name: 
// Target Devices: 
// Tool Versions: 
// Description: 
// 
// Dependencies: 
// 
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
// 
//


module dff#(
        parameter integer DFF_LEVEL  = 1,
        parameter integer DATA_WIDTH = 8
)(
        input wire                     clk,
        input wire [DATA_WIDTH - 1:0]  din,
        input wire [DATA_WIDTH - 1:0] dout,
        input wire                    nrst
    );

    reg [DATA_WIDTH - 1:0] din_buff [DFF_LEVEL-1:0];
    assign dout = din_buff[DFF_LEVEL-1];
    integer i;

    always @(posedge clk or negedge nrst) begin
        if (~nrst) begin
            // reset
            for(i=0;i<DFF_LEVEL;i=i+1)
            begin
                din_buff[i] <= 'b0;
            end
        end
        else begin
            for(i=1;i<DFF_LEVEL;i=i+1)
            begin
                din_buff[i] <= din_buff[i-1];
            end
                din_buff[0] <= din;
        end
    end
    
endmodule

仿真源码:


module uart_tb(

    );

        parameter integer           UART_REF_CLK_FRE = 50_000_000;
        parameter integer           UART_BAUNDS_RATE = 50_000_000/4;
        //FIFO true depth
        parameter integer           FIFO_DATA_DEPTH = 8;
        //FIFO degth's margin
        parameter integer           FIFO_DATA_WIDTH = 8;

        reg                    uart_ref_clk;//uart tx module reference clock
        reg                       uart_nrst;//uart tx module reset signal
        reg  [7:0]             uart_tx_data;//uart tx 8bits data input    
        reg               uart_tx_data_qvld;//send data valid signal
        //uart inner signals 
        wire                   uart_tx_finish;//uart send data finish flag
        wire                     uart_tx_busy;//uart tx module busy flag
        wire                uart_rx_data_qvld;//receive data valid signal
        wire                   uart_rx_finish;//uart send data finish flag
        wire                     uart_rx_busy;//uart rx module busy flag
        wire  [7:0]              uart_rx_data;//uart rx 8bits data input  
        //FIFO status signals
        wire                     tx_fifo_full;
        wire                    tx_fifo_empty;
        wire                     rx_fifo_full;
        wire                    rx_fifo_empty;
        //uart tx/rx port
        wire                     uart_tx_dout;//serial data output
        reg                      uart_rx_din ;//serial data output
        reg                      uart_rx_data_rd;


        initial begin
            uart_ref_clk = 0;
            forever begin
                #1 uart_ref_clk = ~uart_ref_clk;
            end
        end

        initial begin
            uart_nrst = 0;
            uart_tx_data = 0;
            uart_tx_data_qvld= 0;
            uart_rx_din = 1;
            #2 uart_nrst = 1;

            forever begin
                if(~tx_fifo_full)
                begin
                       uart_tx_data = uart_tx_data+1;
                    #2 uart_tx_data_qvld = 1;
                    #2 uart_tx_data_qvld = 0;
                end
                else #2;
            end
        end


    always @(posedge uart_ref_clk) begin
        if(~uart_nrst) begin
             uart_rx_data_rd <= 1'b0;
        end 
        else 
        begin
             if(~rx_fifo_empty & uart_rx_data_rd == 1'b0)
             begin
                 uart_rx_data_rd <= 1'b1;
             end
             else
             begin
                 uart_rx_data_rd <= 1'b0;
             end
        end
    end


    uart_dtxrx_top#(
        .UART_REF_CLK_FRE(UART_REF_CLK_FRE),
        .UART_BAUNDS_RATE(UART_BAUNDS_RATE),
        .FIFO_DATA_DEPTH (FIFO_DATA_DEPTH),
        .FIFO_DATA_WIDTH (FIFO_DATA_WIDTH)

    )uart_dtxrx_top_inist(
       .       uart_ref_clk(uart_ref_clk),//uart tx module reference clock
       .          uart_nrst(uart_nrst),//uart tx module reset signal
       .       uart_tx_data(uart_tx_data),//uart tx 8bits data input    
       .  uart_tx_data_qvld(uart_tx_data_qvld),//send data valid signal
       . uart_rx_data_rd(uart_rx_data_rd),
       . uart_baunds_div(4),
        //uart inner signals 
        .     uart_tx_finish(uart_tx_finish),//uart send data finish flag
        .       uart_tx_busy(uart_tx_busy),//uart tx module busy flag
        .  uart_rx_data_qvld(uart_rx_data_qvld),//receive data valid signal
        .     uart_rx_finish(uart_rx_finish),//uart send data finish flag
        .       uart_rx_busy(uart_rx_busy),//uart rx module busy flag
        .       uart_rx_data(uart_rx_data),//uart rx 8bits data input  
        //FIFO status signals
        .        tx_fifo_full(tx_fifo_full),
        .       tx_fifo_empty(tx_fifo_empty),
        . rx_fifo_full ( rx_fifo_full),
        . rx_fifo_empty(rx_fifo_empty),
        //uart tx/rx port
        . uart_tx_dout(uart_tx_dout), //serial data output
        .  uart_rx_din(uart_tx_dout) //serial data output
    );
endmodule

仿真结果:
基于verilog的UART串行总线协议模块设计(含原理、源码、AXI封装、C驱动文件)_第5张图片


六、AXI接口封装

如果是纯FPGA设计,这步就可以省略了,上面的模块已经能够使用了;
AXI主要是为了能够让其与CPU交互,进行软硬协同设计;


`timescale 1 ns / 1 ps

module AXI_Uart_Lite_v1_0_S0_AXI #
(
// Users to add parameters here
parameter integer UART_REF_CLK_FRE = 50_000_000,
        parameter integer UART_BAUNDS_RATE = 115200,
        parameter integer FIFO_DATA_DEPTH = 8,
        parameter integer FIFO_DATA_WIDTH = 8,
// User parameters ends
// Do not modify the parameters beyond this line

// Width of S_AXI data bus
parameter integer C_S_AXI_DATA_WIDTH= 32,
// Width of S_AXI address bus
parameter integer C_S_AXI_ADDR_WIDTH= 5
)
(
// Users to add ports here
input  wire                     uart_ref_clk,//uart tx module reference clock
output wire                uart_tx_interrupt,//uart tx event finish interrupt
        output wire                uart_rx_interrupt, //uart rx event finish interrupt
output wire                     uart_tx_dout,//serial data output
        input  wire                      uart_rx_din,//serial data output
// User ports ends
// Do not modify the ports beyond this line

// Global Clock Signal
input wire  S_AXI_ACLK,
// Global Reset Signal. This Signal is Active LOW
input wire  S_AXI_ARESETN,
// Write address (issued by master, acceped by Slave)
input wire [C_S_AXI_ADDR_WIDTH-1 : 0] S_AXI_AWADDR,
// Write channel Protection type. This signal indicates the
    // privilege and security level of the transaction, and whether
    // the transaction is a data access or an instruction access.
input wire [2 : 0] S_AXI_AWPROT,
// Write address valid. This signal indicates that the master signaling
    // valid write address and control information.
input wire  S_AXI_AWVALID,
// Write address ready. This signal indicates that the slave is ready
    // to accept an address and associated control signals.
output wire  S_AXI_AWREADY,
// Write data (issued by master, acceped by Slave) 
input wire [C_S_AXI_DATA_WIDTH-1 : 0] S_AXI_WDATA,
// Write strobes. This signal indicates which byte lanes hold
    // valid data. There is one write strobe bit for each eight
    // bits of the write data bus.    
input wire [(C_S_AXI_DATA_WIDTH/8)-1 : 0] S_AXI_WSTRB,
// Write valid. This signal indicates that valid write
    // data and strobes are available.
input wire  S_AXI_WVALID,
// Write ready. This signal indicates that the slave
    // can accept the write data.
output wire  S_AXI_WREADY,
// Write response. This signal indicates the status
    // of the write transaction.
output wire [1 : 0] S_AXI_BRESP,
// Write response valid. This signal indicates that the channel
    // is signaling a valid write response.
output wire  S_AXI_BVALID,
// Response ready. This signal indicates that the master
    // can accept a write response.
input wire  S_AXI_BREADY,
// Read address (issued by master, acceped by Slave)
input wire [C_S_AXI_ADDR_WIDTH-1 : 0] S_AXI_ARADDR,
// Protection type. This signal indicates the privilege
    // and security level of the transaction, and whether the
    // transaction is a data access or an instruction access.
input wire [2 : 0] S_AXI_ARPROT,
// Read address valid. This signal indicates that the channel
    // is signaling valid read address and control information.
input wire  S_AXI_ARVALID,
// Read address ready. This signal indicates that the slave is
    // ready to accept an address and associated control signals.
output wire  S_AXI_ARREADY,
// Read data (issued by slave)
output wire [C_S_AXI_DATA_WIDTH-1 : 0] S_AXI_RDATA,
// Read response. This signal indicates the status of the
    // read transfer.
output wire [1 : 0] S_AXI_RRESP,
// Read valid. This signal indicates that the channel is
    // signaling the required read data.
output wire  S_AXI_RVALID,
// Read ready. This signal indicates that the master can
    // accept the read data and response information.
input wire  S_AXI_RREADY
);

// UART signals
 wire   uart_tx_finish;//uart send data finish flag
     wire   uart_tx_busy;//uart tx module busy flag
     wire   uart_rx_data_qvld;//receive data valid signal
     wire   uart_rx_finish;//uart send data finish flag
     wire   uart_rx_busy;//uart rx module busy flag
     wire [7:0]     uart_rx_data;//uart rx 8bits data input  
    //FIFO status signals
     wire   tx_fifo_full;//tx data fifo full signal
     wire   tx_fifo_empty;//tx data fifo empty signal
     wire   rx_fifo_full;//rx data fifo full signal
     wire   rx_fifo_empty;//rx data fifo empty signal

// AXI4LITE signals
reg [C_S_AXI_ADDR_WIDTH-1 : 0] axi_awaddr;
reg  axi_awready;
reg  axi_wready;
reg [1 : 0] axi_bresp;
reg  axi_bvalid;
reg [C_S_AXI_ADDR_WIDTH-1 : 0] axi_araddr;
reg  axi_arready;
reg [C_S_AXI_DATA_WIDTH-1 : 0] axi_rdata;
reg [1 : 0] axi_rresp;
reg  axi_rvalid;

// Example-specific design signals
// local parameter for addressing 32 bit / 64 bit C_S_AXI_DATA_WIDTH
// ADDR_LSB is used for addressing 32/64 bit registers/memories
// ADDR_LSB = 2 for 32 bits (n downto 2)
// ADDR_LSB = 3 for 64 bits (n downto 3)
localparam integer ADDR_LSB = (C_S_AXI_DATA_WIDTH/32) + 1;
localparam integer OPT_MEM_ADDR_BITS = 2;
//----------------------------------------------
//-- Signals for user logic register space example
//------------------------------------------------
//-- Number of Slave Registers 8
reg [C_S_AXI_DATA_WIDTH-1:0]slv_reg0;
reg [C_S_AXI_DATA_WIDTH-1:0]slv_reg1;
reg [C_S_AXI_DATA_WIDTH-1:0]slv_reg2;
reg [C_S_AXI_DATA_WIDTH-1:0]slv_reg3;
reg [C_S_AXI_DATA_WIDTH-1:0]slv_reg4;
reg [C_S_AXI_DATA_WIDTH-1:0]slv_reg5;
reg [C_S_AXI_DATA_WIDTH-1:0]slv_reg6;
reg [C_S_AXI_DATA_WIDTH-1:0]slv_reg7;
wire slv_reg_rden;
wire slv_reg_wren;
reg [C_S_AXI_DATA_WIDTH-1:0] reg_data_out;
integer byte_index;
reg aw_en;

// I/O Connections assignments

assign S_AXI_AWREADY= axi_awready;
assign S_AXI_WREADY= axi_wready;
assign S_AXI_BRESP= axi_bresp;
assign S_AXI_BVALID= axi_bvalid;
assign S_AXI_ARREADY= axi_arready;
assign S_AXI_RDATA= axi_rdata;
assign S_AXI_RRESP= axi_rresp;
assign S_AXI_RVALID= axi_rvalid;
// Implement axi_awready generation
// axi_awready is asserted for one S_AXI_ACLK clock cycle when both
// S_AXI_AWVALID and S_AXI_WVALID are asserted. axi_awready is
// de-asserted when reset is low.

always @( posedge S_AXI_ACLK )
begin
  if ( S_AXI_ARESETN == 1'b0 )
    begin
      axi_awready <= 1'b0;
      aw_en <= 1'b1;
    end 
  else
    begin    
      if (~axi_awready && S_AXI_AWVALID && S_AXI_WVALID && aw_en)
        begin
          // slave is ready to accept write address when 
          // there is a valid write address and write data
          // on the write address and data bus. This design 
          // expects no outstanding transactions. 
          axi_awready <= 1'b1;
          aw_en <= 1'b0;
        end
        else if (S_AXI_BREADY && axi_bvalid)
            begin
              aw_en <= 1'b1;
              axi_awready <= 1'b0;
            end
      else           
        begin
          axi_awready <= 1'b0;
        end
    end 
end       

// Implement axi_awaddr latching
// This process is used to latch the address when both 
// S_AXI_AWVALID and S_AXI_WVALID are valid. 

always @( posedge S_AXI_ACLK )
begin
  if ( S_AXI_ARESETN == 1'b0 )
    begin
      axi_awaddr <= 0;
    end 
  else
    begin    
      if (~axi_awready && S_AXI_AWVALID && S_AXI_WVALID && aw_en)
        begin
          // Write Address latching 
          axi_awaddr <= S_AXI_AWADDR;
        end
    end 
end       

// Implement axi_wready generation
// axi_wready is asserted for one S_AXI_ACLK clock cycle when both
// S_AXI_AWVALID and S_AXI_WVALID are asserted. axi_wready is 
// de-asserted when reset is low. 

always @( posedge S_AXI_ACLK )
begin
  if ( S_AXI_ARESETN == 1'b0 )
    begin
      axi_wready <= 1'b0;
    end 
  else
    begin    
      if (~axi_wready && S_AXI_WVALID && S_AXI_AWVALID && aw_en )
        begin
          // slave is ready to accept write data when 
          // there is a valid write address and write data
          // on the write address and data bus. This design 
          // expects no outstanding transactions. 
          axi_wready <= 1'b1;
        end
      else
        begin
          axi_wready <= 1'b0;
        end
    end 
end       

// Implement memory mapped register select and write logic generation
// The write data is accepted and written to memory mapped registers when
// axi_awready, S_AXI_WVALID, axi_wready and S_AXI_WVALID are asserted. Write strobes are used to
// select byte enables of slave registers while writing.
// These registers are cleared when reset (active low) is applied.
// Slave register write enable is asserted when valid address and data are available
// and the slave is ready to accept the write address and write data.
assign slv_reg_wren = axi_wready && S_AXI_WVALID && axi_awready && S_AXI_AWVALID;

always @( posedge S_AXI_ACLK )
begin
  if ( S_AXI_ARESETN == 1'b0 )
    begin
      slv_reg0 <= 0;
      slv_reg1 <= 0;
      slv_reg2 <= 0;
      slv_reg3 <= 0;
      slv_reg4 <= 0;
      slv_reg5 <= 0;
      slv_reg6 <= 0;
      slv_reg7 <= 0;
    end 
  else begin
    if (slv_reg_wren)
      begin
        case ( axi_awaddr[ADDR_LSB+OPT_MEM_ADDR_BITS:ADDR_LSB] )
          3'h0:
            for ( byte_index = 0; byte_index <= (C_S_AXI_DATA_WIDTH/8)-1; byte_index = byte_index+1 )
              if ( S_AXI_WSTRB[byte_index] == 1 ) begin
                // Respective byte enables are asserted as per write strobes 
                // Slave register 0
                slv_reg0[(byte_index*8) +: 8] <= S_AXI_WDATA[(byte_index*8) +: 8];
              end  
          3'h1:
            for ( byte_index = 0; byte_index <= (C_S_AXI_DATA_WIDTH/8)-1; byte_index = byte_index+1 )
              if ( S_AXI_WSTRB[byte_index] == 1 ) begin
                // Respective byte enables are asserted as per write strobes 
                // Slave register 1
                slv_reg1[(byte_index*8) +: 8] <= S_AXI_WDATA[(byte_index*8) +: 8];
              end  
          3'h2:
            for ( byte_index = 0; byte_index <= (C_S_AXI_DATA_WIDTH/8)-1; byte_index = byte_index+1 )
              if ( S_AXI_WSTRB[byte_index] == 1 ) begin
                // Respective byte enables are asserted as per write strobes 
                // Slave register 2
                slv_reg2[(byte_index*8) +: 8] <= S_AXI_WDATA[(byte_index*8) +: 8];
              end  
          3'h3:
            for ( byte_index = 0; byte_index <= (C_S_AXI_DATA_WIDTH/8)-1; byte_index = byte_index+1 )
              if ( S_AXI_WSTRB[byte_index] == 1 ) begin
                // Respective byte enables are asserted as per write strobes 
                // Slave register 3
                slv_reg3[(byte_index*8) +: 8] <= S_AXI_WDATA[(byte_index*8) +: 8];
              end  
          3'h4:
            for ( byte_index = 0; byte_index <= (C_S_AXI_DATA_WIDTH/8)-1; byte_index = byte_index+1 )
              if ( S_AXI_WSTRB[byte_index] == 1 ) begin
                // Respective byte enables are asserted as per write strobes 
                // Slave register 4
                slv_reg4[(byte_index*8) +: 8] <= S_AXI_WDATA[(byte_index*8) +: 8];
              end  
          3'h5:
            for ( byte_index = 0; byte_index <= (C_S_AXI_DATA_WIDTH/8)-1; byte_index = byte_index+1 )
              if ( S_AXI_WSTRB[byte_index] == 1 ) begin
                // Respective byte enables are asserted as per write strobes 
                // Slave register 5
                slv_reg5[(byte_index*8) +: 8] <= S_AXI_WDATA[(byte_index*8) +: 8];
              end  
          3'h6:
            for ( byte_index = 0; byte_index <= (C_S_AXI_DATA_WIDTH/8)-1; byte_index = byte_index+1 )
              if ( S_AXI_WSTRB[byte_index] == 1 ) begin
                // Respective byte enables are asserted as per write strobes 
                // Slave register 6
                slv_reg6[(byte_index*8) +: 8] <= S_AXI_WDATA[(byte_index*8) +: 8];
              end  
          3'h7:
            for ( byte_index = 0; byte_index <= (C_S_AXI_DATA_WIDTH/8)-1; byte_index = byte_index+1 )
              if ( S_AXI_WSTRB[byte_index] == 1 ) begin
                // Respective byte enables are asserted as per write strobes 
                // Slave register 7
                slv_reg7[(byte_index*8) +: 8] <= S_AXI_WDATA[(byte_index*8) +: 8];
              end  
          default : begin
                      slv_reg0 <= slv_reg0;
                      slv_reg1 <= slv_reg1;
                      slv_reg2 <= slv_reg2;
                      slv_reg3 <= slv_reg3;
                      slv_reg4 <= slv_reg4;
                      slv_reg5 <= slv_reg5;
                      slv_reg6 <= slv_reg6;
                      slv_reg7 <= slv_reg7;
                    end
        endcase
      end
  end
end    

// Implement write response logic generation
// The write response and response valid signals are asserted by the slave 
// when axi_wready, S_AXI_WVALID, axi_wready and S_AXI_WVALID are asserted.  
// This marks the acceptance of address and indicates the status of 
// write transaction.

always @( posedge S_AXI_ACLK )
begin
  if ( S_AXI_ARESETN == 1'b0 )
    begin
      axi_bvalid  <= 0;
      axi_bresp   <= 2'b0;
    end 
  else
    begin    
      if (axi_awready && S_AXI_AWVALID && ~axi_bvalid && axi_wready && S_AXI_WVALID)
        begin
          // indicates a valid write response is available
          axi_bvalid <= 1'b1;
          axi_bresp  <= 2'b0; // 'OKAY' response 
        end                   // work error responses in future
      else
        begin
          if (S_AXI_BREADY && axi_bvalid) 
            //check if bready is asserted while bvalid is high) 
            //(there is a possibility that bready is always asserted high)   
            begin
              axi_bvalid <= 1'b0; 
            end  
        end
    end
end   

// Implement axi_arready generation
// axi_arready is asserted for one S_AXI_ACLK clock cycle when
// S_AXI_ARVALID is asserted. axi_awready is 
// de-asserted when reset (active low) is asserted. 
// The read address is also latched when S_AXI_ARVALID is 
// asserted. axi_araddr is reset to zero on reset assertion.

always @( posedge S_AXI_ACLK )
begin
  if ( S_AXI_ARESETN == 1'b0 )
    begin
      axi_arready <= 1'b0;
      axi_araddr  <= 32'b0;
    end 
  else
    begin    
      if (~axi_arready && S_AXI_ARVALID)
        begin
          // indicates that the slave has acceped the valid read address
          axi_arready <= 1'b1;
          // Read address latching
          axi_araddr  <= S_AXI_ARADDR;
        end
      else
        begin
          axi_arready <= 1'b0;
        end
    end 
end       

// Implement axi_arvalid generation
// axi_rvalid is asserted for one S_AXI_ACLK clock cycle when both 
// S_AXI_ARVALID and axi_arready are asserted. The slave registers 
// data are available on the axi_rdata bus at this instance. The 
// assertion of axi_rvalid marks the validity of read data on the 
// bus and axi_rresp indicates the status of read transaction.axi_rvalid 
// is deasserted on reset (active low). axi_rresp and axi_rdata are 
// cleared to zero on reset (active low).  
always @( posedge S_AXI_ACLK )
begin
  if ( S_AXI_ARESETN == 1'b0 )
    begin
      axi_rvalid <= 0;
      axi_rresp  <= 0;
    end 
  else
    begin    
      if (axi_arready && S_AXI_ARVALID && ~axi_rvalid)
        begin
          // Valid read data is available at the read data bus
          axi_rvalid <= 1'b1;
          axi_rresp  <= 2'b0; // 'OKAY' response
        end   
      else if (axi_rvalid && S_AXI_RREADY)
        begin
          // Read data is accepted by the master
          axi_rvalid <= 1'b0;
        end                
    end
end    

// Implement memory mapped register select and read logic generation
// Slave register read enable is asserted when valid address is available
// and the slave is ready to accept the read address.
assign slv_reg_rden = axi_arready & S_AXI_ARVALID & ~axi_rvalid;
always @(*)
begin
      // Address decoding for reading registers
      case ( axi_araddr[ADDR_LSB+OPT_MEM_ADDR_BITS:ADDR_LSB] )
        3'h0   : reg_data_out <= slv_reg0;
        3'h1   : reg_data_out <= slv_reg1;
        3'h2   : reg_data_out <= uart_rx_data;
        3'h3   : reg_data_out <= {uart_rx_data_qvld,uart_rx_busy,uart_rx_finish,uart_tx_busy,uart_tx_finish,
        rx_fifo_empty,rx_fifo_full,tx_fifo_empty,tx_fifo_full};
        3'h4   : reg_data_out <= slv_reg4;
        3'h5   : reg_data_out <= slv_reg5;
        3'h6   : reg_data_out <= slv_reg6;
        3'h7   : reg_data_out <= slv_reg7;
        default : reg_data_out <= 0;
      endcase
end

// Output register or memory read data
always @( posedge S_AXI_ACLK )
begin
  if ( S_AXI_ARESETN == 1'b0 )
    begin
      axi_rdata  <= 0;
    end 
  else
    begin    
      // When there is a valid read address (S_AXI_ARVALID) with 
      // acceptance of read address by the slave (axi_arready), 
      // output the read dada 
      if (slv_reg_rden)
        begin
          axi_rdata <= reg_data_out;     // register read data
        end   
    end
end    

// Add user logic here
uart_dtxrx_top#(
       .UART_REF_CLK_FRE(UART_REF_CLK_FRE),
       .UART_BAUNDS_RATE(UART_BAUNDS_RATE),
       .FIFO_DATA_DEPTH (FIFO_DATA_DEPTH),
       .FIFO_DATA_WIDTH (FIFO_DATA_WIDTH)
    )uart_dtxrx_top_inist(
        .uart_ref_clk(uart_ref_clk),//uart tx module reference clock
        .uart_nrst(slv_reg0[0]),//uart tx module reset signal
        .uart_baunds_div(slv_reg4),
.uart_tx_interrupt_en(slv_reg0[3]),
.uart_rx_interrupt_en(slv_reg0[4]),
        .uart_tx_data(slv_reg1[7:0]),//uart tx 8bits data input    
        .uart_tx_data_qvld(slv_reg0[1]),//send data valid signal
        .uart_rx_data_rd(slv_reg0[2]),//send data valid signal
        //uart inner signals 
        .uart_tx_finish(uart_tx_finish),//uart send data finish flag
        .uart_tx_busy(uart_tx_busy),//uart tx module busy flag
        .uart_rx_data_qvld(uart_rx_data_qvld),//receive data valid signal
        .uart_rx_finish(uart_rx_finish),//uart send data finish flag
        .uart_rx_busy(uart_rx_busy),//uart rx module busy flag
        .uart_rx_data(uart_rx_data),//uart rx 8bits data input  
        //FIFO status signals
        .tx_fifo_full(tx_fifo_full),//tx data fifo full signal
        .tx_fifo_empty(tx_fifo_empty),//tx data fifo empty signal
        .rx_fifo_full(rx_fifo_full),//rx data fifo full signal
        .rx_fifo_empty(rx_fifo_empty),//rx data fifo empty signal
        //uart tx/rx port
        .uart_tx_dout(uart_tx_dout),//serial data output
        .uart_rx_din(uart_rx_din),//serial data output
        //interrupt signals output
       .uart_tx_interrupt(uart_tx_interrupt),//uart tx event finish interrupt
       .uart_rx_interrupt(uart_rx_interrupt) //uart rx event finish interrupt
    );
// User logic ends

endmodule

七、C函数设计

在完成了AXI封装后,我们只需通过C语言控制其寄存器完成相关逻辑即可。
由于篇幅原因,这里只给出头文件,想要C的可以私聊博主;

/*******************************************************************************
 * IIC_Master_user.h
 *
 *  Platform: Vivado.2018.3
 *  Created on: 2022年9月18日
 *      Author: Ge Wen Jie
 *  Copyright owner:Ge Wen Jie
 //--------------------------------Describe-------------------------------------
  * This file contains definitions of the offset address of AXI_Uart_Lite registers
  * and the functional definitions of each bit of a register.
  * If users want to add or alter the basic addresses of AXI_Uart_Lite devices, you
  * can alter the AXI_UART_Lite_0 definition.
  *   *Other: to ensure a right Baunds rate can be set
  *User must alter the AXI_UART_LITE_SOURCE_CLK definition in AXI_Uart_Lite_user.h as the real clock frequency of AXI_Uart_Lite device
  *in SOC block design project.
 //-----------------------------------------------------------------------------
 *You can see http://hihii11.github.io/GWJ_BLOG.html for more information
 *about IPs.
 *******************************************************************************/

#ifndef USER_AXI_UART_LITE_USER_H_
#define USER_AXI_UART_LITE_USER_H_

#define     AXI_UART_LITE_SOURCE_CLK                50000000                      //

//AXI_Uart_Lite base addresses
#define AXI_UART_Lite_0                     (unsigned int)(0x43C30000)         //

//AXI_Uart_Lite registers offset addresses
#define     AXI_Uart_CTL0                       (unsigned int)(0x00000000)         //
#define     AXI_Uart_TXBUF                      (unsigned int)(0x00000004)         //
#define     AXI_Uart_RXBUF                      (unsigned int)(0x00000008)         //
#define     AXI_Uart_IFG0                       (unsigned int)(0x0000000C)         //
#define     AXI_Uart_BAUNDS_DIV                 (unsigned int)(0x00000010)         //

//AXI_Uart_Lite bit function definitions of CTL0 register
#define    AXI_UART_CTL0_RST                    (unsigned int)(0x00000000)         //
#define    AXI_UART_CTL0_EN                     (unsigned int)(0x00000001)         //
#define    AXI_UART_CTL0_DVALID                 (unsigned int)(0x00000002)         //
#define    AXI_UART_CTL0_RD                     (unsigned int)(0x00000004)         //
#define    AXI_UART_CTL0_TXINTEN                (unsigned int)(0x00000008)         //
#define    AXI_UART_CTL0_RXINTEN                (unsigned int)(0x00000010)         //

//AXI_Uart_Lite bit function definitions of IFG0 register
#define    AXI_UART_IFG0_TXFIFO_FULL            (unsigned int)(0x00000001)         //
#define    AXI_UART_IFG0_TXFIFO_EMPTY           (unsigned int)(0x00000002)         //
#define    AXI_UART_IFG0_RXFIFO_FULL            (unsigned int)(0x00000004)         //
#define    AXI_UART_IFG0_RXFIFO_EMPTY           (unsigned int)(0x00000008)         //
#define    AXI_UART_IFG0_TX_FINISH              (unsigned int)(0x00000010)         //
#define    AXI_UART_IFG0_TX_BUSY                (unsigned int)(0x00000020)         //
#define    AXI_UART_IFG0_RX_FINISH              (unsigned int)(0x00000040)         //
#define    AXI_UART_IFG0_RX_BUSY                (unsigned int)(0x00000080)         //
#define    AXI_UART_IFG0_RX_DVALID              (unsigned int)(0x00000100)         //

#define    Uart_Device_Delay                       usleep(100)

extern void AXI_Uart_Lite_reset(AXI_BaseAddress_Data_Type Base_addr);
extern void AXI_Uart_Lite_enable(AXI_BaseAddress_Data_Type Base_addr);
extern void AXI_Uart_Lite_disable(AXI_BaseAddress_Data_Type Base_addr);
extern void AXI_Uart_Lite_Set_Baunds_Rate_div(AXI_BaseAddress_Data_Type Base_addr,AXI_Register_Data_Type Baunds_Div);
extern void AXI_Uart_Lite_Set_Baunds(AXI_BaseAddress_Data_Type Base_addr,uint32 Baunds);
extern void AXI_Uart_Lite_Tx_interrupt_enable(AXI_BaseAddress_Data_Type Base_addr);
extern void AXI_Uart_Lite_Tx_interrupt_disable(AXI_BaseAddress_Data_Type Base_addr);
extern void AXI_Uart_Lite_Rx_interrupt_enable(AXI_BaseAddress_Data_Type Base_addr);
extern void AXI_Uart_Lite_Rx_interrupt_disable(AXI_BaseAddress_Data_Type Base_addr);
extern void AXI_Uart_Lite_send_byte(AXI_BaseAddress_Data_Type Base_addr, uint8 data);
extern uint8 AXI_Uart_Lite_read_byte(AXI_BaseAddress_Data_Type Base_addr, uint8 * data);
extern uint16 AXI_Uart_Lite_read_IFG(AXI_BaseAddress_Data_Type Base_addr);
extern void AXI_Uart_Lite_send_len(AXI_BaseAddress_Data_Type Base_addr,uint8 * buffer,uint16 len);
extern void AXI_Uart_Lite_send_str(AXI_BaseAddress_Data_Type Base_addr,uint8 * str);

#endif /* USER_AXI_UART_LITE_USER_H_ */


你可能感兴趣的:(基于Vivado的硬件设计,fpga开发,硬件工程,硬件架构,嵌入式硬件)