Porting SPI flash from SPI to QSPI

Go To Last Post
19 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 1

I am trying to move my Spansion S25FL127 SPI Flash Memory device on M7/E70 from Standard SPI interface to QSPI and observed several Problems I'd like to introduce and discuss in this thread. I'm not an SPI expert but I as far as I remember it wasn't a big problem running this Flash device on SPI0 using Interrupts, XDMA and other stuff. Sadly using QSPI this is not so easy as expected.

 

I thought it is best to start with the QSPI ASF demo for S25xxx devices which came with ASF 3.34.0 for V71 XULT board. I adapted it to my custom E70 board and tried both modes: Standard SPI and Serial Memory with Little success.

 

I seems to me that there are several restrictions and bugs in the QSPI stuff.

 

1. QSPI hangs with long DLYCS 

There is a bug in the M7 core you should know about when porting from SPI to QSPI, see chapter 61.10 in the E70 Manual.

 

2. DLYBCT must be set to 0 in Serial mode

See chapter 41.6.4 in the E70 Manual.

 

3. ASF QSPI bug

Sending multiple Bytes to SPI slave Fails on my side. Only the first Byte is sent. In the ASF source file qspi.c the following code might introduce a bug. If the empty bit is not set writing the Byte to send Register is skipped but the data is lost because the Counter is incremented by mistake.

  for(; num_of_bytes_write < num_of_bytes; num_of_bytes_write++) {
   if (qspi->QSPI_SR & QSPI_SR_TXEMPTY) {
    qspi_write_spi(qspi, (uint16_t)(*pw_data));
    pw_data += Addr_Inc;
    num_of_attempt = 0;
    status = STATUS_OK;
   } else {
    status = STATUS_ERR_BUSY;
    num_of_attempt++;
    if(num_of_attempt > 0xFFFF) {
     status = ERR_TIMEOUT;
     break;
    }
   }
  }

4. ASF demo not using DMA

In the source demo pack for V71 which can be downloaded from Atmel site there is a demo for QSPI using XDMA. But this code seems to be discontinued and the current ASF Version 3.34.0 does not provide DMA.

 

I'll get back to you soon with additional observations and some questions.

 

 

SAME newbie

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 1

5. No multiple slaves Support on QSPI

No way to configure QIO2 and QIO3 for use for additional Hardware CSs. As a Workaround you can configure These pions as GPIOs and do you CS in Software.

 

6. Hardware reset feature missing

On SPI Flash the Reset# pin is shared with QIO3. Try to do a Hardware reset by temporarily configuring the QIO3 as GPIO and provide reset.

 

7. Hardware protection missing

On SPI Flash the Hardware write protection pin is shared with QIO2. Don't know how to provide Hardware protection.

 

8. Baudrate clock must be higher than 1 MHz

For slow communication paths e.g. using optocouplers, long distances, noisy environment etc. I'd prefer 500kHz. QSPI Init() returns an error for clock slower than ca. 1Mhz.

 

9. No ASF demo for QSPI in Standard SPI mode

Regarding the demo it seems to me that the QSPI Interface is not intended for used in Standard SPI mode.

 

 Am I missing something ? Comments ? To be continued...

  

SAME newbie

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

10. ASF Status Register write fails

The S25xx Flash has 3 Registers for status info and configuration. The WRR(0x01) command supports 1, 2 or 3 Byte write but due to the ASF source code file s25fl1xx.c a WRR command followed by 2 and 3 byte data is rounded up to 4 Byte write.

