../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: