This discussion has been locked.
You can no longer post new replies to this discussion. If you have a question you can start a new discussion

DMA doesn't work in Keil, but it works in Atmel Studio

Hello to all,
I' using Keil MDK ARM IDE to work on a a project with an ATSAML21E18B connected to a shift register. I want to use DMA to send data through SCLK and MOSI pins. To configure my project i used the AtmelStart online platform.
I connected the SPI pins to a digital signal analyzer and I program/debugg it with the Segger J-Link.
The test program I'm working on is simple:
1- init the hardware
2- fill an array with fixed value
3- send it using DMA
4- a callback function blinks a LED when DMA has finished the data sending
5- loop 2,3,4 forever

Everything seems correct, instead it doesn't work: using Keil ARM 5.29 my code compiles (compilation without optimizations) then runs, it seems to set up all hardware devices correctly, but when my code calls the "spi_m_dma_transfer(...)" function (line 65) nothing flows out from SPI pins, and the DMA callback is never called.
I couldn't understand where the problem was until I tried to compile the exact same project with AtmelStudio: it worked!
I setted up a Keil project in the same folder with the AtmelStudio project: compiling with keil does not work, compiling with AtmelStudio works.

After two week of sleepless nights I can't understand how the same code could work differently using two IDEs and I don't know which test can still be done.
I attach here the project (Keil and AtmelStudio project) and I hope some of you can help me.
Thanks!

/* Transfer completed callback */
static void vUtil_DMA_transfer_SPI_LED( struct _dma_resource *resource ) {
	while( hri_sercomspi_get_INTFLAG_TXC_bit( SERCOM0 ) == 0 );					// wait for the last bit is sent

	gpio_set_pin_level( STAT_LED, true );												// turn ON the status LED
	delay_short();																				// wait
	gpio_set_pin_level( STAT_LED, false );												// turn OFF the status LED
	delay_short();																				// wait
}

int main( void ) {
	init_mcu();																					// system_init
	// SPI_LED_CLOCK_init
	hri_gclk_write_PCHCTRL_reg( GCLK, SERCOM0_GCLK_ID_CORE, CONF_GCLK_SERCOM0_CORE_SRC | ( 1 << GCLK_PCHCTRL_CHEN_Pos ) );
	hri_gclk_write_PCHCTRL_reg( GCLK, SERCOM0_GCLK_ID_SLOW, CONF_GCLK_SERCOM0_SLOW_SRC | ( 1 << GCLK_PCHCTRL_CHEN_Pos ) );
	hri_mclk_set_APBCMASK_SERCOM0_bit( MCLK );
	// SPI_LED_init
	spi_m_dma_init( &SPI_LED, SERCOM0 );												// init the DMA using HAL function
	// SPI_LED_PORT_init
	gpio_set_pin_direction( LED_MISO, GPIO_DIRECTION_IN );
	gpio_set_pin_pull_mode( LED_MISO, GPIO_PULL_OFF );
	gpio_set_pin_function( LED_MISO, PINMUX_PA04D_SERCOM0_PAD0 );
	gpio_set_pin_level( LED_SCLK, false );
	gpio_set_pin_direction( LED_SCLK, GPIO_DIRECTION_OUT );
	gpio_set_pin_function( LED_SCLK, PINMUX_PA05D_SERCOM0_PAD1 );
	gpio_set_pin_level( LED_MOSI, false );
	gpio_set_pin_direction( LED_MOSI, GPIO_DIRECTION_OUT );
	gpio_set_pin_function( LED_MOSI, PINMUX_PA07D_SERCOM0_PAD3 );
	// status LED init
	gpio_set_pin_function( STAT_LED, GPIO_PIN_FUNCTION_OFF );					// no special function on PIN
	gpio_set_pin_direction( STAT_LED, GPIO_DIRECTION_OUT );						// PIN setted as OUT
	gpio_set_pin_level( STAT_LED, false );												// turn OFF the status LED
	//
	spi_m_dma_register_callback( &SPI_LED, SPI_M_DMA_CB_TX_DONE, vUtil_DMA_transfer_SPI_LED );	// set the callback function for DMA operations
	spi_m_dma_enable( &SPI_LED );															// enable the DMA

	while( 1 ) {
		tx_stream[0] = 0x00;
		tx_stream[1] = 0x01;
		tx_stream[2] = 0x04;
		tx_stream[3] = 0x01;
		tx_stream[4] = 0x00;
		tx_stream[5] = 0x01;
		tx_stream[6] = 0x00;
		tx_stream[7] = 0x01;
		tx_stream[8] = 0x00;
		tx_stream[9] = 0x01;
		spi_m_dma_transfer( &SPI_LED, tx_stream, NULL, 10 );
		delay_short();
	}

	return 0;
}