static void s25fl1xx_exec_command(struct qspid_t *qspid, uint8_t instr, uint32_t *tx_data, uint32_t *rx_data, enum qspi_access read_write, uint32_t size)
{
:
    /** To prevent unaligned access */
    if((size % sizeof(uint32_t)) && size > 1) {
        size += (sizeof(uint32_t) - (size % sizeof(uint32_t)));
    }

Then the Register write is ignored, no bit is set or cleared. The programming error PE bit is not set.

 

(to be continued)

SAME newbie

Last Edited: Fri. Jul 7, 2017 - 10:50 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

11. Unpredictable behavior of QSPI in debugger mode

I noticed weird behaviour if the QSPI demo is debugged. I am using AS 7.0.1417 and SAM-ICE via SWD. The following screenshots show the Read Jedec command which is used here to read the first 4 Bytes of vendor info.

If the app is run without breakpoints or started without Debugging then the scope Shows exactly what I expected: CS going active, command Byte 0x9f, 4 Bytes Jedec data 0x01 (= Spansion), 0x20, 0x18, 0x4d and finally CS going inactive.

 

This is also OK if I run the demo in the Debugger using single step or breakpoints AND the LOCALS debug window is not visible in AS. Here for example the WATCH window is visible and the returned uint32 variable contains the expected value 0x4d182001 as shown in this screenshot below:

 

 

But if I switch now from the watch window to the local variable view the variable content of ulId changes on the next step over the jedec read() as shown in the next sceenshot:

 

 

Now in the scope 4 additional bytes appear after the jedec read() info bytes.

 

 

I assume that the debugger causes a second "SPI xfer" of 4 Bytes. Due to the fact that the CS is not toggled between the first 4 Bytes and the following additional 4 bytes the SPI Flash returns additional bytes of the vendor info which now overwrite the function return value by mistake.

 

Maybe this is a debugger problem ? I don't know a workaround for debugging my application. Does anybody know what is going on here ?

 

P.S: If I comment out the "memory" variable which initially points to the QSPI Memory start address then the problem is gone. Is this a debugger or AS problem ?

 uint8_t *memory = (uint8_t *)QSPIMEM_ADDR;

 

SAME newbie

Last Edited: Fri. Jul 7, 2017 - 12:48 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

12. Write Quad missing in ASF demo

I implemented write quad command in order to write nibbles using QIO0-QIO3. I adapted the write single() function slightly (using the SPI Flash command 0x32 and initializing the QSPI using 4 I/O pins). Note that width must be set before each page write command because it is reset to single bit SPI mode after transfer. Here is the code if somebody is interested in

 uint8_t s25fl1xx_write_quad(struct qspid_t *qspid, uint32_t *pdata, uint32_t size, uint32_t address, uint8_t secure)
{
    uint32_t i = 0;

     /** Size / page_zize */
    uint32_t  number_of_writes = (size >> 8);
    uint32_t addr = address;

    /** If less than page size */
    if(number_of_writes == 0) {
        mem->inst_frame.bm.b_width = QSPI_IFR_WIDTH_QUAD_OUTPUT;
        s25fl1xx_enable_write(qspid);
        s25fl1xx_wait_memory_access_end(qspid);
        s25fl1xx_memory_access(qspid, S25FL1XX_BYTE_PAGE_PROGRAM_QUAD , addr, pdata, 0,  QSPI_WRITE_ACCESS, size, secure);
        s25fl1xx_wait_memory_access_end(qspid);
        s25fl1xx_disable_write(qspid);
    } else {
        /** Multiple page */
        for(i=0; i< number_of_writes; i++) {
            mem->inst_frame.bm.b_width = QSPI_IFR_WIDTH_QUAD_OUTPUT; // is reset to single after transfer, must be set always before quad transfer
            s25fl1xx_enable_write(qspid);
            s25fl1xx_wait_memory_access_end(qspid);
            s25fl1xx_memory_access(qspid, S25FL1XX_BYTE_PAGE_PROGRAM_QUAD , addr, pdata, 0, QSPI_WRITE_ACCESS, PAGE_SIZE, secure);
            s25fl1xx_wait_memory_access_end(qspid);
            s25fl1xx_disable_write(qspid);
            pdata += (PAGE_SIZE >> 2);
            addr += PAGE_SIZE;
        }
        if(size % PAGE_SIZE) {
            mem->inst_frame.bm.b_width = QSPI_IFR_WIDTH_QUAD_OUTPUT;
         s25fl1xx_enable_write(qspid);
            s25fl1xx_wait_memory_access_end(qspid);
            s25fl1xx_memory_access(qspid, S25FL1XX_BYTE_PAGE_PROGRAM_QUAD , addr, pdata, 0, QSPI_WRITE_ACCESS, (size - (number_of_writes * PAGE_SIZE)), secure);
            s25fl1xx_wait_memory_access_end(qspid);
            s25fl1xx_disable_write(qspid);
        }
    }
    return 0;
}

 

SAME newbie

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

13. Quad write command requires extra handling

Quad write (code 32h) vs Page write (code 02h) requires extra code to handle data append to partially written pages. See S25FLxx Manual:

 

Page Program (PP 02h or 4PP 12h)

If less than a page of data is sent to the device, these data bytes will be programmed in sequence,

starting at the provided address within the page, without having any affect on the other bytes of the same page.

 

Quad Page Program (QPP 32h or 38h, or 4QPP 34h)

QPP requires programming to be done one full page at a time. While less than a full page of data may be

loaded for programming, the entire page is considered programmed, any locations not filled with data will be

left as ones, the same page must not be programmed more than once.

  

SAME newbie

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

We also found some problems using SAM-ICE with debugger view that some values are not read correctly (simple test: write a long array of 0x55 into memory and then see in Memory View some incorrect values). Another problem is in Device Programming when reading flash that reads a different hex image every time. Atmel-ICE doesn't have that problem. 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

My debugger is blue and is called "ATMEL SAM-ICE" and made by Segger.

Looks like this one here:

 

http://www.atmel.com/Images/SAM-ICE.gif

 

Which one do you have and which one has the problems you've found out ?
 

SAME newbie

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I have that one you displayed in the picture and has the problems. I also have the white Atmel-ICE (from Atmel/Microchip), which didn't have these problems, it looks like this one:

http://uk.farnell.com/microchip/...

 

Did you get QSPI working in SPI mode with XDMA? Does it work with external RAM?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

This is valuable information. I'll buy one as soon as possible. Thanks!

 

I had SPI flash on SPI1 running with XDMAC before. Now I have the same flash memory connected to the QSPI pins and it runs in memory mode. The ASF code does not use XDMAC but as I understand the ASF code the data transfer is done like memcpy() and should therefore less CPU consuming than SPI (no RDY bit polling necessary). Am I wrong ? I use slow SPI clock rate and in my application (software update via ethernet) the flash write is more than twice as fast as before. My application is using internal SRAM.

 

I tried the SPI mode of QSPI too and as far as I remember it was OK after I fixed the problem in 3.)

