zephyrproject-rtos / zephyr

Primary Git Repository for the Zephyr Project. Zephyr is a new generation, scalable, optimized, secure RTOS for multiple hardware architectures.
https://docs.zephyrproject.org
Apache License 2.0
10.84k stars 6.6k forks source link

Issue with DMA transfers outside of the Zephyr DMA driver on STM32F767 #33485

Closed ddkn closed 3 years ago

ddkn commented 3 years ago

Describe the bug End goal is to use a Nucleo F767ZI board (STM32F767) to do continuous ADC sampling and write to an SD card with the DMA to assist. Currently I am trying to get a handle on the DMA functionality by implementing it without the Zehpyr driver.

I am currently trying to do a basic memory-to-memory transfer, the code currently generates a "transfer complete" interrupt, but no data is ever written to the destination array. I have printed out the registers to see if there are any conflicts, and checked for the better part of this week online researching other settings that I may have missed.

Unfortunately, no one seems to have an issue with successful interrupt generation and no DMA data transfers. Even with the ADC, I have the same issue, but I can do basic polling outside of a disabled ADC Zephyr driver (no DMA either) so I know I can work outside of the ecosystem if need be.

My question is, is there something that Zehpyr may do to prevent DMA transfers outside of the DMA driver, while still allowing "successful interrupt transfers"? Something to do with memory being protected? Or something I may be unfamiliar with?

In the log and additional notes I have attached what should be relevant. I tried to implement on DMA2->Stream0 set with Channel 0, with a simple one time transfer of 10 half-words. Using the sample/basic/button code from Zehpyr as a base.

Expected behavior I expect data from one array (test_src) to transfer to the other array (test_dst) which is just a memory-to-memory on DMA2 Stream 0, Channel 0.

Impact Looking to move forward on working with the ADC + DMA functionality

Logs and console output

␀*** Booting Zephyr OS build zephyr-v2.5.0-965-g33c9be90ccea  ***
DMA2_Stream0->FCR  : 0x00000021
DMA2_Stream0->NDTR : 0x0000000a
Starting DMA
Entered DMA2 Interrupt
Transfer complete
test_src        test_dst
0               0
1               0
2               0
3               0
4               0
5               0
6               0
7               0
8               0
9               0
Set up button at GPIOC pin 13
Set up LED at GPIOB pin 0
Press the button
RCC->CR            : 0x03077c80
RCC->CFGR          : 0x0000100a
RCC->PLLCFGR       : 0x23401204
RCC->AHB1ENR       : 0x005007ff
DMA2_Stream0->CR   : 0x00022e94
DMA2_Stream0->NDTR : 0x00000000
DMA2_Stream0->PAR  : 0x200204e0
DMA2_Stream0->MOAR : 0x200204cc
DMA2_Stream0->FCR  : 0x00000025
&dma_adc_sample[0] : 0x200204cc
dma_adc_sample     : 0x200204cc
test_src           : 0x200204e0
DMA transfer complete? 

Environment (please complete the following information):

Additional context Here is the relevant code snippets that is added to the sample/basic/button code from Zehpyr

#define MY_MAX_SIZE 10

static volatile uint16_t test_dst[MY_MAX_SIZE];
static volatile uint16_t test_src[MY_MAX_SIZE];

static volatile bool dma_complete = false;
static volatile bool dma_error = false;

static void DMA2_Stream0_IRQHandler(void *args)
{
›   printk("Entered DMA2 Interrupt\n");
›   if (DMA2->LISR & DMA_LISR_TCIF0) {
›   ›   DMA2->LIFCR |= DMA_LIFCR_CTCIF0;
›   ›   printk("Transfer complete\n");
›   ›   dma_complete = true;
›   }
›
›   if (DMA2->LISR & DMA_LISR_TEIF0) {
›   ›   DMA2->LIFCR |= DMA_LIFCR_CTEIF0;
›   ›   dma_error = true;
›   }
›   printk("test_src\ttest_dst\n");
›   for (int i = 0; i < MY_MAX_SIZE; i++) {
›   ›   printk("%x\t\t%x\n",
›   ›   ›   (uint32_t)test_src[i],
›   ›   ›   (uint32_t)test_dst[i]);
›   }
}