Parents
  • Dear Andy,

    I setted up my code only in the main.c file i posted. I also setted up the same project in Keil and AtmelStudio using the same folders and sources.

    with both IDEs i can compile my code without errors (i use -O0 optimization), and run them on my PCBA.
    I assure you that the code compiled with AtmeStudio results in a working DMA. With Keil the DMA doesn't work.

    I can't find what are flaws in my code. Following your suggestion here there are some answers:

    1. I compiled with -O1, -O2 and -O3 in AtmelStudio: DMA works
    2. I compiled with -O1, -O2 and -O3 in Keil: DMA doesn't works, nothing flows out from SPI pins, and the DMA callback is never called.
    3. AtmelStudio linker settings are:
      -mthumb -Wl,-Map="$(OutputFileName).map"
      --specs=nano.specs -Wl,--start-group -lm  -Wl,
      --end-group -L"...\Keil_vs_AtmelStudio\Device_Startup"  -Wl,
      --gc-sections -mcpu=cortex-m0plus -Tsaml21e18b_flash.ld 
    4. Keil linker settings are:
      --cpu Cortex-M0+ *.o 
      --strict --scatter ".\Objects\LED Cube.sct" 
      --summary_stderr --info summarysizes --map --xref --callgraph --symbols 
      --info sizes --info totals --info unused --info veneers 
      --list ".\Listings\LED Cube.map" 
      -o ".\Objects\LED Cube.axf"
    5. I don't know is the startup codes are the same. How can I check it?
    6. I tried without DMA: une month ago I used the async SPI library with Keil and the SPI worked well

    I run the code compiled with both IDE and run them with Segger J-Link in debug mode. I compared the content of DMAC, MCLK, SERCOM0 and PORT registers. All of these registers bank contains the same settings. I set a breakpoint at line 65, just before the spi_m_dma_transfer(...) function call. The only difference is the BASEADDR and WRBADDR in DMAC registers:

    • in Keil project BASEADDR = 0x20000170    WRBADDR = 0x20000270
    • in AtmelStudio project BASEADDR = 0x30000000    WRBADDR = 0x30000100

    So I forced the code BASEADDR = 0x30000000    WRBADDR = 0x30000100 also in Keil but it does not work.

    About your last question: yes, of course all the code compiles without errors in both Keil and AtmelStart.
    In keil I see this warning (line 43 of hpl_dmac.c):

    #warning Please double confirm if your DMA descriptor needs specific RAM

    I looked for hints in the code but I didn't find anything about its.

     

    I hope I answered all the questions,

    thanks for your help!

