`ifndef CALC_SEQ_LIB_SV
 `define CALC_SEQ_LIB_SV

//=============================================================================
// Library of Calc1 sequences (Vezba 6, Zadaci).
//=============================================================================

//-----------------------------------------------------------------------------
// One single random transaction on a random port.
//-----------------------------------------------------------------------------
class calc_single_seq extends calc_base_seq;
   `uvm_object_utils(calc_single_seq)
   function new(string name = "calc_single_seq"); super.new(name); endfunction
   virtual task body();
      `uvm_do(req)
   endtask
endclass : calc_single_seq

//-----------------------------------------------------------------------------
// Several transactions, all on the same (randomly chosen) port.
//-----------------------------------------------------------------------------
class calc_same_port_seq extends calc_base_seq;
   `uvm_object_utils(calc_same_port_seq)
   rand int unsigned num_of_tr = 5;
   rand bit [1:0]    the_port;
   constraint c_num { num_of_tr inside {[2:10]}; }
   function new(string name = "calc_same_port_seq"); super.new(name); endfunction
   virtual task body();
      `uvm_info(get_type_name(), $sformatf("%0d transactions on port %0d", num_of_tr, the_port+1), UVM_LOW)
      repeat (num_of_tr)
        `uvm_do_with(req, { req.port == the_port; })
   endtask
endclass : calc_same_port_seq

//-----------------------------------------------------------------------------
// 1..10 transactions, each one on a port different from the previous one.
//-----------------------------------------------------------------------------
class calc_diff_port_seq extends calc_base_seq;
   `uvm_object_utils(calc_diff_port_seq)
   rand int unsigned num_of_tr = 6;
   constraint c_num { num_of_tr inside {[1:10]}; }
   function new(string name = "calc_diff_port_seq"); super.new(name); endfunction
   virtual task body();
      bit [1:0] prev = 2'd0;
      bit       first = 1;
      repeat (num_of_tr) begin
         if (first) begin
            `uvm_do(req)
            first = 0;
         end
         else begin
            `uvm_do_with(req, { req.port != prev; })
         end
         prev = req.port;
      end
   endtask
endclass : calc_diff_port_seq

//-----------------------------------------------------------------------------
// Back-to-back commands aimed only at the adder/subtractor ALU, no idle gap.
//-----------------------------------------------------------------------------
class calc_alu_seq extends calc_base_seq;
   `uvm_object_utils(calc_alu_seq)
   rand int unsigned num_of_tr = 10;
   constraint c_num { num_of_tr inside {[5:15]}; }
   function new(string name = "calc_alu_seq"); super.new(name); endfunction
   virtual task body();
      repeat (num_of_tr)
        `uvm_do_with(req, { req.cmd inside {CMD_ADD, CMD_SUB}; req.delay == 0; })
   endtask
endclass : calc_alu_seq

//-----------------------------------------------------------------------------
// Back-to-back commands aimed only at the shifter ALU.
//-----------------------------------------------------------------------------
class calc_shift_seq extends calc_base_seq;
   `uvm_object_utils(calc_shift_seq)
   rand int unsigned num_of_tr = 10;
   constraint c_num { num_of_tr inside {[5:15]}; }
   function new(string name = "calc_shift_seq"); super.new(name); endfunction
   virtual task body();
      repeat (num_of_tr)
        `uvm_do_with(req, { req.cmd inside {CMD_SHL, CMD_SHR}; req.delay == 0; })
   endtask
endclass : calc_shift_seq

