十六、uart(3)带FIFO缓存(普通fifo模式)

1、uart_tx.v

/*
*
*@Author: X-Z
*@Date:2023-02-09 12:44:54
*@Function:串口发送模块,将接收模块接收到的1帧10bit并行数据转换为10bit的串行数据并通过发送引脚tx输出送到pc机的接收引脚
*/
/************************这里采用的是普通fifo模式数据在rd_req之后才会有效
如果直接把rd_req信号赋值给tx_data_vld会出现数据有效信号在数据出现之前出现高电平,
从而采集到不定态,给din_vld同步打拍没有用,因为ready信号会同步变换
所以采用两个标志信号的方式处理时序,在这里传进来的数据是延后din_vld一个时钟周期的
所以将din_vld同步打拍延迟一下,产生reg0去控制数据的发送,此时是同步的
而ready信号的控制信号则直接由din_vld同步扩展产生,这样会使ready信号在数据刚开始变成将要发送的有效数据时就变得无效即fifo
不能读数据,而数据在ready信号拉低的下一个周期后才才开始发送,从而避免了读取到错误数据的情况
验证发现只要同步就可以实现,即延迟一个时钟周期
**********************************/

module  uart_tx(
    input    wire               clk        ,
    input    wire               rst_n      ,
    input    wire    [7:0]      din        ,//发送端送给接收端的有效的8bit并行数据
    input    wire               din_vld    ,//输入数据有效标志信号

    output   reg                uart_tx     , //发送引脚  
    output   wire               ready         //指示可以数据让它发送
);

    // wire [7:0] din;
    // //测试
    // assign din = 8'h5a;


    //波特率为115200时传输1位所需的时钟周期
    parameter       CNT_MAX_BPS = 9'd433;//(50_000_000/115200)=434
    reg  [8:0]      cnt_bps    ;
    wire            add_cnt_bps;
    wire            end_cnt_bps;

    reg [3:0]       cnt_bit    ;//计数0-9构成一帧数据
    wire            add_cnt_bit;
    wire            end_cnt_bit;

    //对din_vld信号时长延长
    reg             din_vld_flag;
    reg             din_vld_flag1;
    reg  [9:0]      data ;
    reg             reg0;

    always@(posedge clk or negedge rst_n)begin
        if(!rst_n)begin
            reg0 <= 1'b0;
        end
        else begin
            reg0 <= din_vld;
        end
    end

    always@(posedge clk or negedge rst_n)begin
        if(!rst_n)begin
            din_vld_flag <= 1'b0;
        end
        else if(reg0)begin
            din_vld_flag <= 1'b1;
        end
        else if(end_cnt_bit && end_cnt_bps)begin//一帧数据发送完后拉低
            din_vld_flag <= 1'b0; 
        end
    end

    always@(posedge clk or negedge rst_n)begin
        if(!rst_n)begin
            din_vld_flag1 <= 1'b0;
        end
        else if(end_cnt_bit && end_cnt_bps)begin//一帧数据发送完后拉低
            din_vld_flag1 <= 1'b0; 
        end
        else if(din_vld)begin
            din_vld_flag1 <= 1'b1;
        end
    end

    //1bit计数器
    always@(posedge clk or negedge rst_n)begin
        if(!rst_n)begin
            cnt_bps <= 1'b0;
        end
        else if(add_cnt_bps)begin
            if(end_cnt_bps)begin
                cnt_bps <= 1'b0;
            end
            else begin
                cnt_bps <= cnt_bps + 1'b1;
            end
        end
    end 

    assign add_cnt_bps = din_vld_flag;
    assign end_cnt_bps = add_cnt_bps && cnt_bps == CNT_MAX_BPS;

    //1帧数据计数器10bit
    always@(posedge clk or negedge rst_n)begin
        if(!rst_n)begin
            cnt_bit <= 1'b0;
        end
        else if(add_cnt_bit)begin
            if(end_cnt_bit)begin
                cnt_bit <= 1'b0;
            end
            else begin
                cnt_bit <= cnt_bit + 1'b1;
            end
        end
    end

    assign add_cnt_bit = end_cnt_bps;
    assign end_cnt_bit = add_cnt_bit && cnt_bit == 4'd9;
    
    always@(posedge clk or negedge rst_n)begin//一帧数据发送完后拉低
        if(!rst_n)
            data <= 10'd0;
        else if(reg0)
            data <= {1'b1,din[7:0],1'b0};
    end 

    //根据通信协议发送数据帧的每一位
    always@(posedge clk or negedge rst_n)begin
        if(!rst_n)begin
            uart_tx <= 1'b1;//初始化空闲状态高电平
        end
        else if(din_vld_flag && cnt_bps == 1)
            uart_tx <= data[cnt_bit];
        else begin
            uart_tx <= uart_tx;//其他情况为空闲状态
        end
    end

    assign ready = ~din_vld_flag1;//当tx发送数据的时候ready无效不能从fifo中读数据,只要发送完成才可以读取下一个数据
    
