同步FIFO和异步FIFO

FIFO分为一个同步FIFO,一个异步FIFO,FIFO有读口和写口

读写时钟是一个,就是同步FIFO;读写时钟不是一个,异步FIFO

  • IP核设计中,一般使用同步FIFO设计
  • SOC设计或者跨时钟域的内容使用异步FIFO

RAM

FIFO中的数据,存储在寄存器中或者是SRAM中

  • 数据少--存储在寄存器中
  • 数据多--使用SRAM
  • 一般而言fab会提供RAM仿真的模型,仿真库和物理库

    设计实现一个16*8的双端口RAM



RAM在时钟的驱动下,可以通过一个端口写数据,从另外一个端口读取数据,并且读写可以同时进行,如果要写入数据,首先要有写入的数据,并且要知道写到什么地方,要知道写的地址,RAM不可能一直在写,所以要有写使能;读的时候也要知道从什么地方读,有读的地址,有读使能决定什么时候读,读取出来得到数据

  • RAM的位宽,写成参数化的形式,能参数化的部分,全部进行参数化parameter

  • 定义完接口数据之后,定义内部;数据是存放在一个存储器中的,RTL中是不能直接生成存储器的,需要使用代码进行模拟,实际综合的时候,将module代码替换为SRAM,SRAM是一个已经综合好的硬件,可以直接进行使用
  • 首先定义存储体,使用reg或者logic类型
  1. reg/logic [RAM_WIDTH-1:0] memory[RAM_DEPTH-1:0]; //memory存储体
  • 写操作,在写使能的驱动下,根据写地址,将数据赋值到写地址中,得到的是寄存器组
  • 读操作,从entrance中,根据读地址,将memory中的数据读取出来,read_data需要尽心输出寄存的
  • 实际上生成的是一个总容量为128bit的寄存器,数据比较多的时候,使用SRAM存储数据,面积会更小;数据量比较少的时候,使用寄存器,面积也不会大

  1. always @ (posedge clk) begin
  2. if(read_en)
  3. read_data <= memory[read_addr]
  4. end
  5. // SRAM实现
  6. wire [DATA_WIDTH-1:0] read_data_nxt;
  7. assign read_data_nxt = memeory[read_addr];
  8. always @ (posedge clk) begin
  9. if(read_en)
  10. read_data <= read_data_nxt;
  11. end
  12. // 寄存器实现
  13. wire [DATA_WIDTH-1:0] read_data_nxt;
  14. assign read_data_nxt = memeory[read_addr];
  • SRAM的时序是在读使能的下一个cycle,得到读数据;用register实现fifo,可以使用组合逻辑,在当前的cycle得到数据

FIFO设计

  • 先写存储体,可以使用register或者sram搭建
  • FIFO control,FIFO除了时钟和复位,还有写端口和读端口
  • FIFO 是先进先出的队列,先写进去的数据,读的时候先读出来;FIFO中的数据存储在存储体中,类似于SRAM,有写数据,写使能,读使能,读数据,但是没有读地址和写地址,读地址和写地址是由FIFO内部的功能模块,FIFO control功能模块进行控制
  • 端口:读写使能\读写数据,没有读地址和写地址
  • FIFO原则:满不能写,空不能读,取决于full和empty信号,上游模块看到full信号,不向里面写,下游模块看到empty之后不读,FIFO自己也需要进行保护



    FIFO 端口

  • Fcounter,当前FIFO中有效的数据量,写入一个数据,加一,读出一个数据为0,在标准的FIFO中是没有的
  1. module SYNCFIFO(
  2. Fifo_rst, //async reset
  3. Clock, // write and read clock
  4. Read_enable,
  5. write_enable,
  6. Read_data,
  7. write_data,
  8. Full, // full flag
  9. Empty, // empty flag
  10. Fcounter // count the number of data in FIFO
  11. );
  12. endmodule

