Verilog实现UART接收时采用打拍检测RX脚下降沿导致连续接收字节出错

  代码如下:
  发送模块uart_wr:

module UART_WR(
    input clk,
    input rst_n,

    input wr_en,
    input[7:0] data_wr,

    output reg uart_tx,
    output reg rdy
);

parameter CLK_FREQ = 32'd50_000_000; // 输入时钟为50MHz
parameter BAUDRATE = 32'd115200; // 波特率为115200
localparam TICK = CLK_FREQ / BAUDRATE;

localparam STAT_IDLE = 4'b0001;  // 空闲
localparam STAT_START = 4'b0010; // 起始位
localparam STAT_DATA = 4'b0100;  // 数据位
localparam STAT_STOP = 4'b1000;  // 停止位

reg[23:0] cnt;

reg[2:0] bit_cnt;

reg[7:0] data_r;

reg[3:0] cstate, nstate;

reg flg;

wire add_cnt, end_cnt;

wire add_bit_cnt, end_bit_cnt;

wire idle2start, start2data, data2stop, stop2idle; // 状态转换条件

always @(posedge clk or negedge rst_n) begin
    if(!rst_n)
        cstate <= STAT_IDLE;
    else
        cstate <= nstate;
end

always @(*) begin
    case(cstate)
        STAT_IDLE:
            if(idle2start)
                nstate <= STAT_START;
            else
                nstate <= cstate;
        STAT_START:
            if(start2data)
                nstate <= STAT_DATA;
            else
                nstate <= cstate;
        STAT_DATA:
            if(data2stop)
                nstate <= STAT_STOP;
            else
                nstate <= cstate;
        STAT_STOP:
            if(stop2idle)
                nstate <= STAT_IDLE;
            else
                nstate <= cstate;
        default:
            nstate <= STAT_IDLE;
    endcase
end