static inline void _dma_init()
{
›   /* Goal select DMA2 to use ADC1 on stream 0 channel 0 */

›   volatile uint32_t tmpreg;
›   /* Enable peripherial clock for DMA */
›   RCC->AHB1ENR |= RCC_AHB1ENR_DMA2EN;
›   /* Give 2 clock cycles to stabilize */
›   tmpreg = (RCC->AHB1ENR & RCC_AHB1ENR_DMA2EN);
›   (void)tmpreg;

›   /* Select Channel 0 for Stream 0 on DMA2
›    * On chip reset selects channel 0 by default
›    */
›   //DMA2_Stream0->CR = (0x0 << DMA_SxCR_CHSEL_Pos);

›   /* Configuration transfer */
›   /* Reset assumes periph. to memory, perih. no inc., DMA flow controller */
›   const uint32_t cr = (DMA_SxCR_MINC ›| /* Memory inc. */
›   ›   ›        DMA_SxCR_PINC ›| /* Peripheral inc. */
›   ›   ›        DMA_SxCR_MSIZE_0 › | /* 16-bit */
›   ›   ›        DMA_SxCR_PSIZE_0 › | /* 16-bit */
›   ›   ›        DMA_SxCR_DIR_1 ›   | /* Mem to mem */
›   ›   ›        DMA_SxCR_PL_1);›     /* Priority high */

›   /* Populate source */
›   for (int i = 0; i < MY_MAX_SIZE; i++) {
›   ›   test_src[i] = i;
›   }
›
›   /* Specify transfer addresses and size for ADC and data */
›   DMA2_Stream0->PAR = (uint32_t)test_src;
›   DMA2_Stream0->M0AR = (uint32_t)test_dst;

›   /* Single byte transfer */
›   DMA2_Stream0->NDTR = sizeof(test_dst)/sizeof(uint16_t);

›   /* XXX Zephyr style IRQ */
›   IRQ_CONNECT(DMA2_Stream0_IRQn, PRIORITY_DMA,
›   ›       DMA2_Stream0_IRQHandler, 0, 0);
›   irq_enable(DMA2_Stream0_IRQn);

›   /* Enable transfer error+complete interrupts */
›   DMA2_Stream0->CR = (cr | DMA_SxCR_TCIE | DMA_SxCR_TEIE);

›   /* Enable transfers + Clear interrupt flags (datasheet suggestion) */
›   DMA2->LIFCR |= (DMA_LIFCR_CTCIF0 | DMA_LIFCR_CTEIF0 |
›   ›   ›   DMA_LIFCR_CTCIF1 | DMA_LIFCR_CTEIF1 |
›   ›   ›   DMA_LIFCR_CTCIF2 | DMA_LIFCR_CTEIF2 |
›   ›   ›   DMA_LIFCR_CTCIF3 | DMA_LIFCR_CTEIF3);
›   DMA2->HIFCR |= (DMA_HIFCR_CTCIF4 | DMA_HIFCR_CTEIF4 |
›   ›   ›   DMA_HIFCR_CTCIF5 | DMA_HIFCR_CTEIF5 |
›   ›   ›   DMA_HIFCR_CTCIF6 | DMA_HIFCR_CTEIF6 |
›   ›   ›   DMA_HIFCR_CTCIF7 | DMA_HIFCR_CTEIF7);

›   printk("DMA2_Stream0->FCR  : 0x%08x\n", DMA2_Stream0->FCR);
›   printk("DMA2_Stream0->NDTR : 0x%08x\n", DMA2_Stream0->NDTR);
›   printk("Starting DMA\n");
›   DMA2_Stream0->CR |= DMA_SxCR_EN;
}

Snippet added to the main loop

›   _dma_init();

›   printk("Press the button\n");
›   printk("RCC->CR            : 0x%08x\n", RCC->CR);
›   printk("RCC->CFGR          : 0x%08x\n", RCC->CFGR);
›   printk("RCC->PLLCFGR       : 0x%08x\n", RCC->PLLCFGR);
›   printk("RCC->AHB1ENR       : 0x%08x\n", RCC->AHB1ENR);
›   printk("DMA2_Stream0->CR   : 0x%08x\n", DMA2_Stream0->CR);
›   printk("DMA2_Stream0->NDTR : 0x%08x\n", DMA2_Stream0->NDTR);
›   printk("DMA2_Stream0->PAR  : 0x%08x\n", DMA2_Stream0->PAR);
›   printk("DMA2_Stream0->MOAR : 0x%08x\n", DMA2_Stream0->M0AR);
›   printk("DMA2_Stream0->FCR  : 0x%08x\n", DMA2_Stream0->FCR);
›   printk("&dma_adc_sample[0] : 0x%08x\n", (uint32_t)&dma_adc_sample[0]);
›   printk("dma_adc_sample     : 0x%08x\n", (uint32_t)dma_adc_sample);
›   printk("test_src           : 0x%08x\n", (uint32_t)test_src);

›   while (1) {
›   ›   match_led_to_button(button, led);
›   ›   k_msleep(SLEEP_TIME_MS);
›   ›   if (ADC1->SR & ADC_SR_OVR) {
›   ›   ›   ADC1->SR &= ~ADC_SR_OVR;
›   ›   ›   printk("ADC Error!\n");
›   ›   }

›   ›   if (dma_error) {
›   ›   ›   dma_error = false;
›   ›   ›   printk("DMA Error!\n");
›   ›   }

›   ›   if (dma_complete) {
›   ›   ›   dma_complete = false;
›   ›   ›   printk("DMA transfer complete?\n");
›   ›   }
›   }
}
ddkn commented 3 years ago

So the DMA issue seems to be caused by the Dcache of this specific chip. In the beginning of the program if I pass,

void main(void)
{
›   SCB_DisableDCache();
›   _dma_init();
›   ...
}

The DMA works as expected, I assume this is an issue with how that is stored in the startup file in the chip? My friend pointed me to this, which suggests it is a problem with ChibiOS and NuttX possibly, otherwise an annoyance to handle.

I was sifting around trying to disable CACHE_MANAGEMENT in Kconfig, but to no avail. I am just posting this here in the event it helps someone else.

I will leave this open if someone would like to add more relevant information or a better approach within the Zephyr eco-system.

FRASTM commented 3 years ago

It is very similar to the reported issue "UART failure with CONFIG_UART_ASYNC_API" #31711 As you did, a workaround is about to avoid using Dcache with stm32F7 during DMA operations (https://github.com/zephyrproject-rtos/zephyr/pull/32833). Starting point to fix this could be found in cleaning/invalidating the cache (like https://github.com/zephyrproject-rtos/zephyr/pull/32832 or https://github.com/zephyrproject-rtos/zephyr/pull/27911)

erwango commented 3 years ago

@ddkn can you check the following PR could answer your request ? https://github.com/zephyrproject-rtos/zephyr/pull/33846

Also, I'm removing the 'bug' tag as not directly showing issue with existing feature.

ddkn commented 3 years ago

Hi @erwango, Sorry I have been busy with classes and wasn't able to respond for a bit, only now getting back to things.

I ended up just implementing the ADC+DMA outside the zephyr ecosystem. Looking at #33846 looks like it is specific to the DAC DMA continuous mode with hardware triggering, andthat could provide some needed guidance for the ADC continuous mode. I also saw that there is this #32719 but haven't played with it.

erwango commented 3 years ago

@ddkn, thanks for your feedback. I'm closing the point then.

github-actions[bot] commented 3 years ago

This issue has been marked as stale because it has been open (more than) 60 days with no activity. Remove the stale label or add a comment saying that you would like to have the label removed otherwise this issue will automatically be closed in 14 days. Note, that you can always re-open a closed issue at any time.