【FPGA】UART串口通信---基于FIFO

这篇具有很好参考价值的文章主要介绍了【FPGA】UART串口通信---基于FIFO。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

前言

我们在上一章完成了UART串口通信的收发模块,这一章我们将FIFO引入进来,使用FIFO进行缓存数据,来连接串口通信的收发模块

一丶FIFO介绍

1.什么是FIFO?

FIFO即First In First Out,是一种先进先出数据存储、缓冲器,我们知道一般的存储器是用外部的读写地址来进行读写,而FIFO这种存储器的结构并不需要外部的读写地址而是通过自动的加一操作来控制读写,这也就决定了FIFO只能顺序的读写数据

2.FIFO分类

同步FIFO
读和写应用同一个时钟。它的作用一般是做交互数据的一个缓冲,也就是说它的主要作用就是一个buffer1

异步FIFO
读写应用不同的时钟,它有两个主要的作用,一个是实现数据在不同时钟域进行传递,另一个作用就是实现不同数据宽度的数据接口。

3.FIFO主要参数

同步FIFO和异步FIFO略有不同,下面的参数适用于两者。

宽度,用参数FIFO_data_size表示,也就是FIFO存储的数据宽度;
深度,用参数FIFO_addr_size表示,也就是地址的大小,也就是说能存储多少个数据;
满标志,full,当FIFO中的数据满了以后将不再能进行数据的写入;
空标志,empty,当FIFO为空的时候将不能进行数据的读出;
写地址,w_addr,由自动加一生成,将数据写入该地址;
读地址,r_addr,由自动加一生成,将该地址上的数据读出;

同步FIFO和异步FIFO的最主要的不同就体现在空满标志产生的方式上,由此引出两者一些不同的参数。
同步FIFO

  • 时钟,clk,rst,读写应用同一个时钟;
  • 计数器,count,用计数器来进行空满标志的判断;

异步FIFO

  • 时钟,clk_w,rst_w,clk_r,rst_r,读写应用不同的时钟;
  • 指针,w_pointer_gray,r_pointer_gray,用指针来判断空满标识;
  • 同步指针,w_pointer_gray_sync,r_pointer_gray_sync,指针的同步操作,用来做对比产生空满标志符;

4.测试

首先配置IP核

【FPGA】UART串口通信---基于FIFO
设置路径,我们一般会在工程目录下创建一个文件夹 ip 用来存放IP核文件

【FPGA】UART串口通信---基于FIFO
【FPGA】UART串口通信---基于FIFO配置参数
【FPGA】UART串口通信---基于FIFO【FPGA】UART串口通信---基于FIFO
正常模式与前显模式:
【FPGA】UART串口通信---基于FIFO

区别:正常模式,输出数据与读请求信号差一个时钟周期;前显模式,将数据放于数据线上,在读请求信号拉高时,在下一个时钟周期,输出FIFO中的第二个数据。

【FPGA】UART串口通信---基于FIFO

【FPGA】UART串口通信---基于FIFO

【FPGA】UART串口通信---基于FIFO
最后这样就成功引入FIFO了
【FPGA】UART串口通信---基于FIFO

5.仿真

调用ip核

module control (
    input   	        clk     ,
    input       	    rst_n   ,
    input	[7:0]       data    ,
    input   	        rdreq   ,
    input           	wrreq   ,
    output         	    empty   ,
    output       	    full    ,
    output	[7:0]       q       ,
    output	[7:0]       usedw
);
fifo	fifo_inst (
	.aclr        ( ~rst_n    ),     //复位信号取反
	.clock       ( clk       ),     //系统时钟  
	.data        ( data      ),     //写入数据     
	.rdreq       ( rdreq     ),     //读使能
	.wrreq       ( wrreq     ),     //写使能
	.empty       ( empty     ),     //fifo为空信号
	.full        ( full      ),     //fifo存满信号
	.q           ( q         ),     //读出数据
	.usedw       ( usedw     )      //可用数据量
	);

endmodule //control

testbench编写

