How to use QSPI-NAND flash for Non Volatile memory to store backup data while system configured for QSPI boot.

cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 

How to use QSPI-NAND flash for Non Volatile memory to store backup data while system configured for QSPI boot.

Jump to solution
6,027 Views
abdulnihad
Contributor III

Hi,

I have developed an mqx 4.0.2 based audio application using vybrid tower system.I am using DS5 and Vybrid tower system for my development. my application boot from QSPI-NAND flash.

In my application I want to save the state of my application in QSPI-NAND flash. SO I need to do two things here,

1) Boot the system from QSPI-NAND Flash

2) Store/save state of my application in QSPI Flash while running.

I am using qspi sample code from MQX (C:\Freescale\Freescale_MQX_4_0\mqx\examples\qspi) to save data in QSPI flash.

Is this possible to use QSPI for both purpose(booting and storing) together at a time.

For storing data I want to use 4KB sector.

It shows busy when I try to write my data in QSPI Flash after booting and running my application from QSPI FLash.

Do I need to do erase before wring any data. In this case How to erase only one sector of size 4 KB (). I tried with below code but not erasing the sector.

#define CMD_PAR_SEC_ERASE       0x20

#define QuadSPI_CHIP_4K_SEC_ERASE  QuadSPI_SEQ(QuadSPI_INST_CMD, QuadSPI_SINGLE_PAD, CMD_PAR_SEC_ERASE)

#define NVM_ADDR                 0x20030000

#define NVM_SEC_ADDR            0x030000

qspi_mem_4kSec_erase(p->qspifd,NVM_ADDR, NVM_SEC_ADDR);

int_32 qspi_mem_4kSec_erase(MQX_FILE_PTR qspifd, uint_32 addr, uint_32 sector_addr)

{

    _mqx_int result;

    uint_8 buffer[4+QuadSPI_ADDR_BYTES] = {0};

    uint_8_ptr src_ptr = (uint_8_ptr)sector_addr;

    /* Enable flash memory write */

    qspi_mem_set_write_en(qspifd, addr, TRUE);

    /* Send erase command */

    buffer[0] = QuadSPI_CHIP_4K_SEC_ERASE & 0xFF;

    buffer[1] = (QuadSPI_CHIP_4K_SEC_ERASE >> 8) & 0xFF;

    buffer[2] = QuadSPI_LOOKUP_STOP & 0xFF;

    buffer[3] = (QuadSPI_LOOKUP_STOP >> 8) & 0xFF;

    sector_addr_to_data_buf((uint_32) src_ptr, &(buffer[4]));

    /* Write instruction */

    result = fwrite(buffer, 1, 4 + QuadSPI_ADDR_BYTES, qspifd);

    /* Wait till the flash is not busy at program */

    qspi_mem_wait_for_not_busy(qspifd);

    printf("Erase chip ... ");

    if (result < 0) {

        printf("ERROR\n");

        return -1;

    }

    printf("QuadSPI Successfully Erase Flash\n");

    return 0;

}

BR/-

Nihad

Labels (5)
1 Solution
3,018 Views
juangutierrez
NXP Employee
NXP Employee

Hi

Do I need to do erase before wring any data. In this case How to erase only one sector of size 4 KB (). I tried with below code but not erasing the sector.


Yes, you need to erase the sector before writing on it. I'm not familiar with MQX implementation but I have done it for u-boot.

Find attached the qspi driver file implemented for u-boot as reference.


For erasing the sector you need to first enabling the writing mode and then erase the sector.

The writing enable and erasing sector are routines that must be included in your LUT during qspi initialization. When you need to execute one of these routines you should point to the corresponding index by the QSPI_IPCR[SEQID] field.


void quadspi_erase_sector(unsigned int addr)

{

      debug ("Erasing QuadSPI flash addr=0x%x\n",addr);

      qspi->sfar = addr;

      /*write enable */

      qspi->ipcr = 1 << 24;  // This point to seqId = 1 in LUT corresponds to write enable

      while(qspi->sr & 0x1);

      /*send erase sector command */

      qspi->ipcr = 7 << 24; // This point to seqId = 7 in LUT corresponds to erase sector

      while(qspi->sr & 0x1);

      quadspi_wait_while_flash_busy();

}

static void quadspi_setup_lut (void)

