====== DMA ====== The gamepad features 5 DMA channels. The first two channels are intended for communication with peripherals such as SPI and IR, while the three last are general DMA channels suitable for uses such as blitting graphics to the framebuffer. The base I/O addresses for the DMA channels are as follows: ^ Address ^ Desc. ^ | 0xF0004000 | General DMA registers | | 0xF0004040 | DMA0 - peripheral | | 0xF0004060 | DMA1 - peripheral | | 0xF0004100 | DMA2 - general purpose | | 0xF0004140 | DMA3 - general purpose | | 0xF0004180 | DMA4 - general purpose | The DMA channels trigger the following IRQs upon completion: ^ Channel ^ IRQ ^ | DMA0 | 0x08 | | DMA1 | 0x09 | | DMA2 | 0x0D | | DMA3 | 0x0E | | DMA4 | 0x0C | ===== General registers ===== ^ Address ^ Desc. ^ | 0xF0004000 | General DMA enable | **0xF0004000** General DMA enable. ^ Bits ^ Desc. ^ | 0 | DMA enable | | 2-7 | DMA memory priority? | | 15 | ?? no observed effect | Bit 0 seems to also control framebuffer transfer to the LCD? When it is cleared, the screen stops refreshing. Bit 1 is set by the bootloader, but it doesn't seem to actually exist (or is write-only?). Bit 2-7 are set to zero. They probably affect the priority of DMA relative to the CPU and other devices that access RAM. 0 is the fastest setting for DMA. 63 is the slowest, making DMA about 9 times slower than 0. Bit 15 is set by the firmware but not the bootloader. ===== DMA0/1 ===== These DMA channels are intended for communication with peripherals such as SPI and IR. They transfer data from memory to a peripheral, or the other way around. ^ Offset ^ Desc. ^ | 0x00 | Start/stop | | 0x04 | Transfer control | | 0x08 | Chunk size | | 0x0C | Memory buffer stride | | 0x10 | Total byte count minus one | | 0x14 | Memory address | **Base+0x00** DMA start/stop. ^ Bits ^ Desc. ^ | 0 | Start transfer / Busy | | 1 | Stop transfer | Bit 0 remains set while the transfer is in progress. Bit 1 may be used to stop the transfer at any point. It is recommended to explicitly stop a transfer after it has completed -- it seems that not doing so can cause weird occasional problems. **Base+0x04** Controls various aspects of the DMA transfer. ^ Bits ^ Desc. ^ | 0 | Direction; 0=read from peripheral, 1=write to peripheral | | 1-3 | Peripheral select | The peripheral select determines which peripheral is accessed. Here are the known settings: ^ Value ^ Desc. ^ | 0 | ?? | | 1 | ?? | | 2 | SPI | | 3 | ?? | | 4 | UART0 | | 5 | UART1 | | 6 | UART2 | | 7 | ?? | Values 0, 2, 3, 4, 6 occur in the stock firmware. Note that the peripheral must be configured correctly for the DMA transfer to work, ie. for SPI, the transfer direction in 0xF0004404 must match that of the DMA channel, and so on. **Base+0x08** Chunk size, ie. size in bytes of each chunk to copy. This register is 12 bits wide. Setting this register to zero will treat the memory buffer as a contiguous buffer, ignoring the stride register. Setting this register to a value greater than the memory buffer stride will result in DMA malfunction. **Base+0x0C** Memory buffer stride, ie. byte offset between each chunk in the memory buffer. This register is 12 bits wide. **Base+0x10** Total byte count minus one. This register is updated as the transfer progresses. This register is 20 bits wide. When the transfer has completed, it reads as 0xFFFFF. **Base+0x14** Memory address. This register is updated as the transfer progresses. This register is 24 bits wide. ===== DMA2/3/4 ===== General purpose DMA channels. They are intended for blitting graphics to the screen, but may also be used for general memory fill/copy operations. They are quite faster than doing copy loops on the CPU. ^ Offset ^ Desc. ^ | 0x00 | Start/stop | | 0x04 | Transfer control | | 0x08 | Chunk size | | 0x0C | Source buffer stride | | 0x10 | Destination buffer stride | | 0x14 | Total byte count minus one | | 0x18 | Source address | | 0x1C | Destination address | | 0x20 | Fill value 1 | | 0x24 | Fill value 2 | | 0x28 | Positive memory offset | | 0x2C | Negative memory offset | **Base+0x00** DMA start/stop. ^ Bits ^ Desc. ^ | 0 | Start transfer / Busy | | 1 | Stop transfer | Bit 0 remains set while the transfer is in progress. Bit 1 may be used to stop the transfer at any point. It is recommended to explicitly stop a transfer after it has completed -- it seems that not doing so can cause weird occasional problems. **Base+0x04** Controls various aspects of the DMA transfer. ^ Bits ^ Desc. ^ | 0 | Reverse byte order in each 16-byte block | | 1 | Reverse byte order in each 8-byte block | | 2-5 | Logic operator | | 6 | Fill unit; 0=8-bit, 1=16-bit | | 7 | Masked fill mode | | 8 | Background for masked fill mode; 0=use fill value 2, 1=do not fill | | 9 | Bit order for masked fill mode; 0=MSb first, 1=LSb first | | 10 | Simple fill mode (has priority over bit 7) | | 16-17 | N value for bit 18; 0=1 byte, 1=2 bytes, 2=4 bytes, 3=8 bytes | | 18 | Skip every other N source bytes | | 19 | Apply memory offsets to source address | | 20 | Apply memory offsets to destination address | The logic operator bits specify how the source and destination data are combined. The following operators exist: ^ Value ^ Operator ^ Desc. ^ | 0x0 | Clear | dst = 0x00 | | 0x1 | NOR | dst = !(dst OR src) | | 0x2 | AND inverted | dst = dst AND !src | | 0x3 | Copy inverted | dst = !src | | 0x4 | AND reverse | dst = !dst AND src | | 0x5 | Invert | dst = !dst | | 0x6 | Exclusive OR | dst = dst XOR src | | 0x7 | NAND | dst = !(dst AND src) | | 0x8 | AND | dst = dst AND src | | 0x9 | Equivalence | dst = !(dst XOR src) | | 0xA | Noop | dst = dst | | 0xB | OR inverted | dst = dst OR !src | | 0xC | Copy | dst = src | | 0xD | OR reverse | dst = !dst OR src | | 0xE | OR | dst = dst OR src | | 0xF | Set | dst = 0xFF | Logic operators also apply in fill modes -- in these modes, the source data is the fill color. Simple fill mode ignores the source address and fills the destination with fill value 1. Masked fill mode uses the source data to determine how to fill the destination. Each byte read from the source address is used as a mask for the next 8 destination units. If a mask bit is 1, fill value 1 is used. If a mask bit is 0, either fill value 2 is used, or the destination data is left unchanged (see bit 8). Not yet known if there is a way to do a masked copy. When bit 18 is set, twice as much source data is read. This bit also applies in masked fill mode. Bits 19 and 20 allow to apply fixed offsets to the source and destination addresses. **Base+0x08** Chunk size, ie. size in bytes of each line to copy/fill. This register is 12 bits wide. Setting this register to zero will treat the source and destination buffers as contiguous buffers, ignoring the stride registers. Setting this register to a value greater than the source or destination buffer stride will result in DMA malfunction. **Base+0x0C** Source buffer stride, ie. byte offset between each line in the source buffer. This register is 12 bits wide. In simple fill mode, this register is ignored. In masked fill mode, this register is ignored. The mask buffer stride must be 1/8th of the destination buffer stride. **Base+0x10** Destination buffer stride, ie. byte offset between each line in the destination buffer. This register is 12 bits wide. **Base+0x14** Total byte count minus one. This register is updated as the transfer progresses. This register is 24 bits wide. When the transfer has completed, it reads as 0xFFFFFF. **Base+0x18** Source address. This register is updated as the transfer progresses. This register is 24 bits wide. In simple fill mode, this register is ignored. **Base+0x1C** Destination address. This register is updated as the transfer progresses. This register is 24 bits wide. **Base+0x20** Fill value 1. Used in simple fill mode, and in masked fill mode where the mask is 1. This register is 16 bits wide. **Base+0x24** Fill value 2. Used in masked fill mode where the mask is 0. This register is 16 bits wide. **Base+0x28** Positive memory offset. When bit 19 of the control register are set, this offset is added to the source address. When bit 20 is set, this offset is added to the destination address. Mask is 0x3FFFF0, thus the offset is expressed in 16-byte units. Not sure what the intended use of this register is, or if there's a way to have it auto-update under certain circumstances? **Base+0x2C** Negative memory offset. Same function as base+0x28, except this offset is subtracted from source/destination addresses.