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