{

  . . .

      /* seqid 1 - write enable  */

      qspi->lut[4] = 0x406;

  . . .

      /* seqid 7 - sector erase  */

      qspi->lut[28] = 0x081804d8;

    . . .

}

View solution in original post

0 Kudos
16 Replies
3,019 Views
juangutierrez
NXP Employee
NXP Employee

Hi

Do I need to do erase before wring any data. In this case How to erase only one sector of size 4 KB (). I tried with below code but not erasing the sector.


Yes, you need to erase the sector before writing on it. I'm not familiar with MQX implementation but I have done it for u-boot.

Find attached the qspi driver file implemented for u-boot as reference.


For erasing the sector you need to first enabling the writing mode and then erase the sector.

The writing enable and erasing sector are routines that must be included in your LUT during qspi initialization. When you need to execute one of these routines you should point to the corresponding index by the QSPI_IPCR[SEQID] field.


void quadspi_erase_sector(unsigned int addr)

{

      debug ("Erasing QuadSPI flash addr=0x%x\n",addr);

      qspi->sfar = addr;

      /*write enable */

      qspi->ipcr = 1 << 24;  // This point to seqId = 1 in LUT corresponds to write enable

      while(qspi->sr & 0x1);

      /*send erase sector command */

      qspi->ipcr = 7 << 24; // This point to seqId = 7 in LUT corresponds to erase sector

      while(qspi->sr & 0x1);

      quadspi_wait_while_flash_busy();

}

static void quadspi_setup_lut (void)

{

  . . .

      /* seqid 1 - write enable  */

      qspi->lut[4] = 0x406;

  . . .

      /* seqid 7 - sector erase  */

      qspi->lut[28] = 0x081804d8;

    . . .

}

0 Kudos
3,018 Views
juangutierrez
NXP Employee
NXP Employee

The LUT command can be seen as   CMD for ERASE_64K using 1Pad, the ADDR will be 24bits


qspi->lut[28] = 0x081804d8

INSTR     PINS  OPERAND   INSTR     PINS    OPERAND

b000010  b00    0x18            b000001  b00      0xD8             = 0x081804d8

ADDR      1Pad    24             CMD     1Pad   Erase 64K

The command operand used in MQX is CMD_PAR_SEC_ERASE = 0x20 which is Erase4K.

I recall having some issue with this command (0x20), but not remember exactly what was the problen :smileysad:, then I switch to Erase64K (0xD8) and it worked fine for me. Maybe you can try to use CMD_PAR_SEC_ERASE = 0xD8 and see if it helps.

0 Kudos
3,018 Views
abdulnihad
Contributor III

My application is sitting in 0x20000800 QSPI flash address. SO I want to use 0x20030000 as my NVM back up space to store status data.

So I assume that 24 bit sector address which I want to erase is 0x030000

APP_BASE_ADDR       0x20000800

NVM_FLASH_BASE     0x20030000

SECTOR_ADDR          0x00030000

How to pass address of sector to be erased?. Do I need to pass both address 32 bit flash base address and 24 bit sector address.

In below line you are not passing 24 bit sector address

qspi->lut[28] = 0x081804d8

INSTR     PINS  OPERAND   INSTR     PINS    OPERAND

b000010  b00    0x18            b000001  b00      0xD8             = 0x081804d8

ADDR      1Pad    24             CMD     1Pad   Erase 64K

Suppose if I want to set address before sending 0x081804d8 as shown below, which address should I set 0x20030000 or 0x030000

debug ("Erasing QuadSPI flash addr=0x%x\n",addr);

qspi->sfar = addr;

BR/-

Nihad

0 Kudos
3,018 Views
kef2
Senior Contributor IV

You need to write to SFAR real Vybrid address. QSPI first compares SFAR=xx against values in SF??AD registers to determine which chip should be addressed, then calculates SPI memory addresses as a difference between base address and address you write to SFAR.

3,017 Views
juangutierrez
NXP Employee
NXP Employee

Yes, like Edward says you need to write the address in the QSPI_SFAR regsiter.

Just take into account that qspi->lut[28] = 0x081804d8 is a friendly instruction for the QSPI Sequence Engine, which abstract the interaction with the SPI data lines, so this instruction will be translated into the bit-stream in the SO/IO signal or in the IO[0:3] signals (the latter in case more than 1 Pad is specified in the PINS field).

The address will be taken from the SFAR register. In this case (ADDR      1Pad    24), you command to take only 24bit from this register, and as Edward said the MSB are used to determined with Flash device should be selected.

