2020-07-10 01:08 AM
Hi,
I have been breaking my head, looking at the following code:
STM32Cube_FW_H7_V1.7.0\Driver\STM32H7xx_HAL_Driver\Src\stm32h7xx_hal_dma.c
Specifically at the following:
typedef struct
{
__IO uint32_t ISR; /*!< DMA interrupt status register */
__IO uint32_t Reserved0;
__IO uint32_t IFCR; /*!< DMA interrupt flag clear register */
} DMA_Base_Registers;
typedef struct
{
__IO uint32_t ISR; /*!< BDMA interrupt status register */
__IO uint32_t IFCR; /*!< BDMA interrupt flag clear register */
} BDMA_Base_Registers;
static void DMA_SetConfig(DMA_HandleTypeDef *hdma, uint32_t SrcAddress, uint32_t DstAddress, uint32_t DataLength)
{
/* calculate DMA base and stream number */
DMA_Base_Registers *regs_dma = (DMA_Base_Registers *)hdma->StreamBaseAddress;
BDMA_Base_Registers *regs_bdma = (BDMA_Base_Registers *)hdma->StreamBaseAddress;
if(IS_DMA_DMAMUX_ALL_INSTANCE(hdma->Instance) != 0U) /* No DMAMUX available for BDMA1 */
{
/* Clear the DMAMUX synchro overrun flag */
hdma->DMAmuxChannelStatus->CFR = hdma->DMAmuxChannelStatusMask;
if(hdma->DMAmuxRequestGen != 0U)
{
/* Clear the DMAMUX request generator overrun flag */
hdma->DMAmuxRequestGenStatus->RGCFR = hdma->DMAmuxRequestGenStatusMask;
}
}
if(IS_DMA_STREAM_INSTANCE(hdma->Instance) != 0U) /* DMA1 or DMA2 instance */
{
/* Clear all interrupt flags at correct offset within the register */
regs_dma->IFCR = 0x3FUL << (hdma->StreamIndex & 0x1FU);
/* Clear DBM bit */
((DMA_Stream_TypeDef *)hdma->Instance)->CR &= (uint32_t)(~DMA_SxCR_DBM);
/* Configure DMA Stream data length */
((DMA_Stream_TypeDef *)hdma->Instance)->NDTR = DataLength;
/* Peripheral to Memory */
if((hdma->Init.Direction) == DMA_MEMORY_TO_PERIPH)
{
/* Configure DMA Stream destination address */
((DMA_Stream_TypeDef *)hdma->Instance)->PAR = DstAddress;
/* Configure DMA Stream source address */
((DMA_Stream_TypeDef *)hdma->Instance)->M0AR = SrcAddress;
}
/* Memory to Peripheral */
else
{
/* Configure DMA Stream source address */
((DMA_Stream_TypeDef *)hdma->Instance)->PAR = SrcAddress;
/* Configure DMA Stream destination address */
((DMA_Stream_TypeDef *)hdma->Instance)->M0AR = DstAddress;
}
}
else if(IS_BDMA_CHANNEL_INSTANCE(hdma->Instance) != 0U) /* BDMA instance(s) */
{
/* Clear all flags */
regs_bdma->IFCR = (BDMA_ISR_GIF0) << (hdma->StreamIndex & 0x1FU);
/* Configure DMA Channel data length */
((BDMA_Channel_TypeDef *)hdma->Instance)->CNDTR = DataLength;
/* Peripheral to Memory */
if((hdma->Init.Direction) == DMA_MEMORY_TO_PERIPH)
{
/* Configure DMA Channel destination address */
((BDMA_Channel_TypeDef *)hdma->Instance)->CPAR = DstAddress;
/* Configure DMA Channel source address */
((BDMA_Channel_TypeDef *)hdma->Instance)->CM0AR = SrcAddress;
}
/* Memory to Peripheral */
else
{
/* Configure DMA Channel source address */
((BDMA_Channel_TypeDef *)hdma->Instance)->CPAR = SrcAddress;
/* Configure DMA Channel destination address */
((BDMA_Channel_TypeDef *)hdma->Instance)->CM0AR = DstAddress;
}
}
else
{
/* Nothing To Do */
}
}
In the Datasheet, while the BDMA module has 32 bit wide registers
BDMA_ISR,
BDMA_IFCR
which looks alright but
the DMA module has 32 bit wide registers
DMA_LISR,
DMA_HISR,
DMA_LIFCR,
DMA_HIFCR
The register definition for the DMA ISR/LIFCR in stm32h7xx_hal_dma.c
shows ISR/LIFCR 32 bits wide.
The LISR/LIFCR applies only to Streams 0 -3
while the HISR/HIFCR applies to Streams 4 - 7
But DMA_SetConfig() appears to assume that DMA_ISR/LIFCR as one contiguous 32 bit wide register, thereby rendering Streams 4 - 7 inoperable.
This appears to be a bug.
Am I looking at this correctly ?
Can someone clarify, please ?
Thanks,
Manu
Solved! Go to Solution.
2020-07-10 04:17 AM
This hurt my head a bit tracking down as well.
HAL_DMA_Init calls DMA_CalcBaseAndBitshift, which sets the values for StreamBaseAddress and StreamIndex. In particular, StreamBaseAddress points to either LISR or HISR or ISR depending on the DMA type and stream number:
/* lookup table for necessary bitshift of flags within status registers */
static const uint8_t flagBitshiftOffset[8U] = {0U, 6U, 16U, 22U, 0U, 6U, 16U, 22U};
hdma->StreamIndex = flagBitshiftOffset[stream_number & 0x7U];
if (stream_number > 3U)
{
/* return pointer to HISR and HIFCR */
hdma->StreamBaseAddress = (((uint32_t)((uint32_t*)hdma->Instance) & (uint32_t)(~0x3FFU)) + 4U);
}
else
{
/* return pointer to LISR and LIFCR */
hdma->StreamBaseAddress = ((uint32_t)((uint32_t*)hdma->Instance) & (uint32_t)(~0x3FFU));
}
After this point, such as when calling DMA_SetConfig, StreamBaseAddress is a pointer to either LISR or HISR based on the stream number, and StreamIndex can be used to clear the flags.
So no bug, I think.
2020-07-10 04:17 AM
This hurt my head a bit tracking down as well.
HAL_DMA_Init calls DMA_CalcBaseAndBitshift, which sets the values for StreamBaseAddress and StreamIndex. In particular, StreamBaseAddress points to either LISR or HISR or ISR depending on the DMA type and stream number:
/* lookup table for necessary bitshift of flags within status registers */
static const uint8_t flagBitshiftOffset[8U] = {0U, 6U, 16U, 22U, 0U, 6U, 16U, 22U};
hdma->StreamIndex = flagBitshiftOffset[stream_number & 0x7U];
if (stream_number > 3U)
{
/* return pointer to HISR and HIFCR */
hdma->StreamBaseAddress = (((uint32_t)((uint32_t*)hdma->Instance) & (uint32_t)(~0x3FFU)) + 4U);
}
else
{
/* return pointer to LISR and LIFCR */
hdma->StreamBaseAddress = ((uint32_t)((uint32_t*)hdma->Instance) & (uint32_t)(~0x3FFU));
}
After this point, such as when calling DMA_SetConfig, StreamBaseAddress is a pointer to either LISR or HISR based on the stream number, and StreamIndex can be used to clear the flags.
So no bug, I think.
2020-07-10 04:37 AM
Thanks for confirming; Good to know that there is no bug!
Looking at HAL, provokes a feeling that it needs to be rewritten in a sane way.
2020-07-19 06:24 AM
It looks as if they've tried very, very hard to use common code as much as possible instead of replicating code sequences, even when that might be more clear to the reader. Pick your poison... ;)