fpga收发,但是回显缺失数据。波特率越低,缺失越多

问题描述

最近学习fpga,我使用verilog,基于小脚丫MAX10编写了一个串口收发模块。在串口助手发什么它会立刻回显什么。
仿真已通过,但是在上板的时候发现有字符缺失的现象,而且波特率越低,现象越严重。


截图

img

波特率57600,可以接收79%的数据。


img

波特率115200,接收率89%。


img

波特率调到串口助手的最大值,可以接收98%的数据。

而波特率调到2400时,即使发送单个字符也无法接收。

个人思考

可以看到波特率的影响很大,所以我查看了源码中跟波特率有关的部分,发现只有将单比特数据持续时间转化为时钟数这一部分是相关的。

parameter BSP_CNT = TIME_1S / B_SPEED ;
reg[19:0] cnt;
wire add_cnt = flag;
wire end_cnt = add_cnt && cnt == BSP_CNT - 1;
always @(posedge clk or negedge rst_n) begin
    if(!rst_n)begin
        cnt <= 20'd0;
    end
    else if(add_cnt)begin
        if(end_cnt) begin
            cnt <= 20'd0;
        end
        else begin
            cnt <= cnt + 1'b1;
        end
    end
    else begin
            cnt <= cnt;
    end
end

因为要用系统时钟计时,而根据公式,波特率越大,每次计时越短。因此我自己觉得可能是计数器的误差,导致不能够准确表示每一比特数据在串口数据序列中的持续时间。等计数到BSP_CNT/2的时候读取或发送某一比特时,实际数据早已发生变化。但是我不确定是不是这个原因。

源码

我的源码在下面,希望大家指教 =(

//顶层模块
module my_uart  #(parameter TIME_1S = 12000000 , B_SPEED = 460800)(
    input   clk ,
    input   rst_n   ,
    input   dat_in  ,
    output   dout
); 
    wire [7:0]   dat_out ;
    wire dout_vld    ;
     
     //接收模块
    uart_rx#(.TIME_1S(TIME_1S) , .B_SPEED(B_SPEED)) u_uart_rx(
        .clk (clk),
        .rst_n (rst_n),
        .dat_in (dat_in),
        .dat_out (dat_out),
        .dout_vld (dout_vld)
    );
     
     //回发模块
    uart_tx#(.TIME_1S(TIME_1S) , .B_SPEED(B_SPEED)) u_uart_tx(
        .clk (clk),
        .rst_n (rst_n),
        .din (dat_out),
        .din_vld (dout_vld),
        .dout (dout)
    );
endmodule 