SAME newbie

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

For me QSPI (SPI mode) was working fine with XDMA when buffer was in SRAM, but when using SDRAM occasionally the reading from flash hangs while still 4 more bytes need to be received. 

I will move to memory mode at some point, just concerned that developers need to ensure 4 byte alignment for the buffers sent to QSPI and the Note 41.5.4 in datasheet.

Most likely ASF does some polling for memcpy to QSPI (wait_memory_access_end) so I would use XDMA instead with interrupt at the end of the transfer that would set a semaphore (if you use a RTOS). 

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I guess _qspi_memcpy() is pretty fast and therefore I simply put the flash task to sleep (task_delay) if some things needs a certain time to complete. But you are right when you say that XDMAC is better for RTOS and I will check this.

SAME newbie

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Seems this issue went away after reducing QSPI bus frequency from 50MHz to 37.5MHz (150MHz/4).  The flash chip (S25FL256S) is capable of 50MHz in serial mode so I'm now puzzled why it made a difference. Looking with the scope at 50MHz clock, I was getting the exact number of clocks required for transmit to read back the page. I don't consider this issue solved, but for now I will have to go with it until I have the time to move to QSPI memory mode.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Which issue ?

SAME newbie

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

My issue with QSPI was that XDMA was working fine when buffer was in SRAM, but when using SDRAM occasionally the reading from flash hangs while still 1, 4 more bytes need to be received. Now they seem gone with lower frequency but I'm suspecting some issues with our SDRAM when we move heap there though and can't figure them out yet...

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hello, looks like you also had the same issue. 

