LOADING

加载过慢请开启缓存 浏览器默认开启

Verilog-UART设计

2023/10/23 Verilog

Abstract: Verilog UART  设计


目录:


更新记录

当前为 v4 版本

解决了之前版本连续收发时丢数据的问题

模块代码

Rx 部分

// UART 接收模块 可选波特率 可选数据位 仅支持一位停止位

module UART_Rx_module #(
    parameter CLK_FREQ_MHZ = 27,
    parameter BAUD_RATE = 9600,
    parameter DATA_WIDTH = 8  // 数据位宽
) (
    input in_sys_clk,
    input in_rst_n,
    input in_rx_ready,  // 准备好接受串口数据
    input in_rx_pin,

    output [DATA_WIDTH-1:0] out_rx_data,  // 接收到的串口数据 rx_data_ready和rx_data_valid均为高时送出
    output out_rx_data_valid  // 接收到的串口数据有效
);

    localparam BAUD_CNT_THRESHOLD = CLK_FREQ_MHZ * 1000000 / BAUD_RATE;  // 波特率计数最大值
    localparam BAUD_CLK_CNT_THRESHOLD = (DATA_WIDTH + 2);  // 波特率时钟计数最大值 数据位+起始位+结束位

    wire w_rx_negedge;
    wire w_rx_done;  // 接收接收标识
    wire w_sample_signal;
    wire w_bit_rev_done;

    reg r_rx_pin_t0, r_rx_pin_t1;
    reg r_rx_state;  // 接收状态
    reg [$clog2(BAUD_CNT_THRESHOLD)-1:0] r_baud_rate_cnt;  // 波特率计数器
    reg [$clog2(BAUD_CLK_CNT_THRESHOLD)-1:0] r_baud_rate_clk_cnt;  // 波特率时钟计数
    reg [BAUD_CLK_CNT_THRESHOLD-1:0] r_rx_data_frame_byte;  // 完整数据帧

    assign w_rx_negedge = (r_rx_pin_t1 && ~r_rx_pin_t0);
    assign w_rx_done = (r_baud_rate_clk_cnt==BAUD_CLK_CNT_THRESHOLD-1) && (r_baud_rate_cnt==BAUD_CNT_THRESHOLD/2+2);  // 不判断完整的结束位
    assign w_sample_signal = (r_baud_rate_cnt==BAUD_CNT_THRESHOLD/2);
    assign w_bit_rev_done = (r_baud_rate_cnt==BAUD_CNT_THRESHOLD-1);

    assign out_rx_data = (out_rx_data_valid && in_rx_ready)? r_rx_data_frame_byte[BAUD_CLK_CNT_THRESHOLD-2:1] : 'd0;
    assign out_rx_data_valid = w_rx_done;

//****输入数据同步****//
    always @(posedge in_sys_clk or negedge in_rst_n) begin
        if (~in_rst_n) begin
            r_rx_pin_t0 <= 1'b0; 
            r_rx_pin_t1 <= 1'b0;
        end
        else begin
            r_rx_pin_t0 <= in_rx_pin;
            r_rx_pin_t1 <= r_rx_pin_t0;
        end
    end
//********//

//****接收状态逻辑****//
    always @(posedge in_sys_clk or negedge in_rst_n) begin
        if (~in_rst_n) begin
            r_rx_state <= 1'b0;
        end
        else begin
            if (w_rx_negedge && ~r_rx_state) begin  // 下降沿到来
                r_rx_state <= 1'b1;
            end
            else if (w_rx_done) begin  // 接收完成
                r_rx_state <= 1'b0;
            end
            else if (r_baud_rate_clk_cnt=='d1 && r_rx_data_frame_byte[0]==1'b1) begin  // 起始位采样后高电平居多
                r_rx_state <= 1'b0;
            end
            else begin
                r_rx_state <= r_rx_state;  // 在一次接收中rx_state保持高电平
            end
        end
    end
//********//