`timescale 1ns/1ps
module tb_control ();
reg            clk      ; 
reg            rst_n    ; 
reg  [7:0]     data     ; 
reg            rdreq    ; 
reg            wrreq    ; 
wire           empty    ; 
wire           full     ; 
wire [7:0]     q        ; 
wire [7:0]     usedw    ; 

control control(
    .clk            (clk    )  ,
    .rst_n          (rst_n  )  ,
    .data           (data   )  ,
    .rdreq          (rdreq  )  ,
    .wrreq          (wrreq  )  ,
    .empty          (empty  )  ,
    .full           (full   )  ,
    .q              (q      )  ,
    .usedw          (usedw  )
);

parameter CYCLE = 20;
always #(CYCLE/2) clk=~clk;
integer i=0,j=0;

initial begin
    clk=1;
    rst_n=1;
    data=0;
    #200.1;
    rst_n=0;   //复位
    rdreq=0;
    wrreq=0;
    #(CYCLE*10);
    rst_n=1;
    #(CYCLE*10)
   //wrreq  50M
    for(i=0;i<256;i=i+1)begin     //因为我们的数据深度设置的是256,所以这里写进去256个数据
        wrreq = 1'b1;//写使能
        data = {$random};
        #CYCLE;
    end
    wrreq = 1'b0;//写完拉低
    #(CYCLE*5);

    //rdreq  50M
    for(j=0;j<256;j=j+1)begin
        rdreq = 1'b1;//读使能
        #CYCLE;
    end
    rdreq = 1'b0;
    #(CYCLE*10);
    

    $stop;
end

endmodule //tb_control

写数据:
【FPGA】UART串口通信---基于FIFO

读数据:

【FPGA】UART串口通信---基于FIFO

二丶UART引入FIFO

思路
首先我们将整个项目分为4个模块

uart_rx:接收模块- - -从上位机接收数据,然后将数据发送给control模块
uart_tx:发送模块- - -从control模块接收数据,然后发送给上位机
control:FIFO缓存模块- - -缓存uart_rx接收的数据并输出给uart_tx
top: 顶层模块

1.模块原理图

【FPGA】UART串口通信---基于FIFO
其中发送模块uart_tx增加了一个ready输出信号,因为发送模块每434个周期发送一位数据,为了防止FIFO不停的输出数据给发送模块,使用ready信号控制FIFO输出数据

2.代码设计

由于只改动了发送模块和新增了control模块,这里只展示这两部分,源码见文章末尾

control:

module control (
    input   	        clk        ,
    input       	    rst_n      ,

    input	[7:0]       dout       ,
    input               dout_vld   ,

    input               ready      , 

    output  [7:0]       din        ,
    output	            din_vld          
);
wire     [7:0]      data ;
wire     	        rdreq;
wire             	wrreq;
wire            	empty;
wire          	    full ;
wire     [7:0]      q    ;
wire     [7:0]      usedw;
reg                 flag ;

assign data=dout;
assign wrreq=dout_vld&&~full;

assign din=q;

//flag
always @(posedge clk or negedge rst_n) begin
    if (!rst_n) begin
        flag<=0;
    end
    else if(usedw>7) begin  //存满8个字节拉高flag
        flag<=1;
    end
    else if (empty) begin
        flag<=0;
    end
end

assign rdreq=flag&&ready&&~empty;  
assign din_vld=rdreq;   //每次将din_vld拉高一个周期,输出一字节数据

fifo	fifo_inst (
	.aclr        ( ~rst_n    ),     //复位信号取反
	.clock       ( clk       ),     //系统时钟  
	.data        ( data      ),     //写入数据     
	.rdreq       ( rdreq     ),     //读使能
	.wrreq       ( wrreq     ),     //写使能
	.empty       ( empty     ),     //fifo为空信号
	.full        ( full      ),     //fifo存满信号
	.q           ( q         ),     //读出数据
	.usedw       ( usedw     )      //可用数据量
	);

endmodule //control

uart_tx:

module uart_tx (
    input  wire         clk,
    input  wire         rst_n,
    input  wire [7:0]   din,
    input  wire         din_vld,
    output reg          tx,
    output reg          ready
);
//定义一个寄存器来锁存 din_vld 时的din
reg [9:0]       data;

//波特率计数器
reg [8:0]       cnt_bps;
wire            add_cnt_bps;
wire            end_cnt_bps;

//比特计数器
reg [4:0]       cnt_bit;
wire            add_cnt_bit;
wire            end_cnt_bit;

reg             flag;   //计数器开启标志位

parameter       BPS_115200=434;  //发送一bit数据需要的周期数

//data
always @(posedge clk or negedge rst_n) begin
    if (!rst_n) begin
        data<=0;
    end
    else if(din_vld) begin
        data<={1'b1,din,1'b0};   //拼接起始位和停止位
    end
    else
        data<=data;
end

//发送数据 tx
always @(posedge clk or negedge rst_n) begin
    if (!rst_n) begin
        tx<=1'b1;
    end
    else if(cnt_bps==1) begin
        tx<=data[cnt_bit];
    end
end

//flag
always @(posedge clk or negedge rst_n) begin
    if (!rst_n) begin
        flag<=0;
    end
    else if(din_vld) begin
        flag<=1;
    end
    else if(end_cnt_bit) begin   //发送完成关闭计数器
        flag<=0;
    end
    else
        flag<=flag;
end

//ready
always @(*) begin
    if (!rst_n) begin
        ready<=1;
    end
    else if(flag) begin
        ready<=0;
    end
    else begin
        ready<=1;
    end
end

//cnt_bps
always @(posedge clk or negedge rst_n) begin
    if (!rst_n) begin
        cnt_bps<=0;
    end
    else if(add_cnt_bps) begin
        if (end_cnt_bps) begin
            cnt_bps<=0;
        end
        else
            cnt_bps<=cnt_bps+1;
    end
end

assign add_cnt_bps=flag;
assign end_cnt_bps=add_cnt_bps&&cnt_bps==BPS_115200-1;

//cnt_bit
always @(posedge clk or negedge rst_n) begin
    if (!rst_n) begin
        cnt_bit<=0;
    end
    else if(add_cnt_bit) begin
        if (end_cnt_bit) begin
            cnt_bit<=0;
        end
        else
            cnt_bit<=cnt_bit+1;
    end
end

assign add_cnt_bit=end_cnt_bps;
assign end_cnt_bit=add_cnt_bit&&cnt_bit==9;

endmodule //uart_tx

3.仿真与分析

testbench:

`timescale 1ns/1ps
module tb_uart ();
reg         clk;
reg         rst_n;
reg [7:0]   din;
reg         din_vld;
wire        tx_r;   //用来连接上位机的tx和从机的rx
wire        rx;