```c
//接收模块
module uart_rx #(parameter TIME_1S = 12000000 , B_SPEED = 115200) (
    input   clk ,
    input   rst_n   ,
    input   dat_in  ,
    output  reg [7:0]   dat_out ,
    output  reg dout_vld
); 

//打拍,监测下降沿,并减小亚稳态
reg     dat_in_2;
reg     dat_in_3;
wire nedge;
wire podge;
always @(posedge clk or negedge rst_n) begin
    if (!rst_n) begin
        dat_in_2 <= 1'b0;
        dat_in_3 <= 1'b0;
    end
    else begin
        dat_in_2 <= dat_in;
        dat_in_3 <= dat_in_2;
    end
end
assign nedge = ~dat_in_2 & dat_in_3;
assign podge = dat_in_2 & ~dat_in_3;//下降沿



wire add_bits;//数据比特计数条件
wire end_add_bits;//是否接收了全部数据


reg flag;//通讯标志,下降沿置1,接收完所有数据变成0
reg[3:0] rx_bits;
always @(posedge clk or negedge rst_n) begin
    if (!rst_n) begin
        flag <= 1'b0;
    end
    else begin
        if(nedge)begin
            flag <= 1'b1;
        end
        if(end_add_bits) begin
            flag <= 1'b0;
        end
    end
end


parameter BSP_CNT = TIME_1S / B_SPEED ;//一比特数据持续的时钟周期数
reg[19:0] cnt;//计数每个比特的时钟周期数
wire add_cnt = flag;
wire end_cnt = add_cnt && cnt == BSP_CNT - 1;
always @(posedge clk or negedge rst_n) begin
    if(!rst_n)begin
        cnt <= 20'd0;
    end
    else if(add_cnt)begin
        if(end_cnt) begin
            cnt <= 20'd0;
        end
        else begin
            cnt <= cnt + 1'b1;
        end
    end
    else begin
            cnt <= cnt;
    end
end

//rx_bits表示接受的数据的下标,8表示数据位全部接收到
assign add_bits = end_cnt;
assign end_add_bits = add_bits && rx_bits == 8;
always @(posedge clk or negedge rst_n) begin
    if(!rst_n)begin
        rx_bits <= 4'd0;
    end
    else if(add_bits) begin
        rx_bits <= rx_bits + 1'b1;
        if(end_add_bits)begin
            rx_bits <= 4'd0;
        end
    end
    else begin
        rx_bits <= rx_bits;
    end
end

reg [8:0] data;
always @(posedge clk or negedge rst_n) begin
    if (!rst_n) begin
        data <= 9'b000000000;
    end
    else begin
        if(flag && cnt == BSP_CNT/2)begin//在每个比特持续的中间时刻取值保存
            data[rx_bits] <= dat_in_3;
        end
        else begin
            data <= data;
        end
    end
end

//输出与数据有效标志
always @(posedge clk or negedge rst_n) begin
    if(!rst_n)begin
        dat_out <= 8'b0;
    end
    else if(rx_bits == 8)begin
        dat_out <= data[8:1];
    end
    else begin
        dat_out <= 8'b0;
    end
end

always @(posedge clk or negedge rst_n) begin
    if(!rst_n)begin
        dout_vld <= 1'b0;
    end
    else if(rx_bits == 8)begin
        dout_vld <= 1'b1;
    end
    else begin
        dout_vld <= 1'b0;
    end
end
endmodule  
//回发模块
module uart_tx  #(parameter TIME_1S = 12000000 , B_SPEED = 115200)(
    input   clk ,
    input   rst_n   ,
    input   [7:0]   din ,//上位机发的串行数据
    input   din_vld ,
    output  reg dout
); 

//打牌,监测有效标志上升沿
reg din_vld_2;
reg din_vld_3;
always @(posedge clk or negedge rst_n) begin
    if(!rst_n)begin
        din_vld_2 <= 1'b0;
    end
    else begin
        din_vld_2 <= din_vld;
        din_vld_3 <= din_vld_2;
    end
end

wire nedge;
wire podge;
assign  nedge = ~din_vld_2 && din_vld_3;
assign  podge = din_vld_2 && ~din_vld_3;



parameter BSP_CNT = TIME_1S / B_SPEED;

reg flag;

reg [19:0] cnt_bsp;
wire add_cnt_bsp;
wire end_cnt_bsp;
assign add_cnt_bsp = flag;
assign end_cnt_bsp = add_cnt_bsp && cnt_bsp == BSP_CNT - 1;

reg [3:0]  cnt_bit;
wire add_cnt_bit;
wire end_cnt_bit;
assign add_cnt_bit = end_cnt_bsp;
assign end_cnt_bit = add_cnt_bit && cnt_bit == 10 - 1;

reg [9:0] data;
always @(posedge clk or negedge rst_n) begin
    if(!rst_n)begin
        data <= 10'd0;
    end
    else if (podge)begin
        data <= {1'b1,din[7:0],1'b0};
    end
    else if(end_cnt_bit)begin
        data <= 10'd0;
    end
    else begin
        data <= data;
    end
end

always @(posedge clk or negedge rst_n) begin
    if(!rst_n)begin
        flag <= 1'b0;
    end
    else if (podge)begin
        flag <= 1'b1;
    end
    else if(end_cnt_bit)begin
        flag <= 1'b0;
    end
    else begin
        flag <= flag;
    end
end

//每个比特的时钟计数
always @(posedge clk or negedge rst_n) begin
    if(!rst_n)begin
        cnt_bsp <= 20'd0;
    end
    else if (add_cnt_bsp)begin
        if (end_cnt_bsp)begin
            cnt_bsp <= 20'd0;
        end
        else begin
            cnt_bsp <= cnt_bsp + 1'b1;
        end
    end
    else begin
        cnt_bsp <= cnt_bsp;
    end
end

//当前发送的数据下标
always @(posedge clk or negedge rst_n) begin
    if(!rst_n)begin
        cnt_bit <= 4'd0;
    end
    else if (add_cnt_bit)begin
        if (end_cnt_bit)begin
            cnt_bit <= 4'd0;
        end
        else begin
            cnt_bit <= cnt_bit + 1'b1;
        end
    end
    else begin
        cnt_bit <= cnt_bit;
    end
end

//把数据串行发送出去
always @(posedge clk or negedge rst_n) begin
    if(!rst_n)begin
        dout <= 1'b1;
    end
    else if (flag && cnt_bsp == BSP_CNT/2)begin
        dout <= data[cnt_bit];
    end
    else begin
        dout <= dout;
    end
end
endmodule  

将顶层模块的//顶层模块,_B_SPEED=460800修改为115200,并将串口调试助手固定以115200的波特率发送数据。
出现数据丢失,可能是波特率不匹配的问题。

module my_uart  #(parameter TIME_1S = 12000000 , _B_SPEED = 460800****_)(
    input   clk ,
    input   rst_n   ,
    input   dat_in  ,
    output   dout
);