Skip to content

Commit

Permalink
[jtag_dmi_monitor,dv] Make monitor pickier about the DMI sequence
Browse files Browse the repository at this point in the history
It turns out that seeing a DmiOpOk JTAG response isn't *quite* enough
to conclude that the DMI is not busy. If you're a bit unlucky, it can
only realise that it's busy at the same time as it responds with
DmiOpOk the first time.

This change makes the monitor "safer" in this setting. It will only
conclude that a requested DMI operation has been sent after it has
seen another response since the original request.

Of course, there's still a question about what to do if we see a
request immediately after a request. The code here goes for the easy
option and fails instantly. We can fix the drivers to ensure they
don't do that!

Signed-off-by: Rupert Swarbrick <rswarbrick@lowrisc.org>
  • Loading branch information
rswarbrick committed Apr 17, 2024
1 parent c592837 commit 0ac161f
Showing 1 changed file with 48 additions and 23 deletions.
71 changes: 48 additions & 23 deletions hw/dv/sv/jtag_dmi_agent/jtag_dmi_monitor.sv
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,17 @@ class jtag_dmi_monitor #(type ITEM_T = jtag_dmi_item) extends dv_base_monitor#(
// address.
bit dmi_selected = 1'b0;

// Normally, we consider a request to have gone through if the JTAG transaction has a rsp_op
// other than DmiOpInProgress. Unfortunately, there's a confusing window immediately after a
// request is made: the first JTAG transaction immediately after a request won't necessarily
// realise it should respond with DmiOpInProgress.
//
// To avoid things getting out of sync, we don't have an opinion about whether that transaction
// managed to cause a request. If we see a transaction other than DmiOpNone in that position,
// the monitor raises an error. The quiet_period flag is set if we can't trust the response of
// the next JTAG transaction.
bit quiet_period = 1'b0;

forever begin
bit is_ir_update, is_dr_update;

Expand Down Expand Up @@ -72,46 +83,60 @@ class jtag_dmi_monitor #(type ITEM_T = jtag_dmi_item) extends dv_base_monitor#(

// At this point, we know we're operating on the DMI register. Handle any DR update.
if (is_dr_update) begin

// Item has both the response for the previous request and a new DMI request. Capture the
// response first.
bit busy = capture_response(jtag_item);

// If the TAP was not busy, any previous DMI transaction has completed so the TAP will also
// have seen the request.
if (!busy) capture_request(jtag_item);
jtag_dmi_op_req_e req_op = jtag_dmi_op_req_e'(get_field_val(cfg.jtag_dtm_ral.dmi.op,
jtag_item.dr));

if (quiet_period) begin
if (req_op != DmiOpNone) begin
`uvm_error(`gfn, $sformatf("JTAG operation %0d != DmiOpNone in quiet period", req_op))
end
quiet_period = 1'b0;
end else begin
// The item has both the response for the previous request and a new DMI request. Capture
// the response first.
bit in_progress = capture_response(jtag_item);

// Since we know we are not in the quiet period, the DUT will have taken the request if it
// did not report an operation in progress. Set the quiet_period flag unless the operation
// was DmiOpNone.
if (req_op != DmiOpNone && !in_progress) begin
capture_request(jtag_item);
quiet_period = 1'b1;
end
end
end
end
endtask

// Capture a new DMI request, if the previous DMI transaction is not in progress.
//
// Returns true if the response was captured, or if there was no previous request, false if the
// response returned was busy.
// Returns true if there was a previous request which is still in progress. Otherwise returns
// false.
virtual function bit capture_response(jtag_item jtag_item);
jtag_dmi_op_rsp_e rsp_op = jtag_dmi_op_rsp_e'(
get_field_val(cfg.jtag_dtm_ral.dmi.op, jtag_item.dout));

if (rsp_op == DmiOpInProgress) begin
return 1;
end

if (dmi_req_q.size() != 0) begin
if (rsp_op == DmiOpInProgress) begin
return 1;
end else begin
ITEM_T dmi_item = dmi_req_q.pop_front();
dmi_item.rsp_op = rsp_op;
if (dmi_item.req_op == DmiOpRead) begin
uvm_reg_data_t data = get_field_val(cfg.jtag_dtm_ral.dmi.data,
jtag_item.dout);
dmi_item.rdata = data;
end
`uvm_info(`gfn, $sformatf("Writing DMI item to analysis_port: %0s",
dmi_item.sprint(uvm_default_line_printer)), UVM_HIGH)
analysis_port.write(dmi_item);
ITEM_T dmi_item = dmi_req_q.pop_front();
dmi_item.rsp_op = rsp_op;
if (dmi_item.req_op == DmiOpRead) begin
uvm_reg_data_t data = get_field_val(cfg.jtag_dtm_ral.dmi.data,
jtag_item.dout);
dmi_item.rdata = data;
end
`uvm_info(`gfn, $sformatf("Writing DMI item to analysis_port: %0s",
dmi_item.sprint(uvm_default_line_printer)), UVM_HIGH)
analysis_port.write(dmi_item);
end else begin
if (rsp_op != DmiOpOk) begin
`uvm_error(`gfn, $sformatf("Non-ok response seen with no previous DMI request."))
end
end

return 0;
endfunction

Expand Down

0 comments on commit 0ac161f

Please sign in to comment.