前言
此FPGA系列文章内容均基于b站小梅哥2020年发布的系列课程【零基础轻松学习FPGA】小梅哥Xilinx FPGA基础入门到项目应用培训教程_哔哩哔哩_bilibili
其中串口部分内容参考2023年新课程
小梅哥2023全新ZYNQ FPGA Verilog数字逻辑设计与验证--0基础手把手学FPGA系列--小梅哥Xilinx ZYNQ_哔哩哔哩_bilibili
截至此篇笔记发布时,小梅哥csdn主页的笔记只同步到了第10节串口通信发送逻辑,而无后续记录。本人为了在FPGA学习过程中能够留下些痕迹,方便回顾巩固,也是为各位FPGA学习者提供一份参照,故编写此系列文章。以下内容均为个人总结,如有不正确之处欢迎各位评论指正,共同学习。
设计要点
1.模块化设计:先设计一个通用uart接收模块,再在上层模块中将其例化,
2.采样点问题:与uart发送数据相同,用一个分频计数器对每一位数据进行计数。而由于上升沿、下降沿均占有一定时间,故应在每一位数据的计数中点取出数据,保证数据传输的准确性。
3.检测起始位下降沿:如何检测串口接收数据起始位的下降沿,以使得分频计数器开始计数?
下降沿:高电平 → 低电平
上升沿:低电平 → 高电平
FPGA中有专门的逻辑电路可以实现:
4.如何设计使能信号:
①下降沿检测电路检测到起始位下降沿时使能,En=1。10位数据全部接受完成后结束使能En=0
②毛刺干扰信号情况:在采样起始时刻,若中点采样结果为高电平,说明之前检测到的下降沿只是个毛刺,为干扰信号,应当不使能,En=0;
5.位计数器逻辑
6.位接收逻辑:
7.数据接收完成后,产生一个结束标志信号
8.亚稳态:在常规的下降沿检测电路中用D触发器实现,而输入信号D端的uart_rx是外部输入,不受时钟控制,有可能输uart_rx的时刻恰好很接近时钟上升沿或与之重合,这就会导致D触发器无法准确判断当前uart_rx的值,逻辑判断出现问题,工作不稳定。因此在下降沿检测电路的D触发器前,应当再加上两个D触发器,进行同步(打拍),就能够将该信号同步到D触发器的时钟域。
(亚稳态及其解决方法在后续课程会详细介绍)
uart_rx接收模块代码实现
设计文件
`timescale 1ns / 1ps
module uart_byte_rx(
Clk,
Reset_n,
uart_rx,
Data,
Rx_Done
);
input Clk;
input Reset_n;
input uart_rx;
output reg [7:0]Data;
output reg Rx_Done;
reg [29:0]baud_div_count;
reg [3:0]bit_cnt;
reg En;
wire w_Rx_Done;
reg r_uart_rx;//D触发器
wire nedge_uart_rx;
reg dff0_uart_rx , dff1_uart_rx;
reg [7:0]r_Data; //八位寄存器,用于临时寄存8位uart_data,待8位数据全部接收完毕后,再从该寄存器中一起输出
parameter BAUD = 9600;
parameter CLOCK_FREQ = 50_000_000;
parameter MCNT_BAUD = CLOCK_FREQ / BAUD - 1;
//波特分频计数器
always@(posedge Clk or negedge Reset_n)
if(!Reset_n)
baud_div_count <= 0;
else if(En)begin //使能端有效
if(baud_div_count == MCNT_BAUD)
baud_div_count <= 0;
else
baud_div_count <= baud_div_count + 1'd1;
end
else //使能端无效
baud_div_count <= 0;
//下降沿检测逻辑
always@(posedge Clk)
dff0_uart_rx <= uart_rx;
always@(posedge Clk)
dff1_uart_rx <= dff0_uart_rx;
always@(posedge Clk)
r_uart_rx <= dff1_uart_rx;
assign nedge_uart_rx = (dff1_uart_rx == 0) && (r_uart_rx == 1);
//使能信号逻辑 注意:毛刺干扰也不能计数
always@(posedge Clk or negedge Reset_n)
if(!Reset_n)
En <= 0;
else if(nedge_uart_rx)
En <= 1;
else if((bit_cnt == 9)&&(baud_div_count == MCNT_BAUD)) //10位数据接收完成
En <= 0;
else if((bit_cnt == 0)&&(baud_div_count == MCNT_BAUD/2)&&(dff1_uart_rx == 1)) //起始时有毛刺
En <= 0;
//位计数器
always@(posedge Clk or negedge Reset_n)
if(!Reset_n)
bit_cnt <= 0;
else if(baud_div_count == MCNT_BAUD)begin
if(bit_cnt == 9)
bit_cnt <= 0;
else
bit_cnt <= bit_cnt + 1'd1;
end
//接收完毕停止计数
always@(posedge Clk or negedge Reset_n)
if(!Reset_n)
En <= 0;
else if(((bit_cnt == 9)&&(baud_div_count == MCNT_BAUD)))
En <= 1;
//数据位接收逻辑(串转并)
always@(posedge Clk or negedge Reset_n)
if(!Reset_n)
Data <= 8'd0;
else if(baud_div_count == MCNT_BAUD/2)begin //中点处采样
case(bit_cnt)
1:r_Data[0] <= dff1_uart_rx;
2:r_Data[1] <= dff1_uart_rx;
3:r_Data[2] <= dff1_uart_rx;
4:r_Data[3] <= dff1_uart_rx;
5:r_Data[4] <= dff1_uart_rx;
6:r_Data[5] <= dff1_uart_rx;
7:r_Data[6] <= dff1_uart_rx;
8:r_Data[7] <= dff1_uart_rx;
default:r_Data <= r_Data;
endcase
end
// 接收完成标志信号
assign w_Rx_Done = (bit_cnt == 9)&&(baud_div_count == MCNT_BAUD);
always@(posedge Clk)
Rx_Done <= w_Rx_Done;
always@(posedge Clk)
if(w_Rx_Done)
Data <= r_Data;
endmodule
仿真文件
需要模拟产生串行uart_rx信号发送进入的过程,让接收模块进行接收。
`timescale 1ns / 1ps
module uart_byte_rx_tb( );
reg Clk;
reg Reset_n;
reg uart_rx;
wire Rx_Done;
wire [7:0]Data;
uart_byte_rx uart_byte_rx(
.Clk(Clk),
.Reset_n(Reset_n),
.uart_rx(uart_rx),
.Data(Data),
.Rx_Done(Rx_Done)
);
initial Clk = 1;
always #10 Clk = ~Clk;
initial begin
Reset_n = 0;
uart_rx = 1;
#201;
Reset_n = 1;
#200;
uart_rx = 0; #(5208*20); //起始位 //5208是波特分频计数器计数一轮的次数
uart_rx = 1; #(5208*20) //发送8'b0101_0101(注意右边是低位) bit0
uart_rx = 0; #(5208*20) //bit1
uart_rx = 1; #(5208*20)//bit2
uart_rx = 0; #(5208*20)//bit3
uart_rx = 1; #(5208*20)//bit4
uart_rx = 0; #(5208*20)//bit5
uart_rx = 1; #(5208*20)//bit6
uart_rx = 0; #(5208*20)//bit7
uart_rx = 1; #(5208*20)//停止位
#(5208*20*10);
uart_rx = 0; #(5208*20); //起始位
uart_rx = 0; #(5208*20) //发送8'b1010_1010
uart_rx = 1; #(5208*20)
uart_rx = 0; #(5208*20)
uart_rx = 1; #(5208*20)
uart_rx = 0; #(5208*20)
uart_rx = 1; #(5208*20)
uart_rx = 0; #(5208*20)
uart_rx = 1; #(5208*20)
uart_rx = 1; #(5208*20)//停止位
#(5208*20*10);
uart_rx = 0; #(5208*20); //起始位
uart_rx = 0; #(5208*20) //发送8'b1111_0000
uart_rx = 0; #(5208*20)
uart_rx = 0; #(5208*20)
uart_rx = 0; #(5208*20)
uart_rx = 1; #(5208*20)
uart_rx = 1; #(5208*20)
uart_rx = 1; #(5208*20)
uart_rx = 1; #(5208*20)
uart_rx = 1; #(5208*20)//停止位
#(5208*20*10);
uart_rx = 0; #(5208*20); //起始位
uart_rx = 1; #(5208*20) //发送8'b0000_1111
uart_rx = 1; #(5208*20)
uart_rx = 1; #(5208*20)
uart_rx = 1; #(5208*20)
uart_rx = 0; #(5208*20)
uart_rx = 0; #(5208*20)
uart_rx = 0; #(5208*20)
uart_rx = 0; #(5208*20)
uart_rx = 1; #(5208*20)//停止位
#(5208*20*10);
$stop;
end
endmodule
仿真波形
任务实现(例化)
`timescale 1ns / 1ps
module uart_byte_rx_test(
Clk,
Reset_n,
uart_rx,
Data,
Rx_Done,
Led
);
input Clk;
input Reset_n;
input uart_rx;
output reg [7:0]Data;
output reg Rx_Done;
output reg Led;
uart_byte_rx_test uart_byte_rx(
.Clk(Clk),
.Reset_n(Reset_n),
.uart_rx(uart_rx),
.Data(Data),
.Rx_Done(Rx_Done)
);
//波特率重定义
defparam uart_bytr_rx.BAUD = 115200;
//LED 翻转逻辑
always@(posedge Clk or negedge Reset_n)
if(!Reset_n)
Led <= 0;
else if(Rx_Done)
Led <= ~Led;
endmodule