当前位置: 首页 > article >正文

FWFT-FIFO的同步和异步verilog代码

//-----------------------------------------------------------------------------
// File Name   : fifo_sync.v
//-----------------------------------------------------------------------------
//参数化同步FIFO。DEPTH可以是任何大于0的整数值。实际使用的内存深度是depth,如果depth不是2的幂,
//则四舍五入到下一个2的幂。然而,满/空标志将表现得好像实际深度是depth。
//例如,DEPTH = 1000将使用ram大小WIDTH x 1024,但当level = 1000并且不执行后续写入时,
//将断言full flag。
//
// XST will synthesize to a block RAM if the size is large enough.
//
// parameter ranges:
//
// DEPTH    powers of 2, > 1
// WIDTH    > 0
//
// Actual write depth   = DEPTH
// Read latency         = 1
//
//=============================================================================

module fifo_sync_fwft
#(
   parameter RAM_STYLE = "block", //block, distributed, or ultra
   parameter WIDTH = 8,
   parameter DEPTH = 1024
)
(
   // use 'old style' ports since 'level' needs a calculated width
   input sreset,
   input clk,

   input wr_en,
   input [WIDTH-1:0] wr_data,
   output reg full = 1'b0,
   output reg alm_full = 1'b0,
   output reg overflow = 1'b0,

   input rd_en,
   output [WIDTH-1:0] rd_data,
   output reg empty = 1'b1,
   output reg underflow = 1'b0,
   output reg [$clog2(DEPTH+1)-1:0] level = 0

);

   // `include "sv_clog2.vh"

   localparam LVL_WID = $clog2(DEPTH+1);
   localparam AWID = $clog2(DEPTH);

   // variable length constants
   localparam [LVL_WID-1:0] LEVEL_ZERO = {LVL_WID {1'b0}};
   localparam [LVL_WID-1:0] LEVEL_ONE  = {{LVL_WID-1 {1'b0}}, 1'b1};
   localparam [AWID-1:0] PTR_ZERO      = {AWID {1'b0}};
   localparam [AWID-1:0] PTR_ONE       = {{AWID-1 {1'b0}}, 1'b1};

   // pointers
   reg [AWID-1:0]  wr_ptr = PTR_ZERO;
   reg [AWID-1:0]  rd_ptr = PTR_ZERO;
   reg [AWID-1:0]  rd_ptr_plus1 = PTR_ONE;

   // RAM
   reg wren_q1;
   reg [AWID-1:0] wr_ptr_q1;
   reg [WIDTH-1:0] wr_data_q1;
   reg take_new;
   reg [WIDTH-1:0] rd_data_mem = {WIDTH {1'b0}};
   reg [WIDTH-1:0] rd_data_new;

   // round up to the next power of 2 for depths which are not a power of 2
   (* ram_style = RAM_STYLE *)
   reg [WIDTH-1:0] mem [(1 << AWID)-1:0];

   // write pointer
   always @(posedge clk)
      if(sreset == 1'b1)
         wr_ptr <= PTR_ZERO;
      else if(wr_en == 1'b1 && full == 1'b0)
         wr_ptr <= wr_ptr + 1;

   // read pointer
   always @(posedge clk)
      if(sreset == 1'b1) begin
         rd_ptr         <= PTR_ZERO;
         rd_ptr_plus1   <= PTR_ONE;
      end
      else if(rd_en == 1'b1 && empty == 1'b0) begin
         rd_ptr         <= rd_ptr_plus1;
         rd_ptr_plus1   <= rd_ptr_plus1 + PTR_ONE;
      end

   // full
   always @(posedge clk) begin
      if(sreset == 1'b1)
         full <= 1'b0;

      // assert when level is 1 less than
      // the depth and a write occurs and
      // no read occurs
      else if(wr_en == 1'b1 && rd_en == 1'b0 && level == (DEPTH-1))
         full <= 1'b1;

      // de-assert when a read occurs since a write
      // is not honored when full == 1
      else if(rd_en == 1'b1)
         full <= 1'b0;
   end

   // alm_full, asserted when level greater than 3/4 Depth
   always @(posedge clk) begin
      if(sreset == 1'b1)
         alm_full <= 1'b0;
      else if(level >= (3*DEPTH>>2))
         alm_full <= 1'b1;
      else
         alm_full <= 1'b0;
   end

   // empty
   always @(posedge clk)
      if(sreset == 1'b1)
         empty <= 1'b1;

      // assert when the level is 1 and a read occurs, but no write occurs
      else if(rd_en == 1'b1 && wr_en == 1'b0 && level == LEVEL_ONE)
         empty <= 1'b1;

      // deassert when a write occurs since a read is not honored when empty = 1
      else if(wr_en == 1'b1)
         empty <= 1'b0;

   // level
   always @(posedge clk)
      if(sreset == 1'b1)
         level <= LEVEL_ZERO;
      else begin
         case({(wr_en & ~full),(rd_en & ~empty)})
            2'b00: level <= level;
            2'b01: level <= level - LEVEL_ONE;
            2'b10: level <= level + LEVEL_ONE;
            2'b11: level <= level;
         endcase
      end

   // overflow
   // the write fails when the FIFO is in reset
   always @(posedge clk)
      if(sreset == 1'b1)
         overflow <= wr_en;
      else
         overflow <= wr_en & full;

   // underflow
   // the read fails when the FIFO is in reset
   always @(posedge clk)
      if(sreset == 1'b1)
         underflow <= rd_en;
      else
         underflow <= rd_en & empty;

   //---------------------------------- RAM -----------------------------------

   // for XST to synthesize to a BRAM, the write address must be latched
   always @(posedge clk)
   begin
      wren_q1     <= wr_en & ~full;
      wr_data_q1  <= wr_data;
      wr_ptr_q1   <= wr_ptr;
   end

   always @(posedge clk)
      if(wren_q1 == 1'b1)
         mem[wr_ptr_q1] <= wr_data_q1;

   // Since the write is delayed by 1 clock,reading a memory cell that 
   // was written on the previous cycle will not return the new data.
   // To prevent this, return the data that was just written instead of 
   // the memory cell contents.

   // check for read after write on same memory address
   always@(posedge clk) begin
   // if the address of the desired memory cell (rd_ptr_plus1) is a pending write, forward the data that is still in the pipe to the output
      if(rd_en == 1'b1 && empty == 1'b0) begin
         if(rd_ptr_plus1 == wr_ptr || rd_ptr_plus1 == wr_ptr_q1) begin
            take_new <= 1'b1;
         end
         else
            take_new <= 1'b0;

         if(rd_ptr_plus1 == wr_ptr)
            rd_data_new <= wr_data;
         else if(rd_ptr_plus1 == wr_ptr_q1)
            rd_data_new <= wr_data_q1;
      end
      // if the address of the desired memory cell (rd_ptr) is a pending write, forward the data that is still in the pipe to the output
      else begin
         // rd_ptr == wr_ptr
         if(empty == 1'b1 || (rd_ptr == wr_ptr_q1 && wren_q1 == 1'b1))
            take_new <= 1'b1;
         else
            take_new <= 1'b0;

         if(rd_ptr == wr_ptr)
            rd_data_new <= wr_data;
         else if(rd_ptr == wr_ptr_q1)
            rd_data_new <= wr_data_q1;
      end
   end
   always @(posedge clk)
      if(rd_en == 1'b1 && empty == 1'b0)
         rd_data_mem <= mem[rd_ptr_plus1];
      else
         rd_data_mem <= mem[rd_ptr];

   assign rd_data = take_new ? rd_data_new : rd_data_mem;

endmodule

上面是同步FWFT-FIFO的verilog手写代码,这种FIFO的一般用于低延时场景,优点是读使能与读数据是同步输出的,没有时延。

module fifo_async_fwft
#(
   parameter WIDTH = 8,
   parameter DEPTH = 1024
)
(
   // use 'old style' ports since
   // wr_level and rd_level need a calculated width
   reset,

   wr_clk,
   wr_en,
   wr_data,
   wr_full,
   wr_overflow,
   wr_level,

   rd_clk,
   rd_en,
   rd_data,
   rd_empty,
   rd_underflow,
   rd_level
);

   // `include "sv_clog2.vh"

   localparam AWID = $clog2(DEPTH);

   // variable length constants
   localparam [AWID-1:0] ZERO = {AWID {1'b0}};
   localparam [AWID-1:0] ONE  = {{AWID-1 {1'b0}}, 1'b1};

   function [AWID-1:0] bin2gray;
      input [AWID-1:0] bin;
   begin

      bin2gray = (bin >> 1) ^ bin;

   end
   endfunction

   function [AWID-1:0] gray2bin;
      input [AWID-1:0] gray;
      integer i;
   begin

      gray2bin = gray;

      for(i = 1; i < AWID; i = 2 * i)
         gray2bin = gray2bin ^ (gray2bin >> i);

   end
   endfunction

   // PORTS
   // asynchronous reset
   input reset;

   // wr_clk domain ports
   input wr_clk;
   input wr_en;
   input [WIDTH-1:0] wr_data;
   output reg wr_full = 1'b0;
   output reg wr_overflow = 1'b0;
   output reg [AWID-1:0] wr_level = ZERO;

   // rd_clk domain ports
   input rd_clk;
   input rd_en;
   output reg [WIDTH-1:0] rd_data;
   output reg rd_empty = 1'b1;
   output reg rd_underflow = 1'b0;
   output reg [AWID-1:0] rd_level = ZERO;

   genvar k;

   reg rd_reset_ms;
   reg rd_reset;

   reg wr_reset_ms;
   reg wr_reset;

   // pointers
   reg [AWID-1:0] wr_ptr_bin      = ZERO;
   reg [AWID-1:0] wr_ptr_bin_q1   = ZERO;
   reg [AWID-1:0] wr_ptr_gray     = ZERO;
   reg [AWID-1:0] wr_ptr_gray_ms  = ZERO;
   reg [AWID-1:0] wr_ptr_gray_rd  = ZERO;
   reg [AWID-1:0] wr_ptr_bin_rd   = ZERO;

   reg [AWID-1:0] rd_ptr_bin      = ZERO;
   reg [AWID-1:0] rd_ptr_gray     = ZERO;
   reg [AWID-1:0] rd_ptr_gray_ms  = ZERO;
   reg [AWID-1:0] rd_ptr_gray_wr  = ZERO;
   reg [AWID-1:0] rd_ptr_bin_wr   = ZERO;

   // RAM
   reg wren_q1;
   reg [AWID-1:0] wr_ptr_gray_q1 = ZERO;
   reg [AWID-1:0] wr_ptr_gray_q2 = ZERO;
   reg [WIDTH-1:0] wr_data_q1;

   // round up to the next power of 2 for depths which are not a power of 2
   reg [WIDTH-1:0] mem [(1 << AWID)-1:0];

   // Initialize memory array for simulation
   integer i;
   initial begin
      for (i=0; i<(1 << AWID); i=i+1)
         mem[i] = {WIDTH{1'b1}};
   end

   //--------------------------- synchronize resets ---------------------------

   // sync reset to wr_clk domain
   (* HBLKNM="wr_reset_sync_ffs" *)
   always @(posedge wr_clk or posedge reset)
      if(reset == 1'b1) begin
         wr_reset_ms <= 1'b1;
         wr_reset    <= 1'b1;
      end
      else begin
         wr_reset_ms <= 1'b0;
         wr_reset    <= wr_reset_ms;
      end

   // sync reset to rd_clk domain
   (* HBLKNM="rd_reset_sync_ffs" *)
   always @(posedge rd_clk or posedge reset)
      if(reset == 1'b1) begin
         rd_reset_ms <= 1'b1;
         rd_reset    <= 1'b1;
      end
      else begin
         rd_reset_ms <= 1'b0;
         rd_reset    <= rd_reset_ms;
      end

   //----------------------------- write signals ------------------------------

   // write pointers - binary & gray
   always @(posedge wr_clk)
      if(wr_reset == 1'b1) begin
         wr_ptr_bin  <= ZERO;
         wr_ptr_gray <= ZERO;
      end
      else if(wr_en == 1'b1 && wr_full == 1'b0) begin
         wr_ptr_bin  <= wr_ptr_bin + ONE;
         wr_ptr_gray <= bin2gray(wr_ptr_bin + ONE);
      end

   // synchronize rd_ptr_gray to wr_clk domain
   generate
      for(k = 0; k < AWID; k = k + 1) begin: rd_ptr_sync

         // place each sync FF pair in the same component
         // for max settling time

         (* HBLKNM="rd_ptr_sync_ffs" *)
         always @(posedge wr_clk) begin
            rd_ptr_gray_ms[k] <= rd_ptr_gray[k];
            rd_ptr_gray_wr[k] <= rd_ptr_gray_ms[k];
         end
      end
   endgenerate

   always @(posedge wr_clk)
      rd_ptr_bin_wr <= gray2bin(rd_ptr_gray_wr);

   // wr_level
   always @(posedge wr_clk)
      if(wr_reset == 1'b1)
         wr_level <= ZERO;
      else
         wr_level <= wr_ptr_bin - rd_ptr_bin_wr + (wr_en & ~wr_full);

   // wr_full
   always @(posedge wr_clk)
      if(wr_reset == 1'b1)
         wr_full <= 1'b1;
      else
         wr_full <= &(wr_ptr_bin - rd_ptr_bin_wr + (wr_en & ~wr_full));

   // wr_overflow
   // the write fails when the FIFO is in reset
   always @(posedge wr_clk)
      if(wr_reset == 1'b1)
         wr_overflow <= wr_en;
      else
         wr_overflow <= wr_en & wr_full;

   //------------------------------ read signals ------------------------------

   // read pointer - binary & gray
   always @(posedge rd_clk)
      if(rd_reset == 1'b1) begin
         rd_ptr_bin  <= ZERO;
         rd_ptr_gray <= ZERO;
      end

      else if(rd_en == 1'b1 && rd_empty == 1'b0) begin
         rd_ptr_bin  <= rd_ptr_bin + ONE;
         rd_ptr_gray <= bin2gray(rd_ptr_bin + ONE);
      end

   // synchronize wr_ptr_gray_q2 to rd_clk domain
   generate
      for(k = 0; k < AWID; k = k + 1) begin: wr_ptr_sync

         // place each sync FF pair in the same component for max settling time

         (* HBLKNM="wr_ptr_sync_ffs" *)
         always @(posedge rd_clk) begin
            wr_ptr_gray_ms[k] <= wr_ptr_gray_q2[k];
            wr_ptr_gray_rd[k] <= wr_ptr_gray_ms[k];
         end
      end
   endgenerate

   always @(posedge rd_clk)
      wr_ptr_bin_rd <= gray2bin(wr_ptr_gray_rd);

   // rd_level
   always @(posedge rd_clk)
      if(rd_reset == 1'b1)
         rd_level <= ZERO;
      else
         rd_level <= wr_ptr_bin_rd - rd_ptr_bin - (rd_en & ~rd_empty);

   // rd_empty
   always @(posedge rd_clk)
      if(rd_reset == 1'b1)
         rd_empty <= 1'b1;
      else
         rd_empty <= (wr_ptr_bin_rd - rd_ptr_bin - (rd_en & ~rd_empty)) == ZERO;

   // rd_underflow the read fails when the FIFO is in reset
   always @(posedge rd_clk)
      if(rd_reset == 1'b1)
         rd_underflow <= rd_en;
      else
         rd_underflow <= rd_en & rd_empty;

   //---------------------------------- RAM -----------------------------------

   // for XST to synthesize to a BRAM, the write address must be latched
   always @(posedge wr_clk) begin
      wren_q1        <= wr_en & ~wr_full;
      wr_data_q1     <= wr_data;
      wr_ptr_bin_q1  <= wr_ptr_bin;
      wr_ptr_gray_q1 <= wr_ptr_gray;
      wr_ptr_gray_q2 <= wr_ptr_gray_q1;
   end

   always @(posedge wr_clk)
      if(wren_q1 == 1'b1)
         mem[wr_ptr_bin_q1] <= wr_data_q1;

   always @(posedge rd_clk)
      if(rd_en == 1'b1 && rd_empty == 1'b0)
         rd_data <= mem[rd_ptr_bin + ONE];
      else
         rd_data <= mem[rd_ptr_bin];

endmodule

上面是异步FWFT-FIFO的verilog代码,这种FIFO的一般用于低延时场景,优点是读使能与读数据是同步输出的,没有时延。

以上的两份代码均非原创,原创大佬另有其人。代码片不保证无BUG,只是在这里分享给各位借鉴与使用。


http://www.kler.cn/a/153911.html

相关文章:

  • MySQL 日志 主从复制
  • vue 常用特性 ( 计算属性 | 侦听器 | 过滤器 )
  • 用Ruby编写一个自动化测试脚本,验证网站登录功能的正确性。
  • 纯前端实现语音文字互转
  • 为何数据库推荐将IPv4地址存储为32位整数而非字符串?
  • 通过华为鲲鹏认证发行上市的集成平台产品推荐
  • Swoole的多进程模块
  • 22、DS1302实时时钟
  • YOLOv8 第Y7周 水果识别
  • NXP iMX8M Plus Qt5 双屏显示
  • 【开源】基于JAVA语言的校园电商物流云平台
  • 匿名结构体类型、结构体的自引用、结构体的内存对齐以及结构体传参
  • Git多库多账号本地SSH连接配置方法
  • LINUX 嵌入式C编程--信号编程
  • IDEA在左下角显示当前代码所属的类名/方法名
  • vue---消息订阅与发布(pubsub)
  • 网络通信原理,进制转化总结
  • SQL Server数据库部署
  • 九、FreeRTOS之FreeRTOS列表和列表项
  • 基于ASP.NET MVC技术的图书管理系统的设计与实现
  • Discuz论坛自动采集发布软件
  • JS中 require 与 import 的区别
  • Android File Transfer for Mac:畅享强大的安卓文件传输工具
  • Sock0s1.1
  • ssh连接docker容器处理备忘
  • python处理日期和时间