cancel
Showing results for 
Search instead for 
Did you mean: 

STM32 Memory-to-Memory DMA transfer to 16-bit SRAM

dohzer
Associate II
Posted on November 03, 2014 at 10:17

I have an STM32F429 connected via FMC to a 16-bit wide SRAM. I can read and write to the SRAM correctly using simple memory writes.

DMA works correctly too when I configure it with two streams; one to send internal memory words to the half-word memory, and a second stream to read half-words to back to the internal memory.

Even though it works, I'm confused about whether I'm configuring my DMA correctly, and a couple of answers would help greatly:

  1. Do I need to specify in the DMA that the memory transfers are from word to half-word, and vice versa? Or does the FMC automatically handle the data-width translation? i.e. can I use DMA with word-transfers even though the SRAM is 16-bit?
  2. Is there any advantage to using FIFO mode with memory-to-memory transfers?
  3. Memory-to-Memory transfers do not allow Direct Mode to be used. I can see the FIFO being enabled by hardware (DMDIS = 1). But I'm not configuring BURST sizes and it seems to work fine. Is all this performed by hardware?

#dma #fifo #stm32 #mem-to-mem
5 REPLIES 5
Posted on November 03, 2014 at 10:54

> Do I need to specify in the DMA that the memory transfers are from word to half-word, and

> vice versa? Or does the FMC automatically handle the data-width translation? i.e. can I use

> DMA with word-transfers even though the SRAM is 16-bit?

Read RM0090 rev.7 ch.37.3.1:

''AHB transaction size is greater than the memory size:

In this case, the FMC splits the AHB transaction into smaller consecutive memory

accesses to meet the external data width. ''

> Is there any advantage to using FIFO mode with memory-to-memory transfers?

You answered this in your next question..

>Memory-to-Memory transfers do not allow Direct Mode to be used. I can see the FIFO being enabled by hardware (DMDIS = 1).

In other words, FIFO is used automatically when you use M-to-M.

> But I'm not configuring BURST sizes and it seems to work fine. Is all this performed by hardware?

No. Bursts are used only if you enable them (set to non-single-transfer), but they are not necessary.

JW
dohzer
Associate II
Posted on November 04, 2014 at 05:40

So I just changed my code to use word (32-bit) transactions. Previously I had word on internal memory and half-word on my SRAM external memory.

When I write the memory with one 128-word DMA transfer, and then read it back with another DMA transfer, the memory is not as expected.

The data read back is correct for the most significant 16-bits, but the least significant 16-bits are read as the same as the most-significant.

Here's my initialisation for the DMA:

DMA_HandleTypeDef dmaHandleMem2Sram;

DMA_HandleTypeDef dmaHandleSram2Mem;

void dmaInit(void)

{

//Memory to SRAM (DMA2 Stream 0, high priority)

//Memory = Source = ''Peripheral''

//SRAM = Destination = ''Memory''

dmaHandleMem2Sram.Init.Channel = DMA_CHANNEL_0; //Channel 0

dmaHandleMem2Sram.Init.Direction = DMA_MEMORY_TO_MEMORY; //Memory-to-memory mode

dmaHandleMem2Sram.Init.PeriphInc = DMA_PINC_ENABLE; //Peripheral increment mode enabled

dmaHandleMem2Sram.Init.MemInc = DMA_MINC_ENABLE; //Memory increment mode enabled

dmaHandleMem2Sram.Init.PeriphDataAlignment = DMA_PDATAALIGN_WORD; //Peripheral data alignment: Word (source)

dmaHandleMem2Sram.Init.MemDataAlignment = DMA_MDATAALIGN_WORD; //Memory data alignment: Word (destination)

dmaHandleMem2Sram.Init.Mode = DMA_NORMAL; //Normal DMA mode (not circular or peripheral control modes)

dmaHandleMem2Sram.Init.Priority = DMA_PRIORITY_HIGH; //Priority level: High

dmaHandleMem2Sram.Init.FIFOMode = DMA_FIFOMODE_DISABLE; //FIFO mode disabled

dmaHandleMem2Sram.Init.FIFOThreshold = DMA_FIFO_THRESHOLD_FULL; //FIFO threshold: Full

dmaHandleMem2Sram.Init.MemBurst = DMA_MBURST_SINGLE; //Memory burst: Single

dmaHandleMem2Sram.Init.PeriphBurst = DMA_PBURST_SINGLE; //Peripheral burst: Single

//Select the DMA instance to be used for the transfer : DMA2_Stream0

dmaHandleMem2Sram.Instance = DMA2_Stream0;

//Select Call-backs functions called after Transfer complete and Transfer error

dmaHandleMem2Sram.XferCpltCallback  = dmaComplete;

dmaHandleMem2Sram.XferErrorCallback = dmaError;

//Initialize the DMA stream

if(HAL_DMA_Init(&dmaHandleMem2Sram) != HAL_OK)

{

Error_Handler(); //Initialization Error

}

//Configure NVIC for DMA transfer complete/error interrupts

HAL_NVIC_SetPriority(DMA2_Stream0_IRQn, 0, 0);

HAL_NVIC_EnableIRQ(DMA2_Stream0_IRQn);

//SRAM to Memory (DMA2 Stream 1, medium priority)

//SRAM = Source = Peripheral

//Memory = Destination = Memory

//Select the DMA functional Parameters

dmaHandleSram2Mem.Init.Channel = DMA_CHANNEL_0; //Channel 0

dmaHandleSram2Mem.Init.Direction = DMA_MEMORY_TO_MEMORY; //Memory-to-memory mode

dmaHandleSram2Mem.Init.PeriphInc = DMA_PINC_ENABLE; //Peripheral increment mode enabled

dmaHandleSram2Mem.Init.MemInc = DMA_MINC_ENABLE; //Memory increment mode enabled

dmaHandleSram2Mem.Init.PeriphDataAlignment = DMA_PDATAALIGN_WORD; //Peripheral data alignment: Word (source)

dmaHandleSram2Mem.Init.MemDataAlignment = DMA_MDATAALIGN_WORD; //Memory data alignment: Word (destination)

dmaHandleSram2Mem.Init.Mode = DMA_NORMAL; //Normal DMA mode (not circular or peripheral control modes)

dmaHandleSram2Mem.Init.Priority = DMA_PRIORITY_MEDIUM; //Priority level: Medium ***NOTE: Lower Priority than Stream0***

dmaHandleSram2Mem.Init.FIFOMode = DMA_FIFOMODE_DISABLE; //FIFO mode disabled

dmaHandleSram2Mem.Init.FIFOThreshold = DMA_FIFO_THRESHOLD_FULL; //FIFO threshold: Full

dmaHandleSram2Mem.Init.MemBurst = DMA_MBURST_SINGLE; //Memory burst: Single

dmaHandleSram2Mem.Init.PeriphBurst = DMA_PBURST_SINGLE; //Peripheral burst: Single

//Select the DMA instance to be used for the transfer : DMA2_Stream1

dmaHandleSram2Mem.Instance = DMA2_Stream1;

//Select Call-backs functions called after Transfer complete and Transfer error

dmaHandleSram2Mem.XferCpltCallback  = dmaComplete;

dmaHandleSram2Mem.XferErrorCallback = dmaError;

//Initialize the DMA stream

if(HAL_DMA_Init(&dmaHandleSram2Mem) != HAL_OK)

{

Error_Handler(); //Initialization Error

}

//Configure NVIC for DMA transfer complete/error interrupts

HAL_NVIC_SetPriority(DMA2_Stream1_IRQn, 1, 0);

HAL_NVIC_EnableIRQ(DMA2_Stream1_IRQn);

}

void dmaTransferMem2Sram(uint32_t *src, uint32_t *dst, uint32_t size)

{

if(HAL_DMA_Start_IT(&dmaHandleMem2Sram, (uint32_t)src, (uint32_t)dst, size) != HAL_OK)

{

Error_Handler(); //Initialization Error

}

}

void dmaTransferSram2Mem(uint32_t *src, uint32_t *dst, uint32_t size)

{

if(HAL_DMA_Start_IT(&dmaHandleSram2Mem, (uint32_t)src, (uint32_t)dst, size) != HAL_OK)

{

Error_Handler(); //Initialization Error

}

}

And here's the FMC.

void fmcSramInit(void)

