我的 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 个字节后,结果似乎发生了变化。我还发现添加第二个停止位似乎可以在模拟级别解决问题,但在硬件级别问题仍然存在。
您正在尝试以固定时间点运行接收器进行采样。
您应该在每个 UART 传输批次的初始边沿(
rx
上的边沿)执行时钟同步,并使用它来确定稳定的采样点(例如,在距初始边沿 50% 周期偏移处进行采样)。
目前,您很可能尝试在切换点几乎精确地进行采样(假设您都在同一时钟上运行并使用相同的复位信号)。如果它们不隶属于同一个时钟,您仍然会遇到这个问题,因为时钟漂移最终会让您陷入糟糕的状态。