blob: 7165e9d4ad16f7d369fd0abb808c9543206fb99e [file] [log] [blame]
// Copyright lowRISC contributors.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0
// Frontdoor (indirect) access of JTAG DMI registers for use within the RAL.
//
// JTAG DMI registers are accessed indirectly by writing {op, addr, data} to the JTAG DTM DMI
// register and polling it back for status and the response.
class jtag_dmi_reg_frontdoor extends uvm_reg_frontdoor;
`uvm_object_utils(jtag_dmi_reg_frontdoor)
// Handle to JTAG agent cfg which has the handle to the DTM RAL model and the vif.
jtag_agent_cfg jtag_agent_cfg_h;
// The same frontdoor instance can be attached to all DMI registers, since they all need to be
// accessed via the same shared resource (the DTM DMI register). The semaphore ensures atomicity.
// This is created and set externally, because it needs to be singular for all instances of this
// object.
semaphore jtag_dtm_ral_sem_h;
`uvm_object_new
virtual task body();
csr_field_t csr_or_fld;
uvm_reg_data_t wdata, rdata;
jtag_dtm_reg_block jtag_dtm_ral = jtag_agent_cfg_h.jtag_dtm_ral;
jtag_dmi_op_rsp_e op_rsp;
jtag_dtm_ral_sem_h.get();
`uvm_info(`gfn, $sformatf("DMI CSR req started: %0s", rw_info.convert2string()), UVM_HIGH)
`DV_CHECK_FATAL(rw_info.element_kind inside {UVM_REG, UVM_FIELD})
`DV_CHECK_FATAL(rw_info.kind inside {UVM_READ, UVM_WRITE})
`DV_CHECK_FATAL(rw_info.path == UVM_FRONTDOOR)
csr_or_fld = decode_csr_or_field(rw_info.element);
// Prepare the transaction by setting wdata as concatenated value of {addr, data, op} obtained
// from this transaction.
if (rw_info.kind == UVM_WRITE) begin
wdata = rw_info.value[0];
if (csr_or_fld.field != null) begin
wdata = get_csr_val_with_updated_field(csr_or_fld.field,
csr_or_fld.csr.get_mirrored_value(),
rw_info.value[0]);
end
end
wdata = get_csr_val_with_updated_field(jtag_dtm_ral.dmi.data, '0, wdata);
wdata = get_csr_val_with_updated_field(jtag_dtm_ral.dmi.op, wdata,
(rw_info.kind == UVM_WRITE ? DmiOpWrite : DmiOpRead));
wdata = get_csr_val_with_updated_field(jtag_dtm_ral.dmi.address, wdata,
csr_or_fld.csr.get_address());
// Start the DMI request.
if (!jtag_agent_cfg_h.in_reset) begin
csr_wr(.ptr(jtag_dtm_ral.dmi), .value(wdata), .blocking(1), .predict(1));
// Wait 10 tck cycles to allow the DMI req to complete. If we read the DTM DMI register too
// soon, it may end up setting the in progress sticky bit, causing more time wasted.
jtag_agent_cfg_h.vif.wait_tck(10);
end else begin
`uvm_info(`gfn, "DMI CSR req skipped due to reset", UVM_HIGH)
end
// Poll for completion. Reset DMI if the sticky bit 'InProgress' is set.
`DV_SPINWAIT_EXIT(
do begin
csr_rd(.ptr(jtag_dtm_ral.dmi), .value(rdata), .blocking(1));
op_rsp = jtag_dmi_op_rsp_e'(get_field_val(jtag_dtm_ral.dmi.op, rdata));
`uvm_info(`gfn, $sformatf("DMI CSR req status: %0s", op_rsp.name()), UVM_HIGH)
if (op_rsp == DmiOpInProgress) begin
csr_wr(.ptr(jtag_dtm_ral.dtmcs.dmireset), .value(1), .blocking(1), .predict(1));
end else begin
rw_info.status = op_rsp == DmiOpOk ? UVM_IS_OK : UVM_NOT_OK;
if (rw_info.kind == UVM_READ) begin
rdata = get_field_val(jtag_dtm_ral.dmi.data, rdata);
if (csr_or_fld.field != null) begin
rdata = get_field_val(csr_or_fld.field, rdata);
end
rw_info.value = new[1];
rw_info.value[0] = rdata;
end
end
end while (op_rsp == DmiOpInProgress);,
begin
fork
wait(jtag_agent_cfg_h.in_reset);
// TODO: Make timeout more configurable.
`DV_WAIT_TIMEOUT(jtag_agent_cfg_h.vif.tck_period_ps * 10_000_000 /*10 ms*/)
join_any
end
)
`uvm_info(`gfn, $sformatf("DMI CSR req completed: %0s", rw_info.convert2string()), UVM_HIGH)
jtag_dtm_ral_sem_h.put();
endtask
endclass