{

SRAM_HandleTypeDef hsram;

FMC_NORSRAM_TimingTypeDef SRAM_Timing;

//SRAM Configuration

hsram.Instance  = FMC_NORSRAM_DEVICE;

hsram.Extended  = FMC_NORSRAM_EXTENDED_DEVICE;

SRAM_Timing.AddressSetupTime       = 1; //Don't care

SRAM_Timing.AddressHoldTime        = 1; //Don't care

SRAM_Timing.DataSetupTime          = 1; //Don't care

SRAM_Timing.BusTurnAroundDuration  = 0;

SRAM_Timing.CLKDivision            = 2; //Must be greater than 1

SRAM_Timing.DataLatency            = 0; //Must be '0' for CRAM (PSRAM)

SRAM_Timing.AccessMode             = FMC_ACCESS_MODE_A; //Not used since we are synchronous

hsram.Init.NSBank = FMC_NORSRAM_BANK1; //Use bank 1 since we only have access to the NE1 signal

hsram.Init.DataAddressMux = FMC_DATA_ADDRESS_MUX_DISABLE; //Disable multiplex

hsram.Init.MemoryType = FMC_MEMORY_TYPE_PSRAM; //PSRAM allowed CLK

hsram.Init.MemoryDataWidth = FMC_NORSRAM_MEM_BUS_WIDTH_16;

hsram.Init.BurstAccessMode = FMC_BURST_ACCESS_MODE_ENABLE;

hsram.Init.WaitSignalPolarity = FMC_WAIT_SIGNAL_POLARITY_HIGH; //NOTE: With synchronous mode and NWAIT disabled, make this HIGH with NWAIT disconnected from AF12 GPIO

hsram.Init.WrapMode = FMC_WRAP_MODE_DISABLE;

hsram.Init.WaitSignalActive = FMC_WAIT_TIMING_BEFORE_WS;

hsram.Init.WriteOperation = FMC_WRITE_OPERATION_ENABLE;

hsram.Init.WaitSignal = FMC_WAIT_SIGNAL_DISABLE;

hsram.Init.ExtendedMode = FMC_EXTENDED_MODE_DISABLE; //Disable extended mode

hsram.Init.AsynchronousWait = FMC_ASYNCHRONOUS_WAIT_DISABLE;

hsram.Init.WriteBurst = FMC_WRITE_BURST_ENABLE;

hsram.Init.ContinuousClock = FMC_CONTINUOUS_CLOCK_SYNC_ONLY; //Unsure which this should be, but will leave it as only synchronous, 24/09/2014

//Enable FMC clock

__FMC_CLK_ENABLE();

//Initialize the SRAM controller

if(HAL_SRAM_Init(&hsram, &SRAM_Timing, &SRAM_Timing) != HAL_OK)

{

//Initialization Error

Error_Handler();

}

}

And these are the calls to DMA:

#define SRAM_BANK_ADDR ((uint32_t)0x60000000)

#define BUF_SZ ((uint32_t)0x80)

__attribute__((aligned(64))) uint32_t randArray[BUF_SZ];

dmaTransferMem2Sram(randArray, (uint32_t *)SRAM_BANK_ADDR, BUF_SZ);

**CODE INBETWEEN TO WAIT FOR PREVIOUS TRANSFER TO FINISH**

dmaTransferSram2Mem((uint32_t *)SRAM_BANK_ADDR, randArray, BUF_SZ);

I've attached an image of the memory I see, before writing (on the left) and after reading back from the memory (on the right)

0690X00000605BhQAI.png
Posted on November 04, 2014 at 09:18

Could you read out the external memory after the first transfer?

Could you clear the internal memory between the transfers? Any change in the readback values?

Can you perform the transfer using the processor, first 16-bit, then 32-bit?

JW

dohzer
Associate II
Posted on November 06, 2014 at 05:20

Hi waclawek.jan,

Last night I did some simple read/write tests without DMA. I can only get them to work when performing 16-bit transactions. 32-bit transactions have the same problem the DMA had, so I don't think it's so much a DMA problem as it is a problem with the FMC or a related area.

Hopefully I'll have some time tonight and I'll try to compare the STM32F429I-Discovery's 16-bit external memory software example with my interface. It looks like their example should work, the only difference being the type of external memory.

dohzer
Associate II
Posted on November 21, 2014 at 01:51

So I've solved the problem, and it turned out to be a combination of things:

  1. A sneaky, intermittent solder fault on one of the SRAM data lines. I had tested them all to rule this problem out, but it kept showing up, probably every time bumped the board, or when I flipped the board to check soldering.
  2. Incorrect memory timings. I had them configured as per the datasheet, but my circuit layout was causing some timing delays, etc, and so I have to run the memory bus a bit slower than normal. I had tried running them as slow as possible, but the solder problem listed above made me think it wasn't working, so I started chasing ghosts in the code.
  3. Just to be sure, I added some code to explicitly disable the SDRAM chip to avoid clashes with the SRAM (although this causing any problems I had seen).
  4. I got rid of the FMC clock output (that wasn't necessary for SRAM, but will be needed for FPGA block RAM communications when I get to that point) and reconfigured for a minimal SRAM setup.

Everything is working fine now with the FMC and DMA configuration!