FIFO专题之单口RAM实现FIFO(同步)

这篇具有很好参考价值的文章主要介绍了FIFO专题之单口RAM实现FIFO(同步)。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

使用单口RAM实现FIFO,其实很简单,其中的重点就是区分出读写,

读写如果同时启动,你肯定会思考单口RAM肯定会出问题,毕竟单口RAM只有一个口,肯定不能实现同时读写,那么怎么解决这个问题呢。

有两种办法:

第一种办法就是采用两个单口RAM,这样就可以了,两个单口RAM分开奇偶,相当于乒乓的意思,然后再加一个REG,这就相当于把读写分开了

那么就可能分为以下几种情况:

①同时读写:读写同时为奇,这种情况就是在当前一拍,将写数据存入REG中,并将REG_VALID拉高告诉FIFO我下一拍要写数据,并在当前拍从奇数的FIFO中读取数据,那么下一拍如果再此发生同时读写,那么此时的同时读写就为偶,这一拍发生的情况就是将前一拍REG中的数据写入FIFO,然后将REG中数据更新新数据,然后将REG_VALID再拉高,告诉偶数FIFO下一拍要写数据了,并同时从偶数FIFO中取出要读的数据。

其实核心观点就是用两个单口RAM一个REG,用来区分最难的读写同时发生的情况,通过将RAM分为奇偶再加一个REG寄存器用来缓存,这就使不能同时读写的情况给解决了。

如果同时读写,且奇偶不同,那这种情况就更容易解决了,当前拍写数据的模块将数据写入REG,读模块的读出数据,然后下一拍将REG再写入单口RAM,和之前同时读写同时为奇偶的情况很相似。

②不同时读写:这种情况就是你只要写就先将数据写入REG,然后拉高VALID下一拍将REG中的数据写入单口RAM,如果读就直接读出数据。

通过这种方式就完美的解决了单口RAM没办法同时操作RAM的情况。

代码如下

