../src/lowrisc_prim_ram_1p_adv_0.1/rtl/prim_ram_1p_adv.sv Cov: 66.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: // Single-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_1p_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_i,
35: input rst_ni,
36:
37: input req_i,
38: input write_i,
39: input [Aw-1:0] addr_i,
40: input [Width-1:0] wdata_i,
41: input [Width-1:0] wmask_i,
42: output logic [Width-1:0] rdata_o,
43: output logic rvalid_o, // read response (rdata_o) is valid
44: output logic [1:0] rerror_o, // Bit1: Uncorrectable, Bit0: Correctable
45:
46: // config
47: input [CfgW-1:0] cfg_i
48: );
49:
50: `ASSERT_INIT(CannotHaveEccAndParity_A, !(EnableParity && EnableECC))
51:
52: // While we require DataBitsPerMask to be per Byte (8) at the interface in case Byte parity is
53: // enabled, we need to switch this to a per-bit mask locally such that we can individually enable
54: // the parity bits to be written alongside the data.
55: localparam int LocalDataBitsPerMask = (EnableParity) ? 1 : DataBitsPerMask;
56:
57: // Calculate ECC width
58: localparam int ParWidth = (EnableParity) ? Width/8 :
59: (!EnableECC) ? 0 :
60: (Width <= 4) ? 4 :
61: (Width <= 11) ? 5 :
62: (Width <= 26) ? 6 :
63: (Width <= 57) ? 7 :
64: (Width <= 120) ? 8 : 8 ;
65: localparam int TotalWidth = Width + ParWidth;
66:
67: ////////////////////////////
68: // RAM Primitive Instance //
69: ////////////////////////////
70:
71: logic req_q, req_d ;
72: logic write_q, write_d ;
73: logic [Aw-1:0] addr_q, addr_d ;
74: logic [TotalWidth-1:0] wdata_q, wdata_d ;
75: logic [TotalWidth-1:0] wmask_q, wmask_d ;
76: logic rvalid_q, rvalid_d, rvalid_sram ;
77: logic [Width-1:0] rdata_q, rdata_d ;
78: logic [TotalWidth-1:0] rdata_sram ;
79: logic [1:0] rerror_q, rerror_d ;
80:
81: prim_ram_1p #(
82: .MemInitFile (MemInitFile),
83:
84: .Width (TotalWidth),
85: .Depth (Depth),
86: .DataBitsPerMask (LocalDataBitsPerMask)
87: ) u_mem (
88: .clk_i,
89:
90: .req_i (req_q),
91: .write_i (write_q),
92: .addr_i (addr_q),
93: .wdata_i (wdata_q),
94: .wmask_i (wmask_q),
95: .rdata_o (rdata_sram)
96: );
97:
98: always_ff @(posedge clk_i or negedge rst_ni) begin
99: if (!rst_ni) begin
100: rvalid_sram <= 1'b0;
101: end else begin
102: rvalid_sram <= req_q & ~write_q;
103: end
104: end
105:
106: assign req_d = req_i;
107: assign write_d = write_i;
108: assign addr_d = addr_i;
109: assign rvalid_o = rvalid_q;
110: assign rdata_o = rdata_q;
111: assign rerror_o = rerror_q;
112:
113: /////////////////////////////
114: // ECC / Parity Generation //
115: /////////////////////////////
116:
117: if (EnableParity == 0 && EnableECC) begin : gen_secded
118:
119: // check supported widths
120: `ASSERT_INIT(SecDecWidth_A, Width inside {32})
121:
122: // the wmask is constantly set to 1 in this case
123: `ASSERT(OnlyWordWritePossibleWithEccPortA_A, req_i |->
124: wmask_i == {TotalWidth{1'b1}})
125:
126: assign wmask_d = {TotalWidth{1'b1}};
127:
128: if (Width == 32) begin : gen_secded_39_32
129: prim_secded_39_32_enc u_enc (.in(wdata_i), .out(wdata_d));
130: prim_secded_39_32_dec u_dec (
131: .in (rdata_sram),
132: .d_o (rdata_d[0+:Width]),
133: .syndrome_o ( ),
134: .err_o (rerror_d)
135: );
136: end
137: end else if (EnableParity) begin : gen_byte_parity
138:
139: `ASSERT_INIT(WidthNeedsToBeByteAligned_A, Width % 8 == 0)
140: `ASSERT_INIT(ParityNeedsByteWriteMask_A, DataBitsPerMask == 8)
141:
142: always_comb begin : p_parity
143: rerror_d = '0;
144: wmask_d[0+:Width] = wmask_i;
145: wdata_d[0+:Width] = wdata_i;
146:
147: for (int i = 0; i < Width/8; i ++) begin
148: // parity generation (odd parity)
149: wdata_d[Width + i] = ~(^wdata_i[i*8 +: 8]);
150: wmask_d[Width + i] = &wmask_i[i*8 +: 8];
151: // parity decoding (errors are always uncorrectable)
152: rerror_d[1] |= ~(^{rdata_sram[i*8 +: 8], rdata_sram[Width + i]});
153: end
154: // tie to zero if the read data is not valid
155: rerror_d &= {2{rvalid_sram}};
156: end
157:
158: assign rdata_d = rdata_sram[0+:Width];
159: end else begin : gen_nosecded_noparity
160: assign wmask_d = wmask_i;
161: assign wdata_d = wdata_i;
162:
163: assign rdata_d = rdata_sram[0+:Width];
164: assign rerror_d = '0;
165: end
166:
167: assign rvalid_d = rvalid_sram;
168:
169: /////////////////////////////////////
170: // Input/Output Pipeline Registers //
171: /////////////////////////////////////
172:
173: if (EnableInputPipeline) begin : gen_regslice_input
174: // Put the register slices between ECC encoding to SRAM port
175: always_ff @(posedge clk_i or negedge rst_ni) begin
176: if (!rst_ni) begin
177: req_q <= '0;
178: write_q <= '0;
179: addr_q <= '0;
180: wdata_q <= '0;
181: wmask_q <= '0;
182: end else begin
183: req_q <= req_d;
184: write_q <= write_d;
185: addr_q <= addr_d;
186: wdata_q <= wdata_d;
187: wmask_q <= wmask_d;
188: end
189: end
190: end else begin : gen_dirconnect_input
191: assign req_q = req_d;
192: assign write_q = write_d;
193: assign addr_q = addr_d;
194: assign wdata_q = wdata_d;
195: assign wmask_q = wmask_d;
196: end
197:
198: if (EnableOutputPipeline) begin : gen_regslice_output
199: // Put the register slices between ECC decoding to output
200: always_ff @(posedge clk_i or negedge rst_ni) begin
201: if (!rst_ni) begin
202: rvalid_q <= '0;
203: rdata_q <= '0;
204: rerror_q <= '0;
205: end else begin
206: rvalid_q <= rvalid_d;
207: rdata_q <= rdata_d;
208: rerror_q <= rerror_d;
209: end
210: end
211: end else begin : gen_dirconnect_output
212: assign rvalid_q = rvalid_d;
213: assign rdata_q = rdata_d;
214: assign rerror_q = rerror_d;
215: end
216:
217: endmodule : prim_ram_1p_adv
218: