-
Notifications
You must be signed in to change notification settings - Fork 7.4k
stm32H7 external NOR ospi flash in memorymapped mode #62349
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
Tested with stm32h7b3i_dk in MemoryMapped mode |
@FRASTM, when used with a bootloader (i.e. MCU-Boot) shouldn't If I understand it correctly:
The FW starts after the jump. There, the init sequence is going to be performed for all device and sys nodes (happens twice if the device is used in the FW and bootloader). Or did I miss something? |
No, as you say I could face a pb of clocking.
|
By xSPI I meant OSPI/QSPI. I am saying that the flash is currently re-initialized after the jump. It shouldn't happen when we are executing from it. Similarly to what is done here . Also, #57337 kills the PLL1 clock during clock initialization (after jump). That affects XIP if the flash is clocked using it (default). |
In the sample application which is compiled linked and written for the external NOR flash, there is no oSPI flash driver (no oSPI peripheral) This is just a hello_word or a basic/blinky. About PLL and system clock I cannot tell. Once jumped to the NOR I cannot debug anymore after instruction
|
I was able to adapt samples/application_development/code_relocation_nocopy to work with the stm32h7b3i_dk based on this. Created a stm32h7b3i_dk.conf in the boards dir there, containing:
Our use-case was to load image data into the external flash and have it accessible to the user application. For this any external flash destined data is defined const inside of a file to be targeted by code relocation. Then modify CMakeLists.txt to point the code relocation to the appropriate memory regions:
Finally for any code/rodata relocated to external flash, you need a external loader configured appropriately to flash it. I was able to use the default MX25LM51245G_STM32H7B3I-DISCO.stldr provided with STM32CubeProgrammer. After the above changes, flashing the zephyr.hex with STM32CubeProgrammer properly uploads to the external flash. |
Quite interesting! Thanks @ajhemphill91 for sharing this work. |
My test is to build the mcuboot in the internal flash (0x8000000) and a sample application for the NOR flash I am using the STM32CubeProgrammer to flash the mcuboot @0x8000000 and the zephyr.signed.bin @ 0x90000000
and application is executed from the external NOR :
|
dbe0a43
to
3f1c537
Compare
dd54d1c
to
5a91280
Compare
s_command.Instruction = (dev_cfg->data_mode == OSPI_SPI_MODE) | ||
? ((stm32_ospi_hal_address_size(dev) == | ||
HAL_OSPI_ADDRESS_24_BITS) | ||
? SPI_NOR_CMD_PP | ||
: SPI_NOR_CMD_PP_4B) | ||
: SPI_NOR_OCMD_PAGE_PRG; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why not using the write_opcode
and base lines configuration on the opcode?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
do you suggest
if (dev_cfg->data_rate == OSPI_STR_TRANSFER) {
s_command.Instruction = (dev_cfg->data_mode == OSPI_SPI_MODE)
? ((stm32_ospi_hal_address_size(dev) ==
HAL_OSPI_ADDRESS_24_BITS)
? SPI_NOR_CMD_PP
: SPI_NOR_CMD_PP_4B)
: SPI_NOR_OCMD_PAGE_PRG;
} else {
s_command.Instruction = write_opcode;
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if (dev_cfg->data_rate == OSPI_STR_TRANSFER) {
s_command.Instruction = (dev_cfg->data_mode == OSPI_SPI_MODE)
? dev_data->write_opcode
: SPI_NOR_OCMD_PAGE_PRG;
} else {
s_command.Instruction = SPI_NOR_OCMD_PAGE_PRG;
The spi_nor_process_bfp
sets write_opcode
accounting to mode if it is not set from DT. The address width is taken into consideration as well.
Also for write and read the lines might need adaptation not only based on mode, but also on the opcode. Something like:
write
if (dev_cfg->data_mode == OSPI_OPI_MODE) {
cmd->AddressSize = HAL_OSPI_ADDRESS_32_BITS;
cmd->InstructionSize = HAL_OSPI_INSTRUCTION_16_BITS;
cmd->InstructionMode = HAL_OSPI_INSTRUCTION_8_LINES;
cmd->AddressMode = HAL_OSPI_ADDRESS_8_LINES;
cmd->DataMode = HAL_OSPI_DATA_8_LINES;
} else {
cmd->AddressSize = stm32_ospi_hal_address_size(dev);
cmd->InstructionSize = HAL_OSPI_INSTRUCTION_8_BITS;
switch (cmd->Instruction) {
case SPI_NOR_CMD_PP_4B:
__fallthrough;
case SPI_NOR_CMD_PP:
cmd->InstructionMode = HAL_OSPI_INSTRUCTION_1_LINE;
cmd->AddressMode = HAL_OSPI_ADDRESS_1_LINE;
cmd->DataMode = HAL_OSPI_DATA_1_LINE;
break;
case SPI_NOR_CMD_PP_1_1_4_4B:
__fallthrough;
case SPI_NOR_CMD_PP_1_1_4:
cmd->InstructionMode = HAL_OSPI_INSTRUCTION_1_LINE;
cmd->AddressMode = HAL_OSPI_ADDRESS_1_LINE;
cmd->DataMode = HAL_OSPI_DATA_4_LINES;
break;
case SPI_NOR_CMD_PP_1_4_4_4B:
__fallthrough;
case SPI_NOR_CMD_PP_1_4_4:
cmd->InstructionMode = HAL_OSPI_INSTRUCTION_1_LINE;
cmd->AddressMode = HAL_OSPI_ADDRESS_4_LINES;
cmd->DataMode = HAL_OSPI_DATA_4_LINES;
break;
default:
return -ENOTSUP;
}
}
read
/* initially set lines by mode */
switch (dev_cfg->data_mode) {
case OSPI_QUAD_MODE:
cmd->InstructionMode = HAL_OSPI_INSTRUCTION_4_LINES;
cmd->AddressMode = HAL_OSPI_ADDRESS_4_LINES;
cmd->DataMode = HAL_OSPI_DATA_4_LINES;
break;
case OSPI_DUAL_MODE:
cmd->InstructionMode = HAL_OSPI_INSTRUCTION_2_LINES;
cmd->AddressMode = HAL_OSPI_ADDRESS_2_LINES;
cmd->DataMode = HAL_OSPI_DATA_2_LINES;
break;
default:
return -ENOTSUP;
}
/* adapt by read mode */
switch (dev_data->read_mode) {
case JESD216_MODE_112:
cmd->InstructionMode = HAL_OSPI_INSTRUCTION_1_LINE;
cmd->AddressMode = HAL_OSPI_ADDRESS_1_LINE;
cmd->DataMode = HAL_OSPI_DATA_2_LINES;
break;
case JESD216_MODE_122:
cmd->InstructionMode = HAL_OSPI_INSTRUCTION_1_LINE;
cmd->AddressMode = HAL_OSPI_ADDRESS_2_LINES;
cmd->DataMode = HAL_OSPI_DATA_2_LINES;
break;
case JESD216_MODE_114:
cmd->InstructionMode = HAL_OSPI_INSTRUCTION_1_LINE;
cmd->AddressMode = HAL_OSPI_ADDRESS_1_LINE;
cmd->DataMode = HAL_OSPI_DATA_4_LINES;
break;
case JESD216_MODE_144:
cmd->InstructionMode = HAL_OSPI_INSTRUCTION_1_LINE;
cmd->AddressMode = HAL_OSPI_ADDRESS_4_LINES;
cmd->DataMode = HAL_OSPI_DATA_4_LINES;
break;
default:
return -ENOTSUP;
}
Similar is done for normal operations. Therefore, some driver refactoring could be beneficial.
drivers/flash/flash_stm32_ospi.c
Outdated
s_command.Instruction = (dev_cfg->data_rate == OSPI_STR_TRANSFER) | ||
? ((dev_cfg->data_mode == OSPI_SPI_MODE) | ||
? ((stm32_ospi_hal_address_size(dev) == | ||
HAL_OSPI_ADDRESS_24_BITS) | ||
? SPI_NOR_CMD_READ_FAST | ||
: SPI_NOR_CMD_READ_FAST_4B) | ||
: SPI_NOR_OCMD_RD) | ||
: SPI_NOR_OCMD_DTR_RD; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why not using read_opcode
and base other configuration on available read_mode
, data_mode
, data_rate
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why not using
read_opcode
and base other configuration on availableread_mode
,data_mode
,data_rate
?
do you suggest :
s_command.Instruction = (dev_cfg->data_rate == OSPI_STR_TRANSFER)
? ((dev_cfg->data_mode == OSPI_SPI_MODE)
? ((stm32_ospi_hal_address_size(dev) ==
HAL_OSPI_ADDRESS_24_BITS)
? SPI_NOR_CMD_READ_FAST
: SPI_NOR_CMD_READ_FAST_4B)
: dev_data->read_opcode)
: SPI_NOR_OCMD_DTR_RD;
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Something like
if (dev_cfg->data_rate == OSPI_DTR_TRANSFER) {
/* DTR transfer rate (==> Octal mode) */
cmd->Instruction = SPI_NOR_OCMD_DTR_RD;
cmd->DummyCycles = SPI_NOR_DUMMY_RD_OCTAL_DTR;
} else {
/* STR transfer rate */
if (dev_cfg->data_mode == OSPI_OPI_MODE) {
/* OPI and STR */
cmd->Instruction = SPI_NOR_OCMD_RD;
cmd->DummyCycles = SPI_NOR_DUMMY_RD_OCTAL;
} else {
/* use SFDP:BFP read instruction */
cmd->Instruction = dev_data->read_opcode;
cmd->DummyCycles = dev_data->read_dummy_cycles;
}
}
At init stage the read opcode, dummy cycles, write opcode are supposed to be set. Some from SFDP:BFP and/or DT.
5a91280
to
045dbbb
Compare
rebasing on d7873e2 --> requires a new MPU region to be specified for the ext mem flash
|
045dbbb
to
f6dfcb1
Compare
rebase on d7873e2
|
|
588b336
to
61e3968
Compare
61e3968
to
00ca393
Compare
For the flash driver, the base address is the MCU internal flash address (usualyy 0x8000000). This PR gets the that address from the device tree node "st,stm32-nv-flash" instead of relying on the CONFIG_FLASH_BASE_ADDRESS which might differ when building for another flash memory. Signed-off-by: Francois Ramu <[email protected]>
This CONFIG_STM32_MEMMAP is for enabling the MemoryMapped mode on external octo or quad spi memory. In this case, the flash_stm32_read is done in mem map mode the flash_stm32_erase is not available. Signed-off-by: Francois Ramu <[email protected]>
Enable the MemoryMapped Mode for the stm32 octoFlash driver Configure the Flash in MemoryMapped to use in XiP mode. With this mode the erase and write are not supported. Address and size are given by the DTS register property. Signed-off-by: Francois Ramu <[email protected]>
This change is aborting the memoryMapped mode of the octo-flash before erasing or writing the NOR. Operations are performed in command mode. Reading is always performed in MemoryMapped mode (memcopy) Signed-off-by: Francois Ramu <[email protected]>
Enable the DCACHE1 in INCR burt mode to allow writing to the external NOR octoFlash when in MemoryMapped mode Signed-off-by: Francois Ramu <[email protected]>
Define the Device tree of the b_u585i_iot02a disco kit to access the external NOR octo-flash in MemoryMapped mode for XiP Signed-off-by: Francois Ramu <[email protected]>
Gives a sample to execute the little fs on external memory map (XiP) where the lfs1 partition is in internal mcu flash The application is built/linked/stored in the external NOR flash on slot1 partition. Signed-off-by: Francois Ramu <[email protected]>
Skip the PLL1 init if it is already running, this will avoid disabling the PLL when running after a jump from mcuboot Signed-off-by: Francois Ramu <[email protected]>
Define the MPU attribute to be ATTR_MPU_IO for the qspi region, starting at 0x90000000 of the stm32h7 serie. Signed-off-by: Francois Ramu <[email protected]>
Define the Device tree of the stm32h7b3i_dk disco board to access the external NOR octo-flash in MemoryMapped mode for XiP Set openocd runner for debugging. Signed-off-by: Francois Ramu <[email protected]>
Gives a sample to execute the little fs on external memory map (XiP) where the lfs1 partition is in internal mcu flash Signed-off-by: Francois Ramu <[email protected]>
00ca393
to
c54eca3
Compare
if (stm32_ospi_is_memorymap(dev)) { | ||
/* If the Flash is in MemoryMapped mode, abort it and continue */ | ||
LOG_INF("MemoryMap : disable before write"); | ||
if (stm32_ospi_abort_memmap(dev) != 0) { | ||
LOG_ERR("MemoryMap not aborted correctly"); | ||
return -EIO; | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It would be fatal when accidently writing while running in XIP.
I guess the idea here is to allow bootloader to exit memory map mode that is entered in OSPI init to allow erase & write operations.
In that case, the CONFIG_MCUBOOT
is going to be set to y
for the mcuboot while the app won't have it. Extra macro could be used to prevent memory map mode exit in the application code.
An alternative, protect write abortion with extra Kconfig option that the application can set.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Another alternative is to run flash driver from RAM. Not proposed today as not yet fully functional on our side, but this is an option used by others.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am not sure what is the footprint on RAM in that case. But having flash driver in ram might not be required for all applications. After switching to the slot in o/qSPI. I assume it is rare when writing and erasing are required outside of the bootloader (unless update slot is there as well).
@@ -989,6 +1127,19 @@ static int flash_stm32_ospi_erase(const struct device *dev, off_t addr, | |||
return -ENOTSUP; | |||
} | |||
|
|||
#ifdef CONFIG_STM32_MEMMAP |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
- Requires relocation
if (stm32_ospi_is_memorymap(dev)) { | ||
/* If the Flash is in MemoryMapped mode, abort it and continue */ | ||
LOG_INF("MemoryMap : disable before write"); | ||
if (stm32_ospi_abort_memmap(dev) != 0) { | ||
LOG_ERR("MemoryMap not aborted correctly"); | ||
return -EIO; | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Another alternative is to run flash driver from RAM. Not proposed today as not yet fully functional on our side, but this is an option used by others.
see #68607 |
This PR completes the #61082
to make the stm32h7 or stm32u5 with octo-NOR flash tested when the external NOR octoFlash is configured in MemoryMapped mode.
CONFIG_STM32_MEMMAP is enabling the external NOR flash MemoryMapped mode.
CONFIG_STM32_MEMMAP_EXT_FLASH_BASE_ADDRESS is defining the external nor flash base address