156 lines
9.7 KiB
Plaintext
156 lines
9.7 KiB
Plaintext
<<<
|
|
:sectnums:
|
|
==== Direct Memory Access Controller (DMA)
|
|
|
|
[cols="<3,<3,<4"]
|
|
[frame="topbot",grid="none"]
|
|
|=======================
|
|
| Hardware source file(s): | neorv32_dma.vhd |
|
|
| Software driver file(s): | neorv32_dma.c |
|
|
| | neorv32_dma.h |
|
|
| Top entity port: | none |
|
|
| Configuration generics: | `IO_DMA_EN` | implement DMA when `true`
|
|
| CPU interrupts: | fast IRQ channel 10 | DMA transfer done (see <<_processor_interrupts>>)
|
|
|=======================
|
|
|
|
|
|
**Overview**
|
|
|
|
The NEORV32 DMA provides a small-scale scatter/gather direct memory access controller that allows to transfer and
|
|
modify data independently of the CPU. A single read/write transfer channel is implemented that is configured via
|
|
memory-mapped registers. a configured transfer can either be triggered manually or by a programmable CPU FIRQ interrupt
|
|
(see <<_neorv32_specific_fast_interrupt_requests>>).
|
|
|
|
The DMA is connected to the central processor-internal bus system (see section <<_address_space>>) and can access the same
|
|
address space as the CPU core. It uses _interleaving mode_ accessing the central processor bus only if the CPU does not
|
|
currently request and bus access.
|
|
|
|
The controller can handle different data quantities (e.g. read bytes and write them back as sign-extend words) and can
|
|
also change the Endianness of data while transferring.
|
|
|
|
.DMA Demo Program
|
|
[TIP]
|
|
A DMA example program can be found in `sw/example/demo_dma`.
|
|
|
|
|
|
**Theory of Operation**
|
|
|
|
The DMA provides four memory-mapped interface registers: A status and control register `CTRL` and three registers for
|
|
configuring the actual DMA transfer. The base address of the source data is programmed via the `SRC_BASE` register.
|
|
Vice versa, the base address of the destination data is programmed via the `DST_BASE`. The third configuration register
|
|
`TTYPE` is use to configure the actual transfer type and the number of elements to transfer.
|
|
|
|
The DMA is enabled by setting the `DMA_CTRL_EN` bit of the control register. Manual trigger mode (i.e. the DMA transfer is
|
|
triggered by writing to the `TTYPE` register) is selected if `DMA_CTRL_AUTO` is cleared. Alternatively, the DMA transfer can
|
|
be triggered by a processor internal FIRQ signal if `DMA_CTRL_AUTO` is set (see section below).
|
|
|
|
The DMA uses a load-modify-write data transfer process. Data is read from the bus system, internally modified and then written
|
|
back to the bus system. This combination is implemented as an atomic progress, so canceling the current transfer by clearing the
|
|
`DMA_CTRL_EN` bit will stop the DMA right after the current load-modify-write operation.
|
|
|
|
If the DMA controller detects a bus error during operation, it will set either the `DMA_CTRL_ERROR_RD` (error during
|
|
last read access) or `DMA_CTRL_ERROR_WR` (error during last write access) and will terminate the current transfer.
|
|
Software can read the `SRC_BASE` or `DST_BASE` register to retrieve the address that caused the according error.
|
|
Alternatively, software can read back the `NUM` bits of the control register to determine the index of the element
|
|
that caused the error. The error bits are automatically cleared when starting a new transfer.
|
|
|
|
When the `DMA_CTRL_DONE` flag is set the DMA has actually executed a transfer. However, the `DMA_CTRL_ERROR_*` flags
|
|
should also be checked to verify that the executed transfer completed without errors. The `DMA_CTRL_DONE` flag is
|
|
automatically cleared when writing the `CTRL` register.
|
|
|
|
.DMA Access Privilege Level
|
|
[WARNING]
|
|
Transactions performed by the DMA are executed as bus transactions with elevated **machine-mode** privilege level.
|
|
Additionally, all physical memory protection rules (<<_pmp_isa_extension>>) defined by the CPU are **bypassed**.
|
|
|
|
|
|
**Transfer Configuration**
|
|
|
|
If the DMA is set to **manual trigger mode** (`DMA_CTRL_AUTO` = 0) writing the `TTRIG` register will start the
|
|
programmed DMA transfer. Once started, the DMA will read one data quantity from the source address, processes it internally
|
|
and then will write it back to the destination address. The `DMA_TTYPE_NUM` bits of the `TTYPE` register define how many
|
|
times this process is repeated by specifying the number of elements to transfer.
|
|
|
|
Optionally, the source and/or destination addresses can be increments according to the data quantities
|
|
automatically by setting the according `DMA_TTYPE_SRC_INC` and/or `DMA_TTYPE_DST_INC` bit.
|
|
|
|
Four different transfer quantities are available, which are configured via the `DMA_TTYPE_QSEL` bits:
|
|
|
|
* `00`: Read source data as byte, write destination data as byte
|
|
* `01`: Read source data as byte, write destination data as zero-extended word
|
|
* `10`: Read source data as byte, write destination data as sign-extended word
|
|
* `11`: Read source data as word, write destination data as word
|
|
|
|
Optionally, the DMA controller can automatically convert Endianness of the transferred data if the `DMA_TTYPE_ENDIAN`
|
|
bit is set.
|
|
|
|
.Address Alignment
|
|
[IMPORTANT]
|
|
Make sure to align the source and destination base addresses to the according transfer data quantities. For instance,
|
|
word-to-word transfers require that the two LSB of `SRC_BASE` and `DST_BASE` are cleared.
|
|
|
|
.Writing to IO Device
|
|
[IMPORTANT]
|
|
When writing data to IO / peripheral devices (for example to the <<_cyclic_redundancy_check_crc>>) the destination
|
|
data quantity has to be set to **word** (32-bit) since all IO registers can only be written in full 32-bit word mode.
|
|
|
|
|
|
**Automatic Trigger**
|
|
|
|
As an alternative to the manual trigger mode, the DMA can be configured to **automatic trigger mode** starting a pre-configured
|
|
transfer if a specific processor-internal peripheral issues an interrupt request. The automatic trigger mode is enabled by
|
|
setting the `CTRL` register's `DMA_CTRL_AUTO` bit. In this configuration _no_ transfer is started when writing to the DMA's
|
|
`TTYPE` register.
|
|
|
|
The actual trigger is configured via the control register `DMA_CTRL_FIRQ_MASK`. These bits reflect the state of the CPU's
|
|
<<_mip>> CSR showing any pending fast interrupt requests (for a full list see <<_neorv32_specific_fast_interrupt_requests>>).
|
|
The same bit definitions/locations as for the <<_mip>> and <<_mie>> CPU CSRs are used.
|
|
If any of the enabled sources issues an interrupt the DMA will start the pre-configured transfer (note that all enabled
|
|
sources are logically OR-ed).
|
|
|
|
.FIRQ Trigger
|
|
[NOTE]
|
|
The DMA transfer will start if a **rising edge** is detected on _any_ of the enabled FIRQ source channels.
|
|
|
|
|
|
**Memory Barrier / Fence Operation**
|
|
|
|
Optionally, the DMA can issue a FENCE request to the downstream memory system when a transfer has been completed
|
|
without errors. This can be used to re-sync caches (flush and reload) and buffers to maintain data coherency.
|
|
This automatic fencing is enabled by the setting the control register's `DMA_CTRL_FENCE` bit.
|
|
|
|
|
|
**DMA Interrupt**
|
|
|
|
The DMA features a single CPU interrupt that is triggered when the programmed transfer has completed. This
|
|
interrupt is also triggered if the DMA encounters a bus error during operation. An active DMA interrupt has to be
|
|
explicitly cleared again by writing zero to the according <<_mip>> CSR bit.
|
|
|
|
|
|
**Register Map**
|
|
|
|
.DMA Register Map (`struct NEORV32_DMA`)
|
|
[cols="<2,<1,<4,^1,<7"]
|
|
[options="header",grid="all"]
|
|
|=======================
|
|
| Address | Name [C] | Bit(s), Name [C] | R/W | Function
|
|
.10+<| `0xffffed00` .10+<| `CTRL` <|`0` `DMA_CTRL_EN` ^| r/w <| DMA module enable
|
|
<|`1` `DMA_CTRL_AUTO` ^| r/w <| Enable automatic mode (FIRQ-triggered)
|
|
<|`2` `DMA_CTRL_FENCE` ^| r/w <| Issue a downstream FENCE operation when DMA transfer completes (without errors)
|
|
<|`7:3` _reserved_ ^| r/- <| reserved, read as zero
|
|
<|`8` `DMA_CTRL_ERROR_RD` ^| r/- <| Error during read access, clears when starting a new transfer
|
|
<|`9` `DMA_CTRL_ERROR_WR` ^| r/- <| Error during write access, clears when starting a new transfer
|
|
<|`10` `DMA_CTRL_BUSY` ^| r/- <| DMA transfer in progress
|
|
<|`11` `DMA_CTRL_DONE` ^| r/c <| Set if a transfer was executed; auto-clears on write-access
|
|
<|`15:12` _reserved_ ^| r/- <| reserved, read as zero
|
|
<|`31:16` `DMA_CTRL_FIRQ_MASK_MSB : DMA_CTRL_FIRQ_MASK_LSB` ^| r/w <| FIRQ trigger mask (same bits as in <<_mip>>)
|
|
| `0xffffed04` | `SRC_BASE` |`31:0` | r/w | Source base address (shows the last-accessed source address when read)
|
|
| `0xffffed08` | `DST_BASE` |`31:0` | r/w | Destination base address (shows the last-accessed destination address when read)
|
|
.6+<| `0xffffed0c` .6+<| `TTYPE` <|`23:0` `DMA_TTYPE_NUM_MSB : DMA_TTYPE_NUM_LSB` ^| r/w <| Number of elements to transfer (shows the last-transferred element index when read)
|
|
<|`26:24` _reserved_ ^| r/- <| reserved, read as zero
|
|
<|`28:27` `DMA_TTYPE_QSEL_MSB : DMA_TTYPE_QSEL_LSB` ^| r/w <| Source data quantity select (`00` = byte, `01` = half-word, `10` = word)
|
|
<|`29` `DMA_TTYPE_SRC_INC` ^| r/w <| Constant (`0`) or incrementing (`1`) source address
|
|
<|`30` `DMA_TTYPE_DST_INC` ^| r/w <| Constant (`0`) or incrementing (`1`) destination address
|
|
<|`31` `DMA_TTYPE_ENDIAN` ^| r/w <| Swap Endianness when set
|
|
|=======================
|