blob: 1b5b813e6a77f855f2253489e7c505b12be6a356 [file] [log] [blame]
// Copyright lowRISC contributors.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0
typedef class tl_host_base_seq;
// Class: register adapter type parameterized with the default tl_seq_item type. The idea is to
// extend tl_seq_item for further constraints or customizations if required and create the
// tl_reg_adapter instance with the overridden type.
class tl_reg_adapter #(type ITEM_T = tl_seq_item) extends uvm_reg_adapter;
`uvm_object_param_utils(tl_reg_adapter#(ITEM_T))
// Ensure that when an instance of this adapter is created, the cfg handle below is initialized to
// the `tl_agent_cfg` instance associated with this adapter instance.
tl_agent_cfg cfg;
function new(string name = "tl_reg_adapter");
super.new(name);
// Force the uvm_reg_map to use this sequence to sync with the driver instead.
parent_sequence = tl_host_base_seq#(ITEM_T)::type_id::create("m_tl_host_base_seq");
supports_byte_enable = 1;
provides_responses = 1;
endfunction : new
function uvm_sequence_item reg2bus(const ref uvm_reg_bus_op rw);
uvm_reg_item item = get_item();
ITEM_T bus_req;
bus_req = ITEM_T::type_id::create("bus_req");
// TODO: add a knob to contrl the randomization in case TLUL implementation changes and does
// not support partial read/write
// randomize CSR partial or full read
// for partial read DUT (except memory) always return the entire 4 bytes bus data
// if CSR full read (all bytes are enabled) & !MEM, randomly select full or partial read
// if CSR field read, will do a partial read if protocal allows by setting a_mask to byte_en
if (rw.kind == UVM_READ) begin
if (rw.byte_en == '1 && item.element_kind == UVM_REG) begin // csr full read
`DV_CHECK_RANDOMIZE_WITH_FATAL(bus_req,
a_opcode == tlul_pkg::Get;
a_addr[AddrWidth-1:2] == rw.addr[AddrWidth-1:2];
$countones(a_mask) dist {MaskWidth :/ 1,
[0:MaskWidth-1] :/ 1};)
end else begin // csr field read
`DV_CHECK_RANDOMIZE_WITH_FATAL(bus_req,
a_opcode == tlul_pkg::Get;
a_addr[AddrWidth-1:2] == rw.addr[AddrWidth-1:2];
a_mask == rw.byte_en;)
end
end else begin // randomize CSR partial or full write
// Actual width of the CSR may be < DataWidth bits depending on fields and their widths
// In that case, the transaction size in bytes and partial write mask need to be at least as
// wide as the CSR to be a valid transaction. Otherwise, the DUT can return an error response
int msb;
// Check if csr addr or mem addr; accordingly, get the msb bit.
if (item.element_kind == UVM_REG) begin
dv_base_reg csr;
uvm_object rg = item.element;
`downcast(csr, rg)
msb = csr.get_msb_pos();
end else if (item.element_kind == UVM_MEM) begin
msb = DataWidth - 1;
end else begin
`uvm_fatal(`gfn, $sformatf("Unexpected address 0x%0h", rw.addr))
end
`DV_CHECK_RANDOMIZE_WITH_FATAL(bus_req,
a_opcode inside {PutFullData, PutPartialData};
a_addr == rw.addr;
a_data == rw.data;
a_mask[0] == 1;
$countones(a_mask) > (msb / 8);)
end
if (cfg.csr_access_abort_pct_in_adapter > $urandom_range(0, 100)) begin
bus_req.req_abort_after_a_valid_len = 1;
`uvm_info(`gtn, $sformatf("tl reg req item is allowed to be aborted"), UVM_MEDIUM)
end
`uvm_info(`gtn, {"tl_reg_adapter::reg2bus: ", bus_req.convert2string()}, UVM_HIGH)
return bus_req;
endfunction : reg2bus
function void bus2reg(uvm_sequence_item bus_item, ref uvm_reg_bus_op rw);
ITEM_T bus_rsp;
`downcast(bus_rsp, bus_item)
rw.kind = bus_rsp.is_write() ? UVM_WRITE : UVM_READ;
rw.addr = bus_rsp.a_addr;
rw.data = (rw.kind == UVM_WRITE) ? bus_rsp.a_data : bus_rsp.d_data;
rw.byte_en = bus_rsp.a_mask;
`DV_CHECK_EQ(bus_rsp.d_source, bus_rsp.a_source)
// expect d_error = 0 as we won't drive any error case through RAL
`DV_CHECK_EQ(bus_rsp.d_error, 0)
// indicate if the item is completed successfully for upper level to update predict value
rw.status = !bus_rsp.req_completed ? UVM_NOT_OK : UVM_IS_OK;
`uvm_info(`gtn, {"tl_reg_adapter::bus2reg: ", bus_rsp.convert2string()}, UVM_HIGH)
endfunction: bus2reg
endclass : tl_reg_adapter