前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >带选通信号的同步FIFO(可综合RTL设计)

带选通信号的同步FIFO(可综合RTL设计)

作者头像
数字IC小站
发布2020-07-01 14:51:38
7190
发布2020-07-01 14:51:38
举报
文章被收录于专栏:数字IC小站

还是上次那个同步FIFO,传送门在这~

带选通信号的同步FIFO(重发)

上次讲的是用SystemVerilog去设计这个FIFO,那么如果用可综合的RTL代码怎么设计呢?

  • 因为本次FIFO的输入数据位宽实际上可以看成是不固定的,每次输出的数据却都是4byte,那么很容易产生的一个问题就是,前一时刻还是未满状态,下一时刻却已经是溢出(overflow)了
  • 那么为了解决上一个问题,我的想法是,当FIFO深度不足8byte时,就拉低Ready_in信号,不再写入数据。
  • 有同学和我讨论过一个问题,那就是设置为深度不足4byte的时候可不可以,原本想法是当FIFO中存在足够数据时,那么每个clock都会输出4byte数据,此时输入8byte还是可以存储的,可能这个问题和仿真器有关,我们不确定到底是先读还是先写,这个会存在不确定态,所以最好还是预留8byte的空间为好。
  • 我们最好把FIFO宽度设置为1byte,从交上来的作业来看,我们常犯的错误是写入数据指针的设置,有点同学直接设置一个基指针,然后就是如下代码所示:
代码语言:javascript
复制
// write pointer 
  always @(posedge clock or negedge reset_n) begin
    if (!reset_n) begin
      wptr <= 0;
    end else begin
      if (valid_in & ready_in) begin
        // can write into the fifo when input is valid and fifo is ready
        case (size)
          2'b00: begin 
            fifo_mem[wptr[ADDR_WIDTH-1:0]]    <= data_in[7:0];
            wptr <= wptr + 1;
          end
          2'b01: begin
            fifo_mem[wptr[ADDR_WIDTH-1:0]]    <= data_in[7:0];
            fifo_mem[wptr[ADDR_WIDTH-1:0]+1] <= data_in[15:8];
            wptr <= wptr + 2;
          end
          2'b10: begin 
            fifo_mem[wptr[ADDR_WIDTH-1:0]]    <= data_in[7:0];
            fifo_mem[wptr[ADDR_WIDTH-1:0]+1] <= data_in[15:8];
            fifo_mem[wptr[ADDR_WIDTH-1:0]+2] <= data_in[23:16];
            fifo_mem[wptr[ADDR_WIDTH-1:0]+3] <= data_in[31:24];
            wptr <= wptr + 4;

上述代码的主要问题是,会存在数据写飞的情况...

数据在哪会丢失呢?确实,我们把wptr写指针的数据宽度设置好了,为5位宽,因此wptr最多为31,没毛病,可是这时候wptr[ADDR_WIDTH-1:0]+3为多少呢?这个数据会写到地址为34的FIFO中,因为不存在这个地址,然后数据就丢了...丢了

解决方法有好几种,比如我们多设计几个中间变量,把变量的宽度进行限制;比如我们把上述写指针取余,这样就不可能宽度溢出...还有很多方法

设计FIFO的方法有很多,可以加计数器,判断FIFO内部还有多少数据,甚至可以写状态机...

我设计的时候就是不想加计数器,这样可以减少硬件资源,只通过读写地址之间的关系判断是否空满...

主要思路如下(code仅供参考,欢迎讨论):

