From 2a4aa3aa28e99f50374b3dc7a6f764f99db01642 Mon Sep 17 00:00:00 2001 From: Henrik Fegran Date: Mon, 30 Oct 2023 09:34:11 +0100 Subject: [PATCH] Fixed interrupt coverage model connections to rvfi, added clic model, naming cleanup Signed-off-by: Henrik Fegran --- cv32e40s/env/uvme/cov/uvme_clic_covg.sv | 219 ++++++++++++++++++ .../env/uvme/cov/uvme_cv32e40s_cov_model.sv | 4 + cv32e40s/env/uvme/cov/uvme_interrupt_covg.sv | 18 +- cv32e40s/env/uvme/uvme_cv32e40s_cfg.sv | 2 + cv32e40s/env/uvme/uvme_cv32e40s_env.sv | 4 +- cv32e40s/env/uvme/uvme_cv32e40s_pkg.sv | 1 + 6 files changed, 240 insertions(+), 8 deletions(-) create mode 100644 cv32e40s/env/uvme/cov/uvme_clic_covg.sv diff --git a/cv32e40s/env/uvme/cov/uvme_clic_covg.sv b/cv32e40s/env/uvme/cov/uvme_clic_covg.sv new file mode 100644 index 0000000000..2255930f1e --- /dev/null +++ b/cv32e40s/env/uvme/cov/uvme_clic_covg.sv @@ -0,0 +1,219 @@ +// Copyright 2020 OpenHW Group +// Copyright 2020 Datum Technology Corporation +// Copyright 2023 Silicon Labs, Inc. +// +// Licensed under the Solderpad Hardware Licence, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://solderpad.org/licenses/ +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +covergroup cg_clic_irq_entry(string name, + bit ext_m_supported, + bit ext_c_supported, + bit ext_zba_supported, + bit ext_zbb_supported, + bit ext_zbc_supported, + bit ext_zbs_supported, + bit ext_a_supported) +with function sample(uvma_isacov_instr_c instr); + option.name = name; + `per_instance_fcov + cp_irq : coverpoint(instr.name) { + `ISACOV_IGN_BINS + // These instructions will enter the exception handler which will gate off any interrupts + // by disabling MSIE immediately upon execution + ignore_bins ebreak_excp = { EBREAK }; + ignore_bins c_ebreak_excp = { C_EBREAK }; + ignore_bins ecal_excp = { ECALL }; + } +endgroup : cg_clic_irq_entry + +covergroup cg_clic_wfi_entry(string name, + bit ext_m_supported, + bit ext_c_supported, + bit ext_zba_supported, + bit ext_zbb_supported, + bit ext_zbc_supported, + bit ext_zbs_supported, + bit ext_a_supported) +with function sample(uvma_isacov_instr_c instr); + option.name = name; + `per_instance_fcov + cp_wfi : coverpoint instr.name { + `ISACOV_IGN_BINS + } +endgroup : cg_clic_wfi_entry + +covergroup cg_clic_irq_exit(string name, + bit ext_m_supported, + bit ext_c_supported, + bit ext_zba_supported, + bit ext_zbb_supported, + bit ext_zbc_supported, + bit ext_zbs_supported, + bit ext_a_supported) +with function sample(uvma_isacov_instr_c instr); + option.name = name; + `per_instance_fcov + cp_irq : coverpoint instr.name { + `ISACOV_IGN_BINS + // Should not exit an IRQ into an MRET (usually interrupts are disabled at end of ISR) + ignore_bins mret_excp = { MRET }; + // Should not exit an IRQ into a DRET + ignore_bins dret_excp = { DRET }; + } +endgroup : cg_clic_irq_exit + +covergroup cg_clic_wfi_exit(string name, + bit ext_m_supported, + bit ext_c_supported, + bit ext_zba_supported, + bit ext_zbb_supported, + bit ext_zbc_supported, + bit ext_zbs_supported, + bit ext_a_supported) +with function sample(uvma_isacov_instr_c instr); + option.name = name; + `per_instance_fcov + cp_wfi : coverpoint instr.name { + `ISACOV_IGN_BINS + } +endgroup : cg_clic_wfi_exit + +class uvme_clic_covg extends uvm_component; + + `uvm_analysis_imp_decl(_clic) + `uvm_analysis_imp_decl(_isacov) + + string info_tag = "CLICIRQCOVG"; + uvma_isacov_mon_trn_c last_instr_trn; + uvma_core_cntrl_cfg_c cfg; + + int unsigned irq_nested_count; // Count interrupt entry count for functional coverage + + cg_clic_irq_entry irq_entry_cg; + cg_clic_wfi_entry wfi_entry_cg; + cg_clic_irq_exit irq_exit_cg; + cg_clic_wfi_exit wfi_exit_cg; + + uvm_analysis_imp_clic#(uvma_rvfi_instr_seq_item_c#(ILEN,XLEN), uvme_clic_covg) clic_mon_export; + uvm_analysis_imp_isacov#(uvma_isacov_mon_trn_c, uvme_clic_covg) isacov_mon_export; + + `uvm_component_utils(uvme_clic_covg) + + extern function new(string name = "clic_covg", uvm_component parent = null); + extern function void build_phase(uvm_phase phase); + extern task run_phase(uvm_phase phase); + + extern function void write_clic(uvma_rvfi_instr_seq_item_c#(ILEN,XLEN) trn); + extern function void write_isacov(uvma_isacov_mon_trn_c trn); + +endclass : uvme_clic_covg + +function uvme_clic_covg::new(string name = "clic_covg", uvm_component parent = null); + super.new(name, parent); + + clic_mon_export = new("clic_mon_export", this); + isacov_mon_export = new("isacov_mon_export", this); + +endfunction : new + +function void uvme_clic_covg::build_phase(uvm_phase phase); + super.build_phase(phase); + + void'(uvm_config_db#(uvma_core_cntrl_cfg_c)::get(this, "", "cfg", cfg)); + if (!cfg) begin + `uvm_fatal("CFG", "Configuration handle is null") + end + + irq_entry_cg = new("clic_irq_entry", + .ext_m_supported(cfg.ext_m_supported), + .ext_c_supported(cfg.ext_c_supported), + .ext_zba_supported(cfg.ext_zba_supported), + .ext_zbb_supported(cfg.ext_zbb_supported), + .ext_zbc_supported(cfg.ext_zbc_supported), + .ext_zbs_supported(cfg.ext_zbs_supported), + .ext_a_supported(cfg.ext_a_supported)); + + wfi_entry_cg = new("clic_wfi_entry", + .ext_m_supported(cfg.ext_m_supported), + .ext_c_supported(cfg.ext_c_supported), + .ext_zba_supported(cfg.ext_zba_supported), + .ext_zbb_supported(cfg.ext_zbb_supported), + .ext_zbc_supported(cfg.ext_zbc_supported), + .ext_zbs_supported(cfg.ext_zbs_supported), + .ext_a_supported(cfg.ext_a_supported)); + + irq_exit_cg = new("clic_irq_exit", + .ext_m_supported(cfg.ext_m_supported), + .ext_c_supported(cfg.ext_c_supported), + .ext_zba_supported(cfg.ext_zba_supported), + .ext_zbb_supported(cfg.ext_zbb_supported), + .ext_zbc_supported(cfg.ext_zbc_supported), + .ext_zbs_supported(cfg.ext_zbs_supported), + .ext_a_supported(cfg.ext_a_supported)); + + wfi_exit_cg = new("clic_wfi_exit", + .ext_m_supported(cfg.ext_m_supported), + .ext_c_supported(cfg.ext_c_supported), + .ext_zba_supported(cfg.ext_zba_supported), + .ext_zbb_supported(cfg.ext_zbb_supported), + .ext_zbc_supported(cfg.ext_zbc_supported), + .ext_zbs_supported(cfg.ext_zbs_supported), + .ext_a_supported(cfg.ext_a_supported)); +endfunction : build_phase + +task uvme_clic_covg::run_phase(uvm_phase phase); + + super.run_phase(phase); + + `uvm_info(info_tag, "The clic interrupt coverage model is running", UVM_LOW); + +endtask : run_phase + +function void uvme_clic_covg::write_clic(uvma_rvfi_instr_seq_item_c#(ILEN,XLEN) trn); + + if (cfg.clic_interrupt_enable) begin + if (trn.intr.intr == 1 && trn.intr.interrupt == 1 && last_instr_trn != null) begin + `uvm_info(info_tag, $sformatf("IRQ entered from %s", last_instr_trn.instr.name.name()), UVM_DEBUG) + irq_entry_cg.sample(last_instr_trn.instr); + irq_nested_count++; + end + end + +endfunction : write_clic + +function void uvme_clic_covg::write_isacov(uvma_isacov_mon_trn_c trn); + + if (cfg.clic_interrupt_enable) begin + // If this is a WFI, then sample the last instruction + if (trn.instr.name == WFI && last_instr_trn != null) begin + wfi_entry_cg.sample(last_instr_trn.instr); + end + + // If last instruction was WFI, then sample exit WFI coverage + if (last_instr_trn != null && last_instr_trn.instr.name == WFI) begin + `uvm_info(info_tag, $sformatf("WFI Exit: instruction is %s", trn.instr.name.name()), UVM_DEBUG) + wfi_exit_cg.sample(trn.instr); + end + + // For each mret decrement the interrupt count + if (last_instr_trn != null && last_instr_trn.instr.name == MRET && irq_nested_count) begin + `uvm_info(info_tag, $sformatf("IRQ exited to %s", trn.instr.name.name()), UVM_DEBUG) + irq_exit_cg.sample(trn.instr); + irq_nested_count--; + end + + // When an instruction is sampled, just save the handle here + // It will be sampled when an interrupt or wfi event occurs + last_instr_trn = trn; + end + +endfunction : write_isacov diff --git a/cv32e40s/env/uvme/cov/uvme_cv32e40s_cov_model.sv b/cv32e40s/env/uvme/cov/uvme_cv32e40s_cov_model.sv index 2b07cd8f93..4f1d743f92 100644 --- a/cv32e40s/env/uvme/cov/uvme_cv32e40s_cov_model.sv +++ b/cv32e40s/env/uvme/cov/uvme_cv32e40s_cov_model.sv @@ -29,6 +29,7 @@ class uvme_cv32e40s_cov_model_c extends uvm_component; uvme_cv32e40s_cfg_c cfg; uvme_cv32e40s_cntxt_c cntxt; + uvme_clic_covg clic_covg; uvme_interrupt_covg interrupt_covg; uvme_debug_covg debug_covg; uvme_exceptions_covg exceptions_covg; @@ -82,6 +83,9 @@ function void uvme_cv32e40s_cov_model_c::build_phase(uvm_phase phase); `uvm_fatal("CNTXT", "Context handle is null") end + clic_covg = uvme_clic_covg::type_id::create("clic_covg", this); + uvm_config_db#(uvma_core_cntrl_cfg_c)::set(this, "clic_covg", "cfg", cfg); + interrupt_covg = uvme_interrupt_covg::type_id::create("interrupt_covg", this); uvm_config_db#(uvma_core_cntrl_cfg_c)::set(this, "interrupt_covg", "cfg", cfg); diff --git a/cv32e40s/env/uvme/cov/uvme_interrupt_covg.sv b/cv32e40s/env/uvme/cov/uvme_interrupt_covg.sv index 7ecb188faa..784f907d5e 100644 --- a/cv32e40s/env/uvme/cov/uvme_interrupt_covg.sv +++ b/cv32e40s/env/uvme/cov/uvme_interrupt_covg.sv @@ -19,7 +19,7 @@ /////////////////////////////////////////////////////////////////////////////// `uvm_analysis_imp_decl(_interrupt) -`uvm_analysis_imp_decl(_instr) +`uvm_analysis_imp_decl(_isacov) /* * Covergroups @@ -115,7 +115,7 @@ class uvme_interrupt_covg extends uvm_component; cg_wfi_exit wfi_exit_cg; uvm_analysis_imp_interrupt#(uvma_rvfi_instr_seq_item_c#(ILEN,XLEN), uvme_interrupt_covg) interrupt_mon_export; - uvm_analysis_imp_instr#(uvma_isacov_mon_trn_c, uvme_interrupt_covg) instr_mon_export; + uvm_analysis_imp_isacov#(uvma_isacov_mon_trn_c, uvme_interrupt_covg) isacov_mon_export; `uvm_component_utils(uvme_interrupt_covg); @@ -124,7 +124,7 @@ class uvme_interrupt_covg extends uvm_component; extern task run_phase(uvm_phase phase); extern function void write_interrupt(uvma_rvfi_instr_seq_item_c#(ILEN,XLEN) trn); - extern function void write_instr(uvma_isacov_mon_trn_c trn); + extern function void write_isacov(uvma_isacov_mon_trn_c trn); endclass : uvme_interrupt_covg @@ -136,7 +136,7 @@ function uvme_interrupt_covg::new(string name = "interrupt_covg", uvm_component interrupt_mon_export = new("interrupt_mon_export", this); - instr_mon_export = new("instr_mon_export", this); + isacov_mon_export = new("isacov_mon_export", this); endfunction : new @@ -197,16 +197,19 @@ endtask : run_phase function void uvme_interrupt_covg::write_interrupt(uvma_rvfi_instr_seq_item_c#(ILEN,XLEN) trn); - if (trn.intr == 1 && last_instr_trn != null) begin + if (cfg.basic_interrupt_enable) begin + if (trn.intr.intr == 1 && trn.intr.interrupt == 1 && last_instr_trn != null) begin `uvm_info("INTERRUPTCOVG", $sformatf("IRQ entered from %s", last_instr_trn.instr.name.name()), UVM_DEBUG) irq_entry_cg.sample(last_instr_trn.instr); irq_nested_count++; end + end endfunction : write_interrupt -function void uvme_interrupt_covg::write_instr(uvma_isacov_mon_trn_c trn); +function void uvme_interrupt_covg::write_isacov(uvma_isacov_mon_trn_c trn); + if (cfg.basic_interrupt_enable) begin // If this is a WFI, then sample the last instruction if (trn.instr.name == WFI && last_instr_trn != null) begin wfi_entry_cg.sample(last_instr_trn.instr); @@ -228,5 +231,6 @@ function void uvme_interrupt_covg::write_instr(uvma_isacov_mon_trn_c trn); // When an instruction is sampled, just save the handle here // It will be sampled when an interrupt or wfi event occurs last_instr_trn = trn; + end -endfunction : write_instr +endfunction : write_isacov diff --git a/cv32e40s/env/uvme/uvme_cv32e40s_cfg.sv b/cv32e40s/env/uvme/uvme_cv32e40s_cfg.sv index 47348aa9db..48804c2db2 100644 --- a/cv32e40s/env/uvme/uvme_cv32e40s_cfg.sv +++ b/cv32e40s/env/uvme/uvme_cv32e40s_cfg.sv @@ -297,6 +297,8 @@ class uvme_cv32e40s_cfg_c extends uvma_core_cntrl_cfg_c; isacov_cfg.cov_model_enabled == 1; debug_cfg.cov_model_enabled == 1; pma_cfg.cov_model_enabled == 1; + clic_cfg.cov_model_enabled == clic_interrupt_enable; + interrupt_cfg.cov_model_enabled == basic_interrupt_enable; obi_memory_instr_cfg.cov_model_enabled == 1; obi_memory_data_cfg.cov_model_enabled == 1; } diff --git a/cv32e40s/env/uvme/uvme_cv32e40s_env.sv b/cv32e40s/env/uvme/uvme_cv32e40s_env.sv index c164d9ac74..caf1983d17 100644 --- a/cv32e40s/env/uvme/uvme_cv32e40s_env.sv +++ b/cv32e40s/env/uvme/uvme_cv32e40s_env.sv @@ -530,12 +530,14 @@ function void uvme_cv32e40s_env_c::connect_coverage_model(); isacov_agent.monitor.ap.connect(cov_model.exceptions_covg.isacov_mon_export); isacov_agent.monitor.ap.connect(cov_model.counters_covg.isacov_mon_export); + isacov_agent.monitor.ap.connect(cov_model.interrupt_covg.isacov_mon_export); + isacov_agent.monitor.ap.connect(cov_model.clic_covg.isacov_mon_export); obi_memory_data_agent.mon_ap.connect(pma_agent.monitor.obi_d_export); foreach (rvfi_agent.instr_mon_ap[i]) begin rvfi_agent.instr_mon_ap[i].connect(isacov_agent.monitor.rvfi_instr_imp); rvfi_agent.instr_mon_ap[i].connect(cov_model.interrupt_covg.interrupt_mon_export); - //rvfi_agent.instr_mon_ap[i].connect(cov_model.clic_covg.clic_mon_export); // TODO: silabs-hfegran + rvfi_agent.instr_mon_ap[i].connect(cov_model.clic_covg.clic_mon_export); rvfi_agent.instr_mon_ap[i].connect(pma_agent.monitor.rvfi_instr_export); end diff --git a/cv32e40s/env/uvme/uvme_cv32e40s_pkg.sv b/cv32e40s/env/uvme/uvme_cv32e40s_pkg.sv index bbe32f5ff2..6134e59ee0 100644 --- a/cv32e40s/env/uvme/uvme_cv32e40s_pkg.sv +++ b/cv32e40s/env/uvme/uvme_cv32e40s_pkg.sv @@ -92,6 +92,7 @@ package uvme_cv32e40s_pkg; `include "uvma_cv32e40s_core_cntrl_drv.sv" `include "uvma_cv32e40s_core_cntrl_agent.sv" `include "uvme_interrupt_covg.sv" + `include "uvme_clic_covg.sv" `include "uvme_debug_covg.sv" `include "uvme_exceptions_covg.sv" `include "uvme_counters_covg.sv"