I want to extend the SDK's flash support to include setting write-protect mode for flash memory sectors. It looks like all I have to do is create new commands in customLUT, along with API functions. Is there a guide for doing this?
I'm using MCUXpresso SDK 2.6.2 (sorry, but it's too close to production time to risk changing it) and the MiMXRT1062 with a Macronix MX25L25645G.
已解决! 转到解答。
Hi
I started preparing a document explaining some of the principles of FlexSPI and how to configure and use the LUT: https://www.utasker.com/docs/iMX/FlexSPI.pdf
which may give some basic ideas.
This is how I write-protect the QSPI flash (code is in fact compatible with the Macronix part as far as I can see):
// Ensure that the boot block is write protected by default
//
fnFlexSPI_Command(0, NOR_CMD_LUT_SEQ_IDX_READFUNCTION, 1, &ucStatusRegister, 0, 0); // read the function register
if ((ucStatusRegister & FUNCTION_TBS) == 0) {
ucStatusRegister = FUNCTION_TBS; // write to bottom configuration in order to protect the boot loader area
fnFlexSPI_Command(0, NOR_CMD_LUT_SEQ_IDX_WRITEENABLE, 0, 0, 0, 0); // enable writes
fnFlexSPI_Command(0, NOR_CMD_LUT_SEQ_IDX_WRITEFUNCTION, 1, 0, &ucStatusRegister, 0); // save the value (one-time-programmable and cannot be changed back again)
}
do {
fnFlexSPI_Command(0, NOR_CMD_LUT_SEQ_IDX_READSTATUSREG, 1, &ucStatusRegister, 0, 0); // read the status register
} while ((ucStatusRegister & STATUS_BUSY) != 0); // repeat if busy
if ((ucStatusRegister & (STATUS_QE | STATUS_BP0 | STATUS_BP1 | STATUS_BP2 | STATUS_BP3)) != (STATUS_QE | STATUS_BP0)) { // check quad mode and write protecion configuration
ucStatusRegister |= (STATUS_QE | STATUS_BP0);
ucStatusRegister &= ~(STATUS_BP1 | STATUS_BP2 | STATUS_BP3); // quad mode configured with write protection on the boot block
fnFlexSPI_Command(0, NOR_CMD_LUT_SEQ_IDX_WRITEENABLE, 0, 0, 0, 0); // enable writes
fnFlexSPI_Command(0, NOR_CMD_LUT_SEQ_IDX_WRITESTATUSREG, 1, 0, &ucStatusRegister, 0); // save the value
do {
fnFlexSPI_Command(0, NOR_CMD_LUT_SEQ_IDX_READSTATUSREG, 1, &ucStatusRegister, 0, 0); // read the status register
} while ((ucStatusRegister & STATUS_BUSY) != 0); // repeat until no longer busy
}
whereby you probably already have entries and examples for reading the status register and enabling its write but you may be missing an entry and method for programming the status register.
Since I don't use the SDK I can't explain exactly how it can be modified there but my LUT entry looks like this (it is a bit more understandable than the SDK method so can probably be easily transferred to it):
{ // Write status register
NOR_CMD_LUT_SEQ_IDX_WRITESTATUSREG,
{
FLEXSPI_LUT_1PAD_SEQ_1(FLEXSPI_Command_SDR, WRITE_STATUS_REGISTER) | FLEXSPI_LUT_1PAD_SEQ_2(FLEXSPI_Command_WRITE_SDR, 0x04),
0,
},
},
whereby the entry is put to any free LUT location (remembering its index - eg. I'll use index 8 for example purposes)
The low level details (the way it works for any implementation at the bottom level) are as follows, whereby the LUT entry in this case is in fact a single long word of 0x20040401 (and a second long word of 0x00000000 which signified that the sequence has ended already).
1. The IPCR1 is written with a value indicating the LUT entry that should control the sequence (8 in example) and the length of the data to be sent (1 since this sequence will just set a single new register value) - 0x00080001 (is the actual register write value)
2. The sequence is triggered by writing TRG (0x00000001) to IPCMD.
3. The FlexSPi now starts interpeting the LUT entry 8 (containing the sequence to write to the status register) and recognises (from the LUT entry pattern) the first instruction:
Command = 0x01 (write status register command)
Op = 0x01 (command SDR)
pads = 0x00 (in this case I have used a single line command rather than a quad command and many QSPI flash commands can accept a quad and single line version)
which causes the FlexSPI to:
assert the CS line and write the command 0x01 (Write Status Register command - also compatible with your chip)
4. It then interprets the second part of the specified LUT sequence, which is
Command = 0x04
Op = 0x08 (Command write SDR)
pads = 0x00
which causes it to send data that your driver interface will write to (or have written to) the FlexSPI Tx FIFO (TFDR0), although only one byte will have been written for a write status register sequence. Note that the data length is not contained in the LUT but instead was set in the IPCR1 (see above) to 1 so the FlexSPI know that it must one send 1 byte when executing this step of the LUT sequence. After sending the single byte (and since the LUT sequence has already ended - second long word is 0) it terminates by negating the chip select line.
Based on this information it should be possible to interpret the LUT and driver sequence in order understand also how to modify SDK code and to generate and control more complicated sequences when needed.
Beware that in the case of setting new non-volatile (protection) settings in the status register that it takes some time for the operation to terminate and (see code above) it is best to poll the status register's busy flag until it shows that it has completed.
Beware also that if you use NXP-MCU Boot Utility to program (or reprogram) boards that it may fail if the QSPI flash is write protected and no more programming is possible. I found that it couldn't handle (nor unlock) a protected QSPI case and it was necessary to remove the protection by some other means before the board could be programmed again using this tool (I am not 100% sure since I avoided it after that experience so didn't repeat such tests, but it might be an issue).
If you don't yet have a complete field updater and protection concept for your product take a look at the uTasker loader which can be used with any application and gives zero-effort On-the-Fly decryption protection for XiP based code too:
- i.MX RT 1062: https://www.utasker.com/iMX/RT1060.html
- Boot loader concept including XiP on-the-fly decryption, clone protection or AES256 protected RAM execution.
-- Boot Loader concept flow chart: https://www.utasker.com/docs/iMX/Loader.pdf and usage reference https://www.utasker.com/docs/iMX/uTaskerLoader_TestDrive.pdf
-- Serial Loader features: https://www.utasker.com/docs/uTasker/uTaskerSerialLoader.pdf
-- Building the loader with MCUXpress: https://www.utasker.com/docs/iMX/MCUXpresso.pdf (and video guide https://youtu.be/p_eUGo6GypY ) - the guide document explains how to use with any application (eg. SDK) and to enabling its operation with On-The-Fly decryption in 5 minutes
-- Building the loader with IAR: https://www.utasker.com/docs/iMX/IAR.pdf (and video guide https://youtu.be/XPCwVndP99s )
-- Building the loader with VisualStudio and GCC: https://www.utasker.com/docs/iMX/GCC.pdf (and video guide https://youtu.be/0UzLLSXABK8 )
Regards
Mark
uTasker project developer for Kinetis and i.MX RT]
Contact me by personal message or on the uTasker web site to discuss professional training or product development requirements
Hi
I started preparing a document explaining some of the principles of FlexSPI and how to configure and use the LUT: https://www.utasker.com/docs/iMX/FlexSPI.pdf
which may give some basic ideas.
This is how I write-protect the QSPI flash (code is in fact compatible with the Macronix part as far as I can see):
// Ensure that the boot block is write protected by default
//
fnFlexSPI_Command(0, NOR_CMD_LUT_SEQ_IDX_READFUNCTION, 1, &ucStatusRegister, 0, 0); // read the function register
if ((ucStatusRegister & FUNCTION_TBS) == 0) {
ucStatusRegister = FUNCTION_TBS; // write to bottom configuration in order to protect the boot loader area
fnFlexSPI_Command(0, NOR_CMD_LUT_SEQ_IDX_WRITEENABLE, 0, 0, 0, 0); // enable writes
fnFlexSPI_Command(0, NOR_CMD_LUT_SEQ_IDX_WRITEFUNCTION, 1, 0, &ucStatusRegister, 0); // save the value (one-time-programmable and cannot be changed back again)
}
do {
fnFlexSPI_Command(0, NOR_CMD_LUT_SEQ_IDX_READSTATUSREG, 1, &ucStatusRegister, 0, 0); // read the status register
} while ((ucStatusRegister & STATUS_BUSY) != 0); // repeat if busy
if ((ucStatusRegister & (STATUS_QE | STATUS_BP0 | STATUS_BP1 | STATUS_BP2 | STATUS_BP3)) != (STATUS_QE | STATUS_BP0)) { // check quad mode and write protecion configuration
ucStatusRegister |= (STATUS_QE | STATUS_BP0);
ucStatusRegister &= ~(STATUS_BP1 | STATUS_BP2 | STATUS_BP3); // quad mode configured with write protection on the boot block
fnFlexSPI_Command(0, NOR_CMD_LUT_SEQ_IDX_WRITEENABLE, 0, 0, 0, 0); // enable writes
fnFlexSPI_Command(0, NOR_CMD_LUT_SEQ_IDX_WRITESTATUSREG, 1, 0, &ucStatusRegister, 0); // save the value
do {
fnFlexSPI_Command(0, NOR_CMD_LUT_SEQ_IDX_READSTATUSREG, 1, &ucStatusRegister, 0, 0); // read the status register
} while ((ucStatusRegister & STATUS_BUSY) != 0); // repeat until no longer busy
}
whereby you probably already have entries and examples for reading the status register and enabling its write but you may be missing an entry and method for programming the status register.
Since I don't use the SDK I can't explain exactly how it can be modified there but my LUT entry looks like this (it is a bit more understandable than the SDK method so can probably be easily transferred to it):
{ // Write status register
NOR_CMD_LUT_SEQ_IDX_WRITESTATUSREG,
{
FLEXSPI_LUT_1PAD_SEQ_1(FLEXSPI_Command_SDR, WRITE_STATUS_REGISTER) | FLEXSPI_LUT_1PAD_SEQ_2(FLEXSPI_Command_WRITE_SDR, 0x04),
0,
},
},
whereby the entry is put to any free LUT location (remembering its index - eg. I'll use index 8 for example purposes)
The low level details (the way it works for any implementation at the bottom level) are as follows, whereby the LUT entry in this case is in fact a single long word of 0x20040401 (and a second long word of 0x00000000 which signified that the sequence has ended already).
1. The IPCR1 is written with a value indicating the LUT entry that should control the sequence (8 in example) and the length of the data to be sent (1 since this sequence will just set a single new register value) - 0x00080001 (is the actual register write value)
2. The sequence is triggered by writing TRG (0x00000001) to IPCMD.
3. The FlexSPi now starts interpeting the LUT entry 8 (containing the sequence to write to the status register) and recognises (from the LUT entry pattern) the first instruction:
Command = 0x01 (write status register command)
Op = 0x01 (command SDR)
pads = 0x00 (in this case I have used a single line command rather than a quad command and many QSPI flash commands can accept a quad and single line version)
which causes the FlexSPI to:
assert the CS line and write the command 0x01 (Write Status Register command - also compatible with your chip)
4. It then interprets the second part of the specified LUT sequence, which is
Command = 0x04
Op = 0x08 (Command write SDR)
pads = 0x00
which causes it to send data that your driver interface will write to (or have written to) the FlexSPI Tx FIFO (TFDR0), although only one byte will have been written for a write status register sequence. Note that the data length is not contained in the LUT but instead was set in the IPCR1 (see above) to 1 so the FlexSPI know that it must one send 1 byte when executing this step of the LUT sequence. After sending the single byte (and since the LUT sequence has already ended - second long word is 0) it terminates by negating the chip select line.
Based on this information it should be possible to interpret the LUT and driver sequence in order understand also how to modify SDK code and to generate and control more complicated sequences when needed.
Beware that in the case of setting new non-volatile (protection) settings in the status register that it takes some time for the operation to terminate and (see code above) it is best to poll the status register's busy flag until it shows that it has completed.
Beware also that if you use NXP-MCU Boot Utility to program (or reprogram) boards that it may fail if the QSPI flash is write protected and no more programming is possible. I found that it couldn't handle (nor unlock) a protected QSPI case and it was necessary to remove the protection by some other means before the board could be programmed again using this tool (I am not 100% sure since I avoided it after that experience so didn't repeat such tests, but it might be an issue).
If you don't yet have a complete field updater and protection concept for your product take a look at the uTasker loader which can be used with any application and gives zero-effort On-the-Fly decryption protection for XiP based code too:
- i.MX RT 1062: https://www.utasker.com/iMX/RT1060.html
- Boot loader concept including XiP on-the-fly decryption, clone protection or AES256 protected RAM execution.
-- Boot Loader concept flow chart: https://www.utasker.com/docs/iMX/Loader.pdf and usage reference https://www.utasker.com/docs/iMX/uTaskerLoader_TestDrive.pdf
-- Serial Loader features: https://www.utasker.com/docs/uTasker/uTaskerSerialLoader.pdf
-- Building the loader with MCUXpress: https://www.utasker.com/docs/iMX/MCUXpresso.pdf (and video guide https://youtu.be/p_eUGo6GypY ) - the guide document explains how to use with any application (eg. SDK) and to enabling its operation with On-The-Fly decryption in 5 minutes
-- Building the loader with IAR: https://www.utasker.com/docs/iMX/IAR.pdf (and video guide https://youtu.be/XPCwVndP99s )
-- Building the loader with VisualStudio and GCC: https://www.utasker.com/docs/iMX/GCC.pdf (and video guide https://youtu.be/0UzLLSXABK8 )
Regards
Mark
uTasker project developer for Kinetis and i.MX RT]
Contact me by personal message or on the uTasker web site to discuss professional training or product development requirements