`timescale 1ns / 1ps
//
// Company: 
// Engineer: Brad
// 
// Create Date: 2022/05/11 19:38:41
// Design Name: 
// Module Name: FIFO_single_ram
// Project Name: 
// Target Devices: 
// Tool Versions: 
// Description: 
// 
// Dependencies: 
// 
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
// 
//


module FIFO_single_ram
#( parameter WIDTH = 8,
   parameter DEEPTH= 256
)
(
    input clk ,
    input rstn ,
    input [WIDTH-1:0] data,
    input wr,
    input rd,
    output reg [WIDTH-1:0] data_out,
    output full,
    output empty
    );
    //-------------下面是FIFO的操作------------------//
    reg [8:0] rd_cnt;  //多一位用来回卷判断空满
    reg [8:0] wr_cnt;
    reg [8:0] wr_cnt_r;
    wire [7:0] rd_addr;
    wire [7:0] wr_addr;
    wire [7:0] addr_odd;
    wire [7:0] addr_even;
    reg [WIDTH-1:0] data_reg;
    reg       reg_valid;
    reg       wr_flag; //用来判断读写的奇偶 0代表奇 1代表偶数
    reg       rd_flag;
    wire      wr_ram;  //用来判断是否可以写
    wire      rd_ram;  //用来判断是否可以读
    wire      wr_en_odd; //奇数写
    wire      wr_en_even;//偶数写
    wire      rd_en_odd; //奇数写
    wire      rd_en_even;//偶数写
    reg      rd_en_odd_r; //奇数写
    reg      rd_en_even_r;//偶数写
    wire [7:0] datao_odd;
    wire [7:0] datao_even;
    assign rd_addr = rd_cnt [7:0];
    assign wr_addr = wr_cnt_r [7:0];
    assign full = (wr_cnt[8]^rd_cnt[8])&&(wr_cnt[7:0]==rd_cnt[7:0]); //最高位不同 低位相同;
    assign empty= (wr_cnt == rd_cnt); //全等
    assign wr_ram = (~full)&(wr);
    assign rd_ram = (~empty)&(rd);
//    assign data_out = (rd_en_odd_r)? datao_odd:datao_even;
    always@(*) begin
    if(rd_en_odd_r)
        data_out = datao_odd;
    else if(rd_en_even_r)
        data_out = datao_even;
    else
        data_out = data_out;
    end
    always@(posedge clk or negedge rstn)begin
    if(~rstn) begin
        wr_cnt <= 9'b0;
        data_reg <= {WIDTH{1'b0}};
        reg_valid <= 1'b0;
        wr_cnt_r <= 9'b0;
        end
    else if(wr_ram)
        begin
        wr_cnt <= wr_cnt + 1'b1;
        data_reg <= data;
        reg_valid <= 1'b1;
        wr_cnt_r <= wr_cnt;
        end
    else
        begin
        reg_valid <= 1'b0;
        end
    end
    always@(posedge clk or negedge rstn)begin
    if(~rstn)
        rd_cnt <= 9'b0;
    else if(rd_ram)
        rd_cnt <= rd_cnt + 1'b1;
    end

    //------------------------读写奇偶判断----------------------
    always@(posedge clk or negedge rstn) begin
    if(~rstn)
        wr_flag <= 1'b0;
    else if(reg_valid)
        wr_flag <=wr_flag + 1'b1; //往里面写数据的同时奇偶改变
    else 
        wr_flag <= wr_flag ; //往里面读数据的同时奇偶改变
    end
    always@(posedge clk or negedge rstn) begin
    if(~rstn)
        rd_flag <= 1'b0;
    else if(rd_ram)
        rd_flag <= rd_flag + 1'b1; //往里面读数据的同时奇偶改变
    else
        rd_flag <= rd_flag;
    end
    //--------------------RAM读写使能控制 分为奇偶两种类型
    always@(posedge clk or negedge rstn) begin
    if(~rstn)begin
        rd_en_odd_r <= 1'b0;
        rd_en_even_r <= 1'b0;
        end
    else
    begin
        rd_en_odd_r <= rd_en_odd;
        rd_en_even_r<=rd_en_even;
    end
    end

    assign wr_en_odd =(rd_en_odd)? 1'b0:( reg_valid & (~wr_flag));
    assign wr_en_even =(rd_en_even)? 1'b0:( reg_valid & (wr_flag));
    assign rd_en_odd = rd_ram & (~rd_flag);
    assign rd_en_even = rd_ram & (rd_flag);
    wire ena_odd = wr_en_odd | rd_en_odd;
    wire ena_even = wr_en_even | rd_en_even;
    assign addr_odd= (wr_en_odd)? wr_addr:rd_addr;
    assign addr_even= (wr_en_even)?wr_addr:rd_addr;
    SINGLE_RAM odd_ram (
      .clka(clk),    // input wire clka
      .ena(ena_odd),      // input wire ena
      .wea(wr_en_odd),      // input wire [0 : 0] wea
      .addra(addr_odd),  // input wire [7 : 0] addra
      .dina(data_reg),    // input wire [7 : 0] dina
      .douta(datao_odd)  // output wire [7 : 0] douta
    );
    SINGLE_RAM even_ram (
      .clka(clk),    // input wire clka
      .ena(ena_even),      // input wire ena
      .wea(wr_en_even),      // input wire [0 : 0] wea
      .addra(addr_even),  // input wire [7 : 0] addra
      .dina(data_reg),    // input wire [7 : 0] dina
      .douta(datao_even)  // output wire [7 : 0] douta
    );
endmodule

我这个代码没有做仔细的仿真,经过各位网友的提醒,改了几处,首先针对读写的时候对奇偶不同时操作,通过加了一个直通路解决,以防止下一拍写的时候漏掉数据。

并对这个代码进行了详细仿真,仿真代码如下:

第二种情况就是针对单口RAM的位宽进行变化,比如FIFO的读写数据位宽为8Bit,那么就针对RAM的位宽设置为FIFO位宽的两倍,外部加两个同等位宽的读写寄存器,通过将读写寄存器与外界读写和读写寄存器与RAM的交互实现FIFO的功能,比如对于同时读写的情况,在时钟上按2拍完成一次寄存器与RAM数据的交互,比如第一拍完成读,第二拍完成写,对于外界的接口而言数据是源源不断的从寄存器出来的,从而实现了单口RAM实现FIFO的功能,这种情况我仔细想了想只适用于 对FIFO连续读写的情况下才能使用,不连续读写总有一次会把其置成同时读写的情况。文章来源地址https://www.toymoban.com/news/detail-430969.html

到了这里,关于FIFO专题之单口RAM实现FIFO(同步)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

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

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

相关文章

  • 同步FIFO的verilog实现(2)——高位扩展法

            在之前的文章中,我们介绍了同步FIFO的verilog的一种实现方法:计数法。其核心在于:在同步FIFO中,我们可以很容易的使用计数来判断FIFO中还剩下多少可读的数据,从而可以判断空、满。         关于计数法实现同步FIFO的详细内容,请参考:同步FIFO的verilog实现(

    2024年02月09日
    浏览(29)
  • 【Verilog】同步FIFO原理及verilog实现(参数化)

            旨在学习理解,项目中还是用成熟IP靠谱~ 目录 一、FIFO原理 二、同步FIFO设计 2.1 位宽和深度 2.2 空、满标志 2.3 FIFO计数 2.4 ram模型 2.5 读/写操作 三、​​​​​​​verilog代码 四、仿真验证 后记 FIFO( First Input First Output)是指先进先出。模型如下:           F

    2024年02月05日
    浏览(27)
  • vivado IP核:ILA、时钟、RAM、FIFO

    vivado工具集成了逻辑分析仪,ILA IP核用于替换外部的逻辑分析仪,添加探针来监控内部信号波形变化。 1)IP Catalog 2)搜索栏可搜索IP核,如创建FIFO、RAM等。 3)搜索并选择。 4)设置ILA各项参数。    5) 设置好IP核参数后点ok。 6)打开ila_0.evo。 7) 复制ila例化模板。 8)在

    2023年04月21日
    浏览(32)
  • 用移位寄存器实现同步FIFO,带空满判断

            如图所示,同步FIFO带有push信号和pop信号,push代表往队列里面压入一个数据,pop代表往队列外面排出一个数据。         同步FIFO的空满判断用一个计数器来判断,收到push信号计数器加1,收到pop信号时计数器减1,考虑同时push和pop的情况计数器不变,当计数器为

    2024年02月14日
    浏览(37)
  • Altera FPGA 储存单元IP核之RAM、FIFO

         只读存储器,系统上电后数据就被写入ROM,运行过程中只能从ROM中读取数据,而不能改变ROM中的数值。      随机存取储存器,可以随时把数据写入任一指定地址的储存单元,也可以随时从任一指定地址中读取数据。其读写速度是由时钟频率决定的。RAM主要用来存放程

    2023年04月08日
    浏览(34)
  • 跨时钟域方法(同步器、异步FIFO、边沿检测器、脉冲同步器、同步FIFO)

    目录 1、跨时钟域方法的原因 2、跨时钟处理的两种思路 3、跨时钟域分类——单比特信号跨时钟 3.1.1慢时钟———快时钟。(满足三边沿准则,有效事件可以被安全采样) 3.1.2慢时钟———快时钟。(不满足三边沿准则,有效事件可以被安全采样) 3.2.1有效事件传输背景下确

    2024年02月12日
    浏览(36)
  • 手撕代码——同步FIFO

      查看Xilinx官方FIFO IP核,其主要的信号有时钟信号、写端口信号、读端口信号,其中,写端口信号包括写满信号full、写使能信号wr_en、写数据输入din、几乎满信号almost_full;读端口信号包括读空信号empty、读使能信号rd_en、读数据输出dout、几乎空信号almost_empty。几乎满信号

    2024年02月06日
    浏览(34)
  • Verilog同步FIFO设计

    同步FIFO(synchronous)的 写时钟和读时钟为同一个时钟,FIFO内部所有逻辑都是同步逻辑 ,常常用于交互数据缓冲。 异步FIFO:数据写入FIFO的时钟和数据读出FIFO的时钟是异步的(asynchronous) 典型同步FIFO有三部分组成: (1) FIFO写控制逻辑; (2)FIFO读控制逻辑; (3)FIFO 存储实体(

    2024年02月12日
    浏览(47)
  • 手把手Verilog HDL同步Vaild-Ready握手FIFO机制

    三级目录 V-R握手FIFO机制,即是两级时钟速率不同的模块之间传递信号的机制,上游往下游传递的数据暂时缓存在FIFO中,上游和FIFO、FIFO和下游之间通过握手传递信号。即在一个FIFO模块外层套了一层握手机制。如下图: 如何用Verilog代码实现呢?我们可以这么来做, 1、先实现

    2024年02月16日
    浏览(26)
  • 【elasticsearch专题】:Logstash从入门到同步MySQL数据

      Elasticsearch是在数据处理生态系统中担任一个开源的分布式 搜索 和 分析 引擎的角色,专为 存储 、 检索 和分析大量的数据而打造。与此相伴的是Kibana,一个开源数据可视化平台,用于以优雅的方式展示Elasticsearch中的数据,并赋予用户创建仪表盘、图表和报告的能力。然

    2024年02月04日
    浏览(38)

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

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

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

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

二维码1

领取红包

二维码2

领红包