fpga.一份带缓冲的uart模块代码分享

一、写在开头

        - 这个代码是整个库文件包工程的一部分,希望能在两年的时间写出完整的包,但是时间也不多,只能晚上空闲时断断续续写的,毕竟不能耽误打游戏不是

        - 该工程的地址分享在gitee,建议直接看gitee的,blog的代码不会更新。基于Tank_nano_4k小蜜蜂开发板的常用开发库: 给用gw1nsr设计的小蜜蜂开发板开发的常用软件库

        - 这个uart模块支持rx、tx波特率分开,在应对io数量抓级时有可以rx、tx分别接只需要rx或tx的ic\模块

        - 支持1200到最高1152000,其实更高也是可以的,设计时理论到11.52Mhz,不过只测到示波器3M,因为上位机软件都已经不支持了。所以只取1200到1.152Mhz这个上位机usb转ttl收发模块都支持的波特率区间

        - 支持1-32bit数据,不过只测试过5/6/78bit,因为上位机只支持5/6/7/8bit所有只取5/6/7/8bit

        - 带硬件流控以及支持校验位,软件流控用的不多,暂时不添加

        - 这个模块对资源的占用很大,在综合软件上看用了129个逻辑资源,而且逻辑写的也不咋地,所以这个模块基本是很难使用了,所以可以转道我的另一个uart_simple.v代码了,通过切除不需要的功能得到一个最简uart模块

二、代码(模块名即文件名)

1、模块文件

/*
 * @Author: 
 * @Date: 2023-11-22 11:20:27
 * @LastEditors: 
 * @LastEditTime: 2023-12-20 02:08:13
 * @FilePath: \gw1nsr_4_package\package\uart\uart.v
 * @Description: baud rate list : 1200*n -> 1152000
 * 
 * Copyright (c) 2023 by ${git_name_email}, All Rights Reserved. 
 */
