{"id":1927,"date":"2023-09-07T16:09:19","date_gmt":"2023-09-07T23:09:19","guid":{"rendered":"http:\/\/visualgdb.com\/w\/?p=1927"},"modified":"2023-09-08T13:57:33","modified_gmt":"2023-09-08T20:57:33","slug":"developing-openocd-flash-drivers-for-external-memories","status":"publish","type":"post","link":"https:\/\/visualgdb.com\/tutorials\/arm\/stm32\/flash\/","title":{"rendered":"Developing OpenOCD FLASH drivers for External Memories"},"content":{"rendered":"<p>This tutorial shows how to develop FLASH drivers for use with the <a href=\"http:\/\/github.com\/sysprogs\/openocd\">Sysprogs fork of OpenOCD<\/a> 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:<\/p>\n<ul>\n<li>Add the external memory via External Memories page of VisualGDB Project Properties<\/li>\n<li>Actually setup the memory mapping from your startup code so that the memory contents is accessible to your program<\/li>\n<li>Develop a FLASH driver that will automatically program the external memory when you start debugging your project<\/li>\n<\/ul>\n<p>This tutorial focuses on developing the FLASH driver; more information on configuring the project can be found in <a href=\"http:\/\/visualgdb.com\/tutorials\/arm\/memories\/\">this tutorial<\/a>.<\/p>\n<p>We will show how to develop a FLASH driver based on the QSPI FLASH of the STM32F7-Discovery board.<\/p>\n<ol>\n<li>Follow <a href=\"http:\/\/visualgdb.com\/tutorials\/arm\/memories\/\">this tutorial<\/a> 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:\n<pre class=\"\">int QSPI_DATA g_ArrayInQSPI[] = { 1, 2, 3, 4, 5 };\r\n\r\nint main()\r\n{\r\n    \/\/...\r\n    volatile int x = g_ArrayInQSPI[0];\r\n}<\/pre>\n<\/li>\n<li>Use the debugger to double-check that the address of <strong>g_ArrayInQSPI<\/strong> is actually inside the QSPI address space (&gt;=0x90000000) and that it contains garbage:<a href=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2016\/05\/01-arr.png\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-8345\" src=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2016\/05\/01-arr.png\" alt=\"\" width=\"1121\" height=\"742\" \/><\/a>The 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.<\/li>\n<li>Before you begin creating your driver project, download the code from the <a href=\"https:\/\/github.com\/sysprogs\/flash_drivers\">Sysprogs FLASH driver framework repository<\/a> somewhere on your computer.<\/li>\n<li>Start another instance of Visual Studio and open the VisualGDB Embedded Project Wizard:<a href=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2016\/05\/02-drv.png\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-8346\" src=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2016\/05\/02-drv.png\" alt=\"\" width=\"890\" height=\"625\" \/><\/a><\/li>\n<li>The driver is just an ELF file with certain mandatory functions defined inside it, so proceed with the default &#8220;Embedded Application&#8221; selection using Advanced CMake:<a href=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2016\/05\/03-cmake.png\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-8347\" src=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2016\/05\/03-cmake.png\" alt=\"\" width=\"886\" height=\"693\" \/><\/a><\/li>\n<li>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 &#8220;Execute from: SRAM&#8221; and &#8220;C library type: Newlib-nano&#8221; to achieve that:<a href=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2016\/05\/04-sram-1.png\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-8349\" src=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2016\/05\/04-sram-1.png\" alt=\"\" width=\"886\" height=\"693\" \/><\/a><\/li>\n<li>Proceed with the default &#8220;LEDBlink (HAL) sample&#8221;:<a href=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2016\/05\/05-blink.png\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-8350\" src=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2016\/05\/05-blink.png\" alt=\"\" width=\"886\" height=\"693\" \/><\/a><\/li>\n<li>On the Debug Method page select allow VisualGDB to automatically detect your ST-Link interface and configure OpenOCD:<a href=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2016\/05\/06-oocd.png\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-8351\" src=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2016\/05\/06-oocd.png\" alt=\"\" width=\"886\" height=\"693\" \/><\/a><\/li>\n<li>Right-click on the top-level project node in Solution Explorer (with the triangular CMake icon) and select &#8220;<strong>Add-&gt;Reference another CMake folder<\/strong>&#8220;:<a href=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2016\/05\/07-ref.png\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-8352\" src=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2016\/05\/07-ref.png\" alt=\"\" width=\"1121\" height=\"742\" \/><\/a>Then point VisualGDB to the <strong>&lt;FLASH drivers repository&gt;\\common<\/strong> directory.<\/li>\n<li>VisualGDB will add an &#8220;<strong>add_subdirectory()<\/strong>&#8221; statement to top-level CMakeLists.txt. If the repository is located outside the project directory, CMake will show a configuration error unless you specify the binary directory name as shown below: <a href=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2016\/05\/08-bin.png\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-8353\" src=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2016\/05\/08-bin.png\" alt=\"\" width=\"1121\" height=\"742\" \/><\/a><\/li>\n<li>Save the CMakeLists.txt file to allow VisualGDB to reload the project. Then right-click on the application node in Solution Explorer (console icon) and select &#8220;<strong>Add-&gt;Add Reference<\/strong>&#8220;:<a href=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2016\/05\/09-addref.png\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-8354\" src=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2016\/05\/09-addref.png\" alt=\"\" width=\"1121\" height=\"742\" \/><\/a><\/li>\n<li>Add a reference to the <strong>FLASHPlugin<\/strong> target:<a href=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2016\/05\/plugin.png\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-8355\" src=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2016\/05\/plugin.png\" alt=\"\" width=\"490\" height=\"200\" \/><\/a><\/li>\n<li>Go to the <strong>Embedded Frameworks<\/strong> page of VisualGDB Project Properties and add a reference to &#8220;STM32746G-Discovery drivers&#8221;. Ensure that the QSPI driver is selected below:<a href=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2016\/05\/10-qspi.png\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-8356\" src=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2016\/05\/10-qspi.png\" alt=\"\" width=\"919\" height=\"605\" \/><\/a><\/li>\n<li>Create a new file called <strong>FLASHPluginConfig.h<\/strong> in your project directory with the following contents:\n<pre class=\"\">#pragma once\r\n\r\n#include &lt;stm32f7xx_hal.h&gt;\r\n#include &lt;stm32746g_discovery_qspi.h&gt;\r\n\r\n#define MINIMUM_PROGRAMMED_BLOCK_SIZE N25Q128A_PAGE_SIZE\r\n#define FLASH_PLUGIN_SUPPORT_ASYNC_PROGRAMMING 1<\/pre>\n<p><a href=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2016\/05\/11-config.png\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-8357\" src=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2016\/05\/11-config.png\" alt=\"\" width=\"1121\" height=\"742\" \/><\/a>Now you should be able to build the project. Once the build succeeds, you have all the necessary components to begin developing the actual driver.<\/li>\n<li>The FLASH driver is a normal ELF file compiled for your target device that must have several important functions that OpenOCD will use:\n<ul>\n<li>First of all, it should have the <strong>FLASHPlugin_Probe()<\/strong> function that will be called to query the size of the FLASH memory, its base address and the amount of independently erasable sectors.<\/li>\n<li>The <strong>FLASHPlugin_FindWorkArea()<\/strong> function should locate the area in the SRAM that can be used to store the FLASH contents while it is being programmed.<\/li>\n<li>The <strong>FLASHPlugin_EraseSectors()<\/strong> function should erase a given range of sectors<\/li>\n<li>The <strong>FLASHPlugin_DoProgramSync()<\/strong> function will be responsible for actually programming the FLASH memory<\/li>\n<li>The <strong>FLASHPlugin_Unload()<\/strong> will be called by OpenOCD when the programming is complete and should undo any initialization done by the driver<\/li>\n<\/ul>\n<p>The FLASH driver does not have a separate initialization function. Instead it should do all initialization in main() as usual and then call <strong>FLASHPlugin_InitDone()<\/strong> to signalize that the initialization is complete. OpenOCD will intercept this call and begin calling functions like <strong>FLASHPlugin_EraseSectors() <\/strong>once the initialization completes.<\/li>\n<li>Add the following implementation for the <strong>FLASHPlugin_Probe()<\/strong> function:\n<pre class=\"\">FLASHBankInfo FLASHPlugin_Probe(unsigned base, unsigned size, unsigned width1, unsigned width2)\r\n{\r\n\u00a0\u00a0 \u00a0InterruptEnabler enabler;\r\n\u00a0\u00a0 \u00a0\r\n\u00a0\u00a0 \u00a0FLASHBankInfo result = {\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0.BaseAddress = base, \r\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0.BlockCount = N25Q128A_FLASH_SIZE \/ N25Q128A_SUBSECTOR_SIZE, \r\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0.BlockSize = N25Q128A_SUBSECTOR_SIZE,\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0.WriteBlockSize = MINIMUM_PROGRAMMED_BLOCK_SIZE\r\n\u00a0\u00a0 \u00a0};\r\n\u00a0\u00a0 \u00a0return result;\r\n}<\/pre>\n<p>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 <strong>base<\/strong> argument and will be specified when configuring the project.<\/li>\n<li>The <strong>FLASHPlugin_FindWorkArea()<\/strong> should simply return 4 kilobytes after the end of stack (the stack will be managed by OpenOCD and won&#8217;t be set to the end of RAM as usual):\n<pre class=\"\">WorkAreaInfo FLASHPlugin_FindWorkArea(void *endOfStack)\r\n{\r\n\u00a0\u00a0 \u00a0InterruptEnabler enabler;\r\n\u00a0\u00a0 \u00a0\r\n\u00a0\u00a0 \u00a0WorkAreaInfo info = { .Address = endOfStack, .Size = 4096 };\r\n\u00a0\u00a0 \u00a0return info;\r\n}<\/pre>\n<\/li>\n<li><strong>The FLASHPlugin_EraseSectors()<\/strong> function should simply call <strong>BSP_QSPI_Erase_Block()<\/strong> for each erased sector:\n<pre class=\"\">int FLASHPlugin_EraseSectors(unsigned firstSector, unsigned sectorCount)\r\n{\r\n\u00a0\u00a0 \u00a0InterruptEnabler enabler;\r\n\u00a0\u00a0 \u00a0\r\n\u00a0\u00a0 \u00a0for (unsigned i = 0; i &lt; sectorCount; i++)\r\n\u00a0\u00a0 \u00a0{\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0uint8_t error = BSP_QSPI_Erase_Block((firstSector + i) * N25Q128A_SUBSECTOR_SIZE);\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0if (error != QSPI_OK)\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0return -1;\r\n\u00a0\u00a0 \u00a0}\r\n\u00a0\u00a0 \u00a0return sectorCount;\r\n}<\/pre>\n<\/li>\n<li><strong>FLASHPlugin_DoProgramSync()<\/strong> should be just a wrapper to <strong>BSP_QSPI_Write()<\/strong>:\n<pre class=\"\">int FLASHPlugin_DoProgramSync(unsigned startOffset, const void *pData, int bytesToWrite)\r\n{\r\n\u00a0\u00a0 \u00a0uint8_t result = BSP_QSPI_Write((uint8_t *)pData, startOffset, bytesToWrite);\r\n\u00a0\u00a0 \u00a0if (result != QSPI_OK)\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0return 0;\r\n\u00a0\u00a0 \u00a0return bytesToWrite;\r\n}<\/pre>\n<\/li>\n<li><strong>FLASHPlugin_Unload()<\/strong> 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:\n<pre class=\"\">int FLASHPlugin_Unload()\r\n{\r\n\u00a0\u00a0 \u00a0BSP_QSPI_DeInit();\r\n\u00a0\u00a0 \u00a0HAL_DeInit();\r\n\u00a0\u00a0 \u00a0SysTick-&gt;CTRL &amp;= ~SysTick_CTRL_ENABLE_Msk;\r\n\u00a0\u00a0 \u00a0\r\n\u00a0\u00a0 \u00a0for (int i = 0; i &lt; sizeof(NVIC-&gt;ICER) \/ sizeof(NVIC-&gt;ICER[0]); i++)\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0NVIC-&gt;ICER[0] = -1;\r\n\u00a0\u00a0 \u00a0\r\n\u00a0\u00a0 \u00a0return 0;\r\n}<\/pre>\n<\/li>\n<li>Finally the main() function will do the necessary initialization and call FLASHPlugin_InitDone() when ready:\n<pre class=\"\">int main(void)\r\n{\r\n\u00a0\u00a0 \u00a0extern void *g_pfnVectors;\r\n\u00a0\u00a0 \u00a0\r\n\u00a0\u00a0 \u00a0SCB-&gt;VTOR = (uint32_t)&amp;g_pfnVectors;\r\n\u00a0\u00a0 \u00a0HAL_Init();\r\n\u00a0\u00a0 \u00a0BSP_QSPI_Init();\r\n\u00a0\u00a0 \u00a0FLASHPlugin_InitDone();\r\n\u00a0\u00a0 \u00a0\r\n\u00a0\u00a0 \u00a0for (;;)\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0;\r\n}<\/pre>\n<p>Note that <strong>FLASHPlugin_InitDone()<\/strong> 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&#8217;t be discarded by the linker.<\/li>\n<li>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 <strong>TestFLASHProgramming()<\/strong> that simplifies that task. Simply call it from main as follows:\n<pre class=\"\">TestFLASHProgramming(0x90000000, 0);<\/pre>\n<\/li>\n<li>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 &#8220;<strong>set $sp=&amp;_estack<\/strong>&#8221; to gdb startup commands:<a href=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2016\/05\/12-estack.png\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-8358\" src=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2016\/05\/12-estack.png\" alt=\"\" width=\"919\" height=\"605\" \/><\/a><\/li>\n<li>Now you can press F5 to start debugging and step through <strong>TestFLASHProgramming()<\/strong> to ensure that your functions return the expected values:<a href=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2016\/05\/13-probe.png\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-8359\" src=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2016\/05\/13-probe.png\" alt=\"\" width=\"1121\" height=\"742\" \/><\/a><\/li>\n<li><strong>TestFLASHProgramming()<\/strong> 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 <strong>0x55555555<\/strong>:<a href=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2016\/05\/filled.png\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-8360\" src=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2016\/05\/filled.png\" alt=\"\" width=\"1185\" height=\"816\" \/><\/a><\/li>\n<li>Go back to your main project. Open VisualGDB Project Properties, expand the Advanced View and add the following line before the &#8220;-c init&#8221; command:\n<pre class=\"\">-c \"flash bank qspi plugin 0x90000000 0 0 0 0 &lt;path to QSPIDriver.elf&gt;\"<\/pre>\n<p>The &#8220;-c&#8221; part tells OpenOCD to load the <strong>QSPIDriver.elf<\/strong> plugin and call its <strong>probe()<\/strong> function with base=0x90000000 to find out the location and size of the QSPI memory. The &#8220;init&#8221; commands ensure that the initialization is performed in the correct order:<a href=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2016\/03\/15-cmd-1.png\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-8337\" src=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2016\/03\/15-cmd-1.png\" alt=\"\" width=\"1211\" height=\"695\" \/><\/a><\/li>\n<li>Set a breakpoint at the last line of <strong>main()<\/strong> and press F5 to start debugging. Observe how the contents of the <strong>g_ArrayInQSPI<\/strong> now matches the expected values:<a href=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2016\/05\/array.png\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-8361\" src=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2016\/05\/array.png\" alt=\"\" width=\"1185\" height=\"816\" \/><\/a><\/li>\n<li>If you are planning to port the FLASH plugin to a different device, you can use the VisualGDB Software Tracing (Custom Edition or higher) to quickly record all SPI commands issued by the driver. It can also record their timing and specific code paths. Go back to the FLASH plugin project, enable the software tracing as shown in <a href=\"https:\/\/visualgdb.com\/tutorials\/tracing\/embedded\/\">this tutorial<\/a>, and set tracepoints at <strong>FLASH_Plugin_*()<\/strong> functions recording the value of <strong>uwTick<\/strong>:<a href=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2016\/05\/14-trace1.png\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-8363\" src=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2016\/05\/14-trace1.png\" alt=\"\" width=\"1287\" height=\"817\" \/><\/a><\/li>\n<li>Set another tracepoint in <strong>HAL_QSPI_Command() <\/strong>recording the <strong>cmd<\/strong> structure and <strong>uwTick<\/strong> as well:<a href=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2016\/05\/15-command.png\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-8364\" src=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2016\/05\/15-command.png\" alt=\"\" width=\"1287\" height=\"817\" \/><\/a><\/li>\n<li>Now you can run the program and immediately see in which order various FLASH plugin functions got called, and how many calls to <strong>HAL_QSPI_Command()<\/strong> each of them generated:<a href=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2016\/05\/16-data.png\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-8365\" src=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2016\/05\/16-data.png\" alt=\"\" width=\"1185\" height=\"816\" \/><\/a>Each call to <strong>HAL_QSPI_Command()<\/strong> will have the entire <strong>cmd<\/strong> structure recorded, so you can see which exact commands were executed.<\/li>\n<li>You can also get a convenient table view of all QSPI commands (with instructions, addresses and timestamps) by creating a table view from the Trace Data window and dragging the variables you would like to watch into the bottom part of the view:<a href=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2016\/05\/17-table.png\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-8366\" src=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2016\/05\/17-table.png\" alt=\"\" width=\"1255\" height=\"547\" \/><\/a><\/li>\n<li>The trace recordings are saved into the trace report files and can be viewed later. So if the FLASH driver stops working at some point, you can always review an older recording and see which QSPI commands it used when everything was working as expected:<a href=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2016\/05\/replay.png\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-8367\" src=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2016\/05\/replay.png\" alt=\"\" width=\"1185\" height=\"816\" \/><\/a><\/li>\n<\/ol>\n","protected":false},"excerpt":{"rendered":"<p>This tutorial shows how to develop FLASH drivers for use with the Sysprogs fork of OpenOCD and VisualGDB. FLASH drivers<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[27],"tags":[53,61],"_links":{"self":[{"href":"https:\/\/visualgdb.com\/w\/wp-json\/wp\/v2\/posts\/1927"}],"collection":[{"href":"https:\/\/visualgdb.com\/w\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/visualgdb.com\/w\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/visualgdb.com\/w\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/visualgdb.com\/w\/wp-json\/wp\/v2\/comments?post=1927"}],"version-history":[{"count":7,"href":"https:\/\/visualgdb.com\/w\/wp-json\/wp\/v2\/posts\/1927\/revisions"}],"predecessor-version":[{"id":8368,"href":"https:\/\/visualgdb.com\/w\/wp-json\/wp\/v2\/posts\/1927\/revisions\/8368"}],"wp:attachment":[{"href":"https:\/\/visualgdb.com\/w\/wp-json\/wp\/v2\/media?parent=1927"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/visualgdb.com\/w\/wp-json\/wp\/v2\/categories?post=1927"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/visualgdb.com\/w\/wp-json\/wp\/v2\/tags?post=1927"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}