blob: 9c152da022a84d9f4c23dc0ea44a5a6585fc2118 [file] [log] [blame]
// Copyright lowRISC contributors.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0
class cip_base_env_cfg #(type RAL_T = dv_base_reg_block) extends dv_base_env_cfg #(RAL_T);
// control knobs to enable/disable covergroups
// these are default enabled. If en_cov is off, these will be disabled as well.
bit en_tl_err_cov = 1;
bit en_tl_intg_err_cov = 1;
// Downstream agent cfg objects.
// If the block supports only one RAL, just use `m_tl_agent_cfg`.
// If there are multiple RALs, `m_tl_agent_cfg` is the default one for RAL with type `RAL_T`.
// For the other RAL, can get from ral_models[string] and agent cfg from m_tl_agent_cfgs[string].
tl_agent_cfg m_tl_agent_cfg;
rand tl_agent_cfg m_tl_agent_cfgs[string];
// Override this alert name at `initialize` if it's not as below.
string tl_intg_alert_name = "fatal_fault";
string sec_cm_alert_name = "fatal_fault";
// If there is a bit in an "alert cause" register that will be set by a corrupt bus access, this
// should be the name of that field (with syntax "reg.field"). Used by cip_base_scoreboard to
// update the relevant field in the RAL model if it sees an error.
// Format: tl_intg_alert_fields[ral.a_reg.a_field] = value
uvm_reg_data_t tl_intg_alert_fields[dv_base_reg_field];
// Flag to indicate tl mem acess are gated due to local or global escalation.
bit tl_mem_access_gated;
// Flag to indicate if it is an IP or chip level testbench.
bit is_chip;
// Similar to the associative array above, if DUT has shadow registers, these two associative
// arrays contains register fields related to shadow register's update and storage error status.
uvm_reg_data_t shadow_update_err_status_fields[dv_base_reg_field];
uvm_reg_data_t shadow_storage_err_status_fields[dv_base_reg_field];
alert_esc_agent_cfg m_alert_agent_cfgs[string];
push_pull_agent_cfg#(.DeviceDataWidth(EDN_DATA_WIDTH)) m_edn_pull_agent_cfgs[];
// EDN clk freq setting, if EDN is present.
rand uint edn_clk_freq_mhz;
rand clk_freq_diff_e tlul_and_edn_clk_freq_diff;
// Common interfaces - interrupts, alerts, edn clk.
intr_vif intr_vif;
devmode_vif devmode_vif;
rst_shadowed_vif rst_shadowed_vif;
virtual clk_rst_if edn_clk_rst_vif;
// en_devmode default sets to 1 because all IPs' devmode_i is tied off internally to 1
// ICEBOX(#16739): enable random drive devmode once design supports
bit has_devmode = 1;
bit en_devmode = 1;
// If the data intg is passthru for the memory and the data intg value in mem is incorrect, it
// won't trigger d_error in this mem block and the check is done in the processor
// User can set this flag to disable the check for d_user.data_intg
bit disable_d_user_data_intg_check_for_passthru_mem;
// Clone the default_map to jtag_riscv_map via adding `run_opts: ["create_jtag_riscv_map=1"]`.
// After setting this flag, please do not forget to set the correct sequencer to this map,
// and if needed, set the map as default_map.
uvm_reg_map jtag_riscv_map;
uint num_interrupts;
uint num_edn;
// if module has alerts, this list_of_alerts needs to override in cfg before super.initialize()
// function is called
string list_of_alerts[] = {};
constraint edn_clk_freq_mhz_c {
solve tlul_and_edn_clk_freq_diff before edn_clk_freq_mhz;
if (tlul_and_edn_clk_freq_diff == ClkFreqDiffNone) {
edn_clk_freq_mhz == clk_freq_mhz;
} else if (tlul_and_edn_clk_freq_diff == ClkFreqDiffSmall) {
edn_clk_freq_mhz != clk_freq_mhz;
// This could have used a function, but per the LRM that could cause circular
// constraints against the "solve ... before" above. Same thing below for the
// "big" setting.
// cast to `int`, as edn_clk_freq_mhz and clk_freq_mhz are unsigned.
int'(edn_clk_freq_mhz - clk_freq_mhz) inside {[-2 : 2]};
} else if (tlul_and_edn_clk_freq_diff == ClkFreqDiffBig) {
// max diff is 100-24=76
!(int'(edn_clk_freq_mhz - clk_freq_mhz) inside {[-70 : 70]});
}
`DV_COMMON_CLK_CONSTRAINT(edn_clk_freq_mhz)
}
`uvm_object_param_utils_begin(cip_base_env_cfg #(RAL_T))
`uvm_field_aa_object_string(m_tl_agent_cfgs, UVM_DEFAULT)
`uvm_field_aa_object_string(m_alert_agent_cfgs, UVM_DEFAULT)
`uvm_field_int (num_interrupts, UVM_DEFAULT)
`uvm_object_utils_end
`uvm_object_new
virtual function void initialize(bit [BUS_AW-1:0] csr_base_addr = '1);
super.initialize(csr_base_addr);
// Create downstream agent cfg objects.
foreach (ral_model_names[i]) begin
string ral_name = ral_model_names[i];
m_tl_agent_cfgs[ral_name] = tl_agent_cfg::type_id::create({"m_tl_agent_cfg_", ral_name});
m_tl_agent_cfgs[ral_name].if_mode = dv_utils_pkg::Host;
// TL host cannot support device same cycle response. Host may drive d_ready=0 when a_valid=1.
m_tl_agent_cfgs[ral_name].host_can_stall_rsp_when_a_valid_high = $urandom_range(0, 1);
// OpenTitan host should hold a_valid high until receiving a_ready, but it's valid behavior
// to drop a_valid without a_ready
// randomize `allow_a_valid_drop_wo_a_ready` to test TL device can support it
m_tl_agent_cfgs[ral_name].allow_a_valid_drop_wo_a_ready = $urandom_range(0, 1);
end
// Assign handle to the default `m_tl_agent_cfg` for default `RAL_T`
if (ral_model_names.size > 0) begin
`DV_CHECK_FATAL(m_tl_agent_cfgs.exists(RAL_T::type_name))
m_tl_agent_cfg = m_tl_agent_cfgs[RAL_T::type_name];
`DV_CHECK_NE_FATAL(m_tl_agent_cfg, null)
end
check_shadow_reg_alerts();
if (list_of_alerts.size() > 0) begin
check_alert_configs();
foreach(list_of_alerts[i]) begin
string alert_name = list_of_alerts[i];
m_alert_agent_cfgs[alert_name] = alert_esc_agent_cfg::type_id::create(
$sformatf("m_alert_agent_cfgs[%s]", alert_name));
`DV_CHECK_RANDOMIZE_FATAL(m_alert_agent_cfgs[alert_name])
m_alert_agent_cfgs[alert_name].if_mode = dv_utils_pkg::Device;
m_alert_agent_cfgs[alert_name].is_async = 1; // default async_on, can override this
m_alert_agent_cfgs[alert_name].en_ping_cov = 0;
m_alert_agent_cfgs[alert_name].en_lpg_cov = 0;
end
end
m_edn_pull_agent_cfgs = new[num_edn];
foreach (m_edn_pull_agent_cfgs[i]) begin
m_edn_pull_agent_cfgs[i] = push_pull_agent_cfg#(.DeviceDataWidth(EDN_DATA_WIDTH))::type_id::
create("m_edn_pull_agent_cfgs");
`DV_CHECK_RANDOMIZE_FATAL(m_edn_pull_agent_cfgs[i])
m_edn_pull_agent_cfgs[i].agent_type = PullAgent;
m_edn_pull_agent_cfgs[i].if_mode = Device;
m_edn_pull_agent_cfgs[i].hold_d_data_until_next_req = 1;
end
if (jtag_riscv_map != null) ral.set_base_addr(ral.default_map.get_base_addr(), jtag_riscv_map);
endfunction
protected virtual function void post_build_ral_settings(dv_base_reg_block ral);
// Usually plusargs are collected during build_phase, but need this variable here during RAL
// initialization.
bit create_jtag_riscv_map;
void'($value$plusargs("create_jtag_riscv_map=%0b", create_jtag_riscv_map));
if (create_jtag_riscv_map) begin
jtag_riscv_map = clone_reg_map("jtag_riscv_map", ral.default_map);
`uvm_info(`gfn, "Cloned default_map to jtag_riscv_map.", UVM_HIGH)
end
endfunction
virtual function void check_alert_configs();
dv_base_reg_block sub_blks[$];
ral.get_dv_base_reg_blocks(sub_blks);
// For top-level, check alert_configs by each sub-block that triggers alerts.
if (sub_blks.size() > 0) begin
foreach(sub_blks[i]) begin
// top-level alert name is consist of ${block_name}_${alert_name}
// the following logic will take ${alert_name} in each block into a alerts_q and compare
// against the block's alert_test field names
string alerts_q[$];
string blk_name = sub_blks[i].get_name();
foreach (list_of_alerts[j]) begin
string alert_name = list_of_alerts[j];
if (alert_name.substr(0, blk_name.len() - 1) == blk_name) begin
alerts_q.push_back(alert_name.substr(blk_name.len() + 1, list_of_alerts[j].len() - 1));
end
end
if (alerts_q.size() > 0) check_alert_configs_by_block(sub_blks[i], alerts_q);
end
end else begin
// For IP level testbench, directly use ral as dv_base_reg_block object.
string alerts_q[$] = list_of_alerts;
check_alert_configs_by_block(ral, alerts_q);
end
endfunction
// Checks if the hardcoded cfg.list_of_alerts array matches the information in corresponding
// alert_test register.
virtual function void check_alert_configs_by_block(dv_base_reg_block blk,
const ref string alert_names[$]);
dv_base_reg alert_test_csr;
alert_test_csr = blk.get_dv_base_reg_by_name("alert_test");
`DV_CHECK_NE_FATAL(alert_test_csr, null,
$sformatf("cannot find alert_test csr in %0s", blk.get_name()))
`DV_CHECK_EQ(alert_test_csr.get_n_used_bits(), alert_names.size(),
"alert_test field number and list_of_alerts size mismatch")
foreach(alert_names[i]) begin
uvm_reg_field alert_test_field = blk.get_field_by_name(alert_names[i]);
`DV_CHECK_NE_FATAL(alert_test_field, null, $sformatf("cannot find field %s", alert_names[i]))
`DV_CHECK_EQ(alert_test_field.get_lsb_pos(), i,
$sformatf("alert %0s position does not match", alert_names[i]))
end
endfunction
// This function retrieves all shadowed registers in the design, then check:
// - If the update error and storage error alerts are assigned to each shadowed register
// - If input alert names are within the cfg.list_of_alerts
// - Note that shadow alerts originating inside the alert_handler are not checked here
// since these are wired up as "local" alerts within the alert_handler.
virtual function void check_shadow_reg_alerts();
dv_base_reg shadowed_csrs[$];
string update_err_alert_name, storage_err_alert_name;
foreach (ral_models[i]) ral_models[i].get_shadowed_regs(shadowed_csrs);
foreach (shadowed_csrs[i]) begin
update_err_alert_name = shadowed_csrs[i].get_update_err_alert_name();
storage_err_alert_name = shadowed_csrs[i].get_storage_err_alert_name();
if (update_err_alert_name == "" || storage_err_alert_name == "") begin
`uvm_fatal(shadowed_csrs[i].get_full_name,
"please add update_err and storage_err alert names in Hjson!")
end
// check if alert names are valid
if (!(update_err_alert_name inside {list_of_alerts} ||
update_err_alert_name == "alert_handler_")) begin
`uvm_fatal(shadowed_csrs[i].get_full_name, $sformatf(
"update_err alert name %0s not in list_of_alerts", update_err_alert_name))
end
if (!(storage_err_alert_name inside {list_of_alerts} ||
storage_err_alert_name == "alert_handler_")) begin
`uvm_fatal(shadowed_csrs[i].get_full_name, $sformatf(
"storage_err alert name %0s not in list_of_alerts", storage_err_alert_name))
end
end
endfunction
endclass