The SPI signal will be look like below

qspi_erase_inst.png

0 Kudos
3,018 Views
abdulnihad
Contributor III

Hi Edward and Juan,

As you suggested I have erased 64K sector instead of 4K. Now My application able to erase and write new data to QSPI serial Flash at 0x20030000 when I boot my application from internal SRAM.

But when I boot from QSPI serial FLash I am not able to erase 64K sector.  Before erasing I want to make write enable. but while I am doing that Reading status register returned error and it print  'memory_read_status1: ERROR (rx)'. I am copying few code lines below. As Edward pointed in early post, erasing sector while vybrid executing(reading) application from QSPI serial flash is not permissible. Any way to fix this issue without moving code to SRAM. Also let me know how to make application load region in QSPI flash and execution region in internal SRAM. I am Also attaching my scatter file which I used for QSPI_XIP boot.

boolean qspi_mem_read_status1(MQX_FILE_PTR qspifd, uint_8_ptr status)

{

    _mqx_int result;

    uint_8 temp = *status;

    uint_8 buffer[6 + QuadSPI_ADDR_BYTES] = {0};

    buffer[0] = QuadSPI_READ_STATUS1 & 0xFF;

    buffer[1] = (QuadSPI_READ_STATUS1 >> 8) & 0xFF;

    /* 1 byte status-1*/

    buffer[2] = QuadSPI_READ_DATA(1) & 0xFF;

    buffer[3] = (QuadSPI_READ_DATA(1) >> 8) & 0xFF;

    buffer[4] = QuadSPI_LOOKUP_STOP & 0xFF;

    buffer[5] = (QuadSPI_LOOKUP_STOP >> 8) & 0xFF;

    /* Write instruction */

    result = fwrite(buffer, 1, 6 + QuadSPI_ADDR_BYTES + 1, qspifd);

    if(result < 0) {

      /* Stop transfer */

        printf("ERROR (tx)\n");

        return FALSE;

    }

    /* Read memory status: 1byte */

    result = fread (status, 1, 1, qspifd);

    if(result < 1) {

        printf ("memory_read_status1: ERROR (rx)\n");

        *status = temp;

        return FALSE;

    }

    return TRUE;

}

void qspi_mem_wait_for_write_en(MQX_FILE_PTR qspifd)

{

    uint_8 status = 0x0;

    while(!(status & 0x2)) {

        if (qspi_mem_read_status1(qspifd, &status) == FALSE)

            status = 0x0;

    }

}

BR/-

Nihad

0 Kudos
3,017 Views
kef2
Senior Contributor IV

Abdul,

erase and program while executing from QSPI is possible in two cases: 1) you move/copy your erase/program routines to RAM and execute from there, 2) preload your erase/program routines in I- and D- cache. D-cache should be involved, since most likely your compiler puts some constant addresses in code and loads them as data. I had success going 2nd way on small experimental program, which was smaller than available I- and D- cache sizes. It may be tricky to use the same approach on larger programms. You need good understanding of what is cached when and how to prevent cache reloads from QSPi space, which could break your program/erase routines in cache. Since sudden interrupt may make access to vector table in QSPI and to ISR in QSPI, interrupts should be disabled while you erase or program.

Don't forget to exit continuous read mode (mode bits = 0xA?) before launching any non-read flash command.

3,018 Views
abdulnihad
Contributor III

Could you please share any sample code which will do option1 or option2. Could you please suggest any document which will help me to understand how to use cache

BR/-

Nihad

0 Kudos
3,018 Views
kef2
Senior Contributor IV

Dear Abdul,

Unfortunately I have nothing ready for public domain. Regarding option 2 I'm not sure if I'm doing it right. Code that worked in cache is assembly coded and executes two times. One time with argument that tells it to not do any harm to QSPI, and 2nd time to program/erase. Two passes make code 100% preloaded in cache. I need to come back to it and review, I think I'll switch to more easy and reliable option 1.

For option 1 I don't have any program/erase code yet. I just figured how to put any C function to RAM using DS-5. I must tell I hate DS-5 and all other ARM.com documentation pretty much. It is nightmare to find one piece of info, then find 2nd small piece of info. I was quite frustrated figuring how to make working zero init and initialization of static storage variables in this compiler. Certainly this standard C requirement shouldn't be so hard to figure for people not familiar with DS-5. And here's my know how for zero init, static vars init, foo() allocating in RAM and initializing from ROM at program startup:

