hw/ip/prim/rtl/prim_fifo_async.sv Cov: 100%

   1: // Copyright lowRISC contributors.
   2: // Licensed under the Apache License, Version 2.0, see LICENSE for details.
   3: // SPDX-License-Identifier: Apache-2.0
   4: //
   5: // Generic asynchronous fifo for use in a variety of devices.
   6: 
   7: module prim_fifo_async #(
   8:   parameter  int unsigned Width  = 16,
   9:   parameter  int unsigned Depth  = 3,
  10:   localparam int unsigned DepthW = $clog2(Depth+1) // derived parameter representing [0..Depth]
  11: ) (
  12:   // write port
  13:   input                  clk_wr_i,
  14:   input                  rst_wr_ni,
  15:   input                  wvalid,
  16:   output                 wready,
  17:   input [Width-1:0]      wdata,
  18:   output [DepthW-1:0]    wdepth,
  19: 
  20:   // read port
  21:   input                  clk_rd_i,
  22:   input                  rst_rd_ni,
  23:   output                 rvalid,
  24:   input                  rready,
  25:   output [Width-1:0]     rdata,
  26:   output [DepthW-1:0]    rdepth
  27: );
  28: 
  29:   `ASSERT_INIT(paramCheckDepth,  Depth >= 3)
  30: 
  31:   localparam int unsigned PTRV_W = $clog2(Depth);
  32:   localparam logic [PTRV_W-1:0] DepthMinus1 = PTRV_W'(Depth - 1);
  33:   localparam int unsigned PTR_WIDTH = PTRV_W+1;
  34: 
  35:   logic [PTR_WIDTH-1:0]    fifo_wptr, fifo_rptr;
  36:   logic [PTR_WIDTH-1:0]    fifo_wptr_sync_combi,   fifo_rptr_sync;
  37:   logic [PTR_WIDTH-1:0]    fifo_wptr_gray_sync,    fifo_rptr_gray_sync;
  38:   logic [PTR_WIDTH-1:0]    fifo_wptr_gray,         fifo_rptr_gray;
  39:   logic                    fifo_incr_wptr, fifo_incr_rptr, empty;
  40: 
  41:   logic full_wclk, full_rclk;
  42: 
  43:   assign wready = !full_wclk;
  44:   assign rvalid = !empty;
  45: 
  46:   // create the write and read pointers
  47: 
  48:   assign fifo_incr_wptr = wvalid & wready;
  49:   assign fifo_incr_rptr = rvalid & rready;
  50: 
  51:   ///////////////////
  52:   // write pointer //
  53:   ///////////////////
  54: 
  55:   always_ff @(posedge clk_wr_i or negedge rst_wr_ni)
  56:     if (!rst_wr_ni) begin
  57:       fifo_wptr <= {(PTR_WIDTH){1'b0}};
  58:     end else if (fifo_incr_wptr) begin
  59:       if (fifo_wptr[PTR_WIDTH-2:0] == DepthMinus1) begin
  60:         fifo_wptr <= {~fifo_wptr[PTR_WIDTH-1],{(PTR_WIDTH-1){1'b0}}};
  61:       end else begin
  62:         fifo_wptr <= fifo_wptr + {{(PTR_WIDTH-1){1'b0}},1'b1};
  63:     end
  64:   end
  65: 
  66:   // gray-coded version
  67:   always_ff @(posedge clk_wr_i or negedge rst_wr_ni)
  68:     if (!rst_wr_ni) begin
  69:       fifo_wptr_gray <= {(PTR_WIDTH){1'b0}};
  70:     end else if (fifo_incr_wptr) begin
  71:       if (fifo_wptr[PTR_WIDTH-2:0] == DepthMinus1) begin
  72:         fifo_wptr_gray <= dec2gray({~fifo_wptr[PTR_WIDTH-1],{(PTR_WIDTH-1){1'b0}}});
  73:       end else begin
  74:         fifo_wptr_gray <= dec2gray(fifo_wptr + {{(PTR_WIDTH-1){1'b0}},1'b1});
  75:       end
  76:     end
  77: 
  78:   prim_flop_2sync #(.Width(PTR_WIDTH)) sync_wptr (
  79:     .clk_i    (clk_rd_i),
  80:     .rst_ni   (rst_rd_ni),
  81:     .d        (fifo_wptr_gray),
  82:     .q        (fifo_wptr_gray_sync));
  83: 
  84:   assign fifo_wptr_sync_combi = gray2dec(fifo_wptr_gray_sync);
  85: 
  86:   //////////////////
  87:   // read pointer //
  88:   //////////////////
  89: 
  90:   always_ff @(posedge clk_rd_i or negedge rst_rd_ni)
  91:     if (!rst_rd_ni) begin
  92:       fifo_rptr <= {(PTR_WIDTH){1'b0}};
  93:     end else if (fifo_incr_rptr) begin
  94:       if (fifo_rptr[PTR_WIDTH-2:0] == DepthMinus1) begin
  95:         fifo_rptr <= {~fifo_rptr[PTR_WIDTH-1],{(PTR_WIDTH-1){1'b0}}};
  96:       end else begin
  97:         fifo_rptr <= fifo_rptr + {{(PTR_WIDTH-1){1'b0}},1'b1};
  98:     end
  99:   end
 100: 
 101:   // gray-coded version
 102:   always_ff @(posedge clk_rd_i or negedge rst_rd_ni)
 103:     if (!rst_rd_ni) begin
 104:       fifo_rptr_gray <= {(PTR_WIDTH){1'b0}};
 105:     end else if (fifo_incr_rptr) begin
 106:       if (fifo_rptr[PTR_WIDTH-2:0] == DepthMinus1) begin
 107:         fifo_rptr_gray <= dec2gray({~fifo_rptr[PTR_WIDTH-1],{(PTR_WIDTH-1){1'b0}}});
 108:       end else begin
 109:         fifo_rptr_gray <= dec2gray(fifo_rptr + {{(PTR_WIDTH-1){1'b0}},1'b1});
 110:       end
 111:     end
 112: 
 113:   prim_flop_2sync #(.Width(PTR_WIDTH)) sync_rptr (
 114:     .clk_i    (clk_wr_i),
 115:     .rst_ni   (rst_wr_ni),
 116:     .d        (fifo_rptr_gray),
 117:     .q        (fifo_rptr_gray_sync));
 118: 
 119:   always_ff @(posedge clk_wr_i or negedge rst_wr_ni)
 120:     if (!rst_wr_ni) begin
 121:       fifo_rptr_sync <= {PTR_WIDTH{1'b0}};
 122:     end else begin
 123:       fifo_rptr_sync <= gray2dec(fifo_rptr_gray_sync);
 124:     end
 125: 
 126:   //////////////////
 127:   // empty / full //
 128:   //////////////////
 129: 
 130:   assign  full_wclk = (fifo_wptr == (fifo_rptr_sync ^ {1'b1,{(PTR_WIDTH-1){1'b0}}}));
 131:   assign  full_rclk = (fifo_wptr_sync_combi == (fifo_rptr ^ {1'b1,{(PTR_WIDTH-1){1'b0}}}));
 132: 
 133:   // Current depth in the write clock side
 134:   logic  wptr_msb;
 135:   logic  rptr_sync_msb;
 136:   logic  [PTRV_W-1:0] wptr_value;
 137:   logic  [PTRV_W-1:0] rptr_sync_value;
 138:   assign wptr_msb = fifo_wptr[PTR_WIDTH-1];
 139:   assign rptr_sync_msb = fifo_rptr_sync[PTR_WIDTH-1];
 140:   assign wptr_value = fifo_wptr[0+:PTRV_W];
 141:   assign rptr_sync_value = fifo_rptr_sync[0+:PTRV_W];
 142:   assign wdepth = (full_wclk) ? DepthW'(Depth) :
 143:                   (wptr_msb == rptr_sync_msb) ? DepthW'(wptr_value) - DepthW'(rptr_sync_value) :
 144:                   (DepthW'(Depth) - DepthW'(rptr_sync_value) + DepthW'(wptr_value)) ;
 145: 
 146:   // Same again in the read clock side
 147:   assign empty = (fifo_wptr_sync_combi ==  fifo_rptr);
 148:   logic  rptr_msb;
 149:   logic  wptr_sync_msb;
 150:   logic  [PTRV_W-1:0] rptr_value;
 151:   logic  [PTRV_W-1:0] wptr_sync_value;
 152:   assign wptr_sync_msb = fifo_wptr_sync_combi[PTR_WIDTH-1];
 153:   assign rptr_msb = fifo_rptr[PTR_WIDTH-1];
 154:   assign wptr_sync_value = fifo_wptr_sync_combi[0+:PTRV_W];
 155:   assign rptr_value = fifo_rptr[0+:PTRV_W];
 156:   assign rdepth = (full_rclk) ? DepthW'(Depth) :
 157:                   (wptr_sync_msb == rptr_msb) ? DepthW'(wptr_sync_value) - DepthW'(rptr_value) :
 158:                   (DepthW'(Depth) - DepthW'(rptr_value) + DepthW'(wptr_sync_value)) ;
 159: 
 160:   /////////////
 161:   // storage //
 162:   /////////////
 163: 
 164:   logic [Width-1:0] storage [Depth];
 165: 
 166:   always_ff @(posedge clk_wr_i)
 167:     if (fifo_incr_wptr) begin
 168:       storage[fifo_wptr[PTR_WIDTH-2:0]] <= wdata;
 169:     end
 170: 
 171:   assign rdata = storage[fifo_rptr[PTR_WIDTH-2:0]];
 172: 
 173:   // gray code conversion functions.  algorithm walks up from 0..N-1
 174:   // then flips the upper bit and walks down from N-1 to 0.
 175: 
 176:   function automatic [PTR_WIDTH-1:0] dec2gray(input logic [PTR_WIDTH-1:0] decval);
 177:     logic [PTR_WIDTH-1:0] decval_sub;
 178:     logic [PTR_WIDTH-2:0] decval_in;
 179:     logic                 unused_decval_msb;
 180: 
 181:     decval_sub = Depth - {1'b0,decval[PTR_WIDTH-2:0]} - 1'b1;
 182: 
 183:     {unused_decval_msb, decval_in} = decval[PTR_WIDTH-1] ? decval_sub : decval;
 184:     // Was done in two assigns for low bits and top bit
 185:     // but that generates a (bogus) verilator warning, so do in one assign
 186:     dec2gray = {decval[PTR_WIDTH-1],
 187:                 {1'b0,decval_in[PTR_WIDTH-2:1]} ^ decval_in[PTR_WIDTH-2:0]};
 188:   endfunction
 189: 
 190:   function automatic [PTR_WIDTH-1:0] gray2dec(input logic [PTR_WIDTH-1:0] grayval);
 191:     logic [PTR_WIDTH-2:0] dec_tmp, dec_tmp_sub;
 192:     logic                 unused_decsub_msb;
 193: 
 194:     dec_tmp[PTR_WIDTH-2] = grayval[PTR_WIDTH-2];
 195:     for (int i = PTR_WIDTH-3; i >= 0; i--)
 196:       dec_tmp[i] = dec_tmp[i+1]^grayval[i];
 197:     {unused_decsub_msb, dec_tmp_sub} = Depth - {1'b0,dec_tmp} - 1'b1;
 198:     if (grayval[PTR_WIDTH-1])
 199:       gray2dec = {1'b1,dec_tmp_sub};
 200:     else
 201:       gray2dec = {1'b0,dec_tmp};
 202:   endfunction
 203: 
 204:   // TODO: assertions on full, empty, gray transitions
 205: 
 206: endmodule
 207: