cancel
Showing results for 
Search instead for 
Did you mean: 

On the USB1 HS of the STM32H753 How do you reset the receive DMA engine so you can reuse an address? The TX DMA engine works fine.

CSchu.11.56
Associate

Software:

STM32Cube_FW_H7_V1.7.0

HW Info:

STM32H753I-EVAL2

Using OTG1_HS with external PHY in dedicated Host mode.

USB device - camera running 30 fps that sends its data over a BULK channel instead of a ISOC channel (Vendor supplied device I do not get to change).

USB Analyzer (Teledyne Lecroy Mercury T2) – To see what is actually on the wire

Issues:

When DMA is disabled everything works, My problem in this mode is that 80% of the processor time is utilized by the ISR. Therefore I want to use the DAM engine to reduce the number of ISR calls. I enable DMA { hhcd.Init.dma_enable = 1; } and then I can not even enumerate the device. I have tried allocating the buffers in three different memory areas: AXI {Address: 0x24003840}, SRAM1 {Address: 0x30001220}, and SDRAM {Address: 0xD1488020}. No matter where I allocate the buffer the AHBERR (AHB error) interrupt never gets set. When I use AXI and SRAM1 the USBH_Get_DevDesc() function transmits 8 bytes of zero’s. When I use SDRAM the first 2 transmissions work (DMAed from SDRAM), The first receive of 8 bytes works (DMA’ed into SDRAM), but the second receive of 18 bytes from the full device descriptor writes nothing into ram. In fact whatever is in RAM does not get changed. I have verified that the address of any buffer used is always 32 bit aligned. I have verified using the ST-Link debugger that the OTG_HCDMAB1 register of the correct channel is getting set to the correct address just before the USB channel gets enabled. The OTG_HCTSIZ1 register is getting set to 1 packet (PKTCNT) of 64 bytes (XFRSIZ) as expected. The OTG_HCINT1 is all zeros as expected. After the enable a single step of the debugger allows the USB to finish the transaction. The hardware sets HCTSIZ1 to 0 packets (PKTCNT) and XFRSIZ is 46 (This indicates 18 bytes were received). The address stored at OTG_HCDMAB1 increments by 20 (This coincides with the 32 bit alignment requirement). OTG_HCINT1 only indicates ACK, CHH, and XFRC as expected.

I have become way too familiar with source code of the ST USB driver provided in STM32Cube_FW_H7_V1.7.0 (USB Host Lib / HAL_HCD / LL_USB).

I have tried submitting the same address for the same buffer for every transaction. I have also tried ping ponging between 2 buffers, all with no change.

Will data Cache or instruction Cache have an impact on this?

I have tried all the setting of the DMA burst Length [HBSTLEN[3:0] of OTG_GAHBCFG] with no change.

Rev 7 of the Reference manual has added a little info on the DMA scatter/gather. Is there any other documentation for this?

/****************** Update ***************************/

