libdragon
Files | Functions

DMA functionality for transfers between cartridge space and RDRAM. More...

Files

file  dma.c
 DMA Controller.
 
file  dma.h
 DMA Controller.
 

Functions

bool io_accessible (uint32_t pi_address)
 Check whether the specified PI address can be accessed doing I/O from CPU. More...
 
volatile int dma_busy (void)
 Return whether the DMA controller is currently busy. More...
 
void dma_read_raw_async (void *ram_address, unsigned long pi_address, unsigned long len)
 Start reading data from a peripheral through PI DMA (low-level) More...
 
void dma_write_raw_async (const void *ram_address, unsigned long pi_address, unsigned long len)
 Start writing data to a peripheral through PI DMA (low-level) More...
 
void dma_read_async (void *ram_pointer, unsigned long pi_address, unsigned long len)
 Start reading data from a peripheral through PI DMA. More...
 
void dma_wait (void)
 Wait until an async DMA or I/O transfer is finished.
 
void dma_read (void *ram_address, unsigned long pi_address, unsigned long len)
 Read data from a peripheral through PI DMA, waiting for completion. More...
 
void dma_write (const void *ram_address, unsigned long rom_address, unsigned long len)
 Write to a peripheral. More...
 
uint32_t io_read (uint32_t pi_address)
 Read a 32 bit integer from a peripheral using the CPU. More...
 
void io_write (uint32_t pi_address, uint32_t data)
 Write a 32 bit integer to a peripheral using the CPU. More...
 

PI Status Register Bit Definitions

#define PI_STATUS_DMA_BUSY   ( 1 << 0 )
 PI DMA Busy.
 
#define PI_STATUS_IO_BUSY   ( 1 << 1 )
 PI IO Busy.
 
#define PI_STATUS_ERROR   ( 1 << 2 )
 PI Error.
 

Detailed Description

DMA functionality for transfers between cartridge space and RDRAM.

The DMA controller is responsible for handling block and word accesses from the cartridge domain. Because of the nature of the cartridge interface, code cannot use memcpy or standard pointer accesses on memory mapped to the cartridge. Consequently, the peripheral interface (PI) provides a DMA controller for accessing data.

The DMA controller requires no initialization. Using dma_read and dma_write will allow reading from the cartridge and writing to the cartridge respectively in block mode. io_read and io_write will allow a single 32-bit integer to be read from or written to the cartridge. These are especially useful for manipulating registers on a cartridge such as a gameshark. Code should never make raw 32-bit reads or writes in the cartridge domain as it could collide with an in-progress DMA transfer or run into caching issues.

Function Documentation

◆ io_accessible()

bool io_accessible ( uint32_t  pi_address)

Check whether the specified PI address can be accessed doing I/O from CPU.

The PI bus covers the full 32-bit address range. The full range is only accessible via DMA, though. A part of the range is also memory mapped to the CPU and can be accessed via io_read and io_write.

The ranges of PI address that can be accessed via CPU are:

  • 0x0500_0000 - 0x0FFF_FFFF: used by N64DD and SRAM on cartridge
  • 0x1000_0000 - 0x1FBF_FFFF: cartridge ROM
  • 0x1FD0_0000 - 0x1FFF_FFFF: no known PI peripherals use this

The rest of the 32-bit address range is only accessible via DMA.

Notice also that the range 0x2000_0000 - 0x7FFF_FFFF is theoretically accessible by the CPU but only via 64-bit addressing, so it requires assembly instructions (as the libdragon toolchain uses 32-bit pointers). No known PI peripherals use this range anyway.

This function checks whether the specified address falls into the range accessible via CPU or not.

Parameters
pi_addressPI address to check
Returns
True if the address is memory mapped, false if it is not

◆ dma_busy()

volatile int dma_busy ( void  )

Return whether the DMA controller is currently busy.

Returns
nonzero if the DMA controller is busy or 0 otherwise

◆ dma_read_raw_async()

void dma_read_raw_async ( void *  ram_address,
unsigned long  pi_address,
unsigned long  len 
)

Start reading data from a peripheral through PI DMA (low-level)

This function should be used when reading from a cartridge peripheral (typically ROM). This function just begins executing a raw DMA transfer, which is well-defined only for RAM addresses which are multiple of 8, ROM addresses which are multiple of 2, and lengths which are multiple of 2.

Use dma_wait to wait for the end of the transfer.

See dma_read_async for a higher level primitive which can perform almost arbitrary transfers.