module uart #(
    parameter baudRateTX = 9600,        //1200*n -> 1152000
    parameter baudRateRX = 9600,        //1200*n -> 1152000
    parameter dataFlowControl = 0,      //0(none) 1(hard) 2(soft)
    parameter verifyBit = 0,            //0(none) 1(even parity) 2(odd parity) 3(space parity) 4(mark parity)
    parameter dataBitWidth = 8,         //<= 32
    parameter stopBit = 1               //1、2 //不支持1.5停止位
) (
    input rst, clk_2304000Hz,//clk = 1200Hz * n
    input [dataBitWidth-1 : 0] send, 
    input write_en, read_en,//写1使能,读1使能
    output reg [dataBitWidth-1 : 0] receive, 
    output reg read_operation_int,//读1中断发生,
    output write_operation_allow, //写1允许
    input rx,
    output tx,
    input  cts,   //clear to send
    output reg rts   //require to send
);
    
    reg [21:0]clk_divTX = 22'b0;
    reg [21:0]clk_divRX = 22'b0;
    reg clk_swapTX = 1'b0;
    reg clk_swapRX = 1'b0, RXclk_clr = 1'b0, RXclk_clr_last = 1'b0;
    // 分频出给tx、rx使用的时钟
    always @(posedge clk_2304000Hz) begin
        if(!rst) begin
            clk_divTX <= 22'b0;
            clk_divRX <= 22'b0;
            clk_swapTX <= 1'b0;
            clk_swapRX <= 1'b0;
            RXclk_clr_last <= 1'b0;
        end else begin
            // TX的时钟
            if(clk_divTX == 1152000*2*10 / baudRateTX - 1) begin
                clk_swapTX <= 1'b1;
                clk_divTX <= 22'b0;
            end else if(clk_divTX < 1152000*2*10 / baudRateTX - 1) begin
                clk_swapTX <= 1'b0;
                clk_divTX <= clk_divTX + 1'b1;
            end

            if(RXclk_clr == 1'b1 && RXclk_clr_last == 1'b0) begin
                clk_divRX <= 22'b0;
                RXclk_clr_last <= RXclk_clr;
            end else begin
                RXclk_clr_last <= RXclk_clr;
                // RX的时钟
                if(clk_divRX == 1152000*2*10 / baudRateRX - 1) begin
                    clk_swapRX <= 1'b1;
                    clk_divRX <= 22'b0;
                end else if(clk_divRX <= 1152000*10 / baudRateRX - 1) begin
                    clk_swapRX <= 1'b1;
                    clk_divRX <= clk_divRX + 1'b1;
                end else if(clk_divRX > 1152000*10 / baudRateRX - 1) begin
                    clk_swapRX <= 1'b0;
                    clk_divRX <= clk_divRX + 1'b1;
                end 
            end

                  
        end
    end

    reg [5:0]count = 6'b0; 
    reg [dataBitWidth-1:0]tx_buff_reg = 32'b0;
    reg txbuff_full = 32'b0;//发送缓冲寄存器
    reg [dataBitWidth-1:0]tx_displacement_reg = 32'b0;
    reg tx_busy = 32'b0;//移位寄存器
    reg verify = 1'b0;
    reg [2:0]stop_count = 3'b0;
    reg tx_pin = 1'b1;

    reg [5:0]count_rx = 6'b0;
    reg [dataBitWidth-1:0]rx_buff_reg = 32'b0;
    reg rxbuff_full = 1'b0;//接收缓冲寄存器
    reg [dataBitWidth-1:0]rx_displacement_reg = 32'b0;
    reg rx_busy = 1'b0;//接收移位寄存器
    reg verify_rx = 1'b0, verify_ok = 1'b1;
    reg [2:0]stop_count_rx = 3'b0;
    wire rx_pin;

    generate
        case (dataFlowControl)
            32'd0 ,32'd1 : begin : none_or_hard
                always @(negedge clk_2304000Hz) begin
                    if(rx == 1'b0 && rx_busy == 1'b0) begin
                        RXclk_clr <= 1'b1;//重置时钟计数
                    end else if(rx_busy == 1'b1) begin
                        RXclk_clr <= 1'b0;
                    end
                end

                always @(negedge clk_swapRX) begin//rx
                    if (!rst) begin
                        count_rx <= 6'b0;
                        rx_buff_reg <= 32'b0;
                        rxbuff_full <= 1'b0;
                        rx_displacement_reg <= 32'b0;
                        rx_busy <= 1'b0;
                        verify_rx <= 1'b0;
                        verify_ok <= 1'b1;
                        stop_count_rx <= 3'b0;
                        rts <= 1'b0;
                    end else begin
                        if(rxbuff_full) begin
                            if(read_en) begin
                                receive <= rx_buff_reg;
                                rx_buff_reg <= 32'b0;
                                read_operation_int <= 1'b0;
                                rxbuff_full <= 1'b0;
                            end
                        end 

						if(rx == 1'b0 && rx_busy == 1'b0) begin
                            //忙碌接收移位寄存器
                            rx_busy <= 1'b1;
                        end else if(rx_busy == 1'b1) begin
                            //接收数据到移位寄存器,并校验数据
                            if(count_rx == dataBitWidth) begin
                                if (verifyBit == 0) begin//无校验
                                    //查空,把移位寄存器数据上发到缓冲,并清除或设置各个中间变量
                                    if(rxbuff_full == 1'b0) begin
                                        rx_buff_reg <= rx_displacement_reg;
                                        rxbuff_full <= 1'b1;
                                        stop_count_rx <= 2'b0;
                                        verify_rx <= 1'b0;
                                        count_rx <= 6'b0;
                                        rx_busy <= 1'b0;
                                        read_operation_int <= 1'b1;
                                        rts <= 1'b0;
                                    end else begin
                                        rts <= 1'b1;
                                    end
                                end else begin
                                    if(stop_count_rx == 1'b0)begin
                                        verify_rx <= verify_rx + rx_pin;
                                    end else if(stop_count_rx == 1'b1) begin
                                        //检查校验位
                                        if (verifyBit == 1) begin//偶校验
                                            if(verify_rx == 1'b0) begin
                                                verify_ok <= 1'b0;
                                            end
                                        end else if (verifyBit == 2) begin//奇校验
                                            if(verify_rx == 1'b1) begin
                                                verify_ok <= 1'b0;
                                            end
                                        end else if (verifyBit == 3) begin//恒0
                                            if(verify_rx == 1'b0) begin
                                                verify_ok <= 1'b0;
                                            end
                                        end else if (verifyBit == 4) begin//恒1
                                            if(verify_rx == 1'b1) begin
                                                verify_ok <= 1'b0;
                                            end
                                        end
                                    end

                                    if (stop_count_rx == stopBit) begin
                                        //查空,把移位寄存器数据上发到缓冲,并清除或设置各个中间变量
                                        if(rxbuff_full == 1'b0) begin
                                            rx_buff_reg <= rx_displacement_reg;
                                            rxbuff_full <= 1'b1;
                                            stop_count_rx <= 2'b0;
                                            verify_rx <= 1'b0;
                                            count_rx <= 6'b0;
                                            rx_busy <= 1'b0;
                                            read_operation_int <= 1'b1;
                                            rts <= 1'b0;
                                        end else begin
                                            rts <= 1'b1;
                                        end
                                    end else begin
                                        stop_count_rx <= stop_count_rx + 1'b1;
                                    end 
                                end
                            end else begin
                                rx_displacement_reg <= rx_displacement_reg >> 1;
                                rx_displacement_reg[dataBitWidth-1] <= rx_pin;
                                verify_rx <= verify_rx + rx_pin;
                                count_rx <= count_rx + 1'b1;
                            end
                        end
                    end
                end
                assign rx_pin = rx;
                
                always @(posedge clk_swapTX) begin//tx
                    if (!rst) begin
                        count <= 6'b0;
                        tx_buff_reg <= 32'b0;
                        txbuff_full <= 1'b0;
                        tx_displacement_reg <= 32'b0;
                        tx_busy <= 1'b0;
                        verify <= 1'b0;
                        stop_count <= 3'b0;
                        tx_pin <= 1'b1;
                    end else begin
                        //上层检查缓存满标志为空,将data发送到缓冲寄存器,外设自动置位缓存满标志
                        if (write_en) begin//检查到写使能后把数据压入缓冲
                            tx_buff_reg <= send;
                            txbuff_full <= 1'b1;
                        end 

                        //检查流控是否允许传输
                        if(!cts) begin
                            //检查移位寄存器忙碌标志,若空闲则缓冲满标志清除,同时把缓冲发送到移位寄存器,并移位寄存器标志位忙碌
                            if(txbuff_full) begin
                                if (!tx_busy) begin
                                    tx_displacement_reg <= tx_buff_reg;
                                    tx_buff_reg <= 32'b0;
                                    tx_busy <= 1'b1;
                                    txbuff_full <= 1'b0;    
                                end
                            end
                        end
                        //发送数据、发送校验位、发送停止位
                        if (tx_busy) begin
                            if(count == dataBitWidth+1) begin
                                //发送停止位
                                if (verifyBit == 1'b0) begin //无校验
                                    if (stop_count == stopBit) begin
                                        //移位寄存器发送后,移位寄存器空闲
                                        count <= 5'b0;
                                        tx_busy <= 1'b0;
                                        stop_count <= 2'b0;
                                    end else begin
                                        tx_pin <= 1'b1;
                                        stop_count <= stop_count + 1'b1;
                                    end
                                end else begin
                                    if (stop_count == stopBit+1'b1) begin
                                        //移位寄存器发送后,移位寄存器空闲
                                        count <= 5'b0;
                                        tx_busy <= 1'b0;
                                        stop_count <= 2'b0;
                                    end else begin
                                        if (stop_count == 1'b0) begin
                                            //发送校验位
                                            if (verifyBit == 1) begin//偶校验
                                                tx_pin <= verify;
                                            end else if (verifyBit == 2) begin//奇校验
                                                tx_pin <= !verify;
                                            end else if (verifyBit == 3) begin//恒0
                                                tx_pin <= 1'b0;
                                            end else if (verifyBit == 4) begin//恒1
                                                tx_pin <= 1'b1;
                                            end
                                        end else begin
                                            tx_pin <= 1'b1;
                                        end
                                        stop_count <= stop_count + 1'b1;
                                    end
                                end
                            end else begin
                                if (count == 5'b0) begin
                                    tx_pin <= 1'b0;//起始位
                                    count <= count + 1'b1;
                                end else begin
                                    verify <= verify + tx_displacement_reg[0]; 
                                    tx_pin <= tx_displacement_reg[0];
                                    tx_displacement_reg <= tx_displacement_reg >> 1;
                                    count <= count + 1'b1;
                                end
                            end
                        end
                    end
                end
                assign write_operation_allow = !txbuff_full;
                assign tx = tx_pin;
            end        
            32'd2 : begin : soft
            
            end
            default : begin : NOP end
        endcase
    endgenerate

endmodule

2、工程测试顶层文件

/*
 * @Author: 
 * @Date: 2023-12-05 09:17:34
 * @LastEditors: 
 * @LastEditTime: 2023-12-21 00:31:41
 * @FilePath: \gw1nsr_4_package\project\uart_test\uart_top.v
 * @Description: 
 * 
 * Copyright (c) 2023 by ${git_name_email}, All Rights Reserved. 
 */
//`define __UART_TOP_V__
`ifdef __UART_TOP_V__
    `include "../../package/uart/uart.v"
`endif

module uart_top (
    input rst, rx, clk,
    output tx, tx_copy, rx_copy
);
    wire clk_23_04Mhz;
    reg [7:0] send;
    wire [7:0] receive;
    reg write_en = 1'b0, read_en = 1'b0;
    wire read_operation_int, write_operation_allow;

    reg wait_allow = 1'b0;

    reg read_flag = 1'b0;
    reg operation_r2 = 3'b0;
	
	assign tx_copy = tx;
	assign rx_copy = rx;

    always @(posedge clk) begin
        if (!rst) begin
            read_en <= 1'b0;
            send <= 8'b0;
            operation_r2 <= 3'b0;
            wait_allow <= 1'b0;
        end else begin
            if (read_operation_int) begin//rx
                read_en <= 1'b1;
                read_flag <= 1'b1;
                write_en <= 1'b0;
            end else begin
                if (read_flag == 1'b1) begin
                    read_flag <= 1'b0;
                    read_en <= 1'b0;
                    operation_r2 <= 1'b1;
                end
            end

            //operation_r2[2:1] <= {operation_r2[1], operation_r2[0]};
            if (operation_r2 == 1'b1) begin
                if (write_operation_allow == 1'b1) begin
                    send <= receive;
                    write_en <= 1'b1;
                    wait_allow <= 1'b1;
                end 
                if (wait_allow == 1'b1 && write_operation_allow == 1'b0) begin
                    wait_allow <= 1'b0;
                    write_en <= 1'b0;
                    operation_r2 <= 3'b0;
                end
            end else begin
                write_en <= 1'b0;
            end
        end
    end

    Gowin_PLLVR clk27_to_23Mhz(
        .clkout(clk_23_04Mhz),
        .clkin(clk)
    );
	
    uart #(
        .baudRateTX         (1152000   ),
        .baudRateRX         (1152000   ),
        .dataFlowControl    (0      ), //0(none)
        .verifyBit          (0      ), //0(none)
        .dataBitWidth       (5      ),
        .stopBit            (1      )
    ) uart_advance (
        .rst                    (rst),
        .clk_2304000Hz          (clk_23_04Mhz),
        .send                   (send),
        .write_en               (write_en),
        .read_en                (read_en),
        .receive                (receive),
        .read_operation_int     (read_operation_int),
        .write_operation_allow  (write_operation_allow),
        .rx                     (rx),
        .tx                     (tx),
        .cts                    (1'b0),
        .rts                    ()
    );
endmodule

3、仿真文件

/*
 * @Author: 
 * @Date: 2023-12-05 16:17:51
 * @LastEditors: 
 * @LastEditTime: 2023-12-19 00:30:44
 * @FilePath: \gw1nsr_4_package\project\uart_test\tb.v
 * @Description: 这是对uart模块的测试
 * 
 * Copyright (c) 2023 by ${git_name_email}, All Rights Reserved. 
 */
`timescale 1ns/1ns

`default_nettype none

module tb_uart;
    reg clk;
    reg rst_n;
    reg tx = 1'b1;
    wire rx;

    uart_top gt(
        .rst  (rst_n),
        .clk    (clk),
        .rx     (tx),
        .tx     (rx)
    );

    localparam CLK_PERIOD = 36;
    always begin //27Mhz
        #(CLK_PERIOD/2) clk=1'b0;
        #(CLK_PERIOD/2+1) clk=1'b1;
    end

    initial begin
        $dumpfile("tb_.vcd");
        $dumpvars(0, tb_uart);
    end

    initial begin
        #1 rst_n<=1'bx;clk<=1'bx;
        #(CLK_PERIOD*3) rst_n<=1;
        #(CLK_PERIOD*3) rst_n<=0;clk<=0;
        repeat(5) @(posedge clk);
        rst_n<=1;
        @(posedge clk);
        repeat(2) @(posedge clk);
        //$finish(2);
    end

	reg clk_9600;
	reg [11:0]count_div;
	always @(posedge clk) begin
		if(!rst_n) begin
			count_div <= 12'b0;
		end else begin
			if(count_div == 12'd2812) begin
				count_div <= 12'b0;
				clk_9600 <= 1'b1;
			end else begin
				count_div = count_div + 1'b1;
				clk_9600 <= 1'b0;
			end
		end
	end
	
	reg [7:0]count = 8'b0;
	always @(posedge clk_9600)begin
		if(~rst_n) begin
			count <= 8'b0;
		end else begin
			if(count == 8'd9) begin
				count = 8'd0;
                tx <= 1'b1;
            end else if(count >= 8'd8) begin
                tx <= 1'b1;
                count = count + 1'b1;
            end else begin
                tx <= 1'b0;
                count = count + 1'b1;
            end
		end
		
	end
	
endmodule
`default_nettype wire

三、注意事项

因为rx模块上传与tx模块下传都要消耗一单位特率时间,导致模块内部rx连tx来转发上位机发来的数据到上位机时,总会因为这个2单位时间消耗掉所有的缓冲。所以无流控设计时注意好tx、rx的数据量。

你可能感兴趣的:(fpga开发,嵌入式硬件,学习)