hw/ip/tlul/rtl/tlul_adapter_sram.sv Cov: 99.4%

   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: /**
   6:  * Tile-Link UL adapter for SRAM-like devices
   7:  *
   8:  * - Intentionally omitted BaseAddr in case of multiple memory maps are used in a SoC,
   9:  *   it means that aliasing can happen if target slave size in TL-UL crossbar is bigger
  10:  *   than SRAM size
  11:  */
  12: module tlul_adapter_sram #(
  13:   parameter int SramAw      = 12,
  14:   parameter int SramDw      = 32, // Current version supports TL-UL width only
  15:   parameter int Outstanding = 1,  // Only one request is accepted
  16:   parameter bit ByteAccess  = 1,  // 1: true, 0: false
  17:   parameter bit ErrOnWrite  = 0,  // 1: Writes not allowed, automatically error
  18:   parameter bit ErrOnRead   = 0   // 1: Reads not allowed, automatically error
  19: ) (
  20:   input   clk_i,
  21:   input   rst_ni,
  22: 
  23:   // TL-UL interface
  24:   input   tlul_pkg::tl_h2d_t  tl_i,
  25:   output  tlul_pkg::tl_d2h_t  tl_o,
  26: 
  27:   // SRAM interface
  28:   output logic              req_o,
  29:   input                     gnt_i,
  30:   output logic              we_o,
  31:   output logic [SramAw-1:0] addr_o,
  32:   output logic [SramDw-1:0] wdata_o,
  33:   output logic [SramDw-1:0] wmask_o,
  34:   input        [SramDw-1:0] rdata_i,
  35:   input                     rvalid_i,
  36:   input        [1:0]        rerror_i // 2 bit error [1]: Uncorrectable, [0]: Correctable
  37: );
  38: 
  39:   import tlul_pkg::*;
  40: 
  41:   localparam int SramByte = SramDw/8; // TODO: Fatal if SramDw isn't multiple of 8
  42:   localparam int DataBitWidth = $clog2(SramByte);
  43: 
  44:   typedef enum logic [1:0] {
  45:     OpWrite,
  46:     OpRead,
  47:     OpUnknown
  48:   } req_op_e ;
  49: 
  50:   typedef struct packed {
  51:     req_op_e                    op ;
  52:     logic                       error ;
  53:     logic [top_pkg::TL_SZW-1:0] size ;
  54:     logic [top_pkg::TL_AIW-1:0] source ;
  55:   } req_t ;
  56: 
  57:   typedef struct packed {
  58:     logic [SramDw-1:0] data ;
  59:     logic              error ;
  60:   } rsp_t ;
  61: 
  62:   localparam int ReqFifoWidth = $bits(req_t) ;
  63:   localparam int RspFifoWidth = $bits(rsp_t) ;
  64: 
  65:   // FIFO signal in case OutStand is greater than 1
  66:   // If request is latched, {write, source} is pushed to req fifo.
  67:   // Req fifo is popped when D channel is acknowledged (v & r)
  68:   // D channel valid is asserted if it is write request or rsp fifo not empty if read.
  69:   logic reqfifo_wvalid, reqfifo_wready;
  70:   logic reqfifo_rvalid, reqfifo_rready;
  71:   req_t reqfifo_wdata,  reqfifo_rdata;
  72: 
  73:   logic rspfifo_wvalid, rspfifo_wready;
  74:   logic rspfifo_rvalid, rspfifo_rready;
  75:   rsp_t rspfifo_wdata,  rspfifo_rdata;
  76: 
  77:   logic error_internal; // Internal protocol error checker
  78:   logic wr_attr_error;
  79:   logic wr_vld_error;
  80:   logic rd_vld_error;
  81:   logic tlul_error;     // Error from `tlul_err` module
  82: 
  83:   logic a_ack, d_ack, unused_sram_ack;
  84:   assign a_ack    = tl_i.a_valid & tl_o.a_ready ;
  85:   assign d_ack    = tl_o.d_valid & tl_i.d_ready ;
  86:   assign unused_sram_ack = req_o        & gnt_i ;
  87: 
  88:   // Valid handling
  89:   logic d_valid, d_error;
  90:   always_comb begin
  91:     d_valid = 1'b0;
  92: 
  93:     if (reqfifo_rvalid) begin
  94:       if (reqfifo_rdata.error) begin
  95:         // Return error response. Assume no request went out to SRAM
  96:         d_valid = 1'b1;
  97:       end else if (reqfifo_rdata.op == OpRead) begin
  98:         d_valid = rspfifo_rvalid;
  99:       end else begin
 100:         // Write without error
 101:         d_valid = 1'b1;
 102:       end
 103:     end else begin
 104:       d_valid = 1'b0;
 105:     end
 106:   end
 107: 
 108:   always_comb begin
 109:     d_error = 1'b0;
 110: 
 111:     if (reqfifo_rvalid) begin
 112:       if (reqfifo_rdata.op == OpRead) begin
 113:         d_error = rspfifo_rdata.error | reqfifo_rdata.error;
 114:       end else begin
 115:         d_error = reqfifo_rdata.error;
 116:       end
 117:     end else begin
 118:       d_error = 1'b0;
 119:     end
 120:   end
 121: 
 122:   assign tl_o = '{
 123:       d_valid  : d_valid ,
 124:       d_opcode : (d_valid && reqfifo_rdata.op != OpRead) ? AccessAck : AccessAckData,
 125:       d_param  : '0,
 126:       d_size   : (d_valid) ? reqfifo_rdata.size : '0,
 127:       d_source : (d_valid) ? reqfifo_rdata.source : '0,
 128:       d_sink   : 1'b0,
 129:       d_data   : (d_valid && rspfifo_rvalid && reqfifo_rdata.op == OpRead)
 130:                  ? rspfifo_rdata.data : '0,
 131:       d_user   : '0,
 132:       d_error  : d_error,
 133: 
 134:       a_ready  : (gnt_i | error_internal) & reqfifo_wready
 135:   };
 136: 
 137:   // a_ready depends on the FIFO full condition and grant from SRAM (or SRAM arbiter)
 138:   // assemble response, including read response, write response, and error for unsupported stuff
 139: 
 140:   // Output to SRAM:
 141:   //    Generate request only when no internal error occurs. If error occurs, the request should be
 142:   //    dropped and returned error response to the host. So, error to be pushed to reqfifo.
 143:   //    In this case, it is assumed the request is granted (may cause ordering issue later?)
 144:   assign req_o    = tl_i.a_valid & reqfifo_wready & ~error_internal;
 145:   assign we_o     = tl_i.a_valid & logic'(tl_i.a_opcode inside {PutFullData, PutPartialData});
 146:   assign addr_o   = (tl_i.a_valid) ? tl_i.a_address[DataBitWidth+:SramAw] : '0;
 147: 
 148:   `ASSERT_INIT(TlUlEqualsToSramDw, top_pkg::TL_DW == SramDw)
 149: 
 150:   // Convert byte mask to SRAM bit mask.
 151:   always_comb begin
 152:     for (int i = 0 ; i < top_pkg::TL_DW/8 ; i++) begin
 153:       wmask_o[8*i+:8] = (tl_i.a_valid) ? {8{tl_i.a_mask[i]}} : '0;
 154:       // only forward valid data here.
 155:       wdata_o[8*i+:8] = (tl_i.a_mask[i] && we_o) ? tl_i.a_data[8*i+:8] : '0;
 156:     end
 157:   end
 158: 
 159: 
 160:   // Begin: Request Error Detection
 161: 
 162:   // wr_attr_error: Check if the request size,mask are permitted.
 163:   //    Basic check of size, mask, addr align is done in tlul_err module.
 164:   //    Here it checks any partial write if ByteAccess isn't allowed.
 165:   assign wr_attr_error = (tl_i.a_opcode == PutFullData || tl_i.a_opcode == PutPartialData) ?
 166:                          (ByteAccess == 0) ? (tl_i.a_mask != '1 || tl_i.a_size != 2'h2) : 1'b0 :
 167:                          1'b0;
 168: 
 169:   if (ErrOnWrite == 1) begin : gen_no_writes
 170:     assign wr_vld_error = tl_i.a_opcode != Get;
 171:   end else begin : gen_writes_allowed
 172:     assign wr_vld_error = 1'b0;
 173:   end
 174: 
 175:   if (ErrOnRead == 1) begin: gen_no_reads
 176:     assign rd_vld_error = tl_i.a_opcode == Get;
 177:   end else begin : gen_reads_allowed
 178:     assign rd_vld_error = 1'b0;
 179:   end
 180: 
 181:   tlul_err u_err (
 182:     .clk_i,
 183:     .rst_ni,
 184:     .tl_i,
 185:     .err_o (tlul_error)
 186:   );
 187: 
 188:   assign error_internal = wr_attr_error | wr_vld_error | rd_vld_error | tlul_error;
 189:   // End: Request Error Detection
 190: 
 191:   assign reqfifo_wvalid = a_ack ; // Push to FIFO only when granted
 192:   assign reqfifo_wdata  = '{
 193:     op:     (tl_i.a_opcode != Get) ? OpWrite : OpRead, // To return AccessAck for opcode error
 194:     error:  error_internal,
 195:     size:   tl_i.a_size,
 196:     source: tl_i.a_source
 197:   }; // Store the request only. Doesn't have to store data
 198:   assign reqfifo_rready = d_ack ;
 199: 
 200:   assign rspfifo_wvalid = rvalid_i & reqfifo_rvalid;
 201:   assign rspfifo_wdata  = '{
 202:     data:  rdata_i,
 203:     error: rerror_i[1]  // Only care for Uncorrectable error
 204:   };
 205:   assign rspfifo_rready = (reqfifo_rdata.op == OpRead & ~reqfifo_rdata.error)
 206:                         ? reqfifo_rready : 1'b0 ;
 207: 
 208:   // FIFO instance: REQ, RSP
 209: 
 210:   // ReqFIFO is to store the Access type to match to the Response data.
 211:   //    For instance, SRAM accepts the write request but doesn't return the
 212:   //    acknowledge. In this case, it may be hard to determine when the D
 213:   //    response for the write data should send out if reads/writes are
 214:   //    interleaved. So, to make it in-order (even TL-UL allows out-of-order
 215:   //    responses), storing the request is necessary. And if the read entry
 216:   //    is write op, it is safe to return the response right away. If it is
 217:   //    read reqeust, then D response is waiting until read data arrives.
 218:   prim_fifo_sync #(
 219:     .Width  (ReqFifoWidth),
 220:     .Pass   (1'b0),
 221:   // The oustanding+1 allows the reqfifo to absorb back to back transactions
 222:   // without any wait states.  Alternatively, the depth can be kept as
 223:   // oustanding as long as the outgoing ready is qualified with the acceptance
 224:   // of the response in the same cycle.  Doing so however creates a path from
 225:   // ready_i to ready_o, which may not be desireable.
 226:     .Depth  (Outstanding+1'b1)
 227:   ) u_reqfifo (
 228:     .clk_i,
 229:     .rst_ni,
 230:     .clr_i  (1'b0),
 231:     .wvalid (reqfifo_wvalid),
 232:     .wready (reqfifo_wready),
 233:     .wdata  (reqfifo_wdata),
 234:     .depth  (),
 235:     .rvalid (reqfifo_rvalid),
 236:     .rready (reqfifo_rready),
 237:     .rdata  (reqfifo_rdata)
 238:   );
 239: 
 240:   // Rationale having #Outstanding depth in response FIFO.
 241:   //    In normal case, if the host or the crossbar accepts the response data,
 242:   //    response FIFO isn't needed. But if in any case it has a chance to be
 243:   //    back pressured, the response FIFO should store the returned data not to
 244:   //    lose the data from the SRAM interface. Remember, SRAM interface doesn't
 245:   //    have back-pressure signal such as read_ready.
 246:   prim_fifo_sync #(
 247:     .Width (RspFifoWidth),
 248:     .Pass  (1'b1),
 249:     .Depth (Outstanding)
 250:   ) u_rspfifo (
 251:     .clk_i,
 252:     .rst_ni,
 253:     .clr_i  (1'b0),
 254:     .wvalid (rspfifo_wvalid),
 255:     .wready (rspfifo_wready),
 256:     .wdata  (rspfifo_wdata),
 257:     .depth  (),
 258:     .rvalid (rspfifo_rvalid),
 259:     .rready (rspfifo_rready),
 260:     .rdata  (rspfifo_rdata)
 261:   );
 262: 
 263:   // below assertion fails when SRAM rvalid is asserted even though ReqFifo is empty
 264:   `ASSERT(rvalidHighReqFifoEmpty, rvalid_i |-> reqfifo_rvalid, clk_i, !rst_ni)
 265: 
 266:   // below assertion fails when outstanding value is too small (SRAM rvalid is asserted
 267:   // even though the RspFifo is full)
 268:   `ASSERT(rvalidHighWhenRspFifoFull, rvalid_i |-> rspfifo_wready, clk_i, !rst_ni)
 269: 
 270:   // If both ErrOnWrite and ErrOnRead are set, this block is useless
 271:   `ASSERT_INIT(adapterNoReadOrWrite, (ErrOnWrite & ErrOnRead) == 0)
 272: 
 273:   // make sure outputs are defined
 274:   `ASSERT_KNOWN(TlOutKnown_A,    tl_o,    clk_i, !rst_ni)
 275:   `ASSERT_KNOWN(ReqOutKnown_A,   req_o,   clk_i, !rst_ni)
 276:   `ASSERT_KNOWN(WeOutKnown_A,    we_o,    clk_i, !rst_ni)
 277:   `ASSERT_KNOWN(AddrOutKnown_A,  addr_o,  clk_i, !rst_ni)
 278:   `ASSERT_KNOWN(WdataOutKnown_A, wdata_o, clk_i, !rst_ni)
 279:   `ASSERT_KNOWN(WmaskOutKnown_A, wmask_o, clk_i, !rst_ni)
 280: 
 281: endmodule
 282: