diff --git a/hw/ip/dma/data/dma.hjson b/hw/ip/dma/data/dma.hjson index 5a731481166c7..2807840fbc554 100644 --- a/hw/ip/dma/data/dma.hjson +++ b/hw/ip/dma/data/dma.hjson @@ -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: [ @@ -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. @@ -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" diff --git a/hw/ip/dma/doc/registers.md b/hw/ip/dma/doc/registers.md index 41ba14f791551..43ce2c4ccbccd 100644 --- a/hw/ip/dma/doc/registers.md +++ b/hw/ip/dma/doc/registers.md @@ -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 @@ -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. | diff --git a/hw/ip/dma/dv/env/dma_env_cov.sv b/hw/ip/dma/dv/env/dma_env_cov.sv index c8d94a0455281..bf40a7147ae6c 100644 --- a/hw/ip/dma/dv/env/dma_env_cov.sv +++ b/hw/ip/dma/dv/env/dma_env_cov.sv @@ -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 @@ -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; diff --git a/hw/ip/dma/dv/env/dma_env_pkg.sv b/hw/ip/dma/dv/env/dma_env_pkg.sv index a43bf26b3b18c..c86adc33a5d99 100644 --- a/hw/ip/dma/dv/env/dma_env_pkg.sv +++ b/hw/ip/dma/dv/env/dma_env_pkg.sv @@ -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 { diff --git a/hw/ip/dma/dv/env/dma_scoreboard.sv b/hw/ip/dma/dv/env/dma_scoreboard.sv index 52996196c4bab..3c8b119b887c5 100644 --- a/hw/ip/dma/dv/env/dma_scoreboard.sv +++ b/hw/ip/dma/dv/env/dma_scoreboard.sv @@ -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 { @@ -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. @@ -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? // @@ -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 @@ -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 @@ -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; @@ -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; @@ -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 @@ -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, @@ -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; @@ -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 @@ -1063,10 +1103,12 @@ 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; @@ -1074,13 +1116,15 @@ class dma_scoreboard extends cip_base_scoreboard #( 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 @@ -1091,10 +1135,10 @@ 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 @@ -1102,6 +1146,10 @@ class dma_scoreboard extends cip_base_scoreboard #( 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 @@ -1109,13 +1157,14 @@ class dma_scoreboard extends cip_base_scoreboard #( // 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 diff --git a/hw/ip/dma/dv/env/seq_lib/dma_base_vseq.sv b/hw/ip/dma/dv/env/seq_lib/dma_base_vseq.sv index 49b90c54e6209..6b586770793ad 100644 --- a/hw/ip/dma/dv/env/seq_lib/dma_base_vseq.sv +++ b/hw/ip/dma/dv/env/seq_lib/dma_base_vseq.sv @@ -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) diff --git a/hw/ip/dma/dv/env/seq_lib/dma_generic_vseq.sv b/hw/ip/dma/dv/env/seq_lib/dma_generic_vseq.sv index b9374ee280602..bd7345f0d1844 100644 --- a/hw/ip/dma/dv/env/seq_lib/dma_generic_vseq.sv +++ b/hw/ip/dma/dv/env/seq_lib/dma_generic_vseq.sv @@ -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(); @@ -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 diff --git a/hw/ip/dma/rtl/dma.sv b/hw/ip/dma/rtl/dma.sv index 2e2b97afdbead..1adcfb9ffd757 100644 --- a/hw/ip/dma/rtl/dma.sv +++ b/hw/ip/dma/rtl/dma.sv @@ -75,7 +75,7 @@ module dma logic dma_state_error; dma_ctrl_state_e ctrl_state_q, ctrl_state_d; - logic set_error_code, clear_go, clear_status, clear_sha_status; + logic set_error_code, clear_go, clear_status, clear_sha_status, chunk_done; logic [INTR_CLEAR_SOURCES_WIDTH-1:0] clear_index_d, clear_index_q; logic clear_index_en, intr_clear_tlul_rsp_valid; @@ -609,6 +609,7 @@ module dma clear_index_en = '0; clear_go = 1'b0; + chunk_done = 1'b0; // Mux the TLUL grant and response signals depending on the selected bus interface intr_clear_tlul_gnt = reg2hw.clear_intr_bus.q[clear_index_q]? dma_host_tlul_gnt : @@ -968,8 +969,9 @@ module dma end else if (chunk_byte_d >= reg2hw.chunk_data_size.q) begin // Conditionally clear the go bit when not being in hardware handshake mode. // In non-hardware handshake mode, finishing one chunk should raise the done IRQ - // and done bit, and release the go bit for the next FW-controlled chunk. + // and chunk done bit, and release the go bit for the next FW-controlled chunk. clear_go = !control_q.cfg_handshake_en; + chunk_done = !control_q.cfg_handshake_en; ctrl_state_d = DmaIdle; end else begin ctrl_state_d = DmaAddrSetup; @@ -1083,17 +1085,38 @@ module dma assign sha2_data.mask = {<<1{req_dst_be_q}}; // Interrupt logic - logic test_done_interrupt; - logic test_error_interrupt; - logic data_move_state; - logic update_dst_addr_reg, update_src_addr_reg; + prim_intr_hw #( + .IntrT ( "Status" ) + ) u_intr_dma_done ( + .clk_i ( clk_i ), + .rst_ni ( rst_ni ), + .event_intr_i ( reg2hw.status.done.q ), + .reg2hw_intr_enable_q_i ( reg2hw.intr_enable.dma_done.q ), + .reg2hw_intr_test_q_i ( reg2hw.intr_test.dma_done.q ), + .reg2hw_intr_test_qe_i ( reg2hw.intr_test.dma_done.qe ), + .reg2hw_intr_state_q_i ( reg2hw.intr_state.dma_done.q ), + .hw2reg_intr_state_de_o ( hw2reg.intr_state.dma_done.de ), + .hw2reg_intr_state_d_o ( hw2reg.intr_state.dma_done.d ), + .intr_o ( intr_dma_done_o ) + ); - assign test_done_interrupt = reg2hw.intr_test.dma_done.q && reg2hw.intr_test.dma_done.qe; - assign test_error_interrupt = reg2hw.intr_test.dma_error.q && reg2hw.intr_test.dma_error.qe; + prim_intr_hw #( + .IntrT ( "Status" ) + ) u_intr_error ( + .clk_i ( clk_i ), + .rst_ni ( rst_ni ), + .event_intr_i ( reg2hw.status.error.q ), + .reg2hw_intr_enable_q_i ( reg2hw.intr_enable.dma_error.q ), + .reg2hw_intr_test_q_i ( reg2hw.intr_test.dma_error.q ), + .reg2hw_intr_test_qe_i ( reg2hw.intr_test.dma_error.qe ), + .reg2hw_intr_state_q_i ( reg2hw.intr_state.dma_error.q ), + .hw2reg_intr_state_de_o ( hw2reg.intr_state.dma_error.de ), + .hw2reg_intr_state_d_o ( hw2reg.intr_state.dma_error.d ), + .intr_o ( intr_dma_error_o ) + ); - // Signal interrupt controller whenever an enabled interrupt info bit is set - assign intr_dma_done_o = reg2hw.intr_state.dma_done.q && reg2hw.intr_enable.dma_done.q; - assign intr_dma_error_o = reg2hw.intr_state.dma_error.q && reg2hw.intr_enable.dma_error.q; + logic data_move_state; + logic update_dst_addr_reg, update_src_addr_reg; assign data_move_state = (ctrl_state_q == DmaSendWrite) || (ctrl_state_q == DmaWaitWriteResponse) || @@ -1118,7 +1141,9 @@ module dma transfer_remaining_bytes : chunk_remaining_bytes; always_comb begin - hw2reg = '0; + // Because of using the primitves for interrupt handling, the hw2reg registers cannot get a + // common default value since would create a second driver to to the interrupt registers. Thus + // it must ensured that all registers are initialized mannually to avoid creating latches. // Clear the go bit if we are in a single transfer and finished the DMA operation, // hardware handshake mode when we finished all transfers, or when aborting the transfer. @@ -1142,6 +1167,8 @@ module dma end end + hw2reg.control.initial_transfer.de = 1'b0; + hw2reg.control.initial_transfer.d = 1'b0; // Clear the inline initial transfer flag starting flag when leaving the DmaIdle the first time if ((ctrl_state_q == DmaIdle) && (ctrl_state_d != DmaIdle) && reg2hw.control.initial_transfer.q) begin @@ -1177,9 +1204,10 @@ module dma clear_sha_status = (ctrl_state_q == DmaIdle) && (ctrl_state_d != DmaIdle) && reg2hw.control.initial_transfer.q; - // Set done bit and raise interrupt when we either finished a single transfer or all transfers - // in hardware handshake mode. Automatically clear the done bit when starting a new transfer - hw2reg.status.done.de = ((!cfg_abort_en) && data_move_state && clear_go) | clear_status; + // Set the done bit only when finishing all chunks. Automatically clear the done bit when + // starting a new transfer + hw2reg.status.done.de = ((!cfg_abort_en) && data_move_state && clear_go && ~chunk_done) | + clear_status; hw2reg.status.done.d = clear_status? 1'b0 : 1'b1; hw2reg.status.error.de = (ctrl_state_d == DmaError) | clear_status; @@ -1191,6 +1219,9 @@ module dma hw2reg.status.sha2_digest_valid.de = sha2_digest_set | clear_sha_status; hw2reg.status.sha2_digest_valid.d = sha2_digest_set; + hw2reg.status.chunk_done.de = ((!cfg_abort_en) && chunk_done) | clear_status; + hw2reg.status.chunk_done.d = clear_status? 1'b0 : 1'b1; + // Write digest to CSRs when needed. The digest is an 8-element 64-bit datatype. Depending on // the selected hashing algorithm, the digest is stored differently in the digest datatype: // SHA2-256: digest[0-7][31:0] store the 256-bit digest. The upper 32-bits of all digest @@ -1199,6 +1230,7 @@ module dma // SHA2-512: digest[0-7][63:0] store the 512-bit digest. for (int i = 0; i < NR_SHA_DIGEST_ELEMENTS; i++) begin hw2reg.sha2_digest[i].de = sha2_digest_set | clear_sha_status; + hw2reg.sha2_digest[i].d = '0; end // Only mux the digest data when sha2_digest_set is set. Setting the digest happens during the @@ -1251,13 +1283,6 @@ module dma hw2reg.control.abort.de = hw2reg.status.aborted.de; hw2reg.control.abort.d = 1'b0; - // interrupt management - hw2reg.intr_state.dma_done.de = reg2hw.status.done.q | test_done_interrupt; - hw2reg.intr_state.dma_done.d = 1'b1; - - hw2reg.intr_state.dma_error.de = reg2hw.status.error.q | test_error_interrupt; - hw2reg.intr_state.dma_error.d = 1'b1; - // Clear the SHA2 digests if the SHA2 valid flag is cleared (RW1C) if (reg2hw.status.sha2_digest_valid.qe & reg2hw.status.sha2_digest_valid.q) begin for (int i = 0; i < NR_SHA_DIGEST_ELEMENTS; i++) begin diff --git a/hw/ip/dma/rtl/dma_reg_pkg.sv b/hw/ip/dma/rtl/dma_reg_pkg.sv index 0b24ce866ca1f..f0565bdd5a292 100644 --- a/hw/ip/dma/rtl/dma_reg_pkg.sv +++ b/hw/ip/dma/rtl/dma_reg_pkg.sv @@ -135,6 +135,9 @@ package dma_reg_pkg; } dma_reg2hw_control_reg_t; typedef struct packed { + struct packed { + logic q; + } chunk_done; struct packed { logic q; logic qe; @@ -242,6 +245,10 @@ package dma_reg_pkg; logic d; logic de; } sha2_digest_valid; + struct packed { + logic d; + logic de; + } chunk_done; } dma_hw2reg_status_reg_t; typedef struct packed { @@ -286,24 +293,24 @@ package dma_reg_pkg; // Register -> HW type typedef struct packed { - dma_reg2hw_intr_state_reg_t intr_state; // [1037:1036] - dma_reg2hw_intr_enable_reg_t intr_enable; // [1035:1034] - dma_reg2hw_intr_test_reg_t intr_test; // [1033:1030] - dma_reg2hw_alert_test_reg_t alert_test; // [1029:1028] - dma_reg2hw_src_addr_lo_reg_t src_addr_lo; // [1027:996] - dma_reg2hw_src_addr_hi_reg_t src_addr_hi; // [995:964] - dma_reg2hw_dst_addr_lo_reg_t dst_addr_lo; // [963:932] - dma_reg2hw_dst_addr_hi_reg_t dst_addr_hi; // [931:900] - dma_reg2hw_addr_space_id_reg_t addr_space_id; // [899:892] - dma_reg2hw_enabled_memory_range_base_reg_t enabled_memory_range_base; // [891:859] - dma_reg2hw_enabled_memory_range_limit_reg_t enabled_memory_range_limit; // [858:826] - dma_reg2hw_range_valid_reg_t range_valid; // [825:825] - dma_reg2hw_range_regwen_reg_t range_regwen; // [824:821] - dma_reg2hw_total_data_size_reg_t total_data_size; // [820:789] - dma_reg2hw_chunk_data_size_reg_t chunk_data_size; // [788:757] - dma_reg2hw_transfer_width_reg_t transfer_width; // [756:755] - dma_reg2hw_control_reg_t control; // [754:743] - dma_reg2hw_status_reg_t status; // [742:737] + dma_reg2hw_intr_state_reg_t intr_state; // [1038:1037] + dma_reg2hw_intr_enable_reg_t intr_enable; // [1036:1035] + dma_reg2hw_intr_test_reg_t intr_test; // [1034:1031] + dma_reg2hw_alert_test_reg_t alert_test; // [1030:1029] + dma_reg2hw_src_addr_lo_reg_t src_addr_lo; // [1028:997] + dma_reg2hw_src_addr_hi_reg_t src_addr_hi; // [996:965] + dma_reg2hw_dst_addr_lo_reg_t dst_addr_lo; // [964:933] + dma_reg2hw_dst_addr_hi_reg_t dst_addr_hi; // [932:901] + dma_reg2hw_addr_space_id_reg_t addr_space_id; // [900:893] + dma_reg2hw_enabled_memory_range_base_reg_t enabled_memory_range_base; // [892:860] + dma_reg2hw_enabled_memory_range_limit_reg_t enabled_memory_range_limit; // [859:827] + dma_reg2hw_range_valid_reg_t range_valid; // [826:826] + dma_reg2hw_range_regwen_reg_t range_regwen; // [825:822] + dma_reg2hw_total_data_size_reg_t total_data_size; // [821:790] + dma_reg2hw_chunk_data_size_reg_t chunk_data_size; // [789:758] + dma_reg2hw_transfer_width_reg_t transfer_width; // [757:756] + dma_reg2hw_control_reg_t control; // [755:744] + dma_reg2hw_status_reg_t status; // [743:737] dma_reg2hw_handshake_intr_enable_reg_t handshake_intr_enable; // [736:726] dma_reg2hw_clear_intr_src_reg_t clear_intr_src; // [725:715] dma_reg2hw_clear_intr_bus_reg_t clear_intr_bus; // [714:704] @@ -313,14 +320,14 @@ package dma_reg_pkg; // HW -> register type typedef struct packed { - dma_hw2reg_intr_state_reg_t intr_state; // [699:696] - dma_hw2reg_src_addr_lo_reg_t src_addr_lo; // [695:663] - dma_hw2reg_src_addr_hi_reg_t src_addr_hi; // [662:630] - dma_hw2reg_dst_addr_lo_reg_t dst_addr_lo; // [629:597] - dma_hw2reg_dst_addr_hi_reg_t dst_addr_hi; // [596:564] - dma_hw2reg_cfg_regwen_reg_t cfg_regwen; // [563:560] - dma_hw2reg_control_reg_t control; // [559:554] - dma_hw2reg_status_reg_t status; // [553:544] + dma_hw2reg_intr_state_reg_t intr_state; // [701:698] + dma_hw2reg_src_addr_lo_reg_t src_addr_lo; // [697:665] + dma_hw2reg_src_addr_hi_reg_t src_addr_hi; // [664:632] + dma_hw2reg_dst_addr_lo_reg_t dst_addr_lo; // [631:599] + dma_hw2reg_dst_addr_hi_reg_t dst_addr_hi; // [598:566] + dma_hw2reg_cfg_regwen_reg_t cfg_regwen; // [565:562] + dma_hw2reg_control_reg_t control; // [561:556] + dma_hw2reg_status_reg_t status; // [555:544] dma_hw2reg_error_code_reg_t error_code; // [543:528] dma_hw2reg_sha2_digest_mreg_t [15:0] sha2_digest; // [527:0] } dma_hw2reg_t; diff --git a/hw/ip/dma/rtl/dma_reg_top.sv b/hw/ip/dma/rtl/dma_reg_top.sv index d83a06d18e61b..6940688c60497 100644 --- a/hw/ip/dma/rtl/dma_reg_top.sv +++ b/hw/ip/dma/rtl/dma_reg_top.sv @@ -121,11 +121,8 @@ module dma_reg_top ( // Define SW related signals // Format: __{wd|we|qs} // or _{wd|we|qs} if field == 1 or 0 - logic intr_state_we; logic intr_state_dma_done_qs; - logic intr_state_dma_done_wd; logic intr_state_dma_error_qs; - logic intr_state_dma_error_wd; logic intr_enable_we; logic intr_enable_dma_done_qs; logic intr_enable_dma_done_wd; @@ -201,6 +198,8 @@ module dma_reg_top ( logic status_error_qs; logic status_error_wd; logic status_sha2_digest_valid_qs; + logic status_chunk_done_qs; + logic status_chunk_done_wd; logic error_code_src_addr_error_qs; logic error_code_dst_addr_error_qs; logic error_code_opcode_error_qs; @@ -306,7 +305,7 @@ module dma_reg_top ( // F[dma_done]: 0:0 prim_subreg #( .DW (1), - .SwAccess(prim_subreg_pkg::SwAccessW1C), + .SwAccess(prim_subreg_pkg::SwAccessRO), .RESVAL (1'h0), .Mubi (1'b0) ) u_intr_state_dma_done ( @@ -314,8 +313,8 @@ module dma_reg_top ( .rst_ni (rst_ni), // from register interface - .we (intr_state_we), - .wd (intr_state_dma_done_wd), + .we (1'b0), + .wd ('0), // from internal hardware .de (hw2reg.intr_state.dma_done.de), @@ -333,7 +332,7 @@ module dma_reg_top ( // F[dma_error]: 1:1 prim_subreg #( .DW (1), - .SwAccess(prim_subreg_pkg::SwAccessW1C), + .SwAccess(prim_subreg_pkg::SwAccessRO), .RESVAL (1'h0), .Mubi (1'b0) ) u_intr_state_dma_error ( @@ -341,8 +340,8 @@ module dma_reg_top ( .rst_ni (rst_ni), // from register interface - .we (intr_state_we), - .wd (intr_state_dma_error_wd), + .we (1'b0), + .wd ('0), // from internal hardware .de (hw2reg.intr_state.dma_error.de), @@ -1162,14 +1161,14 @@ module dma_reg_top ( // R[status]: V(False) logic status_qe; - logic [4:0] status_flds_we; + logic [5:0] status_flds_we; prim_flop #( .Width(1), .ResetValue(0) ) u_status0_qe ( .clk_i(clk_i), .rst_ni(rst_ni), - .d_i(&(status_flds_we | 5'h11)), + .d_i(&(status_flds_we | 6'h11)), .q_o(status_qe) ); // F[busy]: 0:0 @@ -1309,6 +1308,33 @@ module dma_reg_top ( ); assign reg2hw.status.sha2_digest_valid.qe = status_qe; + // F[chunk_done]: 5:5 + prim_subreg #( + .DW (1), + .SwAccess(prim_subreg_pkg::SwAccessW1C), + .RESVAL (1'h0), + .Mubi (1'b0) + ) u_status_chunk_done ( + .clk_i (clk_i), + .rst_ni (rst_ni), + + // from register interface + .we (status_we), + .wd (status_chunk_done_wd), + + // from internal hardware + .de (hw2reg.status.chunk_done.de), + .d (hw2reg.status.chunk_done.d), + + // to internal hardware + .qe (status_flds_we[5]), + .q (reg2hw.status.chunk_done.q), + .ds (), + + // to register interface (read) + .qs (status_chunk_done_qs) + ); + // R[error_code]: V(False) // F[src_addr_error]: 0:0 @@ -2975,11 +3001,6 @@ module dma_reg_top ( end // Generate write-enables - assign intr_state_we = addr_hit[0] & reg_we & !reg_error; - - assign intr_state_dma_done_wd = reg_wdata[0]; - - assign intr_state_dma_error_wd = reg_wdata[1]; assign intr_enable_we = addr_hit[1] & reg_we & !reg_error; assign intr_enable_dma_done_wd = reg_wdata[0]; @@ -3056,6 +3077,8 @@ module dma_reg_top ( assign status_aborted_wd = reg_wdata[2]; assign status_error_wd = reg_wdata[3]; + + assign status_chunk_done_wd = reg_wdata[5]; assign handshake_intr_enable_we = addr_hit[36] & reg_we & !reg_error; assign handshake_intr_enable_wd = reg_wdata[10:0]; @@ -3135,7 +3158,7 @@ module dma_reg_top ( // Assign write-enables to checker logic vector. always_comb begin reg_we_check = '0; - reg_we_check[0] = intr_state_we; + reg_we_check[0] = 1'b0; reg_we_check[1] = intr_enable_we; reg_we_check[2] = intr_test_we; reg_we_check[3] = alert_test_we; @@ -3291,6 +3314,7 @@ module dma_reg_top ( reg_rdata_next[2] = status_aborted_qs; reg_rdata_next[3] = status_error_qs; reg_rdata_next[4] = status_sha2_digest_valid_qs; + reg_rdata_next[5] = status_chunk_done_qs; end addr_hit[19]: begin diff --git a/sw/device/lib/dif/autogen/dif_dma_autogen.c b/sw/device/lib/dif/autogen/dif_dma_autogen.c index d79f9be8878a2..55f586c408e1f 100644 --- a/sw/device/lib/dif/autogen/dif_dma_autogen.c +++ b/sw/device/lib/dif/autogen/dif_dma_autogen.c @@ -65,8 +65,8 @@ static bool dma_get_irq_bit_index(dif_dma_irq_t irq, } static dif_irq_type_t irq_types[] = { - kDifIrqTypeEvent, - kDifIrqTypeEvent, + kDifIrqTypeStatus, + kDifIrqTypeStatus, }; OT_WARN_UNUSED_RESULT diff --git a/sw/device/lib/dif/autogen/dif_dma_autogen_unittest.cc b/sw/device/lib/dif/autogen/dif_dma_autogen_unittest.cc index 8bf21514c738c..31fdc68c3bd63 100644 --- a/sw/device/lib/dif/autogen/dif_dma_autogen_unittest.cc +++ b/sw/device/lib/dif/autogen/dif_dma_autogen_unittest.cc @@ -77,7 +77,7 @@ TEST_F(IrqGetTypeTest, Success) { dif_irq_type_t type; EXPECT_DIF_OK(dif_dma_irq_get_type(&dma_, kDifDmaIrqDmaDone, &type)); - EXPECT_EQ(type, kDifIrqTypeEvent); + EXPECT_EQ(type, kDifIrqTypeStatus); } class IrqGetStateTest : public DmaTest {}; diff --git a/sw/device/lib/dif/dif_dma.h b/sw/device/lib/dif/dif_dma.h index d0e93c2babfa5..35d667f1fa595 100644 --- a/sw/device/lib/dif/dif_dma.h +++ b/sw/device/lib/dif/dif_dma.h @@ -226,6 +226,8 @@ typedef enum dif_dma_status_code { kDifDmaStatusError = 0x01 << DMA_STATUS_ERROR_BIT, // Set once the SHA2 digest is valid after finishing a transfer kDifDmaStatusSha2DigestValid = 0x01 << DMA_STATUS_SHA2_DIGEST_VALID_BIT, + // Transfer of a single chunk is complete. + kDifDmaStatusChunkDone = 0x01 << DMA_STATUS_CHUNK_DONE_BIT, } dif_dma_status_code_t; /** diff --git a/sw/device/lib/dif/dif_dma_unittest.cc b/sw/device/lib/dif/dif_dma_unittest.cc index acbc5e1265fae..2b41714ef867f 100644 --- a/sw/device/lib/dif/dif_dma_unittest.cc +++ b/sw/device/lib/dif/dif_dma_unittest.cc @@ -324,6 +324,7 @@ INSTANTIATE_TEST_SUITE_P( {1 << DMA_STATUS_ABORTED_BIT, kDifDmaStatusAborted}, {1 << DMA_STATUS_ERROR_BIT, kDifDmaStatusError}, {1 << DMA_STATUS_SHA2_DIGEST_VALID_BIT, kDifDmaStatusSha2DigestValid}, + {1 << DMA_STATUS_CHUNK_DONE_BIT, kDifDmaStatusChunkDone}, }})); TEST_F(StatusGetTest, GetBadArg) { @@ -350,6 +351,7 @@ INSTANTIATE_TEST_SUITE_P( {1 << DMA_STATUS_ABORTED_BIT, kDifDmaStatusAborted}, {1 << DMA_STATUS_ERROR_BIT, kDifDmaStatusError}, {1 << DMA_STATUS_SHA2_DIGEST_VALID_BIT, kDifDmaStatusSha2DigestValid}, + {1 << DMA_STATUS_CHUNK_DONE_BIT, kDifDmaStatusChunkDone}, }})); TEST_F(StatusWriteTest, GetBadArg) { diff --git a/sw/device/lib/testing/autogen/isr_testutils.c b/sw/device/lib/testing/autogen/isr_testutils.c index 08a51444855af..c6224321ad4e5 100644 --- a/sw/device/lib/testing/autogen/isr_testutils.c +++ b/sw/device/lib/testing/autogen/isr_testutils.c @@ -213,6 +213,7 @@ void isr_testutils_csrng_isr( } void isr_testutils_dma_isr(plic_isr_ctx_t plic_ctx, dma_isr_ctx_t dma_ctx, + bool mute_status_irq, top_earlgrey_plic_peripheral_t *peripheral_serviced, dif_dma_irq_t *irq_serviced) { // Claim the IRQ at the PLIC. @@ -243,6 +244,8 @@ void isr_testutils_dma_isr(plic_isr_ctx_t plic_ctx, dma_isr_ctx_t dma_ctx, CHECK_DIF_OK(dif_dma_irq_get_type(dma_ctx.dma, irq, &type)); if (type == kDifIrqTypeEvent) { CHECK_DIF_OK(dif_dma_irq_acknowledge(dma_ctx.dma, irq)); + } else if (mute_status_irq) { + CHECK_DIF_OK(dif_dma_irq_set_enabled(dma_ctx.dma, irq, kDifToggleDisabled)); } // Complete the IRQ at the PLIC. diff --git a/sw/device/lib/testing/autogen/isr_testutils.h b/sw/device/lib/testing/autogen/isr_testutils.h index 51cef3137eb9b..544fc4e2e9484 100644 --- a/sw/device/lib/testing/autogen/isr_testutils.h +++ b/sw/device/lib/testing/autogen/isr_testutils.h @@ -691,11 +691,13 @@ void isr_testutils_csrng_isr( * * @param plic_ctx A PLIC ISR context handle. * @param dma_ctx A(n) dma ISR context handle. + * @param mute_status_irq set to true to disable the serviced status type IRQ. * @param[out] peripheral_serviced Out param for the peripheral that was * serviced. * @param[out] irq_serviced Out param for the IRQ that was serviced. */ void isr_testutils_dma_isr(plic_isr_ctx_t plic_ctx, dma_isr_ctx_t dma_ctx, + bool mute_status_irq, top_earlgrey_plic_peripheral_t *peripheral_serviced, dif_dma_irq_t *irq_serviced);