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
Solved! Go to Solution.
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;
. . .
}
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;
. . .
}
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.
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
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.
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
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
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.
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
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
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.
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
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).
Hi,
No attachment in your reply!!!!!!
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
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.
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.