用状态机实现串口多字节数据发送

这次设计一个多字节(8-256位)且波特率可更改(通过修改例化模块的参数)的串口发送模块。

1、状态机的设定

image

  • 状态机的设定有空闲、发送、和数据移位三个状态,其中空闲状态为等待多字节发送的信号;
  • 发送状态为给8位串口发送模块传输待发送的8位数据,同时判断是否发送完数据回到空闲状态;
  • 数据移位状态为等到前面8位字节数据发送完后,将接下来待发送的8位数据移动到数据寄存器的低8位中。若数据在发送中则会进行等待;

2、需要的模块

(1)8位串口发送模块

`timescale 1ns / 1ps
//////////////////////////////////////////////////////////////////////////////////
// Engineer: Lclone
// 
// Create Date: 2022/12/10 00:21:40
// Design Name: uart_byte_tx
// Module Name: uart_byte_tx
// Project Name: uart_byte_tx
// Description: 8位串口发送模块,波特率可通过参数设置。
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
// 
//////////////////////////////////////////////////////////////////////////////////


module uart_byte_tx
    #(
        parameter   TX_BAUD  = 9600,
        parameter   CLK_FQC  = 50_000_000,
        parameter   BAUD_CNT = CLK_FQC/TX_BAUD)
    (
        input       [7:0]   Data,
        input               Send_en,
        input               Clk,
        input               Rst_n,
        output  reg        Uart_Tx,
        output  reg        Tx_done
    );
    


    
    reg   [15:0]   baud_cnt;            
    reg   [ 3:0]   bit_cnt;
    reg            Send_en_r;
    reg            Send_en_rr;
    reg            Tx_flag;
    
    always @(posedge Clk) begin   
        Send_en_r <= Send_en;
        Send_en_rr <= Send_en_r;
    end
       
    always @(posedge Clk or negedge Rst_n) begin
        if(Rst_n == 0)
            Tx_flag <= 0;
        else if(~Send_en_rr & Send_en_r)
            Tx_flag <= 1'b1;
        else if(bit_cnt == 10 - 1 & baud_cnt == BAUD_CNT - 1)
            Tx_flag <= 1'b0;            
    end   
        
    always @(posedge Clk or negedge Rst_n) begin
        if(Rst_n == 0)
            baud_cnt <= 0;
        else if(baud_cnt == BAUD_CNT - 1)
            baud_cnt <= 0;
        else if(Tx_flag)
            baud_cnt <= baud_cnt + 1'b1;
    end
    
    always @(posedge Clk or negedge Rst_n) begin
        if(Rst_n == 0)    
            bit_cnt <= 0;
        else if(bit_cnt == 10 - 1 & baud_cnt == BAUD_CNT - 1)
            bit_cnt <= 0;
        else if(baud_cnt == BAUD_CNT - 1)
            bit_cnt <= bit_cnt + 1'b1;
    end   
    
    always @(posedge Clk or negedge Rst_n) begin
        if(Rst_n == 0)
            Uart_Tx <= 1'b1;
        else if(Tx_flag == 0)
            Uart_Tx <= 1'b1;
        else case(bit_cnt)
            0: Uart_Tx <= 1'b0;
            1: Uart_Tx <= Data[0];
            2: Uart_Tx <= Data[1];
            3: Uart_Tx <= Data[2];
            4: Uart_Tx <= Data[3];
            5: Uart_Tx <= Data[4];
            6: Uart_Tx <= Data[5];
            7: Uart_Tx <= Data[6];
            8: Uart_Tx <= Data[7];
            9: Uart_Tx <= 1'b1;
            default: Uart_Tx <= 1'b1;
         endcase
     end
    
     always @(posedge Clk or negedge Rst_n) begin
        if(Rst_n == 0)
            Tx_done <= 1'b0;
        else if(bit_cnt == 9 & baud_cnt == BAUD_CNT - 1)
            Tx_done <= 1'b1;
        else
            Tx_done <= 1'b0;            
     end
endmodule

3、设计的模块代码

`timescale 1ns / 1ps

