diff --git a/hw/ip/keymgr/data/keymgr.hjson b/hw/ip/keymgr/data/keymgr.hjson index 6b1e8ebc68840..79f0f948161de 100644 --- a/hw/ip/keymgr/data/keymgr.hjson +++ b/hw/ip/keymgr/data/keymgr.hjson @@ -669,7 +669,7 @@ name: "VAL", resval: "0x100" desc: ''' - Number of key manager cycles before the entropy is reseeded + Number of internal PRNG updates before a reseed is requested. ''' }, ] diff --git a/hw/ip/keymgr/dv/env/keymgr_if.sv b/hw/ip/keymgr/dv/env/keymgr_if.sv index 10021f3228ba3..c27c2525d989a 100644 --- a/hw/ip/keymgr/dv/env/keymgr_if.sv +++ b/hw/ip/keymgr/dv/env/keymgr_if.sv @@ -38,6 +38,9 @@ interface keymgr_if(input clk, input rst_n); // connect EDN for assertion check wire edn_clk, edn_rst_n, edn_req, edn_ack; + // connect to PRNG to track PRNG updates for EDN assertion + wire lfsr_en; + // keymgr_en is async, create a sync one for use in scb lc_ctrl_pkg::lc_tx_t keymgr_en_sync1, keymgr_en_sync2; @@ -72,12 +75,15 @@ interface keymgr_if(input clk, input rst_n); // keymgr will request edn twice for 64 bit data each time, use this to indicate if it's first or // second req. 0: wait for 1st req, 1: for 2nd bit edn_req_cnt; + bit edn_wait_cnt_incr; + bit edn_wait_cnt_clr; int edn_wait_cnt; int edn_interval; // synchronize req/ack from async domain bit edn_req_sync; bit edn_req_ack_sync; bit edn_req_ack_sync_done; + bit edn_req_ack_sync_done_qq; // If we need to wait for internal signal to be certain value, we may not be able to get that // when the sim is close to end. Define a cnt and MaxWaitCycle to avoid sim hang @@ -90,7 +96,7 @@ interface keymgr_if(input clk, input rst_n); string msg_id = "keymgr_if"; - int edn_tolerance_cycs = 20; + int edn_tolerance_upd = 20; task automatic init(bit rand_otp_key, bit invalid_otp_key); // Keymgr only latches OTP key once, so this scb does not support change OTP key on the @@ -355,8 +361,8 @@ interface keymgr_if(input clk, input rst_n); otbn_sideload_status <= SideLoadClear; endfunction - function automatic void update_edn_toleranc_cycs(int edn_clk, int main_clk); - if ((main_clk/edn_clk) * 10 > edn_tolerance_cycs) edn_tolerance_cycs = (main_clk/edn_clk) * 10; + function automatic void update_edn_tolerance_upd(int edn_clk, int main_clk); + if ((main_clk/edn_clk) * 10 > edn_tolerance_upd) edn_tolerance_upd = (main_clk/edn_clk) * 10; endfunction logic valid_done_window; @@ -649,13 +655,27 @@ interface keymgr_if(input clk, input rst_n); end end + always_ff @(posedge clk or negedge rst_n) begin + if (!rst_n) begin + edn_req_ack_sync_done_qq <= 1'b0; + end else begin + edn_req_ack_sync_done_qq <= edn_req_ack_sync_done; + end + end + + // Increment the counter for PRNG updates happening unless we're waiting for EDN. + assign edn_wait_cnt_incr = lfsr_en && (!edn_req_sync || (edn_req_sync && edn_req_ack_sync)); + + // Clear the counter upon rising edges of the synchronized req_ack_done signal. + assign edn_wait_cnt_clr = edn_req_ack_sync_done & !edn_req_ack_sync_done_qq; + always @(posedge clk or negedge rst_n) begin if (!rst_n) begin edn_wait_cnt <= 0; - end else if (!edn_req_sync) begin - edn_wait_cnt <= edn_wait_cnt + 1; - end else begin + end else if (edn_wait_cnt_clr) begin edn_wait_cnt <= 0; + end else if (edn_wait_cnt_incr) begin + edn_wait_cnt <= edn_wait_cnt + 1; end end @@ -671,16 +691,16 @@ interface keymgr_if(input clk, input rst_n); end end - // consider async handshaking and a few cycles to start the req. allow no more than - // `edn_tolerance_cycs` tolerance error on the cnt. - // `edn_tolerance_cycs` default value is 20, but if the frequency difference between edn and main + // consider async handshaking and a few PRNG update requests to start the req. allow no more than + // `edn_tolerance_upd` tolerance error on the cnt. + // `edn_tolerance_upd` default value is 20, but if the frequency difference between edn and main // clock is too big, the testbench will scale it up to a larger value. `ASSERT(CheckEdn1stReq, $rose(edn_req_sync) && edn_req_cnt == 0 && start_edn_req |-> - (edn_wait_cnt > edn_interval) && (edn_wait_cnt - edn_interval < edn_tolerance_cycs), + (edn_wait_cnt >= edn_interval) && (edn_wait_cnt - edn_interval < edn_tolerance_upd), clk, !rst_n || !en_chk) `ASSERT(CheckEdn2ndReq, $rose(edn_req_sync) && edn_req_cnt == 1 |-> - edn_wait_cnt < edn_tolerance_cycs, + edn_wait_cnt < edn_tolerance_upd, clk, !rst_n || !en_chk) `undef ASSERT_IFF_KEYMGR_LEGAL diff --git a/hw/ip/keymgr/dv/env/seq_lib/keymgr_base_vseq.sv b/hw/ip/keymgr/dv/env/seq_lib/keymgr_base_vseq.sv index 8cbbde204ae87..7caa5a43bfb22 100644 --- a/hw/ip/keymgr/dv/env/seq_lib/keymgr_base_vseq.sv +++ b/hw/ip/keymgr/dv/env/seq_lib/keymgr_base_vseq.sv @@ -56,7 +56,7 @@ class keymgr_base_vseq extends cip_base_vseq #( virtual task dut_init(string reset_kind = "HARD"); super.dut_init(); - cfg.keymgr_vif.update_edn_toleranc_cycs(cfg.edn_clk_freq_mhz, cfg.clk_freq_mhz); + cfg.keymgr_vif.update_edn_tolerance_upd(cfg.edn_clk_freq_mhz, cfg.clk_freq_mhz); op_before_enable_keymgr(); cfg.keymgr_vif.init(do_rand_otp_key, do_invalid_otp_key); diff --git a/hw/ip/keymgr/dv/tb.sv b/hw/ip/keymgr/dv/tb.sv index cf5da1a36a09f..ebd82cf4d1d64 100644 --- a/hw/ip/keymgr/dv/tb.sv +++ b/hw/ip/keymgr/dv/tb.sv @@ -39,6 +39,7 @@ module tb; assign keymgr_if.edn_rst_n = edn_if[0].rst_n; assign keymgr_if.edn_req = edn_if[0].req; assign keymgr_if.edn_ack = edn_if[0].ack; + assign keymgr_if.lfsr_en = dut.lfsr_en; // dut keymgr #( diff --git a/hw/ip/keymgr/rtl/keymgr.sv b/hw/ip/keymgr/rtl/keymgr.sv index 44d199bb54930..61150df22862f 100644 --- a/hw/ip/keymgr/rtl/keymgr.sv +++ b/hw/ip/keymgr/rtl/keymgr.sv @@ -183,6 +183,7 @@ module keymgr // The second case is less sensitive and is applied directly. If the inputs // have more bits than the lfsr output, the lfsr value is simply replicated + logic lfsr_en; logic seed_en; logic [LfsrWidth-1:0] seed; logic reseed_req; @@ -199,6 +200,7 @@ module keymgr .reseed_interval_i(reg2hw.reseed_interval_shadowed.q), .edn_o, .edn_i, + .lfsr_en_i(lfsr_en), .seed_en_o(seed_en), .seed_o(seed), .cnt_err_o(reseed_cnt_err) @@ -206,6 +208,7 @@ module keymgr logic [63:0] lfsr; logic ctrl_lfsr_en, data_lfsr_en, sideload_lfsr_en; + assign lfsr_en = ctrl_lfsr_en | data_lfsr_en | sideload_lfsr_en; prim_lfsr #( .LfsrDw(LfsrWidth), @@ -217,7 +220,7 @@ module keymgr ) u_lfsr ( .clk_i, .rst_ni, - .lfsr_en_i(ctrl_lfsr_en | data_lfsr_en | sideload_lfsr_en), + .lfsr_en_i(lfsr_en), .seed_en_i(seed_en), .seed_i(seed), .entropy_i('0), diff --git a/hw/ip/keymgr/rtl/keymgr_reseed_ctrl.sv b/hw/ip/keymgr/rtl/keymgr_reseed_ctrl.sv index 369c6d491ff4d..1d7c56522d110 100644 --- a/hw/ip/keymgr/rtl/keymgr_reseed_ctrl.sv +++ b/hw/ip/keymgr/rtl/keymgr_reseed_ctrl.sv @@ -25,6 +25,7 @@ module keymgr_reseed_ctrl import keymgr_pkg::*; ( input edn_pkg::edn_rsp_t edn_i, // interface to lfsr + input logic lfsr_en_i, output logic seed_en_o, output logic [LfsrWidth-1:0] seed_o, @@ -97,7 +98,7 @@ module keymgr_reseed_ctrl import keymgr_pkg::*; ( .clr_i(edn_done), .set_i('0), .set_cnt_i('0), - .incr_en_i(cnt_en), + .incr_en_i(cnt_en & lfsr_en_i), .decr_en_i(1'b0), .step_i(16'h1), .commit_i(1'b1), diff --git a/hw/ip/keymgr_dpe/data/keymgr_dpe.hjson b/hw/ip/keymgr_dpe/data/keymgr_dpe.hjson index a32cecc77ce78..f4d19c054a2d5 100644 --- a/hw/ip/keymgr_dpe/data/keymgr_dpe.hjson +++ b/hw/ip/keymgr_dpe/data/keymgr_dpe.hjson @@ -621,7 +621,7 @@ countermeasures: [ name: "VAL", resval: "0x100" desc: ''' - Number of key manager cycles before the entropy is reseeded + Number of internal PRNG updates before a reseed is requested. ''' }, ] diff --git a/hw/ip/keymgr_dpe/dv/env/keymgr_dpe_if.sv b/hw/ip/keymgr_dpe/dv/env/keymgr_dpe_if.sv index 34f2b5507c638..7363710f26b0c 100644 --- a/hw/ip/keymgr_dpe/dv/env/keymgr_dpe_if.sv +++ b/hw/ip/keymgr_dpe/dv/env/keymgr_dpe_if.sv @@ -38,6 +38,9 @@ interface keymgr_dpe_if(input clk, input rst_n); // connect EDN for assertion check wire edn_clk, edn_rst_n, edn_req, edn_ack; + // connect to PRNG to track PRNG updates for EDN assertion + wire lfsr_en; + // keymgr_dpe_en is async, create a sync one for use in scb lc_ctrl_pkg::lc_tx_t keymgr_dpe_en_sync1, keymgr_dpe_en_sync2; @@ -74,12 +77,15 @@ interface keymgr_dpe_if(input clk, input rst_n); // use this to indicate if it's first or // second req. 0: wait for 1st req, 1: for 2nd bit edn_req_cnt; + bit edn_wait_cnt_incr; + bit edn_wait_cnt_clr; int edn_wait_cnt; int edn_interval; // synchronize req/ack from async domain bit edn_req_sync; bit edn_req_ack_sync; bit edn_req_ack_sync_done; + bit edn_req_ack_sync_done_qq; // If we need to wait for internal signal to be certain value, we may not be able to get that // when the sim is close to end. Define a cnt and MaxWaitCycle to avoid sim hang @@ -92,7 +98,7 @@ interface keymgr_dpe_if(input clk, input rst_n); string msg_id = "keymgr_dpe_if"; - int edn_tolerance_cycs = 20; + int edn_tolerance_upd = 20; // assigned from the keymgr_dpe.keymgr_dpe_ctrl.key_slots_q signal, which should hold the // current value of the keyslots in the dut. @@ -385,8 +391,8 @@ interface keymgr_dpe_if(input clk, input rst_n); otbn_sideload_status <= SideLoadClear; endfunction - function automatic void update_edn_toleranc_cycs(int edn_clk, int main_clk); - if ((main_clk/edn_clk) * 10 > edn_tolerance_cycs) edn_tolerance_cycs = (main_clk/edn_clk) * 10; + function automatic void update_edn_tolerance_upd(int edn_clk, int main_clk); + if ((main_clk/edn_clk) * 10 > edn_tolerance_upd) edn_tolerance_upd = (main_clk/edn_clk) * 10; endfunction logic valid_done_window; @@ -696,13 +702,27 @@ interface keymgr_dpe_if(input clk, input rst_n); end end + always_ff @(posedge clk or negedge rst_n) begin + if (!rst_n) begin + edn_req_ack_sync_done_qq <= 1'b0; + end else begin + edn_req_ack_sync_done_qq <= edn_req_ack_sync_done; + end + end + + // Increment the counter for PRNG updates happening unless we're waiting for EDN. + assign edn_wait_cnt_incr = lfsr_en && (!edn_req_sync || (edn_req_sync && edn_req_ack_sync)); + + // Clear the counter upon rising edges of the synchronized req_ack_done signal. + assign edn_wait_cnt_clr = edn_req_ack_sync_done & !edn_req_ack_sync_done_qq; + always @(posedge clk or negedge rst_n) begin if (!rst_n) begin edn_wait_cnt <= 0; - end else if (!edn_req_sync) begin - edn_wait_cnt <= edn_wait_cnt + 1; - end else begin + end else if (edn_wait_cnt_clr) begin edn_wait_cnt <= 0; + end else if (edn_wait_cnt_incr) begin + edn_wait_cnt <= edn_wait_cnt + 1; end end @@ -718,16 +738,16 @@ interface keymgr_dpe_if(input clk, input rst_n); end end - // consider async handshaking and a few cycles to start the req. allow no more than - // `edn_tolerance_cycs` tolerance error on the cnt. - // `edn_tolerance_cycs` default value is 20, but if the frequency difference between edn and main + // consider async handshaking and a few PRNG update requests to start the req. allow no more than + // `edn_tolerance_upd` tolerance error on the cnt. + // `edn_tolerance_upd` default value is 20, but if the frequency difference between edn and main // clock is too big, the testbench will scale it up to a larger value. `ASSERT(CheckEdn1stReq, $rose(edn_req_sync) && edn_req_cnt == 0 && start_edn_req |-> - (edn_wait_cnt > edn_interval) && (edn_wait_cnt - edn_interval < edn_tolerance_cycs), + (edn_wait_cnt >= edn_interval) && (edn_wait_cnt - edn_interval < edn_tolerance_upd), clk, !rst_n || !en_chk) `ASSERT(CheckEdn2ndReq, $rose(edn_req_sync) && edn_req_cnt == 1 |-> - edn_wait_cnt < edn_tolerance_cycs, + edn_wait_cnt < edn_tolerance_upd, clk, !rst_n || !en_chk) `undef ASSERT_IFF_KEYMGR_DPE_LEGAL diff --git a/hw/ip/keymgr_dpe/dv/env/seq_lib/keymgr_dpe_base_vseq.sv b/hw/ip/keymgr_dpe/dv/env/seq_lib/keymgr_dpe_base_vseq.sv index 6c6454b165c4c..eb0f4875bb678 100644 --- a/hw/ip/keymgr_dpe/dv/env/seq_lib/keymgr_dpe_base_vseq.sv +++ b/hw/ip/keymgr_dpe/dv/env/seq_lib/keymgr_dpe_base_vseq.sv @@ -67,7 +67,7 @@ class keymgr_dpe_base_vseq extends cip_base_vseq #( virtual task dut_init(string reset_kind = "HARD"); super.dut_init(); - cfg.keymgr_dpe_vif.update_edn_toleranc_cycs(cfg.edn_clk_freq_mhz, cfg.clk_freq_mhz); + cfg.keymgr_dpe_vif.update_edn_tolerance_upd(cfg.edn_clk_freq_mhz, cfg.clk_freq_mhz); op_before_enable_keymgr(); cfg.keymgr_dpe_vif.init(do_rand_otp_key, do_invalid_otp_key); diff --git a/hw/ip/keymgr_dpe/dv/tb.sv b/hw/ip/keymgr_dpe/dv/tb.sv index f159c4c221a75..6da67b85fa2e2 100644 --- a/hw/ip/keymgr_dpe/dv/tb.sv +++ b/hw/ip/keymgr_dpe/dv/tb.sv @@ -41,6 +41,7 @@ module tb; assign keymgr_dpe_if.edn_rst_n = edn_if[0].rst_n; assign keymgr_dpe_if.edn_req = edn_if[0].req; assign keymgr_dpe_if.edn_ack = edn_if[0].ack; + assign keymgr_dpe_if.lfsr_en = dut.lfsr_en; // dut keymgr_dpe #( diff --git a/hw/ip/keymgr_dpe/rtl/keymgr_dpe.sv b/hw/ip/keymgr_dpe/rtl/keymgr_dpe.sv index 305781e5fa31d..ecbc767ec979c 100644 --- a/hw/ip/keymgr_dpe/rtl/keymgr_dpe.sv +++ b/hw/ip/keymgr_dpe/rtl/keymgr_dpe.sv @@ -174,6 +174,7 @@ module keymgr_dpe // The second case is less sensitive and is applied directly. If the inputs // have more bits than the lfsr output, the lfsr value is simply replicated + logic lfsr_en; logic seed_en; logic [LfsrWidth-1:0] seed; logic reseed_req; @@ -190,6 +191,7 @@ module keymgr_dpe .reseed_interval_i(reg2hw.reseed_interval_shadowed.q), .edn_o, .edn_i, + .lfsr_en_i(lfsr_en), .seed_en_o(seed_en), .seed_o(seed), .cnt_err_o(reseed_cnt_err) @@ -197,6 +199,7 @@ module keymgr_dpe logic [63:0] lfsr; logic ctrl_lfsr_en, data_lfsr_en, sideload_lfsr_en; + assign lfsr_en = ctrl_lfsr_en | data_lfsr_en | sideload_lfsr_en; prim_lfsr #( .LfsrDw(LfsrWidth), @@ -208,7 +211,7 @@ module keymgr_dpe ) u_lfsr ( .clk_i, .rst_ni, - .lfsr_en_i(ctrl_lfsr_en | data_lfsr_en | sideload_lfsr_en), + .lfsr_en_i(lfsr_en), .seed_en_i(seed_en), .seed_i(seed), .entropy_i('0),