diff --git a/dv/uvm/icache/data/ibex_icache_testplan.hjson b/dv/uvm/icache/data/ibex_icache_testplan.hjson index 71cf2334..6ccd1d83 100644 --- a/dv/uvm/icache/data/ibex_icache_testplan.hjson +++ b/dv/uvm/icache/data/ibex_icache_testplan.hjson @@ -138,7 +138,7 @@ Tests are selected from the sequences above. Add occasional resets (in the middle of sequences)''' milestone: V2 - tests: [] + tests: ["ibex_icache_stress_all_with_reset"] } ] } diff --git a/dv/uvm/icache/dv/env/ibex_icache_env.core b/dv/uvm/icache/dv/env/ibex_icache_env.core index c8ec2a28..e61837e6 100644 --- a/dv/uvm/icache/dv/env/ibex_icache_env.core +++ b/dv/uvm/icache/dv/env/ibex_icache_env.core @@ -28,6 +28,7 @@ filesets: - seq_lib/ibex_icache_many_errors_vseq.sv: {is_include_file: true} - seq_lib/ibex_icache_ecc_vseq.sv: {is_include_file: true} - seq_lib/ibex_icache_combo_vseq.sv: {is_include_file: true} + - seq_lib/ibex_icache_reset_vseq.sv: {is_include_file: true} file_type: systemVerilogSource targets: diff --git a/dv/uvm/icache/dv/env/ibex_icache_env.sv b/dv/uvm/icache/dv/env/ibex_icache_env.sv index 29d19e7c..97f4515a 100644 --- a/dv/uvm/icache/dv/env/ibex_icache_env.sv +++ b/dv/uvm/icache/dv/env/ibex_icache_env.sv @@ -113,8 +113,8 @@ class ibex_icache_env extends dv_base_env #( // is triggered. heartbeat.start(hb_event); forever begin - // Every 2000 clocks, check the heartbeat monitor - cfg.core_agent_cfg.vif.wait_clks(2000); + // Every 2000 clocks, check the heartbeat monitor (not stopping early on reset) + cfg.core_agent_cfg.vif.wait_clks(2000, 1'b0); hb_event.trigger(); end endtask diff --git a/dv/uvm/icache/dv/env/seq_lib/ibex_icache_combo_vseq.sv b/dv/uvm/icache/dv/env/seq_lib/ibex_icache_combo_vseq.sv index cd752ab7..3eb0c9e2 100644 --- a/dv/uvm/icache/dv/env/seq_lib/ibex_icache_combo_vseq.sv +++ b/dv/uvm/icache/dv/env/seq_lib/ibex_icache_combo_vseq.sv @@ -27,6 +27,10 @@ class ibex_icache_combo_vseq "ibex_icache_many_errors_vseq", "ibex_icache_passthru_vseq"}; + // If this is set, occasionally reset the DUT and start a new sequence at a time that the core + // sequence wouldn't normally expect. + bit random_reset = 1'b0; + // How many sequences have we executed so far? int unsigned seqs_so_far = 0; @@ -53,7 +57,13 @@ class ibex_icache_combo_vseq // little. trans_now = $urandom_range(50, 100); - should_reset = $urandom_range(0, 1); + // We don't need to reset if seq_idx == 0 (because we did a reset before starting this task). + // Otherwise, we always reset between sequences if random_reset is true. We'd always need to + // if we killed the previous sequence, and it's easier not to bother tracking properly. If + // random_reset is false (the usual back-to-back sequence test), we reset 1 time in 2. + if (seq_idx == 0) should_reset = 1'b0; + else if (random_reset) should_reset = 1'b1; + else should_reset = $urandom_range(0, 1); `uvm_info(`gfn, $sformatf("Running sequence '%s' (%0d transactions; reset=%0d).", @@ -70,7 +80,15 @@ class ibex_icache_combo_vseq child_seq.do_dut_init = should_reset; child_seq.prev_sequence = prev_seq; - child_seq.start(p_sequencer, this); + // The memory agent is careful to avoid consuming requests from its sequencer's request_fifo + // until it has handled them. This is important if we're not resetting (it essentially allows + // the sequence to change under the feet of the rest of the environment without it noticing), + // but we need to make sure we discard any pending requests on an actual reset. Do that here. + if (should_reset && seqs_so_far > 0) begin + p_sequencer.mem_sequencer_h.request_fifo.flush(); + end + + run_sequence(); prev_seq = child_seq; trans_so_far += trans_now; @@ -78,4 +96,33 @@ class ibex_icache_combo_vseq end endtask : body + // Run a sequence. If random_reset = 0, this will run to completion. Otherwise, the sequence may + // be stopped early. + protected task run_sequence(); + int unsigned cycles_till_reset; + bit reached_timeout = 1'b0; + + if (!random_reset) begin + child_seq.start(p_sequencer, this); + end else begin + `DV_CHECK_STD_RANDOMIZE_WITH_FATAL(cycles_till_reset, + cycles_till_reset dist { + [100:500] :/ 1, + [501:1000] :/ 4 + };) + fork + child_seq.start(p_sequencer, this); + begin + repeat (cycles_till_reset) @(cfg.clk_rst_vif.cb); + reached_timeout = 1'b1; + end + join_any + end + + if (reached_timeout) child_seq.kill(); + + // Kill the timer process if it's still going. + disable fork; + endtask : run_sequence + endclass : ibex_icache_combo_vseq diff --git a/dv/uvm/icache/dv/env/seq_lib/ibex_icache_reset_vseq.sv b/dv/uvm/icache/dv/env/seq_lib/ibex_icache_reset_vseq.sv new file mode 100644 index 00000000..14526ada --- /dev/null +++ b/dv/uvm/icache/dv/env/seq_lib/ibex_icache_reset_vseq.sv @@ -0,0 +1,20 @@ +// Copyright lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 + +// A version of the combination vseq that injects resets at unexpected times. + +class ibex_icache_reset_vseq extends ibex_icache_combo_vseq; + + `uvm_object_utils(ibex_icache_reset_vseq) + `uvm_object_new + + task pre_do (bit is_item); + super.pre_do(is_item); + + // Enable "random reset" functionality, which resets the DUT (and starts a new sequence) at + // random times. + random_reset = 1'b1; + endtask : pre_do + +endclass : ibex_icache_reset_vseq diff --git a/dv/uvm/icache/dv/env/seq_lib/ibex_icache_vseq_list.sv b/dv/uvm/icache/dv/env/seq_lib/ibex_icache_vseq_list.sv index e9f25bb8..ed24e246 100644 --- a/dv/uvm/icache/dv/env/seq_lib/ibex_icache_vseq_list.sv +++ b/dv/uvm/icache/dv/env/seq_lib/ibex_icache_vseq_list.sv @@ -11,3 +11,4 @@ `include "ibex_icache_many_errors_vseq.sv" `include "ibex_icache_ecc_vseq.sv" `include "ibex_icache_combo_vseq.sv" +`include "ibex_icache_reset_vseq.sv" diff --git a/dv/uvm/icache/dv/ibex_icache_core_agent/ibex_icache_core_driver.sv b/dv/uvm/icache/dv/ibex_icache_core_agent/ibex_icache_core_driver.sv index 2de68b58..8f708973 100644 --- a/dv/uvm/icache/dv/ibex_icache_core_agent/ibex_icache_core_driver.sv +++ b/dv/uvm/icache/dv/ibex_icache_core_agent/ibex_icache_core_driver.sv @@ -108,7 +108,8 @@ class ibex_icache_core_driver cfg.vif.driver_cb.enable <= req.enable; // Lower req, invalidate and set new seed immediately. After req_low_cycles cycles, start - // reading instructions. Wiggle the branch_spec line the whole time. + // reading instructions (providing there hasn't been a reset). Wiggle the branch_spec line the + // whole time. fork begin fork @@ -116,7 +117,7 @@ class ibex_icache_core_driver if (req.invalidate) invalidate(); if (req.new_seed != 0) drive_new_seed(req.new_seed); join - read_insns(rsp, req.num_insns); + if (cfg.vif.rst_n) read_insns(rsp, req.num_insns); end drive_branch_spec(); join_any @@ -136,6 +137,8 @@ class ibex_icache_core_driver rsp.saw_error = 1'b1; break; end + // Return early on reset + if (!cfg.vif.rst_n) break; end endtask @@ -151,30 +154,34 @@ class ibex_icache_core_driver virtual task automatic read_insn(); int unsigned delay; - // Maybe (1 time in 10) wait for a valid signal before even considering asserting ready. - if ($urandom_range(9) == 0) - wait (cfg.vif.driver_cb.valid); + // Maybe (1 time in 10) wait for a valid signal before even considering asserting ready. Stops + // early on reset. + if ($urandom_range(9) == 0) begin + cfg.vif.wait_valid(); + if (!cfg.vif.rst_n) + return; + end // Then pick how long we wait before asserting that we are ready. // // TODO: Make this configurable and weight 0 more heavily. cfg.vif.wait_clks($urandom_range(3)); - // Assert ready and then wait until valid + // Assert ready and then wait until valid. If we see a reset, we stop early cfg.vif.driver_cb.ready <= 1'b1; while (1'b1) begin - @(cfg.vif.driver_cb); - if (cfg.vif.driver_cb.valid) + @(cfg.vif.driver_cb or negedge cfg.vif.rst_n); + if (cfg.vif.driver_cb.valid || !cfg.vif.rst_n) break; end cfg.vif.driver_cb.ready <= 1'b0; endtask - // Lower the req line for the given number of cycles + // Lower the req line for the given number of cycles. Returns early on reset virtual task automatic lower_req(int unsigned num_cycles); cfg.vif.driver_cb.req <= 1'b0; - repeat (num_cycles) @(cfg.vif.driver_cb); + cfg.vif.wait_clks(num_cycles); cfg.vif.driver_cb.req <= 1'b1; endtask diff --git a/dv/uvm/icache/dv/ibex_icache_core_agent/ibex_icache_core_if.sv b/dv/uvm/icache/dv/ibex_icache_core_agent/ibex_icache_core_if.sv index 9be8583a..3bf8a625 100644 --- a/dv/uvm/icache/dv/ibex_icache_core_agent/ibex_icache_core_if.sv +++ b/dv/uvm/icache/dv/ibex_icache_core_agent/ibex_icache_core_if.sv @@ -95,9 +95,28 @@ interface ibex_icache_core_if (input clk, input rst_n); driver_cb.invalidate <= 1'b0; endtask - // A task that waits for num_clks posedges on the clk signal - task automatic wait_clks(int num_clks); - repeat (num_clks) @(driver_cb); + // A task that waits for num_clks posedges on the clk signal. Returns early on reset if + // stop_on_reset is true. + task automatic wait_clks(int num_clks, bit stop_on_reset = 1'b1); + if (stop_on_reset && !rst_n) return; + if (stop_on_reset) begin + fork + repeat (num_clks) @(driver_cb); + @(negedge rst_n); + join_any + disable fork; + end else begin + repeat (num_clks) @(driver_cb); + end + endtask + + // Wait until the valid signal goes high. Returns early on reset + task automatic wait_valid(); + fork + @(negedge rst_n); + wait (driver_cb.valid); + join_any + disable fork; endtask // Reset all the signals from the core to the cache (the other direction is controlled by the diff --git a/dv/uvm/icache/dv/ibex_icache_mem_agent/ibex_icache_mem_if.sv b/dv/uvm/icache/dv/ibex_icache_mem_agent/ibex_icache_mem_if.sv index f3ff1ef9..ff2e1d1f 100644 --- a/dv/uvm/icache/dv/ibex_icache_mem_agent/ibex_icache_mem_if.sv +++ b/dv/uvm/icache/dv/ibex_icache_mem_agent/ibex_icache_mem_if.sv @@ -55,9 +55,14 @@ interface ibex_icache_mem_if (input clk, driver_cb.gnt <= 1'b0; endtask - // Wait for num_clks posedges on the clk signal + // Wait for num_clks posedges on the clk signal. Returns early on a reset. task automatic wait_clks(int num_clks); - repeat (num_clks) @(driver_cb); + if (!rst_n) return; + fork + repeat (num_clks) @(driver_cb); + @(negedge rst_n); + join_any + disable fork; endtask // Drive a response with the given rdata and possible error signals for a single cycle diff --git a/dv/uvm/icache/dv/ibex_icache_sim_cfg.hjson b/dv/uvm/icache/dv/ibex_icache_sim_cfg.hjson index 3f654b15..17742dfb 100644 --- a/dv/uvm/icache/dv/ibex_icache_sim_cfg.hjson +++ b/dv/uvm/icache/dv/ibex_icache_sim_cfg.hjson @@ -101,6 +101,11 @@ name: ibex_icache_stress_all uvm_test_seq: ibex_icache_combo_vseq } + + { + name: ibex_icache_stress_all_with_reset + uvm_test_seq: ibex_icache_reset_vseq + } ] // List of regressions. @@ -114,7 +119,8 @@ "ibex_icache_back_line", "ibex_icache_many_errors", "ibex_icache_ecc", - "ibex_icache_stress_all"] + "ibex_icache_stress_all", + "ibex_icache_stress_all_with_reset"] } ] }