1) DS-5 startup files in library make call to __scatterload function, which initializes ZI and RW RAM areas, provided you are using proper *.scat file with separated root and non root load regions. After initializing RAM, __scatterload jumps to main(). It is quite odd, because someone would like to do some extra low level initialization before jumping to main(), but it is how DS-5 is handling it.

DS-5 bare metal examples use "wrong" scat files.

DRAM 0x40200000 0x10000

{

APP_CODE +0

    {

        * (+RO, +RW, +ZI)

APP_CODE is root load region (i think). Having RW and ZI in root region makes RAM initialization not working.

DRAM 0x40200000 0x10000

{

APP_CODE +0

    {

        * (+RO)

   }

  APP_DATA +0

  {

     *(+RW, +ZI)

}

We have the same situation here ^^. APP_DATA still belongs to root load region. To make __scatterload working, we need to replace +0 with absolute address like 0x3F008000
.

I tried searching MQX sources for __scatterload call and didn't find it. Either MQX uses their own .bss and .data initializatiion, or disables ANSI C compatibility... I may try looking at it later, at the moment I don't know how to init RAM vars in MQX.

For bare metal I'm replacing these two lines in crt.s

0 Kudos
3,018 Views
kef2
Senior Contributor IV

   import _main

   BL       _main

with

   import __scatterload

   BLX   __scatterload  ; initializes ZI, RW and calls main()

2) Once ZI and RW initialization is working, it is piece of cake to move C functions to RAM

#pragma arm section code = "rw_code"

whatever function you put between these pragmas

// will be allocated in rw_code and initialized from ROM in __scatterload call

foo() {}

#pragma arm section code

Scatter file should  include rw_code and may look like this

LoadRegion 0x3F000000

    ROOT +0

   {

      * (vectors_section, +FIRST; Vector table and other (assembler) startup code

      * (InRoot$$Sections)  ; All (library) code that must be in a root region

      *(+RO-DATA) 

      *(+RO-CODE)}

   }


   RW_DATA 0x3F400000

    {

        * (+RW, +ZI,  rw_code)   ; rw_code placed in RAM

   }

...

}


To verify if it is working try debugging not from main but form entry point. Once code is loaded, find address of foo() and overwrite it with some garbage. Then after hitting run and stopping in main() foo()'s disassembly should look right again.


Now it should be easy to move QSPI program/erase routines to RAM. Should work if you disable interrupts before program/erase and reenable after program/erase is done. I'm going to try it myself soon.

0 Kudos
3,018 Views
abdulnihad
Contributor III

Hi,

I have put all qspi related code in internal memory as you mentioned. But still application is waiting for qspi_mem_wait_for_write_en while erasing chip. It is never come out of while loop which is inside qspi_mem_wait_for_write_en function. Also Attached scatter file

int xt_nvm_write(TUNER* p)

{

    char* aligned=NULL;

    void* buf = (void*) &(p->savedState);

    int nbytes = sizeof(p->savedState);

    _int_disable();

    /*Erasing flash 64K Sector before write*/

    printf("Erasing NVM QSPI region.....\r\n");

    qspi_mem_Sec_erase(p->qspifd,NVM_ADDR, SECTOR_64K);

    _time_delay(500);

    if ((uint_32)buf & 0x03) {

        // sqi write needs word aligned buffer (malloc will give this)

        printf("Re-align write buffer %p\r\n",buf);

        aligned=malloc(nbytes);

        if (!aligned) {

            printf("allocate %d bytes failed\r\n",nbytes);

            return -1;

        }

        memcpy(aligned,buf,nbytes);

        buf = aligned;

    }

    //printf("Write %d bytes from %p\r\n",nbytes,buf);

    qspi_mem_write_data (p->qspifd, NVM_ADDR, nbytes, buf);

    if (aligned)

        _mem_free(aligned);

    _int_enable();

    return 0;

}

int xt_nvm_read(TUNER* p)

{

    void* buf = (void*) &(p->savedState);

    int nbytes = sizeof(p->savedState);

    _int_disable();

    qspi_mem_read_data(p->qspifd, NVM_ADDR, nbytes, buf);

    _int_enable();

    return 0;

}

/*Below code sits in qspi_code internal memory*/

#pragma arm section code = "qspi_code"

_mqx_int qspi_mem_open(HTUNER h)

{

    TUNER* p = (TUNER*)h;

    _mqx_int    errcode = 0;

    printf ("\nQSPI Flash NVM Region Open\n");

    /* Open the QSPI driver */

    p->qspifd = fopen (QSPI_CHANNEL, NULL);

    if (p->qspifd == NULL) {

        printf ("Error opening QSPI Flash driver!\n");

        _time_delay (200L);

        errcode = MQX_ERROR;

    }

    return errcode;

}

void qspi_mem_wait_for_not_busy(MQX_FILE_PTR qspifd)

{

    uint_8 status = 0x01;

    while(status & 0x1) {

        if (qspi_mem_read_status1(qspifd, &status) == FALSE)

            status = 0x01;

    }

}

void qspi_mem_wait_for_write_en(MQX_FILE_PTR qspifd)

{

    uint_8 status = 0x0;

    printf("Waiting for write enable bit\r\n");

    while(!(status & 0x2)) {

        if (qspi_mem_read_status1(qspifd, &status) == FALSE)

            status = 0x0;

    }

}

static void qspi_mem_set_write_en(MQX_FILE_PTR qspifd, uint_32 addr, boolean enable)

{

    _mqx_int result;

    uint_8 buffer[4+QuadSPI_ADDR_BYTES] = {0};

    result = ioctl(qspifd, QuadSPI_IOCTL_SET_FLASH_ADDR, &addr);

    if (result != MQX_OK) {

        printf("memory_write_data: failed at ioctl set flash address!\n");

        return;

    }

    if (enable) {

        buffer[0] = QuadSPI_WRITE_EN & 0xFF;

        buffer[1] = (QuadSPI_WRITE_EN >> 8) & 0xFF;

    } else {

        buffer[0] = QuadSPI_WRITE_DISABLE & 0xFF;

        buffer[1] = (QuadSPI_WRITE_DISABLE >> 8) & 0xFF;

    }

    buffer[2] = QuadSPI_LOOKUP_STOP & 0xFF;

    buffer[3] = (QuadSPI_LOOKUP_STOP >> 8) & 0xFF;

    printf("Enabling QSPI write\r\n");

    /* Write instruction */

    result = fwrite (buffer, 1, 4 + QuadSPI_ADDR_BYTES, qspifd);

    if (result < 0) {

        printf ("ERROR\n");

        return;

    }

    printf("flushing QSPI \r\n");

    /* Wait till transfer end (and deactivate CS) */

    fflush (qspifd);

    qspi_mem_wait_for_write_en(qspifd);

}

int_32 qspi_mem_Sec_erase(MQX_FILE_PTR qspifd, uint_32 addr,uint_32 sectorFlag)

{

    _mqx_int result;

    uint_8 buffer[6+QuadSPI_ADDR_BYTES] = {0};

    /* Enable flash memory write */

    qspi_mem_set_write_en(qspifd, addr, TRUE);

    /* Send erase command */

    if(SECTOR_64K == sectorFlag)

    {

        printf("Erasing 64K Flash Sector at 0x%X\r\n",addr);

        buffer[0] = QuadSPI_CHIP_64K_SEC_ERASE & 0xFF;

        buffer[1] = (QuadSPI_CHIP_64K_SEC_ERASE >> 8) & 0xFF;

    }

    else if(SECTOR_4K == sectorFlag)

    {

        printf("Erasing 4K Flash Sector at 0x%X\r\n",addr);

        buffer[0] = QuadSPI_CHIP_4K_SEC_ERASE & 0xFF;

        buffer[1] = (QuadSPI_CHIP_4K_SEC_ERASE >> 8) & 0xFF;

    }

    else //SECTOR_8K == sectorFlag)

    {

        printf("Erasing 8K Flash Sector at 0x%X\r\n",addr);

        buffer[0] = QuadSPI_CHIP_8K_SEC_ERASE & 0xFF;

        buffer[1] = (QuadSPI_CHIP_8K_SEC_ERASE >> 8) & 0xFF;

    }

    buffer[2] = QuadSPI_SET_ADDR & 0xFF;

    buffer[3] = (QuadSPI_SET_ADDR >> 8) & 0xFF;

    buffer[4] = QuadSPI_LOOKUP_STOP & 0xFF;

    buffer[5] = (QuadSPI_LOOKUP_STOP >> 8) & 0xFF;

    /* Write instruction */

    result = fwrite(buffer, 1, 6 + QuadSPI_ADDR_BYTES, qspifd);

    printf("Waiting for not busy\r\n");

    /* Wait till the flash is not busy at program */

    qspi_mem_wait_for_not_busy(qspifd);

    if (result < 0) {

        printf("ERROR\n");

        return -1;

    }

    printf("QuadSPI Successfully Erase Flash\n");

    return 0;

}

boolean qspi_mem_read_status1(MQX_FILE_PTR qspifd, uint_8_ptr status)

{

}

uint_8 qspi_mem_read_byte(MQX_FILE_PTR qspifd, uint_32 addr)

{

}

int_32 qspi_mem_write_data (MQX_FILE_PTR qspifd, uint_32 addr, uint_32 size, uint_8_ptr data)

{

}

int_32 qspi_mem_read_data (MQX_FILE_PTR qspifd, uint_32 addr, uint_32 size, uint_8_ptr data)

{

}

#pragma arm section code

0 Kudos
3,018 Views
kef2
Senior Contributor IV

Abdul,

It is not enough to move just your QSPI code to RAM. All routines, which your QSPI erase/write routines are calling also have to be moved to RAM. Remember, while flash is being erased/programmed, it is not readable. This is why you need to move your code to RAM and disable interrupts. All the code, that needs to execute while erasing/programming QSPI, has to be moved away from QSPI. Looking at your code I see printf() and fwrite() calls. Certainly you shouldn't bother moving printf to RAM and just remove all printf calls. fwrite() seems requesting QSPI operations. I guess it will be simplier to use non-MQX QSPI code, rather than moving all used OS layer routines and QSPI driver to RAM...

Did you verify that routines between #pragma arm section code pragmas are properly copied to RAM at run time? I told you already that I'm not sure if MQX allows initializing RAM variables (and code in RAM) at startup. I'm attaching my baremetal code, scat and crt.s, which are working well for me. crt.s is calling DS-5 __scatterload routine, which initializes RAM variables and code in RAM.

Please note also that my QSPI LUT table doesn't use continuous read mode (mode bits != 0xAx).

3,018 Views
abdulnihad
Contributor III

Hi,

No attachment in your reply!!!!!!

0 Kudos
3,018 Views
kef2
Senior Contributor IV

Hi

Perhaps what is attached on community www is not attached to email notifications. I still see attached zip file and it seems being downloadable.

Regards

0 Kudos
3,018 Views
kef2
Senior Contributor IV

I can confirm that booting in XIP mode and storing data in QSPI is possible.

First of all certainly you need to erase before program. This is how every flash and EEPROM memories work. Some smart if they existed (maybe some are existing already) could do erase and then program as a single program step, but real life devices require erase before program.

I'm not using MQX for this purpose, so can't comment your code. Also can't provide my own, it is not finished (I'm strugling with USB video at the moment). But I can share two important things your code needs to provide:

