diff --git a/hw/ip/racl_ctrl/dv/env/racl_ctrl_common.core b/hw/ip/racl_ctrl/dv/env/racl_ctrl_common.core new file mode 100644 index 0000000000000..04c5850ea56af --- /dev/null +++ b/hw/ip/racl_ctrl/dv/env/racl_ctrl_common.core @@ -0,0 +1,21 @@ +CAPI=2: +# Copyright lowRISC contributors (OpenTitan project). +# Licensed under the Apache License, Version 2.0, see LICENSE for details. +# SPDX-License-Identifier: Apache-2.0 +name: "lowrisc:dv:racl_ctrl_common:0.1" +description: "Common files for RACL_CTRL DV UVM environment" +filesets: + files_dv: + depend: + - lowrisc:dv:cip_lib + - lowrisc:dv:racl_error_log_agent + files: + - racl_ctrl_policies_if.sv + - racl_ctrl_reg_window.sv: {is_include_file: true} + - racl_ctrl_env_wrapper_cfg.sv: {is_include_file: true} + file_type: systemVerilogSource + +targets: + default: + filesets: + - files_dv diff --git a/hw/ip/racl_ctrl/dv/racl_ctrl_tests.hjson b/hw/ip/racl_ctrl/dv/racl_ctrl_tests.hjson deleted file mode 100644 index 9d97fd91d7391..0000000000000 --- a/hw/ip/racl_ctrl/dv/racl_ctrl_tests.hjson +++ /dev/null @@ -1,51 +0,0 @@ -// Copyright lowRISC contributors (OpenTitan project). -// Licensed under the Apache License, Version 2.0, see LICENSE for details. -// SPDX-License-Identifier: Apache-2.0 -// -// This is the "real sim_cfg" for racl_ctrl, but the block is templated and generated differently -// for different top-levels. Since almost none of the DV environment is templated, this hjson file -// defines the tests that every instantiation will want. It should get included by the relevant -// top's generated racl_ctrl_sim_cfg.hjson. - -{ - // Top level testbench name (sv module). - tb: tb - - // Simulator used to sign off this block - tool: xcelium - - // Import additional common sim cfg files. - import_cfgs: [// Project wide common sim cfg file - "{proj_root}/hw/dv/tools/dvsim/common_sim_cfg.hjson", - // Common CIP test lists - "{proj_root}/hw/dv/tools/dvsim/tests/csr_tests.hjson", - "{proj_root}/hw/dv/tools/dvsim/tests/alert_test.hjson", - "{proj_root}/hw/dv/tools/dvsim/tests/intr_test.hjson", - "{proj_root}/hw/dv/tools/dvsim/tests/tl_access_tests.hjson", - "{proj_root}/hw/dv/tools/dvsim/tests/stress_tests.hjson"] - - // Default iterations for all tests - each test entry can override this. - reseed: 50 - - // Default UVM test and seq class name. - uvm_test: racl_ctrl_base_test - uvm_test_seq: racl_ctrl_base_vseq - - // List of test specifications. - tests: [ - { - name: racl_ctrl_smoke - uvm_test_seq: racl_ctrl_smoke_vseq - } - - // TODO: add more tests here - ] - - // List of regressions. - regressions: [ - { - name: smoke - tests: ["racl_ctrl_smoke"] - } - ] -} diff --git a/hw/ip_templates/racl_ctrl/dv/env/racl_ctrl_env.core.tpl b/hw/ip_templates/racl_ctrl/dv/env/racl_ctrl_env.core.tpl new file mode 100644 index 0000000000000..67850eece5c94 --- /dev/null +++ b/hw/ip_templates/racl_ctrl/dv/env/racl_ctrl_env.core.tpl @@ -0,0 +1,42 @@ +CAPI=2: +# Copyright lowRISC contributors (OpenTitan project). +# Licensed under the Apache License, Version 2.0, see LICENSE for details. +# SPDX-License-Identifier: Apache-2.0 +name: ${instance_vlnv(f"lowrisc:dv:{module_instance_name}_env:0.1")} +description: "RACL_CTRL DV UVM environment" +filesets: + files_dv: + depend: + - lowrisc:dv:ralgen + - lowrisc:dv:cip_lib + - lowrisc:dv:racl_ctrl_common + - ${instance_vlnv(f"lowrisc:ip:{module_instance_name}:0.1")} + files: + - ${module_instance_name}_env_pkg.sv + - ${module_instance_name}_env_cfg.sv: {is_include_file: true} + - ${module_instance_name}_env_cov.sv: {is_include_file: true} + - ${module_instance_name}_virtual_sequencer.sv: {is_include_file: true} + - ${module_instance_name}_error_arb_predictor.sv: {is_include_file: true} + - ${module_instance_name}_scoreboard.sv: {is_include_file: true} + - ${module_instance_name}_env.sv: {is_include_file: true} + - seq_lib/${module_instance_name}_vseq_list.sv: {is_include_file: true} + - seq_lib/${module_instance_name}_base_vseq.sv: {is_include_file: true} + - seq_lib/${module_instance_name}_common_vseq.sv: {is_include_file: true} + - seq_lib/${module_instance_name}_smoke_vseq.sv: {is_include_file: true} + - seq_lib/${module_instance_name}_stress_all_vseq.sv: {is_include_file: true} + file_type: systemVerilogSource + +generate: + ral: + generator: ralgen + parameters: + name: ${module_instance_name} + ip_hjson: ../../data/${module_instance_name}.hjson + position: prepend + +targets: + default: + filesets: + - files_dv + generate: + - ral diff --git a/hw/ip_templates/racl_ctrl/dv/env/racl_ctrl_env.sv.tpl b/hw/ip_templates/racl_ctrl/dv/env/racl_ctrl_env.sv.tpl new file mode 100644 index 0000000000000..1b1746d1841d1 --- /dev/null +++ b/hw/ip_templates/racl_ctrl/dv/env/racl_ctrl_env.sv.tpl @@ -0,0 +1,56 @@ +// Copyright lowRISC contributors (OpenTitan project). +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 + +class ${module_instance_name}_env extends cip_base_env #(.CFG_T (${module_instance_name}_env_cfg), + .COV_T (${module_instance_name}_env_cov), + .VIRTUAL_SEQUENCER_T(${module_instance_name}_virtual_sequencer), + .SCOREBOARD_T (${module_instance_name}_scoreboard)); + `uvm_component_utils(${module_instance_name}_env) + + // Agents to drive the racl_error_i and racl_error_external_i input ports + racl_error_log_agent internal_error_agent; + racl_error_log_agent external_error_agent; + + extern function new (string name="", uvm_component parent=null); + extern function void build_phase(uvm_phase phase); + extern function void connect_phase(uvm_phase phase); +endclass + +function ${module_instance_name}_env::new (string name="", uvm_component parent=null); + super.new(name, parent); +endfunction + +function void ${module_instance_name}_env::build_phase(uvm_phase phase); + racl_ctrl_env_wrapper_cfg wrapper; + + super.build_phase(phase); + + // Consume the configuration for the environment from uvm_config_db + if (!uvm_config_db#(racl_ctrl_env_wrapper_cfg)::get(null, "*.env", "wrapper", wrapper)) begin + `uvm_fatal(`gfn, "Cannot find wrapper object in config db") + end + cfg.internal_error_agent_cfg.num_subscribing_ips = wrapper.num_subscribing_ips; + cfg.external_error_agent_cfg.num_subscribing_ips = wrapper.num_external_subscribing_ips; + + cfg.policies_vif = wrapper.policies_vif; + cfg.internal_error_agent_cfg.vif = wrapper.internal_error_vif; + cfg.external_error_agent_cfg.vif = wrapper.external_error_vif; + + // Connect up configuration for each of the agents in uvm_config_db + uvm_config_db#(racl_error_log_agent_cfg)::set(this, "internal_error_agent*", "cfg", + cfg.internal_error_agent_cfg); + uvm_config_db#(racl_error_log_agent_cfg)::set(this, "external_error_agent*", "cfg", + cfg.external_error_agent_cfg); + + // Create tha agents + internal_error_agent = racl_error_log_agent::type_id::create("internal_error_agent", this); + external_error_agent = racl_error_log_agent::type_id::create("external_error_agent", this); +endfunction + +function void ${module_instance_name}_env::connect_phase(uvm_phase phase); + super.connect_phase(phase); + + internal_error_agent.errors_seen.connect(scoreboard.internal_errors_export); + external_error_agent.errors_seen.connect(scoreboard.external_errors_export); +endfunction diff --git a/hw/ip_templates/racl_ctrl/dv/env/racl_ctrl_env_cfg.sv.tpl b/hw/ip_templates/racl_ctrl/dv/env/racl_ctrl_env_cfg.sv.tpl new file mode 100644 index 0000000000000..01fd3644d8a65 --- /dev/null +++ b/hw/ip_templates/racl_ctrl/dv/env/racl_ctrl_env_cfg.sv.tpl @@ -0,0 +1,46 @@ +// Copyright lowRISC contributors (OpenTitan project). +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 + +// An environment config for interfacing with ${module_instance_name}. + +class ${module_instance_name}_env_cfg extends cip_base_env_cfg #(.RAL_T(${module_instance_name}_reg_block)); + `uvm_object_utils(${module_instance_name}_env_cfg) + + racl_ctrl_reg_window regs; + + // An interface that will be bound to the racl_policies_o output of the dut. + virtual racl_ctrl_policies_if policies_vif; + + // Configuration for the two error log agents + rand racl_error_log_agent_cfg internal_error_agent_cfg; + rand racl_error_log_agent_cfg external_error_agent_cfg; + + extern function new (string name=""); + extern virtual function void initialize(bit [31:0] csr_base_addr = '1); +endclass + +function ${module_instance_name}_env_cfg::new (string name=""); + super.new(name); + + if (!$cast(regs, racl_ctrl_reg_window::type_id::create("regs"))) + `uvm_fatal(`gfn, "Could not create reg window of correct type") +endfunction + +function void ${module_instance_name}_env_cfg::initialize(bit [31:0] csr_base_addr = '1); + list_of_alerts = ${module_instance_name}_env_pkg::LIST_OF_ALERTS; + + // Tell the CIP base code how many interrupts we have (defaults to zero) + num_interrupts = 1; + + super.initialize(csr_base_addr); + + // Tell regs about ral, which contains the actual register model. + regs.set_reg_block(ral); + + // Used to allow reset operations without waiting for CSR accesses to complete + can_reset_with_csr_accesses = 1; + + internal_error_agent_cfg = racl_error_log_agent_cfg::type_id::create("internal_error_agent_cfg"); + external_error_agent_cfg = racl_error_log_agent_cfg::type_id::create("external_error_agent_cfg"); +endfunction diff --git a/hw/ip_templates/racl_ctrl/dv/env/racl_ctrl_env_cov.sv.tpl b/hw/ip_templates/racl_ctrl/dv/env/racl_ctrl_env_cov.sv.tpl new file mode 100644 index 0000000000000..f274ab3d82157 --- /dev/null +++ b/hw/ip_templates/racl_ctrl/dv/env/racl_ctrl_env_cov.sv.tpl @@ -0,0 +1,32 @@ +// Copyright lowRISC contributors (OpenTitan project). +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 + +/** + * Covergroups that are dependent on run-time parameters that may be available + * only in build_phase can be defined here + * Covergroups may also be wrapped inside helper classes if needed. + */ + +class ${module_instance_name}_env_cov extends cip_base_env_cov #(.CFG_T(${module_instance_name}_env_cfg)); + `uvm_component_utils(${module_instance_name}_env_cov) + + // the base class provides the following handles for use: + // ${module_instance_name}_env_cfg: cfg + + // covergroups + // [add covergroups here] + + function new(string name, uvm_component parent); + super.new(name, parent); + // [instantiate covergroups here] + endfunction : new + + virtual function void build_phase(uvm_phase phase); + super.build_phase(phase); + // [or instantiate covergroups here] + // Please instantiate sticky_intr_cov array of objects for all interrupts that are sticky + // See cip_base_env_cov for details + endfunction + +endclass diff --git a/hw/ip_templates/racl_ctrl/dv/env/racl_ctrl_env_pkg.sv.tpl b/hw/ip_templates/racl_ctrl/dv/env/racl_ctrl_env_pkg.sv.tpl new file mode 100644 index 0000000000000..318407e0761a4 --- /dev/null +++ b/hw/ip_templates/racl_ctrl/dv/env/racl_ctrl_env_pkg.sv.tpl @@ -0,0 +1,48 @@ +// Copyright lowRISC contributors (OpenTitan project). +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 + +package ${module_instance_name}_env_pkg; + // dep packages + import uvm_pkg::*; + import top_pkg::*; + import dv_utils_pkg::*; + import dv_lib_pkg::*; + import tl_agent_pkg::*; + import cip_base_pkg::*; + import dv_base_reg_pkg::*; + import csr_utils_pkg::*; + import ${module_instance_name}_ral_pkg::*; + + import racl_error_log_agent_pkg::racl_error_log_agent; + import racl_error_log_agent_pkg::racl_error_log_agent_cfg; + import racl_error_log_agent_pkg::racl_error_log_sequencer; + import racl_error_log_agent_pkg::racl_error_log_item; + import racl_error_log_agent_pkg::racl_error_log_vec_item; + import racl_error_log_agent_pkg::racl_error_log_sporadic_seq; + + // macro includes + `include "uvm_macros.svh" + `include "dv_macros.svh" + + // The LIST_OF_ALERTS string gives a list of alert names, used by the environment config (through + // cip_base_env_cfg) to instantiate agents for handling alerts. + // + // The connection to the dut is done through interfaces that are put in the config_db in the + // (templated) tb.sv. For this to work, every alert in this list must also be listed in tb.sv. + string LIST_OF_ALERTS[2] = {"fatal_fault", "recov_ctrl_update_err"}; + + localparam int unsigned PolicyBits = $bits(top_racl_pkg::racl_policy_vec_t); + + // package sources + `include "racl_ctrl_reg_window.sv" + `include "racl_ctrl_env_wrapper_cfg.sv" + `include "${module_instance_name}_env_cfg.sv" + `include "${module_instance_name}_env_cov.sv" + `include "${module_instance_name}_virtual_sequencer.sv" + `include "${module_instance_name}_error_arb_predictor.sv" + `include "${module_instance_name}_scoreboard.sv" + `include "${module_instance_name}_env.sv" + `include "${module_instance_name}_vseq_list.sv" + +endpackage diff --git a/hw/ip_templates/racl_ctrl/dv/env/racl_ctrl_error_arb_predictor.sv.tpl b/hw/ip_templates/racl_ctrl/dv/env/racl_ctrl_error_arb_predictor.sv.tpl new file mode 100644 index 0000000000000..54fa54b1b39b5 --- /dev/null +++ b/hw/ip_templates/racl_ctrl/dv/env/racl_ctrl_error_arb_predictor.sv.tpl @@ -0,0 +1,129 @@ +// Copyright lowRISC contributors (OpenTitan project). +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 + +// A predictor for the arbiter that listens to updates in the error_logs at racl_error_i and +// racl_error_external_i. + +class ${module_instance_name}_error_arb_predictor extends uvm_component; + `uvm_component_utils(${module_instance_name}_error_arb_predictor) + + typedef uvm_tlm_analysis_fifo #(racl_error_log_item) error_fifo_t; + typedef uvm_tlm_analysis_fifo #(racl_error_log_vec_item) error_vec_fifo_t; + + // A handle to the environment cfg + ${module_instance_name}_env_cfg cfg; + + // Analysis fifos for messages from the monitors of the two error log agents. + error_vec_fifo_t internal_errors_fifo; + error_vec_fifo_t external_errors_fifo; + + // An output with merged errors + error_fifo_t merged_errors_fifo; + + extern function new(string name="", uvm_component parent=null); + extern function void build_phase(uvm_phase phase); + extern task run_phase(uvm_phase phase); + + // Update the model to match a reset of racl_ctrl + extern task on_reset(); + + // Watch error inputs, collecting up values in the two fifos and pairing them up. + extern local task watch_errors(); + + // Flush the contents of a nonempty fifo of racl_error_log_vec_item objects and return the last + // item that had been in there. + extern local function racl_error_log_vec_item flush_vec_fifo(error_vec_fifo_t fifo); +endclass + +function ${module_instance_name}_error_arb_predictor::new(string name="", uvm_component parent=null); + super.new(name, parent); +endfunction + +function void ${module_instance_name}_error_arb_predictor::build_phase(uvm_phase phase); + internal_errors_fifo = new("internal_errors_fifo", this); + external_errors_fifo = new("external_errors_fifo", this); + merged_errors_fifo = new("merged_errors_fifo", this); +endfunction + +task ${module_instance_name}_error_arb_predictor::run_phase(uvm_phase phase); + fork + super.run_phase(phase); + watch_errors(); + join +endtask + +task ${module_instance_name}_error_arb_predictor::on_reset(); + internal_errors_fifo.flush(); + external_errors_fifo.flush(); + merged_errors_fifo.flush(); +endtask + +task ${module_instance_name}_error_arb_predictor::watch_errors(); + forever begin + racl_error_log_item items[int unsigned]; + bit saw_internal, saw_external; + + // Wait until something appears in a fifo. Since the two fifos are on the same clock, we add a + // tiny delay to collect both sides if something turns up in both at the same time. + fork begin : isolation_fork + fork + begin + racl_error_log_vec_item internal_item; + internal_errors_fifo.peek(internal_item); + saw_internal = 1'b1; + #1ps; + end + begin + racl_error_log_vec_item external_item; + external_errors_fifo.peek(external_item); + saw_external = 1'b1; + #1ps; + end + join_any + disable fork; + end join + + // At this point we're just after a clock edge and we have seen an error log item on at least + // one fifo. Pair up the last item on each of the two inputs, writing its components to items. + if (saw_internal) begin + racl_error_log_vec_item vec_item = flush_vec_fifo(internal_errors_fifo); + foreach (vec_item.errors[i]) begin + `DV_CHECK(`gfn, i < cfg.internal_error_agent_cfg.num_subscribing_ips) + items[i] = vec_item.errors[i]; + end + end + if (saw_external) begin + racl_error_log_vec_item vec_item = flush_vec_fifo(external_errors_fifo); + `DV_CHECK_FATAL(vec_item != null) + foreach (vec_item.errors[i]) begin + `DV_CHECK(`gfn, i < cfg.external_error_agent_cfg.num_subscribing_ips) + items[cfg.internal_error_agent_cfg.num_subscribing_ips + i] = vec_item.errors[i]; + end + end + + // We've now got a single array of errors in items and it's time to do the arbitration. Take the + // first error, which will be the first item in the array, but unconditionally register an + // overflow if there is more than one item. + begin + int unsigned first_idx; + uvm_object arb_item_obj; + racl_error_log_item arb_item; + + `DV_CHECK(items.first(first_idx)); + `downcast(arb_item, items[first_idx].clone()) + arb_item.overflow |= items.size() > 1; + + merged_errors_fifo.write(arb_item); + end + end +endtask + +function racl_error_log_vec_item + ${module_instance_name}_error_arb_predictor::flush_vec_fifo(error_vec_fifo_t fifo); + + racl_error_log_vec_item ret; + while (!fifo.is_empty()) `DV_CHECK_FATAL(fifo.try_get(ret)) + `DV_CHECK_FATAL(ret != null) + return ret; +endfunction diff --git a/hw/ip_templates/racl_ctrl/dv/env/racl_ctrl_scoreboard.sv.tpl b/hw/ip_templates/racl_ctrl/dv/env/racl_ctrl_scoreboard.sv.tpl new file mode 100644 index 0000000000000..5fb7863458e7a --- /dev/null +++ b/hw/ip_templates/racl_ctrl/dv/env/racl_ctrl_scoreboard.sv.tpl @@ -0,0 +1,605 @@ +// Copyright lowRISC contributors (OpenTitan project). +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 + +class ${module_instance_name}_scoreboard extends cip_base_scoreboard #(.CFG_T(${module_instance_name}_env_cfg), + .RAL_T(${module_instance_name}_reg_block), + .COV_T(${module_instance_name}_env_cov)); + `uvm_component_utils(${module_instance_name}_scoreboard) + + uvm_analysis_export #(racl_error_log_vec_item) internal_errors_export; + uvm_analysis_export #(racl_error_log_vec_item) external_errors_export; + + extern function new (string name="", uvm_component parent=null); + + extern function void build_phase(uvm_phase phase); + extern function void connect_phase(uvm_phase phase); + extern task run_phase(uvm_phase phase); + extern task process_tl_access(tl_seq_item item, tl_channels_e channel, string ral_name); + + // An event that triggers a process inside the watch_interrupt task to tell it that the expected + // value of the interrupt might have changed. This should be connected to a callback for + // post_predict on the IE field of the INTR_ENABLE register and similarly for the RACL_ERROR field + // of INTR_TEST. + uvm_event check_interrupt_ev; + + // A function called when process_tl_accesses is notified of a bus access on the A channel. We use + // this to update predictions of the input to the prim_intr_hw that drives the racl_error + // interrupt (we can't use standard register predictions because the full write transaction needs + // to wait for a D-channel response, which only arrives too late). + extern local function void process_tl_a_channel(tl_seq_item item, string ral_name); + + // A function called when process_tl_accesses is notified of a bus access on the D channel. + extern local function void process_tl_d_channel(tl_seq_item item, string ral_name); + + // A function that looks at cfg.policies_vif and checks that its values match those requested in + // the policy registers. + extern local function void check_policies(); + + // A task that watches cfg.policies_vif continuously, checking that the values always match the + // ones requested by the policy registers. + // + // This performs a check just after each change to the output. To make sure everything stays in + // sync, the process_tl_access task does an analogous check after each write to one of those + // policies. + extern local task watch_policies(); + + // Watch the output of arb_predictor. When an item comes out, update the ERROR_LOG and + // ERROR_LOG_ADDRESS registers. Also assert the racl_error interrupt. + extern local task watch_arbitrated_errors(); + + // Consume a write to the ERROR_LOG register, setting error_log_d_pred and thus error_log_q_pred. + // This is called from watch_arbitrated_errors but also called after an A-channel write request + // for the register. + extern local function void take_error_log_write(uvm_reg_data_t data, bit [3:0] byte_mask); + + // Get the prediction of whether the racl_error interrupt should currently be asserted. If + // require_enable is false, this doesn't require the interrupt to be enabled (so can be used to + // predict the INTR_STATE register) + extern local function bit get_intr_pred(bit require_enable); + + // Watch the value of the interrupt and check it stays in sync with ERROR_LOG.VALID. + extern local task watch_interrupt(); + + // Keep the predicted value of the ERROR_LOG register up to date with error_log_q_pred, waiting + // for the register not to be in use. + extern local task mirror_error_log(); + + // Keep the predicted value of the ERROR_LOG_ADDRESS register up to date with + // error_log_address_q_pred, waiting for the register not to be in use. + extern local task mirror_error_log_address(); + + // Keep the predicted value of the INTR_STATE.RACL_ERROR field up to date with intr_valid_q_pred + extern local task mirror_racl_error_field(); + + // Watch the under_reset flag. When a reset is seen, reset all modelled state. + extern local task track_resets(); + + // Watch the clock, copying "d" values to "q" values on each edge. + extern local task model_registers(); + + // Check the pending_csr_write variable and apply any CSR write on the negedge of the clock. The + // variable gets set by process_tl_a_channel on a posedge, and delaying until the negedge ensures + // that the TL write is predicted to win over a HW update. + extern local task process_csr_writes(); + + // A handle pointing at the block's RAL (to avoid lots of tasks having to look it up). + local dv_base_reg_block reg_block; + + // A prediction of the current values of the inputs and values for the INTR_ENABLE and INTR_TEST + // registers. Note that we also can't use get_mirrored_value for the "q predictions" because the + // D-channel response that causes the register prediction might only be accepted several cycles + // after the A-channel request. + local bit intr_enable_d_pred, intr_enable_q_pred; + local bit intr_test_d_pred, intr_test_q_pred; + + // A model that arbitrates between errors that have been detected by the error log agents in the + // environment and fed through to *_errors_export above. Its output is not connected to any + // export, but it is consumed by the watch_arbitrated_errors task. + local ${module_instance_name}_error_arb_predictor arb_predictor; + + // A prediction of the current value of the ERROR_LOG register. This doesn't use the RAL because + // hardware changes might happen at the same time as an access to the register, which would cause + // a race condition. Because we predict the "d side" of the register, this needs separate d/q + // predictions. + local uvm_reg_data_t error_log_d_pred, error_log_q_pred; + + // An event that gets triggered on a change to error_log_pred, telling mirror_error_log to update + // the RAL prediction of the ERROR_LOG register. + local uvm_event error_log_pred_changed_ev; + + // A prediction of the current value of the ERROR_LOG_ADDRESS register and an event for when it + // gets written (see the documentation for error_log_pred and error_log_pred_changed_ev). + local uvm_reg_data_t error_log_address_d_pred, error_log_address_q_pred; + + // An event that gets triggered on a change to error_log_pred, telling mirror_error_log to update + // the RAL prediction of the ERROR_LOG register. + local uvm_event error_log_address_pred_changed_ev; + + // An event that gets changed on a change to intr_valid_q_pred, telling mirror_racl_error_field to + // update the RAL prediction of INTR_STATE.ERROR_LOG. + local uvm_event racl_error_fld_changed_ev; + + // A prediction for the current value of the "valid" field of error_log. We copy this out of + // error_log_pred to two separate variables because this means that consumers don't need to unpack + // the ERROR_LOG register value. + local bit intr_valid_d_pred, intr_valid_q_pred; + + // The most recently seen TL write as a tl_seq_item. This gets populated by process_tl_a_channel + // if it sees a write to a CSR, and is then consumed (and set to null again) on the following + // negedge by the process_csr_writes task. + local tl_seq_item pending_csr_write; +endclass + +function ${module_instance_name}_scoreboard::new (string name="", uvm_component parent=null); + super.new(name, parent); + check_interrupt_ev = new(); + error_log_pred_changed_ev = new(); + error_log_address_pred_changed_ev = new(); + racl_error_fld_changed_ev = new(); +endfunction + +function void ${module_instance_name}_scoreboard::build_phase(uvm_phase phase); + string ral_name; + + super.build_phase(phase); + // TODO: remove once support alert checking + do_alert_check = 0; + + internal_errors_export = new("internal_errors_export", this); + external_errors_export = new("external_errors_export", this); + + arb_predictor = ${module_instance_name}_error_arb_predictor::type_id::create("arb_predictor", this); + arb_predictor.cfg = cfg; + + // We expect there to be a RAL model for exactly one register block. Grab a reference to it. + if (cfg.ral_models.size() != 1) begin + `uvm_fatal(`gfn, $sformatf("Number of register blocks = %0d, not 1.", cfg.ral_models.size())) + end + if (!cfg.ral_models.first(ral_name)) `uvm_fatal(`gfn, "first failed on a nonempty array") + reg_block = cfg.ral_models[ral_name]; + if (reg_block == null) `uvm_fatal(`gfn, "Couldn't find reg block") +endfunction + +function void ${module_instance_name}_scoreboard::connect_phase(uvm_phase phase); + internal_errors_export.connect(arb_predictor.internal_errors_fifo.analysis_export); + external_errors_export.connect(arb_predictor.external_errors_fifo.analysis_export); +endfunction + +task ${module_instance_name}_scoreboard::run_phase(uvm_phase phase); + // At the start of time, wait until rst_n is either 0 or 1 (skipping over any period where it's 'x + // at the start) + wait(!$isunknown(cfg.clk_rst_vif.rst_n)); + fork + super.run_phase(phase); + if (cfg.en_scb) + fork + watch_policies(); + watch_arbitrated_errors(); + watch_interrupt(); + mirror_error_log(); + mirror_error_log_address(); + mirror_racl_error_field(); + track_resets(); + model_registers(); + process_csr_writes(); + join + join +endtask + +task ${module_instance_name}_scoreboard::process_tl_access(tl_seq_item item, + tl_channels_e channel, + string ral_name); + case (channel) + AddrChannel: process_tl_a_channel(item, ral_name); + DataChannel: process_tl_d_channel(item, ral_name); + default: `uvm_fatal(`gfn, $sformatf("Invalid channel: %0h", channel)) + endcase +endtask + +function void ${module_instance_name}_scoreboard::process_tl_a_channel(tl_seq_item item, string ral_name); + uvm_reg_addr_t csr_addr; + + uvm_reg intr_enable_reg, intr_test_reg; + bit intr_enable_d, intr_test_d, intr_value_d; + + // A-channel monitoring is only used to catch write requests, so we needn't track read requests. + if (!item.is_write) return; + + // Figure out what register is being accessed + csr_addr = reg_block.get_word_aligned_addr(item.a_addr); + + // If this is a request to a non-existent register, we don't expect it to have any effect. Ignore + // it. + if (reg_block.default_map.get_reg_by_offset(csr_addr) == null) return; + + // We only care about writes that might touch the bottom bit, so ignore writes where the bottom + // bit of the byte mask is false. + if (!(item.a_mask & 1)) return; + + // If we get here, we know that an A-side channel transaction has just finished that tries to + // write to a known csr. Set the pending_csr_write variable to point at this write. It will then + // be applied on the negedge of the clock (so that it "wins" for the prediction if HW requests a + // register update on the same cycle) + pending_csr_write = item; +endfunction + +function void ${module_instance_name}_scoreboard::process_tl_d_channel(tl_seq_item item, string ral_name); + bit write = item.is_write(); + uvm_reg_addr_t csr_addr = reg_block.get_word_aligned_addr(item.a_addr); + uvm_reg csr = reg_block.default_map.get_reg_by_offset(csr_addr); + + if (csr == null) `uvm_fatal(`gfn, $sformatf("Could not find CSR for address 0x%0h", csr_addr)) + + // If this is the D channel response for a write to one of the policy registers, check that it has + // been applied to racl_policies_o. + if (write && cfg.regs.is_policy_reg(csr)) check_policies(); + + // If this is the D channel response to a register read, work out the value we think it should + // have (probably the mirrored value of the register) and check that item.d_data matches. + if (!write) begin + string csr_name = csr.get_name(); + uvm_reg_data_t allowed_values[$] = {csr.get_mirrored_value()}; + bit matched_value; + + if (csr_name == "error_log") allowed_values = {error_log_q_pred}; + else if (csr_name == "error_log_address") allowed_values = {error_log_address_q_pred}; + else if (csr_name == "intr_state") begin + // Because the bus access might happen at the same time as an error log being raised (which + // gets processed by watch_arbitrated_errors), the INTR_STATE.RACL_ERROR field might have + // changed value. The RAL prediction will be updated by the mirror_error_log task, but this + // bus access might have kept it waiting. Add any new value to the list of allowed values. + uvm_reg_data_t fld_mask = 1 << 0; + allowed_values.push_back((allowed_values[0] & ~fld_mask) | (error_log_q_pred & fld_mask)); + end + + // Work through the list of allowed values and check whether the read transaction we've just + // seen matches any of them. + foreach (allowed_values[i]) begin + if (item.d_data == allowed_values[i]) begin + matched_value = 1'b1; + break; + end + end + + if (!matched_value) begin + `uvm_error(`gfn, + $sformatf("Unexpected value when reading from %0s. Seen=0x%0h; Allowed: %0p", + csr_name, item.d_data, allowed_values)) + end + end +endfunction + +function void ${module_instance_name}_scoreboard::check_policies(); + dv_base_reg regs[$]; + cfg.regs.get_policy_registers(regs); + + for (int unsigned i = 0; i < regs.size(); i++) begin + logic [31:0] seen = cfg.policies_vif.get_policy(i); + logic [31:0] expected = regs[i].get_mirrored_value(); + + // If the register is currently marked as busy, that means something is currently reading or + // writing it. If it's a write, we may not yet have updated the prediction to match the value + // that is being written. Skip the check in this case. Note that we won't miss a mismatch: the + // watch_policies task will call us again on the next clock edge. + if (regs[i].is_busy()) continue; + + if (seen !== expected) begin + `uvm_error(`gfn, $sformatf("Policy output mismatch for index %0d: seen %0x; expected %0x", + i, seen, expected)) + end + end +endfunction + +task ${module_instance_name}_scoreboard::watch_policies(); + forever begin + wait(!cfg.under_reset); + + check_policies(); + + fork begin : isolation_fork + fork + wait(cfg.under_reset); + begin + @(cfg.policies_vif.policies); + @(negedge cfg.clk_rst_vif.clk); + end + join_any + disable fork; + end join + end +endtask + +task ${module_instance_name}_scoreboard::watch_arbitrated_errors(); + dv_base_reg error_log_reg = cfg.regs.get_error_log_reg(); + dv_base_reg error_log_address_reg = cfg.regs.get_error_log_address_reg(); + + // Extract fields from error_log_reg + uvm_reg_field valid_fld = error_log_reg.get_field_by_name("valid"); + uvm_reg_field overflow_fld = error_log_reg.get_field_by_name("overflow"); + uvm_reg_field read_access_fld = error_log_reg.get_field_by_name("read_access"); + uvm_reg_field role_fld = error_log_reg.get_field_by_name("role"); + uvm_reg_field ctn_uid_fld = error_log_reg.get_field_by_name("ctn_uid"); + + // Extract the (only) field from error_log_address_reg + uvm_reg_field address_fld = error_log_address_reg.get_field_by_name("address"); + + // All the fields that we just searched for should have existed (which means that none of the + // objects should be null) + if (! (valid_fld && overflow_fld && read_access_fld && role_fld && ctn_uid_fld)) + `uvm_fatal(`gfn, "Failed to extract one or more fields from ERROR_LOG register.") + + forever begin + racl_error_log_item item; + arb_predictor.merged_errors_fifo.get(item); + + // Predict a new value for the ERROR_LOG register. We can predict the relevant fields (extracted + // above) separately. Note that the register is read-only to software, so these predictions use + // UVM_PREDICT_DIRECT. + // + // Before calling predict() on each field, make sure that the integers in the item's role and + // ctn_uid are small enough to be representable in the register. This should always work: if + // not, generate an error that shows the testbench is broken. + if (item.role >> role_fld.get_n_bits()) + `uvm_error(`gfn, $sformatf("Item has a role of %0h, but the field only has %0d bits", + item.role, + role_fld.get_n_bits())) + if (item.ctn_uid >> ctn_uid_fld.get_n_bits()) + `uvm_error(`gfn, $sformatf("Item has a ctn_uid of %0h, but the field only has %0d bits", + item.ctn_uid, + ctn_uid_fld.get_n_bits())) + + error_log_d_pred = 1 << valid_fld.get_lsb_pos(); + error_log_d_pred |= item.overflow << overflow_fld.get_lsb_pos(); + error_log_d_pred |= item.read_not_write << read_access_fld.get_lsb_pos(); + error_log_d_pred |= item.role << role_fld.get_lsb_pos(); + error_log_d_pred |= item.ctn_uid << ctn_uid_fld.get_lsb_pos(); + + // Predict a new value for the ERROR_LOG_ADDRESS register, using the same framework as the code + // above that predicts ERROR_LOG. + if (item.request_address >> address_fld.get_n_bits()) + `uvm_error(`gfn, $sformatf({"Item has a request_address of %0h, ", + "but the one field of error_log_address only has %0d bits"}, + item.request_address, address_fld.get_n_bits())) + + error_log_address_d_pred = item.request_address << address_fld.get_lsb_pos(); + + // Finally, predict the behaviour of the racl_error interrupt (which must have just been set) + intr_valid_d_pred = 1'b1; + end +endtask + +function void ${module_instance_name}_scoreboard::take_error_log_write(uvm_reg_data_t data, bit [3:0] byte_mask); + // The only field of ERROR_LOG that is not read-only is the VALID field, which is stored at bit + // zero. This is rw1c, so we need to clear its predicted bottom bit if the bottom bit of data is 1 + // and the bottom byte is enabled. + if (data[0] & byte_mask[0]) begin + error_log_d_pred[0] = 1'b0; + intr_valid_d_pred = 0; + end +endfunction + +function bit ${module_instance_name}_scoreboard::get_intr_pred(bit require_enable); + bit enabled = require_enable ? intr_enable_d_pred : 1'b1; + return (intr_valid_q_pred | intr_test_q_pred) & enabled; +endfunction + +task ${module_instance_name}_scoreboard::watch_interrupt(); + dv_base_reg_field intr_state_fld = cfg.regs.get_intr_state_racl_error_fld(); + + forever begin + // The interrupt signal is of Status type and is controlled by a prim_intr_hw called + // u_intr_racl_error. A genuine interrupt would be signalled through its event_intr_i port, + // which gets the ERROR_LOG.VALID field. + // + // That genuine interrupt may not be enabled, so the prediction also requires the INTR_ENABLE + // register to be true. + // + // Another way that the interrupt can be set is through the INTR_TEST register. Since it's a + // hwext register (whose state actually lives in prim_intr_hw), we it isn't modelled in the ral, + // but we model it in an intr_test_q_pred register, which is essentially the test_q signal in + // prim_intr_hw. + bit intr_pred = get_intr_pred(1'b1); + + // Check that the interrupt line matches the prediction in intr_pred + if (intr_pred != cfg.intr_vif.sample() & 1) begin + `uvm_error(`gfn, + $sformatf("racl_error interrupt doesn't match prediction. exp=%0d; seen=%0d", + intr_pred, cfg.intr_vif.sample() & 1)) + end + + if (intr_pred != intr_state_fld.get_mirrored_value()) racl_error_fld_changed_ev.trigger(); + + // Wait until either the interrupt line changes or our prediction of its value might change + // (either because of a change to intr_valid_q_pred or a change to INTR_ENABLE) + fork begin : isolation_fork + fork + @(cfg.intr_vif.pins_o[0]); + @intr_valid_q_pred; + check_interrupt_ev.wait_on(); + join_any + disable fork; + end join + + // Unconditionally reset the timer correctly handle a situation where several different items + // fired in the fork above. + check_interrupt_ev.reset(); + + // At this point, we don't actually know what point of the cycle we are at. It's possible that + // the event that just triggered us + + // To avoid spurious failures when the prediction and the interrupt change at the same time, + // delay for 1ps (to allow things to settle down). + #1ps; + end +endtask + +task ${module_instance_name}_scoreboard::mirror_error_log(); + dv_base_reg error_log_reg = cfg.regs.get_error_log_reg(); + forever begin + // Wait until we are told that the event has been triggered + error_log_pred_changed_ev.wait_on(); + + // Now wait until we can safely update the predicted value of the register, using the register's + // access_lock. + error_log_reg.take_lock(); + + // Give a UVM_PREDICT_DIRECT prediction of the register value using the current value of + // error_log_pred (which may have changed since we started waiting for the lock). This shouldn't + // fail (the only way that it can fail is if a field's prediction sees a collision, but the lock + // we hold should prevent that). + if(!error_log_reg.predict(error_log_q_pred)) begin + `uvm_error(`gfn, "Failed to predict ERROR_LOG: presumably because of a race.") + end + + error_log_reg.release_lock(); + + // Clear the event again (because nothing has run since we last took a copy of error_log_pred), + // so that the next iteration will only run when the prediction is updated again. + error_log_pred_changed_ev.reset(); + end +endtask + +task ${module_instance_name}_scoreboard::mirror_error_log_address(); + dv_base_reg error_log_address_reg = cfg.regs.get_error_log_address_reg(); + forever begin + // Wait until we are told that the event has been triggered + error_log_address_pred_changed_ev.wait_on(); + + // Now wait until we can safely update the predicted value of the register, using the register's + // access_lock. + error_log_address_reg.take_lock(); + + // Give a UVM_PREDICT_DIRECT prediction of the register value using the current value of + // error_log_pred (which may have changed since we started waiting for the lock). This shouldn't + // fail (the only way that it can fail is if a field's prediction sees a collision, but the lock + // we hold should prevent that). + if(!error_log_address_reg.predict(error_log_address_q_pred)) begin + `uvm_error(`gfn, "Failed to predict ERROR_LOG_ADDRESS: presumably because of a race.") + end + + error_log_address_reg.release_lock(); + + // Clear the event again (because nothing has run since we last took a copy of error_log_pred), + // so that the next iteration will only run when the prediction is updated again. + error_log_address_pred_changed_ev.reset(); + end +endtask + +task ${module_instance_name}_scoreboard::mirror_racl_error_field(); + dv_base_reg_field racl_error_fld = cfg.regs.get_intr_state_racl_error_fld(); + uvm_reg intr_state_uvm_reg = racl_error_fld.get_parent(); + dv_base_reg intr_state_reg; + + `downcast(intr_state_reg, intr_state_uvm_reg) + + forever begin + // Wait until we are told that the event has been triggered + racl_error_fld_changed_ev.wait_on(); + + // Now wait until we can safely update the predicted value of the register, using the register's + // access_lock. + intr_state_reg.take_lock(); + + // Give a UVM_PREDICT_DIRECT prediction of the field value using the result of get_intr_pred(0) + // (which may have changed since we started waiting for the lock). This shouldn't fail (the only + // way that it can fail is if a field's prediction sees a collision, but the lock we hold should + // prevent that). + if(!racl_error_fld.predict(get_intr_pred(1'b0))) begin + `uvm_error(`gfn, "Failed to predict ERROR_LOG_ADDRESS: presumably because of a race.") + end + + intr_state_reg.release_lock(); + + // Clear the event again (because nothing has run since we last took a copy of error_log_pred), + // so that the next iteration will only run when the prediction is updated again. + racl_error_fld_changed_ev.reset(); + end +endtask + +task ${module_instance_name}_scoreboard::track_resets(); + dv_base_reg error_log_reg = cfg.regs.get_error_log_reg(); + dv_base_reg error_log_address_reg = cfg.regs.get_error_log_address_reg(); + + forever begin + wait(cfg.under_reset); + + arb_predictor.on_reset(); + error_log_d_pred = error_log_reg.get_reset(); + error_log_q_pred = error_log_reg.get_reset(); + error_log_address_d_pred = error_log_address_reg.get_reset(); + error_log_address_q_pred = error_log_address_reg.get_reset(); + intr_valid_d_pred = 1'b0; + intr_valid_q_pred = 1'b0; + intr_test_d_pred = 1'b0; + intr_test_q_pred = 1'b0; + intr_enable_d_pred = 1'b0; + intr_enable_q_pred = 1'b0; + + wait(!cfg.under_reset); + end +endtask + +task ${module_instance_name}_scoreboard::model_registers(); + bit changes_error_log, changes_error_address_log, may_change_intr; + + forever begin + cfg.clk_rst_vif.wait_clks(1); + if (cfg.under_reset) begin + wait(!cfg.under_reset); + continue; + end + + changes_error_log = |(error_log_q_pred ^ error_log_d_pred); + changes_error_address_log = |(error_log_address_q_pred ^ error_log_address_d_pred); + may_change_intr = ((intr_valid_q_pred ^ intr_valid_d_pred) | + (intr_test_q_pred ^ intr_test_d_pred) | + (intr_enable_q_pred ^ intr_enable_d_pred)); + + intr_test_q_pred = intr_test_d_pred; + intr_enable_q_pred = intr_enable_d_pred; + error_log_q_pred = error_log_d_pred; + error_log_address_q_pred = error_log_address_d_pred; + intr_valid_q_pred = intr_valid_d_pred; + + if (changes_error_log) error_log_pred_changed_ev.trigger(); + if (changes_error_address_log) error_log_address_pred_changed_ev.trigger(); + if (may_change_intr) check_interrupt_ev.trigger(); + end +endtask + +task ${module_instance_name}_scoreboard::process_csr_writes(); + uvm_reg_addr_t csr_addr; + uvm_reg csr; + string csr_name; + + forever begin + cfg.clk_rst_vif.wait_n_clks(1); + if (cfg.under_reset) begin + wait(!cfg.under_reset); + continue; + end + + if (pending_csr_write == null) continue; + + csr_addr = reg_block.get_word_aligned_addr(pending_csr_write.a_addr); + csr = reg_block.default_map.get_reg_by_offset(csr_addr); + + // We expect to find a CSR here (because process_tl_a_channel only sets pending_csr_write if it + // corresponds to a genuine CSR) + if (csr == null) `uvm_fatal(`gfn, "Bogus pending_csr_write with no CSR.") + + // There are only a few CSRs where we need precisely timed predictions (the ones that affect the + // racl_error interrupt). If we are looking at one of them, update our prediction of its bottom + // bit. + csr_name = csr.get_name(); + if (csr_name == "intr_enable") intr_enable_d_pred = pending_csr_write.a_data & 1; + else if (csr_name == "intr_test") intr_test_d_pred = pending_csr_write.a_data & 1; + else if (csr_name == "error_log") begin + take_error_log_write(pending_csr_write.a_data, pending_csr_write.a_mask); + end + + // Now that we have consumed pending_csr_write, set it back to null. + pending_csr_write = null; + end +endtask diff --git a/hw/ip_templates/racl_ctrl/dv/env/racl_ctrl_virtual_sequencer.sv.tpl b/hw/ip_templates/racl_ctrl/dv/env/racl_ctrl_virtual_sequencer.sv.tpl new file mode 100644 index 0000000000000..1d7dac67b74ec --- /dev/null +++ b/hw/ip_templates/racl_ctrl/dv/env/racl_ctrl_virtual_sequencer.sv.tpl @@ -0,0 +1,14 @@ +// Copyright lowRISC contributors (OpenTitan project). +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 + +class ${module_instance_name}_virtual_sequencer extends cip_base_virtual_sequencer #( + .CFG_T(${module_instance_name}_env_cfg), + .COV_T(${module_instance_name}_env_cov) + ); + `uvm_component_utils(${module_instance_name}_virtual_sequencer) + + + `uvm_component_new + +endclass diff --git a/hw/ip_templates/racl_ctrl/dv/env/seq_lib/racl_ctrl_base_vseq.sv.tpl b/hw/ip_templates/racl_ctrl/dv/env/seq_lib/racl_ctrl_base_vseq.sv.tpl new file mode 100644 index 0000000000000..068187aeac6b5 --- /dev/null +++ b/hw/ip_templates/racl_ctrl/dv/env/seq_lib/racl_ctrl_base_vseq.sv.tpl @@ -0,0 +1,149 @@ +// Copyright lowRISC contributors (OpenTitan project). +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 + +class ${module_instance_name}_base_vseq + extends cip_base_vseq #(.RAL_T (${module_instance_name}_reg_block), + .CFG_T (${module_instance_name}_env_cfg), + .COV_T (${module_instance_name}_env_cov), + .VIRTUAL_SEQUENCER_T (${module_instance_name}_virtual_sequencer)); + + `uvm_object_utils(${module_instance_name}_base_vseq) + + // Handles to the sequencers for the internal and external error log agents + racl_error_log_sequencer internal_error_log_sequencer_h; + racl_error_log_sequencer external_error_log_sequencer_h; + + extern function new (string name=""); + extern function uvm_object clone(); + extern task body(); + extern task post_start(); + + // Create a child sequence by name, copying sequencer handles across. Overridden from dv_base_seq. + extern function uvm_sequence create_seq_by_name(string name); + + // Occasionally write 1 to the the error_log register, which will clear the valid bit (clearing + // the error). Stop once the stopping flag goes high. + extern local task clear_error_log(); + + // The sequences that we send to the two error log agents. These are started at the start of + // body() but then told to stop (and waited for) in post_start(). + local racl_error_log_sporadic_seq int_seq, ext_seq; + + // Once the body of this sequence has completed, this bit gets set (which tells the + // clear_error_log background sequence to stop) + local bit stopping; + + // This flag gets set by clear_error_log when it is still running. + local bit clear_error_log_running; +endclass + +function ${module_instance_name}_base_vseq::new (string name=""); + super.new(name); + + int_seq = racl_error_log_sporadic_seq::type_id::create("int_seq"); + ext_seq = racl_error_log_sporadic_seq::type_id::create("ext_seq"); +endfunction + +function uvm_object ${module_instance_name}_base_vseq::clone(); + ${module_instance_name}_base_vseq ret; + `downcast(ret, super.clone()); + + ret.internal_error_log_sequencer_h = internal_error_log_sequencer_h; + ret.external_error_log_sequencer_h = external_error_log_sequencer_h; + + return ret; +endfunction + +task ${module_instance_name}_base_vseq::body(); + `DV_CHECK_FATAL(internal_error_log_sequencer_h) + `DV_CHECK_FATAL(external_error_log_sequencer_h) + + // Start "sporadic" sequences on the two error log agents. These are spawned into background + // threads which are asked to stop in the post_start() task (which will run when the body() task + // has completed, including any subclass implementation) + fork + int_seq.start(internal_error_log_sequencer_h, this); + ext_seq.start(external_error_log_sequencer_h, this); + clear_error_log(); + join_none +endtask + +task ${module_instance_name}_base_vseq::post_start(); + fork + int_seq.request_end(); + ext_seq.request_end(); + join + + // Once the error log sequences have finished, wait one cycle for any error log values to make it + // to the register file. Then set the stopping flag. This will cause the clear_error_log task to + // try one more time to clear any log and then exit. + cfg.clk_rst_vif.wait_clks_or_rst(1); + stopping = 1'b1; + wait(!clear_error_log_running); + + super.post_start(); +endtask + +function uvm_sequence ${module_instance_name}_base_vseq::create_seq_by_name(string name); + uvm_sequence base_ret = super.create_seq_by_name(name); + ${module_instance_name}_base_vseq ret; + + `downcast(ret, base_ret) + `DV_CHECK_FATAL(ret != null) + + ret.internal_error_log_sequencer_h = internal_error_log_sequencer_h; + ret.external_error_log_sequencer_h = external_error_log_sequencer_h; + + return ret; +endfunction + +task ${module_instance_name}_base_vseq::clear_error_log(); + dv_base_reg error_log_reg; + dv_base_reg_field valid_field; + + if (clear_error_log_running) `uvm_fatal(`gfn, "Nested call to clear_error_log") + clear_error_log_running = 1'b1; + + error_log_reg = cfg.ral.get_dv_base_reg_by_name("error_log"); + if (!error_log_reg) `uvm_fatal(`gfn, "Cannot find the ERROR_LOG register.") + + valid_field = error_log_reg.get_field_by_name("valid"); + if (!valid_field) `uvm_fatal(`gfn, "Cannot find a VALID field in the ERROR_LOG register.") + + while (!stopping) begin + uvm_status_e status; + uvm_reg_data_t field_value; + + // Insert a gap between each time we consider clearing the logged error. If the stopping flag + // goes high, stop waiting immediately (but possibly still clear errors) + fork begin : isolation_fork + fork + #100ns; + wait(stopping); + join_any + disable fork; + end join + + // We can clear the error_log.valid flag by writing 1 to the register. But we don't want to do + // this every iteration of the loop: the TL traffic will get in the way of other users. Instead + // of doing that, we only clear the flag if it was previously set. + valid_field.peek(status, field_value, .kind("BkdrRegPathRtl")); + if (status != UVM_IS_OK) begin + `uvm_error(`gfn, "Failed to peek at ERORR_LOG.VALID.") + field_value = 1'b1; + end + + if (field_value) error_log_reg.write(status, 1, .prior(100)); + + if (status != UVM_IS_OK && !cfg.under_reset) begin + `uvm_error(`gfn, "Failed to write error_log register") + end + + // Pause this task if we have gone into reset, but drop out of the pause if stopping becomes + // true (in which case, the clear_error_log task will stop). + wait(stopping || !cfg.under_reset); + end + + clear_error_log_running = 1'b0; +endtask diff --git a/hw/ip_templates/racl_ctrl/dv/env/seq_lib/racl_ctrl_common_vseq.sv.tpl b/hw/ip_templates/racl_ctrl/dv/env/seq_lib/racl_ctrl_common_vseq.sv.tpl new file mode 100644 index 0000000000000..e763eaf9f9624 --- /dev/null +++ b/hw/ip_templates/racl_ctrl/dv/env/seq_lib/racl_ctrl_common_vseq.sv.tpl @@ -0,0 +1,20 @@ +// Copyright lowRISC contributors (OpenTitan project). +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 + +class ${module_instance_name}_common_vseq extends ${module_instance_name}_base_vseq; + `uvm_object_utils(${module_instance_name}_common_vseq) + + constraint num_trans_c { + num_trans inside {[1:2]}; + } + `uvm_object_new + + virtual task body(); + fork + super.body(); + run_common_vseq_wrapper(num_trans); + join + endtask : body + +endclass diff --git a/hw/ip_templates/racl_ctrl/dv/env/seq_lib/racl_ctrl_smoke_vseq.sv.tpl b/hw/ip_templates/racl_ctrl/dv/env/seq_lib/racl_ctrl_smoke_vseq.sv.tpl new file mode 100644 index 0000000000000..5496f4a9d16b1 --- /dev/null +++ b/hw/ip_templates/racl_ctrl/dv/env/seq_lib/racl_ctrl_smoke_vseq.sv.tpl @@ -0,0 +1,51 @@ +// Copyright lowRISC contributors (OpenTitan project). +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 + +// A smoke test vseq that writes a value for each policy, which will check that policy is getting +// mirrored correctly on the racl_policies_o port. + +class ${module_instance_name}_smoke_vseq extends ${module_instance_name}_base_vseq; + `uvm_object_utils(${module_instance_name}_smoke_vseq) + + extern function new (string name=""); + extern task body(); + + // Write random values to the policy registers. This performs two times as many writes as there + // are registers (but doesn't guarantee anything about the distribution of writes to registers) + extern local task write_policy_regs(); +endclass + +function ${module_instance_name}_smoke_vseq::new (string name=""); + super.new(name); +endfunction + +task ${module_instance_name}_smoke_vseq::body(); + fork + super.body(); + write_policy_regs(); + join +endtask + +task ${module_instance_name}_smoke_vseq::write_policy_regs(); + dv_base_reg regs[$]; + cfg.regs.get_policy_registers(regs); + repeat (2 * regs.size()) begin + int unsigned i = $urandom_range(0, regs.size() - 1); + // Write an arbitrary random value to the register. Not every bit will correspond to a role, + // because the encoding uses bits 0..n; 16..16+n for some n. But extra bits will be ignored + // anyway, so it doesn't really matter. + bit [31:0] val = $urandom; + + // Write val to the register. If the register was shadowed, we'll need to do it twice. + repeat (regs[i].get_is_shadowed() ? 2 : 1) begin + uvm_status_e status; + regs[i].write(status, val, .prior(50)); + + // Unless we went into reset, we expect the write to go through + if (cfg.under_reset) return; + if (status != UVM_IS_OK) + `uvm_error(`gfn, $sformatf("Failed to write register %s", regs[i].get_name())) + end + end +endtask diff --git a/hw/ip_templates/racl_ctrl/dv/env/seq_lib/racl_ctrl_stress_all_vseq.sv.tpl b/hw/ip_templates/racl_ctrl/dv/env/seq_lib/racl_ctrl_stress_all_vseq.sv.tpl new file mode 100644 index 0000000000000..e78e298de5b6d --- /dev/null +++ b/hw/ip_templates/racl_ctrl/dv/env/seq_lib/racl_ctrl_stress_all_vseq.sv.tpl @@ -0,0 +1,54 @@ +// Copyright lowRISC contributors (OpenTitan project). +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 + +// A sequence that runs sequences one after the other. + +class ${module_instance_name}_stress_all_vseq extends ${module_instance_name}_base_vseq; + `uvm_object_utils(${module_instance_name}_stress_all_vseq) + + // The sequences that we'll run back-to-back + // + // TODO: At the moment, there is only actually one sequence defined (racl_ctrl_smoke), which makes + // this a little silly. We're still defining racl_ctrl_stress_all_vseq already, because it + // means we can run tests like racl_ctrl_stress_all_with_rand_reset. + local string vseq_names[$] = { + "${module_instance_name}_smoke_vseq" + }; + + extern function new (string name=""); + extern task body(); + + // Create and run a child vseq with the given name + extern local task run_child(string child_vseq_name); +endclass + +function ${module_instance_name}_stress_all_vseq::new (string name=""); + super.new(name); +endfunction + +task ${module_instance_name}_stress_all_vseq::body(); + fork + super.body(); + begin + `uvm_info(`gfn, $sformatf("Running %0d sub-sequences", num_trans), UVM_LOW) + repeat (num_trans) begin + run_child(vseq_names[$urandom_range(0, vseq_names.size() - 1)]); + if (cfg.under_reset) break; + end + end + join +endtask + +task ${module_instance_name}_stress_all_vseq::run_child(string child_vseq_name); + ${module_instance_name}_base_vseq child; + `downcast(child, create_seq_by_name(child_vseq_name)) + child.set_sequencer(p_sequencer); + + // Decide whether the sub-sequence will apply a reset. If the current sequence is configured not + // to apply a reset, don't do so in the child. + child.do_apply_reset = do_apply_reset ? $urandom_range(0, 1) : 0; + + `DV_CHECK_RANDOMIZE_FATAL(child) + child.start(p_sequencer); +endtask diff --git a/hw/ip_templates/racl_ctrl/dv/env/seq_lib/racl_ctrl_vseq_list.sv.tpl b/hw/ip_templates/racl_ctrl/dv/env/seq_lib/racl_ctrl_vseq_list.sv.tpl new file mode 100644 index 0000000000000..7a2c56a60c19c --- /dev/null +++ b/hw/ip_templates/racl_ctrl/dv/env/seq_lib/racl_ctrl_vseq_list.sv.tpl @@ -0,0 +1,8 @@ +// Copyright lowRISC contributors (OpenTitan project). +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 + +`include "${module_instance_name}_base_vseq.sv" +`include "${module_instance_name}_smoke_vseq.sv" +`include "${module_instance_name}_common_vseq.sv" +`include "${module_instance_name}_stress_all_vseq.sv" diff --git a/hw/ip_templates/racl_ctrl/dv/racl_ctrl_ral.core.tpl b/hw/ip_templates/racl_ctrl/dv/racl_ctrl_ral.core.tpl deleted file mode 100644 index aff644a49ae77..0000000000000 --- a/hw/ip_templates/racl_ctrl/dv/racl_ctrl_ral.core.tpl +++ /dev/null @@ -1,28 +0,0 @@ -CAPI=2: -# Copyright lowRISC contributors (OpenTitan project). -# Licensed under the Apache License, Version 2.0, see LICENSE for details. -# SPDX-License-Identifier: Apache-2.0 -name: ${instance_vlnv(f"lowrisc:dv:{module_instance_name}_ral:0.1")} -description: "RACL_CTRL register model" -virtual: - - "lowrisc:dv:racl_ctrl_ral" -filesets: - ral_dep: - depend: - - lowrisc:dv:ralgen - - lowrisc:dv:dv_base_reg - -generate: - ral: - generator: ralgen - parameters: - name: racl_ctrl - ip_hjson: ../data/${module_instance_name}.hjson - position: prepend - -targets: - default: - filesets: - - ral_dep - generate: - - ral diff --git a/hw/ip_templates/racl_ctrl/dv/racl_ctrl_sim.core.tpl b/hw/ip_templates/racl_ctrl/dv/racl_ctrl_sim.core.tpl index 9eea481de3c7a..2346ddd24d48c 100644 --- a/hw/ip_templates/racl_ctrl/dv/racl_ctrl_sim.core.tpl +++ b/hw/ip_templates/racl_ctrl/dv/racl_ctrl_sim.core.tpl @@ -12,9 +12,9 @@ filesets: files_dv: depend: - - lowrisc:dv:racl_ctrl_test - lowrisc:dv:racl_error_log_agent - ${instance_vlnv(f"lowrisc:dv:{module_instance_name}_sva")} + - ${instance_vlnv(f"lowrisc:dv:{module_instance_name}_test")} files: - tb.sv file_type: systemVerilogSource diff --git a/hw/ip_templates/racl_ctrl/dv/racl_ctrl_sim_cfg.hjson.tpl b/hw/ip_templates/racl_ctrl/dv/racl_ctrl_sim_cfg.hjson.tpl index 253f2e936df6b..e46a1dc5f7abe 100644 --- a/hw/ip_templates/racl_ctrl/dv/racl_ctrl_sim_cfg.hjson.tpl +++ b/hw/ip_templates/racl_ctrl/dv/racl_ctrl_sim_cfg.hjson.tpl @@ -2,25 +2,60 @@ // Licensed under the Apache License, Version 2.0, see LICENSE for details. // SPDX-License-Identifier: Apache-2.0 { + // Name of the sim cfg - typically same as the name of the DUT. + name: ${module_instance_name} + + // Top level dut name (sv module). + dut: ${module_instance_name} + + // Top level testbench name (sv module). + tb: tb + + // Simulator used to sign off this block + tool: xcelium + // Fusesoc core file used for building the file list. fusesoc_core: ${instance_vlnv(f"lowrisc:dv:{module_instance_name}_sim:0.1")} - // Testplan hjson file. Note it is not templated (even though this sim_cfg file is) - testplan: "{proj_root}/hw/ip/racl_ctrl/data/racl_ctrl_testplan.hjson" + // Testplan hjson file. + testplan: "{self_dir}/../data/${module_instance_name}_testplan.hjson" - // RAL spec - used to generate the RAL model. - ral_spec: "{self_dir}/../data/${module_instance_name}.hjson" + // Import additional common sim cfg files. + import_cfgs: [// Project wide common sim cfg file + "{proj_root}/hw/dv/tools/dvsim/common_sim_cfg.hjson", + // Common CIP test lists + "{proj_root}/hw/dv/tools/dvsim/tests/csr_tests.hjson", + "{proj_root}/hw/dv/tools/dvsim/tests/intr_test.hjson", + "{proj_root}/hw/dv/tools/dvsim/tests/alert_test.hjson", + "{proj_root}/hw/dv/tools/dvsim/tests/shadow_reg_errors_tests.hjson", + "{proj_root}/hw/dv/tools/dvsim/tests/tl_access_tests.hjson", + "{proj_root}/hw/dv/tools/dvsim/tests/stress_tests.hjson"] - // Top level dut module name - dut: ${module_instance_name} + // Add additional tops for simulation. + sim_tops: ["${module_instance_name}_bind"] - // The "testbench name" that dvsim attaches to all the tests in this configuration - name: racl_ctrl + // Default iterations for all tests - each test entry can override this. + reseed: 50 - // Tell the tool to include the bind module as a top-level. Since the name of the bind module is - // templated, this needs to be done here. - sim_tops: ["${module_instance_name}_bind"] + // Default UVM test and seq class name. + uvm_test: ${module_instance_name}_base_test + uvm_test_seq: ${module_instance_name}_base_vseq + + // List of test specifications. + tests: [ + { + name: racl_ctrl_smoke + uvm_test_seq: ${module_instance_name}_smoke_vseq + } + + // TODO: add more tests here + ] - // Import the underlying sim_cfg (not templated) - import_cfgs: ["{proj_root}/hw/ip/racl_ctrl/dv/racl_ctrl_tests.hjson"] + // List of regressions. + regressions: [ + { + name: smoke + tests: ["racl_ctrl_smoke"] + } + ] } diff --git a/hw/ip_templates/racl_ctrl/dv/sva/racl_ctrl_sva.core.tpl b/hw/ip_templates/racl_ctrl/dv/sva/racl_ctrl_sva.core.tpl index 792375a24f32d..69e7e27bfd2c8 100644 --- a/hw/ip_templates/racl_ctrl/dv/sva/racl_ctrl_sva.core.tpl +++ b/hw/ip_templates/racl_ctrl/dv/sva/racl_ctrl_sva.core.tpl @@ -14,7 +14,7 @@ filesets: - lowrisc:tlul:headers - lowrisc:fpv:csr_assert_gen files: - - racl_ctrl_bind.sv + - ${module_instance_name}_bind.sv file_type: systemVerilogSource files_formal: diff --git a/hw/ip_templates/racl_ctrl/dv/tb.sv.tpl b/hw/ip_templates/racl_ctrl/dv/tb.sv.tpl index 64d581e14956a..94ba72c558d03 100644 --- a/hw/ip_templates/racl_ctrl/dv/tb.sv.tpl +++ b/hw/ip_templates/racl_ctrl/dv/tb.sv.tpl @@ -10,7 +10,7 @@ module tb; // make sure that the EDA tool doesn't throw away the package at elaboration time, because we want // the classes inside it to exist so that their static variables cause them to be registered with // the UVM factory. - import racl_ctrl_test_pkg::racl_ctrl_base_test; + import ${module_instance_name}_test_pkg::${module_instance_name}_base_test; // macro includes `include "uvm_macros.svh" @@ -66,7 +66,7 @@ module tb; ); initial begin - import racl_ctrl_env_pkg::racl_ctrl_env_wrapper_cfg; + import ${module_instance_name}_env_pkg::racl_ctrl_env_wrapper_cfg; automatic racl_ctrl_env_wrapper_cfg wrapper_cfg = new(); diff --git a/hw/ip_templates/racl_ctrl/dv/tests/racl_ctrl_base_test.sv.tpl b/hw/ip_templates/racl_ctrl/dv/tests/racl_ctrl_base_test.sv.tpl new file mode 100644 index 0000000000000..8d8d49c5f0fdb --- /dev/null +++ b/hw/ip_templates/racl_ctrl/dv/tests/racl_ctrl_base_test.sv.tpl @@ -0,0 +1,44 @@ +// Copyright lowRISC contributors (OpenTitan project). +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 + +class ${module_instance_name}_base_test extends cip_base_test #(.CFG_T(${module_instance_name}_env_cfg), + .ENV_T(${module_instance_name}_env)); + + `uvm_component_utils(${module_instance_name}_base_test) + + extern function new (string name="", uvm_component parent=null); + extern function void build_phase(uvm_phase phase); + + // Configure the given sequence, installing sequencers for the error log agents. This test can + // only run (virtual) sequences that derive from ${module_instance_name}_base_vseq. + // + // Overridden from dv_base_test + extern function void configure_sequence(uvm_sequence seq); +endclass + +function ${module_instance_name}_base_test::new (string name="", uvm_component parent=null); + super.new(name, parent); +endfunction + +function void ${module_instance_name}_base_test::build_phase(uvm_phase phase); + dv_base_reg_pkg::dv_base_reg_block blk; + + super.build_phase(phase); + + // Configure the environment config to enable auto-prediction in the system register map for the + // register model. This means that the scoreboard in the environment will not need to manually + // call predict. + blk = cfg.ral_models[${module_instance_name}_ral_pkg::${module_instance_name}_reg_block::type_name]; + blk.default_map.get_root_map().set_auto_predict(); +endfunction + +function void ${module_instance_name}_base_test::configure_sequence(uvm_sequence seq); + ${module_instance_name}_base_vseq seq_; + `downcast(seq_, seq) + + super.configure_sequence(seq); + + seq_.internal_error_log_sequencer_h = env.internal_error_agent.sequencer; + seq_.external_error_log_sequencer_h = env.external_error_agent.sequencer; +endfunction diff --git a/hw/ip_templates/racl_ctrl/dv/tests/racl_ctrl_test.core.tpl b/hw/ip_templates/racl_ctrl/dv/tests/racl_ctrl_test.core.tpl new file mode 100644 index 0000000000000..5d3fd4b3993fd --- /dev/null +++ b/hw/ip_templates/racl_ctrl/dv/tests/racl_ctrl_test.core.tpl @@ -0,0 +1,19 @@ +CAPI=2: +# Copyright lowRISC contributors (OpenTitan project). +# Licensed under the Apache License, Version 2.0, see LICENSE for details. +# SPDX-License-Identifier: Apache-2.0 +name: ${instance_vlnv(f"lowrisc:dv:{module_instance_name}_test:0.1")} +description: "RACL_CTRL DV UVM test" +filesets: + files_dv: + depend: + - ${instance_vlnv(f"lowrisc:dv:{module_instance_name}_env")} + files: + - ${module_instance_name}_test_pkg.sv + - ${module_instance_name}_base_test.sv: {is_include_file: true} + file_type: systemVerilogSource + +targets: + default: + filesets: + - files_dv diff --git a/hw/ip_templates/racl_ctrl/dv/tests/racl_ctrl_test_pkg.sv.tpl b/hw/ip_templates/racl_ctrl/dv/tests/racl_ctrl_test_pkg.sv.tpl new file mode 100644 index 0000000000000..06edfeef9850c --- /dev/null +++ b/hw/ip_templates/racl_ctrl/dv/tests/racl_ctrl_test_pkg.sv.tpl @@ -0,0 +1,22 @@ +// Copyright lowRISC contributors (OpenTitan project). +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 + +package ${module_instance_name}_test_pkg; + // dep packages + import uvm_pkg::*; + import cip_base_pkg::*; + import ${module_instance_name}_env_pkg::*; + + // macro includes + `include "uvm_macros.svh" + `include "dv_macros.svh" + + // local types + + // functions + + // package sources + `include "${module_instance_name}_base_test.sv" + +endpackage diff --git a/hw/ip/racl_ctrl/dv/env/racl_ctrl_env.core b/hw/top_darjeeling/ip_autogen/racl_ctrl/dv/env/racl_ctrl_env.core similarity index 77% rename from hw/ip/racl_ctrl/dv/env/racl_ctrl_env.core rename to hw/top_darjeeling/ip_autogen/racl_ctrl/dv/env/racl_ctrl_env.core index 8d72dc97a6d41..7af85aa2a28ff 100644 --- a/hw/ip/racl_ctrl/dv/env/racl_ctrl_env.core +++ b/hw/top_darjeeling/ip_autogen/racl_ctrl/dv/env/racl_ctrl_env.core @@ -2,24 +2,22 @@ CAPI=2: # Copyright lowRISC contributors (OpenTitan project). # Licensed under the Apache License, Version 2.0, see LICENSE for details. # SPDX-License-Identifier: Apache-2.0 -name: "lowrisc:dv:racl_ctrl_env:0.1" +name: lowrisc:darjeeling_dv:racl_ctrl_env:0.1 description: "RACL_CTRL DV UVM environment" filesets: files_dv: depend: + - lowrisc:dv:ralgen - lowrisc:dv:cip_lib - - lowrisc:dv:racl_ctrl_ral - - lowrisc:dv:racl_error_log_agent + - lowrisc:dv:racl_ctrl_common + - lowrisc:darjeeling_ip:racl_ctrl:0.1 files: - - racl_ctrl_policies_if.sv - racl_ctrl_env_pkg.sv - - racl_ctrl_reg_window.sv: {is_include_file: true} - - racl_ctrl_env_wrapper_cfg.sv: {is_include_file: true} - racl_ctrl_env_cfg.sv: {is_include_file: true} - racl_ctrl_env_cov.sv: {is_include_file: true} - racl_ctrl_virtual_sequencer.sv: {is_include_file: true} - - racl_ctrl_scoreboard.sv: {is_include_file: true} - racl_ctrl_error_arb_predictor.sv: {is_include_file: true} + - racl_ctrl_scoreboard.sv: {is_include_file: true} - racl_ctrl_env.sv: {is_include_file: true} - seq_lib/racl_ctrl_vseq_list.sv: {is_include_file: true} - seq_lib/racl_ctrl_base_vseq.sv: {is_include_file: true} @@ -28,7 +26,17 @@ filesets: - seq_lib/racl_ctrl_stress_all_vseq.sv: {is_include_file: true} file_type: systemVerilogSource +generate: + ral: + generator: ralgen + parameters: + name: racl_ctrl + ip_hjson: ../../data/racl_ctrl.hjson + position: prepend + targets: default: filesets: - files_dv + generate: + - ral diff --git a/hw/ip/racl_ctrl/dv/env/racl_ctrl_env.sv b/hw/top_darjeeling/ip_autogen/racl_ctrl/dv/env/racl_ctrl_env.sv similarity index 100% rename from hw/ip/racl_ctrl/dv/env/racl_ctrl_env.sv rename to hw/top_darjeeling/ip_autogen/racl_ctrl/dv/env/racl_ctrl_env.sv diff --git a/hw/ip/racl_ctrl/dv/env/racl_ctrl_env_cfg.sv b/hw/top_darjeeling/ip_autogen/racl_ctrl/dv/env/racl_ctrl_env_cfg.sv similarity index 100% rename from hw/ip/racl_ctrl/dv/env/racl_ctrl_env_cfg.sv rename to hw/top_darjeeling/ip_autogen/racl_ctrl/dv/env/racl_ctrl_env_cfg.sv diff --git a/hw/ip/racl_ctrl/dv/env/racl_ctrl_env_cov.sv b/hw/top_darjeeling/ip_autogen/racl_ctrl/dv/env/racl_ctrl_env_cov.sv similarity index 100% rename from hw/ip/racl_ctrl/dv/env/racl_ctrl_env_cov.sv rename to hw/top_darjeeling/ip_autogen/racl_ctrl/dv/env/racl_ctrl_env_cov.sv diff --git a/hw/ip/racl_ctrl/dv/env/racl_ctrl_env_pkg.sv b/hw/top_darjeeling/ip_autogen/racl_ctrl/dv/env/racl_ctrl_env_pkg.sv similarity index 100% rename from hw/ip/racl_ctrl/dv/env/racl_ctrl_env_pkg.sv rename to hw/top_darjeeling/ip_autogen/racl_ctrl/dv/env/racl_ctrl_env_pkg.sv diff --git a/hw/ip/racl_ctrl/dv/env/racl_ctrl_error_arb_predictor.sv b/hw/top_darjeeling/ip_autogen/racl_ctrl/dv/env/racl_ctrl_error_arb_predictor.sv similarity index 98% rename from hw/ip/racl_ctrl/dv/env/racl_ctrl_error_arb_predictor.sv rename to hw/top_darjeeling/ip_autogen/racl_ctrl/dv/env/racl_ctrl_error_arb_predictor.sv index 8348cf3742b20..23fc5e1ef1517 100644 --- a/hw/ip/racl_ctrl/dv/env/racl_ctrl_error_arb_predictor.sv +++ b/hw/top_darjeeling/ip_autogen/racl_ctrl/dv/env/racl_ctrl_error_arb_predictor.sv @@ -8,7 +8,7 @@ class racl_ctrl_error_arb_predictor extends uvm_component; `uvm_component_utils(racl_ctrl_error_arb_predictor) - typedef uvm_tlm_analysis_fifo #(racl_error_log_item) error_fifo_t; + typedef uvm_tlm_analysis_fifo #(racl_error_log_item) error_fifo_t; typedef uvm_tlm_analysis_fifo #(racl_error_log_vec_item) error_vec_fifo_t; // A handle to the environment cfg diff --git a/hw/ip/racl_ctrl/dv/env/racl_ctrl_scoreboard.sv b/hw/top_darjeeling/ip_autogen/racl_ctrl/dv/env/racl_ctrl_scoreboard.sv similarity index 100% rename from hw/ip/racl_ctrl/dv/env/racl_ctrl_scoreboard.sv rename to hw/top_darjeeling/ip_autogen/racl_ctrl/dv/env/racl_ctrl_scoreboard.sv diff --git a/hw/ip/racl_ctrl/dv/env/racl_ctrl_virtual_sequencer.sv b/hw/top_darjeeling/ip_autogen/racl_ctrl/dv/env/racl_ctrl_virtual_sequencer.sv similarity index 100% rename from hw/ip/racl_ctrl/dv/env/racl_ctrl_virtual_sequencer.sv rename to hw/top_darjeeling/ip_autogen/racl_ctrl/dv/env/racl_ctrl_virtual_sequencer.sv diff --git a/hw/ip/racl_ctrl/dv/env/seq_lib/racl_ctrl_base_vseq.sv b/hw/top_darjeeling/ip_autogen/racl_ctrl/dv/env/seq_lib/racl_ctrl_base_vseq.sv similarity index 100% rename from hw/ip/racl_ctrl/dv/env/seq_lib/racl_ctrl_base_vseq.sv rename to hw/top_darjeeling/ip_autogen/racl_ctrl/dv/env/seq_lib/racl_ctrl_base_vseq.sv diff --git a/hw/ip/racl_ctrl/dv/env/seq_lib/racl_ctrl_common_vseq.sv b/hw/top_darjeeling/ip_autogen/racl_ctrl/dv/env/seq_lib/racl_ctrl_common_vseq.sv similarity index 100% rename from hw/ip/racl_ctrl/dv/env/seq_lib/racl_ctrl_common_vseq.sv rename to hw/top_darjeeling/ip_autogen/racl_ctrl/dv/env/seq_lib/racl_ctrl_common_vseq.sv diff --git a/hw/ip/racl_ctrl/dv/env/seq_lib/racl_ctrl_smoke_vseq.sv b/hw/top_darjeeling/ip_autogen/racl_ctrl/dv/env/seq_lib/racl_ctrl_smoke_vseq.sv similarity index 100% rename from hw/ip/racl_ctrl/dv/env/seq_lib/racl_ctrl_smoke_vseq.sv rename to hw/top_darjeeling/ip_autogen/racl_ctrl/dv/env/seq_lib/racl_ctrl_smoke_vseq.sv diff --git a/hw/ip/racl_ctrl/dv/env/seq_lib/racl_ctrl_stress_all_vseq.sv b/hw/top_darjeeling/ip_autogen/racl_ctrl/dv/env/seq_lib/racl_ctrl_stress_all_vseq.sv similarity index 100% rename from hw/ip/racl_ctrl/dv/env/seq_lib/racl_ctrl_stress_all_vseq.sv rename to hw/top_darjeeling/ip_autogen/racl_ctrl/dv/env/seq_lib/racl_ctrl_stress_all_vseq.sv diff --git a/hw/ip/racl_ctrl/dv/env/seq_lib/racl_ctrl_vseq_list.sv b/hw/top_darjeeling/ip_autogen/racl_ctrl/dv/env/seq_lib/racl_ctrl_vseq_list.sv similarity index 100% rename from hw/ip/racl_ctrl/dv/env/seq_lib/racl_ctrl_vseq_list.sv rename to hw/top_darjeeling/ip_autogen/racl_ctrl/dv/env/seq_lib/racl_ctrl_vseq_list.sv diff --git a/hw/top_darjeeling/ip_autogen/racl_ctrl/dv/racl_ctrl_ral.core b/hw/top_darjeeling/ip_autogen/racl_ctrl/dv/racl_ctrl_ral.core deleted file mode 100644 index 55a7dd356916f..0000000000000 --- a/hw/top_darjeeling/ip_autogen/racl_ctrl/dv/racl_ctrl_ral.core +++ /dev/null @@ -1,28 +0,0 @@ -CAPI=2: -# Copyright lowRISC contributors (OpenTitan project). -# Licensed under the Apache License, Version 2.0, see LICENSE for details. -# SPDX-License-Identifier: Apache-2.0 -name: lowrisc:darjeeling_dv:racl_ctrl_ral:0.1 -description: "RACL_CTRL register model" -virtual: - - "lowrisc:dv:racl_ctrl_ral" -filesets: - ral_dep: - depend: - - lowrisc:dv:ralgen - - lowrisc:dv:dv_base_reg - -generate: - ral: - generator: ralgen - parameters: - name: racl_ctrl - ip_hjson: ../data/racl_ctrl.hjson - position: prepend - -targets: - default: - filesets: - - ral_dep - generate: - - ral diff --git a/hw/top_darjeeling/ip_autogen/racl_ctrl/dv/racl_ctrl_sim.core b/hw/top_darjeeling/ip_autogen/racl_ctrl/dv/racl_ctrl_sim.core index 81bd9c5c340b6..e1a2b55aa2f4d 100644 --- a/hw/top_darjeeling/ip_autogen/racl_ctrl/dv/racl_ctrl_sim.core +++ b/hw/top_darjeeling/ip_autogen/racl_ctrl/dv/racl_ctrl_sim.core @@ -12,9 +12,9 @@ filesets: files_dv: depend: - - lowrisc:dv:racl_ctrl_test - lowrisc:dv:racl_error_log_agent - lowrisc:darjeeling_dv:racl_ctrl_sva + - lowrisc:darjeeling_dv:racl_ctrl_test files: - tb.sv file_type: systemVerilogSource diff --git a/hw/top_darjeeling/ip_autogen/racl_ctrl/dv/racl_ctrl_sim_cfg.hjson b/hw/top_darjeeling/ip_autogen/racl_ctrl/dv/racl_ctrl_sim_cfg.hjson index 7fc640882154d..015498c405ae4 100644 --- a/hw/top_darjeeling/ip_autogen/racl_ctrl/dv/racl_ctrl_sim_cfg.hjson +++ b/hw/top_darjeeling/ip_autogen/racl_ctrl/dv/racl_ctrl_sim_cfg.hjson @@ -2,25 +2,60 @@ // Licensed under the Apache License, Version 2.0, see LICENSE for details. // SPDX-License-Identifier: Apache-2.0 { + // Name of the sim cfg - typically same as the name of the DUT. + name: racl_ctrl + + // Top level dut name (sv module). + dut: racl_ctrl + + // Top level testbench name (sv module). + tb: tb + + // Simulator used to sign off this block + tool: xcelium + // Fusesoc core file used for building the file list. fusesoc_core: lowrisc:darjeeling_dv:racl_ctrl_sim:0.1 - // Testplan hjson file. Note it is not templated (even though this sim_cfg file is) - testplan: "{proj_root}/hw/ip/racl_ctrl/data/racl_ctrl_testplan.hjson" + // Testplan hjson file. + testplan: "{self_dir}/../data/racl_ctrl_testplan.hjson" - // RAL spec - used to generate the RAL model. - ral_spec: "{self_dir}/../data/racl_ctrl.hjson" + // Import additional common sim cfg files. + import_cfgs: [// Project wide common sim cfg file + "{proj_root}/hw/dv/tools/dvsim/common_sim_cfg.hjson", + // Common CIP test lists + "{proj_root}/hw/dv/tools/dvsim/tests/csr_tests.hjson", + "{proj_root}/hw/dv/tools/dvsim/tests/intr_test.hjson", + "{proj_root}/hw/dv/tools/dvsim/tests/alert_test.hjson", + "{proj_root}/hw/dv/tools/dvsim/tests/shadow_reg_errors_tests.hjson", + "{proj_root}/hw/dv/tools/dvsim/tests/tl_access_tests.hjson", + "{proj_root}/hw/dv/tools/dvsim/tests/stress_tests.hjson"] - // Top level dut module name - dut: racl_ctrl + // Add additional tops for simulation. + sim_tops: ["racl_ctrl_bind"] - // The "testbench name" that dvsim attaches to all the tests in this configuration - name: racl_ctrl + // Default iterations for all tests - each test entry can override this. + reseed: 50 - // Tell the tool to include the bind module as a top-level. Since the name of the bind module is - // templated, this needs to be done here. - sim_tops: ["racl_ctrl_bind"] + // Default UVM test and seq class name. + uvm_test: racl_ctrl_base_test + uvm_test_seq: racl_ctrl_base_vseq + + // List of test specifications. + tests: [ + { + name: racl_ctrl_smoke + uvm_test_seq: racl_ctrl_smoke_vseq + } + + // TODO: add more tests here + ] - // Import the underlying sim_cfg (not templated) - import_cfgs: ["{proj_root}/hw/ip/racl_ctrl/dv/racl_ctrl_tests.hjson"] + // List of regressions. + regressions: [ + { + name: smoke + tests: ["racl_ctrl_smoke"] + } + ] } diff --git a/hw/ip/racl_ctrl/dv/tests/racl_ctrl_base_test.sv b/hw/top_darjeeling/ip_autogen/racl_ctrl/dv/tests/racl_ctrl_base_test.sv similarity index 100% rename from hw/ip/racl_ctrl/dv/tests/racl_ctrl_base_test.sv rename to hw/top_darjeeling/ip_autogen/racl_ctrl/dv/tests/racl_ctrl_base_test.sv diff --git a/hw/ip/racl_ctrl/dv/tests/racl_ctrl_test.core b/hw/top_darjeeling/ip_autogen/racl_ctrl/dv/tests/racl_ctrl_test.core similarity index 82% rename from hw/ip/racl_ctrl/dv/tests/racl_ctrl_test.core rename to hw/top_darjeeling/ip_autogen/racl_ctrl/dv/tests/racl_ctrl_test.core index 785596ad44b19..415381921298b 100644 --- a/hw/ip/racl_ctrl/dv/tests/racl_ctrl_test.core +++ b/hw/top_darjeeling/ip_autogen/racl_ctrl/dv/tests/racl_ctrl_test.core @@ -2,12 +2,12 @@ CAPI=2: # Copyright lowRISC contributors (OpenTitan project). # Licensed under the Apache License, Version 2.0, see LICENSE for details. # SPDX-License-Identifier: Apache-2.0 -name: "lowrisc:dv:racl_ctrl_test:0.1" +name: lowrisc:darjeeling_dv:racl_ctrl_test:0.1 description: "RACL_CTRL DV UVM test" filesets: files_dv: depend: - - lowrisc:dv:racl_ctrl_env + - lowrisc:darjeeling_dv:racl_ctrl_env files: - racl_ctrl_test_pkg.sv - racl_ctrl_base_test.sv: {is_include_file: true} diff --git a/hw/ip/racl_ctrl/dv/tests/racl_ctrl_test_pkg.sv b/hw/top_darjeeling/ip_autogen/racl_ctrl/dv/tests/racl_ctrl_test_pkg.sv similarity index 100% rename from hw/ip/racl_ctrl/dv/tests/racl_ctrl_test_pkg.sv rename to hw/top_darjeeling/ip_autogen/racl_ctrl/dv/tests/racl_ctrl_test_pkg.sv