Closed JonathonReinhart closed 9 months ago
Hi @JonathonReinhart , you are right, the |=
shall be =
. Thanks for point out this. Will fix.
Hi @JonathonReinhart , you are right, the
|=
shall be=
. Thanks for point out this. Will fix.
Or use the DMA_COMMON_REG_SET
macro, e.g.:
DMA_COMMON_REG_SET(base, channel, ENABLESET, 1UL << DMA_CHANNEL_INDEX(base, channel));
Here's my fix: #174
@zejiang0jason I've tested #174 in my target application but haven't evaluated in any of the EVK sample apps. If you could help test, that would be great.
Background
While access to global DMA controller configuration should of course be single-threaded, it is reasonable to expect that configuration of individual channels can happen safely on different threads. For example, I might have one FreeRTOS thread managing a SPI interface while another thread deals with I2C.
Most of the per-channel configuration happens in per-channel registers (
DMA_Type.CHANNEL[i]
), so as long as only one thread manages one channel, there is no concern here.But there are also per-channel bits in a few registers in the common (
DMA_Type.COMMON
) area, e.g.DMA_Type.COMMON[0].ENABLESET
) Accessing these bits with a read-modify-write sequence is racy and could potentially inadvertently re-set bits which were recently cleared. That's why either:SET
/CLR
registers are provided, where only 1 bits being written have any effect on the underlying register.ENABLESET
,ENABLECLR
ABORT
)Problem
Some DMA functions use
|=
in an attempt to only set the desired bit. As described above, this is unnecessary and can actually set bits which were cleared on another thread.E.g.
DMA_EnableChannel()
:https://github.com/nxp-mcuxpresso/mcux-sdk/blob/cf224e2df8c2d8ae5c4f27743259803c365a6a60/drivers/lpc_dma/fsl_dma.h#L405-L410
This will compile to a read-modify-write sequence like this:
If this thread is preempted on the indicated line, and another thread clears bits in the register, they will be re-set when this thread resumes.
To Reproduce I haven't actually observed this yet.
Solution
The code should not care about the current value of the registers. (For write-only registers, it is all zero anyway.)
It should simply write a 1 in the bit to be modified, and let the
SET
/CLR
semantics do the right thing.