[dv/cip_base_scb] Add alert checking functions in cip_base_scb This commit tries to add alert_checking functions in cip_base_scb, so that each IP can just call the set_exp_alert function to trigger alert_checking. Signed-off-by: Cindy Chen <chencindy@google.com>
diff --git a/hw/dv/sv/cip_lib/cip_base_scoreboard.sv b/hw/dv/sv/cip_lib/cip_base_scoreboard.sv index 16cf86f..7aaf33e 100644 --- a/hw/dv/sv/cip_lib/cip_base_scoreboard.sv +++ b/hw/dv/sv/cip_lib/cip_base_scoreboard.sv
@@ -20,6 +20,13 @@ mem_model#() exp_mem; + // alert checking related parameters + bit do_alert_check = 1; + local bit under_alert_handshake[string]; + local bit exp_alert[string]; + local bit is_always_on_alert[string]; + local int alert_max_delay[string]; + `uvm_component_new virtual function void build_phase(uvm_phase phase); @@ -40,6 +47,7 @@ process_tl_a_chan_fifo(); process_tl_d_chan_fifo(); if (cfg.list_of_alerts.size()) process_alert_fifos(); + if (cfg.list_of_alerts.size()) check_alerts(); join_none endtask @@ -100,9 +108,91 @@ end endtask + // this function check if the triggered alert is expected + // to turn off this check, user can set `do_alert_check` to 0 virtual function void process_alert(string alert_name, alert_esc_seq_item item); + if (!(alert_name inside {cfg.list_of_alerts})) begin + `uvm_fatal(`gfn, $sformatf("alert_name %0s is not in cfg.list_of_alerts!", alert_name)) + end + `uvm_info(`gfn, $sformatf("alert %0s detected, alert_status is %s", alert_name, item.alert_handshake_sta), UVM_DEBUG) + if (item.alert_handshake_sta == AlertReceived) begin + under_alert_handshake[alert_name] = 1; + if (do_alert_check) begin + `DV_CHECK_EQ(exp_alert[alert_name], 1, + $sformatf("alert %0s triggered unexpectedly", alert_name)) + end + end else begin + if (!cfg.under_reset && under_alert_handshake[alert_name] == 0) begin + `uvm_error(`gfn, $sformatf("alert %0s is not received!", alert_name)) + end + under_alert_handshake[alert_name] = 0; + end + endfunction + + // this task is implemented to check if expected alert is triggered within certain clock cycles + // if alert is always_on, it will expect alert handshakes until reset + // if alert is not always_on, it will set exp_alert back to 0 once finish alert_checking + virtual task check_alerts(); + foreach (cfg.list_of_alerts[i]) begin + automatic string alert_name = cfg.list_of_alerts[i]; + fork + forever begin + wait(exp_alert[alert_name] == 1 && cfg.under_reset == 0); + if (is_always_on_alert[alert_name]) begin + while (cfg.under_reset == 0) begin + check_alert_triggered(alert_name); + wait(under_alert_handshake[alert_name] == 0 || cfg.under_reset == 1); + end + end else begin + check_alert_triggered(alert_name); + exp_alert[alert_name] = 0; + end + end + join_none + end + endtask + + virtual task check_alert_triggered(string alert_name); + repeat(alert_max_delay[alert_name]) begin + cfg.clk_rst_vif.wait_clks(1); + if (under_alert_handshake[alert_name] || cfg.under_reset) break; + end + if (!under_alert_handshake[alert_name] && !cfg.under_reset) begin + `uvm_error(`gfn, $sformatf("alert %0s did not trigger within %0d cycles", + alert_name, alert_max_delay[alert_name])) + end + endtask + + // this function is used for individual IPs to set when they expect certain alert to trigger + // - input alert_name is the full name of the alert listed in LIST_OF_ALERTS + // - input delay_window sets the max clock cycle delay until the actual alert triggers. This + // number will be added an additional default delay in this function. In async mode, we will + // add additional 6 clock cycles(the min window between two handshakes); in sync mode, 2 clock + // cycles are added + // - input always_on indicates once the alert is triggered, it always sends alert request to + // alert_sender until reset is asserted + // - output is one bit. It returns true if set_exp_alert is successful, it returns false if user + // is trying to set alerts during alert_handshake or exp_alert is already set + virtual function bit set_exp_alert(string alert_name, int max_delay = 0, bit always_on = 0); + if (!(alert_name inside {cfg.list_of_alerts})) begin + `uvm_fatal(`gfn, $sformatf("alert_name %0s is not in cfg.list_of_alerts!", alert_name)) + end + + if (under_alert_handshake[alert_name] || exp_alert[alert_name]) begin + `uvm_info(`gfn, $sformatf("Current %0s alert status under_alert_handshake=%0b,\ + exp_alert=%0b, request ignored", under_alert_handshake[alert_name], + exp_alert[alert_name], alert_name), UVM_MEDIUM) + return 0; + end + + is_always_on_alert[alert_name] = always_on; + // in async mode, 6 clock cycles are the min window between two alert handshakes + // in sync mode, 2 clock cycles are the min window + alert_max_delay[alert_name] = max_delay + cfg.m_alert_agent_cfg[alert_name].is_async ? 6 : 2; + exp_alert[alert_name] = 1; + return 1; endfunction // task to process tl access @@ -225,6 +315,13 @@ virtual function void reset(string kind = "HARD"); tl_a_chan_fifo.flush(); tl_d_chan_fifo.flush(); + foreach(cfg.list_of_alerts[i]) begin + alert_fifos[cfg.list_of_alerts[i]].flush(); + exp_alert[cfg.list_of_alerts[i]] = 0; + under_alert_handshake[cfg.list_of_alerts[i]] = 0; + is_always_on_alert[cfg.list_of_alerts[i]] = 0; + alert_max_delay[cfg.list_of_alerts[i]] = 0; + end endfunction virtual function void check_phase(uvm_phase phase);