`ifndef CALC_SCOREBOARD_SV
 `define CALC_SCOREBOARD_SV

//-----------------------------------------------------------------------------
// Calc1 scoreboard (self-checking, reference-model based - Vezba 10).
//
// For every transaction collected by the monitor, the embedded reference model
// (predictor) computes the expected response/result from the stimulus and
// compares it against what the DUT actually produced.  Mismatches are reported
// as UVM_ERROR.
//-----------------------------------------------------------------------------
class calc_scoreboard extends uvm_scoreboard;

   // control fields
   bit checks_enable   = 1;
   bit coverage_enable = 1;

   // TLM port connecting the scoreboard to the monitor
   uvm_analysis_imp#(calc_seq_item, calc_scoreboard) item_collected_imp;

   // bookkeeping
   int unsigned num_of_tr;
   int unsigned num_passed;
   int unsigned num_failed;

   `uvm_component_utils_begin(calc_scoreboard)
      `uvm_field_int(checks_enable,   UVM_DEFAULT)
      `uvm_field_int(coverage_enable, UVM_DEFAULT)
   `uvm_component_utils_end

   function new(string name = "calc_scoreboard", uvm_component parent = null);
      super.new(name,parent);
      item_collected_imp = new("item_collected_imp", this);
   endfunction : new

   //--------------------------------------------------------------------------
   // Reference model / predictor.
   //   Computes the expected response and result for a Calc1 command, following
   //   the functional specification (Vezba 5, Tabela 6/7/8).
   //   All arithmetic is unsigned.
   //--------------------------------------------------------------------------
   function void predict(input  bit [CMD_WIDTH-1:0]  cmd,
                         input  bit [DATA_WIDTH-1:0]  op1,
                         input  bit [DATA_WIDTH-1:0]  op2,
                         output bit [RESP_WIDTH-1:0]  exp_resp,
                         output bit [DATA_WIDTH-1:0]  exp_data);
      bit [DATA_WIDTH:0] tmp; // one extra bit to catch carry/borrow
      exp_data = '0;
      case (cmd)
         CMD_ADD : begin
            tmp = {1'b0, op1} + {1'b0, op2};
            if (tmp[DATA_WIDTH]) exp_resp = RESP_ERROR;          // overflow
            else begin exp_resp = RESP_SUCCESS; exp_data = tmp[DATA_WIDTH-1:0]; end
         end
         CMD_SUB : begin
            if (op1 < op2) exp_resp = RESP_ERROR;                // underflow
            else begin exp_resp = RESP_SUCCESS; exp_data = op1 - op2; end
         end
         CMD_SHL : begin
            exp_resp = RESP_SUCCESS;
            exp_data = op1 << op2[4:0];
         end
         CMD_SHR : begin
            exp_resp = RESP_SUCCESS;
            exp_data = op1 >> op2[4:0];
         end
         default : begin
            exp_resp = RESP_ERROR;                               // invalid command
         end
      endcase
   endfunction : predict

   //--------------------------------------------------------------------------
   // Analysis-port callback: invoked by the monitor for each collected item.
   //--------------------------------------------------------------------------
   function void write(calc_seq_item tr);
      bit [RESP_WIDTH-1:0] exp_resp;
      bit [DATA_WIDTH-1:0] exp_data;
      bit                  ok;

      num_of_tr++;
      if (!checks_enable) return;

      predict(tr.cmd, tr.op1, tr.op2, exp_resp, exp_data);

      // The result data is only meaningful for a successful operation.
      ok = (tr.resp === exp_resp) &&
           ((exp_resp !== RESP_SUCCESS) || (tr.result === exp_data));

      if (ok) begin
         num_passed++;
         `uvm_info(get_type_name(),
                   $sformatf("PASS: %s | expected resp=%0d data=0x%08h",
                             tr.convert2string(), exp_resp, exp_data), UVM_HIGH)
      end
      else begin
         num_failed++;
         `uvm_error(get_type_name(),
                    $sformatf("MISMATCH on port %0d, cmd=%s op1=0x%08h op2=0x%08h : expected resp=%0d data=0x%08h, got resp=%0d data=0x%08h",
                              tr.port+1, tr.cmd2string(), tr.op1, tr.op2,
                              exp_resp, exp_data, tr.resp, tr.result))
      end
   endfunction : write

   function void report_phase(uvm_phase phase);
      `uvm_info(get_type_name(),
                $sformatf("Scoreboard examined %0d transactions: %0d passed, %0d failed",
                          num_of_tr, num_passed, num_failed), UVM_LOW)
      if (num_failed != 0)
        `uvm_warning(get_type_name(),
                     $sformatf("%0d transaction(s) did NOT match the reference model", num_failed))
   endfunction : report_phase

endclass : calc_scoreboard

`endif
