// Copyright lowRISC contributors (OpenTitan project).
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0

class dv_base_test #(type CFG_T = dv_base_env_cfg,
                     type ENV_T = dv_base_env) extends uvm_test;
  `uvm_component_param_utils(dv_base_test #(CFG_T, ENV_T))

  ENV_T             env;
  CFG_T             cfg;
  bit               run_test_seq = 1'b1;
  string            test_seq_s;

  uint   max_quit_count  = 1;
  uint64 test_timeout_ns = 200_000_000; // 200ms
  uint   drain_time_ns   = 2_000;  // 2us
  bit    poll_for_stop   = 1'b0;
  uint   poll_for_stop_interval_ns = 1000;
  bit    print_topology  = 1'b0;

  `uvm_component_new

  virtual function void build_phase(uvm_phase phase);
    dv_report_server  m_dv_report_server = new();
    dv_report_catcher m_report_catcher;
    uvm_report_server::set_server(m_dv_report_server);

    // Message catcher/demoter
    `uvm_create_obj(dv_report_catcher, m_report_catcher)
    // Add demoted messages - we need to do this here to catch build warnings
    add_message_demotes(m_report_catcher);
    // Register catcher
    uvm_report_cb::add(null, m_report_catcher);

    super.build_phase(phase);

    env = ENV_T::type_id::create("env", this);
    cfg = CFG_T::type_id::create("cfg", this);
    void'($value$plusargs("use_jtag_dmi=%0b", cfg.use_jtag_dmi));
    cfg.initialize();
    `DV_CHECK_RANDOMIZE_FATAL(cfg)
    uvm_config_db#(CFG_T)::set(this, "env", "cfg", cfg);

    // Enable scoreboard (and sub-scoreboard checks) via plusarg.
    void'($value$plusargs("en_scb=%0b", cfg.en_scb));
    void'($value$plusargs("en_scb_tl_err_chk=%0b", cfg.en_scb_tl_err_chk));
    void'($value$plusargs("en_scb_mem_chk=%0b", cfg.en_scb_mem_chk));
    // Enable fastest design performance by configuring zero delays in all agents.
    void'($value$plusargs("zero_delays=%0b", cfg.zero_delays));

    // Enable coverage collection.
    void'($value$plusargs("en_cov=%0b", cfg.en_cov));

    // Enable reduced runtime test.
    void'($value$plusargs("smoke_test=%0b", cfg.smoke_test));

    // Enable print_topology
    void'($value$plusargs("print_topology=%0b", print_topology));
    uvm_top.enable_print_topology = print_topology;

    void'($value$plusargs("cdc_instrumentation_enabled=%d", cfg.en_dv_cdc));
  endfunction : build_phase

  virtual function void end_of_elaboration_phase(uvm_phase phase);
    super.end_of_elaboration_phase(phase);
    void'($value$plusargs("max_quit_count=%0d", max_quit_count));
    set_max_quit_count(max_quit_count);
    void'($value$plusargs("test_timeout_ns=%0d", test_timeout_ns));
    uvm_top.set_timeout((test_timeout_ns * 1ns));
  endfunction : end_of_elaboration_phase

  virtual task run_phase(uvm_phase phase);
    super.run_phase(phase);
    void'($value$plusargs("drain_time_ns=%0d", drain_time_ns));
    phase.phase_done.set_drain_time(this, (drain_time_ns * 1ns));
    void'($value$plusargs("poll_for_stop=%0b", poll_for_stop));
    void'($value$plusargs("poll_for_stop_interval_ns=%0d", poll_for_stop_interval_ns));
    if (poll_for_stop) dv_utils_pkg::poll_for_stop(.interval_ns(poll_for_stop_interval_ns));
    void'($value$plusargs("UVM_TEST_SEQ=%0s", test_seq_s));
    if (run_test_seq) begin
      run_seq(test_seq_s, phase);
    end
  endtask : run_phase

  // Add message demotes here - hook to use by extended tests
  virtual function void add_message_demotes(dv_report_catcher catcher);
  endfunction

  virtual task run_seq(string test_seq_s, uvm_phase phase);
    uvm_sequence test_seq = create_seq_by_name(test_seq_s);

    configure_sequence(test_seq);

    `DV_CHECK_RANDOMIZE_FATAL(test_seq)

    `uvm_info(`gfn, {"Starting test sequence ", test_seq_s}, UVM_MEDIUM)
    phase.raise_objection(this, $sformatf("%s objection raised", `gn));
    test_seq.start(env.virtual_sequencer);
    phase.drop_objection(this, $sformatf("%s objection dropped", `gn));
    `uvm_info(`gfn, {"Finished test sequence ", test_seq_s}, UVM_MEDIUM)
  endtask

  // A virtual function that allows the test to set up the sequence to know about the sequencer
  // where it should run. This runs before the sequence is randomised, which allows the sequence to
  // constrain its randomisation using the environment's cfg handle (getting it through p_sequencer).
  //
  // The base class version of this function registers env.virtual_sequencer. If a testbench wishes
  // to register multiple sequencers with a virtual sequence, it can do so by overriding this
  // function.
  virtual function void configure_sequence(uvm_sequence seq);
    seq.set_sequencer(env.virtual_sequencer);
  endfunction

endclass : dv_base_test
