Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[hw,dma,rtl] Add STATUS.chunk_done bit #24108

Merged
merged 3 commits into from
Nov 12, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 12 additions & 1 deletion hw/ip/dma/data/dma.hjson
Original file line number Diff line number Diff line change
Expand Up @@ -100,9 +100,11 @@
interrupt_list: [
{ name: "dma_done"
desc: "DMA operation has been completed."
type: "status"
}
{ name: "dma_error"
desc: "DMA error has occurred. DMA_STATUS.error_code register shows the details."
type: "status"
}
]
alert_list: [
Expand Down Expand Up @@ -541,7 +543,6 @@
resval: 0x0
hwaccess: "hrw"
swaccess: "ro"
hwaccess: "hrw"
desc: '''
DMA operation is active if this bit is set.
DMA engine clears this bit when operation is complete.
Expand Down Expand Up @@ -583,6 +584,16 @@
This value is cleared on the initial transfer and set when the digest is written.
'''
}
{ bits: "5"
name: "chunk_done"
resval: 0x0
hwaccess: "hrw"
desc: '''
Transfer of a single chunk is complete.
Only raised for multi-chunk memory-to-memory transfers.
Cleared automatically by the hardware when starting the transfer of a new chunk.
'''
}
]
}
{ name: "ERROR_CODE"
Expand Down
13 changes: 7 additions & 6 deletions hw/ip/dma/doc/registers.md
Original file line number Diff line number Diff line change
Expand Up @@ -76,14 +76,14 @@ Interrupt State Register
### Fields

```wavejson
{"reg": [{"name": "dma_done", "bits": 1, "attr": ["rw1c"], "rotate": -90}, {"name": "dma_error", "bits": 1, "attr": ["rw1c"], "rotate": -90}, {"bits": 30}], "config": {"lanes": 1, "fontsize": 10, "vspace": 110}}
{"reg": [{"name": "dma_done", "bits": 1, "attr": ["ro"], "rotate": -90}, {"name": "dma_error", "bits": 1, "attr": ["ro"], "rotate": -90}, {"bits": 30}], "config": {"lanes": 1, "fontsize": 10, "vspace": 110}}
```

| Bits | Type | Reset | Name | Description |
|:------:|:------:|:-------:|:----------|:--------------------------------------------------------------------------|
| 31:2 | | | | Reserved |
| 1 | rw1c | 0x0 | dma_error | DMA error has occurred. DMA_STATUS.error_code register shows the details. |
| 0 | rw1c | 0x0 | dma_done | DMA operation has been completed. |
| 1 | ro | 0x0 | dma_error | DMA error has occurred. DMA_STATUS.error_code register shows the details. |
| 0 | ro | 0x0 | dma_done | DMA operation has been completed. |

## INTR_ENABLE
Interrupt Enable Register
Expand Down Expand Up @@ -532,17 +532,18 @@ Other values are reserved.
Status indication for DMA data movement.
- Offset: `0x48`
- Reset default: `0x0`
- Reset mask: `0x1f`
- Reset mask: `0x3f`

### Fields

```wavejson
{"reg": [{"name": "busy", "bits": 1, "attr": ["ro"], "rotate": -90}, {"name": "done", "bits": 1, "attr": ["rw1c"], "rotate": -90}, {"name": "aborted", "bits": 1, "attr": ["rw1c"], "rotate": -90}, {"name": "error", "bits": 1, "attr": ["rw1c"], "rotate": -90}, {"name": "sha2_digest_valid", "bits": 1, "attr": ["ro"], "rotate": -90}, {"bits": 27}], "config": {"lanes": 1, "fontsize": 10, "vspace": 190}}
{"reg": [{"name": "busy", "bits": 1, "attr": ["ro"], "rotate": -90}, {"name": "done", "bits": 1, "attr": ["rw1c"], "rotate": -90}, {"name": "aborted", "bits": 1, "attr": ["rw1c"], "rotate": -90}, {"name": "error", "bits": 1, "attr": ["rw1c"], "rotate": -90}, {"name": "sha2_digest_valid", "bits": 1, "attr": ["ro"], "rotate": -90}, {"name": "chunk_done", "bits": 1, "attr": ["rw1c"], "rotate": -90}, {"bits": 26}], "config": {"lanes": 1, "fontsize": 10, "vspace": 190}}
```

| Bits | Type | Reset | Name | Description |
|:------:|:------:|:-------:|:------------------|:-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| 31:5 | | | | Reserved |
| 31:6 | | | | Reserved |
| 5 | rw1c | 0x0 | chunk_done | Transfer of a single chunk is complete. Only raised for multi-chunk memory-to-memory transfers. Cleared automatically by the hardware when starting the transfer of a new chunk. |
| 4 | ro | 0x0 | sha2_digest_valid | Indicates whether the SHA2_DIGEST register contains a valid digest. This value is cleared on the initial transfer and set when the digest is written. |
| 3 | rw1c | 0x0 | error | Error occurred during the operation. ERROR_CODE register denotes the source of the error. |
| 2 | rw1c | 0x0 | aborted | Set once aborted operation drains. |
Expand Down
2 changes: 2 additions & 0 deletions hw/ip/dma/dv/env/dma_env_cov.sv
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,7 @@ endgroup
covergroup dma_status_cg with function sample(
bit busy,
bit done,
bit chunk_done,
bit aborted,
bit error,
bit sha2_digest_valid
Expand All @@ -200,6 +201,7 @@ covergroup dma_status_cg with function sample(
option.name = "dma_status_cg";
cp_status_busy: coverpoint busy;
cp_status_done: coverpoint done;
cp_status_chunk_done: coverpoint chunk_done;
cp_status_aborted: coverpoint aborted;
cp_status_error: coverpoint error;
cp_sha2_digest_valid: coverpoint sha2_digest_valid;
Expand Down
2 changes: 2 additions & 0 deletions hw/ip/dma/dv/env/dma_env_pkg.sv
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,10 @@ package dma_env_pkg;
parameter uint SYS_ADDR_WIDTH = 64;

// Index of interrupt in intf_vif
// TODO: rename `DMA_x` to indicate that they are interrupt numbers.
parameter uint DMA_DONE = 0;
parameter uint DMA_ERROR = 1;
parameter uint NumDmaInterrupts = 2;

// Completion status bits (DV-internal)
typedef enum {
Expand Down
87 changes: 68 additions & 19 deletions hw/ip/dma/dv/env/dma_scoreboard.sv
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,10 @@ class dma_scoreboard extends cip_base_scoreboard #(

// Interrupt enable state
bit [NUM_MAX_INTERRUPTS-1:0] intr_enable;
// Interrupt test state (contributes to `intr_state`).
bit [NUM_MAX_INTERRUPTS-1:0] intr_test;
// Hardware interrupt state (contributes to `intr_state`).
bit [NUM_MAX_INTERRUPTS-1:0] intr_state_hw;

// Prediction of the state of an interrupt signal from the DUT.
typedef struct packed {
Expand All @@ -60,7 +64,7 @@ class dma_scoreboard extends cip_base_scoreboard #(
uint num_fifo_reg_write;
// Variable to store clear_intr_src register intended for use in monitor_lsio_trigger task
// since ref argument can not be used in fork-join_none
bit[31:0] clear_intr_src;
bit [31:0] clear_intr_src;
bit [TL_DW-1:0] exp_digest[16];

// Allow up to this number of clock cycles from CSR modification until interrupt signal change.
Expand Down Expand Up @@ -474,6 +478,7 @@ class dma_scoreboard extends cip_base_scoreboard #(
if (item.d_error && !tl_error_suppressed) begin
`uvm_info(`gfn, "Bus error detected", UVM_MEDIUM)
predict_interrupts(BusErrorToIntrLatency, 1 << DMA_ERROR, intr_enable);
intr_state_hw[DMA_ERROR] = 1'b1;
end else if (got_dest_item) begin
// Is this the final destination write?
//
Expand All @@ -483,6 +488,7 @@ class dma_scoreboard extends cip_base_scoreboard #(
// Whether a 'DONE' interrupt is expected depends upon whether it is enabled.
`uvm_info(`gfn, "Final write completed", UVM_MEDIUM)
predict_interrupts(WriteToDoneLatency, 1 << DMA_DONE, intr_enable);
intr_state_hw[DMA_DONE] = 1'b1;
end
end
endtask
Expand Down Expand Up @@ -570,6 +576,8 @@ class dma_scoreboard extends cip_base_scoreboard #(
abort_via_reg_write = 0;
fifo_intr_cleared = 0;
intr_enable = 0;
intr_test = 0;
intr_state_hw = 0;
endfunction

// Method to check if DMA interrupt is expected
Expand All @@ -593,8 +601,7 @@ class dma_scoreboard extends cip_base_scoreboard #(
// by the function `predict_interrupts` above.
if (cfg.under_reset) begin
// Interrupts shall be deasserted by DUT reset, and any predictions no longer apply.
`uvm_info(`gfn, "Clearing interrupt predictions", UVM_MEDIUM)
for (uint i = 0; i < NUM_MAX_INTERRUPTS; i++) exp_intr_queue[i].delete();
clear_intr_predictions();
prev_intr = 'b0;
end else if (cfg.en_scb) begin
bit [NUM_MAX_INTERRUPTS-1:0] exp_intr;
Expand Down Expand Up @@ -658,10 +665,14 @@ class dma_scoreboard extends cip_base_scoreboard #(
// Note: this explicitly does NOT mean that they must CHANGE to achieve that state; only that
// they must be in that state by then.
function void predict_interrupts(uint max_delay, bit [31:0] intr_affected, bit [31:0] exp_state);
// Clear all bits that do not map to defined interrupts, to avoid confusion in the log messages.
intr_affected &= {NumDmaInterrupts{1'b1}};
exp_state &= {NumDmaInterrupts{1'b1}};

`uvm_info(`gfn, $sformatf("Predicting interrupt [0,%0x) -> intr_affected 0x%x == 0x%0x",
max_delay, intr_affected, exp_state), UVM_HIGH)

for (uint i = 0; i < NUM_MAX_INTERRUPTS && |intr_affected; i++) begin
for (uint i = 0; i < NumDmaInterrupts && |intr_affected; i++) begin
if (intr_affected[i]) begin
dma_intr_pred_t predict;
predict.delay = max_delay;
Expand All @@ -672,6 +683,12 @@ class dma_scoreboard extends cip_base_scoreboard #(
end
endfunction

// Clear all pending interrupt predictions; these are no longer expected to occur.
function void clear_intr_predictions();
`uvm_info(`gfn, "Clearing interrupt predictions", UVM_MEDIUM)
for (uint i = 0; i < NUM_MAX_INTERRUPTS; i++) exp_intr_queue[i].delete();
endfunction

// Task to monitor LSIO trigger and update scoreboard internal variables
task monitor_lsio_trigger();
fork
Expand Down Expand Up @@ -748,6 +765,9 @@ class dma_scoreboard extends cip_base_scoreboard #(
endcase
endfunction

// Returns the bitmap of Status-type interrupts that are set because of bits in the `status`
// register being asserted.

// Utility function to check the contents of the destination memory/FIFO against the
// corresponding reference source data.
function void check_data(ref dma_seq_item dma_config, bit [63:0] src_addr, bit [63:0] dst_addr,
Expand Down Expand Up @@ -810,17 +830,27 @@ class dma_scoreboard extends cip_base_scoreboard #(
predict_interrupts(CSRtoIntrLatency, `gmv(ral.intr_state), item.a_data);
end
"intr_state": begin
// Writing 1 to an INTR_STATE bit clears the corresponding asserted interrupt.
predict_interrupts(CSRtoIntrLatency, item.a_data & `gmv(ral.intr_enable), 0);
// Writing 1 to an INTR_STATE bit clears the corresponding asserted 'Event' interrupt;
// Status type interrupts are unaffected.
uvm_reg_data_t intr = item.a_data & `gmv(ral.intr_enable) & ~ral.intr_state.get_ro_mask();
predict_interrupts(CSRtoIntrLatency, intr, 0);
end
"intr_test": begin
// The 'Read Only' fields tell us which are Status-type interrupts.
uvm_reg_data_t ro_mask = ral.intr_state.get_ro_mask();
uvm_reg_data_t now_set;

`uvm_info(`gfn, $sformatf("intr_test write 0x%x with enables 0x%0x",
item.a_data, intr_enable), UVM_HIGH)

// Should raise all tested interrupts that are enabled at the time of the test;
// the intr_state bit and the interrupt line then remain high until cleared.
// Test bits are fire-and-forget; they are not retained anywhere.
predict_interrupts(CSRtoIntrLatency, item.a_data, `gmv(ral.intr_enable));
//
// For Status-type interrupts we must retain the fact that they are asserted because of
// the `intr_test` register.
intr_test = item.a_data & ro_mask;
now_set = item.a_data | intr_state_hw;
predict_interrupts(CSRtoIntrLatency, item.a_data | ro_mask, now_set & intr_enable);
end
"src_addr_lo": begin
dma_config.src_addr[31:0] = item.a_data;
Expand Down Expand Up @@ -953,6 +983,16 @@ class dma_scoreboard extends cip_base_scoreboard #(
index = get_index_from_reg_name(csr.get_name());
dma_config.intr_src_wr_val[index] = item.a_data;
end
"status": begin
uvm_reg_data_t clearing = 0;
clearing[DMA_DONE] = get_field_val(ral.status.done, item.a_data);
clearing[DMA_ERROR] = get_field_val(ral.status.error, item.a_data);
// Clearing the hardware contribution to the `intr_state` fields.
intr_state_hw &= ~clearing;
// Clearing the status bits also clears the corresponding Status-type interrupt(s) unless
// the `intr_test` register is forcing them.
predict_interrupts(CSRtoIntrLatency, clearing, intr_test & intr_enable);
end
"control": begin
bit go, initial_transfer, start_transfer;
// Update the 'Aborted' prediction in response to setting the CONTROL.abort bit
Expand Down Expand Up @@ -1063,24 +1103,28 @@ class dma_scoreboard extends cip_base_scoreboard #(
case (csr.get_name())
"intr_state": begin
`uvm_info(`gfn, $sformatf("intr_state = %0x", item.d_data), UVM_MEDIUM)
do_read_check = 1;
// RAL is unaware of the combined contributions of `intr_test` and the `status` register.
`DV_CHECK_EQ(item.d_data, intr_test | intr_state_hw, "Mismatched interrupt state")
do_read_check = 1'b0;
end
"status": begin
bit busy, done, aborted, error, sha2_digest_valid;
bit busy, done, chunk_done, aborted, error, sha2_digest_valid;
bit exp_aborted = abort_via_reg_write;

do_read_check = 1'b0;
busy = get_field_val(ral.status.busy, item.d_data);
done = get_field_val(ral.status.done, item.d_data);
aborted = get_field_val(ral.status.aborted, item.d_data);
error = get_field_val(ral.status.error, item.d_data);
chunk_done = get_field_val(ral.status.chunk_done, item.d_data);
sha2_digest_valid = get_field_val(ral.status.sha2_digest_valid, item.d_data);

if (done || aborted || error) begin
if (done || aborted || error || chunk_done ) begin
string reasons;
if (done) reasons = "Done ";
if (aborted) reasons = {reasons, "Aborted "};
if (error) reasons = {reasons, "Error" };
if (done) reasons = "Done ";
if (aborted) reasons = {reasons, "Aborted "};
if (error) reasons = {reasons, "Error" };
if (chunk_done) reasons = {reasons, "ChunkDone "};
operation_in_progress = 1'b0;
`uvm_info(`gfn, $sformatf("Detected end of DMA operation (%s)", reasons), UVM_MEDIUM)
// Clear variables
Expand All @@ -1091,31 +1135,36 @@ class dma_scoreboard extends cip_base_scoreboard #(
!(aborted || error) && // no abort or error detected
!(src_tl_error_detected || dst_tl_error_detected))
begin // no TL error
// Check if number of bytes transferred is as expected at this point in the transfer
`DV_CHECK_EQ(num_bytes_transferred, exp_bytes_transferred,
$sformatf("act_data_size: %0d exp_data_size: %0d",
num_bytes_transferred, exp_bytes_transferred))
// Check if number of bytes transferred is as expected at this point in the transfer
`DV_CHECK_EQ(num_bytes_transferred, exp_bytes_transferred,
$sformatf("act_data_size: %0d exp_data_size: %0d",
num_bytes_transferred, exp_bytes_transferred))
end
// STATUS.aborted should only be true if we requested an Abort.
// However, the transfer may just have completed successfully even if we did request an
// Abort and it may even have terminated in response to a TL-UL error for some sequences.
if (abort_via_reg_write) begin
bit bus_error = src_tl_error_detected | dst_tl_error_detected;
`DV_CHECK_EQ(|{aborted, bus_error, done}, 1'b1, "Transfer neither Aborted nor completed.")
// Invalidate any still-pending interrupt changes; the abort may have occurred after
// the final write has completed but before the DMA controller actually completes the
// transfer because e.g. the SHA digest calculation is still completing.
clear_intr_predictions();
end else begin
`DV_CHECK_EQ(aborted, 1'b0, "STATUS.aborted bit set when not expected")
end
if (cfg.en_cov) begin
// Sample dma status and error code
cov.status_cg.sample(.busy (busy),
.done (done),
.chunk_done (chunk_done),
.aborted (aborted),
.error (error),
.sha2_digest_valid (sha2_digest_valid));
end
// Check results after each chunk of the transfer (memory-to-memory) or after the complete
// transfer (handshaking mode).
if (dma_config.is_valid_config && done) begin
if (dma_config.is_valid_config && (done || chunk_done)) begin
if (num_bytes_transferred >= dma_config.total_data_size) begin
// SHA digest (expecting zeros if unused)
// When using inline hashing, sha2_digest_valid must be raised at the end
Expand Down
6 changes: 0 additions & 6 deletions hw/ip/dma/dv/env/seq_lib/dma_base_vseq.sv
Original file line number Diff line number Diff line change
Expand Up @@ -386,12 +386,6 @@ class dma_base_vseq extends cip_base_vseq #(
cfg_interrupts(interrupts, enable);
endtask : enable_interrupts

// Clear one or more interrupts
task clear_interrupts(bit [31:0] clear);
`uvm_info(`gfn, $sformatf("DMA: Clear Interrupt(s) 0x%0x", clear), UVM_HIGH)
csr_wr(ral.intr_state, clear);
endtask : clear_interrupts

// Task: Enable Handshake Interrupt Enable
task enable_handshake_interrupt();
`uvm_info(`gfn, "DMA: Assert Interrupt Enable", UVM_HIGH)
Expand Down
3 changes: 0 additions & 3 deletions hw/ip/dma/dv/env/seq_lib/dma_generic_vseq.sv
Original file line number Diff line number Diff line change
Expand Up @@ -120,8 +120,6 @@ class dma_generic_vseq extends dma_base_vseq;
ral.status.error.set(1'b1);
csr_update(ral.status);
end
// Clear the Error interrupt if set
clear_interrupts(1 << DMA_ERROR);
endtask

virtual task body();
Expand Down Expand Up @@ -276,7 +274,6 @@ class dma_generic_vseq extends dma_base_vseq;
if (status[StatusDone]) begin
// Clear STATUS.done bit and then clear the interrupt, if enabled.
clear_done();
clear_interrupts(1 << DMA_DONE);
status[StatusDone] = 1'b0;
end
if (status[StatusError]) begin
Expand Down
Loading
Loading