parameter       CYCLE=20;

//例化从机(顶层模块,包含了一个uart_rx和一个uart_tx)
uart uart(
    .clk            (clk),
    .rst_n          (rst_n),
    .rx             (tx_r),    //接收
    .tx             (tx)     //发送
);

//例化上位机(用来给从机发送数据)
uart_tx uart_tx(
    .clk              (clk),
    .rst_n            (rst_n),
    .din              (din),
    .din_vld          (din_vld),
    .tx               (tx_r)
);

always #(CYCLE/2)  clk=~clk;

initial begin
    clk=1;
    rst_n=1;
    #200;
    rst_n=0;
    din_vld=0;
    #(CYCLE*10);
    rst_n=1;

    send(8'h11);
    send(8'h22);
    send(8'h33);
    send(8'h44);
    send(8'h55);
    send(8'h66);
    send(8'h77);
    send(8'h88);
    #2000000;
    $stop;

end

task send;
    input [7:0] send_data;
    begin
        din=send_data;
        din_vld=1;
        #CYCLE;
        din_vld=0;
        #(CYCLE*434*22);
    end
endtask

endmodule //tb_uart_tx

分析:
【FPGA】UART串口通信---基于FIFO
1.上位机发送数据到FPGA之后由FPGA的接收模块将数据dout数据有效信号dout_vld输出给FIFO缓存
2.dout_vld作为写使能信号,在写使能开启的时候存储dout
【FPGA】UART串口通信---基于FIFO
3.在FIFO中存储的数据大于7个的时候开启读使能,因为FIFO模式设置的前显模式,所以在读使能生效前,第一位数据就有效了,也就是时序图中的q信号:8’h11

【FPGA】UART串口通信---基于FIFO

然后来看发送数据:
【FPGA】UART串口通信---基于FIFO
箭头处,din_vld拉高一个周期,目的是为了在我们发送完一帧数据之前,只锁存一次数据,保证发送一帧数据期间数据不改变,将数据din拼接起始位和停止位锁存到data

三丶上板验证

因为设置的FIFO存储满8个数据才开始读数据,所以这里看到发送8’h88之后才收到数据!!!

四丶源码

https://github.com/xuranww/uart_fifo.git

参考文章:
1.https://www.cnblogs.com/xuqing125/p/8337586.html
2.https://blog.csdn.net/QWERTYzxw/article/details/121295258


  1. 缓冲区(Buffer)就是在内存中预留指定大小的存储空间用来对I/O的数据做临时存储,这部分预留的内存空间叫缓冲区。
    使用缓冲区有两个好处:
    ①减少实际物理读写次数
    ②缓冲区在创建时就被分配内存,这块内存区域一直被重用,可以减少动态分配和回收内存的次数 ↩︎文章来源地址https://www.toymoban.com/news/detail-412761.html

到了这里,关于【FPGA】UART串口通信---基于FIFO的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【FPGA】UART串口通信——奇偶校验实现

    奇偶校验位是基于uart的数据上进行一个判断 奇校验:数据1个数为奇时,校验为0,反之为1 偶校验:数据0个数为偶时,校验为0,反之为1 Uart回环在之前已经实现,现在需要基于uart增加一个奇偶校验位的需求 uart及代码:https://blog.csdn.net/weixin_59150966/article/details/128005066?spm=10

    2024年02月11日
    浏览(32)
  • FPGA 串口通信(uart)初探篇(含源代码)

    一、UART 串口通信,说实话是日常生活中很常见的一种9针型的主机和显示屏之间的通信模式。本次博客,只为自己复盘相关知识,初学者,错误较多,请多指教。所以本文参考(一)FPGA之串口通信(UART)_fpga uart-CSDN博客 而编著的。 FPGA是一种可编程的逻辑器件,用于各种数字电

    2024年04月14日
    浏览(28)
  • (五)零基础学懂FPGA中的串口通信(UART)

    此篇为专栏 《FPGA学习笔记》 的第五篇,记录我的学习FPGA的一些开发过程和心得感悟,刚接触FPGA的朋友们可以先去此专栏置顶 《FPGA零基础入门学习路线》来做最基础的扫盲。 本篇内容基于笔者实际开发过程和正点原子资料撰写,将会详细讲解此FPGA实验的全流程, 诚挚 地

    2024年02月04日
    浏览(40)
  • uart串口环回(加FIFO)

    UART(universal asynchronous receiver-transmitter)是一种采用异步串行通信方式的通用异步收发传输器。 定义如上,那么出现问题了,什么叫异步串行通信?请关注下文原理。 1.1同步通信 发送方发送出数据后,等接收方响应后才发送下一个数据 1.2异步通信 发送方发出数据后,不等接

    2024年01月24日
    浏览(29)
  • 基于FPGA的超声波测距——UART串口输出

    环境: 1、Quartus18.0 2、vscode 3、板子型号:EP4CE10F17C8 4、超声波模块:HC_SR04 要求: 使用 EP4CE10F17C8开发板驱动 超声波检测模块(HC_SR04 ),并将所测得数据显示到串口助手上。 HC-SR04超声波测距模块可提供2cm-400cm的非接触式距离感测功能,测距精度可达高到3mm;模块包括超声波发

    2024年02月14日
    浏览(33)
  • 【单片机】基于STM32的UART串口通信

    简单讲解一下UART通信协议,以及UART能够实现的一些功能,还有有关使用STM32CubeMX来配置芯片的一些操作。实验内容基于 正点原子精英板 开发板,单片机芯片为 STM32F103ZET6 。 在后面我会以我使用的STM32F429开发板来举例讲解(其他STM32系列芯片大多数都可以按照这些步骤来操作

    2024年01月17日
    浏览(56)
  • 基于树莓派4B与STM32的UART串口通信实验(代码开源)

    前言: 本文为手把手教学 树莓派4B与 STM32 的 UART 通讯 ,本次项目采用 树莓派4B 与  STM32  进行串口通讯,将彼此的数据进行互相传输。本篇博客同时提供了基于 YOLOv5-Lite 的目标检测数据联动,即将 树莓派4B 检测到的信息发送至 STM32 ,后续可以通过这些信息进行各种需求上

    2024年02月16日
    浏览(28)
  • UART串口通信(回环测试)

            UART(Universal Asynchronous Receiver-Transmitter)是采用异步串行通信方式的通用异步收发传输器,在发送数据时将并行数据转换为串行数据,在接收数据时将串行数据转换为并行数据。发送和接收的一帧数据由起始位、数据位、奇偶校验位和停止位组成,其数据帧格式如

    2024年02月05日
    浏览(48)
  • UART 串口通信

    第18.1讲 UART串口通信原理讲解_哔哩哔哩_bilibili 并行通信 一个周期同时发送8bit的数据,占用引脚资源多 串行通信 串行通信的通信方式: 同步通信 同一时钟下进行数据传输 异步通信 发送设备和接收设备的时钟不同 但是 需要约束波特率 (1s内传输的bit数) 串行通信的传输方

    2024年02月02日
    浏览(33)
  • UART串口通信实验

    不管是单片机开发还是嵌入式 Linux 开发,串口都是最常用到的外设。 可以通过串口将开发板与电脑相连,然后在电脑上通过串口调试助手来调试程序。 还有很多模块,比如蓝牙、GPS、GPRS等都使用串口与主控进行通信。 串口全称串行接口,通常也叫做COM接口,,串行接口指

    2024年02月08日
    浏览(43)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包