前言
在此我就不对UART协议细节部分做介绍,网上相关的介绍太多了。目前通过串口调试助手成功实现板载时钟50MHz情况下最大波特率3000000 bit/s通信。
模块组成
顶层:uart_modu
底层:uart_tx_modu、uart_rx_modu
模块接口
顶层
uart_modu #(
.CLK_FRE (50) , //模块时钟,单位MHz
.BAUD_RATE(115200) //通信波特率
)
your_instance_name(
.clk (clk ) , //input[0:0]模块时钟输入
.rst (rst ) , //input[0:0]模块复位输入,高电平有效
.i_tx_begin (i_tx_begin ) , //input[0:0]开始发送信号输入
.i_tx_data (i_tx_data ) , //input[7:0]待发送数据输入
.o_tx_done (o_tx_done ) , //output[0:0]发送完成标志输出
.o_tx_busy (o_tx_busy ) , //output[0:0]模块忙碌标志
.o_tx_pin (o_tx_pin ) , //output[0:0]发送引脚
.o_rx_data (o_rx_data ) , //output[7:0]接收数据输出
.o_rx_vald (o_rx_vald ) , //output[0:0]接收数据有效标志
.o_rx_done (o_rx_done ) , //output[0:0]接收完成标志
.o_rx_busy (o_rx_busy ) , //output[0:0]模块忙碌标志
.i_rx_pin (i_rx_pin ) //input[0:0]接收引脚
);
底层
uart_tx_modu #(
.CLK_FRE (50) , //模块时钟,单位MHz
.BAUD_RATE(115200) //通信波特率
)
your_instance_name(
.clk (clk ), //input[0:0]模块时钟输入
.rst (rst ), //input[0:0]模块复位输入,高电平有效
.tx_begin (tx_begin ), //input[0:0]开始发送信号输入
.tx_data (tx_data ), //input[7:0]待发送数据输入
.tx_done (tx_done ), //output[0:0]发送完成标志输出
.tx_busy (tx_busy ), //output[0:0]模块忙碌标志
.tx_pin (tx_pin ) //output[0:0]发送引脚
);
uart_rx_modu #(
.CLK_FRE (50) , //模块时钟,单位MHz
.BAUD_RATE(115200) //通信波特率
)
your_instance_name(
.clk (clk ), //input[0:0]模块时钟输入
.rst (rst ), //input[0:0]模块复位输入,高电平有效
.rx_data (rx_data ), //output[7:0]接收数据输出
.rx_vald (rx_vald ), //output[0:0]接收数据有效标志
.rx_done (rx_done ), //output[0:0]接收完成标志
.rx_busy (rx_busy ), //output[0:0]模块忙碌标志
.rx_pin (rx_pin ) //input[0:0]接收引脚
);
时许仿真
tx_modu
测试代码
module tx_debug();
localparam CUN = 10;
always #(CUN) clk = ~clk;
reg [0:0] clk = 1'b0;
reg [0:0] rst = 1'b0;
reg [0:0] tx_begin = 1'b0;
reg [7:0] tx_data = 8'hA5;
wire [0:0] tx_done ;
wire [0:0] tx_busy ;
wire [0:0] tx_pin ;
initial begin
#(CUN*4) rst = 1'b1;
#(CUN*2) rst = 1'b0;
#(CUN*4) tx_begin = 1'b1;
#(CUN*2) tx_begin = 1'b0;
end
uart_tx_modu #(
.CLK_FRE (50) , //模块时钟,单位MHz
.BAUD_RATE(115200) //通信波特率
)
uart_tx_debug(
.clk (clk ) , //input[0:0]模块时钟输入
.rst (rst ) , //input[0:0]模块复位输入,高电平有效
.tx_begin (tx_begin ) , //input[0:0]开始发送信号输入
.tx_data (tx_data ) , //input[7:0]待发送数据输入
.tx_done (tx_done ) , //output[0:0]发送完成标志输出
.tx_busy (tx_busy ) , //output[0:0]模块忙碌标志
.tx_pin (tx_pin ) //output[0:0]发送引脚
);
endmodule
仿真截图
rx_modu
仿真源代码
module rx_debug();
localparam CUN = 10;
always #(CUN) clk = ~clk;
reg [0:0] clk = 1'b0; //模块时钟输入
reg [0:0] rst = 1'b0; //模块复位输入,高电平有效
wire [7:0] rx_data; //接收数据输出
wire [0:0] rx_vald; //接收数据有效标志
wire [0:0] rx_done; //接收完成标志
wire [0:0] rx_busy; //模块忙碌标志
reg [0:0] rx_pin = 1'b1; //接收引脚
initial begin
#(CUN*4) rst = 1'b1;
#(CUN*2) rst = 1'b0;
#(CUN*4) rx_pin = 1'b0; //
#8680 rx_pin = 1'b1; //1
#8680 rx_pin = 1'b0; //2
#8680 rx_pin = 1'b1; //3
#8680 rx_pin = 1'b0; //4
#8680 rx_pin = 1'b1; //5
#8680 rx_pin = 1'b0; //6
#8680 rx_pin = 1'b1; //7
#8680 rx_pin = 1'b0; //8
#8680 rx_pin = 1'b1; //
end
uart_rx_modu #(
.CLK_FRE (50) , //模块时钟,单位MHz
.BAUD_RATE(3000000) //通信波特率
)
uart_tx_debug(
.clk (clk ), //input[0:0]模块时钟输入
.rst (rst ), //input[0:0]模块复位输入,高电平有效
.rx_data (rx_data ), //output[7:0]接收数据输出
.rx_vald (rx_vald ), //output[0:0]接收数据有效标志
.rx_done (rx_done ), //output[0:0]接收完成标志
.rx_busy (rx_busy ), //output[0:0]模块忙碌标志
.rx_pin (rx_pin ) //input[0:0]接收引脚
);
endmodule
仿真截图
源码
顶层
/*===============================================
//----------- Begin Cut here for uart_modu Template ---//
uart_modu #(
.CLK_FRE (50) , //模块时钟,单位MHz
.BAUD_RATE(115200) //通信波特率
)
your_instance_name(
.clk (clk ) , //input[0:0]模块时钟输入
.rst (rst ) , //input[0:0]模块复位输入,高电平有效
.i_tx_begin (i_tx_begin ) , //input[0:0]开始发送信号输入
.i_tx_data (i_tx_data ) , //input[7:0]待发送数据输入
.o_tx_done (o_tx_done ) , //output[0:0]发送完成标志输出
.o_tx_busy (o_tx_busy ) , //output[0:0]模块忙碌标志
.o_tx_pin (o_tx_pin ) , //output[0:0]发送引脚
.o_rx_data (o_rx_data ) , //output[7:0]接收数据输出
.o_rx_vald (o_rx_vald ) , //output[0:0]接收数据有效标志
.o_rx_done (o_rx_done ) , //output[0:0]接收完成标志
.o_rx_busy (o_rx_busy ) , //output[0:0]模块忙碌标志
.i_rx_pin (i_rx_pin ) //input[0:0]接收引脚
);
// INST_TAG_END ------ End uart_modu Template ---------
===============================================*/
module uart_modu#(
parameter [7:0] CLK_FRE = 50, //The clock frequency at which the module works, in MHz
parameter [23:0] BAUD_RATE = 115200 //Communication baud rate
)(
input wire[0:0] clk , //模块时钟,单位MHz
input wire[0:0] rst , //通信波特率
input wire[0:0] i_tx_begin , //input[0:0]开始发送信号输入
input wire[7:0] i_tx_data , //input[7:0]待发送数据输入
output wire[0:0] o_tx_done , //output[0:0]发送完成标志输出
output wire[0:0] o_tx_busy , //output[0:0]模块忙碌标志
output wire[0:0] o_tx_pin , //output[0:0]发送引脚
output wire[7:0] o_rx_data , //output[7:0]接收数据输出
output wire[0:0] o_rx_vald , //output[0:0]接收数据有效标志
output wire[0:0] o_rx_done , //output[0:0]接收完成标志
output wire[0:0] o_rx_busy , //output[0:0]模块忙碌标志
input wire[0:0] i_rx_pin //input[0:0]接收引脚
);
wire [0:0] clk_i ;
wire [0:0] rst_i ;
wire [0:0] tx_begin;
wire [0:0] tx_data ;
wire [0:0] tx_done ;
wire [0:0] tx_busy ;
wire [0:0] tx_pin ;
wire [0:0] rx_data ;
wire [0:0] rx_vald ;
wire [0:0] rx_done ;
wire [0:0] rx_busy ;
wire [0:0] rx_pin ;
assign clk_i = clk ;
assign rst_i = rst ;
assign tx_begin = i_tx_begin;
assign tx_data = i_tx_data ;
assign o_tx_done = tx_done ;
assign o_tx_busy = tx_busy ;
assign o_tx_pin = tx_pin ;
assign o_rx_data = rx_data ;
assign o_rx_done = rx_done ;
assign o_rx_busy = rx_busy ;
assign rx_pin = i_rx_pin ;
uart_tx_modu #(
.CLK_FRE (CLK_FRE ), //模块时钟,单位MHz
.BAUD_RATE (BAUD_RATE ) //通信波特率
)
uart_tx_modu_u(
.clk (clk_i ), //input[0:0]模块时钟输入
.rst (rst_i ), //input[0:0]模块复位输入,高电平有效
.tx_begin (tx_begin ), //input[0:0]开始发送信号输入
.tx_data (tx_data ), //input[7:0]待发送数据输入
.tx_done (tx_done ), //output[0:0]发送完成标志输出
.tx_busy (tx_busy ), //output[0:0]模块忙碌标志
.tx_pin (tx_pin ) //output[0:0]发送引脚
);
uart_rx_modu #(
.CLK_FRE (CLK_FRE ), //模块时钟,单位MHz
.BAUD_RATE (BAUD_RATE ) //通信波特率
)
uart_rx_modu_u(
.clk (clk ), //input[0:0]模块时钟输入
.rst (rst ), //input[0:0]模块复位输入,高电平有效
.rx_data (rx_data ), //output[7:0]接收数据输出
.rx_vald (rx_vald ), //output[0:0]接收数据有效标志
.rx_done (rx_done ), //output[0:0]接收完成标志
.rx_busy (rx_busy ), //output[0:0]模块忙碌标志
.rx_pin (rx_pin ) //input[0:0]接收引脚
);
endmodule
底层
rx_modu
/*===============================================
//----------- Begin Cut here for usrt_rx_modu Template ---//
uart_rx_modu #(
.CLK_FRE (50) , //模块时钟,单位MHz
.BAUD_RATE(115200) //通信波特率
)
your_instance_name(
.clk (clk ), //input[0:0]模块时钟输入
.rst (rst ), //input[0:0]模块复位输入,高电平有效
.rx_data (rx_data ), //output[7:0]接收数据输出
.rx_vald (rx_vald ), //output[0:0]接收数据有效标志
.rx_done (rx_done ), //output[0:0]接收完成标志
.rx_busy (rx_busy ), //output[0:0]模块忙碌标志
.rx_pin (rx_pin ) //input[0:0]接收引脚
);
// INST_TAG_END ------ End usrt_rx_modu Template ---------
===============================================*/
module uart_rx_modu #(
parameter [7:0] CLK_FRE = 50, //The clock frequency at which the module works, in MHz
parameter [23:0] BAUD_RATE = 115200 //Communication baud rate
)(
input wire[0:0] clk , //input[0:0]模块时钟输入
input wire[0:0] rst , //input[0:0]模块复位输入,高电平有效
output wire[7:0] rx_data , //output[7:0]接收数据输出
output wire[0:0] rx_vald , //output[0:0]接收数据有效标志
output wire[0:0] rx_done , //output[0:0]接收完成标志
output wire[0:0] rx_busy , //output[0:0]模块忙碌标志
input wire[0:0] rx_pin //input[0:0]接收引脚
);
/*===========计算当前时钟与波特率下bit持续时钟周期==========*/
localparam [15:0] BIT_CYCLE1 = CLK_FRE * 1_000_000 / BAUD_RATE;
localparam [15:0] BIT_CYCLE2 = CLK_FRE * 1_000_000 / BAUD_RATE/2;
/************************************************************/
/*=========================定义状态=========================*/
localparam [1:0] S_IDLE = 2'd0; //空闲状态
localparam [1:0] S_START = 2'd1; //接收开始信号
localparam [1:0] S_SEND = 2'd2; //接收数据
localparam [1:0] S_STOP = 2'd3; //接收结束信号
/************************************************************/
/*=========================定义寄存器=========================*/
wire [0:0] start ; //开始信号上升沿标志
reg [1:0] start_r2 = 2'd3 ; //检测寄存器
reg [2:0] now_state = 3'd0 ; //当前状态寄存器
reg [2:0] nex_state = 3'd0 ; //下一状态寄存器
reg [31:0] time_cnt = 32'd0 ; //bit周期计时器
reg [2:0] bit_cnt = 3'd0 ; //bit计数器
reg [2:0] rx_bit_reg = 3'd0 ; //
reg [7:0] rx_data_reg = 8'd0 ;
/************************************************************/
/*=========================输出信号=========================*/
assign rx_busy = (now_state == S_IDLE) ? 1'b0 : 1'b1;
assign rx_done = (now_state == S_STOP && time_cnt == BIT_CYCLE2- 'd1) ? 1'b1 : 1'b0;
assign rx_data = (bit_cnt == 3'd7 && time_cnt >= BIT_CYCLE2 + 'd4) ? rx_data_reg : 8'd0;
assign rx_vald = (bit_cnt == 3'd7 && (time_cnt == BIT_CYCLE1 - 'd4 || time_cnt == BIT_CYCLE1 - 'd3)) ? 1'b1 : 1'b0;
/************************************************************/
/*=========================捕获开始信号=========================*/
always@ (posedge clk)
begin
if(now_state == S_IDLE)
start_r2 <= {start_r2[0],rx_pin};
else
start_r2 <= 2'd3;
end
assign start = start_r2[1] & ~start_r2[0];
/************************************************************/
/*======================bit周期计时器=======================*/
always@ (posedge clk)
begin
if(now_state == S_IDLE)
time_cnt <= 32'd0;
else if(time_cnt == BIT_CYCLE1 - 1'b1 || (now_state == S_START && time_cnt == BIT_CYCLE1 - 'd2))
time_cnt <= 32'd0;
else
time_cnt <= time_cnt + 1'b1;
end
/************************************************************/
/*========================bit计数器=========================*/
always@ (posedge clk)
begin
if(now_state != S_SEND)
bit_cnt <= 3'd0;
else if(time_cnt == BIT_CYCLE1 - 1'b1)
bit_cnt <= bit_cnt + 1'b1;
end
/************************************************************/
/*=================接收单bit数据并均值滤波==================*/
always@ (posedge clk)
begin
if(now_state == S_IDLE)
rx_bit_reg <= 3'd0;
else if(now_state == S_SEND && time_cnt == BIT_CYCLE1 - 'd2)
rx_bit_reg <= 3'd0;
else if(now_state == S_SEND && (time_cnt == BIT_CYCLE2 - 'd4 || time_cnt == BIT_CYCLE2 - 'd3 || time_cnt == BIT_CYCLE2 - 'd2 || time_cnt == BIT_CYCLE2 - 'd1 || time_cnt == BIT_CYCLE2 || time_cnt == BIT_CYCLE2 + 'd1 || time_cnt == BIT_CYCLE2 + 'd2))
rx_bit_reg <= rx_bit_reg + rx_pin;
else
rx_bit_reg <= rx_bit_reg;
end
/************************************************************/
/*=====================寄存接收到的数据=====================*/
always@ (posedge clk)
begin
if(now_state == S_IDLE)
rx_data_reg <= 8'd0;
else if(now_state == S_SEND && time_cnt == BIT_CYCLE2 + 'd4)
rx_data_reg[bit_cnt] <= rx_bit_reg[2];
else
rx_data_reg <= rx_data_reg;
end
/************************************************************/
/*=========================状态转换=========================*/
always@ (posedge clk or posedge rst)
begin
if(rst)
now_state <= S_IDLE;
else
now_state <= nex_state;
end
/************************************************************/
/*=========================下一状态=========================*/
always@ (*)
begin
case(now_state)
S_IDLE :
begin
if(start)
nex_state = S_START;
else
nex_state = S_IDLE;
end
S_START :
begin
if(time_cnt == BIT_CYCLE1 - 'd2)
nex_state = S_SEND;
else
nex_state = S_START;
end
S_SEND :
begin
if(time_cnt == BIT_CYCLE1 - 1'b1 && bit_cnt == 3'd7)
nex_state = S_STOP;
else
nex_state = S_SEND;
end
S_STOP :
begin
if(time_cnt == BIT_CYCLE2)
nex_state = S_IDLE;
else
nex_state = S_STOP;
end
default :nex_state = S_IDLE;
endcase
end
/************************************************************/
endmodule
tx_modu
/*===============================================
//----------- Begin Cut here for uart_tx_modu Template ---//
uart_tx_modu #(
.CLK_FRE (50) , //模块时钟,单位MHz
.BAUD_RATE(115200) //通信波特率
)
your_instance_name(
.clk (clk ), //input[0:0]模块时钟输入
.rst (rst ), //input[0:0]模块复位输入,高电平有效
.tx_begin (tx_begin ), //input[0:0]开始发送信号输入
.tx_data (tx_data ), //input[7:0]待发送数据输入
.tx_done (tx_done ), //output[0:0]发送完成标志输出
.tx_busy (tx_busy ), //output[0:0]模块忙碌标志
.tx_pin (tx_pin ) //output[0:0]发送引脚
);
// INST_TAG_END ------ End uart_tx_modu Template ---------
===============================================*/
module uart_tx_modu#(
parameter [7:0] CLK_FRE = 50, //The clock frequency at which the module works, in MHz
parameter [23:0] BAUD_RATE = 115200 //Communication baud rate
)(
input wire[0:0] clk , //input[0:0]模块时钟输入
input wire[0:0] rst , //input[0:0]模块复位输入,高电平有效
input wire[0:0] tx_begin , //input[0:0]开始发送信号输入
input wire[7:0] tx_data , //input[7:0]待发送数据输入
output wire[0:0] tx_done , //output[0:0]发送完成标志输出
output wire[0:0] tx_busy , //output[0:0]模块忙碌标志
output wire[0:0] tx_pin //output[0:0]发送引脚
);
/*===========计算当前时钟与波特率下bit持续时钟周期==========*/
localparam [15:0] BIT_CYCLE = CLK_FRE * 1_000_000 / BAUD_RATE;
/************************************************************/
/*=========================定义状态=========================*/
localparam [1:0] S_IDLE = 2'd0; //空闲状态
localparam [1:0] S_START = 2'd1; //发送开始信号
localparam [1:0] S_SEND = 2'd2; //发送数据
localparam [1:0] S_STOP = 2'd3; //发送结束信号
/************************************************************/
/*========================定义寄存器========================*/
reg [1:0] start_r2 = 2'd0 ; //开始信号缓存器
wire [0:0] start ; //开始信号上升沿标志
reg [2:0] now_state = 3'd0 ; //当前状态寄存器
reg [2:0] nex_state = 3'd0 ; //下一状态寄存器
reg [31:0] time_cnt = 32'd0 ; //bit周期计时器
reg [2:0] bit_cnt = 3'd0 ; //bit计数器
reg [7:0] tx_data_reg = 8'd0 ; //待发送数据寄存器
reg [0:0] tx_reg = 1'd1 ; //待发送bit寄存器
/************************************************************/
/*===================-=====输出端口=========================*/
assign tx_done = (now_state == S_STOP && time_cnt == BIT_CYCLE-1) ? 1'b1 : 1'b0;
assign tx_busy = (now_state == S_IDLE) ? 1'b0 : 1'b1;
assign tx_pin = (now_state == S_IDLE) ? 1'b1 : tx_reg;
/************************************************************/
/*====================产生开始信号上升沿====================*/
always@ (posedge clk)
begin
if(now_state == S_IDLE)
start_r2 <= {start_r2[0],tx_begin};
else
start_r2 <= 2'd0;
end
assign start = ~start_r2[1] & start_r2[0];
/************************************************************/
/*======================bit周期计时器=======================*/
always@ (posedge clk)
begin
if(now_state == S_IDLE)
time_cnt <= 32'd0;
else if(time_cnt == BIT_CYCLE - 1'b1)
time_cnt <= 32'd0;
else
time_cnt <= time_cnt + 1'b1;
end
/************************************************************/
/*========================bit计数器=========================*/
always@ (posedge clk)
begin
if(now_state != S_SEND)
bit_cnt <= 3'd0;
else if(time_cnt == BIT_CYCLE - 1'b1)
bit_cnt <= bit_cnt + 1'b1;
end
/************************************************************/
/*======================寄存待发送数据======================*/
always@ (posedge clk)
begin
if(now_state == S_IDLE && nex_state == S_IDLE)
tx_data_reg <= 8'd0;
else if(now_state == S_IDLE && nex_state != S_IDLE)
tx_data_reg <= tx_data;
else
tx_data_reg <= tx_data_reg;
end
/************************************************************/
/*=========================产生输出=========================*/
always@ (posedge clk)
begin
if(now_state == S_IDLE)
tx_reg <= 1'b1;
else if(now_state == S_START)
tx_reg <= 1'b0;
else if(now_state == S_SEND)
tx_reg <= tx_data_reg[bit_cnt];
else if(now_state == S_STOP)
tx_reg <= 1'b1;
end
/************************************************************/
/*=========================状态切换=========================*/
always@ (posedge clk or posedge rst)
begin
if(rst)
now_state <= S_IDLE;
else
now_state <= nex_state;
end
/************************************************************/
/*=======================产生下一状态=======================*/
always@ (*)
begin
case(now_state)
S_IDLE :
begin
if(start)
nex_state = S_START;
else
nex_state = S_IDLE;
end
S_START :
begin
if(time_cnt == BIT_CYCLE - 1'b1)
nex_state = S_SEND;
else
nex_state = S_START;
end
S_SEND :
begin
if(time_cnt == BIT_CYCLE - 1'b1 && bit_cnt == 3'd7)
nex_state = S_STOP;
else
nex_state = S_SEND;
end
S_STOP :
begin
if(time_cnt == BIT_CYCLE - 1'b1)
nex_state = S_IDLE;
else
nex_state = S_STOP;
end
default :begin nex_state = S_IDLE; end
endcase
end
/************************************************************/
endmodule
结束语
本人刚刚入坑FPGA 不久,在代码风格,设计逻辑上还有很多不足之处。欢迎各位大佬指出我的不住之处。