-
Notifications
You must be signed in to change notification settings - Fork 762
/
sram_ctrl.sv
450 lines (392 loc) · 14.7 KB
/
sram_ctrl.sv
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
// Copyright lowRISC contributors.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0
//
// SRAM controller.
//
`include "prim_assert.sv"
module sram_ctrl
import sram_ctrl_pkg::*;
import sram_ctrl_reg_pkg::*;
#(
// Number of words stored in the SRAM.
parameter int MemSizeRam = 32'h1000,
// Enable asynchronous transitions on alerts.
parameter logic [NumAlerts-1:0] AlertAsyncOn = {NumAlerts{1'b1}},
// Enables the execute from SRAM feature.
parameter bit InstrExec = 1,
// Random netlist constants
parameter otp_ctrl_pkg::sram_key_t RndCnstSramKey = RndCnstSramKeyDefault,
parameter otp_ctrl_pkg::sram_nonce_t RndCnstSramNonce = RndCnstSramNonceDefault,
parameter lfsr_seed_t RndCnstLfsrSeed = RndCnstLfsrSeedDefault,
parameter lfsr_perm_t RndCnstLfsrPerm = RndCnstLfsrPermDefault
) (
// SRAM Clock
input logic clk_i,
input logic rst_ni,
// OTP Clock (for key interface)
input logic clk_otp_i,
input logic rst_otp_ni,
// Bus Interface (device) for SRAM
input tlul_pkg::tl_h2d_t ram_tl_i,
output tlul_pkg::tl_d2h_t ram_tl_o,
// Bus Interface (device) for CSRs
input tlul_pkg::tl_h2d_t regs_tl_i,
output tlul_pkg::tl_d2h_t regs_tl_o,
// Alert outputs.
input prim_alert_pkg::alert_rx_t [NumAlerts-1:0] alert_rx_i,
output prim_alert_pkg::alert_tx_t [NumAlerts-1:0] alert_tx_o,
// Life-cycle escalation input (scraps the scrambling keys)
input lc_ctrl_pkg::lc_tx_t lc_escalate_en_i,
input lc_ctrl_pkg::lc_tx_t lc_hw_debug_en_i,
// Otp configuration for sram execution
input prim_mubi_pkg::mubi8_t otp_en_sram_ifetch_i,
// Key request to OTP (running on clk_fixed)
output otp_ctrl_pkg::sram_otp_key_req_t sram_otp_key_o,
input otp_ctrl_pkg::sram_otp_key_rsp_t sram_otp_key_i,
// config
input prim_ram_1p_pkg::ram_1p_cfg_t cfg_i
);
// This is later on pruned to the correct width at the SRAM wrapper interface.
parameter int unsigned Depth = MemSizeRam >> 2;
parameter int unsigned AddrWidth = prim_util_pkg::vbits(Depth);
`ASSERT_INIT(NonceWidthsLessThanSource_A, NonceWidth + LfsrWidth <= otp_ctrl_pkg::SramNonceWidth)
/////////////////////////////////////
// Anchor incoming seeds and constants
/////////////////////////////////////
localparam int TotalAnchorWidth = $bits(otp_ctrl_pkg::sram_key_t) +
$bits(otp_ctrl_pkg::sram_nonce_t);
otp_ctrl_pkg::sram_key_t cnst_sram_key;
otp_ctrl_pkg::sram_nonce_t cnst_sram_nonce;
prim_sec_anchor_buf #(
.Width(TotalAnchorWidth)
) u_seed_anchor (
.in_i({RndCnstSramKey,
RndCnstSramNonce}),
.out_o({cnst_sram_key,
cnst_sram_nonce})
);
//////////////////////////
// CSR Node and Mapping //
//////////////////////////
// We've got two bus interfaces in this module, hence two integ failure sources.
logic [1:0] bus_integ_error;
sram_ctrl_regs_reg2hw_t reg2hw;
sram_ctrl_regs_hw2reg_t hw2reg;
sram_ctrl_regs_reg_top u_reg_regs (
.clk_i,
.rst_ni,
.tl_i (regs_tl_i),
.tl_o (regs_tl_o),
.reg2hw,
.hw2reg,
// SEC_CM: BUS.INTEGRITY
.intg_err_o(bus_integ_error[0]),
.devmode_i (1'b1)
);
// Key and attribute outputs to scrambling device
logic [otp_ctrl_pkg::SramKeyWidth-1:0] key_d, key_q;
logic [otp_ctrl_pkg::SramNonceWidth-1:0] nonce_d, nonce_q;
// tie-off unused nonce bits
if (otp_ctrl_pkg::SramNonceWidth > NonceWidth) begin : gen_nonce_tieoff
logic unused_nonce;
assign unused_nonce = ^nonce_q[otp_ctrl_pkg::SramNonceWidth-1:NonceWidth];
end
//////////////////
// Alert Sender //
//////////////////
logic alert_test;
assign alert_test = reg2hw.alert_test.q & reg2hw.alert_test.qe;
assign hw2reg.status.bus_integ_error.d = 1'b1;
assign hw2reg.status.bus_integ_error.de = |bus_integ_error;
logic init_error;
assign hw2reg.status.init_error.d = 1'b1;
assign hw2reg.status.init_error.de = init_error;
prim_alert_sender #(
.AsyncOn(AlertAsyncOn[0]),
.IsFatal(1)
) u_prim_alert_sender_parity (
.clk_i,
.rst_ni,
.alert_test_i ( alert_test ),
.alert_req_i ( |bus_integ_error | init_error ),
.alert_ack_o ( ),
.alert_state_o ( ),
.alert_rx_i ( alert_rx_i[0] ),
.alert_tx_o ( alert_tx_o[0] )
);
/////////////////////////
// Escalation Triggers //
/////////////////////////
lc_ctrl_pkg::lc_tx_t escalate_en;
prim_lc_sync #(
.NumCopies (1)
) u_prim_lc_sync (
.clk_i,
.rst_ni,
.lc_en_i (lc_escalate_en_i),
.lc_en_o (escalate_en)
);
// SEC_CM: KEY.GLOBAL_ESC
logic escalate;
assign escalate = (escalate_en != lc_ctrl_pkg::Off);
assign hw2reg.status.escalated.d = 1'b1;
assign hw2reg.status.escalated.de = escalate;
// SEC_CM: KEY.LOCAL_ESC
// Aggregate external and internal escalation sources. This is used on countermeasures further
// below (key reset, transaction blocking and scrambling nonce reversal).
logic local_esc;
assign local_esc = escalate |
init_error |
|bus_integ_error |
reg2hw.status.escalated.q |
reg2hw.status.init_error.q |
reg2hw.status.bus_integ_error.q;
///////////////////////
// HW Initialization //
///////////////////////
// A write to the init register reloads the LFSR seed, resets the init counter and
// sets init_q to flag a pending initialization request.
logic init_trig;
assign init_trig = reg2hw.ctrl.init.q & reg2hw.ctrl.init.qe;
logic init_d, init_q, init_done;
assign init_d = (init_done) ? 1'b0 :
(init_trig) ? 1'b1 : init_q;
always_ff @(posedge clk_i or negedge rst_ni) begin : p_init_reg
if(!rst_ni) begin
init_q <= 1'b0;
end else begin
init_q <= init_d;
end
end
// This waits until the scrambling keys are actually valid (this allows the SW to trigger
// key renewal and initialization at the same time).
logic init_req;
logic [AddrWidth-1:0] init_cnt;
logic key_req_pending_d, key_req_pending_q;
assign init_req = init_q & ~key_req_pending_q;
assign init_done = (init_cnt == AddrWidth'(Depth - 1)) & init_req;
// We employ two redundant counters to guard against FI attacks.
// If any of the two is glitched and the two counter states do not agree,
// we trigger an alert.
// SEC_CM: CTR.REDUN
prim_count #(
.Width(AddrWidth),
.OutSelDnCnt(0), // count up
.CntStyle(prim_count_pkg::DupCnt)
) u_prim_count (
.clk_i,
.rst_ni,
.clr_i(init_trig),
.set_i(1'b0),
.set_cnt_i('0),
.en_i(init_req),
.step_i(AddrWidth'(1)),
.cnt_o(init_cnt),
.err_o(init_error)
);
// Clear this bit on local escalation.
assign hw2reg.status.init_done.d = init_done & ~init_trig & ~local_esc;
assign hw2reg.status.init_done.de = init_done | init_trig | local_esc;
////////////////////////////
// Scrambling Key Request //
////////////////////////////
// The scrambling key and nonce have to be requested from the OTP controller via a req/ack
// protocol. Since the OTP controller works in a different clock domain, we have to synchronize
// the req/ack protocol as described in more details here:
// https://docs.opentitan.org/hw/ip/otp_ctrl/doc/index.html#interfaces-to-sram-and-otbn-scramblers
logic key_req, key_ack;
assign key_req = reg2hw.ctrl.renew_scr_key.q & reg2hw.ctrl.renew_scr_key.qe;
assign key_req_pending_d = (key_req) ? 1'b1 :
(key_ack) ? 1'b0 : key_req_pending_q;
// The SRAM scrambling wrapper will not accept any transactions while
// the key req is pending or if we have escalated.
// Note that we're not using key_valid_q here, such that the SRAM can be used
// right after reset, where the keys are reset to the default netlist constant.
logic key_valid;
assign key_valid = ~(key_req_pending_q | reg2hw.status.escalated.q);
// Clear this bit on local escalation.
assign hw2reg.status.scr_key_valid.d = key_ack & ~key_req & ~local_esc;
assign hw2reg.status.scr_key_valid.de = key_req | key_ack | local_esc;
// Clear this bit on local escalation.
logic key_seed_valid;
assign hw2reg.status.scr_key_seed_valid.d = key_seed_valid & ~local_esc;
assign hw2reg.status.scr_key_seed_valid.de = key_ack | local_esc;
always_ff @(posedge clk_i or negedge rst_ni) begin : p_regs
if (!rst_ni) begin
key_req_pending_q <= 1'b0;
// reset case does not use buffered values as the
// reset value will be directly encoded into flop types
key_q <= RndCnstSramKey;
nonce_q <= RndCnstSramNonce;
end else begin
key_req_pending_q <= key_req_pending_d;
if (key_ack) begin
key_q <= key_d;
nonce_q <= nonce_d;
end
// This scraps the keys.
// SEC_CM: KEY.GLOBAL_ESC
// SEC_CM: KEY.LOCAL_ESC
if (local_esc) begin
key_q <= cnst_sram_key;
nonce_q <= cnst_sram_nonce;
end
end
end
prim_sync_reqack_data #(
.Width($bits(otp_ctrl_pkg::sram_otp_key_rsp_t)-1),
.DataSrc2Dst(1'b0)
) u_prim_sync_reqack_data (
.clk_src_i ( clk_i ),
.rst_src_ni ( rst_ni ),
.clk_dst_i ( clk_otp_i ),
.rst_dst_ni ( rst_otp_ni ),
.req_chk_i ( 1'b1 ),
.src_req_i ( key_req_pending_q ),
.src_ack_o ( key_ack ),
.dst_req_o ( sram_otp_key_o.req ),
.dst_ack_i ( sram_otp_key_i.ack ),
.data_i ( {sram_otp_key_i.key,
sram_otp_key_i.nonce,
sram_otp_key_i.seed_valid} ),
.data_o ( {key_d,
nonce_d,
key_seed_valid} )
);
logic unused_csr_sigs;
assign unused_csr_sigs = ^{reg2hw.status.init_done.q,
reg2hw.status.scr_key_seed_valid.q};
////////////////////
// SRAM Execution //
////////////////////
import prim_mubi_pkg::mubi4_t;
import prim_mubi_pkg::MuBi4True;
import prim_mubi_pkg::MuBi4False;
import prim_mubi_pkg::mubi8_test_true_strict;
mubi4_t en_ifetch;
if (InstrExec) begin : gen_instr_ctrl
mubi4_t lc_ifetch_en;
mubi4_t reg_ifetch_en;
// SEC_CM: INSTR.BUS.LC_GATED
assign lc_ifetch_en = (lc_hw_debug_en_i == lc_ctrl_pkg::On) ? MuBi4True :
MuBi4False;
assign reg_ifetch_en = mubi4_t'(reg2hw.exec.q);
assign en_ifetch = (mubi8_test_true_strict(otp_en_sram_ifetch_i)) ? reg_ifetch_en :
lc_ifetch_en;
end else begin : gen_tieoff
assign en_ifetch = MuBi4False;
// tie off unused signals
logic unused_sigs;
assign unused_sigs = ^{lc_hw_debug_en_i,
reg2hw.exec.q,
otp_en_sram_ifetch_i};
end
/////////////////////////
// Initialization LFSR //
/////////////////////////
logic [LfsrWidth-1:0] lfsr_out;
prim_lfsr #(
.LfsrDw ( LfsrWidth ),
.EntropyDw ( LfsrWidth ),
.StateOutDw ( LfsrWidth ),
.DefaultSeed ( RndCnstLfsrSeed ),
.StatePermEn ( 1'b1 ),
.StatePerm ( RndCnstLfsrPerm )
) u_lfsr (
.clk_i,
.rst_ni,
.lfsr_en_i(init_req),
.seed_en_i(init_trig),
.seed_i(nonce_q[NonceWidth +: LfsrWidth]),
.entropy_i('0),
.state_o(lfsr_out)
);
// Compute the correct integrity alongside for the pseudo-random initialization values.
logic [DataWidth - 1 :0] lfsr_out_integ;
tlul_data_integ_enc u_tlul_data_integ_enc (
.data_i(lfsr_out),
.data_intg_o(lfsr_out_integ)
);
/////////////////////////////////
// SRAM with scrambling device //
/////////////////////////////////
logic tlul_req, tlul_gnt, tlul_we;
logic [AddrWidth-1:0] tlul_addr;
logic [DataWidth-1:0] tlul_wdata, tlul_wmask;
logic sram_intg_error, sram_req, sram_gnt, sram_we, sram_rvalid;
logic [AddrWidth-1:0] sram_addr;
logic [DataWidth-1:0] sram_wdata, sram_wmask, sram_rdata;
tlul_adapter_sram #(
.SramAw(AddrWidth),
.SramDw(DataWidth - tlul_pkg::DataIntgWidth),
.Outstanding(2),
.ByteAccess(1),
.CmdIntgCheck(1),
.EnableRspIntgGen(1),
.EnableDataIntgGen(0),
.EnableDataIntgPt(1) // SEC_CM: MEM.INTEGRITY
) u_tlul_adapter_sram (
.clk_i,
.rst_ni,
.tl_i (ram_tl_i),
.tl_o (ram_tl_o),
.en_ifetch_i (en_ifetch),
.req_o (tlul_req),
.req_type_o (),
.gnt_i (tlul_gnt),
.we_o (tlul_we),
.addr_o (tlul_addr),
.wdata_o (tlul_wdata),
.wmask_o (tlul_wmask),
// SEC_CM: BUS.INTEGRITY
.intg_error_o(bus_integ_error[1]),
.rdata_i (sram_rdata),
.rvalid_i (sram_rvalid),
.rerror_i ('0)
);
// Interposing mux logic for initialization with pseudo random data.
assign sram_req = tlul_req | init_req;
assign tlul_gnt = sram_gnt & ~init_req;
assign sram_we = tlul_we | init_req;
assign sram_intg_error = local_esc & ~init_req;
assign sram_addr = (init_req) ? init_cnt : tlul_addr;
assign sram_wdata = (init_req) ? lfsr_out_integ : tlul_wdata;
assign sram_wmask = (init_req) ? {DataWidth{1'b1}} : tlul_wmask;
// SEC_CM: MEM.SCRAMBLE, ADDR.SCRAMBLE
prim_ram_1p_scr #(
.Width(DataWidth),
.Depth(Depth),
.EnableParity(0),
.DataBitsPerMask(DataWidth),
.DiffWidth(DataWidth)
) u_prim_ram_1p_scr (
.clk_i,
.rst_ni,
.key_valid_i (key_valid),
.key_i (key_q),
.nonce_i (nonce_q[NonceWidth-1:0]),
.req_i (sram_req),
.intg_error_i(sram_intg_error),
.gnt_o (sram_gnt),
.write_i (sram_we),
.addr_i (sram_addr),
.wdata_i (sram_wdata),
.wmask_i (sram_wmask),
.rdata_o (sram_rdata),
.rvalid_o (sram_rvalid),
.rerror_o ( ),
.raddr_o ( ),
.cfg_i
);
////////////////
// Assertions //
////////////////
`ASSERT_KNOWN(RegsTlOutKnown_A, regs_tl_o)
`ASSERT_KNOWN(RamTlOutKnown_A, ram_tl_o)
`ASSERT_KNOWN(AlertOutKnown_A, alert_tx_o)
`ASSERT_KNOWN(SramOtpKeyKnown_A, sram_otp_key_o)
// Alert assertions for redundant counters.
`ASSERT_PRIM_COUNT_ERROR_TRIGGER_ALERT(CntCheck_A,
u_prim_count, alert_tx_o[0])
endmodule : sram_ctrl