module uart_bytes_tx_3
    #(
        parameter DATA_WIDTH = 40,
        parameter ROUNDS = DATA_WIDTH / 8
    )
    (
        input                   Clk,
        input                   Rst_n,
        input  [DATA_WIDTH-1:0] Bytes_data,
        input                   Send_bytes_en,
        output reg              Tx_bytes_done,
        output wire             Uart_Tx
    );

    function integer clog2;
        input integer value;
        integer i;
        begin
            value = value - 1;
            for (i = 0; value > 0; i = i + 1)
                value = value >> 1;
            clog2 = i;
        end
    endfunction

    localparam S_IDLE = 3'd0;
    localparam S_SEND = 3'd1;
    localparam S_WAIT = 3'd2;

    localparam ROUNDS_WIDTH = clog2(ROUNDS + 1);

    reg [2:0] state;

    reg [DATA_WIDTH-1:0] bytes_data_reg;
    reg [7:0]            data_reg;
    reg                  send_en;
    wire                 tx_done;

    reg [ROUNDS_WIDTH-1:0] rounds;

    uart_byte_tx
    #(
        .TX_BAUD (9600),
        .CLK_FQC (50_000_000)
    )
    uart_byte_tx_inst
    (
        .Data    (data_reg),
        .Send_en (send_en),
        .Clk     (Clk),
        .Rst_n   (Rst_n),
        .Uart_Tx (Uart_Tx),
        .Tx_done (tx_done)
    );

    always @(posedge Clk or negedge Rst_n) begin
        if (!Rst_n) begin
            state          <= S_IDLE;
            bytes_data_reg <= {DATA_WIDTH{1'b0}};
            data_reg       <= 8'd0;
            send_en        <= 1'b0;
            Tx_bytes_done  <= 1'b0;
            rounds         <= {ROUNDS_WIDTH{1'b0}};
        end
        else begin
            send_en       <= 1'b0;
            Tx_bytes_done <= 1'b0;

            case (state)

                S_IDLE: begin
                    rounds <= {ROUNDS_WIDTH{1'b0}};

                    if (Send_bytes_en) begin
                        bytes_data_reg <= Bytes_data;
                        state <= S_SEND;
                    end
                    else begin
                        state <= S_IDLE;
                    end
                end

                S_SEND: begin
                    if (rounds == ROUNDS) begin
                        Tx_bytes_done <= 1'b1;
                        state <= S_IDLE;
                    end
                    else begin
                        data_reg <= bytes_data_reg[7:0];
                        send_en  <= 1'b1;
                        state    <= S_WAIT;
                    end
                end

                S_WAIT: begin
                    if (tx_done) begin
                        bytes_data_reg <= bytes_data_reg >> 8;
                        rounds <= rounds + 1'b1;
                        state <= S_SEND;
                    end
                    else begin
                        state <= S_WAIT;
                    end
                end

                default: begin
                    state <= S_IDLE;
                end

            endcase
        end
    end

endmodule
endmodule

4、仿真验证

(1)仿真激励

`timescale 1ns / 1ps

module uart_bytes_tx_tb();

    reg Clk;
    reg Rst_n;
    reg Send_bytes_en;
    reg [63:0]Bytes_data;
    wire Tx_bytes_done;
    wire Uart_Tx;
    
    uart_bytes_tx_3   
  # (   .DATA_WIDTH             (64))
    uart_bytes_tx_inst
    (
        .Clk                    (Clk),
        .Rst_n                  (Rst_n),
        .Bytes_data             (Bytes_data),
        .Send_bytes_en          (Send_bytes_en),
        .Tx_bytes_done          (Tx_bytes_done),
        .Uart_Tx                (Uart_Tx)
    );
    defparam uart_bytes_tx_inst.uart_byte_tx_inst.BAUD_CNT = 10;
    
    initial Clk <= 1'b1;
    always #10 Clk <= ~Clk;
    
    initial begin
        Rst_n <= 0;
        Bytes_data <= 0;
        Send_bytes_en <= 0;
        #200
        Rst_n <= 1'b1;
        #20
        Bytes_data <= 64'h0123456789abcdef;
        Send_bytes_en <= 1'b1;
        #20
        Send_bytes_en <= 0;
        #20000
        Bytes_data <= 64'hfedcba9876543210;
        Send_bytes_en <= 1'b1;
        #20
        Send_bytes_en <= 0;
        @(posedge Tx_bytes_done)
        #100
        $stop;
    end

endmodule

(2)仿真波形

第一轮发送:
image
两轮发送:
image

posted @ 2022-12-13 22:50  Lclone  阅读(777)  评论(2)    收藏  举报