Line data Source code
1 : //********************************************************************************
2 : // SPDX-License-Identifier: Apache-2.0
3 : // Copyright 2020 Western Digital Corporation or its affiliates.
4 : // Copyright (c) 2023 Antmicro <www.antmicro.com>
5 : //
6 : // Licensed under the Apache License, Version 2.0 (the "License");
7 : // you may not use this file except in compliance with the License.
8 : // You may obtain a copy of the License at
9 : //
10 : // http://www.apache.org/licenses/LICENSE-2.0
11 : //
12 : // Unless required by applicable law or agreed to in writing, software
13 : // distributed under the License is distributed on an "AS IS" BASIS,
14 : // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 : // See the License for the specific language governing permissions and
16 : // limitations under the License.
17 : //********************************************************************************
18 :
19 : //********************************************************************************
20 : // Icache closely coupled memory --- ICCM
21 : //********************************************************************************
22 :
23 : module el2_ifu_iccm_mem
24 : import el2_pkg::*;
25 : #(
26 : `include "el2_param.vh"
27 : )(
28 69844592 : input logic clk, // Clock only while core active. Through one clock header. For flops with second clock header built in. Connected to ACTIVE_L2CLK.
29 69844592 : input logic active_clk, // Clock only while core active. Through two clock headers. For flops without second clock header built in.
30 339 : input logic rst_l, // reset, active low
31 2 : input logic clk_override, // Override non-functional clock gating
32 :
33 1074 : input logic iccm_wren, // ICCM write enable
34 134206 : input logic iccm_rden, // ICCM read enable
35 160586 : input logic [pt.ICCM_BITS-1:1] iccm_rw_addr, // ICCM read/write address
36 8 : input logic iccm_buf_correct_ecc, // ICCM is doing a single bit error correct cycle
37 8 : input logic iccm_correction_state, // ICCM under a correction - This is needed to guard replacements when hit
38 1 : input logic [2:0] iccm_wr_size, // ICCM write size
39 249 : input logic [77:0] iccm_wr_data, // ICCM write data
40 :
41 : el2_mem_if.veer_iccm iccm_mem_export, // RAM repositioned in testbench and connected by this interface
42 :
43 136799 : output logic [63:0] iccm_rd_data, // ICCM read data
44 161531 : output logic [77:0] iccm_rd_data_ecc, // ICCM read ecc
45 : // Excluding scan_mode from coverage as its usage is determined by the integrator of the VeeR core.
46 : /*verilator coverage_off*/
47 : input logic scan_mode // Scan mode control
48 : /*verilator coverage_on*/
49 :
50 : );
51 :
52 :
53 16 : logic [pt.ICCM_NUM_BANKS-1:0] wren_bank;
54 262048 : logic [pt.ICCM_NUM_BANKS-1:0] rden_bank;
55 262066 : logic [pt.ICCM_NUM_BANKS-1:0] iccm_clken;
56 160058 : logic [pt.ICCM_NUM_BANKS-1:0] [pt.ICCM_BITS-1:pt.ICCM_BANK_INDEX_LO] addr_bank;
57 :
58 22051 : logic [pt.ICCM_NUM_BANKS-1:0] [38:0] iccm_bank_dout, iccm_bank_dout_fn;
59 248 : logic [pt.ICCM_NUM_BANKS-1:0] [38:0] iccm_bank_wr_data;
60 159875 : logic [pt.ICCM_BITS-1:1] addr_bank_inc;
61 4257238 : logic [pt.ICCM_BANK_HI : 2] iccm_rd_addr_hi_q;
62 474893 : logic [pt.ICCM_BANK_HI : 1] iccm_rd_addr_lo_q;
63 161531 : logic [63:0] iccm_rd_data_pre;
64 136799 : logic [63:0] iccm_data;
65 1 : logic [1:0] addr_incr;
66 248 : logic [pt.ICCM_NUM_BANKS-1:0] [38:0] iccm_bank_wr_data_vec;
67 :
68 : // logic to handle hard persisten faults
69 0 : logic [1:0] [pt.ICCM_BITS-1:2] redundant_address;
70 1 : logic [1:0] [38:0] redundant_data;
71 1 : logic [1:0] redundant_valid;
72 8 : logic [pt.ICCM_NUM_BANKS-1:0] sel_red1, sel_red0, sel_red1_q, sel_red0_q;
73 :
74 :
75 246 : logic [38:0] redundant_data0_in, redundant_data1_in;
76 5 : logic redundant_lru, redundant_lru_in, redundant_lru_en;
77 6 : logic redundant_data0_en;
78 6 : logic redundant_data1_en;
79 4 : logic r0_addr_en, r1_addr_en;
80 :
81 : // Testing persistent flip
82 : // logic [3:0] not_iccm_bank_dout;
83 : // logic [15:3] ecc_insert_flip_in, ecc_insert_flip;
84 : // logic flip_en, flip_match, flip_match_q;
85 : //
86 : // assign flip_in = (iccm_rw_addr[3:2] != 2'b00); // dont flip when bank0 - this is to make some progress in DMA streaming cases
87 : // assign flip_en = iccm_rden;
88 : //
89 : // rvdffs #(1) flipmatch (.*,
90 : // .clk(clk),
91 : // .din(flip_in),
92 : // .en(flip_en),
93 : // .dout(flip_match_q));
94 : //
95 : // end of testing flip
96 :
97 :
98 : assign addr_incr[1:0] = (iccm_wr_size[1:0] == 2'b11) ? 2'b10: 2'b01;
99 : assign addr_bank_inc[pt.ICCM_BITS-1 : 1] = iccm_rw_addr[pt.ICCM_BITS-1 : 1] + addr_incr[1:0];
100 :
101 : for (genvar i=0; i<pt.ICCM_NUM_BANKS/2; i++) begin: mem_bank_data
102 : assign iccm_bank_wr_data_vec[(2*i)] = iccm_wr_data[38:0];
103 : assign iccm_bank_wr_data_vec[(2*i)+1] = iccm_wr_data[77:39];
104 : end
105 :
106 1360 : for (genvar i=0; i<pt.ICCM_NUM_BANKS; i++) begin: mem_bank
107 : assign wren_bank[i] = iccm_wren & ((iccm_rw_addr[pt.ICCM_BANK_HI:2] == i) | (addr_bank_inc[pt.ICCM_BANK_HI:2] == i));
108 : assign iccm_bank_wr_data[i] = iccm_bank_wr_data_vec[i];
109 : assign rden_bank[i] = iccm_rden & ( (iccm_rw_addr[pt.ICCM_BANK_HI:2] == i) | (addr_bank_inc[pt.ICCM_BANK_HI:2] == i));
110 : assign iccm_clken[i] = wren_bank[i] | rden_bank[i] | clk_override;
111 : assign addr_bank[i][pt.ICCM_BITS-1 : pt.ICCM_BANK_INDEX_LO] = wren_bank[i] ? iccm_rw_addr[pt.ICCM_BITS-1 : pt.ICCM_BANK_INDEX_LO] :
112 : ((addr_bank_inc[pt.ICCM_BANK_HI:2] == i) ?
113 : addr_bank_inc[pt.ICCM_BITS-1 : pt.ICCM_BANK_INDEX_LO] :
114 : iccm_rw_addr[pt.ICCM_BITS-1 : pt.ICCM_BANK_INDEX_LO]);
115 :
116 1360 : always_comb begin
117 1360 : iccm_mem_export.iccm_clken[i] = iccm_clken[i];
118 1360 : iccm_mem_export.iccm_wren_bank[i] = wren_bank[i];
119 1360 : iccm_mem_export.iccm_addr_bank[i] = addr_bank[i];
120 1360 : iccm_mem_export.iccm_bank_wr_data[i] = iccm_bank_wr_data[i][31:0];
121 1360 : iccm_mem_export.iccm_bank_wr_ecc[i] = iccm_bank_wr_data[i][32+pt.ICCM_ECC_WIDTH-1:32];
122 1360 : iccm_bank_dout[i][31:0] = iccm_mem_export.iccm_bank_dout[i];
123 1360 : iccm_bank_dout[i][32+pt.ICCM_ECC_WIDTH-1:32] = iccm_mem_export.iccm_bank_ecc[i];
124 : end
125 :
126 : // match the redundant rows
127 : assign sel_red1[i] = (redundant_valid[1] & (((iccm_rw_addr[pt.ICCM_BITS-1:2] == redundant_address[1][pt.ICCM_BITS-1:2]) & (iccm_rw_addr[3:2] == i)) |
128 : ((addr_bank_inc[pt.ICCM_BITS-1:2]== redundant_address[1][pt.ICCM_BITS-1:2]) & (addr_bank_inc[3:2] == i))));
129 :
130 : assign sel_red0[i] = (redundant_valid[0] & (((iccm_rw_addr[pt.ICCM_BITS-1:2] == redundant_address[0][pt.ICCM_BITS-1:2]) & (iccm_rw_addr[3:2] == i)) |
131 : ((addr_bank_inc[pt.ICCM_BITS-1:2]== redundant_address[0][pt.ICCM_BITS-1:2]) & (addr_bank_inc[3:2] == i))));
132 :
133 : rvdff #(1) selred0 (.*,
134 : .clk(active_clk),
135 : .din(sel_red0[i]),
136 : .dout(sel_red0_q[i]));
137 :
138 : rvdff #(1) selred1 (.*,
139 : .clk(active_clk),
140 : .din(sel_red1[i]),
141 : .dout(sel_red1_q[i]));
142 :
143 :
144 : // muxing out the memory data with the redundant data if the address matches
145 : assign iccm_bank_dout_fn[i][38:0] = ({39{sel_red1_q[i]}} & redundant_data[1][38:0]) |
146 : ({39{sel_red0_q[i]}} & redundant_data[0][38:0]) |
147 : ({39{~sel_red0_q[i] & ~sel_red1_q[i]}} & iccm_bank_dout[i][38:0]);
148 :
149 : end : mem_bank
150 : // This section does the redundancy for tolerating single bit errors
151 : // 2x 39 bit data values with address[hi:2] and a valid bit is needed to CAM and sub out the reads/writes to the particular locations
152 : // Also a LRU flop is kept to decide which of the redundant element to replace.
153 : assign r0_addr_en = ~redundant_lru & iccm_buf_correct_ecc;
154 : assign r1_addr_en = redundant_lru & iccm_buf_correct_ecc;
155 : assign redundant_lru_en = iccm_buf_correct_ecc | (((|sel_red0[pt.ICCM_NUM_BANKS-1:0]) | (|sel_red1[pt.ICCM_NUM_BANKS-1:0])) & iccm_rden & iccm_correction_state);
156 : assign redundant_lru_in = iccm_buf_correct_ecc ? ~redundant_lru : (|sel_red0[pt.ICCM_NUM_BANKS-1:0]) ? 1'b1 : 1'b0;
157 :
158 : rvdffs #() red_lru (.*, // LRU flop for the redundant replacements
159 : .clk(active_clk),
160 : .en(redundant_lru_en),
161 : .din(redundant_lru_in),
162 : .dout(redundant_lru));
163 :
164 : rvdffs #(pt.ICCM_BITS-2) r0_address (.*, // Redundant Row 0 address
165 : .clk(active_clk),
166 : .en(r0_addr_en),
167 : .din(iccm_rw_addr[pt.ICCM_BITS-1:2]),
168 : .dout(redundant_address[0][pt.ICCM_BITS-1:2]));
169 :
170 : rvdffs #(pt.ICCM_BITS-2) r1_address (.*, // Redundant Row 0 address
171 : .clk(active_clk),
172 : .en(r1_addr_en),
173 : .din(iccm_rw_addr[pt.ICCM_BITS-1:2]),
174 : .dout(redundant_address[1][pt.ICCM_BITS-1:2]));
175 :
176 : rvdffs #(1) r0_valid (.*,
177 : .clk(active_clk), // Redundant Row 0 Valid
178 : .en(r0_addr_en),
179 : .din(1'b1),
180 : .dout(redundant_valid[0]));
181 :
182 : rvdffs #(1) r1_valid (.*, // Redundant Row 1 Valid
183 : .clk(active_clk),
184 : .en(r1_addr_en),
185 : .din(1'b1),
186 : .dout(redundant_valid[1]));
187 :
188 :
189 :
190 : // We will have to update the Redundant copies in addition to the memory on subsequent writes to this memory location.
191 : // The data gets updated on : 1) correction cycle, 2) Future writes - this could be W writes from DMA ( match up till addr[2]) or DW writes ( match till address[3])
192 : // The data to pick also depends on the current address[2], size and the addr[2] stored in the address field of the redundant flop. Correction cycle is always W write and the data is splat on both legs, so choosing lower Word
193 :
194 : assign redundant_data0_en = ((iccm_rw_addr[pt.ICCM_BITS-1:3] == redundant_address[0][pt.ICCM_BITS-1:3]) & ((iccm_rw_addr[2] == redundant_address[0][2]) | (iccm_wr_size[1:0] == 2'b11)) & redundant_valid[0] & iccm_wren) |
195 : (~redundant_lru & iccm_buf_correct_ecc);
196 :
197 : assign redundant_data0_in[38:0] = (((iccm_rw_addr[2] == redundant_address[0][2]) & iccm_rw_addr[2]) | (redundant_address[0][2] & (iccm_wr_size[1:0] == 2'b11))) ? iccm_wr_data[77:39] : iccm_wr_data[38:0];
198 :
199 : rvdffs #(39) r0_data (.*, // Redundant Row 1 data
200 : .clk(active_clk),
201 : .en(redundant_data0_en),
202 : .din(redundant_data0_in[38:0]),
203 : .dout(redundant_data[0][38:0]));
204 :
205 : assign redundant_data1_en = ((iccm_rw_addr[pt.ICCM_BITS-1:3] == redundant_address[1][pt.ICCM_BITS-1:3]) & ((iccm_rw_addr[2] == redundant_address[1][2]) | (iccm_wr_size[1:0] == 2'b11)) & redundant_valid[1] & iccm_wren) |
206 : (redundant_lru & iccm_buf_correct_ecc);
207 :
208 : assign redundant_data1_in[38:0] = (((iccm_rw_addr[2] == redundant_address[1][2]) & iccm_rw_addr[2]) | (redundant_address[1][2] & (iccm_wr_size[1:0] == 2'b11))) ? iccm_wr_data[77:39] : iccm_wr_data[38:0];
209 :
210 : rvdffs #(39) r1_data (.*, // Redundant Row 1 data
211 : .clk(active_clk),
212 : .en(redundant_data1_en),
213 : .din(redundant_data1_in[38:0]),
214 : .dout(redundant_data[1][38:0]));
215 :
216 :
217 : rvdffs #(pt.ICCM_BANK_HI) rd_addr_lo_ff (.*, .clk(active_clk), .din(iccm_rw_addr [pt.ICCM_BANK_HI:1]), .dout(iccm_rd_addr_lo_q[pt.ICCM_BANK_HI:1]), .en(1'b1)); // bit 0 of address is always 0
218 : rvdffs #(pt.ICCM_BANK_BITS) rd_addr_hi_ff (.*, .clk(active_clk), .din(addr_bank_inc[pt.ICCM_BANK_HI:2]), .dout(iccm_rd_addr_hi_q[pt.ICCM_BANK_HI:2]), .en(1'b1));
219 :
220 : assign iccm_rd_data_pre[63:0] = {iccm_bank_dout_fn[iccm_rd_addr_hi_q][31:0], iccm_bank_dout_fn[iccm_rd_addr_lo_q[pt.ICCM_BANK_HI:2]][31:0]};
221 : assign iccm_data[63:0] = 64'({16'b0, (iccm_rd_data_pre[63:0] >> (16*iccm_rd_addr_lo_q[1]))});
222 : assign iccm_rd_data[63:0] = {iccm_data[63:0]};
223 : assign iccm_rd_data_ecc[77:0] = {iccm_bank_dout_fn[iccm_rd_addr_hi_q][38:0], iccm_bank_dout_fn[iccm_rd_addr_lo_q[pt.ICCM_BANK_HI:2]][38:0]};
224 :
225 : endmodule // el2_ifu_iccm_mem
|