Adding External Memories to Embedded Projects

This tutorial shows how to add support for an external off-chip memory, such as a NOR FLASH to a VisualGDB Embedded Project, place some code and data into it and configure VisualGDB to program it automatically. In this example we will use the QSPI memory on the STM32F7-Discovery board. We will map it to the microcontroller’s address space and show how to offload some code and data there to reduce the utilization of the on-chip FLASH memory.

Before you begin, install VisualGDB 5.1 or later.

  1. Start Visual Studio and open the VisualGDB Embedded Project Wizard:01-name
  2. Proceed with the default “Embedded Binary” setting, but uncheck the “bin” checkbox, as trying to fit the contents of memories located far from each other would result in a very big binary file:02-binary
  3. Select your device from the list. In this example we will use the STM32F746NG microcontroller that is installed on the STM32F7-Discovery board:03-device
  4. Select the “LEDBlink (HAL)” sample and select the LED group and port that corresponds to your board layout. For STM32F7-Discovery we select GPIO1 and port 1:04-sample
  5. Select the debugging method that works with your device. For most of the devices we recommend using OpenOCD. Connect your board (and external programmer if any) to USB and click “Detect” to automatically detect the USB programmer type: 05-stlink
  6. Click “Finish” to generate the basic project. Then add the QSPI FLASH driver file (QSPIRoutines.cpp) to your project.06-spifile
  7. The QSPIRoutines.cpp file contains QSPI FLASH setup functions copied from the ST’s QSPI_ExecuteInPlace example. If you are using a different board, you may need to adjust those functions. You can use the Code Map feature provided by VisualGDB’s Clang-based IntelliSense to get a quick overview of the QSPI-related functions and relations between them:
    • QSPI_EnableMemoryMappedMode() is the main function that maps the QSPI FLASH to address 0x90000000
    • Functions like QSPI_DummyCyclesCfg() and QSPI_WriteEnable() configure the external FLASH memory chip by writing chip-specific commands
    • Functions like HAP_QSPI_Transmit() are provided by the STM32 HAL library and are responsible for delivering the commands to the chip07-codemap
  8. Move the code responsible for the blinking LED to a function called FunctionInQSPIFLASH() and edit the main source file to call the QSPI_EnableMemoryMappedMode() function before calling the LED blinking function:

    08-FLASHNote that we have NOT placed the function to the QSPI FLASH yet, so the function will reside in normal FLASH memory. Build the project and note the FLASH memory utilization.
  9. Set a breakpoint at the call to FunctionInQSPIFLASH() and hit F5 to start debugging. Once the breakpoint is hit verify that the address of FunctionInQSPIFLASH is still inside the main FLASH area (0x08xxxxxx) and that the memory at address 0x90000000 is readable (it should show contents of the actual QSPI FLASH memory programmed by any previous QSPI-based program that ran on the board):09-addr
  10. Now we will actually configure the project to use the QSPI memory. Open VisualGDB Project Properties and go to the Additional Memories page. Add a new memory called QSPI at address 0x90000000. In this example we will limit the memory size to 64K (0x10000), however you can specify the actual QSPI FLASH chip size as well:10-extmemNote that the “Additional Memories” feature is supported on VisualGDB Custom Edition and higher.
  11. Press OK to apply the settings. Note how VisualGDB has created a copy of the linker script file and inserted the memory definition lines there. When you edit the external memory settings, VisualGDB will replace the lines between “— begin generated xxx —” and “— end generated xxx –” lines, preserving the rest of the linker script:    11-ldsfile
  12. VisualGDB as also added an ExtraMemories.h file that defines macros for placing your functions and variables to the external memories: 12-memfile
  13. Include the “ExtraMemories.h” file from your main file and add the QSPI_TEXT macro to the declaration of FunctionInQSPIFLASH(). Then build your project:12-qspisizeNote how the used FLASH size has reduced and a new memory called QSPI got added to the list:
  14. If you try to start debugging your project now, you will see that although the main FLASH memory expects the FunctionInQSPIFLASH() to be at address 0x90000000, the actual data at this address is different. This happens because OpenOCD does not know how to program the QSPI FLASH. This can be fixed by downloading and building the QSPI FLASH plugin for OpenOCD as described in this tutorial and configuring OpenOCD to use it to program QSPI:12-dbgsetup
  15. Now you can simply press F5 to automatically program the chip and begin debugging it. Note how the address of FunctionInQSPIFLASH() has changed to 0x90000000 and the memory contents there shows the function body:14-spifunc
  16. If you continue running your program now, you should see the LED blinking just as it did before you moved the function to QSPI FLASH. Note that due to the limitations of the STM32 memory mapping logic you may not be able to set breakpoints in the code running from QSPI FLASH. Stepping through it and viewing local variables should work however. If you cannot step into a function in the QSPI FLASH, try switching to the Disassembly view and stepping one instruction at a time.
  17. Now we will show how to place a pre-initialized array into the QSPI FLASH memory without increasing the utilization of the normal FLASH. Declare a static const array with the QSPI_DATA macro and change FunctionInQSPIFLASH() to use it:

    16-arrayBuild your program and observe how the QSPI utilization has increased while the FLASH and RAM utilizations were unchanged.
  18. You can launch debugging with F5 (don’t forget to re-program the QSPI memory using the ST-Link tool unless you have automated this as described before) and see that LargeArray got placed to QSPI FLASH after the FunctionInQSPIFLASH:17-arrayaddr