Dynamic C version 10 introduced support for the internal DMA controller of the Rabbit 4000 microprocessor. DMA stands for “Direct Memory Access.” The DMA controller takes control of the address and data bus from the CPU so that data transfers occur without processor handling.There are eight DMA channels; a DMA channel is a system pathway for transferring data directly to or from memory and peripheral devices without using the CPU. DMA memory addresses are always physical addresses and are never translated by the MMU.
The rest of this section discusses DMA from a software perspective. For detailed information about the DMA controller, see the Rabbit 4000 Microprocessor User’s Manual.
There are some global resources associated with all DMA channels. These resources are managed by Dynamic C libraries because it would be difficult for most users to determine their optimal usage. The library DMA.LIB contains all of the DMA functionality available to the user. The advanced user can manually override the library settings by directly manipulating the DMA control registers; however, this is not recommended.
The debug function DMAprintRegs() lets you view the values of the DMA master registers:
DMCR (DMA Master Control Register) - Transfer and interrupt priority levels.
DMCSR (DMA Master Control/Status Register) - DMA channel status
DMTCR (DMA Master Timing Control Register) - Sets the burst size, the inter-burst timing and the relative prioritization of the channels.
For more information on Rabbit registers, click on “I/O Registers” on the Dynamic C help menu or consult the Rabbit 4000 Microprocessor User’s Manual (or the user’s manual specific to your Rabbit) to get information about directly manipulating the DMA registers.
Dynamic C provides several API functions for use with the DMA controller that was introduced with the Rabbit 4000. These functions make it unnecessary for an application to directly manipulate the DMA registers. Complete descriptions for all DMA API functions can be found from within Dynamic C using the Function Lookup feature from the help menu (Ctrl+H); also, in the Dynamic C Function Reference Manual. In this section we will look at some of these functions.
The function DMAalloc() is called to allocate a DMA channel; the function DMAunalloc() is called to release it. The handle returned by DMAalloc() is passed to all the DMA transfer functions (see Section 11.4) and must be passed to DMAunalloc() to release the channel. All eight channels are identical, with the priority between them either fixed or rotating.
The function DMAsetParameters() accepts parameters that set transfer and interrupt priority levels, channel priority, maximum bytes per burst and minimum clocks between bursts. DMAsetParameters() must be called by an application before a DMA channel can be used; however, the channel can be allocated before DMAsetParameters() is called. The DMA parameters set in DMAsetParameters() are global, that is, they apply to all channels.
Some low-level functions are also provided for the DMA controller. These functions use the DMA channel number instead of the handle returned by DMAalloc(). The function DMAhandle2chan() provides a DMA channel number when passed a valid handle.
The low-level functions DMAsetBufDesc() and DMAloadBufDesc() work with a buffer descriptor associated with a DMA channel. A buffer descriptor is a memory structure that controls the DMA operation. It contains a control byte, a byte count for the data, a source address, a destination address and an optional link address. Low-level transfer functions are provided for use with the buffer descriptor functions. They are DMAstartAuto() and DMAstartDirect().
Sample programs located in Samples\Rabbit4000\DMA\ illustrate many of the API functions.
An interrupt may be requested when a DMA channel has completed transferring data. All channels assert this type of interrupt at the same priority level, which can be set to level 1, 2, or 3 with a call to DMAsetParameters(). Whether or not an interrupt is requested at the end of a transfer is determined by flag options in the DMA transfer function. See Section 11.4.4 for more information.
Each channel has its own interrupt vector in the processor’s external interrupt vector table.
A DMA transfer is requested when the channel wants the DMA controller to take control of the address and data buses.
DMA transfers may be programmed to occur at any priority level (0, 1, 2, or 3). Relative prioritization among the DMA channels is set using one of the following constants:
DMA_IDP_FIXED - fixed priorities, with higher channel numbers taking precedence
DMA_IDP_ROTATE_FINE - priorities are rotated after every byte transferred
DMA_IDP_ROTATE_COARSE - priorities rotated after every transfer request, the size of which is determined by “chunkiness,” another parameter also passed to the function DMAsetParameters().
The DMA transfer priority and the relative prioritization among channels are set in DMAsetParameters().
DMA transfers can happen in burst or single-cycle mode. The “chunkiness” parameter passed to the DMA transfer function determines the burst size.
There are three types of transfers, with associated transfer functions.
Memory-to-memory transfers. Use DMAmem2mem().
Internal I/O address transfers to or from memory. Use DMAioi2mem() and DMAmem2ioi(), respectively.
External I/O address transfers to or from memory. Use DMAioe2mem() and DMAmem2ioe(), respectively.
The DMA transfer functions accept the following flags:
DMA_F_REPEAT - transfer will be a cycle.
DMA_F_INTERRUPT - indicates an interrupt will be triggered at the completion of the transfer.
DMA_F_LAST_SPECIAL - (only for Ethernet or HDLC peripherals) Internal Source: Status byte written to initial buffer descriptor before last data. Internal Destination: Last byte written to offset address for frame termination.
DMA_F_SRC_DEC - only for transfers with memory source. Indicates the source address should be decremented. (If not specified, a memory source address is incremented.)
DMA_F_DEST_DEC - only for transfers with memory destination. Indicates the destination address should be decremented. (If not specified, a memory destination address is incremented.)
DMA_F_STOP_MATCH - indicates whether or not to stop the DMA transfer when a character is reached. The match byte and mask should have been set previously by calling the DMAmatchSetup() function.
DMA_F_TIMER - indicates the DMA timer will be used. Set the divisor first by calling the DMAtimerSetup() function. DMA_F_TIMER_1BPR indicates that the timed transfers will send one byte per request instead of the entire descriptor.
Use of the Rabbit 4000 Ethernet imposes some restrictions on the global DMA settings. It is recommended that applications make use of the DMA API functions to avoid possibly breaking Ethernet by using DMA settings that are not compatible with the Ethernet restrictions. For example, Ethernet uses DMA channels 6 and 7 and fixed prioritization among the channels. There are also requirements regarding burst size and the minimum time between bursts. If you are using Ethernet and call the function DMAsetParameters() with parameters that are not compatible with the Ethernet restrictions, those parameters will be quietly ignored.