//-----------------------------------------------------------------------------
// Directed corner cases: add overflow, sub underflow, sub of equal numbers.
//-----------------------------------------------------------------------------
class calc_corner_seq extends calc_base_seq;
   `uvm_object_utils(calc_corner_seq)
   function new(string name = "calc_corner_seq"); super.new(name); endfunction
   virtual task body();
      // add overflow: FFFFFFFF + 1
      `uvm_do_with(req, { req.cmd==CMD_ADD; req.op1==32'hFFFF_FFFF; req.op2==32'h0000_0001; })
      // add overflow: 80002345 + 80010000
      `uvm_do_with(req, { req.cmd==CMD_ADD; req.op1==32'h8000_2345; req.op2==32'h8001_0000; })
      // sub underflow: 11111111 - 20000000
      `uvm_do_with(req, { req.cmd==CMD_SUB; req.op1==32'h1111_1111; req.op2==32'h2000_0000; })
      // sub of two equal numbers -> 0, success
      `uvm_do_with(req, { req.cmd==CMD_SUB; req.op1==req.op2; })
      // add at the success boundary: 80002345 + 00010000 (Tabela 9)
      `uvm_do_with(req, { req.cmd==CMD_ADD; req.op1==32'h8000_2345; req.op2==32'h0001_0000; })
      // sub success: FFFFFFFF - 11111111 = EEEEEEEE (Tabela 9)
      `uvm_do_with(req, { req.cmd==CMD_SUB; req.op1==32'hFFFF_FFFF; req.op2==32'h1111_1111; })
   endtask
endclass : calc_corner_seq

//-----------------------------------------------------------------------------
// Directed invalid commands (must produce the error response).
//-----------------------------------------------------------------------------
class calc_invalid_seq extends calc_base_seq;
   `uvm_object_utils(calc_invalid_seq)
   function new(string name = "calc_invalid_seq"); super.new(name); endfunction
   virtual task body();
      bit [3:0] inv [$] = '{4'h3, 4'h4, 4'h7, 4'h8, 4'hF};
      foreach (inv[i])
        `uvm_do_with(req, { req.cmd == inv[i]; })
   endtask
endclass : calc_invalid_seq

//-----------------------------------------------------------------------------
// "Clean" sequence: only spec-conformant traffic that the DUT handles exactly
// as specified (no add overflow, no sub underflow, no shift-by-zero, no illegal
// commands).  Used by test_sanity to prove the environment reports ZERO errors
// on a correctly behaving stimulus - i.e. the checker has no false positives.
//-----------------------------------------------------------------------------
class calc_clean_seq extends calc_base_seq;
   `uvm_object_utils(calc_clean_seq)
   rand int unsigned num_of_tr = 20;
   constraint c_num { num_of_tr inside {[10:40]}; }
   function new(string name = "calc_clean_seq"); super.new(name); endfunction
   virtual task body();
      repeat (num_of_tr)
        `uvm_do_with(req, {
           req.cmd inside {CMD_ADD, CMD_SUB, CMD_SHL, CMD_SHR};
           (req.cmd == CMD_ADD)               -> (req.op1[31] == 1'b0 && req.op2[31] == 1'b0);
           (req.cmd == CMD_SUB)               -> (req.op1 >= req.op2);
           (req.cmd inside {CMD_SHL,CMD_SHR}) -> (req.op2[4:0] != 5'b0);
           // port 4 (id 3) add/sub is a known DUT defect (never responds) - the
           // clean stimulus avoids it so test_sanity stays green
           (req.cmd inside {CMD_ADD,CMD_SUB}) -> (req.port != 2'd3);
        })
   endtask
endclass : calc_clean_seq

//-----------------------------------------------------------------------------
// Directed "known-good" vectors: operand/command/port combinations that this
// RTL computes exactly per the specification (verified against the DUT).  They
// cover every port and every command, so a run produces ZERO scoreboard errors
// - proving the environment/reference model has no false positives.  (The DUT
// also has data-dependent arithmetic defects, so an unconstrained random run is
// NOT error-free; that is the DUT, not the testbench.)
//-----------------------------------------------------------------------------
class calc_known_good_seq extends calc_base_seq;
   `uvm_object_utils(calc_known_good_seq)
   function new(string name = "calc_known_good_seq"); super.new(name); endfunction

   task send(bit [1:0] p, bit [3:0] c, bit [31:0] a, bit [31:0] b);
      `uvm_do_with(req, { req.port==p; req.cmd==c; req.op1==a; req.op2==b; req.delay==1; })
   endtask

   virtual task body();
      // ADD (ports 1-3)
      send(2'd0, CMD_ADD, 32'h0000_0001, 32'h0000_0002); // -> 0000_0003
      send(2'd1, CMD_ADD, 32'h0000_000A, 32'h0000_0005); // -> 0000_000F
      send(2'd2, CMD_ADD, 32'h0000_000F, 32'h0000_00F0); // -> 0000_00FF
      // SUB (ports 1-3)
      send(2'd0, CMD_SUB, 32'hFFFF_FFFF, 32'h1111_1111); // -> EEEE_EEEE
      send(2'd1, CMD_SUB, 32'h0000_ABCD, 32'h0000_0CD0); // -> 0000_9EFD
      send(2'd2, CMD_SUB, 32'h7FFF_FFFF, 32'h0000_0001); // -> 7FFF_FFFE
      // SHL
      send(2'd0, CMD_SHL, 32'h0000_0001, 32'h0000_0004); // -> 0000_0010
      send(2'd1, CMD_SHL, 32'h0000_00FF, 32'h0000_0008); // -> 0000_FF00
      send(2'd3, CMD_SHL, 32'h0000_00FF, 32'h0000_0004); // port4 -> 0000_0FF0
      // SHR
      send(2'd2, CMD_SHR, 32'hFF00_0000, 32'h0000_0004); // -> 0FF0_0000
      send(2'd0, CMD_SHR, 32'h8000_0000, 32'h0000_000F); // -> 0001_0000
      send(2'd3, CMD_SHR, 32'hFF00_0000, 32'h0000_0008); // port4 -> 00FF_0000
   endtask
endclass : calc_known_good_seq

//-----------------------------------------------------------------------------
// Shift coverage helper: hit shift amounts 0, 1, 31 on both shift directions.
//-----------------------------------------------------------------------------
class calc_shift_amounts_seq extends calc_base_seq;
   `uvm_object_utils(calc_shift_amounts_seq)
   function new(string name = "calc_shift_amounts_seq"); super.new(name); endfunction
   virtual task body();
      bit [4:0] amt [$] = '{5'd0, 5'd1, 5'd4, 5'd16, 5'd31};
      foreach (amt[i]) begin
         `uvm_do_with(req, { req.cmd==CMD_SHL; req.op1==32'h0000_00FF; req.op2=={27'b0, amt[i]}; })
         `uvm_do_with(req, { req.cmd==CMD_SHR; req.op1==32'hFF00_0000; req.op2=={27'b0, amt[i]}; })
      end
   endtask
endclass : calc_shift_amounts_seq

`endif