assign idle2start = (cstate == STAT_IDLE) && (wr_en == 1'b1);
assign start2data = (cstate == STAT_START) && end_cnt;
assign data2stop = (cstate == STAT_DATA) && end_bit_cnt;
assign stop2idle = (cstate == STAT_STOP) && end_cnt;

always @(posedge clk or negedge rst_n) begin
    if(!rst_n)
        cnt <= 24'd0;
    else if(add_cnt)
        if(end_cnt)
            cnt <= 24'd0;
        else
            cnt <= cnt + 24'd1;
    else
        cnt <= cnt;
end

assign add_cnt = (cstate != STAT_IDLE);
assign end_cnt = add_cnt && (cnt == TICK - 32'd1);

always @(posedge clk or negedge rst_n) begin
    if(!rst_n)
        bit_cnt <= 3'd0;
    else if(add_bit_cnt)
        if(end_bit_cnt)
            bit_cnt <= 3'd0;
        else
            bit_cnt <= bit_cnt + 3'd1;
    else
        bit_cnt <= bit_cnt;
end

assign add_bit_cnt = (cstate == STAT_DATA) && end_cnt;
assign end_bit_cnt = add_bit_cnt && (bit_cnt == 4'd8 - 4'd1);

// 对输入数据进行缓存
always @(posedge clk or negedge rst_n) begin
    if(!rst_n)
        data_r <= 8'd0;
    else if(idle2start)
        data_r <= data_wr;
    else
        data_r <= data_r;
end

// 生成uart_tx信号
always @(posedge clk or negedge rst_n) begin
    if(!rst_n)
        uart_tx <= 1'b1;
    else if(cstate == STAT_START)
        uart_tx <= 1'b0;
    else if(cstate == STAT_DATA)
        uart_tx <= data_r[bit_cnt];
    else
        uart_tx <= 1'b1;
end

// 生成rdy信号
always @(posedge clk or negedge rst_n) begin
    if(!rst_n)
        flg <= 1'b0;
    else if(wr_en)
        flg <= 1'b1;
    else if(stop2idle)
        flg <= 1'b0;
    else
        flg <= flg;
end

always @(*) begin
    if(wr_en || flg) // 当wr_en为高时,rdy同时拉低;当flg为高时,rdy也为低
        rdy = 1'b0;
    else
        rdy = 1'b1;
end

endmodule

  顶层模块uart,做一个回环测试,即电脑发什么,FPGA就往回发什么:

module UART(
    input sys_clk_50m,
    input sys_rst_n,
    
    input uart_rx,
    output uart_tx,
);

wire rd_vld, rdy;
wire[7:0] data;

UART_WR #(
    .CLK_FREQ(32'd50_000_000),
    .BAUDRATE(32'd115200)
) u_uart_wr(
    .clk(sys_clk_50m),
    .rst_n(sys_rst_n),

    .wr_en(rd_vld),
    .data_wr(data),

    .uart_tx(uart_tx),
    .rdy(rdy)
);

UART_RD #(
    .CLK_FREQ(32'd50_000_000),
    .BAUDRATE(32'd115200)
) u_uart_rd(
    .clk(sys_clk_50m),
    .rst_n(sys_rst_n),
    .uart_rx(uart_rx),

    .rd_vld(rd_vld),
    .data_rd(data)
);

endmodule

  打三拍采样RX脚的接收模块uart_rx:

module UART_RD(
    input clk,
    input rst_n,
    
    input uart_rx,

    output reg rd_vld,
    output reg[7:0] data_rd
);

parameter CLK_FREQ = 32'd50_000_000; // 输入时钟为50MHz
parameter BAUDRATE = 32'd115200; // 波特率为115200
localparam TICK = CLK_FREQ / BAUDRATE;

localparam STAT_IDLE = 4'b0001;  // 空闲
localparam STAT_START = 4'b0010; // 起始位
localparam STAT_DATA = 4'b0100;  // 数据位
localparam STAT_STOP = 4'b1000;  // 停止位

reg[23:0] cnt;

reg[2:0] bit_cnt;

reg[3:0] cstate, nstate;

reg[2:0] uart_rx_ff;

wire add_cnt, end_cnt;

wire add_bit_cnt, end_bit_cnt;

wire idle2start, start2data, data2stop, stop2idle; // 状态转换条件

wire uart_rx_fall; 

always @(posedge clk or negedge rst_n) begin
    if(!rst_n)
        cstate <= STAT_IDLE;
    else
        cstate <= nstate;
end

always @(*) begin
    case(cstate)
        STAT_IDLE:
            if(idle2start)
                nstate <= STAT_START;
            else
                nstate <= cstate;
        STAT_START:
            if(start2data)
                nstate <= STAT_DATA;
            else
                nstate <= cstate;
        STAT_DATA:
            if(data2stop)
                nstate <= STAT_STOP;
            else
                nstate <= cstate;
        STAT_STOP:
            if(stop2idle)
                nstate <= STAT_IDLE;
            else
                nstate <= cstate;
        default:
            nstate <= STAT_IDLE;
    endcase
end

assign idle2start = (cstate == STAT_IDLE) && uart_rx_fall;
//assign idle2start = (cstate == STAT_IDLE) && (uart_rx == 1'b0);
assign start2data = (cstate == STAT_START) && end_cnt;
assign data2stop = (cstate == STAT_DATA) && end_bit_cnt;
assign stop2idle = (cstate == STAT_STOP) && end_cnt;

// 打三拍检测uart_rx的下降沿
always @(posedge clk or negedge rst_n) begin
    if(!rst_n)
        uart_rx_ff <= 3'b1;
    else
        uart_rx_ff <= {uart_rx_ff[1], uart_rx_ff[0], uart_rx};
end

assign uart_rx_fall = (uart_rx_ff[2] == 1'b1) && (uart_rx_ff[1] == 1'b0); // 读取RX脚电平时用的是uart_rx_ff[2]

// 对每个比特的时间长度进行计数
always @(posedge clk or negedge rst_n) begin
    if(!rst_n)
        cnt <= 24'd0;
    else if(add_cnt)
        if(end_cnt)
            cnt <= 24'd0;
        else
            cnt <= cnt + 24'd1;
    else
        cnt <= cnt;
end

assign add_cnt = (cstate != STAT_IDLE);
assign end_cnt = add_cnt && (cnt == TICK - 32'd1);

// 对数据位的比特个数进行计数
always @(posedge clk or negedge rst_n) begin
    if(!rst_n)
        bit_cnt <= 3'd0;
    else if(add_bit_cnt)
        if(end_bit_cnt)
            bit_cnt <= 3'd0;
        else
            bit_cnt <= bit_cnt + 3'd1;
    else
        bit_cnt <= bit_cnt;
end

assign add_bit_cnt = (cstate == STAT_DATA) && end_cnt;
assign end_bit_cnt = add_bit_cnt && (bit_cnt == 4'd8 - 4'd1);

// 读取数据
always @(posedge clk or negedge rst_n) begin
    if(!rst_n)
        data_rd <= 8'd0;
    else if(cstate == STAT_DATA && add_cnt && cnt == (TICK / 32'd2) - 32'd1)
        data_rd[bit_cnt] <= uart_rx_ff[2];
    else
        data_rd <= data_rd;
end

// 生成数据有效信号
always @(posedge clk or negedge rst_n) begin
    if(!rst_n)
        rd_vld <= 1'b0;
    else if(stop2idle)
        rd_vld <= 1'b1;
    else
        rd_vld <= 1'b0;
end

endmodule

  下面是不打拍直接检测电平的接收模块uart_rx,可以正常运行:

module UART_RD(
    input clk,
    input rst_n,
    
    input uart_rx,

    output reg rd_vld,
    output reg[7:0] data_rd
);

parameter CLK_FREQ = 32'd50_000_000; // 输入时钟为50MHz
parameter BAUDRATE = 32'd115200; // 波特率为115200
localparam TICK = CLK_FREQ / BAUDRATE;

localparam STAT_IDLE = 4'b0001;  // 空闲
localparam STAT_START = 4'b0010; // 起始位
localparam STAT_DATA = 4'b0100;  // 数据位
localparam STAT_STOP = 4'b1000;  // 停止位

reg[23:0] cnt;

reg[2:0] bit_cnt;

reg[3:0] cstate, nstate;

reg[2:0] uart_rx_ff;

wire add_cnt, end_cnt;

wire add_bit_cnt, end_bit_cnt;

wire idle2start, start2data, data2stop, stop2idle; // 状态转换条件

wire uart_rx_fall; 

always @(posedge clk or negedge rst_n) begin
    if(!rst_n)
        cstate <= STAT_IDLE;
    else
        cstate <= nstate;
end

always @(*) begin
    case(cstate)
        STAT_IDLE:
            if(idle2start)
                nstate <= STAT_START;
            else
                nstate <= cstate;
        STAT_START:
            if(start2data)
                nstate <= STAT_DATA;
            else
                nstate <= cstate;
        STAT_DATA:
            if(data2stop)
                nstate <= STAT_STOP;
            else
                nstate <= cstate;
        STAT_STOP:
            if(stop2idle)
                nstate <= STAT_IDLE;
            else
                nstate <= cstate;
        default:
            nstate <= STAT_IDLE;
    endcase
end

assign idle2start = (cstate == STAT_IDLE) && (uart_rx == 1'b0);
assign start2data = (cstate == STAT_START) && end_cnt;
assign data2stop = (cstate == STAT_DATA) && end_bit_cnt;
assign stop2idle = (cstate == STAT_STOP) && end_cnt;

// 对每个比特的时间长度进行计数
always @(posedge clk or negedge rst_n) begin
    if(!rst_n)
        cnt <= 24'd0;
    else if(add_cnt)
        if(end_cnt)
            cnt <= 24'd0;
        else
            cnt <= cnt + 24'd1;
    else
        cnt <= cnt;
end

assign add_cnt = (cstate != STAT_IDLE);
assign end_cnt = add_cnt && (cnt == TICK - 32'd1);

// 对数据位的比特个数进行计数
always @(posedge clk or negedge rst_n) begin
    if(!rst_n)
        bit_cnt <= 3'd0;
    else if(add_bit_cnt)
        if(end_bit_cnt)
            bit_cnt <= 3'd0;
        else
            bit_cnt <= bit_cnt + 3'd1;
    else
        bit_cnt <= bit_cnt;
end

assign add_bit_cnt = (cstate == STAT_DATA) && end_cnt;
assign end_bit_cnt = add_bit_cnt && (bit_cnt == 4'd8 - 4'd1);

// 读取数据
always @(posedge clk or negedge rst_n) begin
    if(!rst_n)
        data_rd <= 8'd0;
    else if(cstate == STAT_DATA && add_cnt && cnt == (TICK / 32'd2) - 32'd1)
        data_rd[bit_cnt] <= uart_rx;
    else
        data_rd <= data_rd;
end

// 生成数据有效信号
always @(posedge clk or negedge rst_n) begin
    if(!rst_n)
        rd_vld <= 1'b0;
    else if(stop2idle)
        rd_vld <= 1'b1;
    else
        rd_vld <= 1'b0;
end

endmodule
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值