I have e70q21 nad is25lp064. QSPI init in memory mode.

 

Now I am trying to get NOR flash on QSPI bus works. 

When I am trying to write 4 bytes to flash,  QSPI controller generate frame like below.
Does anyone have any idea? Whats wrong?

 

 

Below frame with comand erase, it also has extra bytes.

 

Thanks in advance.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hi, I don't think it's the same issue, in our case QSPI was set in SPI mode and XDMA was not transferring last 1-3 bytes, but we didn't observed this issue anymore when we selected QSPI bus speed as BOARD_MCK/4.

This is our initialisation for QSPI:

	Qspi *m_qspi = QSPI;
	bool bQspi = false; //SPI mode
	uint8_t spimode = SPI_MODE_0; //for Spansion NOR flash
	uint16_t bps = BOARD_MCK/4; //missing bytes with any other divider
	uint8_t charlen = 8; 

	if (m_qspi == QSPI)
	{
		PMC_EnablePeripheral(ID_QSPI);

		if (bQspi == false)
		{
			/** Pins to configure for the application in SPI mode. */
			static const Pin spi_pins[] = PINS_QSPI_SPIMODE;	//defines 6 pins (CK, CS, IO0, IO1, hold, reset)
			PIO_Configure(&spi_pins[m_CS], PIO_LISTSIZE(spi_pins));//setup CS pin for SPI0
		}
		else
		{
			/** Pins to configure for the application in QSPI memory mode. */
			static const Pin spi_pins[] = PINS_QSPI;	//defines 6 pins (CK, CS, IO0, IO1, IO2, IO3)
			PIO_Configure(&spi_pins[m_CS], PIO_LISTSIZE(spi_pins));//setup CS pin for SPI0
		}
	}
	else
	{
		configASSERT(false);
		return;//unsupported interface
	}

	QSPI_Disable(m_qspi);
	QSPI_SwReset(m_qspi);

	uint32_t config = bQspi ? QSPI_MR_SMM_MEMORY : QSPI_MR_SMM_SPI;
	QSPI_Configure(m_qspi, (config |			//SPI Mode (QSPI for instructions)
							QSPI_MR_LLB_DISABLED |		//no loopback
							QSPI_MR_CSMODE_LASTXFER |
							QSPI_MR_DLYBCT(0) |				//no delay between consecutive transfers
							QSPI_MR_DLYCS (20) |
							(charlen == 16 ? QSPI_MR_NBBITS_16_BIT : QSPI_MR_NBBITS_8_BIT)));

	QspiClockMode_t  ul_reg_val;
	switch (spimode)
	{
		case SPI_MODE_0:
		ul_reg_val = ClockMode_00;
		break;

		case SPI_MODE_1:
		ul_reg_val = ClockMode_10;
		break;

		case SPI_MODE_2:
		ul_reg_val = ClockMode_01;
		break;

		case SPI_MODE_3:
		ul_reg_val = ClockMode_11;
		break;

		default:
		return;//incorrect mode
	}

	QSPI_ConfigureClock(m_qspi, ul_reg_val,	QSPI_SCR_SCBR((BOARD_MCK / bps)- 1) | QSPI_SCR_DLYBS(0));
	//QSPI_ConfigureCs(m_qspi, 0);
	QSPI_Enable(m_qspi);

 

Last Edited: Thu. Mar 29, 2018 - 08:31 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Ok. The problem was with MPU. When you using QSPI controller in serial memory mode.... read below

 

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

davidluca3000 wrote:

Hi, I don't think it's the same issue...

 

Thank you for suggestion and your time.