blob: 5c6ed34cc3add55104ec050c169d5c64c8235d13 [file] [log] [blame]
// Copyright lowRISC contributors.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0
// Pseudo driver to access CSR via JTAG TAP
class jtag_riscv_driver extends dv_base_driver #(jtag_riscv_item, jtag_riscv_agent_cfg);
protected bit do_hard_reset;
`uvm_object_utils(jtag_riscv_driver)
`uvm_object_new
virtual task reset_signals();
forever begin
wait (cfg.in_reset == 1);
`uvm_info(`gfn, "reset_signals: cfg.in_reset=1'b1", UVM_MEDIUM)
do_hard_reset = 1;
// Assert JTAG TRST
// This clears the DMI Request and Response FIFOs.
cfg.m_jtag_agent_cfg.vif.do_trst_n(2);
cfg.rv_dm_activated = 0;
wait (cfg.in_reset == 0);
`uvm_info(`gfn, "reset_signals: cfg.in_reset=1'b0", UVM_MEDIUM)
end
endtask
// drive trans received from sequencer
virtual task get_and_drive();
`uvm_info(`gfn, "get_and_drive: STARTED", UVM_MEDIUM)
forever begin
seq_item_port.get_next_item(req);
`uvm_info(`gfn, {"got request: ", req.sprint(uvm_default_line_printer)}, UVM_HIGH)
accept_tr(req);
`downcast(rsp, req.clone())
rsp.set_id_info(req);
seq_item_port.item_done();
`DV_SPINWAIT_EXIT(
drive_jtag(rsp);,
wait(cfg.in_reset == 1);,)
if (cfg.in_reset) rsp.status = cfg.status_in_reset;
seq_item_port.put_response(rsp);
end
endtask
protected virtual task drive_jtag(ITEM_T drive_req);
bit [DMI_DATAW-1:0] dout;
bit [DMI_DATAW-1:0] rdata;
bit [ DMI_OPW-1:0] status;
`uvm_info(`gfn, {"drive_jtag: ", drive_req.sprint(uvm_default_line_printer)}, UVM_HIGH)
// Mark start of transaction processing
void'(begin_tr(drive_req));
if (do_hard_reset) begin
// A previous reset happend via cfg.in_reset
// Make sure the DMI is reset
if (!cfg.in_reset) begin
send_riscv_ir_req(JtagDtmCsr);
send_dtmcs_dr_req(DmiHardReset);
do_hard_reset = 0;
end
end
send_riscv_ir_req(JtagDmiAccess);
if (drive_req.activate_rv_dm) begin
activate_rv_dm(drive_req.status);
end else begin
if (cfg.is_rv_dm) begin
bit [DMI_DATAW-1:0] sbcs_val = (2'b10 << SbAccess) | ('b1 << SbBusy);
if (drive_req.op == DmiRead) sbcs_val |= 'b1 << SbReadOnAddr;
`DV_CHECK_FATAL(cfg.rv_dm_activated, "Please activate rv_dm before accessing CSRs!")
// If using rv_dm to access csr, need to send the following seq:
// 1). Set busy bit in sbcs.
// 2). Write address to SBAddress0.
// 3). Write/Read csr data via SbData0.
send_csr_req(.op(DmiWrite), .data(sbcs_val), .addr(Sbcs), .dout(dout), .status(status));
send_csr_req(.op(DmiWrite), .data(drive_req.addr), .addr(SbAddress0), .dout(dout),
.status(status));
// For RV_DM jtag operation, all requests except sbcs write will be blocking until sbcs
// indicates the request is done.
if (drive_req.op == DmiRead) wait_sbcs_idle();
send_csr_req(.op(drive_req.op), .data(drive_req.data), .addr(SbData0), .dout(dout),
.status(status));
end else begin
// Drive DR with operation type, address, and data.
send_csr_req(.op(drive_req.op), .data(drive_req.data), .addr(drive_req.addr), .dout(dout),
.status(status));
end
// Get status of previous transfer
drive_req.status = status;
// Update CSR read data
if (drive_req.op == DmiRead) drive_req.data = dout;
end
// Mark end of transaction processing
end_tr(drive_req);
endtask
protected virtual task send_riscv_ir_req(jtag_ir_e riscv_ir_req);
jtag_ir_seq m_ir_seq;
`uvm_create_obj(jtag_ir_seq, m_ir_seq);
`DV_CHECK_RANDOMIZE_WITH_FATAL(m_ir_seq,
ir_len == DMI_IRW;
ir == riscv_ir_req;)
m_ir_seq.start(cfg.jtag_sequencer_h);
endtask
protected virtual task send_dtmcs_dr_req(jtag_dtmcs_e dtmcs_req_idx);
jtag_dr_seq m_dr_seq;
`uvm_create_obj(jtag_dr_seq, m_dr_seq);
`DV_CHECK_RANDOMIZE_WITH_FATAL(m_dr_seq,
dr_len == DTMCS_DRW;
dr == 1 << dtmcs_req_idx;)
m_dr_seq.start(cfg.jtag_sequencer_h);
endtask
// This task finishes a csr read/write transaction:
// 1. Driving JTAG's DR register to start the transaction.
// 2. Check status until the transaction is done.
protected virtual task send_csr_req(input bit [DMI_OPW-1:0] op,
input bit [DMI_DATAW-1:0] data,
input bit [DMI_ADDRW-1:0] addr,
output bit [DMI_DRW-1:0] dout,
output bit [DMI_OPW-1:0] status);
// Drive DR with operation type, address, and data.
send_csr_dr_req(.op(op), .data(data), .addr(addr), .dout(dout));
// Get status of previous transfer
check_csr_req_status(.status(status), .rdata(dout));
endtask
// This task sends a CSR register read/write request via JTAG data register.
protected virtual task send_csr_dr_req(
input bit [DMI_OPW-1:0] op, input bit [DMI_DATAW-1:0] data,
input bit [DMI_ADDRW-1:0] addr, output bit [DMI_DRW-1:0] dout);
jtag_dr_seq m_dr_seq;
`uvm_create_obj(jtag_dr_seq, m_dr_seq);
`DV_CHECK_RANDOMIZE_WITH_FATAL(m_dr_seq,
dr_len == DMI_DRW;
dr == {addr, data, op};)
m_dr_seq.start(cfg.jtag_sequencer_h);
// Might be no response item if the sequence has been killed
if (m_dr_seq.rsp != null) dout = m_dr_seq.rsp.dout;
endtask
// This task checks a CSR register read/write request status via data request.
// This task will output operation status and rdata (this rdata is only meaningful if it is a
// read operation).
protected virtual task check_csr_req_status(output bit [DMI_OPW-1:0] status,
output bit [DMI_DATAW-1:0] rdata);
while (!cfg.in_reset) begin
bit [DMI_DRW-1:0] dout;
if (!cfg.in_reset) send_csr_dr_req(.op(DmiStatus), .addr(0), .data(0), .dout(dout));
status = dout[0 +: DMI_OPW];
// The DmiInProgress status is sticky and has to be cleared by dmireset via DTMCS.
// Accessing the reserved value means there is also something wrong with the JTAG sequence,
// potentially IR request, so also need to reset.
if (status inside {DmiInProgress, DmiReserved}) begin
if (!cfg.in_reset) send_riscv_ir_req(JtagDtmCsr);
if (!cfg.in_reset) send_dtmcs_dr_req(DmiReset);
if (!cfg.in_reset) send_riscv_ir_req(JtagDmiAccess);
end
if (status === DmiNoErr || status === DmiFail) begin
rdata = dout[DMI_OPW +: DMI_DATAW];
break;
end
end
endtask
protected virtual task activate_rv_dm(output jtag_op_status_e activation_status);
bit [bus_params_pkg::BUS_DW-1:0] dmctrl_val, sbcs_val;
bit [DMI_OPW-1:0] status;
int cnter;
// Set dmcontrol's dmactive bit.
while (dmctrl_val == 0 && cnter < cfg.max_rv_dm_activation_attempts) begin
cnter++;
send_csr_req(.op(DmiWrite), .data(1), .addr(DmControl), .dout(dmctrl_val), .status(status));
send_csr_req(.op(DmiRead), .data(0), .addr(DmControl), .dout(dmctrl_val), .status(status));
end
if (cnter >= cfg.max_rv_dm_activation_attempts) begin
string msg = $sformatf("Could not activate RV_DM after %0d attempts!", cnter);
if (cfg.allow_rv_dm_activation_fail) begin
`uvm_info(`gfn, msg, UVM_LOW)
end else begin
`uvm_error(`gfn, msg)
end
activation_status = DmiFail;
return;
end
// Read system bus access control and status register.
// Once the sbcs value is not 0, then RV_DM jtag is ready.
while (sbcs_val == 0) begin
send_csr_req(.op(DmiRead), .data(0), .addr(Sbcs), .dout(sbcs_val), .status(status));
end
// Ensure the RV_DM is set to correct bus width.
`DV_CHECK_EQ(sbcs_val[SbAccess32], 1, "expect SBA width to be 32 bits!", error, msg_id)
cfg.rv_dm_activated = 1;
activation_status = DmiNoErr;
endtask
protected virtual task wait_sbcs_idle();
bit [DMI_DATAW-1:0] sbcs_val;
`DV_SPINWAIT(
do begin
bit [DMI_OPW-1:0] status;
send_csr_req(.op(DmiRead), .data(0), .addr(Sbcs), .dout(sbcs_val), .status(status));
end while (sbcs_val[SbBusy] == 1);,
"timeout waiting for sbcs to be idle", default_jtag_timeout
)
endtask
endclass