endmodule

2、uart_rx.v

module uart_rx (
    input     wire          clk         ,
    input     wire          rst_n       ,
    input     wire          uart_rx     ,

    output    reg  [7:0]    dout        ,
    output    reg           dout_vld      //数据接收完成标志信号
);

    //波特率为115200时传输1位所需的时钟周期
    parameter CNT_MAX_BPS = 9'd433;//(50_000_000/115200)=434
    reg  [8:0] cnt_bps    ;
    wire       add_cnt_bps;
    wire       end_cnt_bps;

    reg [3:0]  cnt_bit    ;//计数0-9构成一帧数据
    wire       add_cnt_bit;
    wire       end_cnt_bit;

    //同步打拍信号定义
    reg        rx_reg0;   
    reg        rx_reg1;

    //开始计数使能信号
    reg        start_en;

    wire       bit_flag;//指示提取到哪一bit了

    reg  [9:0] rx_data;//寄存并行数据

    //同步打拍rx信号
    always@(posedge clk or negedge rst_n)begin
        if(!rst_n)begin
            rx_reg0 <= 1'b1;
            rx_reg1 <= 1'b1;
        end
        else begin
            rx_reg0 <= uart_rx;//同步(跨时钟域处理信号会产生亚稳态)
            rx_reg1 <= rx_reg0;//打第一拍
        end
    end

    //nedge下降沿
    assign nedge = rx_reg1 && (~rx_reg0);

    //开始计数使能信号
    always@(posedge clk or negedge rst_n)begin
        if(!rst_n)begin
            start_en <= 1'b0;//复位初始化
        end
        else if(end_cnt_bit)
            start_en <= 1'b0;
        else if(nedge)begin
            start_en <= 1'b1;
        end
    end

    //1bit计数器
    always@(posedge clk or negedge rst_n)begin
        if(!rst_n)begin
            cnt_bps <= 1'b0;
        end
        else if(add_cnt_bps)begin
            if(end_cnt_bps)begin
                cnt_bps <= 1'b0;
            end
            else begin
                cnt_bps <= cnt_bps + 1'b1;
            end
        end
    end 

    assign add_cnt_bps = start_en;
    assign end_cnt_bps = add_cnt_bps && cnt_bps == CNT_MAX_BPS ;

    assign bit_flag = (cnt_bps == (CNT_MAX_BPS >> 1)) ? 1'b1:1'b0;//计数到一半给高电平

    //1帧数据计数器10bit
    always@(posedge clk or negedge rst_n)begin
        if(!rst_n)begin
            cnt_bit <= 1'b0;
        end
        else if(add_cnt_bit)begin
            if(end_cnt_bit)begin
                cnt_bit <= 1'b0;
            end
            else begin
                cnt_bit <= cnt_bit + 1'b1;
            end
        end
    end

    assign add_cnt_bit = end_cnt_bps;
    assign end_cnt_bit = add_cnt_bit && cnt_bit == 4'd9 ;

    //串并转换
    always@(posedge clk or negedge rst_n)begin
        if(!rst_n)
            rx_data <= 10'd0;
        else if(end_cnt_bit && end_cnt_bps)
            rx_data <= 10'd0;
        else if(bit_flag && start_en)
            rx_data <= {rx_reg1,rx_data[9:1]}; 
        else 
            rx_data <= rx_data;
    end

    //输出赋值
    always@(posedge clk or negedge rst_n)begin
        if(!rst_n)begin
            dout <= 8'd0;//复位初始化
        end
        else if(end_cnt_bit && end_cnt_bps)begin
            dout <= rx_data[8:1];
        end
    end

    //输出赋值
    always@(posedge clk or negedge rst_n)begin
        if(!rst_n)begin
            dout_vld <= 1'd0;//复位初始化
        end
        else if(end_cnt_bit && end_cnt_bps)begin
            dout_vld <= 1'b1;
        end
        else 
            dout_vld <= 1'b0;
    end

endmodule

3、control.v

