小梅哥Xilinx FPGA学习笔记11—串口接收逻辑的Verilog设计与验证

前言

此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

01 试看小梅哥fpga设计思想验证方法视频:http://bbs.elecfans.com/jishu_484523_1_1.html 02 芯航线FPGA开发平台设计初衷:http://bbs.elecfans.com/jishu_514523_1_1.html 03 芯航线FPGA学习平台介绍:http://bbs.elecfans.com/jishu_514526_1_1.html 04 芯航线FPGA学习平台焊接记录:http://bbs.elecfans.com/jishu_514914_1_1.html 05 芯航线FPGA学习平台系统调试:http://bbs.elecfans.com/jishu_515169_1_1.html 06【连载视频教程(一)】科学的开发流程:http://bbs.elecfans.com/jishu_515340_1_1.html 07【连载视频教程(二)】3-8译码器设计验证:http://bbs.elecfans.com/jishu_515468_1_1.html 08【连载视频教程(三)】二进制计数器设计验证:http://bbs.elecfans.com/jishu_515476_1_1.html 09【连载视频教程(四)】高性能计数器IP核使用:http://bbs.elecfans.com/jishu_515714_1_1.html 10【连载视频教程(五)】BCD码计数器设计验证:http://bbs.elecfans.com/jishu_515893_1_1.html 11【连载视频教程(六)】例解阻塞赋值非阻塞赋值:http://bbs.elecfans.com/jishu_516094_1_1.html 12【连载视频教程(七)】例说状态机:http://bbs.elecfans.com/jishu_516249_1_1.html 13【连载视频教程(八)】基于状态机的独立按键消抖:http://bbs.elecfans.com/jishu_516791_1_1.html 14【连载视频教程(九)】独立按键控制LED亚稳态问题引入:http://bbs.elecfans.com/jishu_516795_1_1.html 15【连载视频教程(十)】数码管动态扫描设计实现:http://bbs.elecfans.com/jishu_516979_1_1.html 16【连载视频教程(十一)】UART串口发送模块设计验证:http://bbs.elecfans.com/jishu_516984_1_1.html 17【连载视频教程(十二)】UART串口接收模块设计验证:http://bbs.elecfans.com/jishu_518308_1_1.html 18【芯航线FPGA学习平台众筹进度帖】芯航线FPGA开发板入手测试说明:http://bbs.elecfans.com/jishu_518301_1_1.html 19【芯航线FPGA学习平台众筹进度帖】芯航线FPGA学习套件配置文件固化教程 http://bbs.elecfans.com/jishu_518816_1_1.html 20【芯航线FPGA学习平台众筹进度帖】芯航线FPGA学习套件众筹回报已发放完毕,特公布运单号 http://bbs.elecfans.com/jishu_518819_1_1.html 21【芯航线FPGA学习平台众筹进度帖】芯航线FPGA学习套件众筹总结暨抽奖结果 http://bbs.elecfans.com/jishu_520454_1_1.html 22【芯航线FPGA学习平台众筹进度帖】【有奖活动】芯航线FPGA学习平台有奖活动第一季——看教程,秀笔记,赢好礼 http://bbs.elecfans.com/jishu_520463_1_1.html 23【连载视频教程(十三)】嵌入式块RAM应用之双口RAM:http://bbs.elecfans.com/jishu_520914_1_1.html 24【连载视频教程(十四)】搭建串口收发存取双口RAM简易应用系统: http://bbs.elecfans.com/jishu_520915_1_1.html 25【连载视频教程(十五)】ROM的搭建Signaltap II 工具使用:http://bbs.elecfans.com/jishu_522774_1_1.html 26【连载视频教程(十六)】FIFO介绍时序验证:http://bbs.elecfans.com/jishu_522778_1_1.html 27【连载视频教程(十七)】使用PLL进行设计+Verilog参数化设计介绍: http://bbs.elecfans.com/jishu_524442_1_1.html 28【芯航线FPGA学习平台众筹进度帖】芯航线FPGA学习套件下载器驱动安装说明 http://bbs.elecfans.com/jishu_524793_1_1.html 29【芯航线FPGA学习平台众筹进度帖】Verilog语法基础讲解之参数化设计: http://bbs.elecfans.com/jishu_528770_1_1.html 30【连载视频教程(十八)】基于线性序列机设计思想的串行DAC(TLC5620)驱动: http://bbs.elecfans.com/jishu_533944_1_1.html 31【连载视频教程(十九)】基于线性序列机设计思想的串行ADC驱动: http://bbs.elecfans.com/jishu_537056_1_1.html
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值