//****波特率计数器****//
    always @(posedge in_sys_clk or negedge in_rst_n) begin
        if (~in_rst_n) begin
            r_baud_rate_cnt <= 'd0;
        end
        else begin
            if (r_rx_state && ~w_rx_done) begin  // 接收状态
                if (w_bit_rev_done) begin
                    r_baud_rate_cnt <= 'd0;
                end
                else begin
                    r_baud_rate_cnt <= r_baud_rate_cnt + 1'd1;
                end
            end
            else begin
                r_baud_rate_cnt <= 'd0;
            end
        end
    end
//********//

//****波特率时钟计数****//
    always @(posedge in_sys_clk or negedge in_rst_n) begin
        if (~in_rst_n) begin
            r_baud_rate_clk_cnt <= 'd0;
        end
        else begin
            if (r_rx_state && ~w_rx_done) begin
                if (w_bit_rev_done) begin  // 计数加一
                    r_baud_rate_clk_cnt <= r_baud_rate_clk_cnt + 1'd1;
                end
                else begin
                    r_baud_rate_clk_cnt <= r_baud_rate_clk_cnt;
                end
            end
            else begin
                r_baud_rate_clk_cnt <= 'd0;
            end
        end
    end
//********//

//****串行数据存入****//
    always @(posedge in_sys_clk or negedge in_rst_n) begin
        if (~in_rst_n) begin
            r_rx_data_frame_byte <= 'd0;
        end
        else begin
            if (r_rx_state) begin
                if (w_sample_signal) begin  // 采样并移位
                    r_rx_data_frame_byte <= {r_rx_pin_t0, r_rx_data_frame_byte[BAUD_CLK_CNT_THRESHOLD-1:1]};
                end
                else begin
                    r_rx_data_frame_byte <= r_rx_data_frame_byte;
                end
            end
            else begin
                r_rx_data_frame_byte <= 'd0;
            end
        end
    end
//********//

endmodule

Tx 部分

// UART 发送模块 可选波特率 可选数据位 仅支持一位停止位

