../src/lowrisc_prim_ram_2p_async_adv_0.1/rtl/prim_ram_2p_async_adv.sv Cov: 69.3%

   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: // Asynchronous Dual-Port SRAM Wrapper
   6: //
   7: // Supported configurations:
   8: // - ECC for 32b wide memories with no write mask
   9: //   (Width == 32 && DataBitsPerMask == 32).
  10: // - Byte parity if Width is a multiple of 8 bit and write masks have Byte
  11: //   granularity (DataBitsPerMask == 8).
  12: //
  13: // Note that the write mask needs to be per Byte if parity is enabled. If ECC is enabled, the write
  14: // mask cannot be used and has to be tied to {Width{1'b1}}.
  15: 
  16: `include "prim_assert.sv"
  17: `include "prim_util.svh"
  18: 
  19: module prim_ram_2p_async_adv #(
  20:   parameter  int Depth                = 512,
  21:   parameter  int Width                = 32,
  22:   parameter  int DataBitsPerMask      = 1,  // Number of data bits per bit of write mask
  23:   parameter  int CfgW                 = 8,  // WTC, RTC, etc
  24:   parameter      MemInitFile          = "", // VMEM file to initialize the memory with
  25: 
  26:   // Configurations
  27:   parameter  bit EnableECC            = 0, // Enables per-word ECC
  28:   parameter  bit EnableParity         = 0, // Enables per-Byte Parity
  29:   parameter  bit EnableInputPipeline  = 0, // Adds an input register (read latency +1)
  30:   parameter  bit EnableOutputPipeline = 0, // Adds an output register (read latency +1)
  31: 
  32:   localparam int Aw                   = vbits(Depth)
  33: ) (
  34:   input clk_a_i,
  35:   input clk_b_i,
  36:   input rst_a_ni,
  37:   input rst_b_ni,
  38: 
  39:   input                    a_req_i,
  40:   input                    a_write_i,
  41:   input        [Aw-1:0]    a_addr_i,
  42:   input        [Width-1:0] a_wdata_i,
  43:   input        [Width-1:0] a_wmask_i,  // cannot be used with ECC, tie to 1 in that case
  44:   output logic [Width-1:0] a_rdata_o,
  45:   output logic             a_rvalid_o, // read response (a_rdata_o) is valid
  46:   output logic [1:0]       a_rerror_o, // Bit1: Uncorrectable, Bit0: Correctable
  47: 
  48:   input                    b_req_i,
  49:   input                    b_write_i,
  50:   input        [Aw-1:0]    b_addr_i,
  51:   input        [Width-1:0] b_wdata_i,
  52:   input        [Width-1:0] b_wmask_i,  // cannot be used with ECC, tie to 1 in that case
  53:   output logic [Width-1:0] b_rdata_o,
  54:   output logic             b_rvalid_o, // read response (b_rdata_o) is valid
  55:   output logic [1:0]       b_rerror_o, // Bit1: Uncorrectable, Bit0: Correctable
  56: 
  57:   // config
  58:   input [CfgW-1:0] cfg_i
  59: );
  60: 
  61:   `ASSERT_INIT(CannotHaveEccAndParity_A, !(EnableParity && EnableECC))
  62: 
  63:   // While we require DataBitsPerMask to be per Byte (8) at the interface in case Byte parity is
  64:   // enabled, we need to switch this to a per-bit mask locally such that we can individually enable
  65:   // the parity bits to be written alongside the data.
  66:   localparam int LocalDataBitsPerMask = (EnableParity) ? 1 : DataBitsPerMask;
  67: 
  68:   // Calculate ECC width
  69:   localparam int ParWidth  = (EnableParity) ? Width/8 :
  70:                              (!EnableECC)   ? 0 :
  71:                              (Width <=   4) ? 4 :
  72:                              (Width <=  11) ? 5 :
  73:                              (Width <=  26) ? 6 :
  74:                              (Width <=  57) ? 7 :
  75:                              (Width <= 120) ? 8 : 8 ;
  76:   localparam int TotalWidth = Width + ParWidth;
  77: 
  78:   ////////////////////////////
  79:   // RAM Primitive Instance //
  80:   ////////////////////////////
  81: 
  82:   logic                    a_req_q,    a_req_d ;
  83:   logic                    a_write_q,  a_write_d ;
  84:   logic [Aw-1:0]           a_addr_q,   a_addr_d ;
  85:   logic [TotalWidth-1:0]   a_wdata_q,  a_wdata_d ;
  86:   logic [TotalWidth-1:0]   a_wmask_q,  a_wmask_d ;
  87:   logic                    a_rvalid_q, a_rvalid_d, a_rvalid_sram ;
  88:   logic [Width-1:0]        a_rdata_q,  a_rdata_d ;
  89:   logic [TotalWidth-1:0]   a_rdata_sram ;
  90:   logic [1:0]              a_rerror_q, a_rerror_d ;
  91: 
  92:   logic                    b_req_q,    b_req_d ;
  93:   logic                    b_write_q,  b_write_d ;
  94:   logic [Aw-1:0]           b_addr_q,   b_addr_d ;
  95:   logic [TotalWidth-1:0]   b_wdata_q,  b_wdata_d ;
  96:   logic [TotalWidth-1:0]   b_wmask_q,  b_wmask_d ;
  97:   logic                    b_rvalid_q, b_rvalid_d, b_rvalid_sram ;
  98:   logic [Width-1:0]        b_rdata_q,  b_rdata_d ;
  99:   logic [TotalWidth-1:0]   b_rdata_sram ;
 100:   logic [1:0]              b_rerror_q, b_rerror_d ;
 101: 
 102:   prim_ram_2p #(
 103:     .MemInitFile     (MemInitFile),
 104: 
 105:     .Width           (TotalWidth),
 106:     .Depth           (Depth),
 107:     .DataBitsPerMask (LocalDataBitsPerMask)
 108:   ) u_mem (
 109:     .clk_a_i    (clk_a_i),
 110:     .clk_b_i    (clk_b_i),
 111: 
 112:     .a_req_i    (a_req_q),
 113:     .a_write_i  (a_write_q),
 114:     .a_addr_i   (a_addr_q),
 115:     .a_wdata_i  (a_wdata_q),
 116:     .a_wmask_i  (a_wmask_q),
 117:     .a_rdata_o  (a_rdata_sram),
 118: 
 119:     .b_req_i    (b_req_q),
 120:     .b_write_i  (b_write_q),
 121:     .b_addr_i   (b_addr_q),
 122:     .b_wdata_i  (b_wdata_q),
 123:     .b_wmask_i  (b_wmask_q),
 124:     .b_rdata_o  (b_rdata_sram)
 125:   );
 126: 
 127:   always_ff @(posedge clk_a_i or negedge rst_a_ni) begin
 128:     if (!rst_a_ni) begin
 129:       a_rvalid_sram <= 1'b0;
 130:     end else begin
 131:       a_rvalid_sram <= a_req_q & ~a_write_q;
 132:     end
 133:   end
 134:   always_ff @(posedge clk_b_i or negedge rst_b_ni) begin
 135:     if (!rst_b_ni) begin
 136:       b_rvalid_sram <= 1'b0;
 137:     end else begin
 138:       b_rvalid_sram <= b_req_q & ~b_write_q;
 139:     end
 140:   end
 141: 
 142:   assign a_req_d              = a_req_i;
 143:   assign a_write_d            = a_write_i;
 144:   assign a_addr_d             = a_addr_i;
 145:   assign a_rvalid_o           = a_rvalid_q;
 146:   assign a_rdata_o            = a_rdata_q;
 147:   assign a_rerror_o           = a_rerror_q;
 148: 
 149:   assign b_req_d              = b_req_i;
 150:   assign b_write_d            = b_write_i;
 151:   assign b_addr_d             = b_addr_i;
 152:   assign b_rvalid_o           = b_rvalid_q;
 153:   assign b_rdata_o            = b_rdata_q;
 154:   assign b_rerror_o           = b_rerror_q;
 155: 
 156:   /////////////////////////////
 157:   // ECC / Parity Generation //
 158:   /////////////////////////////
 159: 
 160:   if (EnableParity == 0 && EnableECC) begin : gen_secded
 161: 
 162:     // check supported widths
 163:     `ASSERT_INIT(SecDecWidth_A, Width inside {32})
 164: 
 165:     // the wmask is constantly set to 1 in this case
 166:     `ASSERT(OnlyWordWritePossibleWithEccPortA_A, a_req_i |->
 167:         a_wmask_i == {TotalWidth{1'b1}}, clk_a_i, rst_a_ni)
 168:     `ASSERT(OnlyWordWritePossibleWithEccPortB_A, b_req_i |->
 169:         b_wmask_i == {TotalWidth{1'b1}}, clk_b_i, rst_b_ni)
 170: 
 171:     assign a_wmask_d = {TotalWidth{1'b1}};
 172:     assign b_wmask_d = {TotalWidth{1'b1}};
 173: 
 174:     if (Width == 32) begin : gen_secded_39_32
 175:       prim_secded_39_32_enc u_enc_a (.in(a_wdata_i), .out(a_wdata_d));
 176:       prim_secded_39_32_dec u_dec_a (
 177:         .in         (a_rdata_sram),
 178:         .d_o        (a_rdata_d[0+:Width]),
 179:         .syndrome_o ( ),
 180:         .err_o      (a_rerror_d)
 181:       );
 182:       prim_secded_39_32_enc u_enc_b (.in(b_wdata_i), .out(b_wdata_d));
 183:       prim_secded_39_32_dec u_dec_b (
 184:         .in         (b_rdata_sram),
 185:         .d_o        (b_rdata_d[0+:Width]),
 186:         .syndrome_o ( ),
 187:         .err_o      (b_rerror_d)
 188:       );
 189:     end
 190:   end else if (EnableParity) begin : gen_byte_parity
 191: 
 192:     `ASSERT_INIT(ParityNeedsByteWriteMask_A, DataBitsPerMask == 8)
 193:     `ASSERT_INIT(WidthNeedsToBeByteAligned_A, Width % 8 == 0)
 194: 
 195:     always_comb begin : p_parity
 196:       a_rerror_d = '0;
 197:       b_rerror_d = '0;
 198:       a_wmask_d[0+:Width] = a_wmask_i;
 199:       b_wmask_d[0+:Width] = b_wmask_i;
 200:       a_wdata_d[0+:Width] = a_wdata_i;
 201:       b_wdata_d[0+:Width] = b_wdata_i;
 202: 
 203:       for (int i = 0; i < Width/8; i ++) begin
 204:         // parity generation (odd parity)
 205:         a_wdata_d[Width + i] = ~(^a_wdata_i[i*8 +: 8]);
 206:         b_wdata_d[Width + i] = ~(^b_wdata_i[i*8 +: 8]);
 207:         a_wmask_d[Width + i] = &a_wmask_i[i*8 +: 8];
 208:         b_wmask_d[Width + i] = &b_wmask_i[i*8 +: 8];
 209:         // parity decoding (errors are always uncorrectable)
 210:         a_rerror_d[1] |= ~(^{a_rdata_sram[i*8 +: 8], a_rdata_sram[Width + i]});
 211:         b_rerror_d[1] |= ~(^{b_rdata_sram[i*8 +: 8], b_rdata_sram[Width + i]});
 212:       end
 213:       // tie to zero if the read data is not valid
 214:       a_rerror_d &= {2{a_rvalid_sram}};
 215:       b_rerror_d &= {2{b_rvalid_sram}};
 216:     end
 217: 
 218:     assign a_rdata_d  = a_rdata_sram[0+:Width];
 219:     assign b_rdata_d  = b_rdata_sram[0+:Width];
 220:   end else begin : gen_nosecded_noparity
 221:     assign a_wmask_d  = a_wmask_i;
 222:     assign b_wmask_d  = b_wmask_i;
 223:     assign a_wdata_d  = a_wdata_i;
 224:     assign b_wdata_d  = b_wdata_i;
 225:     assign a_rdata_d  = a_rdata_sram[0+:Width];
 226:     assign b_rdata_d  = b_rdata_sram[0+:Width];
 227:     assign a_rerror_d = '0;
 228:     assign b_rerror_d = '0;
 229:   end
 230: 
 231:   assign a_rvalid_d = a_rvalid_sram;
 232:   assign b_rvalid_d = b_rvalid_sram;
 233: 
 234:   /////////////////////////////////////
 235:   // Input/Output Pipeline Registers //
 236:   /////////////////////////////////////
 237: 
 238:   if (EnableInputPipeline) begin : gen_regslice_input
 239:     // Put the register slices between ECC encoding to SRAM port
 240:     always_ff @(posedge clk_a_i or negedge rst_a_ni) begin
 241:       if (!rst_a_ni) begin
 242:         a_req_q   <= '0;
 243:         a_write_q <= '0;
 244:         a_addr_q  <= '0;
 245:         a_wdata_q <= '0;
 246:         a_wmask_q <= '0;
 247:       end else begin
 248:         a_req_q   <= a_req_d;
 249:         a_write_q <= a_write_d;
 250:         a_addr_q  <= a_addr_d;
 251:         a_wdata_q <= a_wdata_d;
 252:         a_wmask_q <= a_wmask_d;
 253:       end
 254:     end
 255:     always_ff @(posedge clk_b_i or negedge rst_b_ni) begin
 256:       if (!rst_b_ni) begin
 257:         b_req_q   <= '0;
 258:         b_write_q <= '0;
 259:         b_addr_q  <= '0;
 260:         b_wdata_q <= '0;
 261:         b_wmask_q <= '0;
 262:       end else begin
 263:         b_req_q   <= b_req_d;
 264:         b_write_q <= b_write_d;
 265:         b_addr_q  <= b_addr_d;
 266:         b_wdata_q <= b_wdata_d;
 267:         b_wmask_q <= b_wmask_d;
 268:       end
 269:     end
 270:   end else begin : gen_dirconnect_input
 271:     assign a_req_q   = a_req_d;
 272:     assign a_write_q = a_write_d;
 273:     assign a_addr_q  = a_addr_d;
 274:     assign a_wdata_q = a_wdata_d;
 275:     assign a_wmask_q = a_wmask_d;
 276: 
 277:     assign b_req_q   = b_req_d;
 278:     assign b_write_q = b_write_d;
 279:     assign b_addr_q  = b_addr_d;
 280:     assign b_wdata_q = b_wdata_d;
 281:     assign b_wmask_q = b_wmask_d;
 282:   end
 283: 
 284:   if (EnableOutputPipeline) begin : gen_regslice_output
 285:     // Put the register slices between ECC decoding to output
 286:     always_ff @(posedge clk_a_i or negedge rst_a_ni) begin
 287:       if (!rst_a_ni) begin
 288:         a_rvalid_q <= '0;
 289:         a_rdata_q  <= '0;
 290:         a_rerror_q <= '0;
 291:       end else begin
 292:         a_rvalid_q <= a_rvalid_d;
 293:         a_rdata_q  <= a_rdata_d;
 294:         a_rerror_q <= a_rerror_d;
 295:       end
 296:     end
 297:     always_ff @(posedge clk_b_i or negedge rst_b_ni) begin
 298:       if (!rst_b_ni) begin
 299:         b_rvalid_q <= '0;
 300:         b_rdata_q  <= '0;
 301:         b_rerror_q <= '0;
 302:       end else begin
 303:         b_rvalid_q <= b_rvalid_d;
 304:         b_rdata_q  <= b_rdata_d;
 305:         b_rerror_q <= b_rerror_d;
 306:       end
 307:     end
 308:   end else begin : gen_dirconnect_output
 309:     assign a_rvalid_q = a_rvalid_d;
 310:     assign a_rdata_q  = a_rdata_d;
 311:     assign a_rerror_q = a_rerror_d;
 312: 
 313:     assign b_rvalid_q = b_rvalid_d;
 314:     assign b_rdata_q  = b_rdata_d;
 315:     assign b_rerror_q = b_rerror_d;
 316:   end
 317: 
 318: endmodule : prim_ram_2p_async_adv
 319: