`ifndef CALC_SEQ_ITEM_SV
 `define CALC_SEQ_ITEM_SV

parameter DATA_WIDTH  = 32;
parameter RESP_WIDTH  = 2;
parameter CMD_WIDTH   = 4;
parameter NUM_PORTS   = 4;
// Max clock cycles to wait for a response before declaring the request lost.
// A correct Calc1 answers in a handful of cycles; the timeout only fires on a
// DUT that never responds, so the environment reports an error instead of
// hanging forever.
parameter RSP_TIMEOUT = 64;

//-----------------------------------------------------------------------------
// Calc1 command encoding (see Vezba 5, Tabela 6).  All other 4-bit values are
// treated by the design as "invalid" commands.
//-----------------------------------------------------------------------------
typedef enum bit [CMD_WIDTH-1:0] {
   CMD_NOP = 4'b0000, // no operation
   CMD_ADD = 4'b0001, // result = op1 + op2
   CMD_SUB = 4'b0010, // result = op1 - op2
   CMD_SHL = 4'b0101, // result = op1 << op2[4:0]
   CMD_SHR = 4'b0110  // result = op1 >> op2[4:0]
} calc_cmd_e;

//-----------------------------------------------------------------------------
// Calc1 response encoding (see Vezba 5, Tabela 8).
//-----------------------------------------------------------------------------
typedef enum bit [RESP_WIDTH-1:0] {
   RESP_NONE    = 2'b00, // no response this cycle
   RESP_SUCCESS = 2'b01, // operation successful, data on out_dataX
   RESP_ERROR   = 2'b10  // overflow / underflow / invalid command
   // 2'b11 is unused
} calc_resp_e;

//-----------------------------------------------------------------------------
// Sequence item / transaction.
//   - Stimulus (rand)     : port, cmd, op1, op2, delay
//   - Observed (non-rand) : resp, result   (filled in by the monitor)
//-----------------------------------------------------------------------------
class calc_seq_item extends uvm_sequence_item;

   // --- stimulus fields ---------------------------------------------------
   rand bit [1:0]              port;   // target port 0..3  (-> req1..req4)
   rand bit [CMD_WIDTH-1:0]    cmd;    // raw 4-bit command (allows invalid)
   rand bit [DATA_WIDTH-1:0]   op1;    // operand 1
   rand bit [DATA_WIDTH-1:0]   op2;    // operand 2
   rand int unsigned           delay;  // idle cycles before issuing the request

   // --- observed fields (driven by the monitor) ---------------------------
   bit [RESP_WIDTH-1:0]        resp;   // observed out_respX
   bit [DATA_WIDTH-1:0]        result; // observed out_dataX

   // --- constraints --------------------------------------------------------
   // A real request always carries a command; NOP would produce no response.
   constraint c_no_nop  { cmd != CMD_NOP; }

   // By default favour the four legal commands, but keep a small probability
   // of an illegal command so the random regression exercises that path too.
   constraint c_cmd_dist {
      cmd dist {
         CMD_ADD := 25,
         CMD_SUB := 25,
         CMD_SHL := 20,
         CMD_SHR := 20,
         [4'h3:4'h4] :/ 5,   // illegal
         4'h7        :/ 5    // illegal
      };
   }

   constraint c_delay { delay inside {[0:6]}; }

   `uvm_object_utils_begin(calc_seq_item)
      `uvm_field_int (port,   UVM_DEFAULT)
      `uvm_field_int (cmd,    UVM_DEFAULT)
      `uvm_field_int (op1,    UVM_DEFAULT)
      `uvm_field_int (op2,    UVM_DEFAULT)
      `uvm_field_int (delay,  UVM_DEFAULT | UVM_DEC)
      `uvm_field_int (resp,   UVM_DEFAULT)
      `uvm_field_int (result, UVM_DEFAULT)
   `uvm_object_utils_end

   function new (string name = "calc_seq_item");
      super.new(name);
   endfunction

   // True when cmd is one of the four legal Calc1 commands.
   function bit is_legal_cmd();
      return (cmd inside {CMD_ADD, CMD_SUB, CMD_SHL, CMD_SHR});
   endfunction

   // Compact one-line description, handy in logs.
   function string convert2string();
      return $sformatf("port=%0d cmd=%s(0x%0h) op1=0x%08h op2=0x%08h -> resp=%s data=0x%08h",
                       port+1, cmd2string(), cmd, op1, op2, resp2string(), result);
   endfunction

   function string cmd2string();
      case (cmd)
        CMD_NOP : return "NOP";
        CMD_ADD : return "ADD";
        CMD_SUB : return "SUB";
        CMD_SHL : return "SHL";
        CMD_SHR : return "SHR";
        default : return "INVALID";
      endcase
   endfunction

   function string resp2string();
      case (resp)
        RESP_NONE    : return "NONE";
        RESP_SUCCESS : return "SUCCESS";
        RESP_ERROR   : return "ERROR";
        default      : return "RSVD";
      endcase
   endfunction

endclass : calc_seq_item

`endif
