on 2026-01-28 12:35 AM
In this article, we describe the modifications required to port the STM32N6570 example on the STM32N6570-DK board, which uses the MX66UW1G45G memory from Macronix, to the IS25LXWX01G memory from ISSI.
The example that we use describes how to erase a part of an XSPI NOR memory, write data in indirect mode, and access to XSPI NOR memory in memory-mapped mode to check the data. This example is available within STM32CubeN6 under the following path:
STM32Cube_FW_N6_V1.3.0\Projects\STM32N6570-DK\Examples\XSPI
The example is available here on GitHub as well.
The IS25LXWX01G and MX66UW1G45G memories are pin-to-pin compatible, and both operate at 1.8 V, making it straightforward to swap the memories.
The extended serial peripheral interface (XSPI) has 16 I/O data lines and supports single, dual, quad-SPI, octo-SPI, and 16-bit memories so that a wide range of external memory types can be used. The STM32N6570-DK board is configured to use octal mode, where eight data lines (IO0 through IO7) are connected to the external memory, as illustrated below:
Impedance for the high-speed signals between the microcontroller and memory device is important for reliability and should be adjusted based on implementation. This topic is beyond the scope of this article.
The XSPI section in the reference manual (RM0486) shows the different types of memory supported and configured using bitfield MTYP[2:0] in the XSPI_DCR1 register. Data ordering is of particular importance when configuring the interface:
According to the IS25LXWX01G datasheet section READ MEMORY Operations Timings, the data is output in the order D0 followed by D1, and the DQS signal polarity is opposite to the clock, as shown below: excerpt from the IS25LXWX01G datasheet.
This indicates that the IS25LXWX01G is compatible with Micron mode, and the MTYP[2:0] bits in XSPI_DCR1 must be configured to 0b000: Micron Mode.
To implement this change, open STM32CubeMX and load XSPI_NOR_MemoryMapped_DTR.ioc available under:
STM32Cube_FW_N6_V1.3.0\Projects\STM32N6570-DK\Examples\XSPI\XSPI_NOR_MemoryMapped_DTR.
In XSPI2 in Connectivity category under Pinout & Configuration, then under Parameter Settings tab, change the memory type from Macronix to Micron, as shown below:
At this point, save the project and generate the code by clicking the [Generate Code] button located in the top-right corner.
Each memory supplier uses unique commands to communicate with external serial memory. Table 8.1 Command Set of IS25LXWX01G datasheet lists all the commands supported by the memory. The extract below shows the read commands.
The first step is to modify the commands defined in Projects\STM32N6570-DK\Examples\XSPI\XSPI_NOR_MemoryMapped_DTR\FSBL\Inc\main.h to align with the IS25LXWX01G datasheet.
The first command OCTAL_IO_READ_CMD is not utilized, simply remove it.
The second command is OCTAL_IO_DTR_READ_CMD, which initiates a read operation from an external memory device using the Octal I/O interface in Double Transfer Rate (DTR) mode.
Based on the excerpt below from IS25LXWX01G datasheet Table 8.1 Command Set, the memory supports many read commands. For best performance, we want to use one that supports octal mode and Double Data Rate (DDR) which is the same as Double Transfer Rate (DTR).
The two commands that satisfy the two requirements are DDR OCTAL OUTPUT FAST READ (9Dh) and DDR OCTAL I/O FAST READ (FDh). Using the command DDR OCTAL I/O FAST READ (FDh) has the advantage of not requiring to switch from 3-byte address mode to 4-byte address mode. Later in this article I show how to switch the addressing mode even though we are enabling the Octal DTR mode with the command E7h.
Additionally, the memory datasheet in section xSPI Signal Protocol Description specifies in octal DDR mode that the memory decodes the first byte of a valid command on the rising edge of the clock only during the command phase. Therefore, the repeated byte of the command extension becomes a dummy byte as shown by the screenshot below. This means that we can define the command OCTAL_IO_DTR_READ_CMD as FDFDh: the 8-bit MSB is decoded while the 8-bit LSB (repeated) is dummy. Some memory vendors have the pattern of command as cmd+inverted (case of Macronix)
Update the command in main.h as follows:
#define OCTAL_IO_DTR_READ_CMD 0xFDFD
Follow the same process for the other commands: octal page program, octal read status register, octal sector erase, octal write enable, and write to the volatile configuration register based on Table 8.1 Command Set in the memory datasheet:
#define OCTAL_PAGE_PROG_CMD 0x1212
#define OCTAL_READ_STATUS_REG_CMD 0x0505
#define OCTAL_SECTOR_ERASE_CMD 0xD8D8
#define OCTAL_WRITE_ENABLE_CMD 0x0606
#define WRITE_VOL_REG_CMD 0x81
The WRITE_VOL_REG_CMD command is defined as an 8-bit instruction since it is used before the Octal DDR mode is activated. Once Octal DDR mode is set, command instructions require to be 16-bit length.
Dummy cycles are additional clock cycles inserted during read operations from serial NOR flash memory (for example, SPI NOR flash) to allow the memory device sufficient time to respond. The number of dummy cycles varies depending on whether the operation involves data access or register access.
For register read access, 8 clock cycles are required, as shown in the excerpt from the memory datasheet Table 8.1 Command Set.
For data read access, the default requirement is 16 clock cycles, as shown in the excerpt from the memory datasheet Table 8.1 Command Set.
However, it is recommended to adjust the dummy cycles based on the XSPI clock frequency. In this example, the XSPI clock frequency is 100 MHz, so the appropriate dummy cycles configuration is 11. Table 6.7 of the memory datasheet shows the number of dummy clock cycles per operating clock frequency.
The changes regarding the dummy cycles for both register and data read are as follows:
#define DUMMY_CLOCK_CYCLES_READ 11 // Has to match the dummy cycles setup in the configuration register
#define DUMMY_CLOCK_CYCLES_READ_OCTAL 8
The screenshot below illustrates all the changes in main.h. The left-hand side shows the modified code and the right-hand side shows the original code.
Note that OCTAL_IO_READ_CMD and WRITE_CFG_REG_2_CMD are removed:
The memory boots into SPI mode and single transfer rate (STR) so the command needs to be sent into SPI mode with instruction, address, data lines set to 1 line.
As discussed earlier, it is recommended to setup the dummy cycles configuration to 11 in this particular example where the XSPI clock frequency is 100MHz. For the IS25LXWX01G memory, the sequence is as follows:
The table below summarizes the two steps:
| Command | Instruction | Format | Dummy cycles | Address | Data | Explanation |
| Write Enable | 0x06 | 1-0-0 | 0 | None | None | Enable writing to configuration register |
| Write register | 0x81 | 1-1-1 | 0 | 0x01 | 1 byte = 11 (decimal) | Set dummy cycles |
The code implementation for this sequence is shown below:
// Setup dummy cycles
/* Step 1: Write Enable */
sCommand.OperationType = HAL_XSPI_OPTYPE_COMMON_CFG;
sCommand.InstructionMode = HAL_XSPI_INSTRUCTION_1_LINE;
sCommand.Instruction = WRITE_ENABLE_CMD;
sCommand.AddressMode = HAL_XSPI_ADDRESS_NONE;
sCommand.DataMode = HAL_XSPI_DATA_NONE;
sCommand.AlternateBytesMode = HAL_XSPI_ALT_BYTES_NONE;
sCommand.DQSMode = HAL_XSPI_DQS_DISABLE;
if(HAL_XSPI_Command(hxspi, &sCommand, 0xfff) != HAL_OK)
{
Error_Handler();
}
/* Step 2: write the volatile configuration */
sCommand.OperationType = HAL_XSPI_OPTYPE_COMMON_CFG;
sCommand.Instruction = WRITE_VOL_REG_CMD;
sCommand.InstructionMode = HAL_XSPI_INSTRUCTION_1_LINE;
sCommand.InstructionWidth = HAL_XSPI_INSTRUCTION_8_BITS;
sCommand.AddressMode = HAL_XSPI_ADDRESS_1_LINE;
sCommand.AddressWidth = HAL_XSPI_ADDRESS_24_BITS;
sCommand.Address = 0x00000001; // dummy cycles configuration
sCommand.DummyCycles = 0;
sCommand.DataMode = HAL_XSPI_DATA_1_LINE;
sCommand.DataLength = 1;
sCommand.DataDTRMode = HAL_XSPI_DATA_DTR_DISABLE;
reg = DUMMY_CLOCK_CYCLES_READ; // 11 clock cycles at 105MHz
if(HAL_XSPI_Command(hxspi, &sCommand, 0xfff) != HAL_OK)
{
Error_Handler();
}
if(HAL_XSPI_Transmit(hxspi, (uint8_t*)®, 0xffff) != HAL_OK)
{
Error_Handler();
}
If your application changes the XSPI clock frequency, you may need to adjust the dummy cycles accordingly to maintain data integrity from the external serial flash.
The default memory address range is limited to 24 bits, which means only the first 128 Mb are accessible. To access the full memory content (1 Gb with IS25LXWX01G), it is necessary to enable the 4-byte address mode. To achieve this, perform the following steps:
This step is not mandatory. Once the device is configured to Octal DDR mode in the next step, it will be fixed to 4-byte address mode. This is regardless of the register configuration at address 0x05, whether set to 0xff or 0xfe.
The code implementation for this sequence is shown below:
// Enable 4Byte addressing
/* Step 1: Write Enable */
sCommand.OperationType = HAL_XSPI_OPTYPE_COMMON_CFG;
sCommand.InstructionMode = HAL_XSPI_INSTRUCTION_1_LINE;
sCommand.InstructionWidth = HAL_XSPI_INSTRUCTION_8_BITS;
sCommand.Instruction = OCTAL_WRITE_ENABLE_CMD; /* Write volatile configuration register */
sCommand.InstructionDTRMode = HAL_XSPI_INSTRUCTION_DTR_DISABLE;
sCommand.AddressMode = HAL_XSPI_ADDRESS_NONE;
sCommand.DataMode = HAL_XSPI_DATA_NONE;
sCommand.AlternateBytesMode = HAL_XSPI_ALT_BYTES_NONE;
sCommand.DQSMode = HAL_XSPI_DQS_ENABLE;
if(HAL_XSPI_Command(hxspi, &sCommand, 0xfff) != HAL_OK)
{
Error_Handler();
}
/* Step 2: write the volatile configuration */
sCommand.OperationType = HAL_XSPI_OPTYPE_COMMON_CFG;
sCommand.Instruction = WRITE_VOL_REG_CMD; /* Write volatile configuration register */
sCommand.InstructionMode = HAL_XSPI_INSTRUCTION_1_LINE;
sCommand.InstructionWidth = HAL_XSPI_INSTRUCTION_8_BITS;
sCommand.InstructionDTRMode = HAL_XSPI_INSTRUCTION_DTR_DISABLE;
sCommand.AddressMode = HAL_XSPI_ADDRESS_1_LINE;
sCommand.AddressWidth = HAL_XSPI_ADDRESS_24_BITS;
sCommand.Address = 0x00000005; // 4Byte configuration at offset 05h
sCommand.AddressDTRMode = HAL_XSPI_ADDRESS_DTR_DISABLE;
sCommand.DummyCycles = 0;
sCommand.DataMode = HAL_XSPI_DATA_1_LINE;
sCommand.DataLength = 1;
sCommand.DataDTRMode = HAL_XSPI_DATA_DTR_DISABLE;
reg = 0xFE; // Beyond 128Mb address configuration FEh = 4-byte address - FFh = 3-byte address (Default)
if(HAL_XSPI_Command(hxspi, &sCommand, 0xfff) != HAL_OK)
{
Error_Handler();
}
if(HAL_XSPI_Transmit(hxspi, (uint8_t*)®, 0xffff) != HAL_OK)
{
Error_Handler();
}
To enable the Octal Double Transfer Rate (DTR) mode, a specific command must be sent to the memory using the XSPI_NOR_OctalDTRModeCfg() function.
For the IS25LXWX01G memory, the sequence is as follows:
The code implementation for this sequence is shown below. Note that some extra polling steps are present in the code. These are not strictly required for enabling Octal DTR mode, but have been included to maintain consistency with the original code and minimize modifications.
// Setup Octal DDR mode
sCommand.OperationType = HAL_XSPI_OPTYPE_COMMON_CFG;
sCommand.InstructionMode = HAL_XSPI_INSTRUCTION_1_LINE;
sCommand.InstructionWidth = HAL_XSPI_INSTRUCTION_8_BITS;
sCommand.InstructionDTRMode = HAL_XSPI_INSTRUCTION_DTR_DISABLE;
sCommand.AddressDTRMode = HAL_XSPI_ADDRESS_DTR_DISABLE;
sCommand.AlternateBytesMode = HAL_XSPI_ALT_BYTES_NONE;
sCommand.DataDTRMode = HAL_XSPI_DATA_DTR_DISABLE;
sCommand.DummyCycles = 0;
sCommand.DQSMode = HAL_XSPI_DQS_DISABLE;
sConfig.MatchMode = HAL_XSPI_MATCH_MODE_AND;
sConfig.AutomaticStop = HAL_XSPI_AUTOMATIC_STOP_ENABLE;
sConfig.IntervalTime = 0x10;
/* Enable write operations */
sCommand.Instruction = WRITE_ENABLE_CMD;
sCommand.DataMode = HAL_XSPI_DATA_NONE;
sCommand.AddressMode = HAL_XSPI_ADDRESS_NONE;
if (HAL_XSPI_Command(hxspi, &sCommand, HAL_XSPI_TIMEOUT_DEFAULT_VALUE) != HAL_OK)
{
Error_Handler();
}
/* Reconfigure XSPI to automatic polling mode to wait for write enabling */
sConfig.MatchMask = 0x02;
sConfig.MatchValue = 0x02;
sCommand.Instruction = READ_STATUS_REG_CMD;
sCommand.DataMode = HAL_XSPI_DATA_1_LINE;
sCommand.DataLength = 1;
if (HAL_XSPI_Command(hxspi, &sCommand, HAL_XSPI_TIMEOUT_DEFAULT_VALUE) != HAL_OK)
{
Error_Handler();
}
if (HAL_XSPI_AutoPolling(hxspi, &sConfig, HAL_XSPI_TIMEOUT_DEFAULT_VALUE) != HAL_OK)
{
Error_Handler();
}
/* Write Configuration register 2 (with Octal I/O SPI protocol) */
sCommand.Instruction = WRITE_VOL_REG_CMD;
sCommand.AddressMode = HAL_XSPI_ADDRESS_1_LINE;
sCommand.AddressWidth = HAL_XSPI_ADDRESS_32_BITS;
sCommand.Address = 0;
reg = 0xe7; // E7h = Octal DDR
if (HAL_XSPI_Command(hxspi, &sCommand, HAL_XSPI_TIMEOUT_DEFAULT_VALUE) != HAL_OK)
{
Error_Handler();
}
if (HAL_XSPI_Transmit(hxspi, ®, HAL_XSPI_TIMEOUT_DEFAULT_VALUE) != HAL_OK)
{
Error_Handler();
}
sCommand.Instruction = READ_STATUS_REG_CMD;
sCommand.DataMode = HAL_XSPI_DATA_1_LINE;
sCommand.DataLength = 1;
if (HAL_XSPI_Command(hxspi, &sCommand, HAL_XSPI_TIMEOUT_DEFAULT_VALUE) != HAL_OK)
{
Error_Handler();
}
if (HAL_XSPI_AutoPolling(hxspi, &sConfig, HAL_XSPI_TIMEOUT_DEFAULT_VALUE) != HAL_OK)
{
Error_Handler();
}
At this stage, all instructions and addresses must be coded in octal DDR with DQS enabled. The instruction, address, and data modes when available is set to 8 lines.
The complete modified source code is available here: stm32-hotspot/STM32N6_XSPI_IS25LXWX01G.
In this article, we walked through the key steps to successfully port the STM32N6570 XSPI memory-mapped example from the MX66UW1G45G to the IS25LXWX01G memory. By adjusting the XSPI settings, updating command codes, and fine-tuning dummy cycles, you can ensure successful communication with the new memory chip. With these changes, your STM32N6 XSPI memory-mapped DTR example works with the IS25LXWX01G.