代码语言:javascript
复制
`timescale 1ns / 100ps
//******************************************************************

// Author:SJTU_chen
// Date: 2019/10/26
// Version: v1.0
// Module Name: fifo-dut
// Project Name: SystemVerilog Lab1

//*******************************************************************
module fifo_dut(
    clock,reset_n,valid_in,wstrb,data_in,valid_out,data_out,ready_in
    );

    input           clock,reset_n,valid_in;
    input [1:0]     wstrb;
    input [63:0]    data_in;
    output          valid_out;   
    output[31:0]    data_out;
    output          ready_in;

    parameter FIFO_DATA_WIDTH = 8   ; 
    parameter FIFO_DP = 32 ;

    parameter FIFO_ADDR_WIDTH = clogb2(FIFO_DP)  ;

    reg     [31:0]                  data_out;
    reg                             valid_out;
    wire                            enough_data;
    wire                            enough_space; 
    wire                            ready_in;
    wire         [4:0]              fifo_wr_addr1,fifo_wr_addr2,fifo_wr_addr3,fifo_wr_addr4,fifo_wr_addr5,fifo_wr_addr6,fifo_wr_addr7;
    wire     [FIFO_ADDR_WIDTH-1:0]  fifo_wr_addr ;  //fifo write address
    wire     [FIFO_ADDR_WIDTH-1:0]  fifo_rd_addr ;  //fifo read address
    reg      [FIFO_ADDR_WIDTH:0]    wr_addr_ptr;    //fifo write pointer
    reg      [FIFO_ADDR_WIDTH:0]    rd_addr_ptr;    //fifo read pointer

    reg [FIFO_DATA_WIDTH-1:0] fifo_mem [FIFO_DP-1:0] ;  //fifo data width is 8 .depth is 32

    assign   fifo_wr_addr1=fifo_wr_addr+1;
    assign   fifo_wr_addr2=fifo_wr_addr+2;
    assign   fifo_wr_addr3=fifo_wr_addr+3;
    assign   fifo_wr_addr4=fifo_wr_addr+4;
    assign   fifo_wr_addr5=fifo_wr_addr+5;
    assign   fifo_wr_addr6=fifo_wr_addr+6;
    assign   fifo_wr_addr7=fifo_wr_addr+7;

上述参数和中间变量设置好以后,接下来就是读写数据啦

代码语言:javascript
复制
// write data to fifo
    always @(posedge clock or negedge reset_n)
    if (reset_n &&valid_in && enough_space) begin
      if(wstrb==2'b00) begin
      fifo_mem[fifo_wr_addr]   <= data_in[7:0];
      end
      else if (wstrb==2'b01) begin
      fifo_mem[fifo_wr_addr]   <= data_in[7:0];
      fifo_mem[fifo_wr_addr1] <= data_in[15:8];
      end
      else if (wstrb==2'b10) begin
      fifo_mem[fifo_wr_addr]   <= data_in[7:0];
      fifo_mem[fifo_wr_addr1] <= data_in[15:8];
      fifo_mem[fifo_wr_addr2] <= data_in[23:16];
      fifo_mem[fifo_wr_addr3] <= data_in[31:24];
      end
      else if (wstrb==2'b11) begin
      fifo_mem[fifo_wr_addr]   <= data_in[7:0];
      fifo_mem[fifo_wr_addr1] <= data_in[15:8];
      fifo_mem[fifo_wr_addr2] <= data_in[23:16];
      fifo_mem[fifo_wr_addr3] <= data_in[31:24];
      fifo_mem[fifo_wr_addr4] <= data_in[39:32];
      fifo_mem[fifo_wr_addr5] <= data_in[47:40];
      fifo_mem[fifo_wr_addr6] <= data_in[55:48];
      fifo_mem[fifo_wr_addr7] <= data_in[63:56];
      end
    end
    else begin
      fifo_mem[fifo_wr_addr] <= fifo_mem[fifo_wr_addr];
    end
       // read data
    always @(posedge clock or negedge reset_n)
    begin
      if(reset_n == 1'b0) begin
      data_out <= {32{1'b0}} ;
      end
      else if (enough_data) begin
      data_out[7:0]     <= fifo_mem[fifo_rd_addr] ; 
      data_out[15:8]    <= fifo_mem[fifo_rd_addr+1] ; 
      data_out[23:16]   <= fifo_mem[fifo_rd_addr+2] ;         
      data_out[31:24]   <= fifo_mem[fifo_rd_addr+3] ; 
      end
      else  begin
      data_out          <=data_out;
      end    
    end

指针变化

代码语言:javascript
复制
 // write pointer 
    always @(posedge clock or negedge reset_n)
    begin
      if(reset_n == 1'b0)
        wr_addr_ptr <= {(FIFO_ADDR_WIDTH+1){1'b0}} ;
      else if (valid_in && enough_space)begin
      if(wstrb==2'b00) begin
        wr_addr_ptr <= wr_addr_ptr + 1'b1 ;
      end
      else if(wstrb==2'b01) begin
        wr_addr_ptr <= wr_addr_ptr + 2'b10 ;
      end
      else if(wstrb==2'b10) begin
        wr_addr_ptr <= wr_addr_ptr + 3'b100 ;
      end
      else if(wstrb==2'b11) begin
        wr_addr_ptr <= wr_addr_ptr + 4'b1000 ;
      end
    end
    else begin
        wr_addr_ptr <= wr_addr_ptr;
        end      
    end

    //read pointer
    always @(posedge clock or negedge reset_n)
    begin
    if(reset_n == 1'b0)
        rd_addr_ptr <= {(FIFO_ADDR_WIDTH+1){1'b0}} ;
    else if (enough_data)
        rd_addr_ptr <= rd_addr_ptr + 3'b100 ; 
    else
        rd_addr_ptr <= rd_addr_ptr;
    end

状态变化

代码语言:javascript
复制
    // valid_out state
    always @(posedge clock or negedge reset_n)
    begin
      if(reset_n == 1'b0) begin
      valid_out     <='0;
      end
      else if (enough_data) begin
      valid_out     <='1;
      end
      else  begin
      valid_out     <='0;
      end    
    end  

判断内部空间:

代码语言:javascript
复制
    //ready_in
    assign ready_in     = enough_space;

    assign fifo_wr_addr = wr_addr_ptr[FIFO_ADDR_WIDTH-1:0];
    assign fifo_rd_addr = rd_addr_ptr[FIFO_ADDR_WIDTH-1:0];

    assign enough_data  = ((wr_addr_ptr >= rd_addr_ptr+4)||(rd_addr_ptr >= wr_addr_ptr+4))?1:0;

    wire  addr_select1,addr_select2,addr_select3;
    assign addr_select1 =((fifo_rd_addr>fifo_wr_addr)&&( fifo_rd_addr-fifo_wr_addr<8))?1:0;
    assign addr_select2 =((fifo_rd_addr<fifo_wr_addr)&&( fifo_wr_addr-fifo_rd_addr>=24))?1:0;
    assign addr_select3 = (wr_addr_ptr[FIFO_ADDR_WIDTH]^rd_addr_ptr[FIFO_ADDR_WIDTH])?1:0;
    assign addr_select4 = (wr_addr_ptr[FIFO_ADDR_WIDTH]~^rd_addr_ptr[FIFO_ADDR_WIDTH])?1:0;
    assign enough_space =((addr_select1&addr_select3)|(addr_select2&addr_select4))?0:1;

上述代码中,addr_select判断读指针和写指针的位置,1.当读指针大于写指针,两个指针位置小于8,并且wr_addr_ptr的最高位不相同时则接近满。2.当写指针大于读指针,两个指针位置大于24,并且wr_addr_ptr的最高位相同时则接近满。这个是在深度为32时的情况,当深度为其他深度n时,则数值24应该改为n-8。

代码语言:javascript
复制
    function integer clogb2 (input integer size);
    begin
      size = size - 1;
      for (clogb2=1; size>1; clogb2=clogb2+1)
        size = size >> 1;
    end
    endfunction // clogb2

上述代码实现的就是一个取对数函数,用来生成地址宽度与数据深度之间的关系。

上述代码经过questa sim10.6c验证,应该是没问题,但是不保证完全正确,如有bug,欢迎私信交流~

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2019-12-03,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 数字IC小站 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档