下面摘录我写的简单的UART代码,对于灵活性和健壮性做了如下设计:
1、系统时钟及串口波特率以参数形式输入,例化时可以灵活设置
2、接受模块在起始位会检测中点电平是否仍然为低,否则判定为抖动
接收机代码
`timescale 1ns/1ps
// 系统时钟200MHz,波特率115200
module uart_rx #(
parameter BAUDRATE = 115200,
parameter FREQ = 200_000_000)(
input clk, nrst,
input rx,
output reg [7:0] rdata,
output reg vld
);
localparam T = FREQ / BAUDRATE;
// flag接受处理标志位,为1表明当前处于接受状态
reg flag;
always @(posedge clk or negedge nrst) begin
if(nrst == 0)
flag <= 0;
else if(flag == 0 && rx == 0)
flag <= 1;
else if(cnt_bit == 1 - 1 && cnt_clk == T / 2 - 1 && rx == 1)
flag <= 0;
else if(end_cnt_bit)
flag <= 0;
end
// 两层计数结构,cnt_clk计数每一位所占的时钟数,cnt_bit计数1个开始位,8个数据位,一个停止位,共10位
reg [3:0] cnt_bit;
reg [31:0] cnt_clk;
assign end_cnt_clk = cnt_clk == T - 1;
assign end_cnt_bit = end_cnt_clk && cnt_bit == 10 - 1;
always @(posedge clk or negedge nrst) begin
if(nrst == 0)
cnt_clk <= 0;
else if(flag) begin
if(end_cnt_clk)
cnt_clk <= 0;
else
cnt_clk <= cnt_clk + 1'b1;
end
else
cnt_clk <= 0;
end
always @(posedge clk or negedge nrst) begin
if(nrst == 0)
cnt_bit <= 0;
else if(end_cnt_clk) begin
if(end_cnt_bit)
cnt_bit <= 0;
else
cnt_bit <= cnt_bit + 1'b1;
end
end
// 读数据及数据有效指示信号
always @(posedge clk or negedge nrst) begin
if(nrst == 0)
rdata <= 0;
else if(cnt_clk == T / 2 - 1 && cnt_bit != 1 - 1 && cnt_bit != 10 - 1)
rdata[cnt_bit - 1] <= rx;
end
always @(posedge clk or negedge nrst) begin
if(nrst == 0)
vld <= 0;
else if(end_cnt_bit)
vld <= 1;
else
vld <= 0;
end
endmodule
发送机代码
`timescale 1ns/1ps
// 系统时钟200MHz,波特率115200,带忙闲指示信号rdy
module uart_tx #(
parameter BAUDRATE = 115200,
parameter FREQ = 200_000_000)(
input clk, nrst,
input wrreq,
input [7:0] wdata,
output reg tx,
output reg rdy
);
reg [3:0] cnt_bit;
reg [31:0] cnt_clk;
localparam T = FREQ / BAUDRATE;
// 有写请求时将rdy信号拉底,待到数据发送完毕再将信号拉
always @(posedge clk or negedge nrst) begin
if(nrst == 0)
rdy <= 1;
else if(wrreq)
rdy <= 0;
else if(end_cnt_bit)
rdy <= 1;
end
// 两层计数结构,cnt_clk计数每一位所占的时钟数,cnt_bit计数1个开始位,8个数据位,一个停止位,共10位
wire end_cnt_clk;
wire end_cnt_bit;
assign end_cnt_clk = cnt_clk == T - 1;
assign end_cnt_bit = end_cnt_clk && cnt_bit == 10 - 1;
always @(posedge clk or negedge nrst) begin
if(nrst == 0)
cnt_clk <= 0;
else if(rdy == 0) begin
if(end_cnt_clk)
cnt_clk <= 0;
else
cnt_clk <= cnt_clk + 1'b1;
end
end
always @(posedge clk or negedge nrst) begin
if(nrst == 0)
cnt_bit <= 0;
else if(end_cnt_clk) begin
if(end_cnt_bit)
cnt_bit <= 0;
else
cnt_bit <= cnt_bit + 1'b1;
end
end
// 先发送一个起始位0,然后8位数据位,最后是停止位1
always @(posedge clk or negedge nrst) begin
if(nrst == 0)
tx <= 1;
else if(rdy == 0 && cnt_clk == 0) begin
if(cnt_bit == 1 - 1)
tx <= 0;
else if(cnt_bit == 10 - 1)
tx <= 1;
else
tx <= wdata[cnt_bit - 1];
end
end
endmodule
在Xilinx Artix-7平台上验证的顶层代码
`timescale 1ns / 1ps
module uart_top(
input clk_p, clk_n, nrst,
input rx,
output tx
);
localparam BAUDRATE = 115200;
localparam FREQ = 200_000_000;
// 差分时钟信号转为单端信号
IBUFGDS #(
.DIFF_TERM("FALSE"),
.IBUF_LOW_PWR("TRUE"),
.IOSTANDARD("DEFAULT")
) IBUFGDS_inst(
.O(clk),
.I(clk_p),
.IB(clk_n)
);
wire [7:0] data;
wire vld;
uart_rx #(BAUDRATE, FREQ) uart_rx_u(
.clk (clk ),
.nrst (nrst ),
.rx (rx ),
.rdata (data ),
.vld (vld )
);
uart_tx #(BAUDRATE, FREQ) uart_tx_u(
.clk (clk ),
.nrst (nrst ),
.wrreq (vld ),
.wdata (data ),
.tx (tx ),
.rdy ( )
);
ila_0 ila_0_u(
.clk (clk ),
.probe0 (nrst ),
.probe1 ({tx,rx}),
.probe2 (data ),
.probe3 (vld )
);
endmodule