Forum Rule: Always post complete source code & details to reproduce any issue!
Page 1 of 7 1 2 3 ... LastLast
Results 1 to 25 of 159

Thread: NAND flash support in 1.54

  1. #1
    Senior Member
    Join Date
    Sep 2015
    Posts
    108

    NAND flash support in 1.54

    Paul mentioned adding some "official" NAND flash support in the 1.53 Beta #2 thread, initially targeting the Winbond W25N01GVZE1G. Since I'm using that in my current project and I haven't seen a planning thread I thought I'd start one.

    I've only done a proof of concept / sanity test at this point based on a seriously hacked up version of the SPIMemory library, which is GPL and therefore probably not a good general solution. I don't know if the plan would be to extend SPIFFS or use something more targeted to NAND? In my limited research the obvious choices for that would be Dhara or LittleFS.

    Based on my reading of the 1062 datasheet, it appears that we can only use the R/W "macros" on the QSPI port (and earlier Teensy chips lack this support as well), so the library will probably need variants for the chip access that support both standard SPI config and the specialized instructions?

    I'll be happy to contribute once things get started, but that would mostly just be known working SPI sequences for the W25N01GVZE1G.
    Last edited by ecurtz; 06-27-2020 at 04:04 PM. Reason: typo

  2. #2
    Senior Member+ mjs513's Avatar
    Join Date
    Jul 2014
    Location
    New York
    Posts
    5,647
    @ecurtz

    Thanks for starting this thread, kept meaning too but kept getting side tracked with other things

    Anyway @defragster posted a NXP Community question on using NAND: FlexSpi NOR and NAND Flash Configuration which gives you the idea of how to set it up with the 1062 SDK.

    I also found another project that uses the NAND chip which they developed a driver for it on the STM32: https://github.com/betaflight/betafl...f4135c04afe3fd . if you expand the diff for src/main/drivers/flash_w25n01g.c you will see the approach they took.

    I have a few of those chips on older. Once i get them I have to get the courage to try and solder them to a T4.1

  3. #3
    Senior Member brtaylor's Avatar
    Join Date
    Mar 2016
    Location
    Portland, OR
    Posts
    580
    I'd be super keen if someone got NAND flash working with Teensy. Having storage size rivaling an SD card in a soldered on solution would be awesome.

  4. #4
    Senior Member+ defragster's Avatar
    Join Date
    Feb 2015
    Posts
    12,414
    REF >> From thread :: Teensyduino-1-53-Beta-2/page3

    Paul posted this - link to chip/source and notes on potential timeline and where it goes on T_4.1 and ... "improved file & filesystem abstraction and other storage media":
    Quote Originally Posted by PaulStoffregen View Post
    Winbond W25N01GVZE1G

    winbond.com/hq/product/code-storage-flash-memory/qspinand-flash/index.html?__locale=en&partNo=W25N01GV

    digikey.com/product-detail/en/winbond-electronics/W25N01GVZEIG-TR/W25N01GVZEIGCT-ND/7393545

    I designed the wide SOIC8 footprint on Teensy 4.1 with alternatively mounting this WSON8 part onto those pads.
    ...

    Probably best to start a new thread if you want to chat about the details. Perfectly fine to discuss on this forum. But please know I will not be working on NAND flash support until we're shipping Teensy 4 bootloader chips, so don't expect me to be active on that thread. After 1.53, getting a bootloader chip released is my top priority. Realistically, I'll probably also spend some time on much-needed website updates and documentation before getting into improved file & filesystem abstraction and other storage media (the main focus planned for 1.54).

  5. #5
    Senior Member+ mjs513's Avatar
    Join Date
    Jul 2014
    Location
    New York
    Posts
    5,647
    Just by way of update and warning.

    Got my NAND chips from Digikey and just soldered up one to a T4.1. Soldering that chip is not easy, at least for me, it was a real challenge to get everything line up right. Lots of flux and fine tip soldering iron. At least when i hooked up the T4.1 with the NAND chip on it, the T4.1 still would load sketches and run sketches.

    Now to see if I can get it to work which will take some doing I think.

  6. #6
    Senior Member
    Join Date
    Sep 2015
    Posts
    108
    As I said I unfortunately began by hacking on a GPL codebase so I shouldn't just post code without carefully verifying that it's actually stuff that I wrote, but here are a few snippets to help get started verifying the chip. I assume that those bottom pads can be used as regular single channel SPI?
    Code:
    #define CMD_RESET       0xFF
    #define CMD_JEDEC_READ  0x9F
    #define CMD_STAT_READ   0x0F
    #define CMD_STAT_WRITE  0x1F
    #define CMD_WRITE_EN    0x06
    #define CMD_WRITE_DIS   0x04
    #define CMD_BAD_BLOCKS  0xA1
    #define CMD_BBM_READ    0xA5
    #define CMD_ECC_ADDR    0xA9
    #define CMD_ERASE       0xD8
    #define CMD_DATA_PROG   0x02
    #define CMD_DATA_PROG4  0x32
    #define CMD_PROG_EX     0x10
    #define CMD_PAGE_DATA   0x13
    #define CMD_READ        0x03
    #define CMD_FAST_READ   0x0B
    #define CMD_FAST_READ2  0x3B
    #define CMD_FAST_READ4  0x6B
    #define CMD_DUAL_READ2  0xBB
    #define CMD_DUAL_READ4  0xEB
    
    #define SR1_SRP0        (0x01 << 7)
    #define SR1_BP3         (0x01 << 6)
    #define SR1_BP2         (0x01 << 5)
    #define SR1_BP1         (0x01 << 4)
    #define SR1_BP0         (0x01 << 3)
    #define SR1_TB          (0x01 << 2)
    #define SR1_WPE         (0x01 << 1)
    #define SR1_SRP1        (0x01 << 0)
    #define SR1_BLOCKS      (SR1_BP0 | SR1_BP1 | SR1_BP2 | SR1_BP3)
    
    #define SR2_OTPL        (0x01 << 7)
    #define SR2_OTPE        (0x01 << 6)
    #define SR2_SR1L        (0x01 << 5)
    #define SR2_ECCE        (0x01 << 4)
    #define SR2_BUF         (0x01 << 3)
    
    #define SR3_LUTF        (0x01 << 6)
    #define SR3_ECC1        (0x01 << 5)
    #define SR3_ECC0        (0x01 << 4)
    #define SR3_PFAIL       (0x01 << 3)
    #define SR3_EFAIL       (0x01 << 2)
    #define SR3_WELL        (0x01 << 1)
    #define SR3_BUSY        (0x01 << 0)
    
    #define WINBOND_MANID    0xEF  
    #define W25N01GV              0x21
    
    // JEDEC ID is available even if chip is busy, so no need to stall
    void GetJedecID(void) {
     
       if (!spiTransactionOpen) {
          spiSettings = SPISettings(SPI_CLK, MSBFIRST, SPI_MODE0);
          SPI.beginTransaction(spiSettings);
          spiTransactionOpen = true;
       }
       
       digitalWrite(csPin, LOW); // Chip select
       
       SPI.transfer(CMD_JEDEC_READ);
       SPI.transfer(0x00);  // needs an extra dummy byte to complete command
    
       manufacturerID = SPI.transfer(0x00);	// manufacturer id
       memoryTypeID = SPI.transfer(0x00);	// memory type
       capacityID = SPI.transfer(0x00);		// capacity
     	
       digitalWrite(csPin, HIGH); // Chip deselect
    
       if (spiTransactionOpen) {
          SPI.endTransaction();
          spiTransactionOpen = false;
       }   
     }

  7. #7
    Senior Member+ mjs513's Avatar
    Join Date
    Jul 2014
    Location
    New York
    Posts
    5,647
    @ecurtz

    Thanks for posting so I can at least do a verification that i did the soldering correct. Will also help when I try to use FLEXSPI

  8. #8
    Senior Member+ mjs513's Avatar
    Join Date
    Jul 2014
    Location
    New York
    Posts
    5,647
    Quick status. Not sure I did this right, probably not or my chip is not on right Here is the start of using FLEXSPI with the NAND chip where the FLASH goes:
    Code:
    #define LUT0(opcode, pads, operand) (FLEXSPI_LUT_INSTRUCTION((opcode), (pads), (operand)))
    #define LUT1(opcode, pads, operand) (FLEXSPI_LUT_INSTRUCTION((opcode), (pads), (operand)) << 16)
    #define CMD_SDR         FLEXSPI_LUT_OPCODE_CMD_SDR
    #define ADDR_SDR        FLEXSPI_LUT_OPCODE_RADDR_SDR
    #define READ_SDR        FLEXSPI_LUT_OPCODE_READ_SDR
    #define WRITE_SDR       FLEXSPI_LUT_OPCODE_WRITE_SDR
    #define DUMMY_SDR       FLEXSPI_LUT_OPCODE_DUMMY_SDR
    #define PINS1           FLEXSPI_LUT_NUM_PADS_1
    #define PINS4           FLEXSPI_LUT_NUM_PADS_4
    
    // Config/status register addresses
    #define W25N01G_PROT_REG 0xA0
    #define W25N01G_CONF_REG 0xB0
    #define W25N01G_STAT_REG 0xC0
    
    static const uint32_t flashBaseAddr = 0x01000000u;
    static const uint32_t eramBaseAddr = 0x07000000u;
    static char flashID[4];
    
    void setup(){
        Serial.begin(115200);
        delay(1000);
        Serial.println("Begin Init");
        
        memset(flashID, 0, sizeof(flashID));
        
        FLEXSPI2_FLSHA2CR0 = 128u * 1024u * 1024u;          //Flash Size in KByte, 1F400
        FLEXSPI2_FLSHA2CR1 = FLEXSPI_FLSHCR1_CSINTERVAL(2)  //minimum interval between flash device Chip selection deassertion and flash device Chip selection assertion.
        | FLEXSPI_FLSHCR1_TCSH(3)                           //Serial Flash CS Hold time.
        | FLEXSPI_FLSHCR1_TCSS(3);                          //Serial Flash CS setup time
        
        FLEXSPI2_FLSHA2CR2 = FLEXSPI_FLSHCR2_AWRSEQID(6)    //Sequence Index for AHB Write triggered Command
        | FLEXSPI_FLSHCR2_AWRSEQNUM(0)                      //Sequence Number for AHB Read triggered Command in LUT.
        | FLEXSPI_FLSHCR2_ARDSEQID(5)                       //Sequence Index for AHB Read triggered Command in LUT
        | FLEXSPI_FLSHCR2_ARDSEQNUM(0);                     //Sequence Number for AHB Read triggered Command in LUT.
    
    
        // cmd index 7 = read ID bytes
        //Serial.println("LUT28: JDEC");
        FLEXSPI2_LUT28 = LUT0(CMD_SDR, PINS1, 0x9F) ;     //9fh Read JDEC 
        FLEXSPI2_LUT29 = LUT0(DUMMY_SDR, PINS1, 8) | LUT0(READ_SDR, PINS1, 1);
    
        // cmd index 8 = read Status register #1 SPI
        FLEXSPI2_LUT32 = LUT0(CMD_SDR, PINS1, 0x05) 
          | LUT1(CMD_SDR, PINS1, W25N01G_STAT_REG);      //5h Status Register, sr addr 0xC0
        FLEXSPI2_LUT33 = LUT0(READ_SDR, PINS1, 1);
    
        // cmd index 9 = read Status register #2 SPI
        //FLEXSPI2_LUT36 = LUT0(CMD_SDR, PINS1, 0x35) 
        //  | LUT1(READ_SDR, PINS1, 1);           //There is no status reg #2
    
        //cmd index 10 = exit QPI mode
        //FLEXSPI2_LUT40 = LUT0(CMD_SDR, PINS4, 0xFF);
    
        //cmd index 11 = write enable QPI
        //FLEXSPI2_LUT44 = LUT0(CMD_SDR, PINS4, 0x06);
    
        //cmd index 12 = sector erase
        ///FLEXSPI2_LUT48 = LUT0(CMD_SDR, PINS4, 0x20) 
        //| LUT1(ADDR_SDR, PINS4, 24);
    
        //cmd index 13 = page program
        //FLEXSPI2_LUT52 = LUT0(CMD_SDR, PINS4, 0x02) 
        //| LUT1(ADDR_SDR, PINS4, 24);
        //FLEXSPI2_LUT53 = LUT0(WRITE_SDR, PINS4, 1);
    
        //cmd index 14 = set read parameters
        //FLEXSPI2_LUT56 = LUT0(CMD_SDR, PINS4, 0xc0) 
        //| LUT1(CMD_SDR, PINS4, 0x20);
    
        //cmd index 15 = enter QPI mode
        //FLEXSPI2_LUT60 = LUT0(CMD_SDR, PINS1, 0x38);
    
        // reset the chip
        flexspi_ip_command(2, flashBaseAddr); //reset
        delayMicroseconds(150);
    
        Serial.println(); Serial.println("Status after Reset (S/B 0):");
        printStatusRegs();
    
    
        Serial.print("FLASH ID:");
        flexspi_ip_read(7, flashBaseAddr, flashID, sizeof(flashID) ); // flash begins at offset 0x01000000
    
        for (unsigned i = 0; i < sizeof(flashID); i++) Serial.printf(" %02X", flashID[i]);
        Serial.printf("\n");
        Serial.printf("at 0x %x\n", flashBaseAddr);
    
        flexspi2_flash_id(flashBaseAddr);
    }
    
    void loop(){}
    
    void flexspi_ip_read(uint32_t index, uint32_t addr, void *data, uint32_t length)
    {
      uint32_t n;
      uint8_t *p = (uint8_t *)data;
      const uint8_t *src;
    
      FLEXSPI2_IPCR0 = addr;
      FLEXSPI2_IPCR1 = FLEXSPI_IPCR1_ISEQID(index) | FLEXSPI_IPCR1_IDATSZ(length);
      FLEXSPI2_IPCMD = FLEXSPI_IPCMD_TRG;
      while (!((n = FLEXSPI2_INTR) & FLEXSPI_INTR_IPCMDDONE)) {
        if (n & FLEXSPI_INTR_IPRXWA) {
          //Serial.print("*");
          if (length >= 8) {
            length -= 8;
            *(uint32_t *)(p+0) = FLEXSPI2_RFDR0;
            *(uint32_t *)(p+4) = FLEXSPI2_RFDR1;
            p += 8;
          } else {
            src = (const uint8_t *)&FLEXSPI2_RFDR0;
            while (length > 0) {
              length--;
              *p++ = *src++;
            }
          }
          FLEXSPI2_INTR = FLEXSPI_INTR_IPRXWA;
        }
      }
      if (n & FLEXSPI_INTR_IPCMDERR) {
        Serial.printf("Error: FLEXSPI2_IPRXFSTS=%08lX\r\n", FLEXSPI2_IPRXFSTS);
      }
      FLEXSPI2_INTR = FLEXSPI_INTR_IPCMDDONE;
      Serial.printf(" FLEXSPI2_RFDR0=%08lX\r\n", FLEXSPI2_RFDR0);
      if (length > 4) Serial.printf(" FLEXSPI2_RFDR1=%08lX\n", FLEXSPI2_RFDR1);
      if (length > 8) Serial.printf(" FLEXSPI2_RFDR1=%08lX\n", FLEXSPI2_RFDR2);
      if (length > 16) Serial.printf(" FLEXSPI2_RFDR1=%08lX\n", FLEXSPI2_RFDR3);
      src = (const uint8_t *)&FLEXSPI2_RFDR0;
      while (length > 0) {
        *p++ = *src++;
        length--;
      }
      if (FLEXSPI2_INTR & FLEXSPI_INTR_IPRXWA) FLEXSPI2_INTR = FLEXSPI_INTR_IPRXWA;
    }
    
    void printStatusRegs() {
    #if 1
      uint8_t val;
    
      flexspi_ip_read(8, flashBaseAddr, &val, 1 );
      Serial.print("Status 1:");
      Serial.printf(" %02X", val);
      Serial.printf("\n");
      Serial.print("Binary: "); Serial.println(val, BIN);
      Serial.println();
    
      // cmd index 9 = read Status register #2 SPI
      //flexspi_ip_read(9, flashBaseAddr[_spiffs_region], &val, 1 );
      //Serial.print("Status 2:");
      //Serial.printf(" %02X", val);
      //Serial.printf("\n");
    #endif
    }
    
    
    void flexspi_ip_command(uint32_t index, uint32_t addr)
    {
      uint32_t n;
      FLEXSPI2_IPCR0 = addr;
      FLEXSPI2_IPCR1 = FLEXSPI_IPCR1_ISEQID(index);
      FLEXSPI2_IPCMD = FLEXSPI_IPCMD_TRG;
      while (!((n = FLEXSPI2_INTR) & FLEXSPI_INTR_IPCMDDONE)); // wait
      if (n & FLEXSPI_INTR_IPCMDERR) {
        Serial.printf("Error: FLEXSPI2_IPRXFSTS=%08lX\n", FLEXSPI2_IPRXFSTS);
      }
      FLEXSPI2_INTR = FLEXSPI_INTR_IPCMDDONE;
    }
    
    FLASHMEM static uint32_t flexspi2_flash_id(uint32_t addr)
    {
      FLEXSPI2_IPCR0 = addr;
      FLEXSPI2_IPCR1 = FLEXSPI_IPCR1_ISEQID(7) | FLEXSPI_IPCR1_IDATSZ(4);
      FLEXSPI2_IPCMD = FLEXSPI_IPCMD_TRG;
      while (!(FLEXSPI2_INTR & FLEXSPI_INTR_IPCMDDONE)); // wait
      uint32_t id = FLEXSPI2_RFDR0;
      FLEXSPI2_INTR = FLEXSPI_INTR_IPCMDDONE | FLEXSPI_INTR_IPRXWA;
      Serial.println();
      Serial.print("FLASH ID2: "); Serial.println(id , HEX);
      return 0;
    }
    For a start not sure if I understood the LUT setup correctly but at least with what i have i get something even though its not right
    Code:
    Begin Init
    
    
    
    Status after Reset (S/B 0):
    
     FLEXSPI2_RFDR0=00000002  //IMPLIES WRTIE LATCH ENABLED 
    Status 1: 02
    Binary: 10
    
    FLASH ID: FLEXSPI2_RFDR0=78F9E4FD
    
     FD E4 F9 78  //First byte should be a dummy read then EFh, 21h, AAh
    at 0x 1000000
    
    
    FLASH ID2: 78F9E4FD
    So if any one gets around to soldering one on or has any comments let me know

  9. #9
    Senior Member+ mjs513's Avatar
    Join Date
    Jul 2014
    Location
    New York
    Posts
    5,647
    Ok it lives after a successful operation and some playing with LUTs, Raw Dump:
    Code:
    Begin Init
    
    Status before Reset:
    * FLEXSPI2_RFDR0=00000000
    Status 1: 00
    Binary: 0
    
    RESET ISSUED
    
    Status after Reset (S/B 0):
     FLEXSPI2_RFDR0=00000000
    Status 1: 00
    Binary: 0
    
    FLASH ID: FLEXSPI2_RFDR0=21AAEFFF
     FLEXSPI2_RFDR1=00000000
     FF EF AA 21 00
    at 0x 1000000
    Attaching zip file for your reference. So now the fun stuff starts.
    Attached Files Attached Files

  10. #10
    Senior Member+ defragster's Avatar
    Join Date
    Feb 2015
    Posts
    12,414
    Quote Originally Posted by mjs513 View Post
    Ok it lives after a successful operation and some playing with LUTs, Raw Dump:
    ...

    Awesome

  11. #11
    Senior Member
    Join Date
    Sep 2015
    Posts
    108
    Great job! Can anyone confirm that my reading of the datasheet that "regular" SPI can't use the lookup tables and so we'll need two versions of the low level calls correct?

  12. #12
    Senior Member+ mjs513's Avatar
    Join Date
    Jul 2014
    Location
    New York
    Posts
    5,647
    Quote Originally Posted by ecurtz View Post
    Great job! Can anyone confirm that my reading of the datasheet that "regular" SPI can't use the lookup tables and so we'll need two versions of the low level calls correct?
    Way from any expert here but from what I read and have reviewed the answer is no LUTs are used with regular Serial. That one lib I referenced lets you do either/or.

  13. #13
    Senior Member+ mjs513's Avatar
    Join Date
    Jul 2014
    Location
    New York
    Posts
    5,647
    Ok playing around some more but think I am stuck on LUTs again. So anyone that is a LUT expert wondering if you all could help with a few things.

    In going through the existing code for PSRAM and FLASH I see construct:
    Code:
    	FLEXSPI2_LUT20 = LUT0(CMD_SDR, PINS4, 0xEB) | LUT1(ADDR_SDR, PINS4, 24);
    	FLEXSPI2_LUT21 = LUT0(DUMMY_SDR, PINS4, 6) | LUT1(READ_SDR, PINS4, 1);
    I get CMD_SDR, DUMMY_SDR and READ_SDR but not sure what ADDR_SDR is doing. Yes i read the RM but still dont understand.

    That's the first question. So here is the second. This chip is nothing like our existing flash, just a heads up .

    For most of the Op Codes you have to send the OP Code, then either a sub-address and then a value to be written or read (Think SPI) like when I want to read a status register the manual shows:
    Code:
    8.2.3 Read Status Register (0Fh / 05h)
    Click image for larger version. 