Reply
  • Dear Andy,

    I setted up my code only in the main.c file i posted. I also setted up the same project in Keil and AtmelStudio using the same folders and sources.

    with both IDEs i can compile my code without errors (i use -O0 optimization), and run them on my PCBA.
    I assure you that the code compiled with AtmeStudio results in a working DMA. With Keil the DMA doesn't work.

    I can't find what are flaws in my code. Following your suggestion here there are some answers:

    1. I compiled with -O1, -O2 and -O3 in AtmelStudio: DMA works
    2. I compiled with -O1, -O2 and -O3 in Keil: DMA doesn't works, nothing flows out from SPI pins, and the DMA callback is never called.
    3. AtmelStudio linker settings are:
      -mthumb -Wl,-Map="$(OutputFileName).map"
      --specs=nano.specs -Wl,--start-group -lm  -Wl,
      --end-group -L"...\Keil_vs_AtmelStudio\Device_Startup"  -Wl,
      --gc-sections -mcpu=cortex-m0plus -Tsaml21e18b_flash.ld 
    4. Keil linker settings are:
      --cpu Cortex-M0+ *.o 
      --strict --scatter ".\Objects\LED Cube.sct" 
      --summary_stderr --info summarysizes --map --xref --callgraph --symbols 
      --info sizes --info totals --info unused --info veneers 
      --list ".\Listings\LED Cube.map" 
      -o ".\Objects\LED Cube.axf"
    5. I don't know is the startup codes are the same. How can I check it?
    6. I tried without DMA: une month ago I used the async SPI library with Keil and the SPI worked well

    I run the code compiled with both IDE and run them with Segger J-Link in debug mode. I compared the content of DMAC, MCLK, SERCOM0 and PORT registers. All of these registers bank contains the same settings. I set a breakpoint at line 65, just before the spi_m_dma_transfer(...) function call. The only difference is the BASEADDR and WRBADDR in DMAC registers:

    • in Keil project BASEADDR = 0x20000170    WRBADDR = 0x20000270
    • in AtmelStudio project BASEADDR = 0x30000000    WRBADDR = 0x30000100

    So I forced the code BASEADDR = 0x30000000    WRBADDR = 0x30000100 also in Keil but it does not work.

    About your last question: yes, of course all the code compiles without errors in both Keil and AtmelStart.
    In keil I see this warning (line 43 of hpl_dmac.c):

    #warning Please double confirm if your DMA descriptor needs specific RAM

    I looked for hints in the code but I didn't find anything about its.

     

    I hope I answered all the questions,

    thanks for your help!

Children
  • I don't know is the startup codes are the same. How can I check it?

    You have to look into them, and check that they are doing the same things

    saml21e18b_flash.ld 
    .\Objects\LED Cube.sct

    Those are the linker configuration files - you need to check if they are equivalent.

    The only difference is the BASEADDR and WRBADDR in DMAC registers:

    So look into why they are different...

    In keil I see this warning (line 43 of hpl_dmac.c):

    #warning Please double confirm if your DMA descriptor needs specific RAM

    Look into why you don't get that in Atmel Studio.

    Any specific RAM requirements would be detailed in the chip documentation

    That's the kind of thing where the manufacturer's own forum could be more help ...

  • Your chip requires DMA transfer descriptors to be in LP SRAM.  This is why you are getting the warning in Keil because it is being allocated in the "regular" SRAM.  The Atmel Studio version is placing them in "LP SRAM".  You want to make sure that your DMA transfer descriptors are properly allocated in the LP RAM.

  • Your chip requires DMA transfer descriptors to be in LP SRAM. 

    You are right. I supposed that DMA descriptors were placed in LP SRAM (I verified that in Keil project LP SRAM is IRAM2).
    Now I modified my code adding a missing #define in saml21b/include/component/dmac.h file for ARM CC compiler:

    #ifdef __GNUC__
    	#define SECTION_DMAC_DESCRIPTOR      __attribute__ ((section(".lpram")))
    #elif defined(__ICCARM__)
    	#define SECTION_DMAC_DESCRIPTOR      @".lpram"
    #elif defined(__CC_ARM)
    	#define SECTION_DMAC_DESCRIPTOR      __attribute__((section("IRAM2")))
    #endif

    then I cheched the "default" box next to IRAM2 in "Keil -> option for target -> Target"

    so I supposed that the DmacDescriptor "_descriptor_section" and "_write_back_section" variables in hpl_dmac.c were placed in IRAM2

    /* Section containing first descriptors for all DMAC channels */
    COMPILER_ALIGNED(16)
    DmacDescriptor _descriptor_section[DMAC_CH_NUM] SECTION_DMAC_DESCRIPTOR;
    
    /* Section containing current descriptors for all DMAC channels */
    COMPILER_ALIGNED(16)
    DmacDescriptor _write_back_section[DMAC_CH_NUM] SECTION_DMAC_DESCRIPTOR;

    Unfortunatly these variables are still placed in SRAM (IRAM1 in Keil) so whats wrong?

    To avoid this I setted the option "other data" in "hpl_dmac.c -> option for file -> memory assignment -> other data" to be "IRAM2". In this way the DMA is finally working, so I have a last question:

    How can I avoid to use the specific option for "hpl_dmac.c"?

    Is there something wrong with 

    	#define SECTION_DMAC_DESCRIPTOR      __attribute__((section("IRAM2")))

    Thanks for your help!