1). When program or erase commands are executing, most of the memory chips are not readable. And you need to feed Vybrid with code instructions even while QSPI memory is being erased or programmed. You can do one of the following 3:

   a) move all your code to RAM,

   b) move program/erase routines to RAM, disable interrupts or perhaps move vectors table and interrupt handlers to RAM so that Vybrid won't want to read QSPI while your are programming/erasing.

   c) Enable both I- and D-caches for entire program space in QSPI. Enabling D-cache for code is required, because code usually contains some constants, constant addresses stored in memory etc, which are not I-cacheable, but D-cacheable. Of course cache space isn't rubbish, you need to provide that no cache miss happens when you program/erase.

2) Don't forget to invalidate D-cache and QSPI read buffer after program. For example first you read old data at 0x20000000, then erase and program 0x20000000, then read 0x20000000 to verify. Reading certain address from QSPI, first makes QSPI reading and storing data in read buffer, then this data is cached in D-cache. After program you need to invalidate both, D-cache, and QSPI read buffer. For first use MQX invalidate routine, for second try reading QSPI address, which is not stored in QSPI read buffer.

0 Kudos
3,018 Views
kef2
Senior Contributor IV

I forgot to mention one more observation. JMP_ON_CS QSPI command allows skiping transfer of read command to QSPI memory. Instead of cmd-addr-data, sequences are shorted to addr-data, addr-data. This is fine for read-only operation. But if you are going to program, then you need to exit somehow this read command mode. For now I just removed JMP_ON_CS from LUT.

0 Kudos