Skip to content

Commit 916bfcd

Browse files
committed
Add initial L1 parity protection
1 parent 95035dc commit 916bfcd

5 files changed

Lines changed: 165 additions & 37 deletions

File tree

src/snitch_icache.sv

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@ module snitch_icache #(
2626
parameter int unsigned FILL_AW = -1,
2727
/// Fill interface data width. Power of two; >= 8.
2828
parameter int unsigned FILL_DW = -1,
29+
/// Extra parity bits to add to a line for L1 reliability.
30+
parameter int unsigned L1_DATA_PARITY_BITS = 0,
2931
/// Serialize the L1 lookup (parallel tag/data lookup by default)
3032
parameter bit SERIAL_LOOKUP = 0,
3133
/// Replace the L1 tag banks with latch-based SCM.
@@ -90,6 +92,7 @@ module snitch_icache #(
9092
FETCH_DW: FETCH_DW,
9193
FILL_AW: FILL_AW,
9294
FILL_DW: FILL_DW,
95+
L1_DATA_PARITY_BITS: L1_DATA_PARITY_BITS,
9396
L1_TAG_SCM: L1_TAG_SCM,
9497
EARLY_LATCH: EARLY_LATCH,
9598
BUFFER_LOOKUP: 0,

src/snitch_icache_handler.sv

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -218,8 +218,8 @@ module snitch_icache_handler #(
218218
in_req_ready_o = hit_ready;
219219

220220
// The cache lookup was a miss, but there is already a pending
221-
// refill that covers the line.
222-
end else if (pending) begin
221+
// refill that covers the line and the lookup accepted the request.
222+
end else if (pending && !(write_valid_o && !write_ready_i)) begin
223223
push_index = pending_id;
224224
push_enable = 1;
225225

src/snitch_icache_lookup_serial.sv

Lines changed: 158 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -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;

src/snitch_icache_pkg.sv

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ package snitch_icache_pkg;
2626
int unsigned FETCH_DW;
2727
int unsigned FILL_AW;
2828
int unsigned FILL_DW;
29+
int unsigned L1_DATA_PARITY_BITS;
2930
bit L1_TAG_SCM;
3031
bit EARLY_LATCH;
3132
bit BUFFER_LOOKUP;

test/snitch_icache_l0_tb.sv

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,7 @@ module snitch_icache_l0_tb #(
8787
FETCH_DW: FETCH_DW,
8888
FILL_AW: FILL_AW,
8989
FILL_DW: FILL_DW,
90+
L1_DATA_PARITY_BITS: 0,
9091
L1_TAG_SCM: 1'b0,
9192
EARLY_LATCH: EARLY_LATCH,
9293
BUFFER_LOOKUP: BUFFER_LOOKUP,

0 commit comments

Comments
 (0)