@@ -46,6 +46,7 @@ module snitch_icache_lookup_serial #(
4646);
4747
4848 localparam int unsigned DataAddrWidth = $clog2 (CFG .SET_COUNT ) + CFG .COUNT_ALIGN ;
49+ localparam int unsigned TagParity = (CFG .L1_DATA_PARITY_BITS > 0 ) ? 1 : 0 ;
4950
5051`ifndef SYNTHESIS
5152 initial assert (CFG != '0 );
@@ -81,13 +82,19 @@ module snitch_icache_lookup_serial #(
8182 logic error;
8283 } tag_rsp_t ;
8384
85+ typedef struct packed {
86+ logic [ CFG .FETCH_AW - 1 : 0 ] addr;
87+ logic [CFG .SET_ALIGN - 1 : 0 ] cset;
88+ logic parity_error;
89+ } tag_inv_req_t ;
90+
8491 logic req_valid, req_ready;
8592 logic req_handshake;
8693
87- logic [CFG .COUNT_ALIGN - 1 : 0 ] tag_addr;
88- logic [CFG .SET_COUNT - 1 : 0 ] tag_enable;
89- logic [CFG .TAG_WIDTH + 1 : 0 ] tag_wdata, tag_rdata [CFG .SET_COUNT ];
90- logic tag_write;
94+ logic [CFG .COUNT_ALIGN - 1 : 0 ] tag_addr;
95+ logic [CFG .SET_COUNT - 1 : 0 ] tag_enable;
96+ logic [CFG .TAG_WIDTH + 1 + TagParity : 0 ] tag_wdata, tag_rdata [CFG .SET_COUNT ];
97+ logic tag_write;
9198
9299 tag_req_t tag_req_d, tag_req_q;
93100 tag_rsp_t tag_rsp_s, tag_rsp_d, tag_rsp_q, tag_rsp;
@@ -96,36 +103,82 @@ module snitch_icache_lookup_serial #(
96103
97104 logic [CFG .TAG_WIDTH - 1 : 0 ] required_tag;
98105 logic [CFG .SET_COUNT - 1 : 0 ] line_hit;
106+ logic [CFG .SET_COUNT - 1 : 0 ] tag_parity_error_d, tag_parity_error_q;
107+ logic faulty_hit_valid, faulty_hit_ready, faulty_hit_d, faulty_hit_q;
99108
100109 logic [DataAddrWidth- 1 : 0 ] lookup_addr;
101110 logic [DataAddrWidth- 1 : 0 ] write_addr;
102111
112+ tag_inv_req_t data_parity_inv_d, data_parity_inv_q;
113+ logic data_fault_valid, data_fault_ready;
114+
103115 // Connect input requests to tag stage
104116 assign tag_req_d.addr = in_addr_i;
105117 assign tag_req_d.id = in_id_i;
106118
107119 // Multiplex read and write access to the tag banks onto one port, prioritizing write accesses
120+
121+ logic tag_parity_bit;
122+ if (TagParity > 0 ) begin : gen_tag_parity_bit
123+ always_comb begin
124+ tag_parity_bit = ^ write_tag_i;
125+ if (init_phase) begin
126+ tag_parity_bit = 1'b0 ;
127+ end else if (data_fault_valid) begin
128+ tag_parity_bit = 1'b1 ;
129+ end else if (write_valid_i) begin
130+ tag_parity_bit = ^ write_tag_i;
131+ end else if (faulty_hit_valid) begin
132+ tag_parity_bit = 1'b1 ;
133+ end else if (in_valid_i) begin
134+ // read phase: write tag not used
135+ end
136+ tag_wdata[CFG .TAG_WIDTH + 2 ] = tag_parity_bit;
137+ end
138+ end else begin : gen_no_tag_parity_bit
139+ assign tag_parity_bit = '0 ;
140+ end
141+
142+ assign data_fault_valid = (CFG .L1_DATA_PARITY_BITS > 0 ) ? data_parity_inv_q : '0 ;
143+
108144 always_comb begin
109- tag_addr = in_addr_i[CFG .LINE_ALIGN + : CFG .COUNT_ALIGN ];
110- tag_enable = '0 ;
111- tag_wdata = { 1'b1 , write_error_i, write_tag_i} ;
112- tag_write = 1'b0 ;
145+ tag_addr = in_addr_i[CFG .LINE_ALIGN + : CFG .COUNT_ALIGN ];
146+ tag_enable = '0 ;
147+ tag_wdata[ CFG . TAG_WIDTH + 1 : 0 ] = { 1'b1 , write_error_i, write_tag_i} ;
148+ tag_write = 1'b0 ;
113149
114150 write_ready_o = 1'b0 ;
115151 in_ready_o = 1'b0 ;
116152 req_valid = 1'b0 ;
117153
154+ data_fault_ready = 1'b0 ;
155+ faulty_hit_ready = 1'b0 ;
156+
118157 if (init_phase) begin
119- tag_addr = init_count_q;
120- tag_enable = '1 ;
121- tag_wdata = '0 ;
122- tag_write = 1'b1 ;
158+ tag_addr = init_count_q;
159+ tag_enable = '1 ;
160+ tag_wdata[CFG .TAG_WIDTH + 1 : 0 ] = '0 ;
161+ tag_write = 1'b1 ;
162+ end else if (data_fault_valid) begin // Only if data has parity
163+ tag_addr = data_parity_inv_q.addr >> CFG .LINE_ALIGN ;
164+ tag_enable = $unsigned (1 << data_parity_inv_q.cset);
165+ tag_wdata[CFG .TAG_WIDTH + 1 : 0 ] = '0 ;
166+ tag_write = 1'b1 ;
167+ data_fault_ready = 1'b1 ;
123168 end else if (write_valid_i) begin
124169 // Write a refill request
125170 tag_addr = write_addr_i;
126171 tag_enable = $unsigned (1 << write_set_i);
127172 tag_write = 1'b1 ;
128173 write_ready_o = 1'b1 ;
174+ end else if (faulty_hit_valid) begin // Only if tag has parity
175+ // we need to set second bit (valid) of write data of the previous adress to 0
176+ // we do not accept read requests and we do not store data in the pipeline.
177+ tag_addr = tag_req_q >> CFG .LINE_ALIGN ; // buffered version of in_addr_i
178+ tag_enable = tag_parity_error_q; // faulty set must be written to
179+ tag_wdata[CFG .TAG_WIDTH + 1 : 0 ] = '0 ;
180+ tag_write = 1'b0 ;
181+ faulty_hit_ready = 1'b1 ;
129182 end else if (in_valid_i) begin
130183 // Check cache
131184 tag_enable = '1 ;
@@ -139,8 +192,8 @@ module snitch_icache_lookup_serial #(
139192 if (CFG .L1_TAG_SCM ) begin : gen_scm
140193 for (genvar i = 0 ; i < CFG .SET_COUNT ; i++ ) begin : g_sets
141194 register_file_1r_1w # (
142- .ADDR_WIDTH ($clog2 (CFG .LINE_COUNT )),
143- .DATA_WIDTH (CFG .TAG_WIDTH + 2 )
195+ .ADDR_WIDTH ($clog2 (CFG .LINE_COUNT ) ),
196+ .DATA_WIDTH (CFG .TAG_WIDTH + 2 + TagParity )
144197 ) i_tag (
145198 .clk ( clk_i ),
146199 .ReadEnable ( tag_enable[i] && ! tag_write ),
@@ -152,16 +205,17 @@ module snitch_icache_lookup_serial #(
152205 );
153206 end
154207 end else begin : gen_sram
155- logic [CFG .SET_COUNT * (CFG .TAG_WIDTH + 2 )- 1 : 0 ] tag_rdata_flat;
208+ logic [CFG .SET_COUNT * (CFG .TAG_WIDTH + 2 + TagParity )- 1 : 0 ] tag_rdata_flat;
156209 for (genvar i = 0 ; i < CFG .SET_COUNT ; i++ ) begin : g_sets_rdata
157- assign tag_rdata[i] = tag_rdata_flat[i* (CFG .TAG_WIDTH + 2 )+ : CFG .TAG_WIDTH + 2 ];
210+ assign tag_rdata[i] = tag_rdata_flat[i* (CFG .TAG_WIDTH + 2 + TagParity) + :
211+ CFG .TAG_WIDTH + 2 + TagParity];
158212 end
159213 tc_sram_impl # (
160- .DataWidth ( (CFG .TAG_WIDTH + 2 ) * CFG .SET_COUNT ),
161- .ByteWidth ( CFG .TAG_WIDTH + 2 ),
162- .NumWords ( CFG .LINE_COUNT ),
163- .NumPorts ( 1 ),
164- .impl_in_t ( sram_cfg_tag_t )
214+ .DataWidth ( (CFG .TAG_WIDTH + 2 + TagParity ) * CFG .SET_COUNT ),
215+ .ByteWidth ( CFG .TAG_WIDTH + 2 + TagParity ),
216+ .NumWords ( CFG .LINE_COUNT ),
217+ .NumPorts ( 1 ),
218+ .impl_in_t ( sram_cfg_tag_t )
165219 ) i_tag (
166220 .clk_i ( clk_i ),
167221 .rst_ni ( rst_ni ),
@@ -176,6 +230,17 @@ module snitch_icache_lookup_serial #(
176230 );
177231 end
178232
233+ // compute tag parity bit the cycle before reading the tag and buffer it
234+ logic exp_tag_parity_bit_d, exp_tag_parity_bit_q;
235+
236+ if (TagParity> 0 ) begin : gen_ext_tag_parity_bit
237+ assign exp_tag_parity_bit_d = ^ (tag_req_d.addr >> (CFG .LINE_ALIGN + CFG .COUNT_ALIGN ));
238+ `FFL (exp_tag_parity_bit_q, exp_tag_parity_bit_d, req_valid && req_ready, '0 , clk_i, rst_ni);
239+ end else begin : gen_no_ext_tag_parity_bit
240+ assign exp_tag_parity_bit_d = '0 ;
241+ assign exp_tag_parity_bit_q = '0 ;
242+ end
243+
179244 // Determine which set hit
180245 logic [CFG .SET_COUNT - 1 : 0 ] errors;
181246 assign required_tag = tag_req_q.addr[CFG .FETCH_AW - 1 : CFG .LINE_ALIGN + CFG .COUNT_ALIGN ];
@@ -184,9 +249,20 @@ module snitch_icache_lookup_serial #(
184249 tag_rdata[i][CFG .TAG_WIDTH - 1 : 0 ] == required_tag; // check valid bit and tag
185250 assign errors[i] = tag_rdata[i][CFG .TAG_WIDTH ] && line_hit[i]; // check error bit
186251 end
187- assign tag_rsp_s.hit = | line_hit;
188252 assign tag_rsp_s.error = | errors;
189253
254+ if (TagParity> 0 ) begin : gen_parity_error
255+ for (genvar i = 0 ; i < CFG .SET_COUNT ; i++ ) begin : gen_parity_errors
256+ assign tag_parity_error_d[i] = ~ ((tag_rdata[i][CFG .TAG_WIDTH + 2 ] == exp_tag_parity_bit_q));
257+ end
258+ assign tag_rsp_s.hit = | (line_hit & ~ tag_parity_error_d);
259+ assign faulty_hit_d = | (line_hit & tag_parity_error_d);
260+ end else begin : gen_no_parity_error
261+ assign tag_rsp_s.hit = | line_hit;
262+ assign tag_parity_error_d = '0 ;
263+ assign faulty_hit_d = '0 ;
264+ end
265+
190266 lzc # (.WIDTH (CFG .SET_COUNT )) i_lzc (
191267 .in_i ( line_hit ),
192268 .cnt_o ( tag_rsp_s.cset ),
@@ -196,6 +272,17 @@ module snitch_icache_lookup_serial #(
196272 // Buffer the metadata on a valid handshake. Stall on write (implicit in req_valid/ready)
197273 `FFL (tag_req_q, tag_req_d, req_valid && req_ready, '0 , clk_i, rst_ni)
198274 `FF (tag_valid, req_valid ? 1'b1 : tag_ready ? 1'b0 : tag_valid, '0 , clk_i, rst_ni)
275+ if (TagParity> 0 ) begin : gen_parity_ffs
276+ // save faulty sets and clear when upstream invalidated them
277+ `FFL (tag_parity_error_q, tag_parity_error_d, req_valid && req_ready, '0 , clk_i, rst_ni)
278+ `FFL (faulty_hit_q, (faulty_hit_ready && ! (req_valid && req_ready)) ? 1'b0 : faulty_hit_d,
279+ req_valid && req_ready || faulty_hit_ready, '0 , clk_i, rst_ni)
280+ end else begin : gen_no_parity_ffs
281+ assign tag_parity_error_q = '0 ;
282+ assign faulty_hit_q = '0 ;
283+ end
284+ assign faulty_hit_valid = faulty_hit_q;
285+
199286 // Ready if buffer is empy or downstream is reading. Stall on write
200287 assign req_ready = (! tag_valid || tag_ready) && ! tag_write;
201288
@@ -213,7 +300,7 @@ module snitch_icache_lookup_serial #(
213300 tag_rsp_d = tag_rsp_s;
214301 end
215302 // Override the hit if the write that stalled us invalidated the data
216- if (lookup_addr == write_addr && write_valid_i) begin
303+ if (( lookup_addr == write_addr) && write_valid_i && write_ready_o ) begin
217304 tag_rsp_d.hit = 1'b0 ;
218305 end
219306 end
@@ -232,14 +319,15 @@ module snitch_icache_lookup_serial #(
232319
233320 typedef logic [CFG .LINE_WIDTH - 1 : 0 ] data_rsp_t ;
234321
235- logic [DataAddrWidth- 1 : 0 ] data_addr;
236- logic data_enable;
237- data_rsp_t data_wdata, data_rdata;
238- logic data_write;
322+ logic [DataAddrWidth+ CFG . L1_DATA_PARITY_BITS - 1 : 0 ] data_addr;
323+ logic data_enable;
324+ data_rsp_t data_wdata, data_rdata;
325+ logic data_write;
239326
240327 data_req_t data_req_d, data_req_q;
241328 data_rsp_t data_rsp_q;
242329 logic data_valid, data_ready;
330+ logic hit_invalid, hit_invalid_q;
243331
244332 // Connect tag stage response to data stage request
245333 assign data_req_d.addr = tag_req_q.addr;
@@ -251,26 +339,34 @@ module snitch_icache_lookup_serial #(
251339 assign lookup_addr = { tag_rsp.cset, tag_req_q.addr[CFG .LINE_ALIGN + : CFG .COUNT_ALIGN ]} ;
252340 assign write_addr = { write_set_i, write_addr_i} ;
253341
342+ localparam int unsigned LineParitySplit = CFG .LINE_WIDTH / CFG .L1_DATA_PARITY_BITS ;
343+ if (CFG .L1_DATA_PARITY_BITS > 0 ) begin : gen_parity_wdata
344+ for (genvar i = 0 ; i < CFG .L1_DATA_PARITY_BITS ; i++ ) begin : gen_parity_wdata_indiv
345+ assign data_wdata[CFG .LINE_WIDTH + CFG .L1_DATA_PARITY_BITS - 1 - i] =
346+ ~^ write_data_i[CFG .LINE_WIDTH - LineParitySplit* i- 1 - : LineParitySplit];
347+ end
348+ end
349+ assign data_wdata[CFG .LINE_WIDTH - 1 : 0 ] = write_data_i;
350+
254351 // Data bank port mux
255352 always_comb begin
256353 // Default read request
257354 data_addr = lookup_addr;
258355 data_enable = tag_valid && tag_rsp.hit; // Only read data on hit
259- data_wdata = write_data_i;
260356 data_write = 1'b0 ;
261- // Write takes priority
262- if (! init_phase && write_valid_i) begin
357+ // Write takes priority (except with invalidation due to parity error)
358+ if (! init_phase && write_valid_i && ! data_fault_valid ) begin
263359 data_addr = write_addr;
264360 data_enable = 1'b1 ;
265361 data_write = 1'b1 ;
266362 end
267363 end
268364
269365 tc_sram_impl # (
270- .DataWidth ( CFG .LINE_WIDTH ),
271- .NumWords ( CFG .LINE_COUNT * CFG .SET_COUNT ),
272- .NumPorts ( 1 ),
273- .impl_in_t ( sram_cfg_data_t )
366+ .DataWidth ( CFG .LINE_WIDTH + CFG . L1_DATA_PARITY_BITS ),
367+ .NumWords ( CFG .LINE_COUNT * CFG .SET_COUNT ),
368+ .NumPorts ( 1 ),
369+ .impl_in_t ( sram_cfg_data_t )
274370 ) i_data (
275371 .clk_i ( clk_i ),
276372 .rst_ni ( rst_ni ),
@@ -284,6 +380,22 @@ module snitch_icache_lookup_serial #(
284380 .rdata_o ( data_rdata )
285381 );
286382
383+ // Parity check
384+ if (CFG .L1_DATA_PARITY_BITS > 0 ) begin : gen_data_parity_error
385+ logic [CFG .L1_DATA_PARITY_BITS - 1 : 0 ] data_parity_error;
386+
387+ for (genvar i = 0 ; i < CFG .L1_DATA_PARITY_BITS ; i++ ) begin : gen_data_parity_error_indiv
388+ assign data_parity_error[i] =
389+ ^ data_rdata[CFG .LINE_WIDTH - LineParitySplit* i- 1 - : LineParitySplit];
390+ end
391+
392+ assign data_parity_inv_d.parity_error = | data_parity_error;
393+ assign data_parity_inv_d.addr = data_req_q.addr;
394+ assign data_parity_inv_d.cset = data_req_q.id;
395+ end else begin : gen_no_data_parity
396+ assign data_parity_inv_d = '0 ;
397+ end
398+
287399 // Buffer the metadata on a valid handshake. Stall on write (implicit in tag_ready)
288400 `FFL (data_req_q, data_req_d, tag_valid && tag_ready, '0 , clk_i, rst_ni)
289401 `FF (data_valid, (tag_valid && ! data_write) ? 1'b1 : data_ready ? 1'b0 : data_valid,
@@ -300,11 +412,22 @@ module snitch_icache_lookup_serial #(
300412 `FFL (data_rsp_q, data_rdata, tag_handshake && ! data_ready, '0 , clk_i, rst_ni)
301413 assign out_data_o = tag_handshake ? data_rdata : data_rsp_q;
302414
415+ // Buffer the metadata when there is faulty data for the invalidation procedure
416+ if (CFG .L1_DATA_PARITY_BITS > 0 ) begin : gen_data_parity_ffs
417+ `FFL (data_parity_inv_q, (data_fault_ready && ! tag_handshake) ? '0 : data_parity_inv_d,
418+ tag_handshake || data_fault_ready, '0 , clk_i, rst_ni)
419+ `FFL (hit_invalid_q, data_parity_inv_d.parity_error, tag_handshake, '0 , clk_i, rst_ni)
420+ end else begin : gen_no_data_parity_ffs
421+ assign data_parity_inv_q = '0 ;
422+ assign hit_invalid_q = '0 ;
423+ end
424+ assign hit_invalid = tag_handshake ? data_parity_inv_d.parity_error : hit_invalid_q;
425+
303426 // Generate the remaining output signals.
304427 assign out_addr_o = data_req_q.addr;
305428 assign out_id_o = data_req_q.id;
306429 assign out_set_o = data_req_q.cset;
307- assign out_hit_o = data_req_q.hit;
430+ assign out_hit_o = data_req_q.hit && ! hit_invalid ;
308431 assign out_error_o = data_req_q.error;
309432 assign out_valid_o = data_valid;
310433 assign data_ready = out_ready_i;
0 commit comments