Abstract: Verilog UART 设计
目录:
更新记录
当前为 v4 版本
解决了之前版本连续收发时丢数据的问题
模块代码
Rx 部分
// UART 接收模块 可选波特率 可选数据位 仅支持一位停止位
module UART_Rx_module #(
parameter CLK_FREQ_MHZ = 27,
parameter BAUD_RATE = 9600,
parameter DATA_WIDTH = 8 // 数据位宽
) (
input in_sys_clk,
input in_rst_n,
input in_rx_ready, // 准备好接受串口数据
input in_rx_pin,
output [DATA_WIDTH-1:0] out_rx_data, // 接收到的串口数据 rx_data_ready和rx_data_valid均为高时送出
output out_rx_data_valid // 接收到的串口数据有效
);
localparam BAUD_CNT_THRESHOLD = CLK_FREQ_MHZ * 1000000 / BAUD_RATE; // 波特率计数最大值
localparam BAUD_CLK_CNT_THRESHOLD = (DATA_WIDTH + 2); // 波特率时钟计数最大值 数据位+起始位+结束位
wire w_rx_negedge;
wire w_rx_done; // 接收接收标识
wire w_sample_signal;
wire w_bit_rev_done;
reg r_rx_pin_t0, r_rx_pin_t1;
reg r_rx_state; // 接收状态
reg [$clog2(BAUD_CNT_THRESHOLD)-1:0] r_baud_rate_cnt; // 波特率计数器
reg [$clog2(BAUD_CLK_CNT_THRESHOLD)-1:0] r_baud_rate_clk_cnt; // 波特率时钟计数
reg [BAUD_CLK_CNT_THRESHOLD-1:0] r_rx_data_frame_byte; // 完整数据帧
assign w_rx_negedge = (r_rx_pin_t1 && ~r_rx_pin_t0);
assign w_rx_done = (r_baud_rate_clk_cnt==BAUD_CLK_CNT_THRESHOLD-1) && (r_baud_rate_cnt==BAUD_CNT_THRESHOLD/2+2); // 不判断完整的结束位
assign w_sample_signal = (r_baud_rate_cnt==BAUD_CNT_THRESHOLD/2);
assign w_bit_rev_done = (r_baud_rate_cnt==BAUD_CNT_THRESHOLD-1);
assign out_rx_data = (out_rx_data_valid && in_rx_ready)? r_rx_data_frame_byte[BAUD_CLK_CNT_THRESHOLD-2:1] : 'd0;
assign out_rx_data_valid = w_rx_done;
//****输入数据同步****//
always @(posedge in_sys_clk or negedge in_rst_n) begin
if (~in_rst_n) begin
r_rx_pin_t0 <= 1'b0;
r_rx_pin_t1 <= 1'b0;
end
else begin
r_rx_pin_t0 <= in_rx_pin;
r_rx_pin_t1 <= r_rx_pin_t0;
end
end
//********//
//****接收状态逻辑****//
always @(posedge in_sys_clk or negedge in_rst_n) begin
if (~in_rst_n) begin
r_rx_state <= 1'b0;
end
else begin
if (w_rx_negedge && ~r_rx_state) begin // 下降沿到来
r_rx_state <= 1'b1;
end
else if (w_rx_done) begin // 接收完成
r_rx_state <= 1'b0;
end
else if (r_baud_rate_clk_cnt=='d1 && r_rx_data_frame_byte[0]==1'b1) begin // 起始位采样后高电平居多
r_rx_state <= 1'b0;
end
else begin
r_rx_state <= r_rx_state; // 在一次接收中rx_state保持高电平
end
end
end
//********//
//****波特率计数器****//
always @(posedge in_sys_clk or negedge in_rst_n) begin
if (~in_rst_n) begin
r_baud_rate_cnt <= 'd0;
end
else begin
if (r_rx_state && ~w_rx_done) begin // 接收状态
if (w_bit_rev_done) begin
r_baud_rate_cnt <= 'd0;
end
else begin
r_baud_rate_cnt <= r_baud_rate_cnt + 1'd1;
end
end
else begin
r_baud_rate_cnt <= 'd0;
end
end
end
//********//
//****波特率时钟计数****//
always @(posedge in_sys_clk or negedge in_rst_n) begin
if (~in_rst_n) begin
r_baud_rate_clk_cnt <= 'd0;
end
else begin
if (r_rx_state && ~w_rx_done) begin
if (w_bit_rev_done) begin // 计数加一
r_baud_rate_clk_cnt <= r_baud_rate_clk_cnt + 1'd1;
end
else begin
r_baud_rate_clk_cnt <= r_baud_rate_clk_cnt;
end
end
else begin
r_baud_rate_clk_cnt <= 'd0;
end
end
end
//********//
//****串行数据存入****//
always @(posedge in_sys_clk or negedge in_rst_n) begin
if (~in_rst_n) begin
r_rx_data_frame_byte <= 'd0;
end
else begin
if (r_rx_state) begin
if (w_sample_signal) begin // 采样并移位
r_rx_data_frame_byte <= {r_rx_pin_t0, r_rx_data_frame_byte[BAUD_CLK_CNT_THRESHOLD-1:1]};
end
else begin
r_rx_data_frame_byte <= r_rx_data_frame_byte;
end
end
else begin
r_rx_data_frame_byte <= 'd0;
end
end
end
//********//
endmodule
Tx 部分
// UART 发送模块 可选波特率 可选数据位 仅支持一位停止位
module UART_Tx_module #(
parameter CLK_FREQ_MHZ = 27,
parameter BAUD_RATE = 9600,
parameter DATA_WIDTH = 8 // 数据位宽
) (
input in_sys_clk,
input in_rst_n,
input [DATA_WIDTH-1:0] in_tx_data, // 准备发送的数据
input in_tx_data_valid, // 发送的数据有效
output out_tx_ready, // 发送模块已准备好发送数据
output out_tx_pin // 发送的串口数据 tx_data_ready和tx_data_valid都为高时数据被发送
);
localparam BAUD_CLK_CNT_THRESHOLD = (DATA_WIDTH + 2); // 波特率时钟计数最大值 数据位+起始位(两位)+停止位(一位)
localparam BAUD_CNT_THRESHOLD = (CLK_FREQ_MHZ * 1000000 / BAUD_RATE); // 波特率计数最大值
wire w_tx_done; // 发送完成
wire w_shift_signal;
wire w_bit_send_done;
reg [BAUD_CLK_CNT_THRESHOLD-1:0] r_tx_data_frame; // 准备发送的完整数据帧
reg r_tx_state; // 发送状态
reg [$clog2(BAUD_CNT_THRESHOLD)-1:0] r_baud_rate_cnt; // 波特率计数器
reg [$clog2(BAUD_CLK_CNT_THRESHOLD)-1:0] r_baud_rate_clk_cnt; // 波特率时钟计数
reg r_tx_pin;
assign w_tx_done = (r_baud_rate_clk_cnt==BAUD_CLK_CNT_THRESHOLD);
assign w_shift_signal = (r_baud_rate_cnt=='d1);
assign w_bit_send_done = (r_baud_rate_cnt==BAUD_CNT_THRESHOLD-1);
assign out_tx_ready = ~r_tx_state;
assign out_tx_pin = r_tx_pin;
//****发送数据帧生成****//
always @(posedge in_sys_clk or negedge in_rst_n) begin
if (~in_rst_n) begin
r_tx_data_frame <= 'd0;
end
else begin
if (in_tx_data_valid && out_tx_ready) begin
r_tx_data_frame <= {1'b1, in_tx_data, 1'b0};
end
else begin
if (w_shift_signal) begin // 低位发送后移位
r_tx_data_frame <= {1'b0, r_tx_data_frame[BAUD_CLK_CNT_THRESHOLD-1:1]}; // 数据帧移位
end
else begin
r_tx_data_frame <= r_tx_data_frame;
end
end
end
end
//********//
//****发送状态逻辑****//
always @(posedge in_sys_clk or negedge in_rst_n) begin
if (~in_rst_n) begin
r_tx_state <= 1'b0;
end
else begin
if (in_tx_data_valid && out_tx_ready) begin
r_tx_state <= 1'b1;
end
else if (w_tx_done) begin
r_tx_state <= 1'b0;
end
else begin
r_tx_state <= r_tx_state;
end
end
end
//********//
//****波特率计数器****//
always @(posedge in_sys_clk or negedge in_rst_n) begin
if (~in_rst_n) begin
r_baud_rate_cnt <= 'd0;
end
else begin
if (r_tx_state && ~w_tx_done) begin // 接收状态
if (w_bit_send_done) begin
r_baud_rate_cnt <= 'd0;
end
else begin
r_baud_rate_cnt <= r_baud_rate_cnt + 1'd1;
end
end
else begin
r_baud_rate_cnt <= 'd0;
end
end
end
//********//
//****波特率时钟计数****//
always @(posedge in_sys_clk or negedge in_rst_n) begin
if (~in_rst_n) begin
r_baud_rate_clk_cnt <= 'd0;
end
else begin
if (r_tx_state && ~w_tx_done) begin
if (w_bit_send_done) begin
r_baud_rate_clk_cnt <= r_baud_rate_clk_cnt + 1'd1;
end
else begin
r_baud_rate_clk_cnt <= r_baud_rate_clk_cnt;
end
end
else begin
r_baud_rate_clk_cnt <= 'd0;
end
end
end
//********//
//****发送数据部分****//
always @(posedge in_sys_clk or negedge in_rst_n) begin
if (~in_rst_n) begin
r_tx_pin <= 1'b1;
end
else begin
if (r_tx_state && ~w_tx_done) begin
if (w_shift_signal) begin // 数据帧移位前送出
r_tx_pin <= r_tx_data_frame[0]; // 数据低位送出
end
else begin
r_tx_pin <= r_tx_pin;
end
end
else begin
r_tx_pin <= 1'b1;
end
end
end
//********//
endmodule
Module-sim 测试代码
// UART 模型测试
`timescale 1ns/1ns
module UART_module_tb;
wire [7:0] rx_data_tb;
wire uart_tb;
wire rx_data_valid_tb;
wire tx_data_ready_tb;
reg sys_clk_tb;
reg rst_n_tb;
reg tx_data_valid_tb;
reg [7:0] tx_data_tb;
wire true;
assign true = ((tx_data_tb == rx_data_tb) && rx_data_valid_tb);
initial begin
sys_clk_tb = 1'b0;
rst_n_tb = 1'b0;
tx_data_valid_tb = 1'b0;
tx_data_tb = 8'h0;
# 5;
rst_n_tb = 1'b1;
tx_data_valid_tb = 1'b1;
tx_data_tb = 8'h5d;
end
initial begin
# 10000;
$finish;
end
always #1 begin
sys_clk_tb = ~sys_clk_tb;
end
always begin
@(posedge tx_data_ready_tb) tx_data_valid_tb = 1'b1; tx_data_tb = ($random) % 256;
@(negedge tx_data_ready_tb) tx_data_valid_tb = 1'b0; tx_data_tb = tx_data_tb;
end
UART_Rx_module #(
.CLK_FREQ_MHZ(500),
.BAUD_RATE(1152000)
) u_uart_rx (
.in_sys_clk(sys_clk_tb),
.in_rst_n(rst_n_tb),
.in_rx_ready(1'b1), // 准备好接受串口数据
.in_rx_pin(uart_tb),
.out_rx_data(rx_data_tb), // 接收到的串口数据 rx_data_ready和rx_data_valid均为高时送出
.out_rx_data_valid(rx_data_valid_tb) // 接收到的串口数据有效
);
UART_Tx_module #(
.CLK_FREQ_MHZ(500),
.BAUD_RATE(1152000)
) u_uart_tx (
.in_sys_clk(sys_clk_tb),
.in_rst_n(rst_n_tb),
.in_tx_data(tx_data_tb), // 准备发送的数据
.in_tx_data_valid(tx_data_valid_tb), // 发送的数据有效
.out_tx_ready(tx_data_ready_tb), // 发送模块已准备好发送数据
.out_tx_pin(uart_tb) // 发送的串口数据 tx_data_ready和tx_data_valid都为高时数据被发送
);
endmodule
Gowin FPGA 烧录测试代码
// 收到数据后发送回去
module top_module #(
parameter CLK_FREQ_MHZ = 27,
parameter BAUD_RATE = 115200
) (
input in_sys_clk,
input in_rst_n,
input in_rx_pin, // UART 接收
output out_tx_pin // UART 发送
);
wire w_rx_data_valid; // 接收数据有效
wire [7:0] w_rx_data; // 接收到的数据
wire w_tx_ready;
reg r_rx_data_valid;
reg [7:0] r_rx_data;
reg r_tx_ready_t0, r_tx_ready_t1;
always @(posedge in_sys_clk or negedge in_rst_n) begin
if (~in_rst_n) begin
r_tx_ready_t0 <= 1'b0;
r_tx_ready_t1 <= 1'b0;
end
else begin
r_tx_ready_t0 <= w_tx_ready;
r_tx_ready_t1 <= r_tx_ready_t0;
end
end
always @(posedge in_sys_clk or negedge in_rst_n) begin
if (~in_rst_n) begin
r_rx_data_valid <= 1'b0;
r_rx_data <= 8'd0;
end
else begin
if (w_rx_data_valid) begin
r_rx_data_valid <= 1'b1;
end
else if (~r_tx_ready_t0 && r_tx_ready_t1) begin
r_rx_data_valid <= 1'b0;
end
else begin
r_rx_data_valid <= r_rx_data_valid;
end
if (w_rx_data_valid) begin
r_rx_data <= w_rx_data;
end
else begin
r_rx_data <= r_rx_data;
end
end
end
UART_Rx_module #(
.CLK_FREQ_MHZ(CLK_FREQ_MHZ),
.BAUD_RATE(BAUD_RATE)
) u_uart_rx (
.in_sys_clk(in_sys_clk),
.in_rst_n(in_rst_n),
.in_rx_ready(1'b1), // 准备好接受串口数据
.in_rx_pin(in_rx_pin),
.out_rx_data(w_rx_data), // 接收到的串口数据 rx_data_ready和rx_data_valid均为高时送出
.out_rx_data_valid(w_rx_data_valid) // 接收到的串口数据有效
);
UART_Tx_module #(
.CLK_FREQ_MHZ(CLK_FREQ_MHZ),
.BAUD_RATE(BAUD_RATE)
) u_uart_tx (
.in_sys_clk(in_sys_clk),
.in_rst_n(in_rst_n),
.in_tx_data(r_rx_data), // 准备发送的数据
.in_tx_data_valid(r_rx_data_valid), // 发送的数据有效
.out_tx_ready(w_tx_ready), // 发送模块已准备好发送数据
.out_tx_pin(out_tx_pin) // 发送的串口数据 tx_data_ready和tx_data_valid都为高时数据被发送
);
endmodule