`ifndef _AXI4_SEQ_ITEM
`define _AXI4_SEQ_ITEM

`include "uvm_macros.svh"
import uvm_pkg::*;

class axi4_seq_item extends uvm_sequence_item;

typedef enum  {FIXED, INCR, WRAP} burst_type_e;
typedef enum  {NORMAL, EXCLUSIVE} lock_type_e;
typedef enum  {NON_BUFFERABLE, BUFFERABLE} cache_bufferable_type_e;
typedef enum  {NON_MODIFIABLE, MODIFIABLE} cache_modifiable_type_e;
typedef enum  {NO_OTHER_ALLOCATE, OTHER_ALLOCATE} cache_other_allocate_type_e;
typedef enum  {NO_ALLOCATE, ALLOCATE} cache_allocate_type_e;
typedef enum  {UNPRIVILEGED, PRIVILEGED} prot_privileged_type_e;
typedef enum  {NON_SECURE, SECURE} prot_secure_type_e;
typedef enum  {DATA_ACCESS, INSTRUCTION_ACCESS} prot_access_type_e;
typedef enum  {OKAY, EXOKAY, SLVERR, DECERR} resp_type_e;
typedef enum  {NOP,READ, WRITE} rw_type_e;
typedef enum  {REQ, RESP} packet_type_e;
typedef enum  {M0, M1, S0, S1} device_id_e;
	
  rand rw_type_e                    rw_type;
  rand prot_secure_type_e           prot_secure;
  rand bit[31:0]                    address;
  rand bit[7:0]		                number_of_beats;
  rand bit[2:0]                     burst_size;
  rand burst_type_e                 burst_type;
  rand lock_type_e                  lock_type;
  rand bit[3:0]                     qos;
  rand bit[7:0]                     user;
  rand bit[3:0]                     region;
  rand logic[31:0]                  data[$];
  rand bit[3:0]                     strb[$];
  rand cache_bufferable_type_e      cache_bufferable;
  rand cache_modifiable_type_e      cache_modifiable;
  rand cache_other_allocate_type_e  cache_other_allocate;
  rand cache_allocate_type_e        cache_allocate;
  rand prot_privileged_type_e       prot_privileged;
  rand prot_access_type_e           prot_access;
  rand resp_type_e                  resp;
       packet_type_e                packet_type;
       device_id_e                  source;
       device_id_e                  destination;
       resp_type_e                  slave_resp[$];
       bit [7:0]                    id;
       bit                          slave_id;


 `uvm_object_utils_begin(axi4_seq_item)
  `uvm_field_int(address,UVM_ALL_ON)
  `uvm_field_int(number_of_beats,UVM_ALL_ON)
  `uvm_field_int(burst_size,UVM_ALL_ON)
  `uvm_field_int(qos,UVM_ALL_ON)
  `uvm_field_int(id,UVM_ALL_ON)
  `uvm_field_int(slave_id,UVM_ALL_ON)
  `uvm_field_int(user,UVM_ALL_ON)
  `uvm_field_int(region,UVM_ALL_ON)
  `uvm_field_queue_int(data,UVM_ALL_ON)
  `uvm_field_queue_int(strb,UVM_ALL_ON)
  `uvm_field_enum(rw_type_e, rw_type, UVM_ALL_ON)
  `uvm_field_enum(burst_type_e, burst_type, UVM_ALL_ON)
  `uvm_field_enum(lock_type_e,  lock_type, UVM_ALL_ON)
  `uvm_field_enum(cache_bufferable_type_e,  cache_bufferable, UVM_ALL_ON)
  `uvm_field_enum(cache_modifiable_type_e,  cache_modifiable, UVM_ALL_ON)
  `uvm_field_enum(cache_other_allocate_type_e, cache_other_allocate, UVM_ALL_ON)
  `uvm_field_enum(cache_allocate_type_e, cache_allocate, UVM_ALL_ON)
  `uvm_field_enum(prot_privileged_type_e, prot_privileged, UVM_ALL_ON)
  `uvm_field_enum(prot_secure_type_e, prot_secure, UVM_ALL_ON)
  `uvm_field_enum(prot_access_type_e, prot_access, UVM_ALL_ON)
  `uvm_field_enum(resp_type_e, resp, UVM_ALL_ON)
  `uvm_field_queue_enum(resp_type_e,slave_resp,UVM_ALL_ON)
  `uvm_field_enum(packet_type_e,packet_type,UVM_ALL_ON)
  `uvm_field_enum(device_id_e,source,UVM_ALL_ON)
  `uvm_field_enum(device_id_e,destination,UVM_ALL_ON)
 `uvm_object_utils_end
   

  constraint fixed_wrap_busrt_length_c {
	   (burst_type == FIXED || burst_type == WRAP) -> (number_of_beats inside {['d1:'d16]});  // for fixed and wrap burst beat counts should be less than 16  
  }
  
  constraint incr_busrt_length_c {
	  (burst_type == INCR ) -> (number_of_beats inside {['d1:'d256]});  // for INCR burst beat counts should be less than 256
  }

  constraint excl_burst_length_c {
	  (lock_type == EXCLUSIVE ) -> (number_of_beats inside {['d1:'d16]});  // In AXI4, the burst length for an exclusive access must not exceed 16 transfers.
  }

  constraint burst_range { burst_size inside {'d1,'d2,'d4,'d8,'d16,'d32,'d64,'d128}; }

  constraint mem_type_w1_c  {
	   (cache_allocate == NO_ALLOCATE && cache_other_allocate == OTHER_ALLOCATE) -> !(cache_modifiable == NON_MODIFIABLE); //reserved
  }
  
  constraint mem_type_w2_c  {
	   (cache_allocate == ALLOCATE) -> !(cache_other_allocate == NO_OTHER_ALLOCATE);  //reserved
  }
  
  constraint mem_type_w3_c  {
	   (cache_allocate == ALLOCATE && cache_other_allocate == OTHER_ALLOCATE) -> !(cache_modifiable == NON_MODIFIABLE);  //reserved
  }

  constraint mem_type_r1_c {
	   (cache_other_allocate == NO_OTHER_ALLOCATE) -> !(cache_allocate == ALLOCATE);  //reserved
  }
  constraint rw_type_c {
	   rw_type inside{WRITE,READ};  //reserved
  }
  constraint mem_type_r2_c {
	   (cache_other_allocate == OTHER_ALLOCATE && (cache_allocate == NO_ALLOCATE || cache_allocate == ALLOCATE)) -> !(cache_modifiable == NON_MODIFIABLE); //reserved
  }

 constraint data_size_c {
	  solve rw_type before number_of_beats ; 
	  rw_type == WRITE -> data.size() == ( number_of_beats * burst_size ) / 4 ;
	  rw_type == READ -> data.size() == 0; 
  	  number_of_beats <= 'd256;
 } 

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