module control(
    input       wire            clk        ,
    input       wire            rst_n      ,
    input       wire  [7:0]     rx_data    ,
    input       wire            rx_data_vld,
    input       wire            ready      ,//准备好开始fifo读信号控制rd_req

    output      reg    [7:0]    tx_data    ,
    output      reg             tx_data_vld
);

    reg             rd_req            ;
    wire            wr_req            ;
    wire            empty             ;
    wire            full              ;
    wire    [2:0]   usedw             ;
    wire    [7:0]   q                 ;


    reg             tx_flag           ;

    fifo_s  u_fifo_s (
        .aclr   ( ~rst_n    ),
        .clock  ( clk       ),
        .data   ( rx_data   ),
        .rdreq  ( rd_req    ),
        .wrreq  ( wr_req    ),

        .empty  ( empty     ),
        .full   ( full      ),
        .q      ( q         ),
        .usedw  ( usedw     )
    );

    always@(*)begin
        if(!rst_n)
            tx_data <= 8'd0;
        else 
            tx_data <= q;
    end

    //只要fifo中数据大于4个就将fifo清空
    always@(posedge clk or negedge rst_n)begin
        if(!rst_n)
            tx_flag <= 1'b0;
        else if(usedw >= 3'd4) 
            tx_flag <= 1'b1;
        else if(empty)
            tx_flag <= 1'b0;//读空fifo后拉低
    end

    //写请求wr_req
    assign wr_req = ~full && rx_data_vld;//数据有效开始写

    always@(*)begin
        if(!rst_n)
            rd_req = 1'b0;
        else if(tx_flag && ready)
            rd_req = 1'b1;
        else 
            rd_req = 1'b0;
    end

    always@(*)begin
        if(!rst_n)
            tx_data_vld = 1'b0;
        else if(tx_flag)
            tx_data_vld = rd_req; 
        else 
            tx_data_vld = 1'b0;
    end
    
endmodule

4、uart_top.v

/*
*
*@Author: X-Z
*@Date:2023-02-09 11:22:56
*@Function:串口通信顶层的设计
*/

module uart_top(
    input           clk         ,
    input           rst_n       ,
    input           uart_rx     ,//发送引脚

    output          uart_tx      //接收引脚
);

    //中间信号定义
    wire    [7:0]       rx_data,tx_data        ;//8bit的数据
    wire                rx_data_vld,tx_data_vld;
    wire                ready   ;
   // reg                 reg0,reg1;

    //例化接收模块uart_rx
    uart_rx u_uart_rx(
        .clk          (clk         ),
        .rst_n        (rst_n       ),
        .uart_rx      (uart_rx     ),

        .dout         (rx_data     ),
        .dout_vld     (rx_data_vld )   //10bit数据接收完成标志信号
    );

    //控制模块
    control u_control(
        .  clk        (clk        ),
        .  rst_n      (rst_n      ),
        .  rx_data    (rx_data    ),
        .  rx_data_vld(rx_data_vld),
        .  ready      (ready      ),//准备好开始fifo读信号控制rd_req

        .  tx_data    (tx_data    ),
        .  tx_data_vld(tx_data_vld)
    );

    // always@(posedge clk or negedge clk)begin
    //     if(!rst_n)begin
    //         reg0 <= 1'b0;
    //         reg1 <= 1'b0;
    //     end
    //     else begin
    //         reg0 <= tx_data_vld;
    //         reg1 <= reg0;
    //     end
    // end

    //发送模块例化uart_tx
    uart_tx u_uart_tx(
        .clk           (clk        ),
        .rst_n         (rst_n      ),
        .din           (tx_data    ),
        .din_vld       (tx_data_vld),//发送端输入数据有效标志信号

        .uart_tx       (uart_tx    ),
        .ready         (ready      )                 
    );


endmodule

5、rx_tb.v

`timescale 1ns/1ps
module rx_tb();

parameter   CYCLE = 20;
//defparam u_uart_rx.CNT_MAX_BPS = 9'd25;

reg             clk     ;
reg             rst_n   ;
reg             uart_rx ;

wire  [7:0]     dout    ;
wire            dout_vld;

initial begin
    clk    = 1'b1;
    rst_n <= 1'b0;
    uart_rx = 1'b1;
    #(15)
    rst_n <= 1'b1;
end

always #(CYCLE/2) clk = ~clk;

initial begin//1_0000_0001_0---1-----1_0000_0010_0------2
    #(100*CYCLE)
    uart_rx = 1'b0;
    #(434*CYCLE)
    uart_rx = 1'b1;
    #(434*CYCLE)
    uart_rx = 1'b0;
    #(434*CYCLE)
    uart_rx = 1'b0;
    #(434*CYCLE)
    uart_rx = 1'b0;
    #(434*CYCLE)
    uart_rx = 1'b0;
    #(434*CYCLE)
    uart_rx = 1'b0;
    #(434*CYCLE)
    uart_rx = 1'b0;
    #(434*CYCLE)
    uart_rx = 1'b0;
    #(434*CYCLE)
    uart_rx = 1'b1;
    #(434*CYCLE)
    #(800*CYCLE)

    //接收2
    uart_rx = 1'b0;
    #(434*CYCLE)
    uart_rx = 1'b0;
    #(434*CYCLE)
    uart_rx = 1'b1;
    #(434*CYCLE)
    uart_rx = 1'b0;
    #(434*CYCLE)
    uart_rx = 1'b0;
    #(434*CYCLE)
    uart_rx = 1'b0;
    #(434*CYCLE)
    uart_rx = 1'b0;
    #(434*CYCLE)
    uart_rx = 1'b0;
    #(434*CYCLE)
    uart_rx = 1'b0;
    #(434*CYCLE)
    uart_rx = 1'b1;
    #(434*CYCLE)
    #(800*CYCLE)
    $stop;

end

//例化接收模块
uart_rx    u_uart_rx(
    .clk        (clk      ),
    .rst_n      (rst_n    ),
    .uart_rx    (uart_rx  ),

    .dout_vld   (dout_vld ),//输入数据有效标志信号
    .dout       (dout     )  //发送引脚  
);

endmodule

6、uart_rx_tb.v

`timescale 1ns/1ps
module uart_rx_tb();

parameter   CYCLE = 20;
//defparam u_uart_rx.CNT_MAX_BPS = 9'd25;

reg             clk     ;
reg             rst_n   ;
reg             uart_rx ;

wire  [7:0]     dout    ;
wire            dout_vld;

initial begin
    clk    = 1'b1;
    rst_n <= 1'b0;
    #(15)
    rst_n <= 1'b1;
end

always #(CYCLE/2) clk = ~clk;

//将1个8bit的数据按数据帧的格式一位一位的发送
task uart_rx_bit(
    input   [7:0]  data
);

integer i ;

for(i=0;i<10;i=i+1)begin
    case(i)
        0: uart_rx = 1'b0   ;//起始位
        1: uart_rx = data[0];
        2: uart_rx = data[1];
        3: uart_rx = data[2];
        4: uart_rx = data[3];
        5: uart_rx = data[4];
        6: uart_rx = data[5];
        7: uart_rx = data[6];
        8: uart_rx = data[7];
        9: uart_rx = 1'b1   ;//停止位
        default :uart_rx = 1'b1;//其他空闲状态
    endcase
#(CYCLE*434);
end
endtask

initial begin
    #(200*CYCLE)
    uart_rx_bit(8'd1);
    uart_rx = 1'b1;
    #50
    uart_rx_bit(8'd2);
    uart_rx = 1'b1;
    #50
    uart_rx_bit(8'd6);
    uart_rx = 1'b1;
    #(100*CYCLE)
    $stop;
end

//例化发送模块
uart_rx    u_uart_rx(
    .clk        (clk      ),
    .rst_n      (rst_n    ),
    .uart_rx    (uart_rx  ),

    .dout_vld   (dout_vld ),//输入数据有效标志信号
    .dout       (dout     )  //发送引脚  
);

endmodule

7、uart_top_tb.v

`timescale 1ns/1ps
module uart_top_tb();

parameter   CYCLE = 20;
//defparam u_uart_tx.CNT_MAX_BPS = 9'd25;

reg             clk     ;
reg             rst_n   ;
reg    [7:0]    din     ;
reg             din_vld ;

wire            uart_tx ;
wire            tb_uart_tx ;

initial begin
    clk    = 1'b1;
    rst_n <= 1'b0;
    #(15)
    rst_n <= 1'b1;
end

always #(CYCLE/2) clk = ~clk;

initial begin
    din     = 8'd0;
    din_vld = 1'b0;
    #(20*CYCLE)
    //发送8'ha
    din     = 8'hca;
    #CYCLE
    din_vld = 1'b1;
    #CYCLE
    din_vld = 1'b0;
    #(CYCLE*434*11)
    //发送8'h6b
    din     = 8'h6b;
    din_vld = 1'b1;
    #CYCLE
    din_vld = 1'b0;
    #(CYCLE*434*11)

    //发送8'ha
    din     = 8'h22;
    din_vld = 1'b1;
    #CYCLE
    din_vld = 1'b0;
    #(CYCLE*434*11)
    //发送8'h6b
    din     = 8'h55;
    din_vld = 1'b1;
    #CYCLE
    din_vld = 1'b0;
    #(CYCLE*434*11)

    //发送8'h6b
    din     = 8'h65;
    din_vld = 1'b1;
    #CYCLE
    din_vld = 1'b0;

    #(CYCLE*434*11*7)
    $stop;
end

uart_top u_uart_top(
    .clk        (clk        ) ,
    .rst_n      (rst_n      ) ,
    .uart_rx    (uart_tx    ) ,//发送引脚 
    
    .uart_tx    (tb_uart_tx )  //接收引脚
);

//例化发送模块
uart_tx    u_uart_tx(
    .clk        (clk      ),
    .rst_n      (rst_n    ),
    .din        (din      ),//发送端送给接收端的有效的8bit并行数据
    .din_vld    (din_vld  ),//输入数据有效标志信号

    .uart_tx    (uart_tx  )  //发送引脚  
);

endmodule

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值