Differences Between LDMA emlib M2P and M2M Transfers
08/215/2018 | 09:54 PM
Question
How can I use the LDMA to perform transfers that don't have presets in em_ldma.h?
Answer
The em_ldma.h LDMA descriptors (ex. LDMA_DESCRIPTOR_SINGLE_M2M_WORD) do not determine any hardware capabilities, they are merely descriptor setting presets that fit the average use case of the LDMA. You can still set up the LDMA to do transfers that don't fit any of these presets, such as a linked Memory to Peripheral descriptor of halfwords.
To do this, you have two basic options; pick a preset that is close to your desired setup and make slight changes, or build your descriptor from scratch.
Using Presets
First, let's look at what our preset options are. There are three settings that change from one preset to another: size (WORD, HALF, BYTE), link option (SINGLE, LINKABS, LINKREL), and type. The available options for each type is listed below:
M2M (Memory to Memory) - all three link options are available for all three data sizes
M2P/P2M (Memory to Peripheral) - SINGLE and LINKREL options are available for BYTE only
P2P (Peripheral to Peripheral) - only SINGLE transfers are available for BYTE only
WRI (Write transfer) - all three link options are available, and the size is irrelevant
SYNC (Synchronous transfer) - all three link options are available, and the size is irrelevant
As the name suggests, all that the "size" option affects is the .xfer.size variable. "WORD" presets set .xfer.size to ldmaCtrlSizeWord, "HALF" use ldmaCtrlSizeHalf, and "BYTE" use ldmaCtrlSizeByte.
The "link" option affects if linking is used, and how the next descriptor is determined if there is linking. "SINGLE" disables linking while "LINKREL" and "LINKABS" are different ways of indicating the destination of the link. This is seen through 3 variables: linkMode, link, and linkAddr.
SINGLE
LINKABS
LINKREL
linkMode
0
Absolute
Relative
link
0
1
1
linkAddr
0
must be set at runtime
an input variable "linkjump" is used
The "type" option affects six variables, most notably structType. For WRI and SYNC descriptors, structType is set accordingly and therefore many of the other registers don't matter. For all the others, we see the differences in the following table:
M2M
M2P
P2M
P2P
structReq
1
0
0
0
doneIfs
0
1
1
1
reqMode
All
Block
Block
Block
srcInc
One
One
None
None
dstInc
One
None
One
None
So what we see is that with Memory to Memory transfers, the assumption is that the user wants the transfer to be fast. It happens as soon as it's loaded, it moves all the data at once, and it doesn't interrupt at the end (unless it's the last descriptor in the link).
With peripheral transfers, the assumption is that the user wants transfers spread out over multiple requests. The transfers only start when requested by a signal, and an interrupt is requested when the descriptor has finished (even if it's not the last descriptor in the link). Also, the peripheral side of the transfer should have a step size of "None", so that the transfers always use the same peripheral register.
Building from Scratch
You also have the option to define the value of each variable inside the LDMA_Descriptor_t typedef. While this does require you to know exactly what you want, it makes it easier to tell at a glance what any given variable is being set to (instead of having to look inside em_ldma.h to find out). An example of how to do this is given below.
An Example
Let's say we want to use the LDMA to transfer halfwords from memory to a peripheral register. We don't want interrupts or automatic transfers, we want this transfer to be part of a relative linked list of transfers, and we want separate signals for each halfword.
Ideally we could use "LDMA_DESCRIPTOR_LINKREL_M2P_HALF", but unfortunately for us, that doesn't exist. So instead, we'll look at modifying either LINKREL_M2M_HALF or LINKREL_M2P_BYTE, because these are the two closest presets to what we want.
LINKREL_M2M_HALF is convenient because it will set us up with the correct data size and doneIfs settings. However, because it assumes we want fast, large transfers, it has structReq and reqModeAll set, which we would have to clear. Also, we would have to set dstInc to None to ensure that all our individual transfers are to the same peripheral register.
// Declare local variables
uint32_t source, dest, xferSize;
// Setup using preset
LDMA_Descriptor_t myDescriptor = (LDMA_Descriptor_t)LDMA_DESCRIPTOR_LINKREL_M2M_HALF(
&source,
&dest,
xferSize,
1);
// Edit preset to fit individual needs
myDescriptor.xfer.structReq = 0;
myDescriptor.xfer.reqMode = ldmaCtrlReqModeBlock;
myDescriptor.xfer.dstInc = ldmaCtrlDstIncNone;
LINKREL_M2P_BYTE is convenient because it will set us up with the correct peripheral assumptions; we want structReq off, reqMode Block, and dstInc None. Here, we would have to manually clear doneIfs and set size.
// Declare local variables
uint32_t source, dest, xferSize;
// Set up using preset
LDMA_Descriptor_t myDescriptor = (LDMA_Descriptor_t)LDMA_DESCRIPTOR_LINKREL_M2P_BYTE(
&source,
&dest,
xferSize,
1);
// Edit presets to fit individual needs
myDescriptor.xfer.size = ldmaCtrlSizeHalf;
myDescriptor.xfer.doneIfs = false;
We see that in this case, it is slightly easier to edit LINKREL_M2P_BYTE than it is to edit LINKREL_M2M_HALF.
If you want to build your descriptor from scratch, it might look like this:
As we can see, it's probably best to build your own descriptor if you want to do something unusual, but if you just want to get one up and running quickly, there's likely a preset that will work for you with minimal changes necessary.
Differences Between LDMA emlib M2P and M2M Transfers
Question
How can I use the LDMA to perform transfers that don't have presets in em_ldma.h?
Answer
The em_ldma.h LDMA descriptors (ex. LDMA_DESCRIPTOR_SINGLE_M2M_WORD) do not determine any hardware capabilities, they are merely descriptor setting presets that fit the average use case of the LDMA. You can still set up the LDMA to do transfers that don't fit any of these presets, such as a linked Memory to Peripheral descriptor of halfwords.
To do this, you have two basic options; pick a preset that is close to your desired setup and make slight changes, or build your descriptor from scratch.
Using Presets
First, let's look at what our preset options are. There are three settings that change from one preset to another: size (WORD, HALF, BYTE), link option (SINGLE, LINKABS, LINKREL), and type. The available options for each type is listed below:
As the name suggests, all that the "size" option affects is the .xfer.size variable. "WORD" presets set .xfer.size to ldmaCtrlSizeWord, "HALF" use ldmaCtrlSizeHalf, and "BYTE" use ldmaCtrlSizeByte.
The "link" option affects if linking is used, and how the next descriptor is determined if there is linking. "SINGLE" disables linking while "LINKREL" and "LINKABS" are different ways of indicating the destination of the link. This is seen through 3 variables: linkMode, link, and linkAddr.
The "type" option affects six variables, most notably structType. For WRI and SYNC descriptors, structType is set accordingly and therefore many of the other registers don't matter. For all the others, we see the differences in the following table:
So what we see is that with Memory to Memory transfers, the assumption is that the user wants the transfer to be fast. It happens as soon as it's loaded, it moves all the data at once, and it doesn't interrupt at the end (unless it's the last descriptor in the link).
With peripheral transfers, the assumption is that the user wants transfers spread out over multiple requests. The transfers only start when requested by a signal, and an interrupt is requested when the descriptor has finished (even if it's not the last descriptor in the link). Also, the peripheral side of the transfer should have a step size of "None", so that the transfers always use the same peripheral register.
Building from Scratch
You also have the option to define the value of each variable inside the LDMA_Descriptor_t typedef. While this does require you to know exactly what you want, it makes it easier to tell at a glance what any given variable is being set to (instead of having to look inside em_ldma.h to find out). An example of how to do this is given below.
An Example
Let's say we want to use the LDMA to transfer halfwords from memory to a peripheral register. We don't want interrupts or automatic transfers, we want this transfer to be part of a relative linked list of transfers, and we want separate signals for each halfword.
Ideally we could use "LDMA_DESCRIPTOR_LINKREL_M2P_HALF", but unfortunately for us, that doesn't exist. So instead, we'll look at modifying either LINKREL_M2M_HALF or LINKREL_M2P_BYTE, because these are the two closest presets to what we want.
LINKREL_M2M_HALF is convenient because it will set us up with the correct data size and doneIfs settings. However, because it assumes we want fast, large transfers, it has structReq and reqModeAll set, which we would have to clear. Also, we would have to set dstInc to None to ensure that all our individual transfers are to the same peripheral register.
LINKREL_M2P_BYTE is convenient because it will set us up with the correct peripheral assumptions; we want structReq off, reqMode Block, and dstInc None. Here, we would have to manually clear doneIfs and set size.
We see that in this case, it is slightly easier to edit LINKREL_M2P_BYTE than it is to edit LINKREL_M2M_HALF.
If you want to build your descriptor from scratch, it might look like this:
As we can see, it's probably best to build your own descriptor if you want to do something unusual, but if you just want to get one up and running quickly, there's likely a preset that will work for you with minimal changes necessary.