diff --git a/hw/ip/dma/dv/env/dma_env_cov.sv b/hw/ip/dma/dv/env/dma_env_cov.sv index bf40a7147ae6c..df6c301138331 100644 --- a/hw/ip/dma/dv/env/dma_env_cov.sv +++ b/hw/ip/dma/dv/env/dma_env_cov.sv @@ -98,14 +98,13 @@ covergroup dma_config_cg with function sample(dma_seq_item dma_config, cp_handshake: coverpoint dma_config.handshake; - cp_data_direction: coverpoint dma_config.direction { - bins read_from_fifo = {DmaRcvData}; - bins write_to_fifo = {DmaSendData}; - } + cp_dst_chunk_wrap: coverpoint dma_config.dst_chunk_wrap; + + cp_dst_addr_inc: coverpoint dma_config.dst_addr_inc; - cp_fifo_auto_inc: coverpoint dma_config.auto_inc_fifo; + cp_src_chunk_wrap: coverpoint dma_config.src_chunk_wrap; - cp_mem_buffer_auto_inc: coverpoint dma_config.auto_inc_buffer; + cp_src_addr_inc: coverpoint dma_config.src_addr_inc; cp_handshake_intr: coverpoint dma_config.handshake_intr_en; @@ -119,52 +118,51 @@ covergroup dma_config_cg with function sample(dma_seq_item dma_config, cp_dst_addr, cp_dst_asid; - cr_opcode_X_src_asid_X_dst_asid_X_handshake_X_data_direction: cross + cr_opcode_X_src_asid_X_dst_asid_X_handshake: cross cp_opcode, cp_src_asid, cp_dst_asid, - cp_handshake, - cp_data_direction; + cp_handshake; - cr_opcode_X_chunk_data_size_X_src_asid_X_dst_asid_X_data_direction: cross + cr_opcode_X_chunk_data_size_X_src_asid_X_dst_asid: cross cp_opcode, cp_chunk_data_size, cp_src_asid, - cp_dst_asid, - cp_data_direction; + cp_dst_asid; - cr_opcode_X_total_data_size_X_transfer_width_X_data_direction: cross + cr_opcode_X_total_data_size_X_transfer_width_X_src_asid_X_dst_asid: cross cp_opcode, cp_total_data_size, cp_transfer_width, - cp_data_direction; + cp_src_asid, + cp_dst_asid; - cr_opcode_X_handshake_X_chunk_data_size_X_transfer_width_X_data_direction: cross + cr_opcode_X_handshake_X_chunk_data_size_X_transfer_width_X_src_asid_X_dst_asid: cross cp_opcode, cp_handshake, cp_chunk_data_size, cp_transfer_width, - cp_data_direction; + cp_src_asid, + cp_dst_asid; - cr_opcode_X_handshake_X_mem_buffer_auto_inc_X_fifo_auto_inc_X_data_direction: cross + cr_opcode_X_handshake_X_dst_addr_inc_X_dst_chunk_wrap_X_src_addr_inc_X_src_chunk_wrap: cross cp_opcode, cp_handshake, - cp_mem_buffer_auto_inc, - cp_fifo_auto_inc, - cp_data_direction; + cp_dst_chunk_wrap, + cp_dst_addr_inc, + cp_src_chunk_wrap, + cp_src_addr_inc; - cr_opcode_X_handshake_X_data_direction_X_initial_transfer: cross + cr_opcode_X_handshake_X_initial_transfer: cross cp_opcode, cp_handshake, - cp_data_direction, cp_initial_transfer; - cr_src_addr_X_dst_addr_X_mem_range_base_X_mem_range_limit_X_data_direction: cross + cr_src_addr_X_dst_addr_X_mem_range_base_X_mem_range_limit: cross cp_src_addr, cp_dst_addr, cp_mem_range_base, - cp_mem_range_limit, - cp_data_direction; + cp_mem_range_limit; cr_src_addr_alignment_X_dst_addr_alignment_X_total_data_size_alignment_X_transfer_width: cross cp_src_addr_alignment, @@ -181,11 +179,10 @@ covergroup dma_tlul_error_cg with function sample(dma_seq_item dma_config, cp_tl_err_asid: coverpoint tl_err_asid; - cr_tl_err_asid_X_src_asid_X_dst_asid_X_direction: cross + cr_tl_err_asid_X_src_asid_X_dst_asid: cross cp_tl_err_asid, dma_config.src_asid, - dma_config.dst_asid, - dma_config.direction; + dma_config.dst_asid; endgroup diff --git a/hw/ip/dma/dv/env/dma_env_pkg.sv b/hw/ip/dma/dv/env/dma_env_pkg.sv index e3cfc719d6cfe..e49430f7c5ec9 100644 --- a/hw/ip/dma/dv/env/dma_env_pkg.sv +++ b/hw/ip/dma/dv/env/dma_env_pkg.sv @@ -56,13 +56,6 @@ package dma_env_pkg; typedef virtual dma_sys_tl_if dma_sys_tl_vif; typedef class dma_scoreboard; - // DMAC data direction in hardware handshake mode - // enum values are based on dma hjson - typedef enum { - DmaRcvData = 0, - DmaSendData = 1 - } dma_control_data_direction_e; - typedef struct { asid_encoding_e src_id; asid_encoding_e dst_id; diff --git a/hw/ip/dma/dv/env/dma_scoreboard.sv b/hw/ip/dma/dv/env/dma_scoreboard.sv index 5ce61d67e1d73..a6200e7eeb1b7 100644 --- a/hw/ip/dma/dv/env/dma_scoreboard.sv +++ b/hw/ip/dma/dv/env/dma_scoreboard.sv @@ -126,7 +126,7 @@ class dma_scoreboard extends cip_base_scoreboard #( function void check_addr(bit [63:0] addr, // Observed address. bit [63:0] exp_addr, // Expectation. bit restricted, // DMA-enabled range applies. - bit fixed_addr, // Fixed address (FIFO w/out auto-inc). + bit fixed_addr, // Fixed address. // Expected address range for this accesses of this type. bit [63:0] range_start, bit [31:0] range_len, @@ -147,7 +147,6 @@ class dma_scoreboard extends cip_base_scoreboard #( `DV_CHECK(addr[1:0] == 0, $sformatf("Address is not 4 Byte aligned")) // Is this end of the transfer a fixed address? - // (FIFO end of a transfer in hardware handshaking mode, without address auto-incrementing.) if (fixed_addr) begin `DV_CHECK(addr[63:2] == range_start[63:2], $sformatf("0x%0x doesn't match %s start addr:0x%0x (handshake mode no auto-incr)", @@ -201,7 +200,8 @@ class dma_scoreboard extends cip_base_scoreboard #( uint num_bytes, // Start address of transfer (= start of first chunk). bit [63:0] start_addr, - bit fifo_access, + bit addr_inc, + bit chunk_wrap, // Configuration for this transfer. ref dma_seq_item dma_config, input string check_type); // Type of access. @@ -210,18 +210,19 @@ class dma_scoreboard extends cip_base_scoreboard #( // Are we expecting another access? if (num_bytes < dma_config.total_data_size) begin - // No support for fixed source/destination address or overlapping of chunks in - // memory-to-memory mode. - if (dma_config.handshake) begin - if (!dma_config.chunk_data_size || (num_bytes % dma_config.chunk_data_size)) begin - // Still within this chunk; do we advance per bus transaction? - if (fifo_access && !dma_config.auto_inc_fifo) begin - // Fixed address. - next_addr = addr; - end - end else if (fifo_access || !dma_config.auto_inc_buffer) begin - // End of a chunk, but all chunks overlap. - next_addr = start_addr; + if (!dma_config.chunk_data_size || (num_bytes % dma_config.chunk_data_size)) begin + // Still within this chunk; do we advance per bus transaction? + if (!addr_inc) begin + // Fixed address. + next_addr = addr; + end + end else begin + // End of chunk. + if (chunk_wrap) begin + next_addr = start_addr; // All chunks start at the same address. + end else if (!addr_inc) begin + // Chunks do not overlap but all words within a chunk do. + next_addr = addr + dma_config.chunk_data_size; end end end else begin @@ -230,9 +231,8 @@ class dma_scoreboard extends cip_base_scoreboard #( `uvm_info(`gfn, $sformatf("%s prediction 0x%0x (num_bytes 0x%0x after 0x%0x)", check_type, next_addr, num_bytes, addr), UVM_DEBUG) - `uvm_info(`gfn, $sformatf(" (start 0x%0x, fifo %d, inc_fifo %d, inc_buffer %d)", - start_addr, fifo_access, dma_config.auto_inc_fifo, - dma_config.auto_inc_buffer), UVM_DEBUG) + `uvm_info(`gfn, $sformatf(" (start 0x%0x, addr_inc %d, chunk_wrap %d)", + start_addr, addr_inc, chunk_wrap), UVM_DEBUG) return next_addr; endfunction @@ -243,7 +243,6 @@ class dma_scoreboard extends cip_base_scoreboard #( uint expected_per_txn_bytes = dma_config.transfer_width_to_num_bytes( dma_config.per_transfer_width); tl_a_op_e a_opcode = tl_a_op_e'(item.a_opcode); - bit [31:0] memory_range; int intr_source; `uvm_info(`gfn, $sformatf("Got addr txn \n:%s", item.sprint()), UVM_DEBUG) @@ -251,14 +250,6 @@ class dma_scoreboard extends cip_base_scoreboard #( // Check if the transaction is of correct size `DV_CHECK_EQ(item.a_size, 2); // Always 4B - // The range of memory addresses that should be touched by the DMA controller depends upon - // whether the memory_buffer_inc is set in hardware handshake mode - memory_range = dma_config.total_data_size; - if (dma_config.handshake && !dma_config.auto_inc_buffer) begin - // All chunks within the transfer overlap each other in memory - memory_range = dma_config.chunk_data_size; - end - // Interface specific checks // - Read transactions are from Source interface and // - Write transactions are to destination interface @@ -266,7 +257,8 @@ class dma_scoreboard extends cip_base_scoreboard #( // Does the DMA-enabled memory range apply to this type of access? bit restricted = dma_config.mem_range_valid && (dma_config.src_asid == OtInternalAddr && dma_config.dst_asid != OtInternalAddr); - + bit fixed_addr = dma_config.src_chunk_wrap & !dma_config.src_addr_inc; + bit [31:0] memory_range; // Check if the transaction has correct mask `DV_CHECK_EQ($countones(item.a_mask), 4) // Always 4B // Check source ASID for read transaction @@ -281,9 +273,21 @@ class dma_scoreboard extends cip_base_scoreboard #( intr_source = intr_addr_lookup(a_addr); `DV_CHECK_EQ(intr_source, -1, "Unexpected Read access to Clear Interrupt address") + // The range of memory addresses that should be touched by the DMA controller depends upon + // whether address incrementing and/or chunk wrapping are used. + memory_range = dma_config.total_data_size; + if (dma_config.src_chunk_wrap) begin + // All chunks within the transfer overlap each other in memory + memory_range = dma_config.chunk_data_size; + if (!dma_config.src_addr_inc) begin + // This configuration is even more restrictive; all accesses are to a single address. + memory_range = 4; + end + end + // Validate the read address for this source access. - check_addr(a_addr, exp_src_addr, restricted, dma_config.get_read_fifo_en(), - dma_config.src_addr, memory_range, dma_config, "Source"); + check_addr(a_addr, exp_src_addr, restricted, fixed_addr, dma_config.src_addr, memory_range, + dma_config, "Source"); // Push addr item to source queue src_queue.push_back(item); @@ -298,19 +302,33 @@ class dma_scoreboard extends cip_base_scoreboard #( // this is important because the current transaction address is missing its LSBs and thus // cannot be used. exp_src_addr = predict_addr(exp_src_addr, num_bytes_read, dma_config.src_addr, - dma_config.handshake & (dma_config.direction == DmaRcvData), + dma_config.src_addr_inc, dma_config.src_chunk_wrap, dma_config, "Source"); end else begin // Write transaction // Does the DMA-enabled memory range apply to this type of access? bit restricted = dma_config.mem_range_valid && (dma_config.dst_asid == OtInternalAddr && dma_config.src_asid != OtInternalAddr); + bit fixed_addr = dma_config.dst_chunk_wrap & !dma_config.dst_addr_inc; + bit [31:0] memory_range; // Is this address a 'Clear Interrupt' operation? intr_source = intr_addr_lookup(a_addr); // Push addr item to destination queue dst_queue.push_back(item); `uvm_info(`gfn, $sformatf("Addr channel checks done for destination item"), UVM_HIGH) + // The range of memory addresses that should be touched by the DMA controller depends upon + // whether chunks overlap. + memory_range = dma_config.total_data_size; + if (dma_config.dst_chunk_wrap) begin + // All chunks within the transfer overlap each other in memory + memory_range = dma_config.chunk_data_size; + if (!dma_config.dst_addr_inc) begin + // This configuration is even more restrictive; all accesses are to a single address. + memory_range = 4; + end + end + // Write to 'Clear Interrupt' address? if (intr_source < 0) begin // Regular write traffic @@ -320,8 +338,8 @@ class dma_scoreboard extends cip_base_scoreboard #( uint remaining_bytes; // Validate the write address for this destination access. - check_addr(a_addr, exp_dst_addr, restricted, dma_config.get_write_fifo_en(), - dma_config.dst_addr, memory_range, dma_config, "Destination"); + check_addr(a_addr, exp_dst_addr, restricted, fixed_addr, dma_config.dst_addr, memory_range, + dma_config, "Destination"); // Note: this will only work because we KNOW that we don't reprogram the `chunk_data_size` // register, so we can rely upon all non-final chunks being of the same size @@ -374,7 +392,7 @@ class dma_scoreboard extends cip_base_scoreboard #( // this is important because the current transaction address is missing its LSBs and thus // cannot be used. exp_dst_addr = predict_addr(exp_dst_addr, num_bytes_transferred, dma_config.dst_addr, - dma_config.handshake & (dma_config.direction == DmaSendData), + dma_config.dst_addr_inc, dma_config.dst_chunk_wrap, dma_config, "Destination"); end else begin // Write to 'Clear Interrupt' address, so check the value written and the bus to which the @@ -869,23 +887,29 @@ class dma_scoreboard extends cip_base_scoreboard #( end "src_addr_lo": begin dma_config.src_addr[31:0] = item.a_data; - `uvm_info(`gfn, $sformatf("Got src_addr_lo = %0x", - dma_config.src_addr[31:0]), UVM_HIGH) + `uvm_info(`gfn, $sformatf("Got src_addr_lo = %0x", dma_config.src_addr[31:0]), UVM_HIGH) end "src_addr_hi": begin dma_config.src_addr[63:32] = item.a_data; - `uvm_info(`gfn, $sformatf("Got src_addr_hi = %0x", - dma_config.src_addr[63:32]), UVM_HIGH) + `uvm_info(`gfn, $sformatf("Got src_addr_hi = %0x", dma_config.src_addr[63:32]), UVM_HIGH) end "dst_addr_lo": begin dma_config.dst_addr[31:0] = item.a_data; - `uvm_info(`gfn, $sformatf("Got dst_addr_lo = %0x", - dma_config.dst_addr[31:0]), UVM_HIGH) + `uvm_info(`gfn, $sformatf("Got dst_addr_lo = %0x", dma_config.dst_addr[31:0]), UVM_HIGH) end "dst_addr_hi": begin dma_config.dst_addr[63:32] = item.a_data; - `uvm_info(`gfn, $sformatf("Got dst_addr_hi = %0x", - dma_config.dst_addr[63:32]), UVM_HIGH) + `uvm_info(`gfn, $sformatf("Got dst_addr_hi = %0x", dma_config.dst_addr[63:32]), UVM_HIGH) + end + "dst_config", "dst_control": begin + `uvm_info(`gfn, $sformatf("Got dst_config = %0x", item.a_data), UVM_HIGH) + dma_config.dst_chunk_wrap = get_field_val(ral.dst_config.wrap, item.a_data); + dma_config.dst_addr_inc = get_field_val(ral.dst_config.increment, item.a_data); + end + "src_config", "src_control": begin + `uvm_info(`gfn, $sformatf("Got src_config = %0x", item.a_data), UVM_HIGH) + dma_config.src_chunk_wrap = get_field_val(ral.src_config.wrap, item.a_data); + dma_config.src_addr_inc = get_field_val(ral.src_config.increment, item.a_data); end "addr_space_id": begin // Get mirrored field value and cast to associated enum in dma_config @@ -1035,10 +1059,11 @@ class dma_scoreboard extends cip_base_scoreboard #( dma_config.handshake = `gmv(ral.control.hardware_handshake_enable); `uvm_info(`gfn, $sformatf("Got hardware_handshake_mode = %0b", dma_config.handshake), UVM_HIGH) - // Get auto-increment bit - dma_config.auto_inc_buffer = `gmv(ral.control.memory_buffer_auto_increment_enable); - dma_config.auto_inc_fifo = `gmv(ral.control.fifo_auto_increment_enable); - dma_config.direction = dma_control_data_direction_e'(`gmv(ral.control.data_direction)); + // Get auto-increment and wrap bits + dma_config.dst_chunk_wrap = `gmv(ral.dst_config.wrap); + dma_config.src_chunk_wrap = `gmv(ral.src_config.wrap); + dma_config.dst_addr_inc = `gmv(ral.dst_config.increment); + dma_config.src_addr_inc = `gmv(ral.src_config.increment); `uvm_info(`gfn, $sformatf("dma_config\n %s", dma_config.sprint()), UVM_HIGH) @@ -1192,21 +1217,17 @@ class dma_scoreboard extends cip_base_scoreboard #( bit [63:0] dst_addr = dma_config.dst_addr; bit [63:0] src_addr = dma_config.src_addr; - if (!dma_config.handshake || - (dma_config.direction == DmaSendData && dma_config.auto_inc_buffer)) begin + if (dma_config.src_addr_inc && !dma_config.src_chunk_wrap) begin src_addr += num_bytes_checked; end - if (!dma_config.handshake || - (dma_config.direction == DmaRcvData && dma_config.auto_inc_buffer)) begin + if (dma_config.dst_addr_inc && !dma_config.dst_chunk_wrap) begin dst_addr += num_bytes_checked; end - // TODO: we are still unable to check the final output data after the transfer if - // overwriting has occurred; this will happen with a memory source/destination that - // doesn't auto increment after each chunk, _or_ with a FIFO target that is actually - // using a memory model because auto increment is enabled. - if (dma_config.handshake && (!dma_config.auto_inc_buffer || - (dma_config.direction == DmaSendData && dma_config.auto_inc_fifo))) begin + // TODO: we are still unable to check the final output data if in hardware-handshaking + // mode and the destination chunks overlap but auto-increment _is_ used, ie. it's not + // using a FIFO model. + if (dma_config.handshake && dma_config.dst_chunk_wrap && dma_config.dst_addr_inc) begin `uvm_info(`gfn, "Unable to check output data because of chunks overlapping", UVM_LOW) end else begin check_data(dma_config, src_addr, dst_addr, num_bytes_checked, check_bytes); diff --git a/hw/ip/dma/dv/env/dma_seq_item.sv b/hw/ip/dma/dv/env/dma_seq_item.sv index 1e2c11b902b65..5c002072c4183 100644 --- a/hw/ip/dma/dv/env/dma_seq_item.sv +++ b/hw/ip/dma/dv/env/dma_seq_item.sv @@ -12,9 +12,8 @@ // first // - src_asid // - dst_asid -// - direction -// - auto_inc_buffer -// - auto_inc_fifo +// - addr_inc +// - chunk_wrap // - handshake // - per_transfer_width // Then the memory range is randomized @@ -33,11 +32,13 @@ class dma_seq_item extends uvm_sequence_item; // Variables to configure DMA - rand bit auto_inc_buffer; - rand bit auto_inc_fifo; rand bit handshake; rand bit [63:0] src_addr; rand bit [63:0] dst_addr; + rand bit src_chunk_wrap; + rand bit dst_chunk_wrap; + rand bit src_addr_inc; + rand bit dst_addr_inc; rand bit mem_range_valid; rand bit [31:0] mem_range_base; rand bit [31:0] mem_range_limit; @@ -48,7 +49,6 @@ class dma_seq_item extends uvm_sequence_item; rand dma_transfer_width_e per_transfer_width; rand asid_encoding_e src_asid; rand asid_encoding_e dst_asid; - rand dma_control_data_direction_e direction; // Variable to indicate if interrupt needs clearing before reading from FIFO rand bit [dma_reg_pkg::NumIntClearSources-1:0] clear_intr_src; // Variable to indicate the bus on which each interrupt clearing address resides @@ -90,10 +90,13 @@ class dma_seq_item extends uvm_sequence_item; `uvm_object_utils_begin(dma_seq_item) `uvm_field_int(src_addr, UVM_DEFAULT) `uvm_field_int(dst_addr, UVM_DEFAULT) + `uvm_field_int(dst_chunk_wrap, UVM_DEFAULT) + `uvm_field_int(src_chunk_wrap, UVM_DEFAULT) + `uvm_field_int(dst_addr_inc, UVM_DEFAULT) + `uvm_field_int(src_addr_inc, UVM_DEFAULT) `uvm_field_enum(asid_encoding_e, src_asid, UVM_DEFAULT) `uvm_field_enum(asid_encoding_e, dst_asid, UVM_DEFAULT) `uvm_field_enum(opcode_e, opcode, UVM_DEFAULT) - `uvm_field_enum(dma_control_data_direction_e, direction, UVM_DEFAULT) `uvm_field_int(mem_range_valid, UVM_DEFAULT) `uvm_field_int(mem_range_base, UVM_DEFAULT) `uvm_field_int(mem_range_limit, UVM_DEFAULT) @@ -101,8 +104,6 @@ class dma_seq_item extends uvm_sequence_item; `uvm_field_int(total_data_size, UVM_DEFAULT) `uvm_field_int(chunk_data_size, UVM_DEFAULT) `uvm_field_enum(dma_transfer_width_e, per_transfer_width, UVM_DEFAULT) - `uvm_field_int(auto_inc_buffer, UVM_DEFAULT) - `uvm_field_int(auto_inc_fifo, UVM_DEFAULT) `uvm_field_int(handshake, UVM_DEFAULT) `uvm_field_int(is_valid_config, UVM_DEFAULT) `uvm_field_int(handshake_intr_en, UVM_DEFAULT) @@ -163,14 +164,13 @@ class dma_seq_item extends uvm_sequence_item; src_addr >= mem_range_base; src_addr <= mem_range_limit; mem_range_limit - src_addr >= chunk_data_size; - // If auto increment is not used on the memory end of the operation then successive chunks - // overlap each other, but in other cases the entire transfer must fit within the window - if (!handshake || (direction == DmaRcvData && auto_inc_buffer)) { + // If wrapping is not used after chunk than the entire transfer must fit within the window + if (!src_chunk_wrap) { mem_range_limit - src_addr >= total_data_size; } } else { // Choose a source address range that lies partially outside the DMA-enabled memory range. - if (auto_inc_buffer) { + if (!src_chunk_wrap) { // Choose start address to be too low or end address to be too high. src_addr < mem_range_base || src_addr > mem_range_limit || @@ -214,10 +214,8 @@ class dma_seq_item extends uvm_sequence_item; dst_addr >= mem_range_base; dst_addr <= mem_range_limit; mem_range_limit - dst_addr >= chunk_data_size; - // If auto increment is not used on the memory end of the operation, then successive/ - // chunks overlap each other, but in other cases the entire transfer must fit within the - // window - if (!handshake || (direction == DmaSendData && auto_inc_buffer)) { + // If wrapping is not used after chunk than the entire transfer must fit within the window + if (!dst_chunk_wrap) { mem_range_limit - dst_addr >= total_data_size; } if (src_asid == OtInternalAddr) { @@ -232,7 +230,7 @@ class dma_seq_item extends uvm_sequence_item; } else { // Choose a destination address range that lies partially outside the DMA-enabled memory // range. - if (auto_inc_buffer) { + if (!dst_chunk_wrap) { // Choose start address to be too low or end address to be too high. dst_addr < mem_range_base || dst_addr > mem_range_limit || @@ -292,8 +290,8 @@ class dma_seq_item extends uvm_sequence_item; // source/destination addresses meet the alignment requirements for the start of the next // chunk. // For narrower handshaking transfers, there is also a 4n requirement on the chunk size when - // the FIFO address end does not advance, to keep the source and destination alignments equal - if (per_transfer_width == DmaXfer4BperTxn || (handshake && !auto_inc_fifo)) { + // auto-increment is not used, to keep the source and destination alignments equal + if (per_transfer_width == DmaXfer4BperTxn || !src_addr_inc || !dst_addr_inc) { chunk_data_size[1:0] == '0; } else { per_transfer_width == DmaXfer2BperTxn -> chunk_data_size[0] == 1'b0; @@ -306,8 +304,9 @@ class dma_seq_item extends uvm_sequence_item; opcode inside {OpcSha256, OpcSha384, OpcSha512} -> chunk_data_size[1:0] == 2'b00; // Source and destination addresses must have the same alignment at the start of non-initial - // chunks when addresses advance - !handshake || auto_inc_buffer -> chunk_data_size[1:0] == 2'b00; + // chunks when either is not wrapping chunks. + (dst_addr_inc && !dst_chunk_wrap) -> chunk_data_size[1:0] == 2'b00; + (src_addr_inc && !src_chunk_wrap) -> chunk_data_size[1:0] == 2'b00; } // Chunk size must be a multiple of the bytes/transaction @@ -430,10 +429,7 @@ class dma_seq_item extends uvm_sequence_item; // Transfer mode str = {str, - $sformatf("\n\thandshake : %0d", handshake), - $sformatf("\n\tdirection : %0d", direction), - $sformatf("\n\tauto_inc_fifo : %0d", auto_inc_fifo), - $sformatf("\n\tauto_inc_buffer : %0d", auto_inc_buffer) + $sformatf("\n\thandshake : %0d", handshake) }; // Transfer properties @@ -442,6 +438,10 @@ class dma_seq_item extends uvm_sequence_item; $sformatf("\n\tdst_asid : %x", dst_asid), $sformatf("\n\tsrc_addr : 0x%16x", src_addr), $sformatf("\n\tdst_addr : 0x%16x", dst_addr), + $sformatf("\n\tsrc_chunk_wrap : %0d", src_chunk_wrap), + $sformatf("\n\tdst_chunk_wrap : %0d", dst_chunk_wrap), + $sformatf("\n\tsrc_addr_inc : %0d", src_addr_inc), + $sformatf("\n\tdst_addr_inc : %0d", dst_addr_inc), $sformatf("\n\topcode : %0d", opcode), $sformatf("\n\tper_transfer_width : %0d", per_transfer_width), $sformatf("\n\tchunk_data_size : 0x%x", chunk_data_size), @@ -482,7 +482,8 @@ class dma_seq_item extends uvm_sequence_item; // if settings are valid (returns 1), expected request queue must be populated // else (returns 0) queue will not be updated function bit check_config(string reason = ""); - bit [31:0] memory_range; + bit [31:0] src_memory_range; + bit [31:0] dst_memory_range; bit [1:0] align_mask; bit valid_config = 1; @@ -490,10 +491,20 @@ class dma_seq_item extends uvm_sequence_item; // that the configuration is invalid. `uvm_info(`gfn, $sformatf("Checking configuration (%s)", reason), UVM_MEDIUM) - // Ascertain the size of the in-memory buffer - memory_range = total_data_size; - if (handshake && !auto_inc_buffer) begin - memory_range = chunk_data_size; // All chunks overlap each other + // Ascertain the size of the in-memory buffer(s). + src_memory_range = total_data_size; + if (src_chunk_wrap) begin + src_memory_range = chunk_data_size; // All chunks overlap each other + if (!src_addr_inc) begin + src_memory_range = 4; + end + end + dst_memory_range = total_data_size; + if (dst_chunk_wrap) begin + dst_memory_range = chunk_data_size; // All chunks overlaps each other + if (!dst_addr_inc) begin + dst_memory_range = 4; + end end // Testing of the System bus may be waived in this DV environment @@ -503,14 +514,14 @@ class dma_seq_item extends uvm_sequence_item; // above); if the upper bits do not match then reads or writes will be faulted, and 32-bit // address wraparound is not permitted. if (src_asid == SocSystemAddr) begin - if (src_addr[63:32] != soc_system_hi_addr || src_addr[31:0] >= ~memory_range) begin + if (src_addr[63:32] != soc_system_hi_addr || src_addr[31:0] >= ~src_memory_range) begin `uvm_info(`gfn, " - Limitations of 32-bit TL-UL for testing System bus Reads not met", UVM_MEDIUM) valid_config = 0; end end if (dst_asid == SocSystemAddr) begin - if (dst_addr[63:32] != soc_system_hi_addr || dst_addr[31:0] >= ~memory_range) begin + if (dst_addr[63:32] != soc_system_hi_addr || dst_addr[31:0] >= ~dst_memory_range) begin `uvm_info(`gfn, " - Limitations of 32-bit TL-UL for testing System bus Writes not met", UVM_MEDIUM) valid_config = 0; @@ -559,7 +570,8 @@ class dma_seq_item extends uvm_sequence_item; // to OT internal address space, but the memory range restriction does not apply if _both_ // are within the OT internal address space. if (src_asid == OtInternalAddr && dst_asid != OtInternalAddr) begin - if (mem_range_valid && !is_buffer_in_dma_memory_region(src_addr[31:0], memory_range)) begin + if (mem_range_valid && !is_buffer_in_dma_memory_region(src_addr[31:0], + src_memory_range)) begin // If source address space ID points to OT internal address space, // it must be within DMA enabled address range. `uvm_info(`gfn, @@ -572,7 +584,8 @@ class dma_seq_item extends uvm_sequence_item; end else if (dst_asid == OtInternalAddr && src_asid != OtInternalAddr) begin // If destination address space ID points to OT internal address space // it must be within DMA enabled address range. - if (mem_range_valid && !is_buffer_in_dma_memory_region(dst_addr[31:0], memory_range)) begin + if (mem_range_valid && !is_buffer_in_dma_memory_region(dst_addr[31:0], + dst_memory_range)) begin `uvm_info(`gfn, $sformatf( " - Invalid dst addr range found lo: %08x hi: %08x with base: %08x limit: %0x", @@ -623,7 +636,7 @@ class dma_seq_item extends uvm_sequence_item; // Multi-chunk transfers will fault the transfer at the point of starting non-initial chunks // if the `chunk_data_size` values does not ensure that they do not have appropriately-aligned // addresses, so we expect an error at some point even if not immediately. - if (chunk_data_size < total_data_size && (!handshake || auto_inc_buffer)) begin + if (chunk_data_size < total_data_size && (!dst_chunk_wrap || !src_chunk_wrap)) begin if (|(chunk_data_size & align_mask)) begin `uvm_info(`gfn, " - Chunk data does not meet alignment requirements for multi-chunk transfers", @@ -678,49 +691,28 @@ class dma_seq_item extends uvm_sequence_item; src_asid = OtInternalAddr; dst_asid = OtInternalAddr; opcode = OpcCopy; - direction = DmaRcvData; mem_range_base = 0; mem_range_limit = 0; total_data_size = 0; per_transfer_width = DmaXfer1BperTxn; - auto_inc_buffer = 0; - auto_inc_fifo = 0; + dst_addr_inc = 1; + src_addr_inc = 1; + dst_chunk_wrap = 0; + src_chunk_wrap = 0; handshake = 0; // reset non random variables valid_dma_config = 0; range_regwen = MuBi4True; endfunction - // Disable randomization of all variables - function void disable_randomization(); - src_addr.rand_mode(0); - dst_addr.rand_mode(0); - src_asid.rand_mode(0); - dst_asid.rand_mode(0); - opcode.rand_mode(0); - direction.rand_mode(0); - mem_range_base.rand_mode(0); - mem_range_limit.rand_mode(0); - total_data_size.rand_mode(0); - per_transfer_width.rand_mode(0); - auto_inc_buffer.rand_mode(0); - auto_inc_fifo.rand_mode(0); - handshake.rand_mode(0); - range_regwen.rand_mode(0); - endfunction - // Return if Read FIFO mode enabled (no auto increment of source address) function bit get_read_fifo_en(); - return handshake && // Handshake mode enabled - direction == DmaRcvData && // Read from FIFO - !auto_inc_fifo; // FIFO address auto increment disabled + return !src_addr_inc; endfunction // Return if Write FIFO mode enabled (no auto increment of destination address) function bit get_write_fifo_en(); - return handshake && // Handshake mode enabled - direction == DmaSendData && // Write to FIFO - !auto_inc_fifo; // FIFO address auto increment disabled + return !dst_addr_inc; endfunction // Simply utility function that returns the actual size of a chunk starting at the given offset 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 2cb97e1f8c1af..a1baf341ebdc4 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 @@ -187,11 +187,10 @@ class dma_base_vseq extends cip_base_vseq #( // Supply a block of data for this transfer populate_src_fifo(dma_config.src_asid, cfg.src_data, offset, size); end else begin - // The source address depends upon the configuration; chunks may overlap each other for + // The source address depends upon the configuration; chunks may overlap each other. // hardware-handshaking mode. bit [63:0] src_addr = dma_config.src_addr; - if (!dma_config.handshake || - (dma_config.direction == DmaSendData && dma_config.auto_inc_buffer)) begin + if (!dma_config.src_chunk_wrap) begin src_addr += offset; end else begin src_addr += offset % dma_config.chunk_data_size; @@ -247,11 +246,9 @@ class dma_base_vseq extends cip_base_vseq #( set_model_fifo_mode(dma_config.src_asid, dma_config.src_addr, dma_config.per_transfer_width, chunk_size); end else begin - // The source address depends upon the configuration; chunks may overlap each other for - // hardware-handshaking mode. + // The source address depends upon the configuration; chunks may overlap each other. bit [31:0] src_addr = dma_config.src_addr; - if (!dma_config.handshake || - (dma_config.direction == DmaSendData && dma_config.auto_inc_buffer)) begin + if (!dma_config.src_chunk_wrap) begin src_addr += offset; end end @@ -264,7 +261,7 @@ class dma_base_vseq extends cip_base_vseq #( // TODO: Presently there is no way to intervene and check per-chunk data, so gather it all // into a single large FIFO and the scoreboard will check it all at the end of the transfer. bit [31:0] max_size = chunk_size; - if (dma_config.handshake && dma_config.direction == DmaSendData) begin + if (dma_config.handshake) begin max_size = dma_config.total_data_size; end @@ -301,6 +298,20 @@ class dma_base_vseq extends cip_base_vseq #( `uvm_info(`gfn, $sformatf("DMA: Destination Address = 0x%016h", dst_addr), UVM_HIGH) endtask : set_dst_addr + // Task: Write to Source Configuration + task set_src_config(bit chunk_wrap, bit addr_inc); + ral.src_config.wrap.set(chunk_wrap); + ral.src_config.increment.set(addr_inc); + csr_update(ral.src_config); + endtask : set_src_config + + // Task: Write to Destination Configuration + task set_dst_config(bit chunk_wrap, bit addr_inc); + ral.dst_config.wrap.set(chunk_wrap); + ral.dst_config.increment.set(addr_inc); + csr_update(ral.dst_config); + endtask : set_dst_config + // Task: Set DMA Enabled Memory base and limit task set_dma_enabled_memory_range(bit [32:0] base, bit [31:0] limit, bit valid, mubi4_t lock); csr_wr(ral.enabled_memory_range_base, base); @@ -364,6 +375,8 @@ class dma_base_vseq extends cip_base_vseq #( abort_pending = 1'b0; set_src_addr(dma_config.src_addr); set_dst_addr(dma_config.dst_addr); + set_src_config(dma_config.src_chunk_wrap, dma_config.src_addr_inc); + set_dst_config(dma_config.dst_chunk_wrap, dma_config.dst_addr_inc); set_addr_space_id(dma_config.src_asid, dma_config.dst_asid); set_total_size(dma_config.total_data_size); set_chunk_data_size(dma_config.chunk_data_size); @@ -472,6 +485,12 @@ class dma_base_vseq extends cip_base_vseq #( // Task: Start TLUL Sequences virtual task start_device(ref dma_seq_item dma_config); + // Set fifo enable bit; the FIFO is used whenever address incrementing does not occur + // after each (partial-)word transfer; the normal memory model would not cope with that + // and would be continually losing data. + set_seq_fifo_read_mode(dma_config.src_asid, dma_config.get_read_fifo_en()); + set_seq_fifo_write_mode(dma_config.dst_asid, dma_config.get_write_fifo_en()); + if (dma_config.handshake) begin // Will the test sequence generate any interrupts? bit [31:0] fifo_interrupt_mask; @@ -481,9 +500,6 @@ class dma_base_vseq extends cip_base_vseq #( `uvm_info(`gfn, $sformatf("FIFO interrupt enable mask = %0x ", fifo_interrupt_mask), UVM_HIGH) - // Set fifo enable bit - set_seq_fifo_read_mode(dma_config.src_asid, dma_config.get_read_fifo_en()); - set_seq_fifo_write_mode(dma_config.dst_asid, dma_config.get_write_fifo_en()); // TODO: there may be some merit at some point to starting handshaking transfers when // interrupts cannot occur, but only if we're expecting to abort transfers, for example. @@ -577,19 +593,13 @@ class dma_base_vseq extends cip_base_vseq #( task set_control(opcode_e opcode, bit initial_transfer, bit handshake, - bit auto_inc_buffer, - bit auto_inc_fifo, - bit data_direction, bit go); // Commence transfer? uvm_reg_data_t data = 0; string action; action = go ? "Executing" : "Setting"; - `uvm_info(`gfn, $sformatf( - "DMA: %s CONTROL OpC=%d Initial=%d Handshake=%d inc_buffer=%d inc_fifo=%d dir=%d", - action, opcode, initial_transfer, handshake, auto_inc_buffer, auto_inc_fifo, - data_direction), - UVM_HIGH) + `uvm_info(`gfn, $sformatf("DMA: %s CONTROL OpC=%d Initial=%d Handshake=%d", + action, opcode, initial_transfer, handshake), UVM_HIGH) // Exclusive access to CONTROL register // Note: a parallel thread may be attempting to Abort transfers using the CONTROL register. @@ -602,11 +612,6 @@ class dma_base_vseq extends cip_base_vseq #( data = get_csr_val_with_updated_field(ral.control.opcode, data, int'(opcode)); data = get_csr_val_with_updated_field(ral.control.initial_transfer, data, initial_transfer); data = get_csr_val_with_updated_field(ral.control.hardware_handshake_enable, data, handshake); - data = get_csr_val_with_updated_field(ral.control.memory_buffer_auto_increment_enable, data, - auto_inc_buffer); - data = get_csr_val_with_updated_field(ral.control.fifo_auto_increment_enable, data, - auto_inc_fifo); - data = get_csr_val_with_updated_field(ral.control.data_direction, data, data_direction); data = get_csr_val_with_updated_field(ral.control.abort, data, abort_pending); data = get_csr_val_with_updated_field(ral.control.go, data, 1'b0); @@ -626,9 +631,6 @@ class dma_base_vseq extends cip_base_vseq #( set_control(dma_config.opcode, initial_transfer, dma_config.handshake, - dma_config.auto_inc_buffer, - dma_config.auto_inc_fifo, - dma_config.direction, 1'b1); // Go endtask