Name:	Capture.PNG 
Views:	12 
Size:	57.0 KB 
ID:	20860
    and for this I have:
    What I tried was this:
    Code:
        FLEXSPI2_LUT32 = LUT0(CMD_SDR, PINS1, W25N01G_READ_STATUS_REG) | LUT1(CMD_SDR, PINS1, reg); 
        FLEXSPI2_LUT33 = LUT0(READ_SDR, PINS1 1);
        flexspi_ip_read(8, flashBaseAddr, &val, 1 );
    For write it looks like:
    Click image for larger version. 

Name:	Capture1.PNG 
Views:	9 
Size:	56.5 KB 
ID:	20861 Tried something similar but not sure if did it right"
    Code:
        uint8_t buf[1];
        buf[0] = data;
        Serial.print("---> "); Serial.println(data, BIN);
        // cmd index 10 = write Status register #1 SPI
        //FLEXSPI2_LUT40 = LUT0(CMD_SDR, PINS1, W25N01G_WRITE_STATUS_REG) | LUT1(CMD_SDR, PINS1, reg); 
        //FLEXSPI2_LUT42 = LUT0(WRITE_SDR, PINS1, 1);
        
        FLEXSPI2_LUT40 = LUT0(CMD_SDR, PINS4, W25N01G_WRITE_STATUS_REG);
        FLEXSPI2_LUT41 = LUT0(WRITE_SDR, PINS4, 1);
    
        flexspi_ip_write(10, reg, buf, 1);
    Pretty sure none of this is right.

    But i did manage to get erase and readBytes working i think - but just a guess at this point.

    PS: @PaulStoffregen - @Frank please don't laugh Really trying to understand and get this.

  14. #14
    Senior Member PaulStoffregen's Avatar
    Join Date
    Nov 2012
    Posts
    22,775
    Quote Originally Posted by mjs513 View Post
    but not sure what ADDR_SDR is doing ... That's the first question.
    ADDR_SDR causes FlexSPI to transmit the desired memory address you wish to access. It's used only for actually reading & writing the memory. You wouldn't use ADDR_SDR for LUTs which do things like reading the status address.

    When you issue commands manually (the "IP" interface), the 32 bit address is whatever you wrote to the FLEXSPI2_IPCR0 register. When the processor accesses memory in the 70000000-7FFFFFFF range (the "AHB" interface) the ADDR_SDR command is whatever memory address the M7 process wanted to access.

    There are actually 2 different address commands. So far we've only ever used row address and configured column width to zero. Looks like this chip is going to require using column addresses. Details on page 1634.


    So here is the second. This chip is nothing like our existing flash, just a heads up .
    Yeah, it's quite a challenge...


    when I want to read a status register .... What I tried was this:
    That looks pretty good.


    For write it looks like:
    Use PINS1. If you use PIN4, it will transmit on all 4 data pins using only 2 clocks.


    PS: @PaulStoffregen - @Frank please don't laugh Really trying to understand and get this.
    Oh, I know how hard this is. I spent months (back in 2018) before I finally got it to work, for the much simpler W25Q16.

  15. #15
    Senior Member+ mjs513's Avatar
    Join Date
    Jul 2014
    Location
    New York
    Posts
    5,647
    @PaulStoffregen
    Thanks for getting back to me - know you are busier than a one-armed paper hanger right now. Gave me a lot to think about. Now to do some more reading and then back to playing - maybe I will have something today.

    You know, even though this may be hard, its actually a lot of fun trying to get it to work.

  16. #16
    Senior Member
    Join Date
    Sep 2015
    Posts
    108
    I assume you've looked at the example LUTs in sections 27.6.5 and 27.6.3 in the reference manual? I'm trying to get the HW guy to build me up a 4.1 so I can help, since I think the WSON package is beyond my skill level / equipment.

    Here's the W25N01GV code for a few more commands if it helps at all. _beginSPI does the initial chip select and also sends the argument.

    Code:
    bool SPIFlash::_loadPageData(uint16_t pageAddr) {
       if (pageAddr != _currentPage)
       {
          _beginSPI(CMD_PAGE_DATA);
          _nextByte(WRITE, DUMMYBYTE);
          _nextByte(WRITE, (pageAddr >> 8) & 0xFF);
          _nextByte(WRITE, (pageAddr >> 0) & 0xFF);
    
          _currentPage = pageAddr;
          CHIP_DESELECT
       }
       return true;
     }
    
    bool SPIFlash::_readPageData(uint16_t columnAddr, uint8_t* buffer, uint32_t size) {
       if (_isBusy(PAGE_TIMEOUT)) {
          return false;
       }
    
       _beginSPI(CMD_READ);
       _nextByte(WRITE, (columnAddr >> 8) & 0xFF);
       _nextByte(WRITE, (columnAddr >> 0) & 0xFF);
       _nextByte(WRITE, DUMMYBYTE);
    
       for (uint32_t i = 0; i < size; i++)
       {
          buffer[i] = _nextByte(READ);
       }
       
       CHIP_DESELECT
       return true;
     }
     
    bool SPIFlash::_writePageData(uint16_t columnAddr, uint8_t* buffer, uint32_t size)
    {
       if (!_writeEnable()) return false;
       
       _beginSPI(CMD_DATA_PROG);
       _nextByte(WRITE, (columnAddr >> 8) & 0xFF);
       _nextByte(WRITE, (columnAddr >> 0) & 0xFF);
    
       for (uint32_t i = 0; i < size; i++)
       {
          _nextByte(WRITE, buffer[i]);
       }
    
       CHIP_DESELECT
       return true;
    }
    
    bool SPIFlash::_programPageData(uint16_t pageAddr)
    {
       if (!_disableBlockProtect()) return false;
       
       _beginSPI(CMD_PROG_EX);
       _nextByte(WRITE, DUMMYBYTE);
       _nextByte(WRITE, (pageAddr >> 8) & 0xFF);
       _nextByte(WRITE, (pageAddr >> 0) & 0xFF);
    
       CHIP_DESELECT
       return true;
    }

  17. #17
    Senior Member PaulStoffregen's Avatar
    Join Date
    Nov 2012
    Posts
    22,775
    Quote Originally Posted by ecurtz View Post
    _beginSPI does the initial chip select and also sends the argument.
    This is the way traditional SPI works. But FlexSPI is very different. The whole transfer, including chip select, is done completely by hardware. You don't write code to do the individual steps. You can't. The hardware doesn't even support that way, where it does each part of a transfer in response to software. Instead you program the LUTs to tell the FlexSPI hardware how to do everything.

    Once the LUTs are set up, and some other registers are configured as needed, there are 2 ways you can trigger FlexSPI to actually do a transfer. The "IP" interface is driven by your code. You write to the FLEXSPI2_IPCR0 & FLEXSPI2_IPCR1 to give FlexSPI the address and which LUTs to use, and then you write to FLEXSPI2_IPCMD to tell FlexSPI to actually do the work. You don't ever write code to do things like change the chip select pin and move data. The FlexSPI hardware does everything according to how you set up the LUTs. After triggering it to start, you can poll the FLEXSPI2_INTR register to tell when it's done, or you can set it up to generate an interrupt. The data flows through 2 large FIFOs, which you can poll while the transfer is in progress, or you can set up DMA to move the data. But your code doesn't determine when the actual bytes transfer on the SPI pins. The FlexSPI runs the whole transfer all by itself and you just stuff data into the transmit FIFO and pull data out of the receive FIFO as needed, and keep doing that until it's done.

    The reason FlexSPI runs so autonomously is it's mainly designed to be used by the other "AHB" interface, where it will do SPI transfers automatically as the Cortex-M7 cache wants to fill or flush rows. FlexSPI runs all by itself in response to memory access that generates cache misses. As it does the SPI transfers, those FIFOs flow automatically into buffers which the M7 process sees as memory. This is why the PSRAM chip acts like ordinary memory which you can simply use in your program without any SPI code. That's why FlexSPI is all built around configuring LUTs to program the hardware to do transfers without any software.

    If you're used to ordinary SPI, this can be a difficult way to think about SPI. But hopefully understanding the design is all based on making the SPI chip appear as ordinary memory to the Cortex M7 processor can at least give you some context for why it's all based on hardware-only operation with configurable LUTs.

  18. #18
    Senior Member
    Join Date
    Sep 2015
    Posts
    108
    Quote Originally Posted by PaulStoffregen View Post
    This is the way traditional SPI works. But FlexSPI is very different. The whole transfer, including chip select, is done completely by hardware. You don't write code to do the individual steps. You can't. The hardware doesn't even support that way, where it does each part of a transfer in response to software. Instead you program the LUTs to tell the FlexSPI hardware how to do everything.
    I understand this, but I thought the basic commands encoded into the lookup table would still mirror the traditional method?

  19. #19
    Senior Member+ mjs513's Avatar
    Join Date
    Jul 2014
    Location
    New York
    Posts
    5,647
    @PaulStoffregen - Great explanation !

    You can see some of what Paul is talking about if you look at the flexspi_ip_read, flexsp_ip_write and flexspi_ip_command functions in the sketch. Right now at least not getting errors when I am writing the data buffer or trying to readBytes = just not working. Have to read some more - think I am missing something. Think I need a little break.

    Anyway if you want to play I am attaching the latest version.

    Here are a couple more references about NAND from NXP:
    https://community.nxp.com/thread/418534 (not a good one but has and interesting pdf referenced);
    https://community.nxp.com/thread/526984
    https://community.nxp.com/message/12...omment-1206885 (of course I am not doing pages yet )
    https://community.nxp.com/message/11...omment-1116215 (this one is interesting)
    https://community.nxp.com/thread/534700

  20. #20
    Senior Member+ mjs513's Avatar
    Join Date
    Jul 2014
    Location
    New York
    Posts
    5,647
    Quote Originally Posted by ecurtz View Post
    I understand this, but I thought the basic commands encoded into the lookup table would still mirror the traditional method?
    Unfortunately there is not an exact mirror. To give you an idea check the driver for betaflight: https://github.com/betaflight/betafl...f4135c04afe3fd. It implements both ways.

  21. #21
    Senior Member
    Join Date
    Sep 2015
    Posts
    108
    Quote Originally Posted by mjs513 View Post
    Anyway if you want to play I am attaching the latest version.
    Looks like you forgot to include the new code? I should have a new Teensy with the chip on it by the end of the week.

  22. #22
    Senior Member+ defragster's Avatar
    Join Date
    Feb 2015
    Posts
    12,414
    @mjs513 - in github section for extRAM - made a folder there for extNAND_t41 so code is available and can be source controlled? ... rather than forum hacking.

    USPS says the NAND's ordered will be here 7/6 Monday now - I can at least solder and test ...

  23. #23
    Senior Member+ mjs513's Avatar
    Join Date
    Jul 2014
    Location
    New York
    Posts
    5,647
    Quote Originally Posted by ecurtz View Post
    Looks like you forgot to include the new code? I should have a new Teensy with the chip on it by the end of the week.
    Oops -- heres the attachment. Do you have an example sketch that I can use as reference for the calls to library? Just curious.

    Quote Originally Posted by defragster
    @mjs513 - in github section for extRAM - made a folder there for extNAND_t41 so code is available and can be source controlled? ... rather than forum hacking.
    Ok sounds good - going forward i will push it up there.
    Attached Files Attached Files

  24. #24
    Senior Member
    Join Date
    Aug 2017
    Posts
    311
    @mjs513 - Can you give me the exact part number for the NAND chip you are using? When I looked it up I saw that there were at least three different package types available and I want to make sure that I order the proper one for the T4.1.

    Thanks

  25. #25
    Senior Member+ defragster's Avatar
    Join Date
    Feb 2015
    Posts
    12,414
    From prior post - and info this is what was ordered:

    Post #1 and copied over Paul post #4 matches part Number.

    digikey.com... winbond-electronics/W25N01GVZEIG-TR/W25N01GVZEIGCT-ND/7393545

    W25N01GVZEIG TR
    Digi-Key Part Number W25N01GVZEIGCT-ND
    Manufacturer Winbond Electronics
    Manufacturer Part Number W25N01GVZEIG TR
    Description IC FLASH 1G SPI 104MHZ 8WSON
    Detailed Description FLASH - NAND Memory IC 1Gb (128M x 8) SPI 104MHz 8-WSON (8x6)

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •