hw/ip/hmac/rtl/hmac.sv Cov: 99.7%
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: // HMAC-SHA256
6:
7: module hmac
8: import prim_pkg::*;
9: import hmac_pkg::*;
10: import hmac_reg_pkg::*;
11: (
12: input clk_i,
13: input rst_ni,
14:
15: input tlul_pkg::tl_h2d_t tl_i,
16: output tlul_pkg::tl_d2h_t tl_o,
17:
18: output logic intr_hmac_done_o,
19: output logic intr_fifo_full_o,
20: output logic intr_hmac_err_o,
21:
22: // alerts
23: input alert_rx_t [NumAlerts-1:0] alert_rx_i,
24: output alert_tx_t [NumAlerts-1:0] alert_tx_o
25: );
26:
27:
28: /////////////////////////
29: // Signal declarations //
30: /////////////////////////
31: hmac_reg2hw_t reg2hw;
32: hmac_hw2reg_t hw2reg;
33:
34: tlul_pkg::tl_h2d_t tl_win_h2d[1];
35: tlul_pkg::tl_d2h_t tl_win_d2h[1];
36:
37: logic [255:0] secret_key;
38:
39: logic wipe_secret;
40: logic [31:0] wipe_v;
41:
42: logic fifo_rvalid;
43: logic fifo_rready;
44: sha_fifo_t fifo_rdata;
45:
46: logic fifo_wvalid, fifo_wready;
47: sha_fifo_t fifo_wdata;
48: logic fifo_full;
49: logic fifo_empty;
50: logic [4:0] fifo_depth;
51:
52: logic msg_fifo_req;
53: logic msg_fifo_gnt;
54: logic msg_fifo_we;
55: logic [8:0] msg_fifo_addr; // NOT_READ
56: logic [31:0] msg_fifo_wdata;
57: logic [31:0] msg_fifo_wmask;
58: logic [31:0] msg_fifo_rdata;
59: logic msg_fifo_rvalid;
60: logic [1:0] msg_fifo_rerror;
61: logic [31:0] msg_fifo_wdata_endian;
62: logic [31:0] msg_fifo_wmask_endian;
63:
64: logic packer_ready;
65: logic packer_flush_done;
66:
67: logic reg_fifo_wvalid;
68: sha_word_t reg_fifo_wdata;
69: sha_word_t reg_fifo_wmask;
70: logic hmac_fifo_wsel;
71: logic hmac_fifo_wvalid;
72: logic [2:0] hmac_fifo_wdata_sel;
73:
74: logic shaf_rvalid;
75: sha_fifo_t shaf_rdata;
76: logic shaf_rready;
77:
78: logic sha_en;
79: logic hmac_en;
80: logic endian_swap;
81: logic digest_swap;
82:
83: logic reg_hash_start;
84: logic sha_hash_start;
85: logic hash_start; // Valid hash_start_signal
86: logic reg_hash_process;
87: logic sha_hash_process;
88:
89: logic reg_hash_done;
90: logic sha_hash_done;
91:
92: logic [63:0] message_length;
93: logic [63:0] sha_message_length;
94:
95: err_code_e err_code;
96: logic err_valid;
97:
98: sha_word_t [7:0] digest;
99:
100: hmac_reg2hw_cfg_reg_t cfg_reg;
101: logic cfg_block; // Prevent changing config
102:
103: ///////////////////////
104: // Connect registers //
105: ///////////////////////
106: assign hw2reg.status.fifo_full.d = fifo_full;
107: assign hw2reg.status.fifo_empty.d = fifo_empty;
108: assign hw2reg.status.fifo_depth.d = fifo_depth;
109:
110: // secret key
111: assign wipe_secret = reg2hw.wipe_secret.qe;
112: assign wipe_v = reg2hw.wipe_secret.q;
113:
114: always_ff @(posedge clk_i or negedge rst_ni) begin
115: if (!rst_ni) begin
116: secret_key <= '0;
117: end else if (wipe_secret) begin
118: secret_key <= secret_key ^ {8{wipe_v}};
119: end else if (!cfg_block) begin
120: // Allow updating secret key only when the engine is in Idle.
121: for (int i = 0; i < 8; i++) begin
122: if (reg2hw.key[7-i].qe) begin
123: secret_key[32*i+:32] <= reg2hw.key[7-i].q;
124: end
125: end
126: end
127: end
128:
129: for (genvar i = 0; i < 8; i++) begin : gen_key_digest
130: assign hw2reg.key[7-i].d = '0;
131: // digest
132: assign hw2reg.digest[i].d = conv_endian(digest[i], digest_swap);
133: end
134:
135: logic [3:0] unused_cfg_qe;
136:
137: assign unused_cfg_qe = {cfg_reg.sha_en.qe, cfg_reg.hmac_en.qe,
138: cfg_reg.endian_swap.qe, cfg_reg.digest_swap.qe};
139:
140: assign sha_en = cfg_reg.sha_en.q;
141: assign hmac_en = cfg_reg.hmac_en.q;
142: assign endian_swap = cfg_reg.endian_swap.q;
143: assign digest_swap = cfg_reg.digest_swap.q;
144: assign hw2reg.cfg.hmac_en.d = cfg_reg.hmac_en.q;
145: assign hw2reg.cfg.sha_en.d = cfg_reg.sha_en.q;
146: assign hw2reg.cfg.endian_swap.d = cfg_reg.endian_swap.q;
147: assign hw2reg.cfg.digest_swap.d = cfg_reg.digest_swap.q;
148:
149: assign reg_hash_start = reg2hw.cmd.hash_start.qe & reg2hw.cmd.hash_start.q;
150: assign reg_hash_process = reg2hw.cmd.hash_process.qe & reg2hw.cmd.hash_process.q;
151:
152: // Error code register
153: assign hw2reg.err_code.de = err_valid;
154: assign hw2reg.err_code.d = err_code;
155:
156: /////////////////////
157: // Control signals //
158: /////////////////////
159: assign hash_start = reg_hash_start & sha_en;
160:
161: always_ff @(posedge clk_i or negedge rst_ni) begin
162: if (!rst_ni) begin
163: cfg_block <= '0;
164: end else if (hash_start) begin
165: cfg_block <= 1'b 1;
166: end else if (reg_hash_done) begin
167: cfg_block <= 1'b 0;
168: end
169: end
170: // Hold the configuration during the process
171: always_ff @(posedge clk_i or negedge rst_ni) begin
172: if (!rst_ni) begin
173: cfg_reg <= '{endian_swap: '{q: 1'b1, qe: 1'b0}, default:'0};
174: end else if (!cfg_block && reg2hw.cfg.hmac_en.qe) begin
175: cfg_reg <= reg2hw.cfg ;
176: end
177: end
178: ////////////////
179: // Interrupts //
180: ////////////////
181: logic fifo_full_q;
182: always_ff @(posedge clk_i or negedge rst_ni) begin
183: if (!rst_ni) fifo_full_q <= 1'b0;
184: else fifo_full_q <= fifo_full;
185: end
186:
187: logic fifo_full_event;
188: assign fifo_full_event = fifo_full & !fifo_full_q;
189:
190: logic [2:0] event_intr;
191: assign event_intr = {err_valid, fifo_full_event, reg_hash_done};
192:
193: // instantiate interrupt hardware primitive
194: prim_intr_hw #(.Width(1)) intr_hw_hmac_done (
195: .event_intr_i (event_intr[0]),
196: .reg2hw_intr_enable_q_i (reg2hw.intr_enable.hmac_done.q),
197: .reg2hw_intr_test_q_i (reg2hw.intr_test.hmac_done.q),
198: .reg2hw_intr_test_qe_i (reg2hw.intr_test.hmac_done.qe),
199: .reg2hw_intr_state_q_i (reg2hw.intr_state.hmac_done.q),
200: .hw2reg_intr_state_de_o (hw2reg.intr_state.hmac_done.de),
201: .hw2reg_intr_state_d_o (hw2reg.intr_state.hmac_done.d),
202: .intr_o (intr_hmac_done_o)
203: );
204: prim_intr_hw #(.Width(1)) intr_hw_fifo_full (
205: .event_intr_i (event_intr[1]),
206: .reg2hw_intr_enable_q_i (reg2hw.intr_enable.fifo_full.q),
207: .reg2hw_intr_test_q_i (reg2hw.intr_test.fifo_full.q),
208: .reg2hw_intr_test_qe_i (reg2hw.intr_test.fifo_full.qe),
209: .reg2hw_intr_state_q_i (reg2hw.intr_state.fifo_full.q),
210: .hw2reg_intr_state_de_o (hw2reg.intr_state.fifo_full.de),
211: .hw2reg_intr_state_d_o (hw2reg.intr_state.fifo_full.d),
212: .intr_o (intr_fifo_full_o)
213: );
214: prim_intr_hw #(.Width(1)) intr_hw_hmac_err (
215: .event_intr_i (event_intr[2]),
216: .reg2hw_intr_enable_q_i (reg2hw.intr_enable.hmac_err.q),
217: .reg2hw_intr_test_q_i (reg2hw.intr_test.hmac_err.q),
218: .reg2hw_intr_test_qe_i (reg2hw.intr_test.hmac_err.qe),
219: .reg2hw_intr_state_q_i (reg2hw.intr_state.hmac_err.q),
220: .hw2reg_intr_state_de_o (hw2reg.intr_state.hmac_err.de),
221: .hw2reg_intr_state_d_o (hw2reg.intr_state.hmac_err.d),
222: .intr_o (intr_hmac_err_o)
223: );
224:
225: ///////////////
226: // Instances //
227: ///////////////
228:
229: assign msg_fifo_rvalid = msg_fifo_req & ~msg_fifo_we;
230: assign msg_fifo_rdata = '1; // Return all F
231: assign msg_fifo_rerror = '1; // Return error for read access
232: assign msg_fifo_gnt = msg_fifo_req & ~hmac_fifo_wsel & packer_ready;
233:
234: // FIFO control
235: sha_fifo_t reg_fifo_wentry;
236: assign reg_fifo_wentry.data = conv_endian(reg_fifo_wdata, 1'b1); // always convert
237: assign reg_fifo_wentry.mask = {reg_fifo_wmask[0], reg_fifo_wmask[8],
238: reg_fifo_wmask[16], reg_fifo_wmask[24]};
239: assign fifo_full = ~fifo_wready;
240: assign fifo_empty = ~fifo_rvalid;
241: assign fifo_wvalid = (hmac_fifo_wsel && fifo_wready) ? hmac_fifo_wvalid : reg_fifo_wvalid;
242: assign fifo_wdata = (hmac_fifo_wsel) ? '{data: digest[hmac_fifo_wdata_sel], mask: '1}
243: : reg_fifo_wentry;
244:
245: prim_fifo_sync #(
246: .Width ($bits(sha_fifo_t)),
247: .Pass (1'b0),
248: .Depth (MsgFifoDepth)
249: ) u_msg_fifo (
250: .clk_i,
251: .rst_ni,
252: .clr_i (1'b0),
253:
254: .wvalid (fifo_wvalid & sha_en),
255: .wready (fifo_wready),
256: .wdata (fifo_wdata),
257:
258: .depth (fifo_depth),
259:
260: .rvalid (fifo_rvalid),
261: .rready (fifo_rready),
262: .rdata (fifo_rdata)
263: );
264:
265: // TL ADAPTER SRAM
266: tlul_adapter_sram #(
267: .SramAw (9),
268: .SramDw (32),
269: .Outstanding (1),
270: .ByteAccess (1),
271: .ErrOnRead (1)
272: ) u_tlul_adapter (
273: .clk_i,
274: .rst_ni,
275: .tl_i (tl_win_h2d[0]),
276: .tl_o (tl_win_d2h[0]),
277:
278: .req_o (msg_fifo_req ),
279: .gnt_i (msg_fifo_gnt ),
280: .we_o (msg_fifo_we ),
281: .addr_o (msg_fifo_addr ), // Doesn't care the address other than sub-word
282: .wdata_o (msg_fifo_wdata ),
283: .wmask_o (msg_fifo_wmask ),
284: .rdata_i (msg_fifo_rdata ),
285: .rvalid_i (msg_fifo_rvalid),
286: .rerror_i (msg_fifo_rerror)
287: );
288:
289: // TL-UL to MSG_FIFO byte write handling
290: logic msg_write;
291:
292: assign msg_write = msg_fifo_req & msg_fifo_we & ~hmac_fifo_wsel;
293:
294: logic [$clog2(32+1)-1:0] wmask_ones;
295:
296: always_comb begin
297: wmask_ones = '0;
298: for (int i = 0 ; i < 32 ; i++) begin
299: wmask_ones = wmask_ones + reg_fifo_wmask[i];
300: end
301: end
302:
303: // Calculate written message
304: always_ff @(posedge clk_i or negedge rst_ni) begin
305: if (!rst_ni) begin
306: message_length <= '0;
307: end else if (hash_start) begin
308: message_length <= '0;
309: end else if (reg_fifo_wvalid && fifo_wready && !hmac_fifo_wsel) begin
310: message_length <= message_length + 64'(wmask_ones);
311: end
312: end
313:
314: assign hw2reg.msg_length_upper.de = 1'b1;
315: assign hw2reg.msg_length_upper.d = message_length[63:32];
316: assign hw2reg.msg_length_lower.de = 1'b1;
317: assign hw2reg.msg_length_lower.d = message_length[31:0];
318:
319:
320: // Convert endian here
321: // prim_packer always packs to the right, but SHA engine assumes incoming
322: // to be big-endian, [31:24] comes first. So, the data is reverted after
323: // prim_packer before the message fifo. here to reverse if not big-endian
324: // before pushing to the packer.
325: assign msg_fifo_wdata_endian = conv_endian(msg_fifo_wdata, ~endian_swap);
326: assign msg_fifo_wmask_endian = conv_endian(msg_fifo_wmask, ~endian_swap);
327:
328: prim_packer #(
329: .InW (32),
330: .OutW (32)
331: ) u_packer (
332: .clk_i,
333: .rst_ni,
334:
335: .valid_i (msg_write & sha_en),
336: .data_i (msg_fifo_wdata_endian),
337: .mask_i (msg_fifo_wmask_endian),
338: .ready_o (packer_ready),
339:
340: .valid_o (reg_fifo_wvalid),
341: .data_o (reg_fifo_wdata),
342: .mask_o (reg_fifo_wmask),
343: .ready_i (fifo_wready & ~hmac_fifo_wsel),
344:
345: .flush_i (reg_hash_process),
346: .flush_done_o (packer_flush_done) // ignore at this moment
347: );
348:
349:
350: hmac_core u_hmac (
351: .clk_i,
352: .rst_ni,
353:
354: .secret_key,
355:
356: .wipe_secret,
357: .wipe_v,
358:
359: .hmac_en,
360:
361: .reg_hash_start (hash_start),
362: .reg_hash_process (packer_flush_done), // Trigger after all msg written
363: .hash_done (reg_hash_done),
364: .sha_hash_start,
365: .sha_hash_process,
366: .sha_hash_done,
367:
368: .sha_rvalid (shaf_rvalid),
369: .sha_rdata (shaf_rdata),
370: .sha_rready (shaf_rready),
371:
372: .fifo_rvalid,
373: .fifo_rdata,
374: .fifo_rready,
375:
376: .fifo_wsel (hmac_fifo_wsel),
377: .fifo_wvalid (hmac_fifo_wvalid),
378: .fifo_wdata_sel (hmac_fifo_wdata_sel),
379: .fifo_wready,
380:
381: .message_length,
382: .sha_message_length
383: );
384:
385: sha2 u_sha2 (
386: .clk_i,
387: .rst_ni,
388:
389: .wipe_secret,
390: .wipe_v,
391:
392: .fifo_rvalid (shaf_rvalid),
393: .fifo_rdata (shaf_rdata),
394: .fifo_rready (shaf_rready),
395:
396: .sha_en,
397: .hash_start (sha_hash_start),
398: .hash_process (sha_hash_process),
399: .hash_done (sha_hash_done),
400:
401: .message_length (sha_message_length),
402:
403: .digest
404: );
405:
406: hmac_reg_top u_reg (
407: .clk_i,
408: .rst_ni,
409:
410: .tl_i,
411: .tl_o,
412:
413: .tl_win_o (tl_win_h2d),
414: .tl_win_i (tl_win_d2h),
415:
416: .reg2hw,
417: .hw2reg,
418:
419: .devmode_i (1'b1)
420: );
421:
422: /////////////////////////
423: // HMAC Error Handling //
424: /////////////////////////
425: logic msg_push_sha_disabled, hash_start_sha_disabled, update_seckey_inprocess;
426: assign msg_push_sha_disabled = msg_write & ~sha_en;
427: assign hash_start_sha_disabled = reg_hash_start & ~sha_en;
428:
429: always_comb begin
430: update_seckey_inprocess = 1'b0;
431: if (cfg_block) begin
432: for (int i = 0 ; i < 8 ; i++) begin
433: if (reg2hw.key[i].qe) begin
434: update_seckey_inprocess = update_seckey_inprocess | 1'b1;
435: end
436: end
437: end else begin
438: update_seckey_inprocess = 1'b0;
439: end
440: end
441:
442: // Update ERR_CODE register and interrupt only when no pending interrupt.
443: // This ensures only the first event of the series of events can be seen to sw.
444: // It is recommended that the software reads ERR_CODE register when interrupt
445: // is pending to avoid any race conditions.
446: assign err_valid = ~reg2hw.intr_state.hmac_err.q &
447: ( msg_push_sha_disabled | hash_start_sha_disabled
448: | update_seckey_inprocess);
449:
450: always_comb begin
451: err_code = NoError;
452: unique case (1'b1)
453: msg_push_sha_disabled: begin
454: err_code = SwPushMsgWhenShaDisabled;
455: end
456: hash_start_sha_disabled: begin
457: err_code = SwHashStartWhenShaDisabled;
458: end
459:
460: update_seckey_inprocess: begin
461: err_code = SwUpdateSecretKeyInProcess;
462: end
463:
464: default: begin
465: err_code = NoError;
466: end
467: endcase
468: end
469:
470: /////////////////////
471: // Hardware Alerts //
472: /////////////////////
473:
474: // TODO: add CSR with REGWEN to test alert via SW
475: logic [NumAlerts-1:0] alerts;
476: assign alerts = {msg_push_sha_disabled};
477:
478: for (genvar j = 0; j < hmac_pkg::NumAlerts; j++) begin : gen_alert_tx
479: prim_alert_sender #(
480: .AsyncOn(hmac_pkg::AlertAsyncOn[j])
481: ) i_prim_alert_sender (
482: .clk_i ( clk_i ),
483: .rst_ni ( rst_ni ),
484: .alert_i ( alerts[j] ),
485: .alert_rx_i ( alert_rx_i[j] ),
486: .alert_tx_o ( alert_tx_o[j] )
487: );
488: end : gen_alert_tx
489:
490: //////////////////////////////////////////////
491: // Assertions, Assumptions, and Coverpoints //
492: //////////////////////////////////////////////
493:
494: `ifndef VERILATOR
495: `ifndef SYNTHESIS
496: // HMAC assumes TL-UL mask is byte-aligned.
497: property wmask_bytealign_p(wmask_byte, clk, rst_n);
498: @(posedge clk) disable iff (rst_n == 0)
499: msg_fifo_req & msg_fifo_we |-> wmask_byte inside {'0, '1};
500: endproperty
501:
502: for (genvar i = 0 ; i < 4; i++) begin: gen_assert_wmask_bytealign
503: assert property (wmask_bytealign_p(msg_fifo_wmask[8*i+:8], clk_i, rst_ni));
504: end
505:
506: // To pass FPV, this shouldn't add pragma translate_off even these two signals
507: // are used in Assertion only
508: logic in_process;
509: always_ff @(posedge clk_i or negedge rst_ni) begin
510: if (!rst_ni) in_process <= 1'b0;
511: else if (reg_hash_process) in_process <= 1'b1;
512: else if (reg_hash_done) in_process <= 1'b0;
513: end
514:
515: logic initiated;
516: always_ff @(posedge clk_i or negedge rst_ni) begin
517: if (!rst_ni) initiated <= 1'b0;
518: else if (hash_start) initiated <= 1'b1;
519: else if (reg_hash_process) initiated <= 1'b0;
520: end
521:
522: // the host doesn't write data after hash_process until hash_start.
523: // Same as "message_length shouldn't be changed between hash_process and done
524: `ASSERT(ValidWriteAssert, msg_fifo_req |-> !in_process, clk_i, !rst_ni)
525:
526: // `hash_process` shall be toggle and paired with `hash_start`.
527: `ASSERT(ValidHashStartAssert, hash_start |-> !initiated, clk_i, !rst_ni)
528: `ASSERT(ValidHashProcessAssert, reg_hash_process |-> initiated, clk_i, !rst_ni)
529:
530: // between `hash_done` and `hash_start`, message FIFO should be empty
531: `ASSERT(MsgFifoEmptyWhenNoOpAssert,
532: !in_process && !initiated |-> $stable(message_length),
533: clk_i, !rst_ni)
534:
535: // hmac_en should be modified only when the logic is Idle
536: `ASSERT(ValidHmacEnConditionAssert,
537: hmac_en != $past(hmac_en) |-> !in_process && !initiated,
538: clk_i, !rst_ni)
539:
540: // All outputs should be known value after reset
541: `ASSERT_KNOWN(IntrHmacDoneOKnown, intr_hmac_done_o, clk_i, !rst_ni)
542: `ASSERT_KNOWN(IntrFifoFullOKnown, intr_fifo_full_o, clk_i, !rst_ni)
543: `ASSERT_KNOWN(TlODValidKnown, tl_o.d_valid, clk_i, !rst_ni)
544: `ASSERT_KNOWN(TlOAReadyKnown, tl_o.a_ready, clk_i, !rst_ni)
545:
546: // Alert outputs
547: `ASSERT_KNOWN(AlertTxOKnown, alert_tx_o, clk_i, !rst_ni)
548:
549: `endif // SYNTHESIS
550: `endif // VERILATOR
551:
552: endmodule
553: