`timescale 1ns/1ps
`include "uvm_macros.svh"
import uvm_pkg::*;

class lifecycle_catcher extends uvm_report_catcher;
  int overflow, phase_error;
  function new(); super.new("catcher"); endfunction
  function action_e catch();
    if (uvm_is_match("*Response queue overflow*", get_message())) begin
      overflow++; return CAUGHT;
    end
    if (get_id() inside {"UVM/SEQ/LOCK_DEPR", "UVM/SEQ/SP/GET", "UVM/SEQ/SP/SET", "UVM/GET_TO_LOCK_DAP/SAG"}) begin
      phase_error++; return CAUGHT;
    end
    return THROW;
  endfunction
endclass

class lifecycle_sequence extends uvm_sequence#(uvm_sequence_item);
  bit hold_after_grant, granted, route_lock, locked;
  uvm_sequencer_base other;
  function new(string name="seq"); super.new(name); endfunction
  function void overflow_now();
    uvm_sequence_item response = new("response");
    set_response_queue_depth(0);
    put_response(response);
  endfunction
  task body();
    if (route_lock) begin
      lock(other); locked = 1; wait(0);
    end else begin
      req = new("req");
      start_item(req);
      granted = 1;
      if (hold_after_grant) wait(0);
      finish_item(req);
    end
  endtask
endclass

module lifecycle_probe;
  initial begin
    lifecycle_catcher catcher = new();
    lifecycle_sequence controls = new("controls"), routing = new("routing");
    lifecycle_sequence bad = new("bad"), good = new("good");
    uvm_sequencer#(uvm_sequence_item) primary = new("primary", null);
    uvm_sequencer#(uvm_sequence_item) other = new("other", null);
    uvm_sequencer#(uvm_sequence_item) retry_sqr = new("retry", null);
    uvm_sequence_item received;
    uvm_phase phase_a = uvm_run_phase::get(), phase_b = uvm_reset_phase::get(), locked_phase;
    bit completed;
    uvm_report_cb::add(null, catcher);
    $display("REVIEW|default_response_disabled|%0d", controls.get_response_queue_error_report_disabled());
    controls.overflow_now();
    $display("REVIEW|default_overflow_errors|%0d", catcher.overflow);
    catcher.overflow = 0;
    controls.set_response_queue_error_report_disabled(0);
    controls.overflow_now();
    $display("REVIEW|explicit_overflow_errors|%0d", catcher.overflow);
    controls.set_starting_phase(phase_a);
    locked_phase = controls.get_starting_phase();
    controls.set_starting_phase(phase_b);
    locked_phase = controls.get_starting_phase();
    $display("REVIEW|locked_phase_preserved|%0d", locked_phase == phase_a);
    $display("REVIEW|locked_phase_error|%0d", catcher.phase_error > 0);

    routing.route_lock = 1;
    routing.other = other;
    fork routing.start(primary); join_none
    wait(routing.locked);
    routing.kill();
    #1;
    $display("REVIEW|cross_sequencer_lock_after_kill|%0d", other.is_grabbed());

    bad.hold_after_grant = 1;
    fork : retry_workers
      bad.start(retry_sqr, null, 200);
      good.start(retry_sqr, null, 100);
      begin
        #1;
        retry_sqr.get_next_item(received);
        retry_sqr.item_done();
        completed = 1;
      end
      begin wait(bad.granted); bad.kill(); end
    join_none
    #5;
    $display("REVIEW|retry_after_grant_kill|%0d", completed);
    good.kill();
    disable retry_workers;
    $display("REVIEW|done|1");
    $finish;
  end
  initial begin #50; $fatal(1, "lifecycle timeout"); end
endmodule