Parameters
[out]ram_addressPointer to a buffer to place read data (must be 8-byte aligned)
[in]pi_addressMemory address of the peripheral to read from (must be 2-byte aligned)
[in]lenLength in bytes to read into ram_address (must be multiple of 2)

◆ dma_write_raw_async()

void dma_write_raw_async ( const void *  ram_address,
unsigned long  pi_address,
unsigned long  len 
)

Start writing data to a peripheral through PI DMA (low-level)

This function should be used when writing to a cartridge peripheral (typically ROM). This function just begins executing a raw DMA transfer, which is well-defined only for RAM addresses which are multiple of 8, ROM addresses which are multiple of 2, and lengths which are multiple of 2.

Use dma_wait to wait for the end of the transfer.

Parameters
[out]ram_addressPointer to a buffer to read data from (must be 8-byte aligned)
[in]pi_addressMemory address of the peripheral to write to (must be 2-byte aligned)
[in]lenLength in bytes to write into pi_address (must be multiple of 2)

◆ dma_read_async()

void dma_read_async ( void *  ram_pointer,
unsigned long  pi_address,
unsigned long  len 
)

Start reading data from a peripheral through PI DMA.

This function must be used when reading a chunk of data from a cartridge peripheral (typically, ROM). It is a wrapper over dma_read_raw_async that allows arbitrary aligned addresses and any length (including odd sizes). For fully-aligned addresses it quickly falls back to dma_read_raw_async, so it can be used generically as "default" PI DMA transfer function.

The only constraint on alignment is that the RAM and PI addresses must have the same 1-bit misalignment, that is they must either be even addresses or odd addresses. Notice that this function will assert if this constraint is not respected.

Use dma_wait to wait for the end of the transfer.

For non performance sensitive tasks such as reading and parsing data from ROM at loading time, a better option is to use DragonFS, where dfs_read falls back to a CPU memory copy to realign the data when required.

Parameters
[out]ram_pointerPointer to a buffer in RDRAM to place read data
[in]pi_addressMemory address of the peripheral to read from
[in]lenLength in bytes to read into ram_pointer

◆ dma_read()

void dma_read ( void *  ram_address,
unsigned long  pi_address,
unsigned long  len 
)

Read data from a peripheral through PI DMA, waiting for completion.

This function performs a blocking read. See dma_read_async for more information.

Parameters
[out]ram_addressPointer to a buffer in RDRAM to place read data
[in]pi_addressROM address to read from (must be in range (0x10000000-0x1FFFFFFF).
[in]lenLength in bytes to read into ram_address
Note
This function has always had an historical mistake: the pi_address is mangled to be forced into the ROM area (0x10000000-0x1FFFFFFF). This is wrong as the PI bus has full 32-bit address, and the same function could have been used to access the whole range. If you need to read outside the ROM area, use dma_read_async instead.

◆ dma_write()

void dma_write ( const void *  ram_address,
unsigned long  rom_address,
unsigned long  len 
)

Write to a peripheral.

This function should be used when writing to the cartridge.

Parameters
[in]ram_addressPointer to a buffer to read data from
[in]rom_addressCartridge address to write to (must be in range (0x10000000-0x1FFFFFFF).
[in]lenLength in bytes to write to peripheral
Note
This function has always had an historical mistake: the pi_address is mangled to be forced into the ROM area (0x10000000-0x1FFFFFFF). This is wrong as the PI bus has full 32-bit address, and the same function could have been used to access the whole range. If you need to read outside the ROM area, use dma_write_raw_async instead.

◆ io_read()

uint32_t io_read ( uint32_t  pi_address)

Read a 32 bit integer from a peripheral using the CPU.

Parameters
[in]pi_addressMemory address of the peripheral to read from
Returns
The 32 bit value read from the peripheral
Note
This function only works if the specified PI address falls within a range which is memory mapped on the CPU. See io_accessible for more information.
See also
io_accessible

◆ io_write()

void io_write ( uint32_t  pi_address,
uint32_t  data 
)

Write a 32 bit integer to a peripheral using the CPU.

Notice that writes are performed asynchronously, so the data might have not been fully written to the peripheral yet when the function returns. Use dma_wait if you need to wait for the transfer to be finished.

Parameters
[in]pi_addressMemory address of the peripheral to write to
[in]data32 bit value to write to peripheral
Note
This function only works if the specified PI address falls within a range which is memory mapped on the CPU. See io_accessible for more information.
See also
io_accessible