module UART_Tx_module #(
    parameter CLK_FREQ_MHZ = 27,
    parameter BAUD_RATE = 9600,
    parameter DATA_WIDTH = 8  // 数据位宽
) (
    input in_sys_clk,
    input in_rst_n,
    input [DATA_WIDTH-1:0] in_tx_data,  // 准备发送的数据
    input in_tx_data_valid,  // 发送的数据有效

    output out_tx_ready,  // 发送模块已准备好发送数据
    output out_tx_pin  // 发送的串口数据 tx_data_ready和tx_data_valid都为高时数据被发送
);
    localparam BAUD_CLK_CNT_THRESHOLD = (DATA_WIDTH + 2);  // 波特率时钟计数最大值 数据位+起始位(两位)+停止位(一位)
    localparam BAUD_CNT_THRESHOLD = (CLK_FREQ_MHZ * 1000000 / BAUD_RATE);  // 波特率计数最大值

    wire w_tx_done;  // 发送完成
    wire w_shift_signal;
    wire w_bit_send_done;

    reg [BAUD_CLK_CNT_THRESHOLD-1:0] r_tx_data_frame;  // 准备发送的完整数据帧
    reg r_tx_state;  // 发送状态
    reg [$clog2(BAUD_CNT_THRESHOLD)-1:0] r_baud_rate_cnt;  // 波特率计数器
    reg [$clog2(BAUD_CLK_CNT_THRESHOLD)-1:0] r_baud_rate_clk_cnt;  // 波特率时钟计数
    reg r_tx_pin;

    assign w_tx_done = (r_baud_rate_clk_cnt==BAUD_CLK_CNT_THRESHOLD);
    assign w_shift_signal = (r_baud_rate_cnt=='d1);
    assign w_bit_send_done = (r_baud_rate_cnt==BAUD_CNT_THRESHOLD-1);

    assign out_tx_ready = ~r_tx_state;
    assign out_tx_pin = r_tx_pin;

//****发送数据帧生成****//
    always @(posedge in_sys_clk or negedge in_rst_n) begin
        if (~in_rst_n) begin
            r_tx_data_frame <= 'd0;
        end
        else begin
            if (in_tx_data_valid && out_tx_ready) begin
                r_tx_data_frame <= {1'b1, in_tx_data, 1'b0};
            end
            else begin
                if (w_shift_signal) begin  // 低位发送后移位
                    r_tx_data_frame <= {1'b0, r_tx_data_frame[BAUD_CLK_CNT_THRESHOLD-1:1]};  // 数据帧移位
                end
                else begin
                    r_tx_data_frame <= r_tx_data_frame;
                end
            end
        end
    end
//********//

//****发送状态逻辑****//
    always @(posedge in_sys_clk or negedge in_rst_n) begin
        if (~in_rst_n) begin
            r_tx_state <= 1'b0;
        end
        else begin
            if (in_tx_data_valid && out_tx_ready) begin
                r_tx_state <= 1'b1;
            end
            else if (w_tx_done) begin
                r_tx_state <= 1'b0;
            end
            else begin
                r_tx_state <= r_tx_state;
            end
        end
    end
//********//

//****波特率计数器****//
    always @(posedge in_sys_clk or negedge in_rst_n) begin
        if (~in_rst_n) begin
            r_baud_rate_cnt <= 'd0;
        end
        else begin
            if (r_tx_state && ~w_tx_done) begin  // 接收状态
                if (w_bit_send_done) begin
                    r_baud_rate_cnt <= 'd0;
                end
                else begin
                    r_baud_rate_cnt <= r_baud_rate_cnt + 1'd1;
                end
            end
            else begin
                r_baud_rate_cnt <= 'd0;
            end
        end
    end
//********//

//****波特率时钟计数****//
    always @(posedge in_sys_clk or negedge in_rst_n) begin
        if (~in_rst_n) begin
            r_baud_rate_clk_cnt <= 'd0;
        end
        else begin
            if (r_tx_state && ~w_tx_done) begin
                if (w_bit_send_done) begin
                    r_baud_rate_clk_cnt <= r_baud_rate_clk_cnt + 1'd1;
                end
                else begin
                    r_baud_rate_clk_cnt <= r_baud_rate_clk_cnt;
                end
            end
            else begin
                r_baud_rate_clk_cnt <= 'd0;
            end
        end
    end
//********//

//****发送数据部分****//
    always @(posedge in_sys_clk or negedge in_rst_n) begin
        if (~in_rst_n) begin
            r_tx_pin <= 1'b1;
        end
        else begin
            if (r_tx_state && ~w_tx_done) begin
                if (w_shift_signal) begin  // 数据帧移位前送出
                    r_tx_pin <= r_tx_data_frame[0];  // 数据低位送出
                end
                else begin
                    r_tx_pin <= r_tx_pin;
                end
            end
            else begin
                r_tx_pin <= 1'b1;
            end
        end
    end
//********//

endmodule

Module-sim 测试代码

// UART 模型测试

`timescale 1ns/1ns

module UART_module_tb;

    wire [7:0] rx_data_tb;
    wire uart_tb;
    wire rx_data_valid_tb;
    wire tx_data_ready_tb;

    reg sys_clk_tb;
    reg rst_n_tb;
    reg tx_data_valid_tb;
    reg [7:0] tx_data_tb;

    wire true;

    assign true = ((tx_data_tb == rx_data_tb) && rx_data_valid_tb);

    initial begin
        sys_clk_tb = 1'b0;
        rst_n_tb = 1'b0;
        tx_data_valid_tb = 1'b0;
        tx_data_tb = 8'h0;
        # 5;
        rst_n_tb = 1'b1;
        tx_data_valid_tb = 1'b1;
        tx_data_tb = 8'h5d;
    end

    initial begin
        # 10000;
        $finish;
    end

    always #1 begin
        sys_clk_tb = ~sys_clk_tb;
    end

    always begin
        @(posedge tx_data_ready_tb) tx_data_valid_tb = 1'b1; tx_data_tb = ($random) % 256;
        @(negedge tx_data_ready_tb) tx_data_valid_tb = 1'b0; tx_data_tb = tx_data_tb;
    end

    UART_Rx_module #(
        .CLK_FREQ_MHZ(500),
        .BAUD_RATE(1152000)
    ) u_uart_rx (
        .in_sys_clk(sys_clk_tb),
        .in_rst_n(rst_n_tb),
        .in_rx_ready(1'b1),  // 准备好接受串口数据
        .in_rx_pin(uart_tb),

        .out_rx_data(rx_data_tb),  // 接收到的串口数据 rx_data_ready和rx_data_valid均为高时送出
        .out_rx_data_valid(rx_data_valid_tb)  // 接收到的串口数据有效
    );

    UART_Tx_module #(
        .CLK_FREQ_MHZ(500),
        .BAUD_RATE(1152000)
    ) u_uart_tx (
        .in_sys_clk(sys_clk_tb),
        .in_rst_n(rst_n_tb),
        .in_tx_data(tx_data_tb),  // 准备发送的数据
        .in_tx_data_valid(tx_data_valid_tb),  // 发送的数据有效

        .out_tx_ready(tx_data_ready_tb),  // 发送模块已准备好发送数据
        .out_tx_pin(uart_tb)  // 发送的串口数据 tx_data_ready和tx_data_valid都为高时数据被发送
    );

endmodule

Gowin FPGA 烧录测试代码

// 收到数据后发送回去

module top_module #(
    parameter CLK_FREQ_MHZ = 27,
    parameter BAUD_RATE = 115200
) (
    input in_sys_clk,
    input in_rst_n,
    input in_rx_pin,  // UART 接收

    output out_tx_pin  // UART 发送
);
    wire w_rx_data_valid;  // 接收数据有效
    wire [7:0] w_rx_data;  // 接收到的数据
    wire w_tx_ready;

    reg r_rx_data_valid;
    reg [7:0] r_rx_data;
    reg r_tx_ready_t0, r_tx_ready_t1;

    always @(posedge in_sys_clk or negedge in_rst_n) begin
        if (~in_rst_n) begin
            r_tx_ready_t0 <= 1'b0;
            r_tx_ready_t1 <= 1'b0;
        end
        else begin
            r_tx_ready_t0 <= w_tx_ready;
            r_tx_ready_t1 <= r_tx_ready_t0;
        end
    end

    always @(posedge in_sys_clk or negedge in_rst_n) begin
        if (~in_rst_n) begin
            r_rx_data_valid <= 1'b0;
            r_rx_data <= 8'd0;
        end
        else begin
            if (w_rx_data_valid) begin
                r_rx_data_valid <= 1'b1;
            end
            else if (~r_tx_ready_t0 && r_tx_ready_t1) begin
                r_rx_data_valid <= 1'b0;
            end
            else begin
                r_rx_data_valid <= r_rx_data_valid;
            end
            

            if (w_rx_data_valid) begin
                r_rx_data <= w_rx_data;
            end
            else begin
                r_rx_data <= r_rx_data;
            end
        end
    end

    UART_Rx_module #(
        .CLK_FREQ_MHZ(CLK_FREQ_MHZ),
        .BAUD_RATE(BAUD_RATE)
    ) u_uart_rx (
        .in_sys_clk(in_sys_clk),
        .in_rst_n(in_rst_n),
        .in_rx_ready(1'b1),  // 准备好接受串口数据
        .in_rx_pin(in_rx_pin),

        .out_rx_data(w_rx_data),  // 接收到的串口数据 rx_data_ready和rx_data_valid均为高时送出
        .out_rx_data_valid(w_rx_data_valid)  // 接收到的串口数据有效
    );

    UART_Tx_module #(
        .CLK_FREQ_MHZ(CLK_FREQ_MHZ),
        .BAUD_RATE(BAUD_RATE)
    ) u_uart_tx (
        .in_sys_clk(in_sys_clk),
        .in_rst_n(in_rst_n),
        .in_tx_data(r_rx_data),  // 准备发送的数据
        .in_tx_data_valid(r_rx_data_valid),  // 发送的数据有效

        .out_tx_ready(w_tx_ready),  // 发送模块已准备好发送数据
        .out_tx_pin(out_tx_pin)  // 发送的串口数据 tx_data_ready和tx_data_valid都为高时数据被发送
    );
    
endmodule