1、uart_tx.v
/*
*
*@Author: X-Z
*@Date:2023-02-09 12:44:54
*@Function:串口发送模块,将接收模块接收到的1帧10bit并行数据转换为10bit的串行数据并通过发送引脚tx输出送到pc机的接收引脚
*/
/************************这里采用的是普通fifo模式数据在rd_req之后才会有效
如果直接把rd_req信号赋值给tx_data_vld会出现数据有效信号在数据出现之前出现高电平,
从而采集到不定态,给din_vld同步打拍没有用,因为ready信号会同步变换
所以采用两个标志信号的方式处理时序,在这里传进来的数据是延后din_vld一个时钟周期的
所以将din_vld同步打拍延迟一下,产生reg0去控制数据的发送,此时是同步的
而ready信号的控制信号则直接由din_vld同步扩展产生,这样会使ready信号在数据刚开始变成将要发送的有效数据时就变得无效即fifo
不能读数据,而数据在ready信号拉低的下一个周期后才才开始发送,从而避免了读取到错误数据的情况
验证发现只要同步就可以实现,即延迟一个时钟周期
**********************************/
module uart_tx(
input wire clk ,
input wire rst_n ,
input wire [7:0] din ,//发送端送给接收端的有效的8bit并行数据
input wire din_vld ,//输入数据有效标志信号
output reg uart_tx , //发送引脚
output wire ready //指示可以数据让它发送
);
// wire [7:0] din;
// //测试
// assign din = 8'h5a;
//波特率为115200时传输1位所需的时钟周期
parameter CNT_MAX_BPS = 9'd433;//(50_000_000/115200)=434
reg [8:0] cnt_bps ;
wire add_cnt_bps;
wire end_cnt_bps;
reg [3:0] cnt_bit ;//计数0-9构成一帧数据
wire add_cnt_bit;
wire end_cnt_bit;
//对din_vld信号时长延长
reg din_vld_flag;
reg din_vld_flag1;
reg [9:0] data ;
reg reg0;
always@(posedge clk or negedge rst_n)begin
if(!rst_n)begin
reg0 <= 1'b0;
end
else begin
reg0 <= din_vld;
end
end
always@(posedge clk or negedge rst_n)begin
if(!rst_n)begin
din_vld_flag <= 1'b0;
end
else if(reg0)begin
din_vld_flag <= 1'b1;
end
else if(end_cnt_bit && end_cnt_bps)begin//一帧数据发送完后拉低
din_vld_flag <= 1'b0;
end
end
always@(posedge clk or negedge rst_n)begin
if(!rst_n)begin
din_vld_flag1 <= 1'b0;
end
else if(end_cnt_bit && end_cnt_bps)begin//一帧数据发送完后拉低
din_vld_flag1 <= 1'b0;
end
else if(din_vld)begin
din_vld_flag1 <= 1'b1;
end
end
//1bit计数器
always@(posedge clk or negedge rst_n)begin
if(!rst_n)begin
cnt_bps <= 1'b0;
end
else if(add_cnt_bps)begin
if(end_cnt_bps)begin
cnt_bps <= 1'b0;
end
else begin
cnt_bps <= cnt_bps + 1'b1;
end
end
end
assign add_cnt_bps = din_vld_flag;
assign end_cnt_bps = add_cnt_bps && cnt_bps == CNT_MAX_BPS;
//1帧数据计数器10bit
always@(posedge clk or negedge rst_n)begin
if(!rst_n)begin
cnt_bit <= 1'b0;
end
else if(add_cnt_bit)begin
if(end_cnt_bit)begin
cnt_bit <= 1'b0;
end
else begin
cnt_bit <= cnt_bit + 1'b1;
end
end
end
assign add_cnt_bit = end_cnt_bps;
assign end_cnt_bit = add_cnt_bit && cnt_bit == 4'd9;
always@(posedge clk or negedge rst_n)begin//一帧数据发送完后拉低
if(!rst_n)
data <= 10'd0;
else if(reg0)
data <= {1'b1,din[7:0],1'b0};
end
//根据通信协议发送数据帧的每一位
always@(posedge clk or negedge rst_n)begin
if(!rst_n)begin
uart_tx <= 1'b1;//初始化空闲状态高电平
end
else if(din_vld_flag && cnt_bps == 1)
uart_tx <= data[cnt_bit];
else begin
uart_tx <= uart_tx;//其他情况为空闲状态
end
end
assign ready = ~din_vld_flag1;//当tx发送数据的时候ready无效不能从fifo中读数据,只要发送完成才可以读取下一个数据
endmodule
2、uart_rx.v
module uart_rx (
input wire clk ,
input wire rst_n ,
input wire uart_rx ,
output reg [7:0] dout ,
output reg dout_vld //数据接收完成标志信号
);
//波特率为115200时传输1位所需的时钟周期
parameter CNT_MAX_BPS = 9'd433;//(50_000_000/115200)=434
reg [8:0] cnt_bps ;
wire add_cnt_bps;
wire end_cnt_bps;
reg [3:0] cnt_bit ;//计数0-9构成一帧数据
wire add_cnt_bit;
wire end_cnt_bit;
//同步打拍信号定义
reg rx_reg0;
reg rx_reg1;
//开始计数使能信号
reg start_en;
wire bit_flag;//指示提取到哪一bit了
reg [9:0] rx_data;//寄存并行数据
//同步打拍rx信号
always@(posedge clk or negedge rst_n)begin
if(!rst_n)begin
rx_reg0 <= 1'b1;
rx_reg1 <= 1'b1;
end
else begin
rx_reg0 <= uart_rx;//同步(跨时钟域处理信号会产生亚稳态)
rx_reg1 <= rx_reg0;//打第一拍
end
end
//nedge下降沿
assign nedge = rx_reg1 && (~rx_reg0);
//开始计数使能信号
always@(posedge clk or negedge rst_n)begin
if(!rst_n)begin
start_en <= 1'b0;//复位初始化
end
else if(end_cnt_bit)
start_en <= 1'b0;
else if(nedge)begin
start_en <= 1'b1;
end
end
//1bit计数器
always@(posedge clk or negedge rst_n)begin
if(!rst_n)begin
cnt_bps <= 1'b0;
end
else if(add_cnt_bps)begin
if(end_cnt_bps)begin
cnt_bps <= 1'b0;
end
else begin
cnt_bps <= cnt_bps + 1'b1;
end
end
end
assign add_cnt_bps = start_en;
assign end_cnt_bps = add_cnt_bps && cnt_bps == CNT_MAX_BPS ;
assign bit_flag = (cnt_bps == (CNT_MAX_BPS >> 1)) ? 1'b1:1'b0;//计数到一半给高电平
//1帧数据计数器10bit
always@(posedge clk or negedge rst_n)begin
if(!rst_n)begin
cnt_bit <= 1'b0;
end
else if(add_cnt_bit)begin
if(end_cnt_bit)begin
cnt_bit <= 1'b0;
end
else begin
cnt_bit <= cnt_bit + 1'b1;
end
end
end
assign add_cnt_bit = end_cnt_bps;
assign end_cnt_bit = add_cnt_bit && cnt_bit == 4'd9 ;
//串并转换
always@(posedge clk or negedge rst_n)begin
if(!rst_n)
rx_data <= 10'd0;
else if(end_cnt_bit && end_cnt_bps)
rx_data <= 10'd0;
else if(bit_flag && start_en)
rx_data <= {rx_reg1,rx_data[9:1]};
else
rx_data <= rx_data;
end
//输出赋值
always@(posedge clk or negedge rst_n)begin
if(!rst_n)begin
dout <= 8'd0;//复位初始化
end
else if(end_cnt_bit && end_cnt_bps)begin
dout <= rx_data[8:1];
end
end
//输出赋值
always@(posedge clk or negedge rst_n)begin
if(!rst_n)begin
dout_vld <= 1'd0;//复位初始化
end
else if(end_cnt_bit && end_cnt_bps)begin
dout_vld <= 1'b1;
end
else
dout_vld <= 1'b0;
end
endmodule
3、control.v
module control(
input wire clk ,
input wire rst_n ,
input wire [7:0] rx_data ,
input wire rx_data_vld,
input wire ready ,//准备好开始fifo读信号控制rd_req
output reg [7:0] tx_data ,
output reg tx_data_vld
);
reg rd_req ;
wire wr_req ;
wire empty ;
wire full ;
wire [2:0] usedw ;
wire [7:0] q ;
reg tx_flag ;
fifo_s u_fifo_s (
.aclr ( ~rst_n ),
.clock ( clk ),
.data ( rx_data ),
.rdreq ( rd_req ),
.wrreq ( wr_req ),
.empty ( empty ),
.full ( full ),
.q ( q ),
.usedw ( usedw )
);
always@(*)begin
if(!rst_n)
tx_data <= 8'd0;
else
tx_data <= q;
end
//只要fifo中数据大于4个就将fifo清空
always@(posedge clk or negedge rst_n)begin
if(!rst_n)
tx_flag <= 1'b0;
else if(usedw >= 3'd4)
tx_flag <= 1'b1;
else if(empty)
tx_flag <= 1'b0;//读空fifo后拉低
end
//写请求wr_req
assign wr_req = ~full && rx_data_vld;//数据有效开始写
always@(*)begin
if(!rst_n)
rd_req = 1'b0;
else if(tx_flag && ready)
rd_req = 1'b1;
else
rd_req = 1'b0;
end
always@(*)begin
if(!rst_n)
tx_data_vld = 1'b0;
else if(tx_flag)
tx_data_vld = rd_req;
else
tx_data_vld = 1'b0;
end
endmodule
4、uart_top.v
/*
*
*@Author: X-Z
*@Date:2023-02-09 11:22:56
*@Function:串口通信顶层的设计
*/
module uart_top(
input clk ,
input rst_n ,
input uart_rx ,//发送引脚
output uart_tx //接收引脚
);
//中间信号定义
wire [7:0] rx_data,tx_data ;//8bit的数据
wire rx_data_vld,tx_data_vld;
wire ready ;
// reg reg0,reg1;
//例化接收模块uart_rx
uart_rx u_uart_rx(
.clk (clk ),
.rst_n (rst_n ),
.uart_rx (uart_rx ),
.dout (rx_data ),
.dout_vld (rx_data_vld ) //10bit数据接收完成标志信号
);
//控制模块
control u_control(
. clk (clk ),
. rst_n (rst_n ),
. rx_data (rx_data ),
. rx_data_vld(rx_data_vld),
. ready (ready ),//准备好开始fifo读信号控制rd_req
. tx_data (tx_data ),
. tx_data_vld(tx_data_vld)
);
// always@(posedge clk or negedge clk)begin
// if(!rst_n)begin
// reg0 <= 1'b0;
// reg1 <= 1'b0;
// end
// else begin
// reg0 <= tx_data_vld;
// reg1 <= reg0;
// end
// end
//发送模块例化uart_tx
uart_tx u_uart_tx(
.clk (clk ),
.rst_n (rst_n ),
.din (tx_data ),
.din_vld (tx_data_vld),//发送端输入数据有效标志信号
.uart_tx (uart_tx ),
.ready (ready )
);
endmodule
5、rx_tb.v
`timescale 1ns/1ps
module rx_tb();
parameter CYCLE = 20;
//defparam u_uart_rx.CNT_MAX_BPS = 9'd25;
reg clk ;
reg rst_n ;
reg uart_rx ;
wire [7:0] dout ;
wire dout_vld;
initial begin
clk = 1'b1;
rst_n <= 1'b0;
uart_rx = 1'b1;
#(15)
rst_n <= 1'b1;
end
always #(CYCLE/2) clk = ~clk;
initial begin//1_0000_0001_0---1-----1_0000_0010_0------2
#(100*CYCLE)
uart_rx = 1'b0;
#(434*CYCLE)
uart_rx = 1'b1;
#(434*CYCLE)
uart_rx = 1'b0;
#(434*CYCLE)
uart_rx = 1'b0;
#(434*CYCLE)
uart_rx = 1'b0;
#(434*CYCLE)
uart_rx = 1'b0;
#(434*CYCLE)
uart_rx = 1'b0;
#(434*CYCLE)
uart_rx = 1'b0;
#(434*CYCLE)
uart_rx = 1'b0;
#(434*CYCLE)
uart_rx = 1'b1;
#(434*CYCLE)
#(800*CYCLE)
//接收2
uart_rx = 1'b0;
#(434*CYCLE)
uart_rx = 1'b0;
#(434*CYCLE)
uart_rx = 1'b1;
#(434*CYCLE)
uart_rx = 1'b0;
#(434*CYCLE)
uart_rx = 1'b0;
#(434*CYCLE)
uart_rx = 1'b0;
#(434*CYCLE)
uart_rx = 1'b0;
#(434*CYCLE)
uart_rx = 1'b0;
#(434*CYCLE)
uart_rx = 1'b0;
#(434*CYCLE)
uart_rx = 1'b1;
#(434*CYCLE)
#(800*CYCLE)
$stop;
end
//例化接收模块
uart_rx u_uart_rx(
.clk (clk ),
.rst_n (rst_n ),
.uart_rx (uart_rx ),
.dout_vld (dout_vld ),//输入数据有效标志信号
.dout (dout ) //发送引脚
);
endmodule
6、uart_rx_tb.v
`timescale 1ns/1ps
module uart_rx_tb();
parameter CYCLE = 20;
//defparam u_uart_rx.CNT_MAX_BPS = 9'd25;
reg clk ;
reg rst_n ;
reg uart_rx ;
wire [7:0] dout ;
wire dout_vld;
initial begin
clk = 1'b1;
rst_n <= 1'b0;
#(15)
rst_n <= 1'b1;
end
always #(CYCLE/2) clk = ~clk;
//将1个8bit的数据按数据帧的格式一位一位的发送
task uart_rx_bit(
input [7:0] data
);
integer i ;
for(i=0;i<10;i=i+1)begin
case(i)
0: uart_rx = 1'b0 ;//起始位
1: uart_rx = data[0];
2: uart_rx = data[1];
3: uart_rx = data[2];
4: uart_rx = data[3];
5: uart_rx = data[4];
6: uart_rx = data[5];
7: uart_rx = data[6];
8: uart_rx = data[7];
9: uart_rx = 1'b1 ;//停止位
default :uart_rx = 1'b1;//其他空闲状态
endcase
#(CYCLE*434);
end
endtask
initial begin
#(200*CYCLE)
uart_rx_bit(8'd1);
uart_rx = 1'b1;
#50
uart_rx_bit(8'd2);
uart_rx = 1'b1;
#50
uart_rx_bit(8'd6);
uart_rx = 1'b1;
#(100*CYCLE)
$stop;
end
//例化发送模块
uart_rx u_uart_rx(
.clk (clk ),
.rst_n (rst_n ),
.uart_rx (uart_rx ),
.dout_vld (dout_vld ),//输入数据有效标志信号
.dout (dout ) //发送引脚
);
endmodule
7、uart_top_tb.v
`timescale 1ns/1ps
module uart_top_tb();
parameter CYCLE = 20;
//defparam u_uart_tx.CNT_MAX_BPS = 9'd25;
reg clk ;
reg rst_n ;
reg [7:0] din ;
reg din_vld ;
wire uart_tx ;
wire tb_uart_tx ;
initial begin
clk = 1'b1;
rst_n <= 1'b0;
#(15)
rst_n <= 1'b1;
end
always #(CYCLE/2) clk = ~clk;
initial begin
din = 8'd0;
din_vld = 1'b0;
#(20*CYCLE)
//发送8'ha
din = 8'hca;
#CYCLE
din_vld = 1'b1;
#CYCLE
din_vld = 1'b0;
#(CYCLE*434*11)
//发送8'h6b
din = 8'h6b;
din_vld = 1'b1;
#CYCLE
din_vld = 1'b0;
#(CYCLE*434*11)
//发送8'ha
din = 8'h22;
din_vld = 1'b1;
#CYCLE
din_vld = 1'b0;
#(CYCLE*434*11)
//发送8'h6b
din = 8'h55;
din_vld = 1'b1;
#CYCLE
din_vld = 1'b0;
#(CYCLE*434*11)
//发送8'h6b
din = 8'h65;
din_vld = 1'b1;
#CYCLE
din_vld = 1'b0;
#(CYCLE*434*11*7)
$stop;
end
uart_top u_uart_top(
.clk (clk ) ,
.rst_n (rst_n ) ,
.uart_rx (uart_tx ) ,//发送引脚
.uart_tx (tb_uart_tx ) //接收引脚
);
//例化发送模块
uart_tx u_uart_tx(
.clk (clk ),
.rst_n (rst_n ),
.din (din ),//发送端送给接收端的有效的8bit并行数据
.din_vld (din_vld ),//输入数据有效标志信号
.uart_tx (uart_tx ) //发送引脚
);
endmodule