From dab429c9ae65c1af2f78fac3bdea2c4dd37073a3 Mon Sep 17 00:00:00 2001 From: Martin Velay Date: Wed, 6 Nov 2024 15:20:13 +0000 Subject: [PATCH] [hmac,dv] Adpot extern method declarations Signed-off-by: Martin Velay --- hw/ip/hmac/dv/env/hmac_env.sv | 40 +- hw/ip/hmac/dv/env/hmac_env_cfg.sv | 29 +- hw/ip/hmac/dv/env/hmac_env_cov.sv | 34 +- hw/ip/hmac/dv/env/hmac_scoreboard.sv | 1616 +++++++++-------- .../dv/env/seq_lib/hmac_back_pressure_vseq.sv | 52 +- hw/ip/hmac/dv/env/seq_lib/hmac_base_vseq.sv | 1435 ++++++++------- .../hmac/dv/env/seq_lib/hmac_burst_wr_vseq.sv | 19 +- hw/ip/hmac/dv/env/seq_lib/hmac_common_vseq.sv | 93 +- .../env/seq_lib/hmac_datapath_stress_vseq.sv | 54 +- hw/ip/hmac/dv/env/seq_lib/hmac_error_vseq.sv | 29 +- .../hmac/dv/env/seq_lib/hmac_long_msg_vseq.sv | 28 +- hw/ip/hmac/dv/env/seq_lib/hmac_smoke_vseq.sv | 406 +++-- .../dv/env/seq_lib/hmac_stress_all_vseq.sv | 80 +- .../seq_lib/hmac_test_vectors_hmac_vseq.sv | 34 +- .../env/seq_lib/hmac_test_vectors_sha_vseq.sv | 248 +-- .../dv/env/seq_lib/hmac_wipe_secret_vseq.sv | 53 +- hw/ip/hmac/dv/tests/hmac_base_test.sv | 21 +- 17 files changed, 2262 insertions(+), 2009 deletions(-) diff --git a/hw/ip/hmac/dv/env/hmac_env.sv b/hw/ip/hmac/dv/env/hmac_env.sv index cf66966285554c..3c627f2ab85f0f 100644 --- a/hw/ip/hmac/dv/env/hmac_env.sv +++ b/hw/ip/hmac/dv/env/hmac_env.sv @@ -7,23 +7,31 @@ class hmac_env extends cip_base_env #(.CFG_T (hmac_env_cfg), .VIRTUAL_SEQUENCER_T (hmac_virtual_sequencer), .SCOREBOARD_T (hmac_scoreboard)); `uvm_component_utils(hmac_env) - `uvm_component_new - function void build_phase(uvm_phase phase); - super.build_phase(phase); + // Standard SV/UVM methods + extern function new(string name="", uvm_component parent=null); + extern function void build_phase(uvm_phase phase); + extern function void end_of_elaboration_phase(uvm_phase phase); +endclass : hmac_env - // config hmac virtual interface - if (!uvm_config_db#(hmac_vif)::get(this, "", "hmac_vif", cfg.hmac_vif)) begin - `uvm_fatal(`gfn, "failed to get hmac_vif from uvm_config_db") - end - endfunction - virtual function void end_of_elaboration_phase(uvm_phase phase); - dv_base_mem mem; - super.end_of_elaboration_phase(phase); - // hmac mem supports partial write, set it after ral model is locked - `downcast(mem, get_mem_by_addr(cfg.ral, cfg.ral.get_addr_from_offset(HMAC_MSG_FIFO_BASE))) - mem.set_mem_partial_write_support(1); - endfunction +function hmac_env::new(string name="", uvm_component parent=null); + super.new(name, parent); +endfunction : new -endclass +function void hmac_env::build_phase(uvm_phase phase); + super.build_phase(phase); + + // config hmac virtual interface + if (!uvm_config_db#(hmac_vif)::get(this, "", "hmac_vif", cfg.hmac_vif)) begin + `uvm_fatal(`gfn, "failed to get hmac_vif from uvm_config_db") + end +endfunction : build_phase + +function void hmac_env::end_of_elaboration_phase(uvm_phase phase); + dv_base_mem mem; + super.end_of_elaboration_phase(phase); + // hmac mem supports partial write, set it after ral model is locked + `downcast(mem, get_mem_by_addr(cfg.ral, cfg.ral.get_addr_from_offset(HMAC_MSG_FIFO_BASE))) + mem.set_mem_partial_write_support(1); +endfunction : end_of_elaboration_phase diff --git a/hw/ip/hmac/dv/env/hmac_env_cfg.sv b/hw/ip/hmac/dv/env/hmac_env_cfg.sv index 49f3f124ad9da5..5ee5543afffad6 100644 --- a/hw/ip/hmac/dv/env/hmac_env_cfg.sv +++ b/hw/ip/hmac/dv/env/hmac_env_cfg.sv @@ -4,7 +4,6 @@ class hmac_env_cfg extends cip_base_env_cfg #(.RAL_T(hmac_reg_block)); `uvm_object_utils(hmac_env_cfg) - `uvm_object_new // A flag to nofity scoreboard if digest is corrupted by wipe_secret command. bit wipe_secret_triggered; @@ -25,14 +24,24 @@ class hmac_env_cfg extends cip_base_env_cfg #(.RAL_T(hmac_reg_block)); hmac_vif hmac_vif; - virtual function void initialize(bit [TL_AW-1:0] csr_base_addr = '1); - list_of_alerts = hmac_env_pkg::LIST_OF_ALERTS; - super.initialize(csr_base_addr); - // set num_interrupts & num_alerts which will be used to create coverage and more - num_interrupts = ral.intr_state.get_n_used_bits(); + // Standard SV/UVM methods + extern function new(string name=""); - // only support 1 outstanding TL items in tlul_adapter - m_tl_agent_cfg.max_outstanding_req = 1; - endfunction + // Class specific methods + extern function void initialize(bit [TL_AW-1:0] csr_base_addr = '1); +endclass : hmac_env_cfg -endclass + +function hmac_env_cfg::new(string name=""); + super.new(name); +endfunction : new + +function void hmac_env_cfg::initialize(bit [TL_AW-1:0] csr_base_addr = '1); + list_of_alerts = hmac_env_pkg::LIST_OF_ALERTS; + super.initialize(csr_base_addr); + // set num_interrupts & num_alerts which will be used to create coverage and more + num_interrupts = ral.intr_state.get_n_used_bits(); + + // only support 1 outstanding TL items in tlul_adapter + m_tl_agent_cfg.max_outstanding_req = 1; +endfunction : initialize diff --git a/hw/ip/hmac/dv/env/hmac_env_cov.sv b/hw/ip/hmac/dv/env/hmac_env_cov.sv index 95b51dbcedc5c7..1ec87fbb46d0e0 100644 --- a/hw/ip/hmac/dv/env/hmac_env_cov.sv +++ b/hw/ip/hmac/dv/env/hmac_env_cov.sv @@ -143,18 +143,22 @@ class hmac_env_cov extends cip_base_env_cov #(.CFG_T(hmac_env_cfg)); sar_type_x_digest_size : cross save_and_restore_cp, digest_size_cp; endgroup : save_and_restore_cg - function new(string name, uvm_component parent); - super.new(name, parent); - cfg_cg = new(); - status_cg = new(); - msg_len_cg = new(); - err_code_cg = new(); - wr_config_during_hash_cg = new(); - wr_key_during_hash_cg = new(); - wr_key_during_sha_only_cg = new(); - wr_msg_during_hash_cg = new(); - trig_rst_during_hash_cg = new(); - rd_digest_during_hmac_en_cg = new(); - save_and_restore_cg = new(); - endfunction : new -endclass + // Standard SV/UVM methods + extern function new(string name, uvm_component parent); +endclass : hmac_env_cov + + +function hmac_env_cov::new(string name, uvm_component parent); + super.new(name, parent); + cfg_cg = new(); + status_cg = new(); + msg_len_cg = new(); + err_code_cg = new(); + wr_config_during_hash_cg = new(); + wr_key_during_hash_cg = new(); + wr_key_during_sha_only_cg = new(); + wr_msg_during_hash_cg = new(); + trig_rst_during_hash_cg = new(); + rd_digest_during_hmac_en_cg = new(); + save_and_restore_cg = new(); +endfunction : new diff --git a/hw/ip/hmac/dv/env/hmac_scoreboard.sv b/hw/ip/hmac/dv/env/hmac_scoreboard.sv index c04d0a6e0f7a09..e4f8e216e6f955 100644 --- a/hw/ip/hmac/dv/env/hmac_scoreboard.sv +++ b/hw/ip/hmac/dv/env/hmac_scoreboard.sv @@ -6,7 +6,6 @@ class hmac_scoreboard extends cip_base_scoreboard #(.CFG_T (hmac_env_cfg), .RAL_T (hmac_reg_block), .COV_T (hmac_env_cov)); `uvm_component_utils(hmac_scoreboard) - `uvm_component_new bit sha_en, hmac_idle; bit [7:0] msg_q[$]; @@ -33,863 +32,914 @@ class hmac_scoreboard extends cip_base_scoreboard #(.CFG_T (hmac_env_cfg), bit [TL_DW-1:0] intr_test; bit [TL_DW-1:0] intr_state_exp; - function void build_phase(uvm_phase phase); - super.build_phase(phase); - endfunction : build_phase - - task run_phase(uvm_phase phase); - super.run_phase(phase); - wait(cfg.under_reset); - forever begin - wait(!cfg.under_reset); - // This isolation fork is needed to ensure that "disable fork" call won't kill any other - // processes at the same level from the base classes - fork begin : isolation_fork - fork - begin : main_thread - fork - hmac_process_fifo_status(); - hmac_process_fifo_wr(); - hmac_process_fifo_rd(); - hmac_intr_test(); - monitor_cov(); - join - wait fork; // To ensure it will be killed only when the reset will occur - end - begin : reset_thread - wait(cfg.under_reset); - end - join_any - disable fork; // Terminates all descendants and sub-descendants of isolation_fork - end join - end - endtask : run_phase - - virtual task process_tl_access(tl_seq_item item, tl_channels_e channel, string ral_name); - uvm_reg csr; - string csr_name; - bit do_read_check = 1'b1; - bit do_cycle_accurate_check = 1'b1; - bit write = item.is_write(); - bit [TL_AW-1:0] addr_mask = ral.get_addr_mask(); - uvm_reg_addr_t csr_addr = cfg.ral_models[ral_name].get_word_aligned_addr(item.a_addr); - - // if access was to a valid csr, get the csr handle - if (csr_addr inside {cfg.ral_models[ral_name].csr_addrs}) begin - csr = cfg.ral_models[ral_name].default_map.get_reg_by_offset(csr_addr); - `DV_CHECK_NE_FATAL(csr, null) - csr_name = csr.get_name(); - // if addr inside msg fifo, no ral model - end else if (!((item.a_addr & addr_mask) inside {[HMAC_MSG_FIFO_BASE : - HMAC_MSG_FIFO_LAST_ADDR]})) begin - `uvm_fatal(`gfn, $sformatf("Access unexpected addr 0x%0h", csr_addr)) - end + // Standard SV/UVM methods + extern function new(string name="", uvm_component parent=null); + extern function void build_phase(uvm_phase phase); + extern task run_phase(uvm_phase phase); + extern function void check_phase(uvm_phase phase); + + // Class specific methods + // Update predicted digest to RAL mirrored value + extern virtual task process_tl_access(tl_seq_item item, tl_channels_e channel, string ral_name); + // Reset some variables in case of a reset is happening + extern virtual function void reset(string kind = "HARD"); + // Clear some variables after hmac_done/invalid_config + extern virtual function void flush(); + // Model of the message FIFO when is written + extern virtual task hmac_process_fifo_wr(); + // Model of the message FIFO when is read + extern virtual task hmac_process_fifo_rd(); + // Model the status of the message FIFO + extern virtual task hmac_process_fifo_status(); + // Spawn process to check interrupt pins in test mode + extern virtual task hmac_intr_test(); + // Check interrupt pins in test mode + extern virtual task hmac_intr_test_pin(int intr_i); + // Query SHA/HMAC to the C model to get expected digest + extern virtual function void predict_digest(bit [7:0] msg_i[], bit use_gmv = 1, bit sha_en = 0, + bit hmac_en = 0, bit [3:0] digest_size = 0, bit [5:0] key_length = 0); + // Update the write message length + extern virtual function void update_wr_msg_length(int size_bytes); + // Model the interrupt code error + extern virtual task update_err_intr_code(err_code_e err_code_val); + // Check idle output pin and update status register predicted value + extern virtual function void check_idle(bit val); + // Trigger coverage sampling + extern virtual task monitor_cov(); +endclass : hmac_scoreboard - // if incoming access is a write to a valid csr or mem, then update right away on addr channel - if (write && channel == AddrChannel) begin - // push the msg into msg_fifo - if ((item.a_addr & addr_mask) inside {[HMAC_MSG_FIFO_BASE : HMAC_MSG_FIFO_LAST_ADDR]}) begin - // Only push message into the FIFO when intended, as in case of S&R triggered with another - // context, we don't want to process the message of this other context. - if (!cfg.sar_skip_ctxt) begin - if ((!sha_en) || (!hmac_start && !hmac_continue)) begin - update_err_intr_code(SwPushMsgWhenDisallowed); - end else if (!cfg.under_reset) begin - bit [7:0] bytes[4]; - bit [7:0] msg[$]; - // Register coverage for the fact that a write occurs to the message FIFO during - // a compression round - if (cfg.en_cov) cov.wr_msg_during_hash_cg.sample(1); - {< msg_q.size()) begin - msg_slice_length = msg_q.size(); + check_idle(1'b1); end - - // Push partial message into a new queue to be processed by the digest predictor - for (int i=0; i= (hmac_wr_cnt + 1) * 4 || (hmac_process && msg_q.size() > 0)); - // Ensure that current context has to be taken into account or skipped - if (cfg.sar_skip_ctxt) begin - wait(!cfg.sar_skip_ctxt); - end - // if hmac process is issued and there are still unprocessed word (can hold up to at - // most one word), then the hmac_wr_cnt will increment - // if all the written msgs have been process, will skip the counter incrementation - if (hmac_process) begin - if (msg_q.size() <= hmac_wr_cnt * 4) begin - has_unprocessed_msg = 0; - end - msg_q.delete(); - msg_part_q.delete(); + return; + end + + // On reads, if do_read_check, is set, then check mirrored_value against item.d_data + if (!write) begin + case (csr_name) + "intr_state": begin + if (!do_cycle_accurate_check) begin + do_read_check = 0; + end + if (cfg.en_cov) begin + bit [TL_DW-1:0] intr_en = `gmv(ral.intr_enable); + hmac_intr_e intr; + intr = intr.first; + do begin + cov.intr_cg.sample(intr, intr_en[intr], item.d_data[intr]); + cov.intr_pins_cg.sample(intr, cfg.intr_vif.pins[intr]); + intr = intr.next; + end while (intr != intr.first); + end + if (item.d_data[HmacDone]) begin + // here check DUT should only trigger hmac_done when sha is enabled, and + // previously triggered hash_process or hash_stop. + // future throughput test should check the accurate cycles + if (sha_en && (hmac_process || hmac_stopped)) begin + void'(ral.intr_state.hmac_done.predict(.value(1), .kind(UVM_PREDICT_READ))); + end + check_idle(1'b1); + flush(); + end + // hmac has been triggered and config is invalid + if (hmac_start && invalid_cfg) begin + flush(); + end end - if (sha_en && has_unprocessed_msg) begin - // if fifo full, tlul will not write next data until fifo has space again - if ((hmac_wr_cnt - hmac_rd_cnt) == HMAC_MSG_FIFO_DEPTH_WR) begin - wait((hmac_wr_cnt - hmac_rd_cnt) < HMAC_MSG_FIFO_DEPTH_WR); + "digest_0", "digest_1", "digest_2", "digest_3", "digest_4", "digest_5", "digest_6", + "digest_7", "digest_8", "digest_9", "digest_10", "digest_11", "digest_12", "digest_13", + "digest_14", "digest_15": begin + int digest_idx = get_digest_index(csr_name); + // By default, the hardware outputs little-endian data for each digest (32 bits). But DPI + // functions/test vectors from specs expect output to be big-endian. + // Therefore we should flip the expected value if digest_swap is zero. + bit [TL_DW-1:0] real_digest_val; + + // Read and check DIGEST while HMAC is enabled/disabled + if (cfg.en_cov) cov.rd_digest_during_hmac_en_cg.sample(`gmv(ral.cfg.hmac_en)); + + if (!`gmv(ral.cfg.digest_swap)) begin + // digest_swap is set to true to match spec vectors/expected digest word (big-endian) + // when digest swap is not true, then we need to swap the digest word + // to be able to compare it with the expected digest word (big-endian) + real_digest_val = {<<8{item.d_data}}; + end else begin + real_digest_val = item.d_data; + end + + // decide whether to assume previous or new digest size to compare correctly + // with the expected digest, because digests are retained from previous operation + // until new configuration is successfully started + if (previous_digest_size != `gmv(ral.cfg.digest_size)) begin + expected_digest_size = previous_digest_size; + end else begin + expected_digest_size = `gmv(ral.cfg.digest_size); end - @(negedge cfg.clk_rst_vif.clk); + + `uvm_info(`gfn, $sformatf( + "comparing digest sizes (previous, cfg, and expected): %4b, %4b, %4b", + previous_digest_size, `gmv(ral.cfg.digest_size), + expected_digest_size), UVM_HIGH) + + // Predict intermediate digest values when S&R has been triggered + // TODO (#23240): modify the C model to be able to provide intermediate digest + // if (hmac_stopped) predict_digest(msg_q); + // And remove lines, when C model will support intermediate hash. For the moment don't + // compare digest. Only the final digest will be compared in case of S&R, this is enough + // for the moment + // Proceed with read digest and compare values with expected if (!hmac_stopped) begin - hmac_wr_cnt++; - `uvm_info(`gfn, $sformatf("increase wr cnt %0d", hmac_wr_cnt), UVM_HIGH) - cfg.clk_rst_vif.wait_clks(HMAC_WR_WORD_CYCLE); + // If wipe_secret is triggered, ensure the predicted value does not match the read out + // digest and update the predicted value with the read out value. + if (cfg.wipe_secret_triggered) begin + `DV_CHECK_NE(real_digest_val, exp_digest[digest_idx]) + `uvm_info(`gfn, $sformatf("updating digest to read value after wiping 0x%0h", + exp_digest[digest_idx]), UVM_HIGH) + // update new digest data to the exp_digest variable. + exp_digest[digest_idx] = real_digest_val; + end else begin // !cfg.wipe_secret_triggered + // only check till digest_idx = 7 for SHA-2 256 and till digest_idx = 11 for SHA-2 384 + // Remaining digest values are irrelevant or truncated for these digest sizes. + if (((expected_digest_size == SHA2_256) && (digest_idx < 8)) || + ((expected_digest_size == SHA2_384) && (digest_idx < 12)) || + expected_digest_size == SHA2_512) begin + `DV_CHECK_EQ(real_digest_val, exp_digest[digest_idx]) + end + end end + return; end - end - endtask : hmac_process_fifo_wr - - virtual task hmac_process_fifo_status(); - bit pre_fifo_empty_intr; - forever @(hmac_wr_cnt, hmac_rd_cnt, hmac_process, hmac_start, hmac_stopped, hmac_continue) begin - // Store hmac_process and hmac_start to be able to detect when it goes up, as it could remain - // up for a while - bit hmac_process_posedge = hmac_process & ~hmac_process_last; - bit hmac_start_posedge = hmac_start & ~hmac_start_last; - bit hmac_stopped_posedge = hmac_stopped & ~hmac_stopped_last; - bit hmac_continue_posedge = hmac_continue & ~hmac_continue_last; - - // Store last value to be able to detect signal change - hmac_process_last = hmac_process; - hmac_stopped_last = hmac_stopped; - hmac_start_last = hmac_start; - hmac_continue_last = hmac_continue; - - // when hmac_wr_cnt and hmac_rd_cnt update at the same time, wait the clock rising edge - // to guarantee get both update and at the same time as the DUT - cfg.clk_rst_vif.wait_clks(1); - - // Compute FIFO level flags - hmac_fifo_depth = hmac_wr_cnt - hmac_rd_cnt; - hmac_fifo_full = hmac_fifo_depth == HMAC_MSG_FIFO_DEPTH_WR; - hmac_fifo_empty = hmac_fifo_depth == 0; - - // The FIFO empty interrupt is raised only if the message FIFO is actually writable by - // software, i.e., if all of the following conditions are met: - // 1- The HMAC block is not running in HMAC mode and performing the second round of - // computing the final hash of the outer key as well as the result of the first round - // using the inner key. - // 2- Software has not yet written the Process or Stop command to finish the hashing - // operation. - // 3- The message FIFO must also have been full previously. Otherwise, the hardware empties - // the FIFO faster than software can fill it and there is no point in interrupting the - // software to inform it about the message FIFO being empty. - if (fifo_full_detected && hmac_fifo_empty) begin - pre_fifo_empty_intr = 1; - end else begin - pre_fifo_empty_intr = 0; + "status": begin + if (!do_cycle_accurate_check || cfg.sar_skip_ctxt) begin + do_read_check = 0; + end + if (cfg.en_cov) cov.status_cg.sample(item.d_data, `gmv(ral.cfg)); end - - // Delay FIFO empty signal to be aligned with the DUT behavior - fork - begin - cfg.clk_rst_vif.wait_clks(1); - fifo_empty_intr = pre_fifo_empty_intr; + "msg_length_lower": begin + if (cfg.sar_skip_ctxt) begin + do_read_check = 0; + end + if (cfg.en_cov) begin + cov.msg_len_cg.sample(.msg_len_lower(item.d_data), + .msg_len_upper('x), + .cfg(`gmv(ral.cfg))); end - join_none - - // Check whether FIFO full has been detected for the ongoing message but the retrictions cases - // have the priority in case full is set at the same moment - if (hmac_fifo_empty || hmac_start_posedge || hmac_process_posedge || - hmac_stopped_posedge || hmac_continue_posedge) begin - fifo_full_detected = 0; - end else if (hmac_fifo_full) begin - fifo_full_detected = 1; end + "msg_length_upper": begin + if (cfg.sar_skip_ctxt) begin + do_read_check = 0; + end + if (cfg.en_cov) begin + cov.msg_len_cg.sample(.msg_len_lower('x), + .msg_len_upper(item.d_data), + .cfg(`gmv(ral.cfg))); + end + end + "err_code": if (cfg.en_cov) cov.err_code_cg.sample(item.d_data); + "key_0", "key_1", "key_2", "key_3", "key_4", "key_5", "key_6", "key_7", "key_8", "key_9", + "key_10", "key_11", "key_12", "key_13", "key_14", "key_15", "key_16", "key_17", "key_18", + "key_19", "key_20", "key_21", "key_22", "key_23", "key_24", "key_25", "key_26", "key_27", + "key_28", "key_29", "key_30", "key_31", "cfg", "cmd", "intr_enable", "intr_test", + "wipe_secret", "alert_test": begin + // Do nothing + end + default: begin + `uvm_fatal(`gfn, $sformatf("invalid csr: %0s", csr.get_full_name())) + end + endcase + if (do_read_check) begin + `uvm_info(`gfn, $sformatf("%s reg is checked with expected value %0h", + csr_name, `gmv(csr)), UVM_HIGH) + `DV_CHECK_EQ(item.d_data, `gmv(csr), csr_name) end - endtask : hmac_process_fifo_status - - // Spawn process to check interrupt pins in test mode - virtual task hmac_intr_test(); - foreach (intr_test[intr_i]) begin - fork begin - hmac_intr_test_pin(intr_i); - end join_none + void'(csr.predict(.value(item.d_data), .kind(UVM_PREDICT_READ))); + end +endtask : process_tl_access + +function void hmac_scoreboard::reset(string kind = "HARD"); + super.reset(kind); + + if (cfg.en_cov) cov.trig_rst_during_hash_cg.sample(hmac_process); + + // Should be reinitialized before flushing as it will be used as a condition + hmac_stopped = 0; + flush(); + key = '{default:0}; + exp_digest = '{default:0}; + hmac_idle = ral.status.hmac_idle.get_reset(); + hmac_fifo_empty = ral.status.fifo_empty.get_reset(); + hmac_fifo_full = ral.status.fifo_full.get_reset(); + hmac_fifo_depth = ral.status.fifo_depth.get_reset(); + hmac_start = ral.cmd.hash_start.get_reset(); + sha_en = ral.cfg.sha_en.get_reset(); + hmac_stopped = 0; + msg_q.delete(); + msg_part_q.delete(); + cfg.wipe_secret_triggered = 0; +endfunction : reset + +// clear variables after hmac_done +function void hmac_scoreboard::flush(); + // Do not flush counters if the current context should be ignored or if stopped + if (!hmac_stopped && !cfg.sar_skip_ctxt) begin + hmac_wr_cnt = 0; + hmac_rd_cnt = 0; + msg_q.delete(); + end + hmac_process = 0; + hmac_start = 0; + hmac_continue = 0; + hmac_process_last = 0; + hmac_start_last = 0; + hmac_stopped_last = 0; + hmac_continue_last = 0; + fifo_full_detected = 0; + fifo_empty_intr = 0; + void'(ral.intr_state.fifo_empty.predict(.value(fifo_empty_intr | intr_test[HmacMsgFifoEmpty]), + .kind(UVM_PREDICT_READ))); +endfunction : flush + +// hmac_wr_cnt was incremented every time when msg_q has 4 bytes streamed in +// or when hash_process is triggered, and there are some remaining bytes +// this task will also clear the msg_q queue once hmac_process is set +task hmac_scoreboard::hmac_process_fifo_wr(); + bit has_unprocessed_msg; + forever begin + has_unprocessed_msg = 1; + wait(msg_q.size() >= (hmac_wr_cnt + 1) * 4 || (hmac_process && msg_q.size() > 0)); + // Ensure that current context has to be taken into account or skipped + if (cfg.sar_skip_ctxt) begin + wait(!cfg.sar_skip_ctxt); end - endtask : hmac_intr_test - - // Check interrupt pins in test mode - virtual task hmac_intr_test_pin(int intr_i); - bit [TL_DW-1:0] intr_en; - hmac_intr_e intr = hmac_intr_e'(intr_i); - forever @(intr_test[intr_i]) begin - intr_en = `gmv(ral.intr_enable); - if (intr_test[intr_i]) begin - // Check interrupt state pins only if enabled - if (intr_en[intr_i]) begin - fork: intr_pins - begin - wait(cfg.intr_vif.pins[intr_i]); - `uvm_info(`gfn, $sformatf("Detected interrupt on pin %s", intr.name()), UVM_HIGH) - end - begin - cfg.clk_rst_vif.wait_clks(100); // 100 clk cycles timeout - `uvm_error(`gfn, $sformatf("Wait pin interrupt timeout for %s", intr.name())) - end - join_any - disable fork; - end + // if hmac process is issued and there are still unprocessed word (can hold up to at + // most one word), then the hmac_wr_cnt will increment + // if all the written msgs have been process, will skip the counter incrementation + if (hmac_process) begin + if (msg_q.size() <= hmac_wr_cnt * 4) begin + has_unprocessed_msg = 0; + end + msg_q.delete(); + msg_part_q.delete(); + end + if (sha_en && has_unprocessed_msg) begin + // if fifo full, tlul will not write next data until fifo has space again + if ((hmac_wr_cnt - hmac_rd_cnt) == HMAC_MSG_FIFO_DEPTH_WR) begin + wait((hmac_wr_cnt - hmac_rd_cnt) < HMAC_MSG_FIFO_DEPTH_WR); + end + @(negedge cfg.clk_rst_vif.clk); + if (!hmac_stopped) begin + hmac_wr_cnt++; + `uvm_info(`gfn, $sformatf("increase wr cnt %0d", hmac_wr_cnt), UVM_HIGH) + cfg.clk_rst_vif.wait_clks(HMAC_WR_WORD_CYCLE); end end - endtask : hmac_intr_test_pin - - // internal msg_fifo model to check fifo status and interrupt. - // monitor rd_cnt and wr_cnt on the negedge of the clk - // rd_cnt followed by wr_cnt with a clk cycle delay, except: - // 1). hmac process key: DUT will process the key first - // 2). read cnt reaches FIFO_MAX: DUT will process msg in the FIFO - virtual task hmac_process_fifo_rd(); - bit key_processed = 0; + end +endtask : hmac_process_fifo_wr + +task hmac_scoreboard::hmac_process_fifo_status(); + bit pre_fifo_empty_intr; + forever @(hmac_wr_cnt, hmac_rd_cnt, hmac_process, hmac_start, hmac_stopped, hmac_continue) begin + // Store hmac_process and hmac_start to be able to detect when it goes up, as it could remain + // up for a while + bit hmac_process_posedge = hmac_process & ~hmac_process_last; + bit hmac_start_posedge = hmac_start & ~hmac_start_last; + bit hmac_stopped_posedge = hmac_stopped & ~hmac_stopped_last; + bit hmac_continue_posedge = hmac_continue & ~hmac_continue_last; + + // Store last value to be able to detect signal change + hmac_process_last = hmac_process; + hmac_stopped_last = hmac_stopped; + hmac_start_last = hmac_start; + hmac_continue_last = hmac_continue; + + // when hmac_wr_cnt and hmac_rd_cnt update at the same time, wait the clock rising edge + // to guarantee get both update and at the same time as the DUT + cfg.clk_rst_vif.wait_clks(1); + + // Compute FIFO level flags + hmac_fifo_depth = hmac_wr_cnt - hmac_rd_cnt; + hmac_fifo_full = hmac_fifo_depth == HMAC_MSG_FIFO_DEPTH_WR; + hmac_fifo_empty = hmac_fifo_depth == 0; + + // The FIFO empty interrupt is raised only if the message FIFO is actually writable by + // software, i.e., if all of the following conditions are met: + // 1- The HMAC block is not running in HMAC mode and performing the second round of + // computing the final hash of the outer key as well as the result of the first round + // using the inner key. + // 2- Software has not yet written the Process or Stop command to finish the hashing + // operation. + // 3- The message FIFO must also have been full previously. Otherwise, the hardware empties + // the FIFO faster than software can fill it and there is no point in interrupting the + // software to inform it about the message FIFO being empty. + if (fifo_full_detected && hmac_fifo_empty) begin + pre_fifo_empty_intr = 1; + end else begin + pre_fifo_empty_intr = 0; + end + + // Delay FIFO empty signal to be aligned with the DUT behavior fork - begin : process_hmac_key_pad - forever begin - cfg.clk_rst_vif.wait_clks(1); - // Key padding - wait(hmac_start && sha_en); - if (`gmv(ral.cfg.hmac_en) && hmac_rd_cnt == 0 && !invalid_cfg) begin - if (`gmv(ral.cfg.digest_size) == SHA2_256) begin - key_process_cycles = HMAC_KEY_PROCESS_CYCLES_256; - end else begin - key_process_cycles = HMAC_KEY_PROCESS_CYCLES_512; - end - // key_process_cycles for hmac key padding + 1 cycle for hash_start reg to reset - cfg.clk_rst_vif.wait_clks(key_process_cycles + 1); - @(negedge cfg.clk_rst_vif.clk); - key_processed = 1; - end - while (1) begin - // TODO: check if we need the error checking here - might not be necessary - // break if hmac is done or if invalid config error is triggered or if SHA is - // being disabled as HMAC enable configuration could be changed and thus - // key_process_cycles might need to be updated - if (`gmv(ral.intr_state.hmac_done) || - (`gmv(ral.intr_state.hmac_err) && `gmv(ral.err_code) == SwInvalidConfig) || - !sha_en) begin - break; - end - cfg.clk_rst_vif.wait_clks(1); - end - key_processed = 0; - end + begin + cfg.clk_rst_vif.wait_clks(1); + fifo_empty_intr = pre_fifo_empty_intr; end + join_none - begin : process_internal_fifo_rd - forever begin - // Ensure that current context has to be taken into account or skipped - if (cfg.sar_skip_ctxt) begin - wait(!cfg.sar_skip_ctxt); - end - wait((hmac_wr_cnt > hmac_rd_cnt) && (sha_en)); - if (`gmv(ral.cfg.hmac_en) && hmac_rd_cnt == 0) begin - `uvm_info(`gfn, $sformatf("waiting on key processing to complete"), UVM_HIGH) - wait(key_processed); - `uvm_info(`gfn, $sformatf("key processing has completed"), UVM_HIGH) + // Check whether FIFO full has been detected for the ongoing message but the retrictions cases + // have the priority in case full is set at the same moment + if (hmac_fifo_empty || hmac_start_posedge || hmac_process_posedge || + hmac_stopped_posedge || hmac_continue_posedge) begin + fifo_full_detected = 0; + end else if (hmac_fifo_full) begin + fifo_full_detected = 1; + end + end +endtask : hmac_process_fifo_status + +// Spawn process to check interrupt pins in test mode +task hmac_scoreboard::hmac_intr_test(); + foreach (intr_test[intr_i]) begin + fork begin + hmac_intr_test_pin(intr_i); + end join_none + end +endtask : hmac_intr_test + +// Check interrupt pins in test mode +task hmac_scoreboard::hmac_intr_test_pin(int intr_i); + bit [TL_DW-1:0] intr_en; + hmac_intr_e intr = hmac_intr_e'(intr_i); + forever @(intr_test[intr_i]) begin + intr_en = `gmv(ral.intr_enable); + if (intr_test[intr_i]) begin + // Check interrupt state pins only if enabled + if (intr_en[intr_i]) begin + fork: intr_pins + begin + wait(cfg.intr_vif.pins[intr_i]); + `uvm_info(`gfn, $sformatf("Detected interrupt on pin %s", intr.name()), UVM_HIGH) + end + begin + cfg.clk_rst_vif.wait_clks(100); // 100 clk cycles timeout + `uvm_error(`gfn, $sformatf("Wait pin interrupt timeout for %s", intr.name())) end - #1ps; // delay 1 ps to make sure did not sample right at negedge clk - cfg.clk_rst_vif.wait_n_clks(1); - hmac_rd_cnt++; - `uvm_info(`gfn, $sformatf("increase rd cnt %0d", hmac_rd_cnt), UVM_HIGH) - // select correct FIFO read depth and message processing cycles + join_any + disable fork; + end + end + end +endtask : hmac_intr_test_pin + +// internal msg_fifo model to check fifo status and interrupt. +// monitor rd_cnt and wr_cnt on the negedge of the clk +// rd_cnt followed by wr_cnt with a clk cycle delay, except: +// 1). hmac process key: DUT will process the key first +// 2). read cnt reaches FIFO_MAX: DUT will process msg in the FIFO +task hmac_scoreboard::hmac_process_fifo_rd(); + bit key_processed = 0; + fork + begin : process_hmac_key_pad + forever begin + cfg.clk_rst_vif.wait_clks(1); + // Key padding + wait(hmac_start && sha_en); + if (`gmv(ral.cfg.hmac_en) && hmac_rd_cnt == 0 && !invalid_cfg) begin if (`gmv(ral.cfg.digest_size) == SHA2_256) begin - fifo_rd_depth = HMAC_MSG_FIFO_DEPTH_RD_256; - block_process_cycles = HMAC_MSG_PROCESS_CYCLES_256; + key_process_cycles = HMAC_KEY_PROCESS_CYCLES_256; end else begin - fifo_rd_depth = HMAC_MSG_FIFO_DEPTH_RD_512; - block_process_cycles = HMAC_MSG_PROCESS_CYCLES_512; + key_process_cycles = HMAC_KEY_PROCESS_CYCLES_512; end - if (hmac_rd_cnt % fifo_rd_depth == 0) begin - `uvm_info(`gfn, $sformatf("start waiting on message processing now"), UVM_HIGH) - cfg.clk_rst_vif.wait_n_clks(block_process_cycles); - `uvm_info(`gfn, $sformatf("message processing has completed"), UVM_HIGH) + // key_process_cycles for hmac key padding + 1 cycle for hash_start reg to reset + cfg.clk_rst_vif.wait_clks(key_process_cycles + 1); + @(negedge cfg.clk_rst_vif.clk); + key_processed = 1; + end + while (1) begin + // TODO: check if we need the error checking here - might not be necessary + // break if hmac is done or if invalid config error is triggered or if SHA is + // being disabled as HMAC enable configuration could be changed and thus + // key_process_cycles might need to be updated + if (`gmv(ral.intr_state.hmac_done) || + (`gmv(ral.intr_state.hmac_err) && `gmv(ral.err_code) == SwInvalidConfig) || + !sha_en) begin + break; end + cfg.clk_rst_vif.wait_clks(1); end + key_processed = 0; end - join_none - endtask : hmac_process_fifo_rd - - function void check_phase(uvm_phase phase); - super.check_phase(phase); - `DV_CHECK_EQ(cfg.intr_vif.pins[HmacMsgFifoEmpty], 1'b0) - `DV_CHECK_EQ(cfg.intr_vif.pins[HmacDone], 1'b0) - `DV_CHECK_EQ(cfg.intr_vif.pins[HmacErr], 1'b0) - endfunction : check_phase - - // query the sha / hmac c model to get expected digest - // update predicted digest to ral mirrored value - virtual function void predict_digest( - bit [7:0] msg_i[], - bit sha_en = `gmv(ral.cfg.sha_en), - bit hmac_en = `gmv(ral.cfg.hmac_en), - bit [3:0] digest_size = `gmv(ral.cfg.digest_size), - bit [5:0] key_length = `gmv(ral.cfg.key_length)); - bit [7:0] msg_tmp[]; - bit [TL_DW-1:0] big_endian_key[NUM_KEYS]; - exp_digest = '{default:0}; // clear previous expected digest - - // Swap the key when required according to the dedicated register field CFG.key_swap - if (`gmv(ral.cfg.key_swap)) begin - `uvm_info(`gfn, "Swap the key from little-endian to big-endian", UVM_HIGH) - foreach (big_endian_key[key_i]) begin - big_endian_key[key_i] = {<<8{key[key_i]}}; - end - end else begin - `uvm_info(`gfn, "Keep the key in big-endian", UVM_HIGH) - big_endian_key = key; end - `uvm_info(`gfn, $sformatf("Computing digest prediction"), UVM_LOW) - - // TODO: predict even if sha_en == 0? - case ({hmac_en, sha_en}) - 2'b11: begin - if (digest_size == SHA2_256) begin - if (key_length == Key_128) begin - cryptoc_dpi_pkg::sv_dpi_get_hmac_sha256(big_endian_key[0:3], msg_i, exp_digest[0:7]); - end else if (key_length == Key_256) begin - cryptoc_dpi_pkg::sv_dpi_get_hmac_sha256(big_endian_key[0:7], msg_i, exp_digest[0:7]); - end else if (key_length == Key_384) begin - cryptoc_dpi_pkg::sv_dpi_get_hmac_sha256(big_endian_key[0:11], msg_i, exp_digest[0:7]); - end else if (key_length == Key_512) begin - cryptoc_dpi_pkg::sv_dpi_get_hmac_sha256(big_endian_key[0:15], msg_i, exp_digest[0:7]); - end else if (key_length == Key_1024) begin - // model how the HW will limit key length to max of block size - cryptoc_dpi_pkg::sv_dpi_get_hmac_sha256(big_endian_key[0:15], msg_i, exp_digest[0:7]); - end - end else if (digest_size == SHA2_384) begin - if (key_length == Key_128) cryptoc_dpi_pkg::sv_dpi_get_hmac_sha384( - big_endian_key[0:3], msg_i, exp_digest[0:11]); - if (key_length == Key_256) cryptoc_dpi_pkg::sv_dpi_get_hmac_sha384( - big_endian_key[0:7], msg_i, exp_digest[0:11]); - if (key_length == Key_384) cryptoc_dpi_pkg::sv_dpi_get_hmac_sha384( - big_endian_key[0:11], msg_i, exp_digest[0:11]); - if (key_length == Key_512) cryptoc_dpi_pkg::sv_dpi_get_hmac_sha384( - big_endian_key[0:15], msg_i, exp_digest[0:11]); - if (key_length == Key_1024) cryptoc_dpi_pkg::sv_dpi_get_hmac_sha384( - big_endian_key[0:31], msg_i, exp_digest[0:11]); - end else if (digest_size == SHA2_512) begin - if (key_length == Key_128) cryptoc_dpi_pkg::sv_dpi_get_hmac_sha512( - big_endian_key[0:3], msg_i, exp_digest); - if (key_length == Key_256) cryptoc_dpi_pkg::sv_dpi_get_hmac_sha512( - big_endian_key[0:7], msg_i, exp_digest); - if (key_length == Key_384) cryptoc_dpi_pkg::sv_dpi_get_hmac_sha512( - big_endian_key[0:11], msg_i, exp_digest); - if (key_length == Key_512) cryptoc_dpi_pkg::sv_dpi_get_hmac_sha512( - big_endian_key[0:15], msg_i, exp_digest); - if (key_length == Key_1024) cryptoc_dpi_pkg::sv_dpi_get_hmac_sha512( - big_endian_key[0:31], msg_i, exp_digest); - end - `uvm_info(`gfn, $sformatf("HMAC of key=%p (key_swap=%1b), msg_i=%p: %p", - key, `gmv(ral.cfg.key_swap), msg_i, exp_digest), UVM_LOW) - end - 2'b01: begin - if (digest_size == SHA2_256) begin - cryptoc_dpi_pkg::sv_dpi_get_sha256_digest(msg_i, exp_digest[0:7]); - end else if (digest_size == SHA2_384) begin - cryptoc_dpi_pkg::sv_dpi_get_sha384_digest(msg_i, exp_digest[0:11]); - end else if (digest_size == SHA2_512) begin - cryptoc_dpi_pkg::sv_dpi_get_sha512_digest(msg_i, exp_digest); - end - `uvm_info(`gfn, $sformatf("SHA-2 digest of msg_i=%p: %p", msg_i, exp_digest), UVM_LOW) - end - default: begin - // disgest is cleared if sha_en = 0 - exp_digest = '{default:0}; + begin : process_internal_fifo_rd + forever begin + // Ensure that current context has to be taken into account or skipped + if (cfg.sar_skip_ctxt) begin + wait(!cfg.sar_skip_ctxt); + end + wait((hmac_wr_cnt > hmac_rd_cnt) && (sha_en)); + if (`gmv(ral.cfg.hmac_en) && hmac_rd_cnt == 0) begin + `uvm_info(`gfn, $sformatf("waiting on key processing to complete"), UVM_HIGH) + wait(key_processed); + `uvm_info(`gfn, $sformatf("key processing has completed"), UVM_HIGH) + end + #1ps; // delay 1 ps to make sure did not sample right at negedge clk + cfg.clk_rst_vif.wait_n_clks(1); + hmac_rd_cnt++; + `uvm_info(`gfn, $sformatf("increase rd cnt %0d", hmac_rd_cnt), UVM_HIGH) + // select correct FIFO read depth and message processing cycles + if (`gmv(ral.cfg.digest_size) == SHA2_256) begin + fifo_rd_depth = HMAC_MSG_FIFO_DEPTH_RD_256; + block_process_cycles = HMAC_MSG_PROCESS_CYCLES_256; + end else begin + fifo_rd_depth = HMAC_MSG_FIFO_DEPTH_RD_512; + block_process_cycles = HMAC_MSG_PROCESS_CYCLES_512; + end + if (hmac_rd_cnt % fifo_rd_depth == 0) begin + `uvm_info(`gfn, $sformatf("start waiting on message processing now"), UVM_HIGH) + cfg.clk_rst_vif.wait_n_clks(block_process_cycles); + `uvm_info(`gfn, $sformatf("message processing has completed"), UVM_HIGH) + end end - endcase - endfunction : predict_digest - - virtual function void update_wr_msg_length(int size_bytes); - uint64 size_bits = size_bytes * 8; - void'(ral.msg_length_upper.predict(size_bits[TL_DW*2-1:TL_DW])); - void'(ral.msg_length_lower.predict(size_bits[TL_DW-1:0])); - endfunction : update_wr_msg_length - - virtual task update_err_intr_code(err_code_e err_code_val); - if (!`gmv(ral.intr_state.hmac_err)) begin - while (ral.intr_state.is_busy()) begin - // using cfg.clk_rst_vif.wait_clks(1) instead was still resulting in race conditions with - // the incrementing of hmac_rd_cnt and hmac_wr_cnt and a desynchronization - #1ps; + end + join_none +endtask : hmac_process_fifo_rd + +function void hmac_scoreboard::check_phase(uvm_phase phase); + super.check_phase(phase); + `DV_CHECK_EQ(cfg.intr_vif.pins[HmacMsgFifoEmpty], 1'b0) + `DV_CHECK_EQ(cfg.intr_vif.pins[HmacDone], 1'b0) + `DV_CHECK_EQ(cfg.intr_vif.pins[HmacErr], 1'b0) +endfunction : check_phase + +// query the sha / hmac c model to get expected digest +// update predicted digest to ral mirrored value +function void hmac_scoreboard::predict_digest( + bit [7:0] msg_i[], + bit use_gmv = 1, + bit sha_en = 0, + bit hmac_en = 0, + bit [3:0] digest_size = 0, + bit [5:0] key_length = 0); + + bit [7:0] msg_tmp[]; + bit [TL_DW-1:0] big_endian_key[NUM_KEYS]; + + // Use mirrored values, otherwise take values from the arguments + if (use_gmv) begin + sha_en = `gmv(ral.cfg.sha_en); + hmac_en = `gmv(ral.cfg.hmac_en); + digest_size = `gmv(ral.cfg.digest_size); + key_length = `gmv(ral.cfg.key_length); + end + + exp_digest = '{default:0}; // clear previous expected digest + + // Swap the key when required according to the dedicated register field CFG.key_swap + if (`gmv(ral.cfg.key_swap)) begin + `uvm_info(`gfn, "Swap the key from little-endian to big-endian", UVM_HIGH) + foreach (big_endian_key[key_i]) begin + big_endian_key[key_i] = {<<8{key[key_i]}}; + end + end else begin + `uvm_info(`gfn, "Keep the key in big-endian", UVM_HIGH) + big_endian_key = key; + end + + `uvm_info(`gfn, $sformatf("Computing digest prediction"), UVM_LOW) + + // TODO: predict even if sha_en == 0? + case ({hmac_en, sha_en}) + 2'b11: begin + if (digest_size == SHA2_256) begin + if (key_length == Key_128) begin + cryptoc_dpi_pkg::sv_dpi_get_hmac_sha256(big_endian_key[0:3], msg_i, exp_digest[0:7]); + end else if (key_length == Key_256) begin + cryptoc_dpi_pkg::sv_dpi_get_hmac_sha256(big_endian_key[0:7], msg_i, exp_digest[0:7]); + end else if (key_length == Key_384) begin + cryptoc_dpi_pkg::sv_dpi_get_hmac_sha256(big_endian_key[0:11], msg_i, exp_digest[0:7]); + end else if (key_length == Key_512) begin + cryptoc_dpi_pkg::sv_dpi_get_hmac_sha256(big_endian_key[0:15], msg_i, exp_digest[0:7]); + end else if (key_length == Key_1024) begin + // model how the HW will limit key length to max of block size + cryptoc_dpi_pkg::sv_dpi_get_hmac_sha256(big_endian_key[0:15], msg_i, exp_digest[0:7]); + end + end else if (digest_size == SHA2_384) begin + if (key_length == Key_128) cryptoc_dpi_pkg::sv_dpi_get_hmac_sha384( + big_endian_key[0:3], msg_i, exp_digest[0:11]); + if (key_length == Key_256) cryptoc_dpi_pkg::sv_dpi_get_hmac_sha384( + big_endian_key[0:7], msg_i, exp_digest[0:11]); + if (key_length == Key_384) cryptoc_dpi_pkg::sv_dpi_get_hmac_sha384( + big_endian_key[0:11], msg_i, exp_digest[0:11]); + if (key_length == Key_512) cryptoc_dpi_pkg::sv_dpi_get_hmac_sha384( + big_endian_key[0:15], msg_i, exp_digest[0:11]); + if (key_length == Key_1024) cryptoc_dpi_pkg::sv_dpi_get_hmac_sha384( + big_endian_key[0:31], msg_i, exp_digest[0:11]); + end else if (digest_size == SHA2_512) begin + if (key_length == Key_128) cryptoc_dpi_pkg::sv_dpi_get_hmac_sha512( + big_endian_key[0:3], msg_i, exp_digest); + if (key_length == Key_256) cryptoc_dpi_pkg::sv_dpi_get_hmac_sha512( + big_endian_key[0:7], msg_i, exp_digest); + if (key_length == Key_384) cryptoc_dpi_pkg::sv_dpi_get_hmac_sha512( + big_endian_key[0:11], msg_i, exp_digest); + if (key_length == Key_512) cryptoc_dpi_pkg::sv_dpi_get_hmac_sha512( + big_endian_key[0:15], msg_i, exp_digest); + if (key_length == Key_1024) cryptoc_dpi_pkg::sv_dpi_get_hmac_sha512( + big_endian_key[0:31], msg_i, exp_digest); end - void'(ral.intr_state.hmac_err.predict(.value(1), .kind(UVM_PREDICT_DIRECT))); - - while (ral.err_code.is_busy()) begin - // using cfg.clk_rst_vif.wait_clks(1) instead was still resulting in race conditions with - // the incrementing of hmac_rd_cnt and hmac_wr_cnt and a desynchronization - #1ps; + `uvm_info(`gfn, $sformatf("HMAC of key=%p (key_swap=%1b), msg_i=%p: %p", + key, `gmv(ral.cfg.key_swap), msg_i, exp_digest), UVM_LOW) + end + 2'b01: begin + if (digest_size == SHA2_256) begin + cryptoc_dpi_pkg::sv_dpi_get_sha256_digest(msg_i, exp_digest[0:7]); + end else if (digest_size == SHA2_384) begin + cryptoc_dpi_pkg::sv_dpi_get_sha384_digest(msg_i, exp_digest[0:11]); + end else if (digest_size == SHA2_512) begin + cryptoc_dpi_pkg::sv_dpi_get_sha512_digest(msg_i, exp_digest); end - void'(ral.err_code.predict(.value(err_code_val), .kind(UVM_PREDICT_DIRECT))); + `uvm_info(`gfn, $sformatf("SHA-2 digest of msg_i=%p: %p", msg_i, exp_digest), UVM_LOW) end - endtask : update_err_intr_code - - // Check idle output pin and update status register predicted value - virtual function void check_idle(bit val); - if (cfg.under_reset == 0) begin - `DV_CHECK_EQ(cfg.hmac_vif.is_idle(), val) + default: begin + // disgest is cleared if sha_en = 0 + exp_digest = '{default:0}; end - hmac_idle = val; - void'(ral.status.hmac_idle.predict(.value(hmac_idle), .kind(UVM_PREDICT_READ))); - endfunction : check_idle - - virtual task monitor_cov(); - save_and_restore_e sar_ctxt; - uvm_event sar_same_ctxt_ev; - uvm_event sar_different_ctxt_ev; - uvm_event sar_stop_continue_ev; - sar_same_ctxt_ev = uvm_event_sar_pool::get_global("sar_same_context_event"); - sar_different_ctxt_ev = uvm_event_sar_pool::get_global("sar_different_context_event"); - sar_stop_continue_ev = uvm_event_sar_pool::get_global("sar_stop_and_continue_event"); + endcase +endfunction : predict_digest + +function void hmac_scoreboard::update_wr_msg_length(int size_bytes); + uint64 size_bits = size_bytes * 8; + void'(ral.msg_length_upper.predict(size_bits[TL_DW*2-1:TL_DW])); + void'(ral.msg_length_lower.predict(size_bits[TL_DW-1:0])); +endfunction : update_wr_msg_length + +task hmac_scoreboard::update_err_intr_code(err_code_e err_code_val); + if (!`gmv(ral.intr_state.hmac_err)) begin + while (ral.intr_state.is_busy()) begin + // using cfg.clk_rst_vif.wait_clks(1) instead was still resulting in race conditions with + // the incrementing of hmac_rd_cnt and hmac_wr_cnt and a desynchronization + #1ps; + end + void'(ral.intr_state.hmac_err.predict(.value(1), .kind(UVM_PREDICT_DIRECT))); - fork - // Save and Restore with same context - begin - forever begin - sar_same_ctxt_ev.wait_trigger(); - if (cfg.en_cov) cov.save_and_restore_cg.sample(.sar_ctxt(SameContext), - .cfg(`gmv(ral.cfg))); - end + while (ral.err_code.is_busy()) begin + // using cfg.clk_rst_vif.wait_clks(1) instead was still resulting in race conditions with + // the incrementing of hmac_rd_cnt and hmac_wr_cnt and a desynchronization + #1ps; + end + void'(ral.err_code.predict(.value(err_code_val), .kind(UVM_PREDICT_DIRECT))); + end +endtask : update_err_intr_code + +// Check idle output pin and update status register predicted value +function void hmac_scoreboard::check_idle(bit val); + if (cfg.under_reset == 0) begin + `DV_CHECK_EQ(cfg.hmac_vif.is_idle(), val) + end + hmac_idle = val; + void'(ral.status.hmac_idle.predict(.value(hmac_idle), .kind(UVM_PREDICT_READ))); +endfunction : check_idle + +task hmac_scoreboard::monitor_cov(); + save_and_restore_e sar_ctxt; + uvm_event sar_same_ctxt_ev; + uvm_event sar_different_ctxt_ev; + uvm_event sar_stop_continue_ev; + sar_same_ctxt_ev = uvm_event_sar_pool::get_global("sar_same_context_event"); + sar_different_ctxt_ev = uvm_event_sar_pool::get_global("sar_different_context_event"); + sar_stop_continue_ev = uvm_event_sar_pool::get_global("sar_stop_and_continue_event"); + + fork + // Save and Restore with same context + begin + forever begin + sar_same_ctxt_ev.wait_trigger(); + if (cfg.en_cov) cov.save_and_restore_cg.sample(.sar_ctxt(SameContext), + .cfg(`gmv(ral.cfg))); end - // Save and Restore with different contexts - begin - forever begin - sar_different_ctxt_ev.wait_trigger(); - if (cfg.en_cov) cov.save_and_restore_cg.sample(.sar_ctxt(DifferentContext), - .cfg(`gmv(ral.cfg))); - end + end + // Save and Restore with different contexts + begin + forever begin + sar_different_ctxt_ev.wait_trigger(); + if (cfg.en_cov) cov.save_and_restore_cg.sample(.sar_ctxt(DifferentContext), + .cfg(`gmv(ral.cfg))); end - // Stop and Continue without Saving and Restoring - begin - forever begin - sar_stop_continue_ev.wait_trigger(); - if (cfg.en_cov) cov.save_and_restore_cg.sample(.sar_ctxt(StopAndContinue), - .cfg(`gmv(ral.cfg))); - end + end + // Stop and Continue without Saving and Restoring + begin + forever begin + sar_stop_continue_ev.wait_trigger(); + if (cfg.en_cov) cov.save_and_restore_cg.sample(.sar_ctxt(StopAndContinue), + .cfg(`gmv(ral.cfg))); end - join_none - endtask : monitor_cov -endclass : hmac_scoreboard + end + join_none +endtask : monitor_cov diff --git a/hw/ip/hmac/dv/env/seq_lib/hmac_back_pressure_vseq.sv b/hw/ip/hmac/dv/env/seq_lib/hmac_back_pressure_vseq.sv index e10c534d68234c..06c5e2910654d8 100644 --- a/hw/ip/hmac/dv/env/seq_lib/hmac_back_pressure_vseq.sv +++ b/hw/ip/hmac/dv/env/seq_lib/hmac_back_pressure_vseq.sv @@ -7,25 +7,37 @@ class hmac_back_pressure_vseq extends hmac_smoke_vseq; `uvm_object_utils(hmac_back_pressure_vseq) - `uvm_object_new - - constraint msg_c { - msg.size() dist { - [129 :999] :/ 1, // larger than input FIFO depth - [1000 :3000] :/ 8, // 1KB - 2KB according to SW immediate usage - [3001 :10_000] :/ 1 // temp set to 10KB as max length, spec max size is 2^64 bits - }; - } - constraint wr_mask_c { - $countones(wr_mask) dist { - TL_DBW :/ 9, - [1:TL_DBW-1] :/ 1 - }; - } - - virtual task pre_start(); - do_back_pressure = 1'b1; - super.pre_start(); - endtask + // Constraints + extern constraint msg_c; + extern constraint wr_mask_c; + + // Standard SV/UVM methods + extern function new(string name=""); + extern task pre_start(); endclass : hmac_back_pressure_vseq + + +constraint hmac_back_pressure_vseq::msg_c { + msg.size() dist { + [129 :999] :/ 1, // larger than input FIFO depth + [1000 :3000] :/ 8, // 1KB - 2KB according to SW immediate usage + [3001 :10_000] :/ 1 // temp set to 10KB as max length, spec max size is 2^64 bits + }; +} + +constraint hmac_back_pressure_vseq::wr_mask_c { + $countones(wr_mask) dist { + TL_DBW :/ 9, + [1:TL_DBW-1] :/ 1 + }; +} + +function hmac_back_pressure_vseq::new(string name=""); + super.new(name); +endfunction : new + +task hmac_back_pressure_vseq::pre_start(); + do_back_pressure = 1'b1; + super.pre_start(); +endtask : pre_start diff --git a/hw/ip/hmac/dv/env/seq_lib/hmac_base_vseq.sv b/hw/ip/hmac/dv/env/seq_lib/hmac_base_vseq.sv index 77c8d2a962ba90..800e9b6cc27931 100644 --- a/hw/ip/hmac/dv/env/seq_lib/hmac_base_vseq.sv +++ b/hw/ip/hmac/dv/env/seq_lib/hmac_base_vseq.sv @@ -7,7 +7,6 @@ class hmac_base_vseq extends cip_base_vseq #(.CFG_T (hmac_env_cfg) .COV_T (hmac_env_cov), .VIRTUAL_SEQUENCER_T (hmac_virtual_sequencer)); `uvm_object_utils(hmac_base_vseq) - `uvm_object_new // Control knobs bit do_hmac_init = 1'b1; @@ -43,724 +42,778 @@ class hmac_base_vseq extends cip_base_vseq #(.CFG_T (hmac_env_cfg) local bit save_ctx_ongoing; local bit sar_ongoing; - constraint wr_addr_c { - wr_addr inside {[HMAC_MSG_FIFO_BASE : HMAC_MSG_FIFO_LAST_ADDR]}; - } - - constraint wr_mask_c { - $countones(wr_mask) dist { - TL_DBW :/ 1, - [1:TL_DBW-1] :/ 1 - }; - } - - constraint key_length_c { - $countones(key_length) == 1 dist { - 1 :/ 4, // Key_128/Key_256/Key_384/Key_512/Key_1024/Key_None - 0 :/ 1 // Illegal -> should get casted to Key_None in HW - }; - } - - constraint digest_size_c { - $countones(digest_size) == 1 dist { - 1 :/ 4, // SHA2_256/SHA2_384/SHA2_512/SHA2_None - 0 :/ 1 // Illegal -> should get casted to SHA2_None in HW - }; - } - - constraint wr_mask_contiguous_c { - $countones(wr_mask ^ {wr_mask[TL_DBW-2:0], 1'b0}) <= 2; // mask must have contiguous ones - } - - virtual task dut_init(string reset_kind = "HARD"); - super.dut_init(reset_kind); - if (do_hmac_init) begin - hmac_init(); - end - `DV_CHECK_EQ(cfg.hmac_vif.is_idle(), 1'b1) - endtask - - virtual task apply_reset(string kind = "HARD"); - super.apply_reset(kind); - cfg.hash_process_triggered = 0; - endtask - - virtual task hmac_init(bit sha_en = 1'b1, - bit hmac_en = 1'b1, - bit endian_swap = 1'b1, - bit digest_swap = 1'b1, - bit key_swap = 1'b0, - bit [3:0] digest_size = 4'b0001, // SHA-256 - bit [5:0] key_length = 6'b00_0010, // 256-bit key - bit intr_fifo_empty_en = 1'b1, - bit intr_hmac_done_en = 1'b1, - bit intr_hmac_err_en = 1'b1); - bit [TL_DW-1:0] interrupts; - // enable sha, hmac data paths with digest size SHA-2 256 - // and 512-bit key and writing to msg_fifo - ral.cfg.sha_en.set(sha_en); - ral.cfg.hmac_en.set(hmac_en); - ral.cfg.endian_swap.set(endian_swap); - ral.cfg.digest_swap.set(digest_swap); - ral.cfg.key_swap.set(key_swap); - ral.cfg.digest_size.set(digest_size); - ral.cfg.key_length.set(key_length); - csr_update(.csr(ral.cfg)); - - // read digest size and key length after casting from CSRs and update mirrored values - csr_rd_digest_size(cast_digest_size); - csr_rd_key_length(cast_key_length); - - // indicate if config is invalid and would block triggering the hash to start - if ((cast_digest_size == SHA2_None) || - ((cast_key_length == Key_None) && hmac_en) || - ((cast_digest_size == SHA2_256) && (cast_key_length == Key_1024) && hmac_en)) begin - invalid_cfg = 1; - end else begin - invalid_cfg = 0; - end - - `uvm_info(`gfn, $sformatf("invalid config: %1b", invalid_cfg), UVM_LOW) - - // enable interrupts - interrupts = (intr_hmac_err_en << HmacErr) | (intr_hmac_done_en << HmacDone) | - (intr_fifo_empty_en << HmacMsgFifoEmpty); - cfg_interrupts(.interrupts(interrupts), .enable(1'b1)); - endtask + // Constraints + extern constraint wr_addr_c; + extern constraint wr_mask_c; + extern constraint key_length_c; + extern constraint digest_size_c; + extern constraint wr_mask_contiguous_c; + + // Standard SV/UVM methods + extern function new(string name=""); + + // Class specific methods + extern task dut_init(string reset_kind = "HARD"); + extern task apply_reset(string kind = "HARD"); + extern task hmac_init(bit sha_en = 1'b1, bit hmac_en = 1'b1, bit endian_swap = 1'b1, + bit digest_swap = 1'b1, bit key_swap = 1'b0, bit [3:0] digest_size = 4'b0001, + bit [5:0] key_length = 6'b00_0010, bit intr_fifo_empty_en = 1'b1, bit intr_hmac_done_en = 1'b1, + bit intr_hmac_err_en = 1'b1); + extern task write_discard_config_and_key(bit do_wr_config, bit do_wr_key); + extern task sha_enable(); + extern task write_discard_config(); + extern task write_discard_key(); + extern task trigger_hash(); + extern task trigger_hash_continue(); + extern task trigger_hash_stop(); + extern task trigger_process(); + extern task trigger_hash_when_active(); + extern task rd_digest(); + extern function void clear_wipe_secret(); + extern task csr_rd_digest(output bit [TL_DW-1:0] digest[16]); + extern task csr_wr_digest(bit [TL_DW-1:0] digest[16]); + extern task csr_rd_digest_size(output bit [3:0] read_digest_size); + extern task csr_rd_key_length(output bit [5:0] read_key_length); + // Write 1024-bit hashed key + extern task wr_key(bit [TL_DW-1:0] key[]); + extern task wipe_secrets(); + // Write into DUT's message FIFO + extern task wr_msg(bit [7:0] msg[], bit no_sar=0); + // Burst write a chunk of words into DUT's message FIFO + extern task burst_wr_msg(bit [7:0] msg[], int burst_wr_length); + // Read the message length from the DUT reg (but discard it) + extern task rd_msg_length(); + // Read the message length from the DUT reg + extern task csr_rd_msg_length(output bit [2*TL_DW-1:0] msg_length); + // Write message length to the DUT reg + extern task csr_wr_msg_length(bit [2*TL_DW-1:0] msg_length); + // Read status and interrupt state and clear the interrupt state + extern task read_status_intr_clr(); + // Check intr_pin, intr_state, and error_code registers + extern task check_error_code(bit check_err = 1); + extern task compare_digest(bit [7:0] exp_digest[], int tag_len_byte, bit [3:0] digest_size_i); + extern task save_and_restore(); + extern task sar_stop_and_continue(); + extern task sar_same_context(); + extern task sar_different_context(); + extern task save_and_restore_cfg(bit save_current_cfg, bit restore_previous_cfg); + extern function int wait_cycles_with_no_outstanding_accesses(); +endclass : hmac_base_vseq - virtual task write_discard_config_and_key(bit do_wr_config, bit do_wr_key); - if (do_wr_config) begin - write_discard_config(); - end - if (do_wr_key) begin - write_discard_key(); - check_error_code(0); - end - endtask - // keep all the config values, but enable sha_en - virtual task sha_enable(); - ral.cfg.sha_en.set(1'b1); - csr_update(.csr(ral.cfg)); - endtask - - // attempt to change config reg during msg write, design will ignore the change - virtual task write_discard_config(); - bit [TL_DW-1:0] rand_config_value = $urandom(); - csr_wr(ral.cfg, rand_config_value); - endtask - - virtual task write_discard_key(); - bit [TL_DW-1:0] rand_key_value = $urandom(); - int key_idx = $urandom_range(0, 31); - csr_wr(ral.key[key_idx], rand_key_value); - endtask - - // start hash computations - virtual task trigger_hash(); - `uvm_info(`gfn, "triggering hash to start", UVM_LOW) - csr_wr(.ptr(ral.cmd), .value(1'b1 << HashStart)); - // If incorrectly configured or SHA is not enabled, check that an error is signaled. - if (invalid_cfg || !`gmv(ral.cfg.sha_en)) begin - check_error_code(); - end - endtask - - // continue hash computations - virtual task trigger_hash_continue(); - `uvm_info(`gfn, "triggering hash to continue", UVM_LOW) - csr_wr(.ptr(ral.cmd), .value(1'b1 << HashContinue)); - // If SHA is not enabled, check that an error is signaled. - if (!`gmv(ral.cfg.sha_en)) begin - check_error_code(); - end - // Should be triggered while B context to freeze wr_msg/burst_wr_msg as continue will - // be triggered to check a fictive B context with a large msg_length - if (!cfg.sar_skip_ctxt) begin - hash_continue.trigger(); - end - endtask - - // stop hash computations - virtual task trigger_hash_stop(); - `uvm_info(`gfn, "triggering hash to stop", UVM_LOW) - csr_wr(.ptr(ral.cmd), .value(1'b1 << HashStop)); - endtask - - // trigger calculation of digest at the end of a message only when Save and Restore hasn't been - // triggered before - virtual task trigger_process(); - if (!save_ctx_ongoing) begin - `uvm_info(`gfn, "triggering hash to process", UVM_LOW) - csr_wr(.ptr(ral.cmd), .value(1'b1 << HashProcess)); - cfg.hash_process_triggered = 1; - end - endtask - - virtual task trigger_hash_when_active(); - `uvm_info(`gfn, "triggering hash when active", UVM_LOW) - repeat ($urandom_range(1, 10)) trigger_hash(); +constraint hmac_base_vseq::wr_addr_c { + wr_addr inside {[HMAC_MSG_FIFO_BASE : HMAC_MSG_FIFO_LAST_ADDR]}; +} + +constraint hmac_base_vseq::wr_mask_c { + $countones(wr_mask) dist { + TL_DBW :/ 1, + [1:TL_DBW-1] :/ 1 + }; +} + +constraint hmac_base_vseq::key_length_c { + $countones(key_length) == 1 dist { + 1 :/ 4, // Key_128/Key_256/Key_384/Key_512/Key_1024/Key_None + 0 :/ 1 // Illegal -> should get casted to Key_None in HW + }; +} + +constraint hmac_base_vseq::digest_size_c { + $countones(digest_size) == 1 dist { + 1 :/ 4, // SHA2_256/SHA2_384/SHA2_512/SHA2_None + 0 :/ 1 // Illegal -> should get casted to SHA2_None in HW + }; +} + +constraint hmac_base_vseq::wr_mask_contiguous_c { + $countones(wr_mask ^ {wr_mask[TL_DBW-2:0], 1'b0}) <= 2; // mask must have contiguous ones +} + +function hmac_base_vseq::new(string name=""); + super.new(name); +endfunction : new + +task hmac_base_vseq::dut_init(string reset_kind = "HARD"); + super.dut_init(reset_kind); + if (do_hmac_init) begin + hmac_init(); + end + `DV_CHECK_EQ(cfg.hmac_vif.is_idle(), 1'b1) +endtask : dut_init + +task hmac_base_vseq::apply_reset(string kind = "HARD"); + super.apply_reset(kind); + cfg.hash_process_triggered = 0; +endtask : apply_reset + +task hmac_base_vseq::hmac_init( bit sha_en = 1'b1, + bit hmac_en = 1'b1, + bit endian_swap = 1'b1, + bit digest_swap = 1'b1, + bit key_swap = 1'b0, + bit [3:0] digest_size = 4'b0001, // SHA-256 + bit [5:0] key_length = 6'b00_0010, // 256-bit key + bit intr_fifo_empty_en = 1'b1, + bit intr_hmac_done_en = 1'b1, + bit intr_hmac_err_en = 1'b1); + bit [TL_DW-1:0] interrupts; + // enable sha, hmac data paths with digest size SHA-2 256 + // and 512-bit key and writing to msg_fifo + ral.cfg.sha_en.set(sha_en); + ral.cfg.hmac_en.set(hmac_en); + ral.cfg.endian_swap.set(endian_swap); + ral.cfg.digest_swap.set(digest_swap); + ral.cfg.key_swap.set(key_swap); + ral.cfg.digest_size.set(digest_size); + ral.cfg.key_length.set(key_length); + csr_update(.csr(ral.cfg)); + + // read digest size and key length after casting from CSRs and update mirrored values + csr_rd_digest_size(cast_digest_size); + csr_rd_key_length(cast_key_length); + + // indicate if config is invalid and would block triggering the hash to start + if ((cast_digest_size == SHA2_None) || + ((cast_key_length == Key_None) && hmac_en) || + ((cast_digest_size == SHA2_256) && (cast_key_length == Key_1024) && hmac_en)) begin + invalid_cfg = 1; + end else begin + invalid_cfg = 0; + end + + `uvm_info(`gfn, $sformatf("invalid config: %1b", invalid_cfg), UVM_LOW) + + // enable interrupts + interrupts = (intr_hmac_err_en << HmacErr) | (intr_hmac_done_en << HmacDone) | + (intr_fifo_empty_en << HmacMsgFifoEmpty); + cfg_interrupts(.interrupts(interrupts), .enable(1'b1)); +endtask : hmac_init + +task hmac_base_vseq::write_discard_config_and_key(bit do_wr_config, bit do_wr_key); + if (do_wr_config) begin + write_discard_config(); + end + if (do_wr_key) begin + write_discard_key(); check_error_code(0); - endtask - - // read digest value - virtual task rd_digest(); - bit [TL_DW-1:0] digest[16]; - csr_rd_digest(digest); - endtask : rd_digest - - virtual function clear_wipe_secret(); - `uvm_info(`gfn, "wiping secret untriggered", UVM_LOW) - cfg.wipe_secret_triggered = 0; - endfunction : clear_wipe_secret - - // read digest value and output read value - virtual task csr_rd_digest(output bit [TL_DW-1:0] digest[16]); - foreach (digest[i]) begin - csr_rd(.ptr(ral.digest[i]), .value(digest[i])); - `uvm_info(`gfn, $sformatf("digest[%0d]=32'h%08x", i, digest[i]), UVM_MEDIUM) - end - endtask - - // write digest value - virtual task csr_wr_digest(bit [TL_DW-1:0] digest[16]); - foreach (digest[i]) csr_wr(.ptr(ral.digest[i]), .value(digest[i])); - endtask - - // read digest size and update mirrored value - virtual task csr_rd_digest_size(output bit [3:0] read_digest_size); - csr_rd(.ptr(ral.cfg.digest_size), .value(read_digest_size)); - `uvm_info(`gfn, $sformatf("reading digest size: %04b", read_digest_size), UVM_MEDIUM) - endtask - - // read key length and update mirrored value - virtual task csr_rd_key_length(output bit [5:0] read_key_length); - csr_rd(.ptr(ral.cfg.key_length), .value(read_key_length)); - `uvm_info(`gfn, $sformatf("reading key length: %06b", read_key_length), UVM_MEDIUM) - endtask - - // write 1024-bit hashed key - // - // can safely assume that the input array will always have 32 elements - // key_length determines how much of the key array is relevant for the HMAC operation - virtual task wr_key(bit [TL_DW-1:0] key[]); - foreach (key[i]) begin - ral.key[i].set(key[i]); - csr_update(.csr(ral.key[i])); - `uvm_info(`gfn, $sformatf("key[%0d] = 0x%0h", i, key[i]), UVM_HIGH) - end - endtask - - virtual task wipe_secrets(); - bit [TL_DW-1:0] secret_val; - `DV_CHECK_STD_RANDOMIZE_FATAL(secret_val) - csr_wr(.ptr(ral.wipe_secret), .value(secret_val)); - cfg.wipe_secret_triggered = 1; - `uvm_info(`gfn, $sformatf("wiping secret triggered"), UVM_LOW) - endtask - - // write msg to DUT, read status FIFO FULL and check intr FIFO FULL - // Spawn a Save and Restore thread only when needed (as burst_wr_msg might call this task also - // while the sar_thread is already ongoing) - virtual task wr_msg(bit [7:0] msg[], bit no_sar=0); - int bits_written = 0; - bit [7:0] msg_q[$] = msg; - - // Spawn save and restore task only on some occasions - fork : sar_simple_thread - begin - if (!invalid_cfg && !no_sar && !sar_ongoing && (sar_window.get_num_waiters() == 0) && - (cfg.save_and_restore_pct > $urandom_range(0, 99)) && (msg_q.size() > 0)) begin - save_and_restore(); - end + end +endtask : write_discard_config_and_key + +// Keep all the config values, but enable sha_en +task hmac_base_vseq::sha_enable(); + ral.cfg.sha_en.set(1'b1); + csr_update(.csr(ral.cfg)); +endtask : sha_enable + +// Attempt to change config reg during msg write, design will ignore the change +task hmac_base_vseq::write_discard_config(); + bit [TL_DW-1:0] rand_config_value = $urandom(); + csr_wr(ral.cfg, rand_config_value); +endtask : write_discard_config + +task hmac_base_vseq::write_discard_key(); + bit [TL_DW-1:0] rand_key_value = $urandom(); + int key_idx = $urandom_range(0, 31); + csr_wr(ral.key[key_idx], rand_key_value); +endtask : write_discard_key + +// Start hash computations +task hmac_base_vseq::trigger_hash(); + `uvm_info(`gfn, "triggering hash to start", UVM_LOW) + csr_wr(.ptr(ral.cmd), .value(1'b1 << HashStart)); + // If incorrectly configured or SHA is not enabled, check that an error is signaled. + if (invalid_cfg || !`gmv(ral.cfg.sha_en)) begin + check_error_code(); + end +endtask : trigger_hash + +// Continue hash computations +task hmac_base_vseq::trigger_hash_continue(); + `uvm_info(`gfn, "triggering hash to continue", UVM_LOW) + csr_wr(.ptr(ral.cmd), .value(1'b1 << HashContinue)); + // If SHA is not enabled, check that an error is signaled. + if (!`gmv(ral.cfg.sha_en)) begin + check_error_code(); + end + // Should be triggered while B context to freeze wr_msg/burst_wr_msg as continue will + // be triggered to check a fictive B context with a large msg_length + if (!cfg.sar_skip_ctxt) begin + hash_continue.trigger(); + end +endtask : trigger_hash_continue + +// Stop hash computations +task hmac_base_vseq::trigger_hash_stop(); + `uvm_info(`gfn, "triggering hash to stop", UVM_LOW) + csr_wr(.ptr(ral.cmd), .value(1'b1 << HashStop)); +endtask : trigger_hash_stop + +// Trigger calculation of digest at the end of a message only when Save and Restore hasn't been +// triggered before +task hmac_base_vseq::trigger_process(); + if (!save_ctx_ongoing) begin + `uvm_info(`gfn, "triggering hash to process", UVM_LOW) + csr_wr(.ptr(ral.cmd), .value(1'b1 << HashProcess)); + cfg.hash_process_triggered = 1; + end +endtask : trigger_process + +task hmac_base_vseq::trigger_hash_when_active(); + `uvm_info(`gfn, "triggering hash when active", UVM_LOW) + repeat ($urandom_range(1, 10)) trigger_hash(); + check_error_code(0); +endtask : trigger_hash_when_active + +// Read digest value +task hmac_base_vseq::rd_digest(); + bit [TL_DW-1:0] digest[16]; + csr_rd_digest(digest); +endtask : rd_digest + +function void hmac_base_vseq::clear_wipe_secret(); + `uvm_info(`gfn, "wiping secret untriggered", UVM_LOW) + cfg.wipe_secret_triggered = 0; +endfunction : clear_wipe_secret + +// Read digest value and output read value +task hmac_base_vseq::csr_rd_digest(output bit [TL_DW-1:0] digest[16]); + foreach (digest[i]) begin + csr_rd(.ptr(ral.digest[i]), .value(digest[i])); + `uvm_info(`gfn, $sformatf("digest[%0d]=32'h%08x", i, digest[i]), UVM_MEDIUM) + end +endtask : csr_rd_digest + +// Write digest value +task hmac_base_vseq::csr_wr_digest(bit [TL_DW-1:0] digest[16]); + foreach (digest[i]) csr_wr(.ptr(ral.digest[i]), .value(digest[i])); +endtask : csr_wr_digest + +// Read digest size and update mirrored value +task hmac_base_vseq::csr_rd_digest_size(output bit [3:0] read_digest_size); + csr_rd(.ptr(ral.cfg.digest_size), .value(read_digest_size)); + `uvm_info(`gfn, $sformatf("reading digest size: %04b", read_digest_size), UVM_MEDIUM) +endtask : csr_rd_digest_size + +// Read key length and update mirrored value +task hmac_base_vseq::csr_rd_key_length(output bit [5:0] read_key_length); + csr_rd(.ptr(ral.cfg.key_length), .value(read_key_length)); + `uvm_info(`gfn, $sformatf("reading key length: %06b", read_key_length), UVM_MEDIUM) +endtask : csr_rd_key_length + +// Can safely assume that the input array will always have 32 elements +// key_length determines how much of the key array is relevant for the HMAC operation +task hmac_base_vseq::wr_key(bit [TL_DW-1:0] key[]); + foreach (key[i]) begin + ral.key[i].set(key[i]); + csr_update(.csr(ral.key[i])); + `uvm_info(`gfn, $sformatf("key[%0d] = 0x%0h", i, key[i]), UVM_HIGH) + end +endtask : wr_key + +task hmac_base_vseq::wipe_secrets(); + bit [TL_DW-1:0] secret_val; + `DV_CHECK_STD_RANDOMIZE_FATAL(secret_val) + csr_wr(.ptr(ral.wipe_secret), .value(secret_val)); + cfg.wipe_secret_triggered = 1; + `uvm_info(`gfn, $sformatf("wiping secret triggered"), UVM_LOW) +endtask : wipe_secrets + +// Write msg to DUT, read status FIFO FULL and check intr FIFO FULL +// Spawn a Save and Restore thread only when needed (as burst_wr_msg might call this task also +// while the sar_thread is already ongoing) +task hmac_base_vseq::wr_msg(bit [7:0] msg[], bit no_sar=0); + int bits_written = 0; + bit [7:0] msg_q[$] = msg; + + // Spawn save and restore task only on some occasions + fork : sar_simple_thread + begin + if (!invalid_cfg && !no_sar && !sar_ongoing && (sar_window.get_num_waiters() == 0) && + (cfg.save_and_restore_pct > $urandom_range(0, 99)) && (msg_q.size() > 0)) begin + save_and_restore(); end - begin - // randomly pick the size of bytes to write - // unless msg size is smaller than randomized size - while (msg_q.size() > 0) begin - bit [7:0] word_unpack[4]; - bit [TL_DW-1:0] word; - `DV_CHECK_FATAL(randomize(wr_addr, wr_mask) with {$countones(wr_mask) <= msg_q.size();}) - - foreach (wr_mask[i]) begin - // wr_mask is a packed array, word_unpacked is unpack, has different index - if (wr_mask[i]) begin - word_unpack[3 - i] = msg_q.pop_front(); - end else begin - word_unpack[3 - i] = $urandom(); - end + end + begin + // randomly pick the size of bytes to write + // unless msg size is smaller than randomized size + while (msg_q.size() > 0) begin + bit [7:0] word_unpack[4]; + bit [TL_DW-1:0] word; + `DV_CHECK_FATAL(randomize(wr_addr, wr_mask) with {$countones(wr_mask) <= msg_q.size();}) + + foreach (wr_mask[i]) begin + // wr_mask is a packed array, word_unpacked is unpack, has different index + if (wr_mask[i]) begin + word_unpack[3 - i] = msg_q.pop_front(); + end else begin + word_unpack[3 - i] = $urandom(); end - word = {>>byte{word_unpack}}; - `uvm_info(`gfn, $sformatf("wr_addr = %0h, wr_mask = %04b, words = 0x%0h", - wr_addr, wr_mask, word), UVM_LOW) - tl_access(.addr(cfg.ral.get_addr_from_offset(wr_addr)), - .write(1'b1), .data(word), .mask(wr_mask), .blocking(1)); - bits_written += $countones(wr_mask) * 8; - - `uvm_info(`gfn, $sformatf("bits written: %0d", bits_written), UVM_HIGH) - - // Block size has to be not zero to avoid to divide by zero - if (get_block_size(digest_size) != 0) begin - // Multiple of block size reached => opportunity to trigger a save and restore sequence - // Only when message is not completely yet transmitted, as it doesn't make sense - if ((bits_written % get_block_size(digest_size) == 0) && (msg_q.size() > 0)) begin - // Trigger the event only if someone is waiting for it, this is to prevent infinite - // wait in case of a S&R is triggered 2 times for a same message - if (sar_window.get_num_waiters() > 0) begin - sar_window.trigger(); - hash_continue.wait_trigger(); - end + end + word = {>>byte{word_unpack}}; + `uvm_info(`gfn, $sformatf("wr_addr = %0h, wr_mask = %04b, words = 0x%0h", + wr_addr, wr_mask, word), UVM_LOW) + tl_access(.addr(cfg.ral.get_addr_from_offset(wr_addr)), + .write(1'b1), .data(word), .mask(wr_mask), .blocking(1)); + bits_written += $countones(wr_mask) * 8; + + `uvm_info(`gfn, $sformatf("bits written: %0d", bits_written), UVM_HIGH) + + // Block size has to be not zero to avoid to divide by zero + if (get_block_size(digest_size) != 0) begin + // Multiple of block size reached => opportunity to trigger a save and restore sequence + // Only when message is not completely yet transmitted, as it doesn't make sense + if ((bits_written % get_block_size(digest_size) == 0) && (msg_q.size() > 0)) begin + // Trigger the event only if someone is waiting for it, this is to prevent infinite + // wait in case of a S&R is triggered 2 times for a same message + if (sar_window.get_num_waiters() > 0) begin + sar_window.trigger(); + hash_continue.wait_trigger(); end end + end - if (`gmv(ral.cfg.sha_en)) begin - if (!do_back_pressure) begin - if ($urandom_range(0, 1)) begin - read_status_intr_clr(); - end - end - // randomly change key, config regs during msg wr, should trigger error or be discarded - write_discard_config_and_key(wr_config_during_hash, wr_key_during_hash); - // Randomly trigger error code read also when no error is supposed to happen + if (`gmv(ral.cfg.sha_en)) begin + if (!do_back_pressure) begin if ($urandom_range(0, 1)) begin - check_error_code(0); + read_status_intr_clr(); end - end else begin - check_error_code(1); end + // randomly change key, config regs during msg wr, should trigger error or be discarded + write_discard_config_and_key(wr_config_during_hash, wr_key_during_hash); + // Randomly trigger error code read also when no error is supposed to happen + if ($urandom_range(0, 1)) begin + check_error_code(0); + end + end else begin + check_error_code(1); end - // Keep it alive only if needed - if (!sar_ongoing) begin - disable sar_simple_thread; - sar_window.reset(); - end end - join - sar_window.reset(); - // ensure all msg fifo are written before trigger hmac_process - if ($urandom_range(0, 1)) begin - rd_msg_length(); + // Keep it alive only if needed + if (!sar_ongoing) begin + disable sar_simple_thread; + sar_window.reset(); + end end - read_status_intr_clr(); - endtask : wr_msg - - // read fifo_depth reg and burst write a chunk of words - virtual task burst_wr_msg(bit [7:0] msg[], int burst_wr_length); - bit [7:0] msg_q[$] = msg; - bit [7:0] word_unpack[4]; - bit [TL_DW-1:0] word; - int bits_written = 0; - - // Spawn save and restore task only on some occasions - fork : sar_burst_thread - begin - if (!invalid_cfg && !sar_ongoing && (sar_window.get_num_waiters() == 0) && - (cfg.save_and_restore_pct > $urandom_range(0, 99)) && (msg_q.size() > 0)) begin - save_and_restore(); - end + join + sar_window.reset(); + // ensure all msg fifo are written before trigger hmac_process + if ($urandom_range(0, 1)) begin + rd_msg_length(); + end + read_status_intr_clr(); +endtask : wr_msg + +// Read fifo_depth reg and burst write a chunk of words +task hmac_base_vseq::burst_wr_msg(bit [7:0] msg[], int burst_wr_length); + bit [7:0] msg_q[$] = msg; + bit [7:0] word_unpack[4]; + bit [TL_DW-1:0] word; + int bits_written = 0; + + // Spawn save and restore task only on some occasions + fork : sar_burst_thread + begin + if (!invalid_cfg && !sar_ongoing && (sar_window.get_num_waiters() == 0) && + (cfg.save_and_restore_pct > $urandom_range(0, 99)) && (msg_q.size() > 0)) begin + save_and_restore(); end - begin - while (msg_q.size() > 0) begin - // wait until HMAC has enough space to burst write - csr_spinwait(.ptr(ral.status.fifo_depth), - .exp_data(HMAC_MSG_FIFO_DEPTH_WR - burst_wr_length), - .compare_op(CompareOpLe)); - if (msg_q.size() >= burst_wr_length * 4) begin - repeat (burst_wr_length) begin - for (int i = 0; i < 4; i++) word_unpack[i] = msg_q.pop_front(); - word = {>>byte{word_unpack}}; - `uvm_info(`gfn, $sformatf("wr_addr = %0h, wr_mask = %0h, words = 0x%0h", - wr_addr, wr_mask, word), UVM_HIGH) - `DV_CHECK_FATAL(randomize(wr_addr, wr_mask) with {wr_mask == '1;}) - tl_access(.addr(cfg.ral.get_addr_from_offset(wr_addr)), - .write(1'b1), .data(word), .mask(wr_mask), .blocking(1)); - bits_written += $countones(wr_mask) * 8; - - `uvm_info(`gfn, $sformatf("bits written: %0d", bits_written), UVM_HIGH) - - // Block size has to be not zero to avoid to divide by zero - if (get_block_size(digest_size) != 0) begin - // Multiple of block size reached => opportunity to trigger a S&R sequence - // Only when message is not completely yet transmitted, as it doesn't make sense - if ((bits_written % get_block_size(digest_size) == 0) && (msg_q.size() > 0)) begin - // Trigger the event only if someone is waiting for it, this is to prevent - // infinite wait in case of a S&R is triggered 2 times for a same message - if (sar_window.get_num_waiters() > 0) begin - sar_window.trigger(); - hash_continue.wait_trigger(); - end + end + begin + while (msg_q.size() > 0) begin + // wait until HMAC has enough space to burst write + csr_spinwait(.ptr(ral.status.fifo_depth), + .exp_data(HMAC_MSG_FIFO_DEPTH_WR - burst_wr_length), + .compare_op(CompareOpLe)); + if (msg_q.size() >= burst_wr_length * 4) begin + repeat (burst_wr_length) begin + for (int i = 0; i < 4; i++) word_unpack[i] = msg_q.pop_front(); + word = {>>byte{word_unpack}}; + `uvm_info(`gfn, $sformatf("wr_addr = %0h, wr_mask = %0h, words = 0x%0h", + wr_addr, wr_mask, word), UVM_HIGH) + `DV_CHECK_FATAL(randomize(wr_addr, wr_mask) with {wr_mask == '1;}) + tl_access(.addr(cfg.ral.get_addr_from_offset(wr_addr)), + .write(1'b1), .data(word), .mask(wr_mask), .blocking(1)); + bits_written += $countones(wr_mask) * 8; + + `uvm_info(`gfn, $sformatf("bits written: %0d", bits_written), UVM_HIGH) + + // Block size has to be not zero to avoid to divide by zero + if (get_block_size(digest_size) != 0) begin + // Multiple of block size reached => opportunity to trigger a S&R sequence + // Only when message is not completely yet transmitted, as it doesn't make sense + if ((bits_written % get_block_size(digest_size) == 0) && (msg_q.size() > 0)) begin + // Trigger the event only if someone is waiting for it, this is to prevent + // infinite wait in case of a S&R is triggered 2 times for a same message + if (sar_window.get_num_waiters() > 0) begin + sar_window.trigger(); + hash_continue.wait_trigger(); end end end - // Expected error as we may not push message into the FIFO while SHA is disabled - if (!`gmv(ral.cfg.sha_en)) begin - check_error_code(); - end - // remaining msg is smaller than the burst_wr_length - end else begin - wr_msg(msg_q, 1); // Do not S&R on the last piece as message boundary could be wrong - msg_q.delete(); // Flush the queue to avoid infinite loop end - if ($urandom_range(0, 1)) begin - rd_msg_length(); + // Expected error as we may not push message into the FIFO while SHA is disabled + if (!`gmv(ral.cfg.sha_en)) begin + check_error_code(); end - read_status_intr_clr(); + // remaining msg is smaller than the burst_wr_length + end else begin + wr_msg(msg_q, 1); // Do not S&R on the last piece as message boundary could be wrong + msg_q.delete(); // Flush the queue to avoid infinite loop end - // Keep it alive only if needed - if (!sar_ongoing) begin - disable sar_burst_thread; - sar_window.reset(); + if ($urandom_range(0, 1)) begin + rd_msg_length(); end + read_status_intr_clr(); end - join - sar_window.reset(); - endtask : burst_wr_msg - - // read the message length from the DUT reg (but discard it) - virtual task rd_msg_length(); - bit [2*TL_DW-1:0] unused; - csr_rd_msg_length(unused); - endtask - - // read the message length from the DUT reg - virtual task csr_rd_msg_length(output bit [2*TL_DW-1:0] msg_length); - csr_rd(ral.msg_length_upper, msg_length[2*TL_DW-1:TL_DW]); - csr_rd(ral.msg_length_lower, msg_length[TL_DW-1:0]); - endtask - - // write message length to the DUT reg - virtual task csr_wr_msg_length(bit [2*TL_DW-1:0] msg_length); - csr_wr(.ptr(ral.msg_length_upper), .value(msg_length[2*TL_DW-1:TL_DW])); - csr_wr(.ptr(ral.msg_length_lower), .value(msg_length[TL_DW-1:0])); - endtask - - // read status and interrupt state and clear the interrupt state - virtual task read_status_intr_clr(); - bit [TL_DW-1:0] rdata; - csr_rd(ral.status, rdata); - csr_rd(ral.intr_state, rdata); - csr_wr(.ptr(ral.intr_state), .value(rdata)); - endtask - - // this task is called when sha_en=0 and sequence set hash_start, or streamed in msg - // it will check intr_pin, intr_state, and error_code register - // default check_err is 1, if set to 0, means user is not sure if it is error case or not, - // will leave the checking to scoreboard - virtual task check_error_code(bit check_err = 1); - bit [TL_DW-1:0] error_code; - if (check_err) begin - if (`gmv(ral.intr_enable.hmac_err)) begin - check_interrupts(.interrupts((1 << HmacErr)), .check_set(1'b1)); - end else begin - csr_rd_check(.ptr(ral.intr_state), .compare_value(1 << HmacErr)); - csr_wr(.ptr(ral.intr_state), .value(1 << HmacErr)); + // Keep it alive only if needed + if (!sar_ongoing) begin + disable sar_burst_thread; + sar_window.reset(); end - end else begin - csr_rd(.ptr(ral.intr_state), .value(error_code)); - csr_wr(.ptr(ral.intr_state), .value(error_code)); end - csr_rd(ral.err_code, error_code); - `uvm_info(`gfn, $sformatf("Error code: 0x%0h", error_code), UVM_HIGH) - endtask - - // TODO (#23288): remove this check from the seq - virtual task compare_digest(bit [7:0] exp_digest[], int tag_len_byte, bit [3:0] digest_size_i); - bit [TL_DW-1:0] act_digest[16]; - bit [TL_DW-1:0] packed_exp_digest[16]; - csr_rd_digest(act_digest); - // `exp_digest` is guaranteed to always contain 16 words (64 bytes) of data - // since HMAC digest size is max 512 bits. - packed_exp_digest = {>>byte{exp_digest}}; - if (cfg.clk_rst_vif.rst_n) begin - foreach (act_digest[i]) begin - // for HMAC test vectors: - // -only compare up to expected tag length (parsed in for each test vector) - // -which is always divisble by 4 (word-aligned) --> (tag_len_byte/4) - // for SHA-2 (!hmac_en) test vectors: - // -compare up to the correct digest index depending on the digest size - if ((hmac_en && (i < (tag_len_byte/4))) || - (!hmac_en && - ((i < 8) || - ((i >= 8 && i < 12) && (digest_size_i == SHA2_384 || digest_size_i == SHA2_512)) || - ((i >= 12) && (digest_size_i == SHA2_512))))) begin - - `uvm_info(`gfn, $sformatf("Actual digest[%0d]: 0x%0h", i, act_digest[i]), UVM_HIGH) - `uvm_info(`gfn, $sformatf("Expected digest[%0d]: 0x%0h", i, - packed_exp_digest[i]), UVM_HIGH) - `DV_CHECK_EQ(act_digest[i], packed_exp_digest[i], $sformatf("for index %0d", i)) - end - end + join + sar_window.reset(); +endtask : burst_wr_msg + +task hmac_base_vseq::rd_msg_length(); + bit [2*TL_DW-1:0] unused; + csr_rd_msg_length(unused); +endtask : rd_msg_length + +task hmac_base_vseq::csr_rd_msg_length(output bit [2*TL_DW-1:0] msg_length); + csr_rd(ral.msg_length_upper, msg_length[2*TL_DW-1:TL_DW]); + csr_rd(ral.msg_length_lower, msg_length[TL_DW-1:0]); +endtask : csr_rd_msg_length + +task hmac_base_vseq::csr_wr_msg_length(bit [2*TL_DW-1:0] msg_length); + csr_wr(.ptr(ral.msg_length_upper), .value(msg_length[2*TL_DW-1:TL_DW])); + csr_wr(.ptr(ral.msg_length_lower), .value(msg_length[TL_DW-1:0])); +endtask : csr_wr_msg_length + +task hmac_base_vseq::read_status_intr_clr(); + bit [TL_DW-1:0] rdata; + csr_rd(ral.status, rdata); + csr_rd(ral.intr_state, rdata); + csr_wr(.ptr(ral.intr_state), .value(rdata)); +endtask : read_status_intr_clr +// This task is called when sha_en=0 and sequence set hash_start, or streamed in msg. +// It will check intr_pin, intr_state, and error_code registers. +// Default check_err is 1, if set to 0, means user is not sure if it is error case or not, +// will leave the checking to scoreboard +task hmac_base_vseq::check_error_code(bit check_err = 1); + bit [TL_DW-1:0] error_code; + if (check_err) begin + if (`gmv(ral.intr_enable.hmac_err)) begin + check_interrupts(.interrupts((1 << HmacErr)), .check_set(1'b1)); end else begin - `uvm_info(`gfn, "skipped comparison due to reset", UVM_LOW) + csr_rd_check(.ptr(ral.intr_state), .compare_value(1 << HmacErr)); + csr_wr(.ptr(ral.intr_state), .value(1 << HmacErr)); end - endtask - - // Stop hash, save current context, do something/nothing and restore context - // - Test with context A saved and restored - // - Test with context A and B, alternatively saved and restored. Ensure to randomize again: - // key length, digest size, digest swap, endian swap and secret key - virtual task save_and_restore(); - // Wait until message transmission is on a block boundary (multiple of 512 bits in SHA-2 256 - // or 1024 bits SHA-2 384/512) - sar_window.wait_trigger(); - randcase - 1: sar_stop_and_continue(); - 1: sar_same_context(); - 1: sar_different_context(); - endcase - endtask : save_and_restore - - virtual task sar_stop_and_continue(); - uvm_event sar_stop_continue_ev; - - sar_stop_continue_ev = uvm_event_sar_pool::get_global("sar_stop_and_continue_event"); - `uvm_info(`gfn, $sformatf("Stop and trigger continue only"), UVM_LOW) - sar_ongoing = 1; - // Stop hash operations. - trigger_hash_stop(); - // Expose ongoing Save and Restore triggered to avoid to request a new hash process - save_ctx_ongoing = 1; - // Wait for hash to be done so the digest is updated. - csr_spinwait(.ptr(ral.intr_state.hmac_done), .exp_data(1'b1)); - // Clear the interrupt. - csr_wr(.ptr(ral.intr_state.hmac_done), .value(1'b1)); - save_ctx_ongoing = 0; - trigger_hash_continue(); - sar_stop_continue_ev.trigger(); - sar_ongoing = 0; - endtask : sar_stop_and_continue; - - virtual task sar_same_context(); - bit [TL_DW-1:0] digest_a[16]; - bit [2*TL_DW-1:0] msg_length_a; - uvm_event sar_same_ctxt_ev; - - sar_same_ctxt_ev = uvm_event_sar_pool::get_global("sar_same_context_event"); - `uvm_info(`gfn, $sformatf("Saving and restoring with the same context"), UVM_LOW) - sar_ongoing = 1; - // Stop hash operations. - trigger_hash_stop(); - // Expose ongoing Save and Restore triggered to avoid to request a new hash process - save_ctx_ongoing = 1; - // Wait for hash to be done so the digest is updated. - csr_spinwait(.ptr(ral.intr_state.hmac_done), .exp_data(1'b1)); - // Clear the interrupt. - csr_wr(.ptr(ral.intr_state.hmac_done), .value(1'b1)); - // Read the digest and save it. - csr_rd_digest(digest_a); - // Read message length and save it. - csr_rd_msg_length(msg_length_a); - save_ctx_ongoing = 0; - // Disable SHA so we can write digest and message length. - csr_wr(.ptr(ral.cfg.sha_en), .value(1'b0)); - // Clearing the message length is not strictly necessary but currently done to ensure the - // previous value does not persist. - csr_wr_msg_length('0); // - // Reload the digest by writing it back. - csr_wr_digest(digest_a); - // Reload the message length by writing it back. - csr_wr_msg_length(msg_length_a); - // Re-enable SHA and continue hashing. - csr_wr(.ptr(ral.cfg.sha_en), .value(1'b1)); - trigger_hash_continue(); - sar_same_ctxt_ev.trigger(); - sar_ongoing = 0; - endtask : sar_same_context; - - // Different context: - // All those parameters could be changed: key length, digest size, digest swap, endian swap - // and secret key, update all those registers and restart. Operations: - // 1- config registers for A, run A hash, stop A hash - // 2- save A context - // 3- config registers for B, run hash process/continue, wait B hash completion - // 4- run B hash, stop B hash - // 5- restore A context, resume A hash until the end - // Note: here we are taking the advantage of SAR feature to test the msg_length_upper register - virtual task sar_different_context(); - bit [TL_DW-1:0] digest_a[16]; - bit [2*TL_DW-1:0] msg_length_a; - bit [7:0] msg_b[]; - bit [2*TL_DW-1:0] msg_length_rd, msg_length_rand; - uvm_event sar_different_ctxt_ev; - - sar_different_ctxt_ev = uvm_event_sar_pool::get_global("sar_different_context_event"); - // ----- 1- config registers for A, run A hash, stop A hash - `uvm_info(`gfn, $sformatf("Saving and restoring with different contexts"), UVM_LOW) - sar_ongoing = 1; - // Stop hash operations. - trigger_hash_stop(); - // Expose ongoing Save and Restore triggered to avoid to request a new hash process - save_ctx_ongoing = 1; - // Wait for hash to be done so the digest is updated. - csr_spinwait(.ptr(ral.intr_state.hmac_done), .exp_data(1'b1)); - // Clear the interrupt. - csr_wr(.ptr(ral.intr_state.hmac_done), .value(1'b1)); - - // ----- 2- save A context - // Read the digest and save it. - csr_rd_digest(digest_a); - // Read message length and save it. - csr_rd_msg_length(msg_length_a); - save_ctx_ongoing = 0; - - // ----- 3- config registers for B, run hash process/continue, wait B hash completion - // Disable SHA so we can write digest and message length. - csr_wr(.ptr(ral.cfg.sha_en), .value(1'b0)); - // Generate random message for B context - msg_b = new[$urandom_range(0, 400)]; - foreach (msg_b[i]) msg_b[i] = $urandom(); - `uvm_info(`gfn, $sformatf("SAR context B - message size %0d bits", msg_b.size()*8), UVM_LOW) - `uvm_info(`gfn, $sformatf("SAR context B - msg_b=%p", msg_b), UVM_LOW) - // Set this flag to tell the SCB to skip ongoing things - cfg.sar_skip_ctxt = 1; - // Save config A, generate config B and config DUT - save_and_restore_cfg(1, 0); - // In 50% of the case run a new context from the beginning, or restore a hypothetical context - // with a huge message length to test the upper part of the register msg_length_upper - randcase - 1: begin - // Re-enable SHA and continue hashing. - csr_wr(.ptr(ral.cfg.sha_en), .value(1'b1)); - // Start processing message stream - trigger_hash(); - // Write complete message for B context - wr_msg(msg_b, 1); - // Start hash - trigger_process(); - // Wait for hash to be done so the digest is updated. - csr_spinwait(.ptr(ral.intr_state.hmac_done), .exp_data(1'b1)); - end - 1: begin - // Override the msg_length value to be able to verify the upper part of the register. - // We need to cover the 32 MSBs of this register. Be careful, the programmed value - // has to be a multiple of 512/1024 otherwise the DUT won't support it! - randcase - 1: msg_length_rand = 'h0000_0000_FFFF_FC00; // Toggle LSB upper part reg transition - 1: msg_length_rand = 'hFFFF_FFFF_FFFF_A800; // Toggle MSB, without overflowing - endcase - csr_wr_msg_length(msg_length_rand); - // Re-enable SHA and continue hashing. - csr_wr(.ptr(ral.cfg.sha_en), .value(1'b1)); - // Trigger hash to continue - trigger_hash_continue(); - // Write complete message for B context - wr_msg(msg_b, 1); - // Start hash - trigger_process(); - // Wait for hash to be done so the digest is updated. - csr_spinwait(.ptr(ral.intr_state.hmac_done), .exp_data(1'b1)); - // Check message length -> TODO (#23562) move to the SCB when removing sar_skip_ctxt - csr_rd_msg_length(msg_length_rd); - `DV_CHECK_EQ(msg_length_rd, msg_length_rand+msg_b.size()*8) - end - endcase - // Clear the interrupt. - csr_wr(.ptr(ral.intr_state.hmac_done), .value(1'b1)); - // Clear this flag to tell the SCB to proceed with the prediction and checks - cfg.sar_skip_ctxt = 0; - save_ctx_ongoing = 0; - - // ----- 4- restore A context, resume A hash until the end - // Disable SHA so we can write digest and message length. - csr_wr(.ptr(ral.cfg.sha_en), .value(1'b0)); - // Restore config A without saving config B as not required - save_and_restore_cfg(0, 1); - // Reload the digest by writing it back. - csr_wr_digest(digest_a); - // Reload the message length by writing it back. - csr_wr_msg_length(msg_length_a); - // Re-enable SHA and continue hashing. - csr_wr(.ptr(ral.cfg.sha_en), .value(1'b1)); - trigger_hash_continue(); - sar_different_ctxt_ev.trigger(); - sar_ongoing = 0; - endtask : sar_different_context; - - // Save the current config for some registers, and restore the previous saved config or genrate - // a new random one - virtual task save_and_restore_cfg(bit save_current_cfg, - bit restore_previous_cfg); - hmac_base_vseq rand_cfg; // Only here to randomize variables - bit endian_swap_tmp; - bit digest_swap_tmp; - bit key_swap_tmp; - bit [3:0] digest_size_tmp; - bit [5:0] key_length_tmp; - bit [TL_DW-1:0] key_tmp[]; - string secret_key_path = {`DUT_HIER_STR, ".secret_key"}; - logic [NUM_KEYS*TL_DW-1:0] secret_key_probe; - - `DV_CHECK_FATAL(uvm_hdl_read(secret_key_path, secret_key_probe), - $sformatf("Failed to access path %s", secret_key_path)) - - if (save_current_cfg) begin - endian_swap_tmp = `gmv(ral.cfg.endian_swap); - digest_swap_tmp = `gmv(ral.cfg.digest_swap); - key_swap_tmp = `gmv(ral.cfg.key_swap); - digest_size_tmp = `gmv(ral.cfg.digest_size); - key_length_tmp = `gmv(ral.cfg.key_length); - key_tmp = new[get_key_length(key_length_tmp)/TL_DW]; - for (int i=0; i>byte{exp_digest}}; + if (cfg.clk_rst_vif.rst_n) begin + foreach (act_digest[i]) begin + // for HMAC test vectors: + // -only compare up to expected tag length (parsed in for each test vector) + // -which is always divisble by 4 (word-aligned) --> (tag_len_byte/4) + // for SHA-2 (!hmac_en) test vectors: + // -compare up to the correct digest index depending on the digest size + if ((hmac_en && (i < (tag_len_byte/4))) || + (!hmac_en && + ((i < 8) || + ((i >= 8 && i < 12) && (digest_size_i == SHA2_384 || digest_size_i == SHA2_512)) || + ((i >= 12) && (digest_size_i == SHA2_512))))) begin + + `uvm_info(`gfn, $sformatf("Actual digest[%0d]: 0x%0h", i, act_digest[i]), UVM_HIGH) + `uvm_info(`gfn, $sformatf("Expected digest[%0d]: 0x%0h", i, + packed_exp_digest[i]), UVM_HIGH) + `DV_CHECK_EQ(act_digest[i], packed_exp_digest[i], $sformatf("for index %0d", i)) end - // As the probed secret key from the HDL is already swapped, a swap back to the original - // value has to be performed - if (key_swap_tmp) begin - foreach (key_tmp[key_i]) begin - key_tmp[key_i] = {<<8{key_tmp[key_i]}}; + end + end else begin + `uvm_info(`gfn, "skipped comparison due to reset", UVM_LOW) + end +endtask : compare_digest + +// Stop hash, save current context, do something/nothing and restore context +// - Test with context A saved and restored +// - Test with context A and B, alternatively saved and restored. Ensure to randomize again: +// key length, digest size, digest swap, endian swap and secret key +task hmac_base_vseq::save_and_restore(); + // Wait until message transmission is on a block boundary (multiple of 512 bits in SHA-2 256 + // or 1024 bits SHA-2 384/512) + sar_window.wait_trigger(); + randcase + 1: sar_stop_and_continue(); + 1: sar_same_context(); + 1: sar_different_context(); + endcase +endtask : save_and_restore + +task hmac_base_vseq::sar_stop_and_continue(); + uvm_event sar_stop_continue_ev; + + sar_stop_continue_ev = uvm_event_sar_pool::get_global("sar_stop_and_continue_event"); + `uvm_info(`gfn, $sformatf("Stop and trigger continue only"), UVM_LOW) + sar_ongoing = 1; + // Stop hash operations. + trigger_hash_stop(); + // Expose ongoing Save and Restore triggered to avoid to request a new hash process + save_ctx_ongoing = 1; + // Wait for hash to be done so the digest is updated. + csr_spinwait(.ptr(ral.intr_state.hmac_done), .exp_data(1'b1)); + // Clear the interrupt. + csr_wr(.ptr(ral.intr_state.hmac_done), .value(1'b1)); + save_ctx_ongoing = 0; + trigger_hash_continue(); + sar_stop_continue_ev.trigger(); + sar_ongoing = 0; +endtask : sar_stop_and_continue; + +task hmac_base_vseq::sar_same_context(); + bit [TL_DW-1:0] digest_a[16]; + bit [2*TL_DW-1:0] msg_length_a; + uvm_event sar_same_ctxt_ev; + + sar_same_ctxt_ev = uvm_event_sar_pool::get_global("sar_same_context_event"); + `uvm_info(`gfn, $sformatf("Saving and restoring with the same context"), UVM_LOW) + sar_ongoing = 1; + // Stop hash operations. + trigger_hash_stop(); + // Expose ongoing Save and Restore triggered to avoid to request a new hash process + save_ctx_ongoing = 1; + // Wait for hash to be done so the digest is updated. + csr_spinwait(.ptr(ral.intr_state.hmac_done), .exp_data(1'b1)); + // Clear the interrupt. + csr_wr(.ptr(ral.intr_state.hmac_done), .value(1'b1)); + // Read the digest and save it. + csr_rd_digest(digest_a); + // Read message length and save it. + csr_rd_msg_length(msg_length_a); + save_ctx_ongoing = 0; + // Disable SHA so we can write digest and message length. + csr_wr(.ptr(ral.cfg.sha_en), .value(1'b0)); + // Clearing the message length is not strictly necessary but currently done to ensure the + // previous value does not persist. + csr_wr_msg_length('0); // + // Reload the digest by writing it back. + csr_wr_digest(digest_a); + // Reload the message length by writing it back. + csr_wr_msg_length(msg_length_a); + // Re-enable SHA and continue hashing. + csr_wr(.ptr(ral.cfg.sha_en), .value(1'b1)); + trigger_hash_continue(); + sar_same_ctxt_ev.trigger(); + sar_ongoing = 0; +endtask : sar_same_context; + +// Different context: +// All those parameters could be changed: key length, digest size, digest swap, endian swap +// and secret key, update all those registers and restart. Operations: +// 1- config registers for A, run A hash, stop A hash +// 2- save A context +// 3- config registers for B, run hash process/continue, wait B hash completion +// 4- run B hash, stop B hash +// 5- restore A context, resume A hash until the end +// Note: here we are taking the advantage of SAR feature to test the msg_length_upper register +task hmac_base_vseq::sar_different_context(); + bit [TL_DW-1:0] digest_a[16]; + bit [2*TL_DW-1:0] msg_length_a; + bit [7:0] msg_b[]; + bit [2*TL_DW-1:0] msg_length_rd, msg_length_rand; + uvm_event sar_different_ctxt_ev; + + sar_different_ctxt_ev = uvm_event_sar_pool::get_global("sar_different_context_event"); + // ----- 1- config registers for A, run A hash, stop A hash + `uvm_info(`gfn, $sformatf("Saving and restoring with different contexts"), UVM_LOW) + sar_ongoing = 1; + // Stop hash operations. + trigger_hash_stop(); + // Expose ongoing Save and Restore triggered to avoid to request a new hash process + save_ctx_ongoing = 1; + // Wait for hash to be done so the digest is updated. + csr_spinwait(.ptr(ral.intr_state.hmac_done), .exp_data(1'b1)); + // Clear the interrupt. + csr_wr(.ptr(ral.intr_state.hmac_done), .value(1'b1)); + + // ----- 2- save A context + // Read the digest and save it. + csr_rd_digest(digest_a); + // Read message length and save it. + csr_rd_msg_length(msg_length_a); + save_ctx_ongoing = 0; + + // ----- 3- config registers for B, run hash process/continue, wait B hash completion + // Disable SHA so we can write digest and message length. + csr_wr(.ptr(ral.cfg.sha_en), .value(1'b0)); + // Generate random message for B context + msg_b = new[$urandom_range(0, 400)]; + foreach (msg_b[i]) msg_b[i] = $urandom(); + `uvm_info(`gfn, $sformatf("SAR context B - message size %0d bits", msg_b.size()*8), UVM_LOW) + `uvm_info(`gfn, $sformatf("SAR context B - msg_b=%p", msg_b), UVM_LOW) + // Set this flag to tell the SCB to skip ongoing things + cfg.sar_skip_ctxt = 1; + // Save config A, generate config B and config DUT + save_and_restore_cfg(1, 0); + // In 50% of the case run a new context from the beginning, or restore a hypothetical context + // with a huge message length to test the upper part of the register msg_length_upper + randcase + 1: begin + // Re-enable SHA and continue hashing. + csr_wr(.ptr(ral.cfg.sha_en), .value(1'b1)); + // Start processing message stream + trigger_hash(); + // Write complete message for B context + wr_msg(msg_b, 1); + // Start hash + trigger_process(); + // Wait for hash to be done so the digest is updated. + csr_spinwait(.ptr(ral.intr_state.hmac_done), .exp_data(1'b1)); end - end + 1: begin + // Override the msg_length value to be able to verify the upper part of the register. + // We need to cover the 32 MSBs of this register. Be careful, the programmed value + // has to be a multiple of 512/1024 otherwise the DUT won't support it! + randcase + 1: msg_length_rand = 'h0000_0000_FFFF_FC00; // Toggle LSB upper part reg transition + 1: msg_length_rand = 'hFFFF_FFFF_FFFF_A800; // Toggle MSB, without overflowing + endcase + csr_wr_msg_length(msg_length_rand); + // Re-enable SHA and continue hashing. + csr_wr(.ptr(ral.cfg.sha_en), .value(1'b1)); + // Trigger hash to continue + trigger_hash_continue(); + // Write complete message for B context + wr_msg(msg_b, 1); + // Start hash + trigger_process(); + // Wait for hash to be done so the digest is updated. + csr_spinwait(.ptr(ral.intr_state.hmac_done), .exp_data(1'b1)); + // Check message length -> TODO (#23562) move to the SCB when removing sar_skip_ctxt + csr_rd_msg_length(msg_length_rd); + `DV_CHECK_EQ(msg_length_rd, msg_length_rand+msg_b.size()*8) + end + endcase + // Clear the interrupt. + csr_wr(.ptr(ral.intr_state.hmac_done), .value(1'b1)); + // Clear this flag to tell the SCB to proceed with the prediction and checks + cfg.sar_skip_ctxt = 0; + save_ctx_ongoing = 0; + + // ----- 4- restore A context, resume A hash until the end + // Disable SHA so we can write digest and message length. + csr_wr(.ptr(ral.cfg.sha_en), .value(1'b0)); + // Restore config A without saving config B as not required + save_and_restore_cfg(0, 1); + // Reload the digest by writing it back. + csr_wr_digest(digest_a); + // Reload the message length by writing it back. + csr_wr_msg_length(msg_length_a); + // Re-enable SHA and continue hashing. + csr_wr(.ptr(ral.cfg.sha_en), .value(1'b1)); + trigger_hash_continue(); + sar_different_ctxt_ev.trigger(); + sar_ongoing = 0; +endtask : sar_different_context; + +// Save the current config for some registers, and restore the previous saved config or genrate +// a new random one +task hmac_base_vseq::save_and_restore_cfg(bit save_current_cfg, bit restore_previous_cfg); + hmac_base_vseq rand_cfg; // Only here to randomize variables + bit endian_swap_tmp; + bit digest_swap_tmp; + bit key_swap_tmp; + bit [3:0] digest_size_tmp; + bit [5:0] key_length_tmp; + bit [TL_DW-1:0] key_tmp[]; + string secret_key_path = {`DUT_HIER_STR, ".secret_key"}; + logic [NUM_KEYS*TL_DW-1:0] secret_key_probe; + + `DV_CHECK_FATAL(uvm_hdl_read(secret_key_path, secret_key_probe), + $sformatf("Failed to access path %s", secret_key_path)) + + if (save_current_cfg) begin + endian_swap_tmp = `gmv(ral.cfg.endian_swap); + digest_swap_tmp = `gmv(ral.cfg.digest_swap); + key_swap_tmp = `gmv(ral.cfg.key_swap); + digest_size_tmp = `gmv(ral.cfg.digest_size); + key_length_tmp = `gmv(ral.cfg.key_length); + key_tmp = new[get_key_length(key_length_tmp)/TL_DW]; + for (int i=0; i (key_length != Key_None); - (local::hmac_en && digest_size == SHA2_256) -> (key_length != Key_1024); - }) - // Write into registers - ral.cfg.endian_swap.set(rand_cfg.endian_swap); - ral.cfg.digest_swap.set(rand_cfg.digest_swap); - ral.cfg.key_swap.set(rand_cfg.key_swap); - ral.cfg.digest_size.set(rand_cfg.digest_size); - ral.cfg.key_length.set(rand_cfg.key_length); - csr_update(.csr(ral.cfg)); - wr_key(rand_cfg.key); - `uvm_info(`gfn, $sformatf("SAR context B - digest size=%s, key length=%0d", - get_digest_size(rand_cfg.digest_size), - get_key_length(rand_cfg.key_length)), UVM_LOW) - `uvm_info(`gfn, $sformatf("SAR context B - endian/digest/key_swap=%b", - {rand_cfg.endian_swap, rand_cfg.digest_swap, rand_cfg.key_swap}), UVM_LOW) - `uvm_info(`gfn, $sformatf("SAR context B - key=%p", rand_cfg.key), UVM_LOW) + // As the probed secret key from the HDL is already swapped, a swap back to the original + // value has to be performed + if (key_swap_tmp) begin + foreach (key_tmp[key_i]) begin + key_tmp[key_i] = {<<8{key_tmp[key_i]}}; + end end - - // Copy over into the previous config variables - endian_swap_bak = endian_swap_tmp; - digest_swap_bak = digest_swap_tmp; - key_swap_bak = key_swap_tmp; - digest_size_bak = digest_size_tmp; - key_length_bak = key_length_tmp; - key_bak = key_tmp; - endtask : save_and_restore_cfg - - // overriding timeout on outstanding accesses for the hmac_stress_test_all_with_rand_reset test - virtual function int wait_cycles_with_no_outstanding_accesses(); - return 1_000_000; - endfunction -endclass : hmac_base_vseq + end + + // Restore the previous config + if (restore_previous_cfg) begin + ral.cfg.endian_swap.set(endian_swap_bak); + ral.cfg.digest_swap.set(digest_swap_bak); + ral.cfg.key_swap.set(key_swap_bak); + ral.cfg.digest_size.set(digest_size_bak); + ral.cfg.key_length.set(key_length_bak); + csr_update(.csr(ral.cfg)); + wr_key(key_bak); + // Generate new randomized config with the current set of constraints + end else begin + rand_cfg = hmac_base_vseq::type_id::create("rand_cfg"); + rand_cfg.set_sequencer(p_sequencer); // Won't be used but needed to avoid error + // Randomize valid configuration only + `DV_CHECK_FATAL(rand_cfg.randomize() with { + solve digest_size before key_length; + $countones(digest_size) == 1; + $countones(key_length) == 1; + digest_size != SHA2_None; + (local::hmac_en) -> (key_length != Key_None); + (local::hmac_en && digest_size == SHA2_256) -> (key_length != Key_1024); + }) + // Write into registers + ral.cfg.endian_swap.set(rand_cfg.endian_swap); + ral.cfg.digest_swap.set(rand_cfg.digest_swap); + ral.cfg.key_swap.set(rand_cfg.key_swap); + ral.cfg.digest_size.set(rand_cfg.digest_size); + ral.cfg.key_length.set(rand_cfg.key_length); + csr_update(.csr(ral.cfg)); + wr_key(rand_cfg.key); + `uvm_info(`gfn, $sformatf("SAR context B - digest size=%s, key length=%0d", + get_digest_size(rand_cfg.digest_size), + get_key_length(rand_cfg.key_length)), UVM_LOW) + `uvm_info(`gfn, $sformatf("SAR context B - endian/digest/key_swap=%b", + {rand_cfg.endian_swap, rand_cfg.digest_swap, rand_cfg.key_swap}), UVM_LOW) + `uvm_info(`gfn, $sformatf("SAR context B - key=%p", rand_cfg.key), UVM_LOW) + end + + // Copy over into the previous config variables + endian_swap_bak = endian_swap_tmp; + digest_swap_bak = digest_swap_tmp; + key_swap_bak = key_swap_tmp; + digest_size_bak = digest_size_tmp; + key_length_bak = key_length_tmp; + key_bak = key_tmp; +endtask : save_and_restore_cfg + +// Overriding timeout on outstanding accesses for the hmac_stress_test_all_with_rand_reset test +function int hmac_base_vseq::wait_cycles_with_no_outstanding_accesses(); + return 1_000_000; +endfunction : wait_cycles_with_no_outstanding_accesses diff --git a/hw/ip/hmac/dv/env/seq_lib/hmac_burst_wr_vseq.sv b/hw/ip/hmac/dv/env/seq_lib/hmac_burst_wr_vseq.sv index c3cedf2442974f..2a1a2e93daf451 100644 --- a/hw/ip/hmac/dv/env/seq_lib/hmac_burst_wr_vseq.sv +++ b/hw/ip/hmac/dv/env/seq_lib/hmac_burst_wr_vseq.sv @@ -7,11 +7,18 @@ class hmac_burst_wr_vseq extends hmac_long_msg_vseq; `uvm_object_utils(hmac_burst_wr_vseq) - `uvm_object_new - - virtual task pre_start(); - do_burst_wr = 1'b1; - super.pre_start(); - endtask + // Standard SV/UVM methods + extern function new(string name=""); + extern task pre_start(); endclass : hmac_burst_wr_vseq + + +function hmac_burst_wr_vseq::new(string name=""); + super.new(name); +endfunction : new + +task hmac_burst_wr_vseq::pre_start(); + do_burst_wr = 1'b1; + super.pre_start(); +endtask : pre_start diff --git a/hw/ip/hmac/dv/env/seq_lib/hmac_common_vseq.sv b/hw/ip/hmac/dv/env/seq_lib/hmac_common_vseq.sv index 7136afda08238b..570b3b5266367f 100644 --- a/hw/ip/hmac/dv/env/seq_lib/hmac_common_vseq.sv +++ b/hw/ip/hmac/dv/env/seq_lib/hmac_common_vseq.sv @@ -4,45 +4,60 @@ class hmac_common_vseq extends hmac_base_vseq; `uvm_object_utils(hmac_common_vseq) - `uvm_object_new rand bit trig_rst_during_hash; - constraint trig_rst_during_hash_c { - trig_rst_during_hash dist { - 1 :/ 1, - 0 :/ 9 - }; - } - - constraint num_trans_c { - num_trans inside {[1:3]}; - } - - virtual task pre_start(); - do_hmac_init = 1'b0; - super.pre_start(); - endtask - - virtual task body(); - run_common_vseq_wrapper(num_trans); - endtask : body - - virtual task wait_to_issue_reset(uint reset_delay_bound); - `DV_CHECK_MEMBER_RANDOMIZE_FATAL(trig_rst_during_hash) - - if (trig_rst_during_hash) begin - // In hmac_core, the FSM `StPushToMsgFifo` only spans around 20 clock cycles in the entire - // hashing process. So to ensure hmac won't corrupt if reset is issued during - // `StPushToMsgFifo` state, this task try to issue reset (with a large possibility) during - // `StPushToMsgFifo` state. - wait (cfg.hash_process_triggered == 1); - super.wait_to_issue_reset($urandom_range(100, 150)); - #($urandom_range(0, cfg.clk_rst_vif.clk_period_ps) * 1ps); - end else begin - super.wait_to_issue_reset(reset_delay_bound); - end - cfg.hash_process_triggered = 0; - endtask : wait_to_issue_reset - -endclass + // Constraints + extern constraint trig_rst_during_hash_c; + extern constraint num_trans_c; + + // Standard SV/UVM methods + extern function new(string name=""); + extern task pre_start(); + extern task body(); + + // Class specific methods + extern task wait_to_issue_reset(uint reset_delay_bound); +endclass : hmac_common_vseq + + +constraint hmac_common_vseq::trig_rst_during_hash_c { + trig_rst_during_hash dist { + 1 :/ 1, + 0 :/ 9 + }; +} + +constraint hmac_common_vseq::num_trans_c { + num_trans inside {[1:3]}; +} + +function hmac_common_vseq::new(string name=""); + super.new(name); +endfunction : new + +task hmac_common_vseq::pre_start(); + do_hmac_init = 1'b0; + super.pre_start(); +endtask : pre_start + +task hmac_common_vseq::body(); + run_common_vseq_wrapper(num_trans); +endtask : body + +task hmac_common_vseq::wait_to_issue_reset(uint reset_delay_bound); + `DV_CHECK_MEMBER_RANDOMIZE_FATAL(trig_rst_during_hash) + + if (trig_rst_during_hash) begin + // In hmac_core, the FSM `StPushToMsgFifo` only spans around 20 clock cycles in the entire + // hashing process. So to ensure hmac won't corrupt if reset is issued during + // `StPushToMsgFifo` state, this task try to issue reset (with a large possibility) during + // `StPushToMsgFifo` state. + wait (cfg.hash_process_triggered == 1); + super.wait_to_issue_reset($urandom_range(100, 150)); + #($urandom_range(0, cfg.clk_rst_vif.clk_period_ps) * 1ps); + end else begin + super.wait_to_issue_reset(reset_delay_bound); + end + cfg.hash_process_triggered = 0; +endtask : wait_to_issue_reset diff --git a/hw/ip/hmac/dv/env/seq_lib/hmac_datapath_stress_vseq.sv b/hw/ip/hmac/dv/env/seq_lib/hmac_datapath_stress_vseq.sv index 4687bb6d88fd85..4572106ec378a0 100644 --- a/hw/ip/hmac/dv/env/seq_lib/hmac_datapath_stress_vseq.sv +++ b/hw/ip/hmac/dv/env/seq_lib/hmac_datapath_stress_vseq.sv @@ -7,32 +7,44 @@ class hmac_datapath_stress_vseq extends hmac_smoke_vseq; `uvm_object_utils(hmac_datapath_stress_vseq) - `uvm_object_new rand int nb_blk_msg; - constraint nb_blk_msg_c { - nb_blk_msg dist { - 0 :/1, - [1:156] :/1 - }; - } + // Constraints + extern constraint nb_blk_msg_c; + extern constraint msg_c; + extern constraint hmac_en_c; + extern constraint sha_en_c; - constraint msg_c { - solve digest_size before msg; - if (digest_size == SHA2_256) { - msg.size() == 1 + nb_blk_msg * HMAC_BLK_SIZE_SHA2_256; - } else { - msg.size() == 1 + nb_blk_msg * HMAC_BLK_SIZE_SHA2_384_512; - } - } + // Standard SV/UVM methods + extern function new(string name=""); +endclass : hmac_datapath_stress_vseq - constraint hmac_en_c { - hmac_en == 1; - } - constraint sha_en_c { - sha_en == 1; +constraint hmac_datapath_stress_vseq::nb_blk_msg_c { + nb_blk_msg dist { + 0 :/1, + [1:156] :/1 + }; +} + +constraint hmac_datapath_stress_vseq::msg_c { + solve digest_size before msg; + if (digest_size == SHA2_256) { + msg.size() == 1 + nb_blk_msg * HMAC_BLK_SIZE_SHA2_256; + } else { + msg.size() == 1 + nb_blk_msg * HMAC_BLK_SIZE_SHA2_384_512; } +} -endclass : hmac_datapath_stress_vseq +constraint hmac_datapath_stress_vseq::hmac_en_c { + hmac_en == 1; +} + +constraint hmac_datapath_stress_vseq::sha_en_c { + sha_en == 1; +} + +function hmac_datapath_stress_vseq::new(string name=""); + super.new(name); +endfunction : new diff --git a/hw/ip/hmac/dv/env/seq_lib/hmac_error_vseq.sv b/hw/ip/hmac/dv/env/seq_lib/hmac_error_vseq.sv index bda7da576f0744..cf30f913c9bfb2 100644 --- a/hw/ip/hmac/dv/env/seq_lib/hmac_error_vseq.sv +++ b/hw/ip/hmac/dv/env/seq_lib/hmac_error_vseq.sv @@ -6,15 +6,24 @@ class hmac_error_vseq extends hmac_long_msg_vseq; `uvm_object_utils(hmac_error_vseq) - `uvm_object_new - function void pre_randomize(); - this.legal_seq_c.constraint_mode(0); - endfunction : pre_randomize - - virtual task pre_body(); - // No need to trigger Save and Restore for this test - cfg.save_and_restore_pct = 0; - super.pre_body(); - endtask : pre_body + // Standard SV/UVM methods + extern function new(string name=""); + extern function void pre_randomize(); + extern task pre_body(); endclass : hmac_error_vseq + + +function hmac_error_vseq::new(string name=""); + super.new(name); +endfunction : new + +function void hmac_error_vseq::pre_randomize(); + this.legal_seq_c.constraint_mode(0); +endfunction : pre_randomize + +task hmac_error_vseq::pre_body(); + // No need to trigger Save and Restore for this test + cfg.save_and_restore_pct = 0; + super.pre_body(); +endtask : pre_body diff --git a/hw/ip/hmac/dv/env/seq_lib/hmac_long_msg_vseq.sv b/hw/ip/hmac/dv/env/seq_lib/hmac_long_msg_vseq.sv index 3f679389da4408..61d193694d0149 100644 --- a/hw/ip/hmac/dv/env/seq_lib/hmac_long_msg_vseq.sv +++ b/hw/ip/hmac/dv/env/seq_lib/hmac_long_msg_vseq.sv @@ -7,14 +7,24 @@ class hmac_long_msg_vseq extends hmac_smoke_vseq; `uvm_object_utils(hmac_long_msg_vseq) - `uvm_object_new - constraint msg_c { - msg.size() dist { - 0 :/ 1, // Empty - [ 1 : 257] :/ 1, // Up to two 1024-bit blocks - [1000 : 3_000] :/ 5, // 1KB - 2KB according to SW immediate usage - [3001 :10_000] :/ 1 // temp set to 10KB as max length, spec max size is 2^64 bits - }; - } + // Constraints + extern constraint msg_c; + + // Standard SV/UVM methods + extern function new(string name=""); endclass : hmac_long_msg_vseq + + +constraint hmac_long_msg_vseq::msg_c { + msg.size() dist { + 0 :/ 1, // Empty + [ 1 : 257] :/ 1, // Up to two 1024-bit blocks + [1000 : 3_000] :/ 5, // 1KB - 2KB according to SW immediate usage + [3001 :10_000] :/ 1 // temp set to 10KB as max length, spec max size is 2^64 bits + }; +} + +function hmac_long_msg_vseq::new(string name=""); + super.new(name); +endfunction : new diff --git a/hw/ip/hmac/dv/env/seq_lib/hmac_smoke_vseq.sv b/hw/ip/hmac/dv/env/seq_lib/hmac_smoke_vseq.sv index a31fa51101fab6..608566bd0c3ace 100644 --- a/hw/ip/hmac/dv/env/seq_lib/hmac_smoke_vseq.sv +++ b/hw/ip/hmac/dv/env/seq_lib/hmac_smoke_vseq.sv @@ -4,7 +4,6 @@ class hmac_smoke_vseq extends hmac_base_vseq; `uvm_object_utils(hmac_smoke_vseq) - `uvm_object_new rand bit sha_en; rand bit intr_fifo_empty_en; @@ -19,222 +18,239 @@ class hmac_smoke_vseq extends hmac_base_vseq; int key_process_cycles; - constraint num_trans_c { - num_trans inside {[1:50]}; - } - - constraint legal_seq_c { - do_hash_start == 1; - sha_en == 1; - do_hash_start_when_active == 0; - wr_key_during_hash == 0; - wr_config_during_hash == 0; - } - - //msg[] is a streaming msg input so can be of any size and needs its size dist-constrained - constraint msg_c { - msg.size() dist { - 0 :/ 3, // Empty - [ 1: 62] :/ 1, // Less than a SHA-2 256 block (512-bit) - [ 63: 65] :/ 3, // Around one SHA-2 256 block (512-bit) - [ 66:126] :/ 1, // Less than a SHA-2 384/512 block (1024-bit) or two 512-bit blocks - [127:129] :/ 3, // Around one SHA-2 384/512 block (1024-bit) or two 512-bit blocks - [130:254] :/ 1, // Less than two 1024-bit blocks - [255:257] :/ 3 // Around two 1024-bit blocks - }; - } - - constraint burst_wr_c { - burst_wr_length inside {[1 : HMAC_MSG_FIFO_DEPTH_WR]}; - } - - constraint intr_enable_c { - intr_fifo_empty_en dist { - 1'b1 := 8, - 1'b0 := 2 - }; - intr_hmac_done_en dist { - 1'b1 := 8, - 1'b0 := 2 - }; - intr_hmac_err_en dist { - 1'b1 := 8, - 1'b0 := 2 - }; - } - - constraint wipe_secret_c { - do_wipe_secret == NoWipeSecret; - } - - virtual task pre_start(); - do_hmac_init = 1'b0; - super.pre_start(); - endtask - - task body(); - for (int i = 1; i <= num_trans; i++) begin - bit [7:0] msg_q[$]; - `DV_CHECK_RANDOMIZE_FATAL(this) - `uvm_info(`gfn, $sformatf("starting seq %0d/%0d, message size %0d bits, hmac=%0d, sha=%0d", - i, num_trans, msg.size()*8, hmac_en, sha_en), UVM_LOW) - `uvm_info(`gfn, $sformatf("digest size=%s, key length=%0d", - get_digest_size(digest_size), get_key_length(key_length)), UVM_LOW) - `uvm_info(`gfn, $sformatf("intr_fifo_empty/hmac_done/hmac_err=%b, endian/digest/key_swap=%b", - {intr_fifo_empty_en, intr_hmac_done_en, intr_hmac_err_en}, - {endian_swap, digest_swap, key_swap}), UVM_LOW) - `uvm_info(`gfn, $sformatf("wipe secret condition is: %0s", do_wipe_secret.name()), UVM_LOW) - - // initialize hmac configs - hmac_init(.sha_en(sha_en), .hmac_en(hmac_en), .endian_swap(endian_swap), - .digest_swap(digest_swap), .key_swap(key_swap), .digest_size(digest_size), - .key_length(key_length), .intr_fifo_empty_en(intr_fifo_empty_en), - .intr_hmac_done_en(intr_hmac_done_en), .intr_hmac_err_en(intr_hmac_err_en)); - - // always start off the transaction by first clearing cfg.wipe_secret_triggered flag - // and update the exp digest val in scb with last digest - clear_wipe_secret(); + // Constraints + extern constraint num_trans_c; + extern constraint legal_seq_c; + extern constraint msg_c; + extern constraint burst_wr_c; + extern constraint intr_enable_c; + extern constraint wipe_secret_c; + + // Standard SV/UVM methods + extern function new(string name=""); + extern virtual task pre_start(); + extern task body(); +endclass: hmac_smoke_vseq + + +constraint hmac_smoke_vseq::num_trans_c { + num_trans inside {[1:50]}; +} + +constraint hmac_smoke_vseq::legal_seq_c { + do_hash_start == 1; + sha_en == 1; + do_hash_start_when_active == 0; + wr_key_during_hash == 0; + wr_config_during_hash == 0; +} + +//msg[] is a streaming msg input so can be of any size and needs its size dist-constrained +constraint hmac_smoke_vseq::msg_c { + msg.size() dist { + 0 :/ 3, // Empty + [ 1: 62] :/ 1, // Less than a SHA-2 256 block (512-bit) + [ 63: 65] :/ 3, // Around one SHA-2 256 block (512-bit) + [ 66:126] :/ 1, // Less than a SHA-2 384/512 block (1024-bit) or two 512-bit blocks + [127:129] :/ 3, // Around one SHA-2 384/512 block (1024-bit) or two 512-bit blocks + [130:254] :/ 1, // Less than two 1024-bit blocks + [255:257] :/ 3 // Around two 1024-bit blocks + }; +} + +constraint hmac_smoke_vseq::burst_wr_c { + burst_wr_length inside {[1 : HMAC_MSG_FIFO_DEPTH_WR]}; +} + +constraint hmac_smoke_vseq::intr_enable_c { + intr_fifo_empty_en dist { + 1'b1 := 8, + 1'b0 := 2 + }; + intr_hmac_done_en dist { + 1'b1 := 8, + 1'b0 := 2 + }; + intr_hmac_err_en dist { + 1'b1 := 8, + 1'b0 := 2 + }; +} + +constraint hmac_smoke_vseq::wipe_secret_c { + do_wipe_secret == NoWipeSecret; +} + +function hmac_smoke_vseq::new(string name=""); + super.new(name); +endfunction : new + +task hmac_smoke_vseq::pre_start(); + do_hmac_init = 1'b0; + super.pre_start(); +endtask : pre_start + +task hmac_smoke_vseq::body(); + for (int i = 1; i <= num_trans; i++) begin + bit [7:0] msg_q[$]; + `DV_CHECK_RANDOMIZE_FATAL(this) + `uvm_info(`gfn, $sformatf("starting seq %0d/%0d, message size %0d bits, hmac=%0d, sha=%0d", + i, num_trans, msg.size()*8, hmac_en, sha_en), UVM_LOW) + `uvm_info(`gfn, $sformatf("digest size=%s, key length=%0d", + get_digest_size(digest_size), get_key_length(key_length)), UVM_LOW) + `uvm_info(`gfn, $sformatf("intr_fifo_empty/hmac_done/hmac_err=%b, endian/digest/key_swap=%b", + {intr_fifo_empty_en, intr_hmac_done_en, intr_hmac_err_en}, + {endian_swap, digest_swap, key_swap}), UVM_LOW) + `uvm_info(`gfn, $sformatf("wipe secret condition is: %0s", do_wipe_secret.name()), UVM_LOW) + + // initialize hmac configs + hmac_init(.sha_en(sha_en), .hmac_en(hmac_en), .endian_swap(endian_swap), + .digest_swap(digest_swap), .key_swap(key_swap), .digest_size(digest_size), + .key_length(key_length), .intr_fifo_empty_en(intr_fifo_empty_en), + .intr_hmac_done_en(intr_hmac_done_en), .intr_hmac_err_en(intr_hmac_err_en)); + + // always start off the transaction by first clearing cfg.wipe_secret_triggered flag + // and update the exp digest val in scb with last digest + clear_wipe_secret(); + rd_digest(); + + if (do_wipe_secret == WipeSecretBeforeKey) begin + `uvm_info(`gfn, $sformatf("wiping before key"), UVM_HIGH) + wipe_secrets(); + // Check if digest data are corrupted by wiping secrets. rd_digest(); + end - if (do_wipe_secret == WipeSecretBeforeKey) begin - `uvm_info(`gfn, $sformatf("wiping before key"), UVM_HIGH) - wipe_secrets(); - // Check if digest data are corrupted by wiping secrets. - rd_digest(); - end + // write key + wr_key(key); - // write key - wr_key(key); + // clear the flag after writing key + clear_wipe_secret(); - // clear the flag after writing key - clear_wipe_secret(); + // randomly read previous digest, if the previous digest is not corrupted by wipe_secret + if (i != 1 && $urandom_range(0, 1)) begin + rd_digest(); + end - // randomly read previous digest, if the previous digest is not corrupted by wipe_secret - if (i != 1 && $urandom_range(0, 1)) begin + if (do_wipe_secret == WipeSecretBeforeStart) begin + `uvm_info(`gfn, $sformatf("wiping before start"), UVM_HIGH) + wipe_secrets(); + // Here the wipe secret will only corrupt secret keys and current digests. + // If HMAC is not enabled, once we trigger start, the corruption does not affect the digest + // that will get computed. If HMAC is enabled, the key got corrupted, so the digest + // that will get computed will be corrupted, so only call clear_wipe_secret() when !hmac_en. + if (!hmac_en) begin + // read corrupted current digests then... rd_digest(); + // clear the flag at/right before starting + clear_wipe_secret(); end + end - if (do_wipe_secret == WipeSecretBeforeStart) begin - `uvm_info(`gfn, $sformatf("wiping before start"), UVM_HIGH) - wipe_secrets(); - // Here the wipe secret will only corrupt secret keys and current digests. - // If HMAC is not enabled, once we trigger start, the corruption does not affect the digest - // that will get computed. If HMAC is enabled, the key got corrupted, so the digest - // that will get computed will be corrupted, so only call clear_wipe_secret() when !hmac_en. - if (!hmac_en) begin - // read corrupted current digests then... - rd_digest(); - // clear the flag at/right before starting - clear_wipe_secret(); - end - end - - if (sha_en || $urandom_range(0, 1)) begin - bit [TL_DW-1:0] intr_state_val; - // start stream in msg - fork - begin - if (do_hash_start) begin - trigger_hash(); - end - if (do_burst_wr) begin - burst_wr_msg(msg, burst_wr_length); - end else begin - wr_msg(msg); - end + if (sha_en || $urandom_range(0, 1)) begin + bit [TL_DW-1:0] intr_state_val; + // start stream in msg + fork + begin + if (do_hash_start) begin + trigger_hash(); end - - begin - if (do_wipe_secret == WipeSecretBeforeProcess) begin - `uvm_info(`gfn, $sformatf("wiping before process"), UVM_HIGH) - cfg.clk_rst_vif.wait_clks($urandom_range(0, msg.size() * 10)); - wipe_secrets(); - end + if (do_burst_wr) begin + burst_wr_msg(msg, burst_wr_length); + end else begin + wr_msg(msg); end - join + end - if (invalid_cfg) begin - rd_digest(); // capture the digest CSRs before moving on to next transaction - continue; // discard current transaction - end else if (!sha_en) begin - if (re_enable_sha) begin // restream in the message - sha_enable(); - if (do_hash_start) begin - trigger_hash(); - end - wr_msg(msg); - end else begin // discard current transaction - continue; + begin + if (do_wipe_secret == WipeSecretBeforeProcess) begin + `uvm_info(`gfn, $sformatf("wiping before process"), UVM_HIGH) + cfg.clk_rst_vif.wait_clks($urandom_range(0, msg.size() * 10)); + wipe_secrets(); end end + join - if (do_hash_start_when_active && do_hash_start) begin - trigger_hash_when_active(); - `DV_CHECK_MEMBER_RANDOMIZE_FATAL(msg) + if (invalid_cfg) begin + rd_digest(); // capture the digest CSRs before moving on to next transaction + continue; // discard current transaction + end else if (!sha_en) begin + if (re_enable_sha) begin // restream in the message + sha_enable(); + if (do_hash_start) begin + trigger_hash(); + end wr_msg(msg); + end else begin // discard current transaction + continue; end + end - // msg stream in finished, start hash - if (do_hash_start) begin - `uvm_info(`gfn, $sformatf("triggering process because msg stream is finished"), UVM_LOW) - trigger_process(); - end + if (do_hash_start_when_active && do_hash_start) begin + trigger_hash_when_active(); + `DV_CHECK_MEMBER_RANDOMIZE_FATAL(msg) + wr_msg(msg); + end - // there is one clk cycle difference between scb and design when predict fifo_empty, - // it could happen when input message length is not a multiple of 4, then in design - // the `sha2_pad.st_q` will transit from `StFifoReceive` to `StPad80`. - // If the last two fifo_rds are back-to-back, then design will have one cycle delay before - // the last fifo_rd in order to switch the state. - // If the last two fifo_rds are not back-to-back, then there won't be any delay for the - // last fifo_rd. - // the wait_clk below is implemented to avoid checking intr_state during this period of time - // for such corner cases, because it is hard to align the scb with the fifo_empty interrupt. - // Since prim_packer can hold more data, the ignored period of time is extended by * 2. - // TODO revisit this and understand why this particular delay is selected - if (`gmv(ral.cfg.digest_size) == SHA2_256) begin - key_process_cycles = HMAC_KEY_PROCESS_CYCLES_256; - end else begin - key_process_cycles = HMAC_KEY_PROCESS_CYCLES_512; - end - cfg.clk_rst_vif.wait_clks((msg.size() % 4 || !legal_seq_c.constraint_mode()) ? - key_process_cycles * 2 : - $urandom_range(0, key_process_cycles * 2)); - - if (do_hash_start) begin - fork - begin - if (!invalid_cfg) begin - // wait for interrupt to assert, check status and clear it - if (intr_hmac_done_en) begin - `DV_WAIT(cfg.intr_vif.pins[HmacDone] === 1'b1) - end else begin - csr_spinwait(.ptr(ral.intr_state.hmac_done), .exp_data(1'b1)); - end + // msg stream in finished, start hash + if (do_hash_start) begin + `uvm_info(`gfn, $sformatf("triggering process because msg stream is finished"), UVM_LOW) + trigger_process(); + end + + // there is one clk cycle difference between scb and design when predict fifo_empty, + // it could happen when input message length is not a multiple of 4, then in design + // the `sha2_pad.st_q` will transit from `StFifoReceive` to `StPad80`. + // If the last two fifo_rds are back-to-back, then design will have one cycle delay before + // the last fifo_rd in order to switch the state. + // If the last two fifo_rds are not back-to-back, then there won't be any delay for the + // last fifo_rd. + // the wait_clk below is implemented to avoid checking intr_state during this period of time + // for such corner cases, because it is hard to align the scb with the fifo_empty interrupt. + // Since prim_packer can hold more data, the ignored period of time is extended by * 2. + // TODO revisit this and understand why this particular delay is selected + if (`gmv(ral.cfg.digest_size) == SHA2_256) begin + key_process_cycles = HMAC_KEY_PROCESS_CYCLES_256; + end else begin + key_process_cycles = HMAC_KEY_PROCESS_CYCLES_512; + end + cfg.clk_rst_vif.wait_clks((msg.size() % 4 || !legal_seq_c.constraint_mode()) ? + key_process_cycles * 2 : + $urandom_range(0, key_process_cycles * 2)); + + if (do_hash_start) begin + fork + begin + if (!invalid_cfg) begin + // wait for interrupt to assert, check status and clear it + if (intr_hmac_done_en) begin + `DV_WAIT(cfg.intr_vif.pins[HmacDone] === 1'b1) + end else begin + csr_spinwait(.ptr(ral.intr_state.hmac_done), .exp_data(1'b1)); end end - begin - if (do_wipe_secret == WipeSecretBeforeDone) begin - `uvm_info(`gfn, $sformatf("wiping before done"), UVM_HIGH) - cfg.clk_rst_vif.wait_clks($urandom_range(0, 100)); - wipe_secrets(); - end + end + begin + if (do_wipe_secret == WipeSecretBeforeDone) begin + `uvm_info(`gfn, $sformatf("wiping before done"), UVM_HIGH) + cfg.clk_rst_vif.wait_clks($urandom_range(0, 100)); + wipe_secrets(); end - join - end - csr_rd(.ptr(ral.intr_state), .value(intr_state_val)); - csr_wr(.ptr(ral.intr_state), .value(intr_state_val)); - end - - // if disable sha, digest should be cleared - // read msg fifo length - if ($urandom_range(0, 1)) begin - rd_msg_length(); + end + join end + csr_rd(.ptr(ral.intr_state), .value(intr_state_val)); + csr_wr(.ptr(ral.intr_state), .value(intr_state_val)); + end - // read digest from DUT - `uvm_info(`gfn, $sformatf("reading digest"), UVM_LOW) - rd_digest(); + // if disable sha, digest should be cleared + // read msg fifo length + if ($urandom_range(0, 1)) begin + rd_msg_length(); end - endtask : body -endclass : hmac_smoke_vseq + // read digest from DUT + `uvm_info(`gfn, $sformatf("reading digest"), UVM_LOW) + rd_digest(); + end +endtask : body diff --git a/hw/ip/hmac/dv/env/seq_lib/hmac_stress_all_vseq.sv b/hw/ip/hmac/dv/env/seq_lib/hmac_stress_all_vseq.sv index 69a566e8591654..5fbac6f8878bd9 100644 --- a/hw/ip/hmac/dv/env/seq_lib/hmac_stress_all_vseq.sv +++ b/hw/ip/hmac/dv/env/seq_lib/hmac_stress_all_vseq.sv @@ -7,42 +7,48 @@ class hmac_stress_all_vseq extends hmac_base_vseq; `uvm_object_utils(hmac_stress_all_vseq) - `uvm_object_new - - task body(); - string seq_names[] = {"hmac_smoke_vseq", - "hmac_back_pressure_vseq", - "hmac_burst_wr_vseq", - "hmac_common_vseq", // for intr_test - "hmac_datapath_stress_vseq", - "hmac_long_msg_vseq", - "hmac_error_vseq", - "hmac_wipe_secret_vseq"}; - for (int i = 1; i <= num_trans; i++) begin - uvm_sequence seq; - hmac_base_vseq hmac_vseq; - uint seq_idx = $urandom_range(0, seq_names.size - 1); - - seq = create_seq_by_name(seq_names[seq_idx]); - `downcast(hmac_vseq, seq) - - // dut_init (reset) can be skipped - if (do_apply_reset) begin - hmac_vseq.do_apply_reset = $urandom_range(0, 1); - end else begin - hmac_vseq.do_apply_reset = 0; - end - - hmac_vseq.set_sequencer(p_sequencer); - `DV_CHECK_RANDOMIZE_FATAL(hmac_vseq) - // common sequences only intr_test enabled scb - if (seq_names[seq_idx] == "hmac_common_vseq") begin - hmac_common_vseq common_vseq; - `downcast(common_vseq, hmac_vseq) - common_vseq.common_seq_type = "intr_test"; - end - hmac_vseq.start(p_sequencer); + // Standard SV/UVM methods + extern function new(string name=""); + extern task body(); +endclass : hmac_stress_all_vseq + + +function hmac_stress_all_vseq::new(string name=""); + super.new(name); +endfunction : new + +task hmac_stress_all_vseq::body(); + string seq_names[] = {"hmac_smoke_vseq", + "hmac_back_pressure_vseq", + "hmac_burst_wr_vseq", + "hmac_common_vseq", // for intr_test + "hmac_datapath_stress_vseq", + "hmac_long_msg_vseq", + "hmac_error_vseq", + "hmac_wipe_secret_vseq"}; + for (int i = 1; i <= num_trans; i++) begin + uvm_sequence seq; + hmac_base_vseq hmac_vseq; + uint seq_idx = $urandom_range(0, seq_names.size - 1); + + seq = create_seq_by_name(seq_names[seq_idx]); + `downcast(hmac_vseq, seq) + + // dut_init (reset) can be skipped + if (do_apply_reset) begin + hmac_vseq.do_apply_reset = $urandom_range(0, 1); + end else begin + hmac_vseq.do_apply_reset = 0; end - endtask : body -endclass + hmac_vseq.set_sequencer(p_sequencer); + `DV_CHECK_RANDOMIZE_FATAL(hmac_vseq) + // common sequences only intr_test enabled scb + if (seq_names[seq_idx] == "hmac_common_vseq") begin + hmac_common_vseq common_vseq; + `downcast(common_vseq, hmac_vseq) + common_vseq.common_seq_type = "intr_test"; + end + hmac_vseq.start(p_sequencer); + end +endtask : body diff --git a/hw/ip/hmac/dv/env/seq_lib/hmac_test_vectors_hmac_vseq.sv b/hw/ip/hmac/dv/env/seq_lib/hmac_test_vectors_hmac_vseq.sv index acbfbbab96a221..bfd250bc76b3b2 100644 --- a/hw/ip/hmac/dv/env/seq_lib/hmac_test_vectors_hmac_vseq.sv +++ b/hw/ip/hmac/dv/env/seq_lib/hmac_test_vectors_hmac_vseq.sv @@ -7,18 +7,28 @@ class hmac_test_vectors_hmac_vseq extends hmac_test_vectors_sha_vseq; `uvm_object_utils(hmac_test_vectors_hmac_vseq) - `uvm_object_new - constraint hmac_enabled_c { - hmac_en == 1; - } - - task body(); - // replace with HMAC NIST test vectors - vector_list_256 = test_vectors_pkg::hmac_sha256_file_list; - vector_list_384 = test_vectors_pkg::hmac_sha384_file_list; - vector_list_512 = test_vectors_pkg::hmac_sha512_file_list; - super.body(); - endtask + // Constraints + extern constraint hmac_enabled_c; + // Standard SV/UVM methods + extern function new(string name=""); + extern task body(); endclass : hmac_test_vectors_hmac_vseq + + +constraint hmac_test_vectors_hmac_vseq::hmac_enabled_c { + hmac_en == 1; +} + +function hmac_test_vectors_hmac_vseq::new(string name=""); + super.new(name); +endfunction : new + +task hmac_test_vectors_hmac_vseq::body(); + // replace with HMAC NIST test vectors + vector_list_256 = test_vectors_pkg::hmac_sha256_file_list; + vector_list_384 = test_vectors_pkg::hmac_sha384_file_list; + vector_list_512 = test_vectors_pkg::hmac_sha512_file_list; + super.body(); +endtask : body diff --git a/hw/ip/hmac/dv/env/seq_lib/hmac_test_vectors_sha_vseq.sv b/hw/ip/hmac/dv/env/seq_lib/hmac_test_vectors_sha_vseq.sv index eb6359cc4be244..8392901746d913 100644 --- a/hw/ip/hmac/dv/env/seq_lib/hmac_test_vectors_sha_vseq.sv +++ b/hw/ip/hmac/dv/env/seq_lib/hmac_test_vectors_sha_vseq.sv @@ -7,7 +7,6 @@ class hmac_test_vectors_sha_vseq extends hmac_base_vseq; `uvm_object_utils(hmac_test_vectors_sha_vseq) - `uvm_object_new string vector_list_256[] = test_vectors_pkg::sha2_256_file_list; string vector_list_384[] = test_vectors_pkg::sha2_384_file_list; @@ -15,125 +14,140 @@ class hmac_test_vectors_sha_vseq extends hmac_base_vseq; string digest_size_arg; - constraint hmac_disabled_c { - soft hmac_en == 0; - } - - virtual task pre_start(); - cfg.save_and_restore_pct = 0; // Should not be triggered for this test - do_hmac_init = 1'b0; - // grab SHA-2 digest size from the command-line argument - void'($value$plusargs("sha2_digest_size=%s", digest_size_arg)); - // When the command line argument is not defined then randomize the digest_size with valid data - // This is a safety but also necessary for the stress tests as extra argument cannot be passed - if (digest_size_arg == "") begin - `DV_CHECK_MEMBER_RANDOMIZE_WITH_FATAL( - digest_size, - digest_size inside {SHA2_256, SHA2_384, SHA2_512}; - ) - digest_size_arg = get_digest_size(digest_size); + // Constraints + extern constraint hmac_disabled_c; + + // Standard SV/UVM methods + extern function new(string name=""); + extern task pre_start(); + extern task body(); + + // Class specific methods + extern task feed_vectors (string vector_list[], bit [3:0] digest_size); +endclass : hmac_test_vectors_sha_vseq + + +constraint hmac_test_vectors_sha_vseq::hmac_disabled_c { + soft hmac_en == 0; +} + +function hmac_test_vectors_sha_vseq::new(string name=""); + super.new(name); +endfunction : new + +task hmac_test_vectors_sha_vseq::pre_start(); + cfg.save_and_restore_pct = 0; // Should not be triggered for this test + do_hmac_init = 1'b0; + // grab SHA-2 digest size from the command-line argument + void'($value$plusargs("sha2_digest_size=%s", digest_size_arg)); + // When the command line argument is not defined then randomize the digest_size with valid data + // This is a safety but also necessary for the stress tests as extra argument cannot be passed + if (digest_size_arg == "") begin + `DV_CHECK_MEMBER_RANDOMIZE_WITH_FATAL( + digest_size, + digest_size inside {SHA2_256, SHA2_384, SHA2_512}; + ) + digest_size_arg = get_digest_size(digest_size); + end + super.pre_start(); +endtask : pre_start + +task hmac_test_vectors_sha_vseq::feed_vectors (string vector_list[], bit [3:0] digest_size); + test_vectors_pkg::test_vectors_t parsed_vectors[]; + + foreach (vector_list[i]) begin + // import function from the test_vectors_pkg to parse the sha vector file + test_vectors_pkg::get_hash_test_vectors(.test_name(vector_list[i]), + .parsed_vectors(parsed_vectors), + .reverse_key(0)); + parsed_vectors.shuffle(); + + // if in smoke_regression mode, to reduce the run time, we will randomly pick 2 vectors to + // run this sequence + if (cfg.smoke_test) begin + parsed_vectors = parsed_vectors[0:1]; end - super.pre_start(); - endtask - - task feed_vectors (string vector_list[], bit [3:0] digest_size); - test_vectors_pkg::test_vectors_t parsed_vectors[]; - - foreach (vector_list[i]) begin - // import function from the test_vectors_pkg to parse the sha vector file - test_vectors_pkg::get_hash_test_vectors(.test_name(vector_list[i]), - .parsed_vectors(parsed_vectors), - .reverse_key(0)); - parsed_vectors.shuffle(); - - // if in smoke_regression mode, to reduce the run time, we will randomly pick 2 vectors to - // run this sequence - if (cfg.smoke_test) begin - parsed_vectors = parsed_vectors[0:1]; - end - foreach (parsed_vectors[j]) begin - bit [TL_DW-1:0] intr_state_val; - `uvm_info(`gfn, $sformatf("vector[%0d]: %0p", j, parsed_vectors[j]), UVM_LOW) - - case (parsed_vectors[j].sha2_key_length) - 128: key_length = Key_128; - 256: key_length = Key_256; - 384: key_length = Key_384; - 512: key_length = Key_512; - 1024: key_length = Key_1024; - default: key_length = Key_None; - endcase - - // only input HMAC test vectors with valid key length and while key length is - // not 1024-bit for SHA-2 256 to avoid invalid configuration case - if ((!hmac_en) || (hmac_en && - (key_length!= Key_None) && - !(digest_size == SHA2_256 && key_length == Key_1024))) begin - hmac_init(.hmac_en(hmac_en), .endian_swap(1'b1), .digest_swap(1'b0), .key_swap(1'b0), - .digest_size(digest_size), .key_length(key_length)); - - `uvm_info(`gtn, $sformatf("%s, starting seq %0d/%0d, message size %0d bits", - vector_list[i], j+1, parsed_vectors.size(), - parsed_vectors[j].msg_length_byte*8), - UVM_LOW) - - `uvm_info(`gfn, $sformatf("digest size=%s, key length=%0d", - get_digest_size(digest_size), get_key_length(key_length)), UVM_LOW) - - // always start off the transaction by reading previous digest to clear - // cfg.wipe_secret_triggered flag and update the exp digest val in scb with last digest - rd_digest(); - - if ($urandom_range(0, 1) && !hmac_en) begin - `DV_CHECK_RANDOMIZE_FATAL(this) // only key is randomized - wr_key(key); - end else begin - wr_key(parsed_vectors[j].keys); - end - - trigger_hash(); - - // wr_msg is non_blocking to ensure the order of input msg - wr_msg(parsed_vectors[j].msg); - - trigger_process(); - - wait(cfg.intr_vif.pins[HmacDone] === 1'b1); - csr_rd(.ptr(ral.intr_state), .value(intr_state_val)); - csr_wr(.ptr(ral.intr_state), .value(intr_state_val)); - compare_digest(parsed_vectors[j].exp_digest, - parsed_vectors[j].digest_length_byte, - digest_size); + foreach (parsed_vectors[j]) begin + bit [TL_DW-1:0] intr_state_val; + `uvm_info(`gfn, $sformatf("vector[%0d]: %0p", j, parsed_vectors[j]), UVM_LOW) + + case (parsed_vectors[j].sha2_key_length) + 128: key_length = Key_128; + 256: key_length = Key_256; + 384: key_length = Key_384; + 512: key_length = Key_512; + 1024: key_length = Key_1024; + default: key_length = Key_None; + endcase + + // only input HMAC test vectors with valid key length and while key length is + // not 1024-bit for SHA-2 256 to avoid invalid configuration case + if ((!hmac_en) || (hmac_en && + (key_length!= Key_None) && + !(digest_size == SHA2_256 && key_length == Key_1024))) begin + hmac_init(.hmac_en(hmac_en), .endian_swap(1'b1), .digest_swap(1'b0), .key_swap(1'b0), + .digest_size(digest_size), .key_length(key_length)); + + `uvm_info(`gtn, $sformatf("%s, starting seq %0d/%0d, message size %0d bits", + vector_list[i], j+1, parsed_vectors.size(), + parsed_vectors[j].msg_length_byte*8), + UVM_LOW) + + `uvm_info(`gfn, $sformatf("digest size=%s, key length=%0d", + get_digest_size(digest_size), get_key_length(key_length)), UVM_LOW) + + // always start off the transaction by reading previous digest to clear + // cfg.wipe_secret_triggered flag and update the exp digest val in scb with last digest + rd_digest(); + + if ($urandom_range(0, 1) && !hmac_en) begin + `DV_CHECK_RANDOMIZE_FATAL(this) // only key is randomized + wr_key(key); end else begin - `uvm_info(`gtn, $sformatf("Discarding HMAC seq with invalid key length"), UVM_LOW) - continue; + wr_key(parsed_vectors[j].keys); end + + trigger_hash(); + + // wr_msg is non_blocking to ensure the order of input msg + wr_msg(parsed_vectors[j].msg); + + trigger_process(); + + wait(cfg.intr_vif.pins[HmacDone] === 1'b1); + csr_rd(.ptr(ral.intr_state), .value(intr_state_val)); + csr_wr(.ptr(ral.intr_state), .value(intr_state_val)); + compare_digest(parsed_vectors[j].exp_digest, + parsed_vectors[j].digest_length_byte, + digest_size); + end else begin + `uvm_info(`gtn, $sformatf("Discarding HMAC seq with invalid key length"), UVM_LOW) + continue; end end - endtask - - task body(); - string vector_list[]; - if (digest_size_arg == "SHA2_256") begin - digest_size = SHA2_256; - vector_list = vector_list_256; - end else if (digest_size_arg == "SHA2_384") begin - digest_size = SHA2_384; - vector_list = vector_list_384; - end else if (digest_size_arg == "SHA2_512") begin - digest_size = SHA2_512; - vector_list = vector_list_512; - // TODO (issue #22932): after merged with this PR #23771, a simple "else" could be used as - // hmac_en condition won't be there anymore - end else if (digest_size_arg != "SHA2_256" && digest_size_arg != "SHA2_384" && - digest_size_arg != "SHA2_512") begin - `uvm_fatal(`gfn, {"Digest size is not recognized, please use command-line argument as: ", - "sha2_digest_size=SHA2_256/SHA2_384/SHA2_512 or don't pass this argument"}) - end - `uvm_info(`gfn, $sformatf("Starting SHA-2/HMAC %s NIST test vectors...", - digest_size_arg), UVM_LOW) - feed_vectors (vector_list, digest_size); - endtask : body - -endclass : hmac_test_vectors_sha_vseq + end +endtask : feed_vectors + +task hmac_test_vectors_sha_vseq::body(); + string vector_list[]; + if (digest_size_arg == "SHA2_256") begin + digest_size = SHA2_256; + vector_list = vector_list_256; + end else if (digest_size_arg == "SHA2_384") begin + digest_size = SHA2_384; + vector_list = vector_list_384; + end else if (digest_size_arg == "SHA2_512") begin + digest_size = SHA2_512; + vector_list = vector_list_512; + // TODO (issue #22932): after merged with this PR #23771, a simple "else" could be used as + // hmac_en condition won't be there anymore + end else if (digest_size_arg != "SHA2_256" && digest_size_arg != "SHA2_384" && + digest_size_arg != "SHA2_512") begin + `uvm_fatal(`gfn, {"Digest size is not recognized, please use command-line argument as: ", + "sha2_digest_size=SHA2_256/SHA2_384/SHA2_512 or don't pass this argument"}) + end + `uvm_info(`gfn, $sformatf("Starting SHA-2/HMAC %s NIST test vectors...", + digest_size_arg), UVM_LOW) + feed_vectors (vector_list, digest_size); +endtask : body diff --git a/hw/ip/hmac/dv/env/seq_lib/hmac_wipe_secret_vseq.sv b/hw/ip/hmac/dv/env/seq_lib/hmac_wipe_secret_vseq.sv index 98470f88921e01..ce3433a11d24cf 100644 --- a/hw/ip/hmac/dv/env/seq_lib/hmac_wipe_secret_vseq.sv +++ b/hw/ip/hmac/dv/env/seq_lib/hmac_wipe_secret_vseq.sv @@ -5,27 +5,38 @@ // This sequence generates a mix of short and long msgs. // During the transaction, this sequence randomly triggers wipe_secret and uses scb to check if // digest value has been updated. - class hmac_wipe_secret_vseq extends hmac_smoke_vseq; `uvm_object_utils(hmac_wipe_secret_vseq) - `uvm_object_new - - constraint msg_c { - msg.size() dist { - 0 :/ 1, - [1 :128] :/ 1, // 128 bytes is the FIFO depth - [119 :999] :/ 1, - [1000:3000] :/ 7 // 1KB - 2KB according to SW immediate usage - }; - } - - function void pre_randomize(); - this.wipe_secret_c.constraint_mode(0); - endfunction - - virtual task pre_body(); - // No need to trigger Save and Restore for this test - cfg.save_and_restore_pct = 0; - super.pre_body(); - endtask : pre_body + + // Constraints + extern constraint msg_c; + + // Standard SV/UVM methods + extern function new(string name=""); + extern function void pre_randomize(); + extern task pre_body(); endclass : hmac_wipe_secret_vseq + + +constraint hmac_wipe_secret_vseq::msg_c { + msg.size() dist { + 0 :/ 1, + [1 :128] :/ 1, // 128 bytes is the FIFO depth + [119 :999] :/ 1, + [1000:3000] :/ 7 // 1KB - 2KB according to SW immediate usage + }; +} + +function hmac_wipe_secret_vseq::new(string name=""); + super.new(name); +endfunction : new + +function void hmac_wipe_secret_vseq::pre_randomize(); + this.wipe_secret_c.constraint_mode(0); +endfunction : pre_randomize + +task hmac_wipe_secret_vseq::pre_body(); + // No need to trigger Save and Restore for this test + cfg.save_and_restore_pct = 0; + super.pre_body(); +endtask : pre_body diff --git a/hw/ip/hmac/dv/tests/hmac_base_test.sv b/hw/ip/hmac/dv/tests/hmac_base_test.sv index fb3e8d6d416ff5..6c2cc69633fa79 100644 --- a/hw/ip/hmac/dv/tests/hmac_base_test.sv +++ b/hw/ip/hmac/dv/tests/hmac_base_test.sv @@ -5,12 +5,19 @@ class hmac_base_test extends cip_base_test #(.ENV_T(hmac_env), .CFG_T(hmac_env_cfg)); `uvm_component_utils(hmac_base_test) - `uvm_component_new - - virtual function void build_phase(uvm_phase phase); - super.build_phase(phase); - // Trigger this feature randomly and could be disabled for some tests - cfg.save_and_restore_pct = 20; // In percent chance to happen - endfunction + // Standard SV/UVM methods + extern function new(string name="", uvm_component parent=null); + extern function void build_phase(uvm_phase phase); endclass : hmac_base_test + + +function hmac_base_test::new(string name="", uvm_component parent=null); + super.new(name, parent); +endfunction : new + +function void hmac_base_test::build_phase(uvm_phase phase); + super.build_phase(phase); + // Trigger this feature randomly and could be disabled for some tests + cfg.save_and_restore_pct = 20; // In percent chance to happen +endfunction : build_phase