When working with external RAM on an ARM Cortex-M chip, it is crucial to map the external memory regions correctly using scatter load files. Scatter files provide a flexible and convenient way to define the memory layout for your Cortex-M application, allowing you to specify where code and data sections should be placed in physical memory. Using scatter files improperly can result in hard-to-debug memory issues, so understanding the syntax and usage is important.
Overview of External RAM on Cortex-M
The Cortex-Mfamily of ARM processors are designed for embedded applications with tight memory constraints. However, many advanced applications require more RAM capacity than available on-chip. External RAM integrated circuits can be added to provide this extra memory, communicating with the Cortex-M chip via an external bus interface like AHB or AXI.
To utilize external RAM, the memory must be configured correctly via the Memory Protection Unit (MPU). The MPU allows setting access permissions for memory regions, splitting external RAM into multiple blocks if needed. Special care must be taken with timing, bus waits states, and bus arbitration to ensure reliable operation.
Using Scatter Files to Define External RAM Layout
Scatter files provide a flexible method for defining how code and data sections should be arranged in physical memory. The linker will allocate sections into memory regions according to the scatter file when generating the final executable binary.
For external RAM, the scatter file must define one or more regions that cover the full external address space provided. As an example: LR_IROM1 0x00000000 0x00080000 { ; load region size_region ER_IROM1 0x00000000 0x00080000 { ; load address = execution address *.o (RESET, +First) *(InRoot$$Sections) .ANY (+RO) } ; External RAM region LR_ExtRAM 0x20000000 0x00200000 { ER_ExtRAM 0x20000000 0x00200000 { .ANY (+RW +ZI) } } }
This defines a 512KB external RAM region from 0x20000000 to 0x20020000. The .ANY directive will place all RW and ZI sections within this memory.
Placing Code and Data in External RAM
By default, the linker will try to place all code and data into internal memory first. To move specific sections into external RAM, you must use directives.
For example, to move the .text section to external RAM: SECTIONS { .text : { *(.text) } > ER_ExtRAM }
And to move the .bss section: SECTIONS { .bss : { *(.bss) } > ER_ExtRAM }
For placing larger data arrays, the linker also supports putting variables at specific addresses using the AT attribute: int large_array[8192] __attribute__((at(0x20020000)));
This syntax lets you manually place data within the external RAM region as needed.
Avoiding Memory Overlaps and Fragmentation
When defining your external RAM regions in the scatter file, it is critical to get the addresses and sizes correct to avoid overlaps or fragmentation.
Overlapping regions will lead to hard-to-debug memory corruption issues. The linker also requires regions to be defined sequentially with no gaps in between. Otherwise, the unused fragmented memory cannot be utilized.
Make sure your external RAM chip’s datasheet precisely matches the start address and capacity specified in the scatter file. Double check that multiple RAM regions don’t accidentally overlap.
Also beware of fragmentation. For example, defining two 256KB external RAM regions from 0x20000000-0x200FFFFF and 0x20400000-0x20500000 would lose access to the unused 128KB gap, wasting memory.
Adding External RAM Access Rules with MPU
In addition to the scatter file, the MPU configuration must allow access to external RAM address ranges. The MPU allows setting read/write/execute permissions for each defined region.
As an example, enabling read-write access to the 0x20000000-0x2002FFFF external RAM range: MPU->RBAR = 0x20000000; MPU->RASR = 0x00000003; //full access
Failing to configure the MPU properly will lead to hard faults when the Cortex-M processor tries to access external memory. Setting the correct boundaries and access permissions is vital.
Adding Wait States for Reliable Operation
External RAM chips often operate at slower speeds than the processor’s bus interface. Wait states must be inserted to avoid outrunning the external RAM access times and causing incorrect behavior.
For example, adding 2 wait states to an FMC external memory controller peripheral: FMC_Bank->BTCR[0] = 2 //insert 2 wait states
The required number of wait states depends on the RAM chip’s datasheet and the Cortex-M processor’s bus speed. Too few wait states will lead to unreliability.
Verifying Successful External RAM Access
After defining the scatter file and MPU regions, test carefully to validate correct external RAM access. Simple test code can write values and read them back to check for consistency.
Memory scope tools and debuggers that can access external memory areas are also invaluable for more robust testing. Watch memory locations to verify writes are updating properly. Check that different data arrays do not overlap or interfere.
Going through methodical external RAM validation will help identify any bugs in the memory configuration early, saving lots of debug time down the road.
Key Takeaways
Here are some key tips for correctly utilizing external RAM on an ARM Cortex-M processor:
- Use scatter files to define external memory regions and place code/data
- Avoid overlaps between regions to prevent memory corruption
- Configure MPU to enable read/write access to external RAM
- Add sufficient wait states for reliable external RAM access
- Verify successful operation by testing reads and writes
Following these steps carefully when integrating external RAM will help avoid confusing memory errors and save much debugging effort. External memory opens up many possibilities for on-chip RAM limited Cortex-M devices. Using the right configuration approach makes utilizing large external memories simple and effective.