blob: 671d6a017f7ad3b993fe6188b63057d5d9e2df91 [file] [log] [blame]
// Copyright lowRISC contributors.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0
//
// ---------------------------------------------
// Configuration class for TileLink agent
// ---------------------------------------------
class tl_agent_cfg extends dv_base_agent_cfg;
virtual tl_if vif;
// TileLink conformance level supported by this agent
// Right now only TL-UL is supported
tl_level_e tl_level = kTLUL;
// Maximum number of outstanding transactions supported by the DUT.
// 0: Unlimited from the host perspective, might be back-pressured by the device
// 1: Only single transaction at a time
// n: Number of maximum oustanding requests
// This is initialized to an arbitrary value. The DUT may actually support higher or lower than
// this number. Calculating exactly how many outstanding transactions the DUT supports is hard to
// determine. For most comportable IPs, it will likely be less than 16. The testbench attempts to
// pump the DUT with as many transactions as possible. The missing coverage in
// `tl_agent_cov::max_outstanding_cfg` will then indicate what the design actually supported.
// The designer must then confirm if that looks correct (in the coverage report), and then this
// value is updated accordingly in the DUT's test / env class.
//
// Note that this is a device constant. It should only be set within the scope of the test class'
// build_phase. Once set, it MUST NOT BE modified whatsoever.
int unsigned max_outstanding_req = 16;
// If allow_a_valid_drop_wo_a_ready then the host can de-assert valid if there is no response from
// the device on the A channel. The other knobs control how this works. Each transaction has a
// "valid_len". If the host hasn't seen a ready after holding valid high for that many cycles, it
// will de-assert valid for a time before retrying.
//
// If use_seq_item_a_valid_len is true then valid_len will be read from the sequence item (field
// a_valid_len). If not, it will be picked uniformly between a_valid_len_min and a_valid_len_max.
bit allow_a_valid_drop_wo_a_ready = 1;
bit use_seq_item_a_valid_len;
int unsigned a_valid_len_min = 1;
int unsigned a_valid_len_max = 10;
// Knobs controlling when we de-assert valid in device mode: see above for the host mode
// equivalent.
bit use_seq_item_d_valid_len;
bit allow_d_valid_drop_wo_d_ready;
int unsigned d_valid_len_min = 1;
int unsigned d_valid_len_max = 10;
// TileLink channel valid delay (host mode)
bit use_seq_item_a_valid_delay;
int unsigned a_valid_delay_min = 0;
int unsigned a_valid_delay_max = 10;
// TileLink channel ready delay (host mode)
int unsigned d_ready_delay_min = 0;
int unsigned d_ready_delay_max = 10;
// TileLink channel ready delay (device mode)
int unsigned a_ready_delay_min = 0;
int unsigned a_ready_delay_max = 10;
// TileLink channel ready delay (device mode)
bit use_seq_item_d_valid_delay;
int unsigned d_valid_delay_min = 0;
int unsigned d_valid_delay_max = 10;
// TL spec requires host to set d_ready = 1 when a_valid = 1, but some design may not follow this
// rule. Details at #3208. Use below knob to control
bit host_can_stall_rsp_when_a_valid_high = 0;
// knob for device to enable/disable same cycle response
bit device_can_rsp_on_same_cycle = 0;
// A configuration flag for the monitor that configures whether it should synchronise concurrent A
// and D transactions or not. See note above the definition of channel_dir_port in tl_monitor.sv
// for details.
bit synchronise_ports = 1'b0;
// for same cycle response, need to know when a_valid and other data/control are available, so
// that monitor can sample it, then send to sequence to get data for response in the same cycle
// 10 means 10% of TL clock period. This var is only use when device_can_rsp_on_same_cycle = 1
time time_a_valid_avail_after_sample_edge = 1ns;
// Sets the valid a_source width. The DUT may support fewer bits than the whole SourceWidth bits.
int unsigned valid_a_source_width = SourceWidth;
// Maintains a queue of a_source values used in previous TL requests.
//
// The queue size maxes out at max_outstanding_req value, beyond which the a_source values can
// be recycled. The a_source value for a new request is randomized such that it does not match any
// in this queue. The new a_source is pushed into the back of the queue and the oldest one is
// popped from the front when the max size is reached.
//
// NOTE: If a tl_seq_item is being constructed for a new host request (whether in the TL
// sequences, adapter or elsewhere), it is mandatory to constrain the tl_seq_item::a_source to NOT
// match what already exists in the queue. Otherwise, it will violate the spec and the design will
// end up throwing assertion errors. It is recommended to use the `randomize_a_source_in_req()`
// function below to achieve this.
bit [SourceWidth-1:0] a_source_pend_q[$];
// Records the a_source value that was last released from the `a_source_pend_q` pool.
// We influence the randomization of a new req's a_source to reuse this 20% (also a knob) of the
// time. The initial value is randomized (see post_randomize()).
protected bit [SourceWidth-1:0] last_a_source_released;
// Sets the randomization dist weight for a new a_source value to be constrained to the last
// a_source released. This only ATTEMPTS to set the a_source to the last_a_source_released value -
// it is not always guaranteed even at 100%, especially if the test does non-blocking accessses.
rand int unsigned use_last_a_source_released_pct = 20;
// The monitor detects reset and maintains the value below.
bit reset_asserted;
constraint a_source_eq_last_a_source_released_pct_c {
use_last_a_source_released_pct <= 100;
use_last_a_source_released_pct dist {
[0:19] :/ 1,
[20:59] :/ 1,
[60:99] :/ 1,
100 :/ 1
};
}
// TLUL allows host to switch content if the item isn't accepted. RAL doesn't provide interface
// to control this. Use this knob to allow tl_reg_adapter to randomize req_abort_after_a_valid_len
// Note: below set the chance to abort after a_valid_len in tl_reg_adapter. if > 0, csr access may
// return UVM_NOT_OK when the item is aborted without accepted.
int unsigned csr_access_abort_pct_in_adapter = 0;
// Indicate whether to check TLUL errors.
// Added to allow some TLUL checks to be ignored on certain scenarios.
bit check_tl_errs = 1'b1;
// Invalidate drive option
// Setting these to '1', make driver send 'x' during invalidate cycle.
// Setting these to '0', make driver send 'random value' during invalidate cycle.
rand bit invalidate_a_x;
rand bit invalidate_d_x;
constraint invalidate_a_channel_op_c {
invalidate_a_x dist { 1 := 7, 0 := 3};
}
constraint invalidate_d_channel_op_c {
invalidate_d_x dist { 1 := 7, 0 := 3};
}
`uvm_object_utils_begin(tl_agent_cfg)
`uvm_field_int(max_outstanding_req, UVM_DEFAULT)
`uvm_field_enum(tl_level_e, tl_level, UVM_DEFAULT)
`uvm_field_int(a_ready_delay_min, UVM_DEFAULT)
`uvm_field_int(a_ready_delay_max, UVM_DEFAULT)
`uvm_field_int(d_ready_delay_min, UVM_DEFAULT)
`uvm_field_int(d_ready_delay_max, UVM_DEFAULT)
`uvm_field_int(host_can_stall_rsp_when_a_valid_high, UVM_DEFAULT)
`uvm_field_int(device_can_rsp_on_same_cycle, UVM_DEFAULT)
`uvm_field_int(time_a_valid_avail_after_sample_edge, UVM_DEFAULT)
`uvm_field_int(csr_access_abort_pct_in_adapter, UVM_DEFAULT)
`uvm_object_utils_end
`uvm_object_new
function void post_randomize();
// We want to randomize the initial value of last_a_source_released, while respecting any
// constraints on a_source in the tl_seq_item (and its extensions). So we do it here instead.
tl_seq_item dummy = tl_seq_item::type_id::create("dummy");
`DV_CHECK_RANDOMIZE_FATAL(dummy)
randomize_a_source_in_item(.item(dummy), .use_last_a_source_released(1'b0));
last_a_source_released = dummy.a_source;
endfunction
// Randomizes a_source within an item (protected function).
//
// This function takes the tl_seq_item object to specifically randomize the a_source such that it
// does not match an existing one in the a_source_q. This randomization also takes into account
// any existing constriants on a_source that may exist in the class definition. The new a_source
// value is randomized to be equal to the last a_source released from the a_source_pend_q
// 20% of the time (weight is configurable).
//
// For actual reqs made out to the DUT, use randomize_a_source_in_req().
protected function void randomize_a_source_in_item(tl_seq_item item,
bit use_last_a_source_released);
bit [SourceWidth - 1 : 0] a_source;
// Use std::randomize instead of calling item.randomize(a_source). item.randomize will invoke
// post_randomize. If user does sth after post_randomize before calling finish_item, invoking
// post_randomize may override that.
// For example, if we use item.randomize(a_source)
// item.randomize(); // in post_randomize, instr_type is set to False
// item.instr_type = True;
// finish_item(item); // finish_item will call randomize_a_source_in_item.
// // item.randomize(a_source) will invoke post_randomize, instr_type is set
// // to False again.
`DV_CHECK_STD_RANDOMIZE_WITH_FATAL(a_source,
// use soft constraint as seq may still send req when all the valid a_source are used
(!reset_asserted) -> soft !(a_source inside {a_source_pend_q});
// Set the upper a_source bits that are unused to 0.
(a_source >> valid_a_source_width) == 0;
// We cannot guarantee that last_a_source_released is not in a_source_pend_q,
// especially if the test does non-blocking accesses. If the a_source for the new req
// needs to be equal to last_a_source_released, we will need the bottom constraint to be a
// soft one to *potentially* avoid violating the previous constraint. Only way to guarantee
// that the new a_source == last_a_source_released is to ensure the test only does blocking
// accesses.
use_last_a_source_released && !(last_a_source_released inside {a_source_pend_q})
-> soft (a_source == last_a_source_released);)
item.a_source = a_source;
`uvm_info(`gfn, $sformatf("a_source_pend_q: %p a_source: %0h", a_source_pend_q, item.a_source), UVM_DEBUG)
endfunction
// Randomizes the a_source for a 'real' req (public version of the above function).
//
// Invokes randomize_a_source_in_item() and adds the a_source for the new req to the queue.
virtual function void randomize_a_source_in_req(tl_seq_item item);
bit use_last_a_source_released;
use_last_a_source_released = ($urandom_range(1, 100) <= use_last_a_source_released_pct);
randomize_a_source_in_item(item, use_last_a_source_released);
add_to_a_source_pend_q(item.a_source);
endfunction
// Adds a_source to the a_source_pend_q.
//
// Ensures that the queue does not exceed the max outstanding req size. Pops the oldest a_source
// used to last_a_source_released.
virtual function void add_to_a_source_pend_q(bit [SourceWidth-1:0] a_source);
if (reset_asserted) return;
if (a_source_pend_q.size() == max_outstanding_req) begin
last_a_source_released = a_source_pend_q.pop_front();
end
a_source_pend_q.push_back(a_source);
`uvm_info(`gfn, $sformatf("a_source_pend_q: %p", a_source_pend_q), UVM_DEBUG)
endfunction
// Removes the last used a_source from a_source_pend_q.
//
// Invoked by the tl_host_base_seq::get_base_response() which overrides the default UVM
// implementation. It is recommended that all TL host sequence flavors that do not extended from
// tl_host_base_seq invoke this function similarly to avoid running out of a_source IDs when
// creating non-blocking requests.
virtual function void remove_from_a_source_pend_q(bit [SourceWidth-1:0] a_source);
int result[$];
// Responses can return out of order, so the a_source can be anywhere in the queue.
result = a_source_pend_q.find_first_index with (item == a_source);
if (result.size() == 1) begin
a_source_pend_q.delete(result.pop_front());
last_a_source_released = a_source;
`uvm_info(`gfn, $sformatf("a_source_pend_q: %p", a_source_pend_q), UVM_DEBUG)
end
endfunction
endclass