Skip to main content
Explorer II
March 4, 2024
Question

Guide for mapping a new chip as an external flash. Is external loader required ?

  • March 4, 2024
  • 10 replies
  • 6018 views

Hello I am in the task of adding a new flash as an external flash via QSPI interface with the MCU STM32F777ZITx.

The flash that I want to add is S25FL256L as an external flash. I am not finding any references which I can use with this flash. Also it is not getting clear to me whether I need to make an external loader for mapping the external flash in memory mapped mode or only configuration is needed ?

rohan_m_0-1709562790625.pngrohan_m_1-1709562824039.pngrohan_m_2-1709562848167.png

 

    This topic has been closed for replies.

    10 replies

    Technical Moderator
    March 4, 2024

    Hello @rohan_m 

    I suggest you take a look at this MOOC specially the 3rd video which should be a good guide.

    Best Regards.

    STTwo-32

    Super User
    March 4, 2024

    An "external loader" is needed only if you want CubeIDE debugger load the content of your QSPI flash automatically when you start debugging (or running). Also if you use CubeProgrammer to program the QSPI flash. If you use other way to load the QSPI flash content, external loader is not needed. For example - if the ext. flash is used to store data that your application produces, or receives from outside.

    Here is the one part of the ST BSP driver for similar flash chip S25FL512s:

    https://github.com/STMicroelectronics/stm32-s25fl512s

     

    rohan_mAuthor
    Explorer II
    March 4, 2024

    I am facing this problem where the call to the CSP_QSPI_EnableMemoryMappedMode does not fail but the external flash looks like this even after writing something

    rohan_m_0-1709575608461.png

     

    Graduate II
    March 4, 2024

    >>Also it is not getting clear to me whether I need to make an external loader for mapping the external flash in memory mapped mode or only configuration is needed ?

    Configuration here, but blank content will be 0xFF

    Unconfigured, accesses will Hard Fault. Have a Hard Fault Handler that outputs actionable information so you can diagnose / debug the situation.

    Graduate II
    March 4, 2024

    You'll need an External Loader if you want the tools to download content.

    To see the memory in the Debugger, etc, you'll need for your code to have successfully brought up the QSPI interface, pins, and external memory itself.

    You should be able to implement read, write, and command functions to interact with the memory. Try reading the JEDEC ID and some pages first. Once you have that working you can provide this "Read Template" functionality to the Memory Mapping mode startup process, and then access to the 0x90000000 region will use that to allow the MCU to read the content.

    Yours looks like the memory mapping still isn't working, and the access to 0x90000000 is faulting.

    Start by reading the data/sheets and manuals for your S25FL256 memory, and get that working in your own BSP code.

    rohan_mAuthor
    Explorer II
    March 4, 2024

    I mean the confusion I have is that to view the memory map in the debugging window external loader is not needed right ?

    Graduate II
    March 4, 2024

    No, but the memory needs to be up and running. It needs to be visible to the MCU to be visible in the Debugger.

    The way ST does things this will be deep into main() once you've called all the functions. What *ARM* wanted was this do be done in SystemInit(), so the memory is viable for C runtime initialization and the scatter loader, so that in main() you had a nearly complete operating environment working.

    In other platforms the alternative is a "Debug Script" which peeks/pokes the system and peripheral registers to bring up the clocks, GPIO, and QSPI peripheral, and the attached memory.

    ST could perhaps use the Init() function of the External Loader in-lieu of the platform suitable debugger script, but I'm not sure things are at that level of sophistication a decade in.

    rohan_mAuthor
    Explorer II
    March 5, 2024

    Also I believe no modifications in the .ld script just to read the manufacturer ID ?

    Super User
    March 5, 2024

    No modifications in the .ld script. See below code to read S25FL256 ID on STM32F7. Change the ID for S25FL512 per the data sheet.

     

     

     

     

     

    
    int8_t BSP_QSPI_FlashDetect(struct QSPI_FlashInfo *pInfo;)
    {
     uint8_t rc;
     // Ensure it is reset to normal "indirect" mode
     if (QSPI_ResetMemory(&QSPIHandle) != QSPI_OK)
     {
     return QSPI_NOT_SUPPORTED;
     }
    
     HAL_Delay(100); //?? needs delay after reset?
    
     uint8_t id_buf[6];
     rc = BSP_QSPI_ReadFlashID(id_buf, sizeof(id_buf));
     if (QSPI_OK != rc) {
     dbgprintf("ReadID failed: %u\n", (unsigned)rc);
     return -1;
     }
    
     // Infineon S25FL256LAGNFI010
     const uint8_t id2_data[] = { 0x01, 0x60, 0x19, };
     // 0x19-> 32MB, 0x18 -> 16 MB
     // see [DS2, table 51]
     unsigned fl_mb = 0;
     if (0 == memcmp(id2_data, id_buf, 2)) {
     qi.FlashModel = 'S';
     switch(id_buf[2]) {
     case 0x18: fl_mb = 16; break;
     case 0x19: fl_mb = 32; break;
     case 0x1A: fl_mb = 64; break;
     default:
     dbgprintf("ERROR: QSPI Flash size code no match: %2.2X\n", id_buf[2]);
     return QSPI_NOT_SUPPORTED;
     }
     } else {
     dbgprintf("ERROR: QSPI Flash ID no match: %2.2X %2.2X\n", id_buf[0], id_buf[1]);
     return QSPI_NOT_SUPPORTED;
     }
    
     // Fill the flash characteristics:
     // In 3-byte address mode, only 16 MB are available!
     pInfo->FlashSize = fl_mb * 1024u*1024u;
     pInfo->EraseSectorSize = 4*1024u; //4K
     pInfo->BigEraseSectorSize = 64*1024u; //64K
     pInfo->ProgPageSize = 256;
     pInfo->EraseSectorsNumber = pInfo->FlashSize/pinfo->EraseSectorSize;
     pInfo->ProgPagesNumber = pInfo->FlashSize/pInfo->ProgPageSize;
     pInfo->DummyCycles = 0; //dummy cycles not configured yet! TODO
    
     return QSPI_OK;
    }
    
    int8_t BSP_QSPI_ReadFlashID(uint8_t *id_buf, unsigned size)
    {
     QSPI_HandleTypeDef *Ctx = &QSPIHandle;
     if (!id_buf || size < 1 || size > 256) {
     return QSPI_NOT_SUPPORTED;
     }
     QSPI_CommandTypeDef s_command = {
     .InstructionMode = QSPI_INSTRUCTION_1_LINE,
     .Instruction = 0x9F,
     .AddressMode = QSPI_ADDRESS_NONE,
     .AlternateByteMode = QSPI_ALTERNATE_BYTES_NONE,
     .DataMode = QSPI_DATA_1_LINE,
     .NbData = 0,
     .DummyCycles = 0,
     .DdrMode = QSPI_DDR_MODE_DISABLE,
     .DdrHoldHalfCycle = QSPI_DDR_HHC_ANALOG_DELAY,
     .SIOOMode = QSPI_SIOO_INST_EVERY_CMD,
     };
     /* Configure the command */
     s_command.NbData = size;
     if (HAL_QSPI_Command(Ctx, (void*)&s_command, 10) != HAL_OK)
     {
     return QSPI_ERROR;
     }
    
     /* Reception of the data */
     if (HAL_QSPI_Receive(Ctx, id_buf, 10) != HAL_OK)
     {
     return QSPI_ERROR;
     }
    
     return QSPI_OK;
    }

     

     

     

     

     

    Visitor II
    March 6, 2024

    My experience (knowledge) when it comes to external flash memory:

    • it CANNOT have any entry in linker script!
      You cannot define a region with code on external flash: it CANNOT be loaded:
      a) without to initialize the chip (some code has to be running first) - the external flash is not accessible
      b) writing content to an external flash memory needs a special code (procedure) to "program" the flash
    • a debugger connected, e.g. via STM32CubeProgrammer, could flash some content:
      a) this content comes from a different file (not you BIN file generated)
      b) the tool will download code into MCU which will do at the end the programming
    • When you configure external Flash for "Memory Mapped": usually it is a Read-Only memory. In this mode you CANNOT write (flush) the external device.
    • For writing (flushing) the external memory - you have to do in "indirect mode" (disable "memory mapped mode").
      And it needs a special routine to do so (e.g. using command to erase pages, to write pages, to wait for done etc. It is not a RAM)

    So, the usual way with (Read-Only) flash is this:

    • flash the content to be stored on external flash:
      a) via tools, e.g. STM32CubeProgrammer and selecting the right flash loader for this chip
      b) do it yourself by getting code from PC (e.g. via UART, USB, ...) and flash it into external memory, with your 
          own flashing program
    • once this was done (once) - have a FW which enables external flash memory, e.g. a "memory mapped".

    But never! place something in a linker script which would try to read from external flash memory (esp. not anything what would result in writing to external flash memory, e.g. global variable to initialize).

    The flash memory becomes just available if your FW has booted and has initialized the flash memory. Any access before this is done will crash!

    So, just when your FW with flash config has done all - you can execute any code line reading from external flash. Not during boot, not via linker script. It is like "adding external memory" after your MCU is up and running. Just when flash is configured - any access is possible.

    rohan_mAuthor
    Explorer II
    March 5, 2024

    But can you please provide a set of steps of getting it to work in memory mapped mode ?

    I was able to extract the ID as 0x1 0x60 0x19.

    And I also dont know what is the definition of 

    struct QSPI_FlashInfo

     

    Graduate II
    March 5, 2024
    rohan_mAuthor
    Explorer II
    March 6, 2024

    But this seems like a different chip for the external flash. I was asking if you had the same code that could help me setup the chip in memory mapped mode as per the code that you pasted above.

    Super User
    March 5, 2024

     

     

    struct QSPI_FlashInfo {
     uint32_t EraseSectorSize; /*!< Size of smallest sectors for the erase operation */
     uint32_t EraseSectorsNumber; /*!< Number of sectors for the erase operation */
     uint32_t ProgPageSize; /*!< Size of page for the program operation */
     uint32_t ProgPagesNumber; /*!< Number of pages for the program operation */
     uint32_t BigEraseSectorSize; /*!< Size of large sectors for the erase operation */
     uint32_t FlashSize; /*!< Size of the flash in 4-byte addr mode, in bytes */
     uint8_t DummyCycles; /*!< Number of dummy cycles desired */
    };
    
    uint8_t BSP_QSPI_EnableMemoryMappedMode(void)
    {
     QSPI_CommandTypeDef s_command;
     QSPI_MemoryMappedTypeDef s_mem_mapped_cfg;
    
     /* Configure the command for the read instruction */
     s_command.InstructionMode = QSPI_INSTRUCTION_1_LINE;
     s_command.Instruction = QUAD_INOUT_FAST_READ_4_BYTE_ADDR_CMD;
     s_command.AddressMode = QSPI_ADDRESS_4_LINES;
     s_command.AddressSize = QSPI_ADDRESS_32_BITS;
     s_command.AlternateByteMode = QSPI_ALTERNATE_BYTES_NONE;
     s_command.AlternateBytesSize = QSPI_ALTERNATE_BYTES_8_BITS;
     s_command.AlternateBytes = 0;
     s_command.DataMode = QSPI_DATA_4_LINES;
     s_command.DummyCycles = 8; //??? todo check
     s_command.DdrMode = QSPI_DDR_MODE_DISABLE;
     s_command.DdrHoldHalfCycle = QSPI_DDR_HHC_ANALOG_DELAY;
     s_command.SIOOMode = QSPI_SIOO_INST_EVERY_CMD;
    
     /* Configure the memory mapped mode */
     s_mem_mapped_cfg.TimeOutActivation = QSPI_TIMEOUT_COUNTER_DISABLE;
     s_mem_mapped_cfg.TimeOutPeriod = 0;
    
     if (HAL_QSPI_MemoryMapped(&QSPIHandle, &s_command, &s_mem_mapped_cfg) != HAL_OK)
     {
     return QSPI_ERROR;
     }
     /* Configure QSPI: LPTR register with the low-power time out value */
     WRITE_REG(QUADSPI->LPTR, 0xFFF);
     return QSPI_OK;
    }

     

     

    rohan_mAuthor
    Explorer II
    March 6, 2024

    But this seems like a different chip for the external flash. I was asking if you had the same code that could help me setup the chip in memory mapped mode as per the code that you pasted above. @Pavel A. 

    Super User
    March 6, 2024

    @rohan_m The posted code works with 32MB chip (SL25FL256L)

    I don't have handy the 64 MB chip so cannot test. Please update the code (commands, values...) per the FL512 data sheet. 

    The device ID according to data sheet posted by Tesla is { 0x01, 0x02, 0x20, ??, 0, 0x80 }

    If you read  0x1 0x60 0x19, then you have SL25FL256 (32 MB) rather than 512 (64 MB)?

    The difference is important, these two have different erase block sizes and write block sizes!

    Somebody (@Tesla DeLorean  ?) IIRC wrote that the 512 Mb chip is a pair or 256 Mb glued together, this results in erase and write block size doubled *BUT* the poll operation is somehow badly affected, because you have to poll both chips instead of one?

    *UPDATE* Sorry for confusion, you've mentioned that you have S25FL256L.

    Graduate II
    March 6, 2024

    Well there are some of those..

    ST uses Micron MT25TLxxx parts that uses a pair of die side-by-side and 8-bit wide (dual mode), you have to read a status register from both chips, as a 16-bit compare mask/match operation, as data is interleaved at a byte level.

    There are other Micron stacked-die parts in the 2Gbit and 4Gbit ranges now. Access in these chain one after the other, dividing the linear address space, you have to query these cyclically to ensure all die are idle.

    Sorry, focused on the wrong part# not the one in the original post.

    https://www.infineon.com/dgdl/Infineon-S25FL256L_S25FL128L_256-MB_(32-MB)_128-MB_(16-MB)_3.0_V_FL-L_FLASH_MEMORY-DataSheet-v09_00-EN.pdf?fileId=8ac78c8c7d0d8da4017d0ed40e335224

    128Mb (16MB) would work with 3-byte addressing

    256Mb (32MB) would need 4-byte addressing

    Super User
    March 6, 2024

    Sorry for confusion. the OP has S25FL256L, not 512. So yes my code should work, after proper initialization of 4-pin mode and enable 32-bit address (this is optional, some prefer 2*16MB layout with 24-bit address). I have not posted the setup code, it is encumbered with unrelated stuff in my project.

    ** EDIT **

    the problem with memory mapped mode can be in number of dummy cycles or alternate bits. Try to add alternate bits:

     

     

     

     

    uint8_t BSP_QSPI_EnableMemoryMappedMode(void)
    {
     QSPI_CommandTypeDef s_command;
     QSPI_MemoryMappedTypeDef s_mem_mapped_cfg;
    
     s_command.InstructionMode = QSPI_INSTRUCTION_1_LINE;
     s_command.Instruction = QUAD_INOUT_FAST_READ_CMD;
     s_command.AddressMode = QSPI_ADDRESS_4_LINES;
     s_command.AddressSize = QSPI_ADDRESS_24_BITS;
     s_command.AlternateByteMode = QSPI_ALTERNATE_BYTES_4_LINES; //<<< change
     s_command.AlternateBytesSize = QSPI_ALTERNATE_BYTES_8_BITS;
     s_command.AlternateBytes = 0; //note see docum about the alt byte value
     s_command.DataMode = QSPI_DATA_4_LINES;
     s_command.DummyCycles = 8;
     s_command.DdrMode = QSPI_DDR_MODE_DISABLE;
     s_command.DdrHoldHalfCycle = QSPI_DDR_HHC_ANALOG_DELAY;
     s_command.SIOOMode = QSPI_SIOO_INST_EVERY_CMD;
    
     /* Configure the memory mapped mode */
     s_mem_mapped_cfg.TimeOutActivation = QSPI_TIMEOUT_COUNTER_DISABLE;
     s_mem_mapped_cfg.TimeOutPeriod = 0;
    
     if (HAL_QSPI_MemoryMapped(&QSPIHandle, &s_command, &s_mem_mapped_cfg) != HAL_OK)
     {
     return QSPI_ERROR;
     }
     /* Configure QSPI: LPTR register with the low-power time out value */
     WRITE_REG(QUADSPI->LPTR, 0xFFF);
     return QSPI_OK;
    }

     

     

     

    The sum of dummy cycles and alternate byte cycles defines where the output data bits begin. The same number will be with no alternate bytes but 10 dummy cycles instead of 8.

    rohan_mAuthor
    Explorer II
    March 8, 2024

    Even with this function I am not able to enter the quad SPI transfer mode.

    I still I am only using the single line mode. I think I will need to get rid of the quad mode register lock.