I had a bug in my ping pong code. After fixing that the two calls to USBH_Get_DevDesc() work. Each response goes into its own buffer. The address of the device gets set and a 0 length packet is requested and the device ACK’s that. This actually writes the address of 0 to the OTG_HCDMAB1 register. Then 38 micro frames go by and then the config descriptor is requested by calling USBH_Get_CfgDesc(). This call sends the request successful and 9 bytes are received. To bad they are no where to be found :( The sequence of addresses that are written to the DMA buffer registers follow:

  1. 0xD1209140 → OTG_HCDMAB0 /* Write Dev Desc Request */
  2. 0xD1208040 → OTG_HCDMAB1 /* Response */
  3. 0x00000000 → OTG_HCDMAB0 /* Write 0 Length Out Packet */
  4. 0xD1209140 → OTG_HCDMAB0 /* Write Dev Desc Request */
  5. 0xD1208440 → OTG_HCDMAB1 /* Response */
  6. 0x00000000 → OTG_HCDMAB0 /* Write 0 Length Out Packet */
  7. 0xD1209140 → OTG_HCDMAB0 /* Write Set Address Request */
  8. 0x00000000 → OTG_HCDMAB1 /* Request 0 Length In Packet */
  9. 0xD1209140 → OTG_HCDMAB0 /* Write Config Desc Request */
  10. 0xD1208040 → OTG_HCDMAB1 /* No data was written to 0xD1208040 */
  11. 0x00000000 → OTG_HCDMAB0 /* Write 0 Length Out Packet */

Seems as though the DMA for a write (Out Packet) work just fine. The DMA transfers for reads (IN Packet) do not work. I will try and add 4 buffers to my ping pong code. I expect I will receive the first 4 packets and then nothing. This seems to fit with issues other people have had with STM32F processors.

/****************** Update ***************************/

As I expected I get 4 receives with 4 buffers. Old addresses can not be reused. It is not practical to walk through all your memory 1024 bytes at a time. There has to be some sort of register magic to reset the USB DMA engine to be able to reuse addresses. I tried incrementing the address by 4 bytes and reusing it and that does not work. I wonder if changing the address in the register to something that is less then the last used address is the issue?

1 REPLY 1
CSchu.11.56
Associate

After complete frustration for 3 days I took a breath and tried looking up Application Notes for DMAs of the STM32H7 and found the AN5001 (Rev 2) that goes over the MDMA module. I read through that meticulously and it references AN4031 (Rev 3) which is not for the SMT32H7 but apparently is close enough. So I read it meticulously. Section 4.10 talks about cache causing data incoherency. That lead me into looking at how the MPU was configured. It was using the default configuration for everything but SDRAM. That explains why I could only get SDRAM to work a little. I added a region for the external SRAM and a region for SRAM1,2,3. Then I set the attributes to noncacheable and nonshareable for all 3 regions. Abracadabra the DMA transfers now work. I will post my MPU Config below for anyone else that is struggling with DMA transfers. I believe this is a potential issue for all DMA transfers. I hope well placed __DSB() and/or __DMB() instructions will also solve this issue, otherwise I will notch out a dedicated section of memory just for DMA transfers. On another note, This did not solve my ISR issue. It actually made it worse. The processor time was not spent reading from the USB RX buffer any more but is still processing way too many USB Interrupts. I will now find out what interrupt is causing me the issues. I expect it is the SOF interrupt.

static void MPU_Config(void)
{
  MPU_Region_InitTypeDef MPU_InitStruct;
 
  /* Disable the MPU */
  HAL_MPU_Disable();
 
  /* Configure the MPU attributes as WT for SDRAM */
  MPU_InitStruct.Enable = MPU_REGION_ENABLE;
  MPU_InitStruct.BaseAddress = 0xD0000000;
  MPU_InitStruct.Size = MPU_REGION_SIZE_32MB;
  MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS;
  MPU_InitStruct.IsBufferable = MPU_ACCESS_NOT_BUFFERABLE;
  MPU_InitStruct.IsCacheable = MPU_ACCESS_NOT_CACHEABLE;
  MPU_InitStruct.IsShareable = MPU_ACCESS_NOT_SHAREABLE;
  MPU_InitStruct.Number = MPU_REGION_NUMBER0;
  MPU_InitStruct.TypeExtField = MPU_TEX_LEVEL1;
  MPU_InitStruct.SubRegionDisable = 0x00;
  MPU_InitStruct.DisableExec = MPU_INSTRUCTION_ACCESS_DISABLE;
 
  HAL_MPU_ConfigRegion(&MPU_InitStruct);
 
  /* Configure the MPU attributes for External SRAM */
  MPU_InitStruct.Enable = MPU_REGION_ENABLE;
  MPU_InitStruct.BaseAddress = 0x68000000;
  MPU_InitStruct.Size = MPU_REGION_SIZE_1MB;
  MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS;
  MPU_InitStruct.IsBufferable = MPU_ACCESS_NOT_BUFFERABLE;
  MPU_InitStruct.IsCacheable = MPU_ACCESS_NOT_CACHEABLE;
  MPU_InitStruct.IsShareable = MPU_ACCESS_NOT_SHAREABLE;
  MPU_InitStruct.Number = MPU_REGION_NUMBER1;
  MPU_InitStruct.TypeExtField = MPU_TEX_LEVEL1;
  MPU_InitStruct.SubRegionDisable = 0x00;
  MPU_InitStruct.DisableExec = MPU_INSTRUCTION_ACCESS_DISABLE;
 
  HAL_MPU_ConfigRegion(&MPU_InitStruct);
 
  /* Configure the MPU attributes for SRAM1,2,3 */
  MPU_InitStruct.Enable = MPU_REGION_ENABLE;
  MPU_InitStruct.BaseAddress = 0x30000000;
  MPU_InitStruct.Size = MPU_REGION_SIZE_256KB;
  MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS;
  MPU_InitStruct.IsBufferable = MPU_ACCESS_NOT_BUFFERABLE;
  MPU_InitStruct.IsCacheable = MPU_ACCESS_NOT_CACHEABLE;
  MPU_InitStruct.IsShareable = MPU_ACCESS_NOT_SHAREABLE;
  MPU_InitStruct.Number = MPU_REGION_NUMBER2;
  MPU_InitStruct.TypeExtField = MPU_TEX_LEVEL1;
  MPU_InitStruct.SubRegionDisable = 0x00;
  MPU_InitStruct.DisableExec = MPU_INSTRUCTION_ACCESS_DISABLE;
 
  HAL_MPU_ConfigRegion(&MPU_InitStruct);
 
  /* Enable the MPU */
  HAL_MPU_Enable(MPU_PRIVILEGED_DEFAULT);
}