Developing OpenOCD FLASH drivers for External Memories

This tutorial shows how to develop FLASH drivers for use with the Sysprogs fork of OpenOCD and VisualGDB. FLASH drivers are responsible for automatically programming any external FLASH memories that your board is using. If you want to place some code or data in your project into an external FLASH memory and access it from your code as if it was located in the normal FLASH memory, you need to do the following:

  • Add the external memory via External Memories page of VisualGDB Project Properties
  • Actually setup the memory mapping from your startup code so that the memory contents is accessible to your program
  • Develop a FLASH driver that will automatically program the external memory when you start debugging your project

This tutorial focuses on developing the FLASH driver; more information on configuring the project can be found in this tutorial.

We will show how to develop a FLASH driver based on the QSPI FLASH of the STM32F7-Discovery board.

  1. Follow this tutorial up to the step where a FLASH Driver is configured. Instead of configuring the driver, add the following lines to the main source file and start debugging the project as is:
  2. Use the debugger to double-check that the address of g_ArrayInQSPI is actually inside the QSPI address space (>=0x90000000) and that it contains garbage:qspiaddrThe values are incorrect because OpenOCD only knows how to program the internal FLASH memory of the STM32F7 chip, but knows nothing about QSPI. After we are done developing and configuring the driver, the array will be automatically programmed at the beginning of the debug session and will contain expected values.
  3. Before you begin creating your driver project, download the code from the Sysprogs FLASH driver framework repository somewhere on your computer.
  4. Start another instance of Visual Studio and open the VisualGDB Embedded Project Wizard:01-driver
  5. The driver is just an ELF file with certain mandatory functions defined inside it, so proceed with the default “Embedded Binary” selection:02-binary
  6. Select your device from the list (in this tutorial we select STM32F746NG that is installed in the STM32F7-Discovery board). The FLASH driver should be able to run from RAM (to avoid reprogramming the internal device FLASH to load it) and should be as small as possible to minimize the load time. Select “Execute from: SRAM” and “C library type: Newlib-nano” to achieve that: 03-device
  7. Proceed with the default “LEDBlink (HAL) sample”:04-sample
  8. On the Debug Method page select OpenOCD and click “Detect” to detect your programming interface. Then click “Finish” to generate the driver project.05-debug
  9. Open VisualGDB Project Properties and add the “common” directory from the FLASH driver framework to the Include Directories:06-inc
  10. Go to the Embedded Frameworks page and add a reference to “STM32746G-Discovery drivers”. Ensure that the QSPI driver is selected below:07-qspidrv
  11. Finally add the FLASHPluginCommon.cpp file from the common directory in the FLASH driver framework to your project and create a new file called FLASHPluginConfig.h in your project directory with the following contents:

    08-filesNow you should be able to build the project. Once the build succeeds, you have all the necessary components to begin developing the actual driver.
  12. The FLASH driver is a normal ELF file compiled for your target device that must have several important functions that OpenOCD will use:
    • First of all, it should have the FLASHPlugin_Probe() function that will be called to query the size of the FLASH memory, its base address and the amount of independently erasable sectors.
    • The FLASHPlugin_FindWorkArea() function should locate the area in the SRAM that can be used to store the FLASH contents while it is being programmed.
    • The FLASHPlugin_EraseSectors() function should erase a given range of sectors
    • The FLASHPlugin_DoProgramSync() function will be responsible for actually programming the FLASH memory
    • The FLASHPlugin_Unload() will be called by OpenOCD when the programming is complete and should undo any initialization done by the driver

    The FLASH driver does not have a separate initialization function. Instead it should do all initialization in main() as usual and then call FLASHPlugin_InitDone() to signalize that the initialization is complete. OpenOCD will intercept this call and begin calling functions like FLASHPlugin_EraseSectors() once the initialization completes.

  13. Add the following implementation for the FLASHPlugin_Probe() function:

    It will simply use the constants defined in the QSPI driver to provide the information about the FLASH memory to OpenOCD. Note that the base address comes from the base argument and will be specified when configuring the project.
  14. The FLASHPlugin_FindWorkArea() should simply return 4 kilobytes after the end of stack (the stack will be managed by OpenOCD and won’t be set to the end of RAM as usual):
  15. The FLASHPlugin_EraseSectors() function should simply call BSP_QSPI_Erase_Block() for each erased sector:
  16. FLASHPlugin_DoProgramSync() should be just a wrapper to BSP_QSPI_Write():
  17. FLASHPlugin_Unload() will call the regular uninitialization functions and will explicitly disable all interrupts including the SysTick interrupt so that no interrupt handler gets called until the actual program that is being loaded is ready:
  18. Finally the main() function will do the necessary initialization and call FLASHPlugin_InitDone() when ready:

    Note that FLASHPlugin_InitDone() will actually never get called as OpenOCD will intercept the call. The default implementation simply makes sure that all other plugin functions appear referenced by the code and hence won’t be discarded by the linker.
  19. Before trying the driver with the actual project, you can quickly step through its code in the debugger to see how it will work. The framework provides a function called TestFLASHProgramming() that simplifies that task. Simply call it from main as follows:
  20. Last thing that needs to be done before we can try out the driver is manually set the initial stack pointer via VisualGDB Project Properties. Add “set $sp=&_estack” to gdb startup commands:09-estack
  21. Now you can press F5 to start debugging and step through TestFLASHProgramming() to ensure that your functions return the expected values:10-info
  22. TestFLASHProgramming() will program the first page of your flash with a repeating value of 0x55. You can verify that by running your first project again and seeing that the array placed in QSPI is now initialized with 0x55555555:11-pattern
  23. Go back to your main project. As VisualGDB 5.1 does not yet support automatic specification of the OpenOCD FLASH drivers, so open VisualGDB Project Properties, switch OpenOCD to manual mode and add the following line to additional command-line arguments:

    The “-c” part tells OpenOCD to load the QSPIDriver.elf plugin and call its probe() function with base=0x90000000 to find out the location and size of the QSPI memory. The “init” commands ensure that the initialization is performed in the correct order. Ensure you uncheck the “Issue a reset explicitly” checkbox as otherwise the init/reset init will be called before configuring the FLASH bank.12-dbgsetup
  24. Set a breakpoint at the call to FunctionInQSPIFLASH() and press F5 to start debugging. Observe how the contents of the g_ArrayInQSPI now matches the expected values:13-values
  25. You can now press F5 to continue into FunctionInQSPIFLASH() and observe how the LED blinking code contained in it will start running causing the LED to blink.