blob: 4cd990bc456504cfba723c6170317a143c0c5748 [file] [log] [blame]
// Copyright lowRISC contributors.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0
// ---------------------------------------------
// TileLink sequence item
// ---------------------------------------------
// use macro to write constraint in order to reuse in functions:
// get_exp_d_error and randomize_a_chan_with_protocol_error
`define chk_prot_a_opcode \
a_opcode inside {Get, PutFullData, PutPartialData}
// For PutFullData message, mask needs to match with size
`define chk_prot_mask_w_PutFullData \
a_opcode != PutFullData || $countones(a_mask) == (1 << a_size)
// mask must align with addr, same as below
// a_addr[1:0] == 1 -> a_mask[0] == 0;
// a_addr[1:0] == 2 -> a_mask[1:0] == 0;
// a_addr[1:0] == 3 -> a_mask[2:0] == 0;
`define chk_prot_addr_mask_align \
MaskWidth'(a_mask << (4 - a_addr[SizeWidth-1:0])) == 0
// mask must be within enabled lanes
// prevent cases: addr: 0h, size: 1, mask: 'b1100; addr: 0h, size: 0, mask: 'b1000
`define chk_prot_mask_in_enabled_lanes \
((a_mask >> a_addr[SizeWidth-1:0]) >> (1 << a_size)) == 0
// Address must be aligned to the a_size (2 ** a_size bytes)
`define chk_prot_addr_size_align \
(a_addr << (AddrWidth - a_size)) == 0
// max size is 2
`define chk_prot_max_size \
a_size <= 2
class tl_seq_item extends uvm_sequence_item;
rand bit [AddrWidth - 1 : 0] a_addr;
rand bit [DataWidth - 1 : 0] a_data;
rand bit [MaskWidth - 1 : 0] a_mask;
rand bit [SizeWidth - 1 : 0] a_size;
rand bit [2:0] a_param;
rand bit [SourceWidth - 1 : 0] a_source;
rand bit [OpcodeWidth - 1 : 0] a_opcode;
rand bit [AUserWidth - 1 : 0] a_user;
rand bit [2:0] d_param;
rand bit [DataWidth - 1 : 0] d_data;
rand bit [SourceWidth - 1 : 0] d_source;
rand bit [SizeWidth - 1 : 0] d_size;
rand bit [OpcodeWidth - 1 : 0] d_opcode;
rand bit d_error;
rand bit [DUserWidth - 1 : 0] d_user;
rand bit d_sink;
// host mode delays
rand int unsigned a_valid_delay;
rand int unsigned a_valid_len;
// device mode delays
rand int unsigned d_valid_delay;
rand int unsigned d_valid_len;
// Indicates a_source val is overridden.
//
// a_source is randomized and set in tl_host_base_seq::finish_item() to facilitate late
// randomization. If this bit is set, the a_source is assumed to be set to a fixed value instead.
// It is possible that this fixed value might match one of the pending reqs in the that has not
// yet completed. The driver can then use this bit to add more delays if needed before sending
// this request, to avoid protocol violation.
bit a_source_is_overridden;
// after given valid_len, end the req/rsp if it's not accepted, which allows seq to switch
// content and test unaccepted item shouldn't be used in design
bit req_abort_after_a_valid_len;
bit rsp_abort_after_d_valid_len;
// True if the item is completed, not aborted
bit req_completed;
bit rsp_completed;
// param is reserved for future use, must be zero
constraint param_c {
a_param == 0;
d_param == 0;
}
constraint no_d_error_c {
soft d_error == 0;
}
constraint a_valid_len_c {
soft a_valid_len inside {[1:10]};
}
constraint d_valid_len_c {
soft d_valid_len inside {[1:10]};
}
constraint valid_delay_c {
soft a_valid_delay inside {[0:50]};
soft d_valid_delay inside {[0:50]};
}
constraint d_opcode_c {
d_opcode inside {AccessAckData, AccessAck};
}
constraint a_opcode_c {
`chk_prot_a_opcode;
}
// mask must be contiguous, e.g. 'b1001, 'b1010 aren't allowed
constraint mask_contiguous_c {
$countones(a_mask ^ {a_mask[MaskWidth-2:0], 1'b0}) <= 2;
}
constraint mask_w_PutFullData_c {
`chk_prot_mask_w_PutFullData;
}
constraint addr_mask_align_c {
`chk_prot_addr_mask_align;
}
constraint mask_in_enabled_lanes_c {
`chk_prot_mask_in_enabled_lanes;
}
constraint addr_size_align_c {
`chk_prot_addr_size_align;
}
// size can't be more than 2
constraint max_size_c {
`chk_prot_max_size;
}
`uvm_object_utils_begin(tl_seq_item)
`uvm_field_int (a_addr, UVM_DEFAULT)
`uvm_field_int (a_data, UVM_DEFAULT)
`uvm_field_int (a_mask, UVM_DEFAULT)
`uvm_field_int (a_size, UVM_DEFAULT)
`uvm_field_int (a_param, UVM_DEFAULT)
`uvm_field_int (a_source, UVM_DEFAULT)
`uvm_field_int (a_opcode, UVM_DEFAULT)
`uvm_field_int (a_user, UVM_DEFAULT)
`uvm_field_int (d_param, UVM_DEFAULT)
`uvm_field_int (d_source, UVM_DEFAULT)
`uvm_field_int (d_data, UVM_DEFAULT)
`uvm_field_int (d_size, UVM_DEFAULT)
`uvm_field_int (d_opcode, UVM_DEFAULT)
`uvm_field_int (d_error, UVM_DEFAULT)
`uvm_field_int (d_sink, UVM_DEFAULT)
`uvm_field_int (d_user, UVM_DEFAULT)
`uvm_field_int (a_source_is_overridden, UVM_DEFAULT | UVM_NOPACK)
`uvm_field_int (a_valid_delay, UVM_DEFAULT | UVM_NOPACK)
`uvm_field_int (d_valid_delay, UVM_DEFAULT | UVM_NOPACK)
`uvm_field_int (a_valid_len, UVM_DEFAULT | UVM_NOPACK)
`uvm_field_int (d_valid_len, UVM_DEFAULT | UVM_NOPACK)
`uvm_field_int (req_abort_after_a_valid_len, UVM_DEFAULT | UVM_NOPACK)
`uvm_field_int (rsp_abort_after_d_valid_len, UVM_DEFAULT | UVM_NOPACK)
`uvm_field_int (req_completed, UVM_DEFAULT | UVM_NOPACK)
`uvm_field_int (rsp_completed, UVM_DEFAULT | UVM_NOPACK)
`uvm_object_utils_end
function new (string name = "");
super.new(name);
endfunction : new
virtual function string convert2string();
string str;
string a_opcode_name, d_opcode_name;
tl_a_op_e a_op_e;
tl_d_op_e d_op_e;
if ($cast(a_op_e, a_opcode)) a_opcode_name = a_op_e.name();
else a_opcode_name = $sformatf("Invalid, value: %0d", a_opcode);
if ($cast(d_op_e, d_opcode)) d_opcode_name = d_op_e.name();
else d_opcode_name = $sformatf("Invalid, value: %0d", d_opcode);
str = {$sformatf("a_addr = 0x%0h ", a_addr),
$sformatf("a_data = 0x%0h ", a_data),
$sformatf("a_mask = 0x%0h ", a_mask),
$sformatf("a_size = 0x%0h ", a_size),
$sformatf("a_param = 0x%0h ", a_param),
$sformatf("a_source = 0x%0h ", a_source),
$sformatf("a_opcode = %0s ", a_opcode_name),
$sformatf("a_user = 0x%0h ", a_user),
$sformatf("d_data = 0x%0h ", d_data),
$sformatf("d_size = 0x%0h ", d_size),
$sformatf("d_param = 0x%0h ", d_param),
$sformatf("d_source = 0x%0h ", d_source),
$sformatf("d_opcode = %0s ", d_opcode_name),
$sformatf("d_error = %0b ", d_error),
$sformatf("d_user = %0b ", d_user),
$sformatf("d_sink = %0b ", d_sink),
$sformatf("req_abort_after_a_valid_len = %0b ", req_abort_after_a_valid_len),
$sformatf("rsp_abort_after_d_valid_len = %0b ", rsp_abort_after_d_valid_len),
$sformatf("req_completed = %0b ", req_completed),
$sformatf("rsp_completed = %0b ", rsp_completed)};
return str;
endfunction
function void disable_a_chan_randomization();
a_addr.rand_mode(0);
a_data.rand_mode(0);
a_mask.rand_mode(0);
a_size.rand_mode(0);
a_param.rand_mode(0);
a_source.rand_mode(0);
a_opcode.rand_mode(0);
endfunction
// calculate d_error based on values of a_chan
function bit get_exp_d_error();
return get_error_a_opcode_invalid() || get_error_PutFullData_mask_size_mismatched() ||
get_error_addr_mask_misaligned() || get_error_addr_size_misaligned() ||
get_error_mask_not_in_enabled_lanes() || get_error_size_over_max();
endfunction
function bit get_error_a_opcode_invalid();
return !(`chk_prot_a_opcode);
endfunction
function bit get_error_PutFullData_mask_size_mismatched();
return !(`chk_prot_mask_w_PutFullData);
endfunction
function bit get_error_addr_mask_misaligned();
return !(`chk_prot_addr_mask_align);
endfunction
function bit get_error_addr_size_misaligned();
return !(`chk_prot_addr_size_align);
endfunction
function bit get_error_mask_not_in_enabled_lanes();
return !(`chk_prot_mask_in_enabled_lanes);
endfunction
function bit get_error_size_over_max();
return !(`chk_prot_max_size);
endfunction // get_error_size_over_max
function void disable_a_chan_protocol_constraint();
a_opcode_c.constraint_mode(0);
mask_contiguous_c.constraint_mode(0);
mask_w_PutFullData_c.constraint_mode(0);
addr_mask_align_c.constraint_mode(0);
mask_in_enabled_lanes_c.constraint_mode(0);
addr_size_align_c.constraint_mode(0);
max_size_c.constraint_mode(0);
endfunction
// randomly disable constraint_mode for a chan
// at least one constraint_mode needs to be disabled to make sure protocol is violated
function void randomize_a_chan_with_protocol_error();
bit cm_a_opcode, cm_mask_w_PutFullData;
bit cm_addr_mask_align, cm_mask_in_enabled_lanes, cm_addr_size_align, cm_max_size;
`DV_CHECK_FATAL(std::randomize(cm_a_opcode, cm_mask_w_PutFullData,
cm_addr_mask_align, cm_mask_in_enabled_lanes,
cm_addr_size_align, cm_max_size) with {
// at least one constraint_mode is off
!(cm_a_opcode && cm_mask_w_PutFullData &&
cm_addr_mask_align && cm_mask_in_enabled_lanes &&
cm_addr_size_align && cm_max_size);
})
a_opcode_c.constraint_mode(cm_a_opcode);
mask_w_PutFullData_c.constraint_mode(cm_mask_w_PutFullData);
addr_mask_align_c.constraint_mode(cm_addr_mask_align);
mask_in_enabled_lanes_c.constraint_mode(cm_mask_in_enabled_lanes);
addr_size_align_c.constraint_mode(cm_addr_size_align);
max_size_c.constraint_mode(cm_max_size);
`DV_CHECK_FATAL(
// at least one `chk_prot_* is violated
randomize() with {!cm_a_opcode && !(`chk_prot_a_opcode) ||
!cm_mask_w_PutFullData && !(`chk_prot_mask_w_PutFullData) ||
!cm_addr_mask_align && !(`chk_prot_addr_mask_align) ||
!cm_mask_in_enabled_lanes && !(`chk_prot_mask_in_enabled_lanes) ||
!cm_addr_size_align && !(`chk_prot_addr_size_align) ||
!cm_max_size && !(`chk_prot_max_size);})
endfunction
// Find whether this seq item is a read or a write.
virtual function bit is_write();
return (a_opcode inside {PutFullData, PutPartialData});
endfunction
// Check if d_opcode is appropriate for the given a_opcode. This check assumes that the
// response packet has been appended with the corresponding request.
virtual function bit check_opcodes(bit throw_error = 1'b1);
// for read, return AccessAckData; for write or error opcode, return AccessAck
tl_d_op_e exp_d_opcode = a_opcode == Get ? tlul_pkg::AccessAckData : tlul_pkg::AccessAck;
check_opcodes = (d_opcode == int'(exp_d_opcode));
if (!check_opcodes && throw_error) begin
`uvm_error(`gfn, $sformatf("d_opcode: %0d & exp_d_opcode: %0d mismatch",
d_opcode, exp_d_opcode))
end
endfunction
// Check if packet parameters are ok.
virtual function bit is_ok(bit throw_error = 1'b1);
is_ok = 1'b1;
is_ok &= check_opcodes(throw_error);
// addr and data channels should have the same source
is_ok &= (a_source == d_source);
if (!is_ok && throw_error)
`uvm_error(`gfn, $sformatf("a_source: 0x%0h & d_source: 0x%0h mismatch", a_source, d_source))
endfunction
// Compute and check the integrity of the a_channel payload.
//
// The TL agent is generic and adheres to the TLUL spec, which does not define
// how the integrity of the payload on each channel is computed / checked. That
// is up to the chip implementation. Typically, parity / ECC scheme is used, with
// the redundant bits transmitted through *_user.
//
// Returns 1 if the integrity of a_channel is maintained, 0 otherwise. This base
// class implementation vacuously returns 1.
virtual function bit is_a_chan_intg_ok(bit en_cmd_intg_chk = 1,
bit en_data_intg_chk = 1,
bit throw_error = 1'b1);
return 1;
endfunction
// d_channel version of the function above
virtual function bit is_d_chan_intg_ok(bit en_rsp_intg_chk = 1,
bit en_data_intg_chk = 1,
bit throw_error = 1'b1);
return 1;
endfunction
endclass
`undef chk_prot_a_opcode
`undef chk_prot_mask_w_PutFullData
`undef chk_prot_addr_mask_align
`undef chk_prot_mask_in_enabled_lanes
`undef chk_prot_addr_size_align
`undef chk_prot_max_size