FIFO control

  • FIFO control 最主要的就是维护两个地址,读地址和写地址的产生;FIFO control需要有读使能和写使能,读地址和写地址是直接连到SRAM中的,不用经过FIFO control,空满信号也是FIFO control产生的
  • FIFO control 需要有读写使能,空满信号,读指针rp和写指针wp

  • 空满状态使用计数器,计算存储器的容量

  • counter = 3时,来一个写使能,就是再写入一个数据,counter = 4,full从0跳变为1
  • counter = 1时,来一个读使能,再读取一个数据,counter = 0,empty从0跳变为1

    FIFO实现的两种方法
  1. 通过计数器产生空满信号,执行一次写操作,计数器加1;执行一次读操作,计数器减1;
  2. 通过扩展地址位,用最高位判断空满状态
  1. // 方法1
  2. paramter DATA_WIDTH = 8;
  3. parameter ADDR_WIDTH = 9; // 2^9 512 这里设计的是512*8的RAM
  4. input Fifo_rst;
  5. input Clock;
  6. input Read_enable;
  7. input Write_enable;
  8. input [DATA_WIDTH-1:0] Write_data;
  9. output [DATA_WIDTH-1:0] Read_data;
  10. output Full;
  11. output Empty;
  12. output [ADDR_WIDTH-1:0] Fcounter;
  13. reg [DATA_WIDTH-1:0] Read_data;
  14. reg Full;
  15. reg Empty;
  16. // reg [ADDR_WIDTH-1:0] Fcounter;
  17. reg [ADDR_WIDTH] Fcounter;
  18. reg [ADDR_WIDTH-1:0] Reader_addr; // read_address,FIFO control传递给memory的
  19. reg [ADDR_WIDTH-1:0] write_addr; // write_address,FIFO control传递给memory的
  20. wire Read_allow = (Read_enable && !Empty); // 读使能来的时候并且非空,才能读
  21. wire Write_allow = (Write_enable && !FUll); // 写使能来的时候并且非满,才能写

