`timescale 1ns/1ps
`include "uvm_macros.svh"
import uvm_pkg::*;
class predictor_bus extends uvm_sequence_item;
  `uvm_object_utils(predictor_bus)
  uvm_reg_addr_t address;
  uvm_reg_data_t data;
  uvm_status_e status;
  function new(string name="bus"); super.new(name); endfunction
endclass
class predictor_adapter extends uvm_reg_adapter;
  function new(); super.new("adapter"); endfunction
  virtual function uvm_sequence_item reg2bus(const ref uvm_reg_bus_op rw);
    return null;
  endfunction
  virtual function void bus2reg(uvm_sequence_item item, ref uvm_reg_bus_op rw);
    predictor_bus bus;
    if (!$cast(bus,item)) `uvm_fatal("CAST", "Expected predictor_bus")
    rw.kind = UVM_WRITE;
    rw.addr = bus.address;
    rw.data = bus.data;
    rw.status = bus.status;
    rw.byte_en = -1;
  endfunction
endclass
class predictor_register extends uvm_reg;
  uvm_reg_field field_value;
  function new(); super.new("value",64,UVM_NO_COVERAGE); endfunction
  function void build();
    field_value = new("field_value");
    field_value.configure(this,64,0,"RW",0,0,1,0,0);
  endfunction
endclass
class predictor_block extends uvm_reg_block;
  predictor_register value;
  function new(); super.new("model", UVM_NO_COVERAGE); endfunction
  function void build();
    default_map = create_map("map",0,4,UVM_LITTLE_ENDIAN);
    value = new();
    value.configure(this);
    value.build();
    default_map.add_reg(value,0,"RW");
    lock_model();
  endfunction
endclass
class observed_predictor extends uvm_reg_predictor#(predictor_bus);
  uvm_status_e observed_status;
  function new(); super.new("predictor",null); endfunction
  virtual function void pre_predict(uvm_reg_item rw);
    observed_status = rw.status;
  endfunction
endclass
module predictor_probe;
  initial begin
    predictor_block model = new();
    predictor_adapter adapter = new();
    observed_predictor predictor_value = new();
    predictor_bus bus = new();
    bit all_ok;
    uvm_reg_data_t assembled;
    model.build();
    predictor_value.adapter = adapter;
    predictor_value.map = model.default_map;
    for (int failure=0; failure<3; failure++) begin
      void'(model.value.predict('h5a));
      all_ok = 1;
      assembled = 0;
      for (int beat=0; beat<2; beat++) begin
        bus.address = beat*4;
        bus.data = beat == 0 ? 'h89abcdef : 'h01234567;
        bus.status = beat == failure ? UVM_NOT_OK : UVM_IS_OK;
        all_ok &= bus.status == UVM_IS_OK;
        assembled |= bus.data << (32*beat);
        if (!$test$plusargs("MIGRATED")) predictor_value.write(bus);
      end
      // Bounded alternative: complete a monitor transaction before predicting it.
      if ($test$plusargs("MIGRATED") && all_ok)
        void'(model.value.predict(assembled,-1,UVM_PREDICT_WRITE));
      $display("REVIEW|failure_%0d_mirror|%016h", failure, model.value.get_mirrored_value());
      if (!$test$plusargs("MIGRATED"))
        $display("REVIEW|failure_%0d_status|%0d", failure, predictor_value.observed_status);
    end
    $display("REVIEW|done|1");
    $finish;
  end
endmodule
