Abstract: Verilog UART 设计
代码编写参考
模块代码
Rx 部分
// UART 接收模块 可选波特率 仅支持八位数据+一位停止位
module UART_Rx #(
parameter CLK_FREQ_MHZ = 27,
parameter BAUD_RATE = 9600
)(
input clk,
input rst_n,
input rx_data_ready, // 准备好接受串口数据
input rx_pin,
output [7:0] rx_data, // 接收到的串口数据 rx_data_ready和rx_data_valid均为高时送出
output rx_data_valid // 接收到的串口数据有效
);
localparam BAUD_CNT_THRESHOLD = CLK_FREQ_MHZ * 1000000 / BAUD_RATE / 16 - 1; // 波特率计数最大值
localparam BAUD_CLK_CNT_THRESHOLD = (8 + 2) * 16 - 1; // 波特率时钟计数最大值
reg rx_data_r0, rx_data_r1;
reg rx_data_t0, rx_data_t1;
reg rx_state;
reg rx_done;
reg [$clog2(BAUD_CNT_THRESHOLD)-1:0] baud_rate_cnt; // 波特率计数器
reg baud_rate_clk;
reg [$clog2(BAUD_CLK_CNT_THRESHOLD)-1:0] baud_rate_clk_cnt;
reg [2:0] start_bit, stop_bit;
reg [2:0] data_byte [7:0]; // 有效数据
reg [7:0] rx_data_r;
reg rx_data_valid_r;
wire rx_negedge;
integer i;
/****输入数据同步****/
always @(posedge clk or negedge rst_n) begin
if (~rst_n) begin
rx_data_r0 <= 1'b0;
rx_data_r1 <= 1'b0;
end
else begin
rx_data_r0 <= rx_pin;
rx_data_r1 <= rx_data_r0;
end
end
/******************/
/****同步后的数据暂存 边沿检测****/
always @(posedge clk or negedge rst_n) begin
if (~rst_n) begin
rx_data_t0 <= 1'b0;
rx_data_t1 <= 1'b0;
end
else begin
rx_data_t0 <= rx_data_r1;
rx_data_t1 <= rx_data_t0;
end
end
assign rx_negedge = rx_data_t1 && (~rx_data_t0);
/******************/
/****接收状态逻辑****/
always @(posedge clk or negedge rst_n) begin
if (~rst_n) begin
rx_state <= 1'b0;
end
else begin
if (rx_negedge) begin // 下降沿到来
rx_state <= 1'b1;
end
else if (rx_done) begin // 接收完成
rx_state <= 1'b0;
end
else if (baud_rate_clk_cnt=='d12 && start_bit[2]) begin // 起始位采样后高电平居多
rx_state <= 1'b0;
end
else begin
rx_state <= rx_state; // 在一次接收中rx_state保持高电平
end
end
end
/******************/
/****接收结束逻辑****/
always @(posedge clk or negedge rst_n) begin
if (~rst_n) begin
rx_done <= 1'b0;
end
else begin
if (baud_rate_clk_cnt==BAUD_CLK_CNT_THRESHOLD) begin
rx_done <= 1'b1;
end
else begin
rx_done <= 1'b0;
end
end
end
/******************/
/****波特率计数器****/
always @(posedge clk or negedge rst_n) begin
if (~rst_n) begin
baud_rate_cnt <= 'd0;
end
else begin
if (rx_state) begin // 接收状态
if (baud_rate_cnt==BAUD_CNT_THRESHOLD) begin
baud_rate_cnt <= 'd0;
end
else begin
baud_rate_cnt <= baud_rate_cnt + 1'd1;
end
end
else begin
baud_rate_cnt <= 'd0;
end
end
end
/******************/
/****波特率时钟生成****/
always @(posedge clk or negedge rst_n) begin
if (~rst_n) begin
baud_rate_clk <= 1'b0;
end
else begin
if (baud_rate_cnt=='d1) begin
baud_rate_clk <= 1'b1;
end
else begin
baud_rate_clk <= 1'b0;
end
end
end
/******************/
/****波特率时钟计数****/
always @(posedge clk or negedge rst_n) begin
if (~rst_n) begin
baud_rate_clk_cnt <= 'd0;
end
else begin
if (~rx_done) begin
if (baud_rate_clk) begin
baud_rate_clk_cnt <= baud_rate_clk_cnt + 1'd1;
end
else begin
baud_rate_clk_cnt <= baud_rate_clk_cnt;
end
end
else begin
baud_rate_clk_cnt <= 'd0;
end
end
end
/******************/
/****数据采集部分****/
always @(posedge clk or negedge rst_n) begin
if (~rst_n) begin
start_bit <= 3'd0;
stop_bit <= 3'd0;
for (i = 0; i<8; i=i+1) begin
data_byte[i] <= 3'd0;
end
end
else begin
if (baud_rate_clk) begin
case (baud_rate_clk_cnt)
0 : begin // 重置
start_bit <= 3'd0;
stop_bit <= 3'd0;
for (i = 0; i<8; i=i+1) begin
data_byte[i] <= 3'd0;
end
end
6,7,8,9,10,11 : begin // 起始位采集
start_bit <= start_bit+rx_data_r1;
end
22,23,24,25,26,27 : begin // 第一位数据
data_byte[0] <= data_byte[0]+rx_data_r1;
end
38,39,40,41,42,43 : begin // 第二位数据
data_byte[1] <= data_byte[1]+rx_data_r1;
end
54,55,56,57,58,59 : begin // 第三位数据
data_byte[2] <= data_byte[2]+rx_data_r1;
end
70,71,72,73,74,75 : begin // 第四位数据
data_byte[3] <= data_byte[3]+rx_data_r1;
end
86,87,88,89,90,91 : begin // 第五位数据
data_byte[4] <= data_byte[4]+rx_data_r1;
end
102,103,104,105,106,107 : begin // 第六位数据
data_byte[5] <= data_byte[5]+rx_data_r1;
end
118,119,120,121,122,123 : begin // 第七位数据
data_byte[6] <= data_byte[6]+rx_data_r1;
end
134,135,136,137,138,139 : begin // 第八位数据
data_byte[7] <= data_byte[7]+rx_data_r1;
end
150,151,152,153,154,155 : begin // 停止位
stop_bit <= stop_bit+rx_data_r1;
end
default : begin
start_bit <= start_bit;
stop_bit <= stop_bit;
for (i = 0; i<8; i=i+1) begin
data_byte[i] <= data_byte[i];
end
end
endcase
end
else begin
start_bit <= start_bit;
stop_bit <= stop_bit;
for (i = 0; i<8; i=i+1) begin
data_byte[i] <= data_byte[i];
end
end
end
end
/******************/
/****送出接收到的数据****/
always @(posedge clk or negedge rst_n) begin
if (~rst_n) begin
rx_data_r <= 'd0;
rx_data_valid_r <= 1'b0;
end
else begin
if (rx_done) begin
if (rx_data_ready) begin
for (i = 0; i<8; i=i+1) begin
rx_data_r[i] <= data_byte[i][2];
end
end
else begin
rx_data_r <= rx_data_r;
end
rx_data_valid_r <= 1'b1;
end
else begin
rx_data_r <= 'd0;
rx_data_valid_r <= 1'b0;
end
end
end
assign rx_data = rx_data_r;
assign rx_data_valid = rx_data_valid_r;
/******************/
endmodule
Tx 部分
// UART 发送模块 可选波特率 仅支持八位数据+一位停止位
module UART_Tx #(
parameter CLK_FREQ_MHZ = 27,
parameter BAUD_RATE = 9600
)(
input clk,
input rst_n,
input [7:0] tx_data, // 准备发送的数据
input tx_data_valid, // 发送的数据有效
output tx_data_ready, // 发送模块已准备好发送数据
output tx_pin // 发送的串口数据 tx_data_ready和tx_data_valid都为高时数据被发送
);
localparam BAUD_CLK_CNT_THRESHOLD = (8 + 2) + 1; // 波特率时钟计数最大值
localparam BAUD_CNT_THRESHOLD = CLK_FREQ_MHZ * 1000000 / BAUD_RATE - 1; // 波特率计数最大值
reg [7:0] tx_data_r;
reg tx_state;
reg tx_done;
reg [$clog2(BAUD_CNT_THRESHOLD)-1:0] baud_rate_cnt; // 波特率计数器
reg baud_rate_clk;
reg [$clog2(BAUD_CLK_CNT_THRESHOLD)-1:0] baud_rate_clk_cnt;
reg tx_pin_r;
/****发送数据暂存****/
always @(posedge clk or negedge rst_n) begin
if (~rst_n) begin
tx_data_r <= 'd0;
end
else begin
if (tx_data_valid && tx_data_ready) begin
tx_data_r <= tx_data;
end
else begin
tx_data_r <= tx_data_r;
end
end
end
/******************/
/****发送状态逻辑****/
always @(posedge clk or negedge rst_n) begin
if (~rst_n) begin
tx_state <= 1'b0;
end
else begin
if (tx_data_valid && tx_data_ready) begin
tx_state <= 1'b1;
end
else if (tx_done) begin
tx_state <= 1'b0;
end
else begin
tx_state <= tx_state;
end
end
end
/******************/
/****发送结束逻辑****/
always @(posedge clk or negedge rst_n) begin
if (~rst_n) begin
tx_done <= 1'b0;
end
else begin
if (baud_rate_clk_cnt==BAUD_CLK_CNT_THRESHOLD) begin
tx_done <= 1'b1;
end
else begin
tx_done <= 1'b0;
end
end
end
/******************/
/****波特率计数器****/
always @(posedge clk or negedge rst_n) begin
if (~rst_n) begin
baud_rate_cnt <= 'd0;
end
else begin
if (tx_state) begin // 接收状态
if (baud_rate_cnt==BAUD_CNT_THRESHOLD) begin
baud_rate_cnt <= 'd0;
end
else begin
baud_rate_cnt <= baud_rate_cnt + 1'd1;
end
end
else begin
baud_rate_cnt <= 'd0;
end
end
end
/******************/
/****波特率时钟生成****/
always @(posedge clk or negedge rst_n) begin
if (~rst_n) begin
baud_rate_clk <= 1'b0;
end
else begin
if (baud_rate_cnt=='d1) begin
baud_rate_clk <= 1'b1;
end
else begin
baud_rate_clk <= 1'b0;
end
end
end
/******************/
/****波特率时钟计数****/
always @(posedge clk or negedge rst_n) begin
if (~rst_n) begin
baud_rate_clk_cnt <= 'd0;
end
else begin
if (~tx_done) begin
if (baud_rate_clk) begin
baud_rate_clk_cnt <= baud_rate_clk_cnt + 1'd1;
end
else begin
baud_rate_clk_cnt <= baud_rate_clk_cnt;
end
end
else begin
baud_rate_clk_cnt <= 'd0;
end
end
end
/******************/
/****发送数据部分****/
always @(posedge clk or negedge rst_n) begin
if (~rst_n) begin
tx_pin_r <= 1'b0;
end
else begin
case(baud_rate_clk_cnt)
0 : begin tx_pin_r <= 1'b1; end
1 : begin tx_pin_r <= 1'b0; end // 起始位
2 : begin tx_pin_r <= tx_data_r[0]; end // 第一位数据
3 : begin tx_pin_r <= tx_data_r[1]; end // 第二位数据
4 : begin tx_pin_r <= tx_data_r[2]; end // 第三位数据
5 : begin tx_pin_r <= tx_data_r[3]; end // 第四位数据
6 : begin tx_pin_r <= tx_data_r[4]; end // 第五位数据
7 : begin tx_pin_r <= tx_data_r[5]; end // 第六位数据
8 : begin tx_pin_r <= tx_data_r[6]; end // 第七位数据
9 : begin tx_pin_r <= tx_data_r[7]; end // 第八位数据
10 : begin tx_pin_r <= 1'b1; end // 停止位
default : begin tx_pin_r <= 1'b1; end
endcase
end
end
assign tx_data_ready = ~tx_state;
assign tx_pin = tx_pin_r;
/******************/
endmodule
Module-sim 测试代码
`timescale 1ns/1ns
module top_module_tb;
reg clk;
reg rst_n;
reg rx_data_ready;
wire [7:0] rx_data;
wire rx_data_valid;
reg [7:0] tx_data;
reg tx_data_valid;
wire tx_data_ready;
wire uart_pin;
always begin
clk = 1'b0; # 18; // 27M Hz
clk = 1'b1; # 19;
end
initial begin
rst_n = 1'b0;
# 20 rst_n = 1'b1;
end
// initial begin
// # 200000;
// $stop;
// end
integer i;
initial begin
rx_data_ready = 1'b1;
tx_data = 8'h11;
tx_data_valid = 1'b1;
end
UART_Rx #(
.CLK_FREQ_MHZ(27),
.BAUD_RATE(9600)
) u_uart_rx (
.clk(clk),
.rst_n(rst_n),
.rx_data_ready(rx_data_ready), // 准备好接受串口数据
.rx_pin(uart_pin),
.rx_data(rx_data), // 接收到的串口数据 rx_data_ready和rx_data_valid均为高时送出
.rx_data_valid(rx_data_valid) // 接收到的串口数据有效
);
UART_Tx #(
.CLK_FREQ_MHZ(27),
.BAUD_RATE(9600)
) u_uart_tx (
.clk(clk),
.rst_n(rst_n),
.tx_data(tx_data), // 准备发送的数据
.tx_data_valid(tx_data_valid), // 发送的数据有效
.tx_data_ready(tx_data_ready), // 发送模块已准备好发送数据
.tx_pin(uart_pin) // 发送的串口数据 tx_data_ready和tx_data_valid都为高时数据被发送
);
endmodule
Gowin FPGA 烧录测试代码
// 收到数据后发送回去
module UART_test #(
parameter CLK_FREQ_MHZ = 27,
parameter BAUD_RATE = 9600
) (
input sys_clk,
input rst_n,
input rx_pin, // UART 接收
output tx_pin // UART 发送
);
reg [7:0] rx_data_r; // 待发送数据
reg [7:0] tx_data_r; // 接收到的数据
reg tx_data_valid; // 待发送数据有效
wire rx_data_valid; // 接收数据有效
wire tx_data_ready; // 准备好发送数据
wire [7:0] rx_data; // 接收到的数据
always @(posedge sys_clk or negedge rst_n) begin
if (~rst_n) begin
rx_data_r <= 8'd0;
tx_data_r <= 8'd0;
tx_data_valid <= 1'b0;
end
else begin
if (rx_data_valid) begin // 接收到数据
rx_data_r <= rx_data; // 接收到的数据缓存
if (tx_data_ready) begin // 准备好发送数据
tx_data_valid <= 1'b1;
tx_data_r <= rx_data_r;
end
else begin
tx_data_r <= tx_data_r;
tx_data_valid <= 1'b0;
end
end
else begin
tx_data_valid <= 1'b0;
rx_data_r <= rx_data_r;
end
end
end
UART_Rx #(
.CLK_FREQ_MHZ(CLK_FREQ_MHZ),
.BAUD_RATE(BAUD_RATE)
) u_uart_rx (
.clk(sys_clk),
.rst_n(rst_n),
.rx_data_ready(1'b1), // 准备好接受串口数据
.rx_pin(rx_pin),
.rx_data(rx_data), // 接收到的串口数据 rx_data_ready和rx_data_valid均为高时送出
.rx_data_valid(rx_data_valid) // 接收到的串口数据有效
);
UART_Tx #(
.CLK_FREQ_MHZ(CLK_FREQ_MHZ),
.BAUD_RATE(BAUD_RATE)
) u_uart_tx (
.clk(sys_clk),
.rst_n(rst_n),
.tx_data(tx_data_r), // 准备发送的数据
.tx_data_valid(tx_data_valid), // 发送的数据有效
.tx_data_ready(tx_data_ready), // 发送模块已准备好发送数据
.tx_pin(tx_pin) // 发送的串口数据 tx_data_ready和tx_data_valid都为高时数据被发送
);
endmodule