RTL Coding

  1. // 例化 RAM 并连接端口
  2. DUALRAM U_RAM (
  3. .Read_clock(Clock),
  4. .Write_clock(Clock), // 同步复位时钟,使用同一个clock
  5. .Read_allow(Read_allow),
  6. .Write_allow(Write_allow),
  7. .Read_addr(Read_addr),
  8. .Write_addr(Write_addr),
  9. .Write_data(Write_data),
  10. .Read_data(Read_data)
  11. );
  1. // 空信号产生
  2. always @(posedge Clock or posedge Fifo_rst) begin
  3. if(Fifo_rst)
  4. Empty <= 'b1; // 时钟复位的时候 empty=1s
  5. else
  6. Empty <= (!Write_allow && (Fcounter[8:1] == 8'h0)&&(Fcounter[0] ==0||Read_allow));
  7. end
  • 只要有写使能,Empty就为0,!Write_allow表示写使能无效的时候
  • Fcounter[8:1] = 0,表示第1位到第9位都为0
  • Fcounter[0] = 0,因为与的关系,所以Fcounter = 0000_0000_0,FIFO无数据,empty = 1;
  • Fcounter[0] = 1,因为与的关系,所以Fcounter = 0000_0000_1,FIFO有一个数据,此时如果Read_allow来临,FIFO也无数据,empty = 1
  1. // 最后一句的写法
  2. empty <= Write_allow ? 1'b0 :
  3. Read_allow ? (Fcounter == 9'd1):(Fcounter == 9'd0);
  4. // Write_allow 为 1 empty = 0
  5. // Write_allow 为0 empty = Read_allow ? (Fcounter == 9'd1):(Fcounter == 9'd0);
  6. // Read_allow = 1 empty = Fcounter == 9'd1
  7. // 读使能来之后,判断Fcounter是不是等于1,是empty=1,不是empty=0
  8. // Read_allow = 0 empty = Fcounter == 9'd0
  9. // 读使能为0,判断Fcounter是不是等于0,是empty=1,不是empty=0
  10. // 这种方法使用了两个9bit比较器,面积大,不推荐使用
  1. // Full 信号产生
  2. always @(posedge clock or posedge Fifo_rst) begin
  3. if(Fifo_rst)
  4. Full <= 'b0;
  5. else
  6. Full <= (!Read_allow && (Fcounter[8:] == 8'hFF) &&((Fcounter[0]==1 || Write_allow)));
  7. end
  • 分析方法与empty信号产生相同
  • 这里Full信号取值范围为0-511(1111_1111_1),9位的二进制数,取不到512,所以定义Full的时候要用10位
  1. reg [ADDR_WIDTH] Fcounter; // 直接是10bit的位宽
  2. Full <= Read_allow ? 1'b0 :
  3. Write_allow ? (Fcounter == 10'd511) : (Fcounter == 10'd512);
  1. // 地址产生
  2. always @(posedge Clock or posedge Fifo_rst) begin
  3. if(Fifo_rst)
  4. Read_addr <= 'h0
  5. else if(Read_allow)
  6. Read_addr <= Read_addr + 'b1;
  7. end
  8. always @(posedge Clock or posedge Fifo_rst) begin
  9. if(Fifo_rst)
  10. Write_addr <= 'h0;
  11. else
  12. write_addr <= Write_addr + 'b1;
  13. end
  1. // Fcounter
  2. always @(posedge Clock or posedge Fifo_rst) begin
  3. if(Fifo_rst)
  4. Fcounter <= 'h0;
  5. else if((!Read_allow && !Write_allow) || (Read_allow && Write_allow))
  6. begin
  7. if(Write_allow)
  8. Fcounter <= Fcounter + 'b1;
  9. else
  10. Fcounter <= Fcounter - 'b1;
  11. end
  12. end

FIFO的第二种实现方法

  • 扩展地址位,使用最高位表示空和满的情况





  • 使用低位进行寻址

  1. module sync_fifo(clk,rst,wr_en,rd_en,data_in,data_out,empty,full)
  2. input clk,rst,wr_en,rd_en
  3. input [7:0] data_in;
  4. output empty,full;
  5. output [7:0] data_out
  6. reg [7:0] mem[15:0]; //16*8 RAM
  7. reg [7:0] data_out;
  8. wire [3:0] w_addr,r_addr; // 表示寻址
  9. reg [4:0] r_addr_a,w_addr_a; // 表示产生地址
  10. assign r_addr = r_addr_a[3:0];
  11. assign w_addr = w_addr_a[3:0];
  12. always@(posedge clk or negedge rst)
  13. begin
  14. if(!rst)
  15. begin
  16. r_addr_a = 5'b0;
  17. end
  18. else
  19. begin
  20. if (rd_en == 1 && empty == 0)
  21. begin
  22. data_out <=mem[r_addr];
  23. r_addr_a <= r_addr_a+1;
  24. end
  25. end
  26. end
  27. always@(posedge clk or negedge rst)
  28. begin
  29. if(!rst)
  30. begin
  31. w_addr_a = 5'b0;
  32. end
  33. else
  34. begin
  35. if (wr_en == 1 && full == 0)
  36. begin
  37. mem[w_addr] <=data_in;
  38. w_addr_a <= w_addr_a+1;
  39. end
  40. end
  41. end
  42. assign empty = (r_addr_a == w_addr_a) ? 0 : 1;
  43. assign full = (r_addr_a[4] != w_addr_a[4] && r_addr_a[3:0] == w_addr_a[3:0])?1:0;
  44. endmodule

12-Verilog-同步FIFO设计的更多相关文章

  1. 同步fifo的verilogHDL设计实例

    原创 设计一个fifo,输入16bit,输出16bit的data,寻址宽度5bit,有空满标志. top 层如下所示: /* date : 2014/10/14 version : modelsim ...

  2. Verilog学习笔记简单功能实现(八)...............同步FIFO

    Part 1,功能定义: 用16*8 RAM实现一个同步先进先出(FIFO)队列设计.由写使能端控制该数据流的写入FIFO,并由读使能控制FIFO中数据的读出.写入和读出的操作(高电平有效)由时钟的上 ...

  3. 怎么用Verilog语言描述同步FIFO和异步FIFO

    感谢 知乎龚大佬 打杂大佬 网上几个nice的博客(忘了是哪个了....) 前言 虽然FIFO都有IP可以使用,但理解原理还是自己写一个来得透彻. 什么是FIFO? Fist in first out ...

  4. 同步fifo的Verilog实现

    FIFO是一种先进先出的数据缓存器,他与普通存储器相比: 优点:没有外部读写地址线,这样使用起来非常简单: 缺点:只能顺序写入数据,顺序的读出数据, 其数据地址由内部读写指针自动加1完成,不能像普通存 ...

  5. 基于FPGA的异步FIFO设计

    今天要介绍的异步FIFO,可以有不同的读写时钟,即不同的时钟域.由于异步FIFO没有外部地址端口,因此内部采用读写指针并顺序读写,即先写进FIFO的数据先读取(简称先进先出).这里的读写指针是异步的, ...

  6. 同步FIFO学习

    在网上找的一个经典同步FIFO例子. 一.前言 FIFO (First-In-First-Out) 是一种先进先出的数据交互方式,在数字ASIC设计中常常被使用.FIFO按工作时钟域的不同又可以分为: ...

  7. 同步FIFO design and IP level verification

    一.前言 应聘IC前端相关岗位时,FIFO是最常考也是最基本的题目.FIFO经常用于数据缓存.位宽转换.异步时钟域处理.随着芯片规模的快速增长,灵活的system verilog成为设计/验证人员的基 ...

  8. 同步fifo与异步fifo

    参考以下帖子: https://blog.csdn.net/hengzo/article/details/49683707 https://blog.csdn.net/Times_poem/artic ...

  9. FPGA Asynchronous FIFO设计思路

    FPGA Asynchronous FIFO设计思路 将一个多位宽,且在不停变化的数据从一个时钟域传递到另一个时钟域是比较困难的. 同步FIFO的指针比较好确定,当FIFO counter达到上限值时 ...

  10. FIFO设计思考之一

    不管同步FIFO还是异步FIFO,设计难点是full/empty状态flag的正确性. 要保证任何情况 FULL时NO WRITE,EMPTY时NO READ.overflow / underflow ...

随机推荐

  1. 【笔记整理】[案例]使用正则表达式来提取36Kr新闻

    import datetime import json import re import requests class Kr36(object): def __init__(self): self.u ...

  2. 袋鼠云数栈前端从 Multirepo 到 Monorepo 研发效率提升探索之路

    我们是袋鼠云数栈 UED 团队,致力于打造优秀的一站式数据中台产品.我们始终保持工匠精神,探索前端道路,为社区积累并传播经验价值. 本文作者:星野 困境频生前端代码管理何解? 前端代码管理一直是困扰着 ...

  3. 《HelloGitHub》第 93 期

    兴趣是最好的老师,HelloGitHub 让你对编程感兴趣! 简介 HelloGitHub 分享 GitHub 上有趣.入门级的开源项目. https://github.com/521xueweiha ...

  4. 你是否想知道如何应对高并发?Go语言为你提供了答案!

    并发编程是当前软件领域中不可忽视的一个关键概念.随着CPU等硬件的不断发展,我们都渴望让我们的程序运行速度更快.更快.而Go语言在语言层面天生支持并发,充分利用现代CPU的多核优势,这也是Go语言能够 ...

  5. Python笔记三之闭包与装饰器

    本文首发于公众号:Hunter后端 原文链接:Python笔记三之闭包与装饰器 这一篇笔记介绍 Python 里面的装饰器. 在介绍装饰器前,首先提出这样一个需求,我想统计某个函数的执行时间,假设这个 ...

  6. 掌握语义内核(Semantic Kernel):如何精进你的提示词工程

    在人工智能的海洋里,大型语言模型(LLM AI)是高速发展的一艘巨轮,而有效地与其沟通和指导其行为的锚,正是提示语(prompts).提示语是我们提供给模型的输入或查询,以期获取特定的响应.当今,提示 ...

  7. 在线录屏-通过Web API接口轻松实现录屏

    在线录屏是指在互联网上进行屏幕录制的过程.它允许用户通过网络连接,将自己的屏幕活动记录下来,并可以在需要时进行播放.共享或存档.在线录屏常用于教育.培训.演示.游戏等场景,可以帮助用户展示操作步骤.解 ...

  8. DataX快速入门

    DataX3.0快速入门 一.DataX3.0概览 DataX是阿里云DataWorks数据集成的开源版本,在阿里巴巴集团内部被广泛使用的离线数据同步工具/平台.解决了数据库之中的数据同步.迁移问题, ...

  9. 打通Rasa Action Server和LLM接口的尝试方法

      本文使用最简单的方法对打通 Rasa Action Server 和 LLM 接口进行了尝试,即当 Rasa 对话 intent 为 out_of_scope 时,调用 action_gpt_fa ...

  10. Linux IPTables:如何添加防火墙规则

    摘要:本文介绍了如何使用"iptables -A"命令添加 iptables 防火墙规则. 本文分享自华为云社区<Linux IPTables:如何添加防火墙规则(使用允许 ...