FPGA 上的 UART 通信未按指定波特率按预期工作

问题描述 投票:0回答:1

我的 FPGA 有一个 UART 通信系统,我将 FPGA 的波特率设置为 9600。不幸的是,当我尝试发送数据测试数据时,我以 9600 的波特率从我的笔记本电脑发送到我的 FPGA,结果会周期性地失真 9600 波特率下的结果,而如果我以 4800 波特率发送数据,我会得到同样正确的结果 4800 波特率下的结果。这对我来说很奇怪,因为我希望它应该以一半的波特率正常工作。我对 HDL 和 FPGA 实现相当陌生,我找不到代码中的问题所在,我感到绝望,所以我将不胜感激任何帮助

我的接收代码如下


`include "baudrates.v"

module uart_rx(
input               clk,
output reg [7:0]    data,
output reg          rcv,
input               rx,
input               rstn
);

parameter [15:0] BAUDRATE =  `B9600; //1250
reg [15:0]  rx_period_cnt;
reg [3:0]   rx_bit_cnt; // 0: IDLE, 1: Start, 2~9: bit0~7, A:Stop
reg     rx_bit_tick;
reg     rx_sample_tick;

reg [1:0]   rxd_lat;
reg [7:0]   rxd_shift;

reg     rx_valid_tg;
reg [1:0]   rx_valid_tg_clk;

always @(posedge clk)
begin
    if(rstn == 1'b0)
    rxd_lat <= 2'b0;
    else if(rx_bit_cnt==4'hA) //During Stop State, force register to bit 1 until IDLE 
    rxd_lat <= {rxd_lat[0], 1'b1};
    else
    rxd_lat <= {rxd_lat[0], rx};
end

always @(posedge clk)
begin
    if(rstn == 1'b0)
    rxd_shift <= 8'b0;
    else if(rx_sample_tick)
    rxd_shift <= {rxd_lat[0], rxd_shift[7:1]};
end

always @(posedge clk)
begin
    if(rstn == 1'b0)
    rx_valid_tg <= 1'b0;
    else if(rx_sample_tick & (rx_bit_cnt == 4'hA))
    rx_valid_tg <= !rx_valid_tg;
end

always @(posedge clk)
begin
    if(rstn == 1'b0)
    data <= 8'b0;
    else if(rx_sample_tick & (rx_bit_cnt == 4'hA))
    data <= rxd_shift;
end

always @(posedge clk)
begin
    if(rstn == 1'b0)
    rx_bit_cnt <= 4'b0;
    else if((rx_bit_cnt == 4'b0) && (rxd_lat == 2'b10))
    rx_bit_cnt <= 4'd1;
    else if((rx_bit_cnt == 4'hA) && rx_bit_tick)
    rx_bit_cnt <= 4'd0;
    else if((rx_bit_cnt != 4'b0) && rx_bit_tick)
    rx_bit_cnt <= rx_bit_cnt + 4'd1;
end

always @(posedge clk)
begin
    if(rstn == 1'b0)
    rx_period_cnt <= 16'b0;
    else if(rx_bit_cnt == 4'b0)
    rx_period_cnt <= 16'b0;
    else if(rx_period_cnt == 16'b0)
    rx_period_cnt <= BAUDRATE;
    else
    rx_period_cnt <= rx_period_cnt - 16'd1;
end

always @(posedge clk)
begin
    if(rstn == 1'b0)
    rx_bit_tick <= 1'b0;
    else if(rx_period_cnt == 16'd1)
    rx_bit_tick <= 1'b1;
    else
    rx_bit_tick <= 1'b0;
end

always @(posedge clk)
begin
    if(rstn == 1'b0)
    rx_sample_tick <= 1'b0;
    else if(rx_period_cnt == {1'b0, BAUDRATE[15:1]})
    rx_sample_tick <= 1'b1;
    else
    rx_sample_tick <= 1'b0;
end

always @(posedge clk)
begin
    if(rstn == 1'b0)
    rx_valid_tg_clk <= 2'b0;
    else 
    rx_valid_tg_clk <= {rx_valid_tg_clk[0], rx_valid_tg};
end

always @(posedge clk)
begin
    if(rstn == 1'b0)
    rcv <= 1'b0;
    else 
    rcv <= (rx_valid_tg_clk[0] != rx_valid_tg_clk[1]);
end



endmodule


我的发射器代码如下

`include "baudrates.v"

module uart_tx(
         input wire clk,        
         input wire rstn,       
         input wire start,      
         input wire [7:0] data, 
         output reg tx,         
         output wire ready      
);

parameter [15:0] BAUDRATE =  `B9600; //1250

reg [15:0]  tx_period_cnt;
reg [3:0]   tx_bit_cnt; // 0: IDLE, 1: Start, 2~9: bit0~7, A:Stop
reg     tx_bit_tick;


always @(posedge clk)
begin
    if(rstn == 1'b0)
    tx_bit_cnt <= 4'b0;
    else if((tx_bit_cnt == 4'b0) && (start == 1'b1))
    tx_bit_cnt <= 4'd1;
    else if((tx_bit_cnt == 4'hA) && tx_bit_tick)
    tx_bit_cnt <= 4'd0;
    else if((tx_bit_cnt != 4'b0) && tx_bit_tick)
    tx_bit_cnt <= tx_bit_cnt + 4'd1;
end

always @(posedge clk)
begin
    if(rstn == 1'b0)
    tx_period_cnt <= 16'b0;
    else if(tx_bit_cnt == 4'b0)
    tx_period_cnt <= 16'b0;
    else if(tx_period_cnt == 16'b0)
    tx_period_cnt <= BAUDRATE;
    else
    tx_period_cnt <= tx_period_cnt - 16'd1;
end

always @(posedge clk)
begin
    if(rstn == 1'b0)
    tx_bit_tick <= 1'b0;
    else if(tx_period_cnt == 16'd1)
    tx_bit_tick <= 1'b1;
    else
    tx_bit_tick <= 1'b0;
end

assign ready = (tx_bit_cnt == 4'h0);

always @(posedge clk)
begin
    if(rstn == 1'b0)
    tx <= 1'b0;
    else case(tx_bit_cnt)
    4'd1: // start
        tx <= 1'b0;
    4'd2:
        tx <= data[0];
    4'd3:
        tx <= data[1];
    4'd4:
        tx <= data[2];
    4'd5:
        tx <= data[3];
    4'd6:
        tx <= data[4];
    4'd7:
        tx <= data[5];
    4'd8:
        tx <= data[6];
    4'd9:
        tx <= data[7];
    default: // stop & idle
        tx <= 1'b1;
    endcase
end

endmodule

我已经用测试台对其进行了测试,在发送大约 50 个字节后,结果似乎发生了变化。我还发现添加第二个停止位似乎可以在模拟级别解决问题,但在硬件级别问题仍然存在。

verilog fpga uart hdl
1个回答
0
投票

您正在尝试以固定时间点运行接收器进行采样。

您应该在每个 UART 传输批次的初始边沿(

rx
上的边沿)执行时钟同步,并使用它来确定稳定的采样点(例如,在距初始边沿 50% 周期偏移处进行采样)。

目前,您很可能尝试在切换点几乎精确地进行采样(假设您都在同一时钟上运行并使用相同的复位信号)。如果它们不隶属于同一个时钟,您仍然会遇到这个问题,因为时钟漂移最终会让您陷入糟糕的状态。

© www.soinside.com 2019 - 2024. All rights reserved.