While reviewing the notes for another chapter of my book, I came across the following concept that I had written down about five years ago but had forgotten; DMAs are commonly used to transfer data into and out of a block, but what about using the DMA to read and write block configuration values?
Before the block can carry out its task, it has to be configured. To do this, firmware writes appropriate configuration values and data tables into the block. For blocks requiring lots of values, that can translate into a lot of firmware register writes.
Why tie up the CPU to have firmware transfer those contents to the block one register write at a time? Why not have the block’s own DMA transfer those contents from memory directly into itself? And with some additional logic, the block could use its DMA to dump its register contents out to memory for firmware to access.
Here are some ideas on how this feature can be used:
- Configure block – Firmware has a pointer to a set of values needed by the block. Give that pointer to the block’s DMA and let it load the values. Example values are configuration settings for all the registers or lookup tables required for data processing.
- Read block configuration – Firmware could occasionally instruct the block to dump its registers out to memory, providing occasional snapshots of registers, for performance, operational, and error/failure analysis. This does not need to be limited to firmware-accessible registers but could also include any internal registers or values, information that firmware would not otherwise have access to.
- Test and verify block – For test and verification, firmware could have the block upload specific conditions to its internal register to conduct specific tests. This would be similar to what JTAG (Joint Test Action Group) can do but would not require a JTAG connector and test fixture and it could be used in a buttoned up system, especially ones already deployed to customers.
- Manage multiple block configurations – Multiple configuration settings can be stored out in memory and the block could be quickly configured from one configuration to another simply by loading up a different configuration set.
- Save and restore block state – The block could temporarily suspend processing the current job on one chunk of data by dumping its current address pointer, buffers, states, etc., then switch to a high-priority job. After the high-priority job is done, load back in the suspended job and resume right where it left off. This is similar to a processor pushing its registers onto the stack when swapping threads. This can also be used to save away the block’s current state before shutting off power to the block for idle, power save, low battery condition, or shutdown. When power resumes, the device driver loads up the registers and the block can resume its work. This is much like a laptop computer going into hibernation.
I’m sure there are many other applications for this concept, but it is not without its challenges that need to be addressed.
- What if the values being loaded are corrupted? Can the block detect that?
- Will values downloaded from read-only registers be ignored in the upload?
- Will an upload allow the block to launch a task if the task launch bit is set?
- What state will the block be left in if an external event or an error occurs while switching from one configuration to another?
- Can partial registers be downloaded or uploaded?
In theory, using a block’s DMA to read and write the block’s own registers has the potential to make the hardware/firmware interaction more efficient and to provide more debugging capabilities for the block. If it works, this technique could address some important best practices:
- Best Practice: Keep interactions between the firmware and the block as simple as possible.
- Best Practice: Provide debugging access to all internal registers.
However, I have not come across any systems where this method was used.. Have any of you seen this done? How have you used it? Does it work well?
Until the next DMA dump…