Skip to content

Commit

Permalink
[hw,dma,rtl] Flexible address increment options
Browse files Browse the repository at this point in the history
This PR adds support for a flexible address increment mode.
For the source and destination address, the DMA can be
configured to perform:
* No address increment (FIX). The DMA always reads/writes
from the same address.
* Wrapped increment (WRAP). The DMA increments and when
finishing one chunk wraps back to the starting address.
* Linear increment (LINEAR). The DMA always increments the
address

These modes are independent of the hardware handshake mode.

Signed-off-by: Robert Schilling <rschilling@rivosinc.com>
  • Loading branch information
Razer6 committed Aug 9, 2024
1 parent 9d58bef commit 185a262
Show file tree
Hide file tree
Showing 5 changed files with 197 additions and 225 deletions.
69 changes: 44 additions & 25 deletions hw/ip/dma/data/dma.hjson
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,7 @@
Must be aligned to the transfer width.
'''
swaccess: "rw"
hwaccess: "hrw"
hwaccess: "hro"
regwen: "CFG_REGWEN"
fields: [
{ bits: "31:0"
Expand All @@ -149,7 +149,7 @@
Source and destination address must have the same alignment.
'''
swaccess: "rw"
hwaccess: "hrw"
hwaccess: "hro"
regwen: "CFG_REGWEN"
fields: [
{ bits: "31:0"
Expand All @@ -172,7 +172,7 @@
Source and destination address must have the same alignment.
'''
swaccess: "rw"
hwaccess: "hrw"
hwaccess: "hro"
regwen: "CFG_REGWEN"
fields: [
{ bits: "31:0"
Expand All @@ -191,7 +191,7 @@
Source and destination address must have the same alignment.
'''
swaccess: "rw"
hwaccess: "hrw"
hwaccess: "hro"
regwen: "CFG_REGWEN"
fields: [
{ bits: "31:0"
Expand Down Expand Up @@ -464,35 +464,49 @@
No explicit clearing necessary.
'''
}
{ bits: "5"
name: "memory_buffer_auto_increment_enable"
resval: 0x0
desc: '''
Used in conjunction with the hardware handshake mode of operation.
Auto Increments the memory buffer address register by data size to point to the next memory buffer address.
Generate a warning (assert interrupt) if the auto-incremented address reaches close to the value set in limit address register to prevent destination buffer overflow.
Enables firmware to take appropriate action prior to reaching the limit.
'''
}
{ bits: "6"
name: "fifo_auto_increment_enable"
{ bits: "6:5"
name: "src_addr_increment"
resval: 0x0
desc: '''
Used in conjunction with the hardware handshake mode of operation.
If set, reads/writes from/to incremental addresses for FIFO data accesses within each chunk, resetting to the initial value at the beginning of each new chunk.
Else uses the same address for all transactions.
Defines the address increment behavior for the source address.
'''
enum: [
{ value: "0",
name: "FIX"
desc: "Do not increment the address, always read from the same address."
}
{ value: "1",
name: "WRAP"
desc: "Wrap the address to the beginning after transferring one chunk."
}
{ value: "2",
name: "LINEAR"
desc: "Always increment the source address."
}
]
}
{ bits: "7"
name: "data_direction"
{ bits: "8:7"
name: "dst_addr_increment"
resval: 0x0
desc: '''
Used in conjunction with the hardware handshake enable.
0: Receive data from LSIO FIFO to memory buffer.
1: Send data from memory buffer to LSIO FIFO.
Defines the address increment behavior for the destination address.
'''
enum: [
{ value: "0",
name: "FIX"
desc: "Do not increment the address, always write to the same address."
}
{ value: "1",
name: "WRAP"
desc: "Wrap the address to the beginning after transferring one chunk."
}
{ value: "2",
name: "LINEAR"
desc: "Always increment the destination address."
}
]
}
{ bits: "8"
{ bits: "9"
name: "initial_transfer"
resval: 0x0
hwaccess: "hrw"
Expand Down Expand Up @@ -633,6 +647,11 @@
resval: 0x0
desc: "The source or destination ASID contains an invalid value."
}
{ bits: "8"
name: "addr_increment_error"
resval: 0x0
desc: "The source or destination address increment mode contains an invalid value."
}
]
}
{ multireg: {
Expand Down
118 changes: 45 additions & 73 deletions hw/ip/dma/rtl/dma.sv
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,6 @@ module dma

logic capture_return_data;
logic [top_pkg::TL_DW-1:0] read_return_data_q, read_return_data_d, dma_rsp_data;
logic [SYS_ADDR_WIDTH-1:0] new_src_addr, new_dst_addr;

logic dma_state_error;
dma_ctrl_state_e ctrl_state_q, ctrl_state_d;
Expand Down Expand Up @@ -270,14 +269,11 @@ module dma
always_comb begin
control_d.opcode = opcode_e'(reg2hw.control.opcode.q);
control_d.cfg_handshake_en = reg2hw.control.hardware_handshake_enable.q;
control_d.cfg_data_direction = reg2hw.control.data_direction.q;
control_d.cfg_fifo_auto_increment_en = reg2hw.control.fifo_auto_increment_enable.q;
control_d.src_addr_increment = addr_incr_e'(reg2hw.control.src_addr_increment.q);
control_d.dst_addr_increment = addr_incr_e'(reg2hw.control.dst_addr_increment.q);
control_d.range_valid = reg2hw.range_valid.q;
control_d.enabled_memory_range_base = reg2hw.enabled_memory_range_base.q;
control_d.enabled_memory_range_limit = reg2hw.enabled_memory_range_limit.q;

control_d.cfg_memory_buffer_auto_increment_en =
reg2hw.control.memory_buffer_auto_increment_enable.q;
end

prim_flop_en #(
Expand Down Expand Up @@ -743,30 +739,28 @@ module dma
default: next_error[DmaSizeErr] = 1'b1; // Invalid transfer_width
endcase

// Use start address on first byte of transaction
if ((transfer_byte_q == '0) ||
(control_q.cfg_handshake_en &&
// Does the source address need resetting to the configured base address?
((control_q.cfg_data_direction && chunk_byte_q == '0 &&
!control_q.cfg_memory_buffer_auto_increment_en) ||
(!control_q.cfg_data_direction &&
(chunk_byte_q == '0 || !control_q.cfg_fifo_auto_increment_en))))) begin
src_addr_d = {reg2hw.src_addr_hi.q, reg2hw.src_addr_lo.q};
// or when being in the fixed address mode
control_q.src_addr_increment == AddrIncrFix ||
// or when transferring the first byte of a chunk and in wrapped increment mode
(chunk_byte_q == '0 && control_q.src_addr_increment == AddrIncrWrap)) begin
dst_addr_d = {reg2hw.dst_addr_hi.q, reg2hw.dst_addr_lo.q};
end else begin
// Advance from the previous transaction within this chunk
src_addr_d = src_addr_q + SYS_ADDR_WIDTH'(transfer_width_d);
end

// Use start address on first byte of transaction
if ((transfer_byte_q == '0) ||
(control_q.cfg_handshake_en &&
// Does the destination address need resetting to the configured base address?
((!control_q.cfg_data_direction && chunk_byte_q == '0 &&
!control_q.cfg_memory_buffer_auto_increment_en) ||
(control_q.cfg_data_direction &&
(chunk_byte_q == '0 || !control_q.cfg_fifo_auto_increment_en))))) begin
dst_addr_d = {reg2hw.dst_addr_hi.q, reg2hw.dst_addr_lo.q};
// or when being in the fixed address mode
control_q.dst_addr_increment == AddrIncrFix ||
// or when transferring the first byte of a chunk and in wrapped increment mode
(chunk_byte_q == '0 && control_q.dst_addr_increment == AddrIncrWrap)) begin
dst_addr_d = {reg2hw.src_addr_hi.q, reg2hw.src_addr_lo.q};
end else begin
// Advance from the previous transaction within this chunk
dst_addr_d = dst_addr_q + SYS_ADDR_WIDTH'(transfer_width_d);
dst_addr_d = src_addr_q + SYS_ADDR_WIDTH'(transfer_width_d);
end

unique case (transfer_width_d)
Expand Down Expand Up @@ -814,6 +808,16 @@ module dma
next_error[DmaOpcodeErr] = 1'b1;
end

if (!(control_q.src_addr_increment inside {AddrIncrFix, AddrIncrWrap,
AddrIncrLinear})) begin
next_error[DmaAddrIncrErr] = 1'b1;
end

if (!(control_q.dst_addr_increment inside {AddrIncrFix, AddrIncrWrap,
AddrIncrLinear})) begin
next_error[DmaAddrIncrErr] = 1'b1;
end

// Inline hashing is only allowed for 32-bit transfer width
if (use_inline_hashing) begin
if (reg2hw.transfer_width.q != DmaXfer4BperTxn) begin
Expand Down Expand Up @@ -1083,7 +1087,6 @@ module dma
logic test_done_interrupt;
logic test_error_interrupt;
logic data_move_state, data_move_state_valid;
logic update_dst_addr_reg, update_src_addr_reg;

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;
Expand All @@ -1101,14 +1104,6 @@ module dma
(ctrl_state_q == DmaShaWait) ||
(ctrl_state_q == DmaShaFinalize);

assign new_dst_addr = control_q.cfg_data_direction ?
({reg2hw.dst_addr_hi.q, reg2hw.dst_addr_lo.q} + SYS_ADDR_WIDTH'(transfer_width_q)) :
({reg2hw.dst_addr_hi.q, reg2hw.dst_addr_lo.q} + SYS_ADDR_WIDTH'(reg2hw.chunk_data_size.q));

assign new_src_addr = control_q.cfg_data_direction ?
({reg2hw.src_addr_hi.q, reg2hw.src_addr_lo.q} + SYS_ADDR_WIDTH'(reg2hw.chunk_data_size.q)) :
({reg2hw.src_addr_hi.q, reg2hw.src_addr_lo.q} + SYS_ADDR_WIDTH'(transfer_width_q));

// Calculate the number of bytes remaining until the end of the current chunk.
// Note that the total transfer size may be a non-integral multiple of the programmed chunk size,
// so we must consider the `total_data_size` here too; this is important in determining the
Expand All @@ -1126,41 +1121,16 @@ module dma
hw2reg.control.go.de = clear_go || cfg_abort_en;
hw2reg.control.go.d = 1'b0;

// Unlock the register set when not being busy. IDLE is not the right indicator,
// Unlock the register set when not being busy. IDLE is not the right indicator,dm
// since multi-chunked transfers roundtrip via IDLE.
hw2reg.cfg_regwen.d = prim_mubi_pkg::mubi4_bool_to_mubi(~reg2hw.status.busy.q);

// If we are in hardware handshake mode with auto-increment increment the corresponding address
// when finishing a DMA operation when transitioning from a data move state to the idle state
update_dst_addr_reg = 1'b0;
update_src_addr_reg = 1'b0;
if (control_q.cfg_handshake_en && control_q.cfg_memory_buffer_auto_increment_en &&
data_move_state && (ctrl_state_d == DmaIdle)) begin
if (control_q.cfg_data_direction) begin
update_src_addr_reg = 1'b1;
end else begin
update_dst_addr_reg = 1'b1;
end
end

// 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
hw2reg.control.initial_transfer.de = 1'b1;
end

hw2reg.dst_addr_hi.de = update_dst_addr_reg;
hw2reg.dst_addr_hi.d = new_dst_addr[63:32];

hw2reg.dst_addr_lo.de = update_dst_addr_reg;
hw2reg.dst_addr_lo.d = new_dst_addr[31:0];

hw2reg.src_addr_hi.de = update_src_addr_reg;
hw2reg.src_addr_hi.d = new_src_addr[63:32];

hw2reg.src_addr_lo.de = update_src_addr_reg;
hw2reg.src_addr_lo.d = new_src_addr[31:0];

// Assert busy write enable on
// - transitions from IDLE out
// - clearing the go bit (going back to idle)
Expand Down Expand Up @@ -1230,23 +1200,25 @@ module dma
set_error_code = (ctrl_state_q != DmaError) && (ctrl_state_d == DmaError);

// Fiddle out error signals
hw2reg.error_code.src_addr_error.de = set_error_code | clear_status;
hw2reg.error_code.dst_addr_error.de = set_error_code | clear_status;
hw2reg.error_code.opcode_error.de = set_error_code | clear_status;
hw2reg.error_code.size_error.de = set_error_code | clear_status;
hw2reg.error_code.bus_error.de = set_error_code | clear_status;
hw2reg.error_code.base_limit_error.de = set_error_code | clear_status;
hw2reg.error_code.range_valid_error.de = set_error_code | clear_status;
hw2reg.error_code.asid_error.de = set_error_code | clear_status;

hw2reg.error_code.src_addr_error.d = clear_status? '0 : next_error[DmaSrcAddrErr];
hw2reg.error_code.dst_addr_error.d = clear_status? '0 : next_error[DmaDstAddrErr];
hw2reg.error_code.opcode_error.d = clear_status? '0 : next_error[DmaOpcodeErr];
hw2reg.error_code.size_error.d = clear_status? '0 : next_error[DmaSizeErr];
hw2reg.error_code.bus_error.d = clear_status? '0 : next_error[DmaBusErr];
hw2reg.error_code.base_limit_error.d = clear_status? '0 : next_error[DmaBaseLimitErr];
hw2reg.error_code.range_valid_error.d = clear_status? '0 : next_error[DmaRangeValidErr];
hw2reg.error_code.asid_error.d = clear_status? '0 : next_error[DmaAsidErr];
hw2reg.error_code.src_addr_error.de = set_error_code | clear_status;
hw2reg.error_code.dst_addr_error.de = set_error_code | clear_status;
hw2reg.error_code.opcode_error.de = set_error_code | clear_status;
hw2reg.error_code.size_error.de = set_error_code | clear_status;
hw2reg.error_code.bus_error.de = set_error_code | clear_status;
hw2reg.error_code.base_limit_error.de = set_error_code | clear_status;
hw2reg.error_code.range_valid_error.de = set_error_code | clear_status;
hw2reg.error_code.asid_error.de = set_error_code | clear_status;
hw2reg.error_code.addr_increment_error.de = set_error_code | clear_status;

hw2reg.error_code.src_addr_error.d = clear_status? '0 : next_error[DmaSrcAddrErr];
hw2reg.error_code.dst_addr_error.d = clear_status? '0 : next_error[DmaDstAddrErr];
hw2reg.error_code.opcode_error.d = clear_status? '0 : next_error[DmaOpcodeErr];
hw2reg.error_code.size_error.d = clear_status? '0 : next_error[DmaSizeErr];
hw2reg.error_code.bus_error.d = clear_status? '0 : next_error[DmaBusErr];
hw2reg.error_code.base_limit_error.d = clear_status? '0 : next_error[DmaBaseLimitErr];
hw2reg.error_code.range_valid_error.d = clear_status? '0 : next_error[DmaRangeValidErr];
hw2reg.error_code.asid_error.d = clear_status? '0 : next_error[DmaAsidErr];
hw2reg.error_code.addr_increment_error.d = clear_status? '0 : next_error[DmaAddrIncrErr];

// Clear the control.abort bit once we have handled the abort request
hw2reg.control.abort.de = hw2reg.status.aborted.de;
Expand Down
15 changes: 11 additions & 4 deletions hw/ip/dma/rtl/dma_pkg.sv
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ package dma_pkg;
typedef logic [dma_reg_pkg::NumIntClearSources-1:0] lsio_trigger_t;

// Possible error bits the DMA can raise
typedef enum logic [3:0] {
typedef enum logic [4:0] {
DmaSrcAddrErr,
DmaDstAddrErr,
DmaOpcodeErr,
Expand All @@ -17,6 +17,7 @@ package dma_pkg;
DmaBaseLimitErr,
DmaRangeValidErr,
DmaAsidErr,
DmaAddrIncrErr,
DmaErrLast
} dma_error_e;

Expand Down Expand Up @@ -44,14 +45,20 @@ package dma_pkg;
OpcSha512 = 4'h3
} opcode_e;

// Supported address increment modes by the DMA
typedef enum logic [1:0] {
AddrIncrFix = 2'h0,
AddrIncrWrap = 2'h1,
AddrIncrLinear = 2'h2
} addr_incr_e;

// Control state captured during the operation
typedef struct packed {
// Control register
opcode_e opcode;
logic cfg_handshake_en;
logic cfg_memory_buffer_auto_increment_en;
logic cfg_fifo_auto_increment_en;
logic cfg_data_direction;
addr_incr_e src_addr_increment;
addr_incr_e dst_addr_increment;
logic range_valid;
// Enabled memory base register
logic [31:0] enabled_memory_range_base;
Expand Down
Loading

0 comments on commit 185a262

Please sign in to comment.