blob: 13605cc56371d2ccccee18bdc51d299780ec4382 [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 agent sequence library
// ---------------------------------------------
// Host sequence, support multiple outstanding request
// generates 'req_cnt' number of completely random requests
class tl_host_seq extends uvm_sequence#(.REQ(tl_seq_item), .RSP(tl_seq_item));
`uvm_declare_p_sequencer(tl_sequencer)
rand int unsigned req_cnt;
tl_seq_item pending_req[$];
int min_req_delay = 0;
int max_req_delay = 10;
int max_outstanding = 8;
`uvm_object_utils(tl_host_seq)
`uvm_object_new
virtual task body();
fork
begin : wait_response_thread
for (int i = 0; i < req_cnt; i++) begin
tl_seq_item req;
bit found_req;
get_response(rsp);
`uvm_info(`gfn, $sformatf("Received rsp[%0d] : %0s",
i, rsp.convert2string()), UVM_HIGH)
// in case of out of order rsp, find req that has same source as rsp
foreach (pending_req[i]) begin
if (rsp.d_source == pending_req[i].a_source) begin
req = pending_req[i];
pending_req.delete(i);
process_response(req, rsp);
found_req = 1;
break;
end
end // foreach
if (!found_req) begin
`uvm_error(`gfn, $sformatf("fail to find matching req for rsp[%0d]: %0s",
i, rsp.convert2string()))
end
end // for
end : wait_response_thread
begin : request_thread
`uvm_info(`gfn, $sformatf("Start sending %0d host requests", req_cnt), UVM_HIGH)
for (int i = 0; i < req_cnt; i++) begin
wait(pending_req.size < max_outstanding);
req = tl_seq_item::type_id::create("req");
start_item(req);
randomize_req(req, i);
finish_item(req);
`uvm_info(`gfn, $sformatf("Sent req[%0d] : %0s",
i, req.convert2string()), UVM_HIGH)
pending_req.push_back(req);
end
end : request_thread
join
`uvm_info(`gfn, $sformatf("Finished sending %0d host requests", req_cnt), UVM_HIGH)
endtask
// Request randomization, override this functiont to do custom request generation
virtual function void randomize_req(tl_seq_item req, int idx);
if (!(req.randomize() with {
a_valid_delay inside {[min_req_delay:max_req_delay]};})) begin
`uvm_fatal(`gfn, "Cannot randomize req")
end
endfunction
// A reserved function that can be extended to process response packet
virtual function void process_response(tl_seq_item req, tl_seq_item rsp);
endfunction
virtual function void set_max_outstanding(int value);
max_outstanding = value;
set_response_queue_depth(value);
`uvm_info(`gfn, $sformatf("Max outstanding: %0d", value), UVM_HIGH)
endfunction
endclass : tl_host_seq
// extend host seq to send single specific item constructed by the caller
class tl_host_single_seq extends tl_host_seq;
rand bit [top_pkg::TL_AW-1:0] addr;
rand bit [OpcodeWidth-1:0] opcode;
rand bit [top_pkg::TL_SZW-1:0] size;
rand bit [top_pkg::TL_AIW-1:0] source;
rand bit [top_pkg::TL_DBW-1:0] mask;
rand bit [top_pkg::TL_DW-1:0] data;
`uvm_object_utils(tl_host_single_seq)
`uvm_object_new
constraint req_cnt_eq_1_c { req_cnt == 1; }
virtual function void randomize_req(tl_seq_item req, int idx);
if (!(req.randomize() with {
a_valid_delay inside {[min_req_delay:max_req_delay]};
a_addr == addr;
a_opcode == opcode;
a_size == size;
a_source == source;
a_mask == mask;
a_data == data;})) begin
`uvm_fatal(`gfn, "Cannot randomize req")
end
endfunction
endclass
// disable TL protocol related constraint on a_chan for error cases
class tl_host_custom_seq extends tl_host_single_seq;
`uvm_object_utils(tl_host_custom_seq)
`uvm_object_new
virtual function void randomize_req(REQ req, int idx);
req.disable_a_chan_protocol_constraint();
super.randomize_req(req, idx);
endfunction
endclass
// this seq will send an item that triggers d_error due to protocol violation
class tl_host_protocol_err_seq extends tl_host_single_seq;
`uvm_object_utils(tl_host_protocol_err_seq)
`uvm_object_new
// forever randomize the item until we find one that violates the TL protocol
virtual function void randomize_req(REQ req, int idx);
req.a_valid_delay = $urandom_range(min_req_delay, max_req_delay);
req.a_valid_delay.rand_mode(0);
req.randomize_a_chan_with_protocol_error();
endfunction
endclass
// Device sequence, currently support in-order response
class tl_device_seq extends uvm_sequence#(.REQ(tl_seq_item));
int min_rsp_delay = 0;
int max_rsp_delay = 10;
mem_model_pkg::mem_model mem;
tl_seq_item req_q[$];
bit out_of_order_rsp = 0;
`uvm_object_utils(tl_device_seq)
`uvm_declare_p_sequencer(tl_sequencer)
`uvm_object_new
virtual task body();
fork
forever begin // collect req thread
int req_cnt;
tl_seq_item req;
p_sequencer.a_chan_req_fifo.get(req);
req_q.push_back(req);
`uvm_info(`gfn, $sformatf("Received req[%0d] : %0s",
req_cnt, req.convert2string()), UVM_HIGH)
req_cnt++;
end
forever begin // response thread
int rsp_cnt;
tl_seq_item req, rsp;
wait(req_q.size > 0);
if (out_of_order_rsp) req_q.shuffle();
req = req_q.pop_front();
$cast(rsp, req.clone());
randomize_rsp(rsp);
update_mem(rsp);
start_item(rsp);
finish_item(rsp);
`uvm_info(`gfn, $sformatf("Sent rsp[%0d] : %0s, req: %0s",
rsp_cnt, rsp.convert2string(), req.convert2string()), UVM_HIGH)
rsp_cnt++;
end
join
endtask
virtual function void randomize_rsp(tl_seq_item rsp);
rsp.disable_a_chan_randomization();
if (!(rsp.randomize() with
{rsp.d_valid_delay inside {[min_rsp_delay : max_rsp_delay]};
if (rsp.a_opcode == tlul_pkg::Get) {
rsp.d_opcode == tlul_pkg::AccessAckData;
} else {
rsp.d_opcode == tlul_pkg::AccessAck;
}
rsp.d_size == rsp.a_size;
rsp.d_user == '0; // TODO: Not defined yet, tie it to zero
rsp.d_source == rsp.a_source;})) begin
`uvm_fatal(`gfn, "Cannot randomize rsp")
end
endfunction
virtual function void update_mem(tl_seq_item rsp);
if (mem != null) begin
if (req.a_opcode inside {PutFullData, PutPartialData}) begin
bit [tl_agent_pkg::DataWidth-1:0] data;
data = req.a_data;
for (int i = 0; i < $bits(req.a_mask); i++) begin
if (req.a_mask[i]) begin
mem.write_byte(req.a_addr + i, data[7:0]);
end
data = data >> 8;
end
end else begin
for (int i = 2**req.a_size - 1; i >= 0; i--) begin
rsp.d_data = rsp.d_data << 8;
rsp.d_data[7:0] = mem.read_byte(req.a_addr+i);
end
end
end
endfunction
endclass : tl_device_seq