extern virtual function void do_copy(uvm_object rhs);
extern virtual function void randomise_values();



endclass
function void axi4_seq_item::do_copy(uvm_object rhs);
    axi4_seq_item rhs_;
    if(!$cast(rhs_,rhs))begin 
	uvm_report_error("do_copy","Cast failed");
	return;
    end
    super.do_copy(rhs);
    this.address = rhs_.address;
    this.number_of_beats = rhs_.number_of_beats;
    this.burst_size = rhs_.burst_size;
    this.qos = rhs_.qos;
    this.user = rhs_.user;
    this.region = rhs_.region;
    this.data = {rhs_.data};
    this.strb = {rhs_.strb};
    this.rw_type = rhs_.rw_type;
    this.burst_type = rhs_.burst_type;
    this.lock_type = rhs_.lock_type;
    this.cache_bufferable = rhs_.cache_bufferable;
    this.cache_modifiable = rhs_.cache_modifiable;
    this.cache_other_allocate = rhs_.cache_other_allocate;
    this.cache_allocate = rhs_.cache_allocate;
    this.prot_privileged = rhs_.prot_privileged;
    this.prot_secure = rhs_.prot_secure;
    this.prot_access = rhs_.prot_access;
    this.resp = rhs_.resp;
    this.slave_resp = rhs_.slave_resp;
    this.id = rhs_.id;
    this.slave_id = rhs_.slave_id;
    this.packet_type = rhs_.packet_type;
endfunction: do_copy

function void axi4_seq_item::randomise_values();
  bit[31:0] seed;
  int cnt;
  seed = $urandom();
  `uvm_info(get_type_name(),$psprintf("seed generated - %0b",seed),UVM_LOW)

  this.address = seed;

  case (seed[1:0]) 
    00: begin
      this.burst_type = FIXED;
    end
    01:begin
      this.burst_type = INCR;
    end
    10:begin
      this.burst_type = WRAP;
    end
    default: burst_type = FIXED;
  endcase

  case (this.burst_type)
    FIXED: begin 
      this.number_of_beats = $urandom_range(1,16);
    end
    INCR: begin
      this.number_of_beats = $urandom_range(1,256);
    end
    WRAP: begin
      this.number_of_beats = $urandom_range(1,16);
    end
    default : this.number_of_beats = $urandom_range(1,16);
  endcase

  case(seed[3:2])
    00 : this.burst_size = 1;
    11 : this.burst_size = 2;
    10 : this.burst_size = 4;
    default: this.burst_size = 4;
  endcase
  
  this.qos    = seed[3:0];
  this.user   = seed[7:0];
  this.region = seed[31:28];
  
  this.id  = $urandom_range(0,15);

  if (seed[4] == 1)
    this.rw_type = READ;
  else 
    this.rw_type = WRITE;

  if (seed[5]) this.lock_type = EXCLUSIVE;
  else this.lock_type = NORMAL;

  if (seed[8]) this.cache_bufferable = BUFFERABLE;
  else this.cache_bufferable = NON_BUFFERABLE;

  if (seed[9]) this.cache_modifiable = MODIFIABLE;
  else this.cache_modifiable = NON_MODIFIABLE; 

  if (seed[10]) this.cache_other_allocate = OTHER_ALLOCATE;
  else this.cache_other_allocate = NO_OTHER_ALLOCATE;

  if (seed[11]) this.cache_allocate = ALLOCATE;
  else this.cache_allocate = NO_ALLOCATE;

  if (seed[12]) this.prot_privileged = PRIVILEGED;
  else this.prot_privileged = UNPRIVILEGED;

  if (seed[13]) this.prot_secure = SECURE;
  else this.prot_secure = NON_SECURE;

  if (seed[14]) this.prot_access = INSTRUCTION_ACCESS;
  else this.prot_access = DATA_ACCESS;

  cnt = (this.number_of_beats * this.burst_size)/4;
  for (int i=0; i<cnt; i++) begin
    this.data.push_back(i*(32'b11111111111111111111111111111111));
  end

endfunction : randomise_values


`endif
