NAND flash support in 1.54

Sounds like a good thing to pull your hair out trying to understand! Is this even more clear and understandable as the ADC_ETC stuff? ;) Maybe I should do the checkout at Digikey with it!
 
Sounds like a good thing to pull your hair out trying to understand! Is this even more clear and understandable as the ADC_ETC stuff? ;) Maybe I should do the checkout at Digikey with it!

Not sure which is worse, ADC_ETC or NAND :).

Think ADC_ETC may have been worse. At least Paul has the PSRAM and FLASH code as samples to get an idea :)
 
Think I know what the problem MIGHT be but not 100% sure.

One of the things that have to be cleared, it looks like, are the write protection bits in the Protection Status Register. To clear I do a:
Code:
    // No protection, WP-E off, WP-E prevents use of IO2
    w25n01g_writeStatusRegister(W25N01G_PROT_REG, W25N01G_PROT_CLEAR);
    w25n01g_readStatusRegister(W25N01G_PROT_REG, true);
This should set all the bits to 0. But when I print it out the protection bits are still set to 1 and not sure why its not updating the register. I can update the config and status registers so am at a loss.
 
You have to use the write enable command (0x06) to set that bit, you can't do it by writing to the status register.

EDIT: Actually maybe that's just for the regular writes and you need to use the Status Register Protect Bits as described in section 7.1.3 to modify that register.
 
Last edited:
You have to use the write enable command (0x06) to set that bit, you can't do it by writing to the status register.

EDIT: Actually maybe that's just for the regular writes and you need to use the Status Register Protect Bits as described in section 7.1.3 to modify that register.

You got it - thats what I am trying to clear. The status register, 0xC0, contains the WEL bit that you use for writes, that is set for the writes. You do have to remember to disable it for reads. But the protection register, 0xA0, has protection bits that have to be cleared otherwise you can write to the FLASH. To set it you use send the CMD 0x1F plus the SR address which for the protection is 0xA0. This is what doesn't seem to want to change.
 
The ability to write to the protection register is controlled by 3 bits of the status register as shown on the chart on page 16 of the datasheet.
 
The ability to write to the protection register is controlled by 3 bits of the status register as shown on the chart on page 16 of the datasheet.

Yep. Just reread the section on Write Protection. As usual reading stuff wrong :)

WP-E and SR1 are both 0 so that the device is in S/W protect mode and HOLD/WP are multiplexed as IO pins so think I am alright. Ok on the next idea
 
I found this result:
Code:
Begin Init
0
18
Status of reg 0xb0: 
(HEX: ) 0x18, (Binary: )11000

Found W25N01G Flash Chip
Loading data
w25n01g_programExecute:
Reading Data
-1, 0
Write Page Addr Complete
0, 0
READING DATA START
Command 14 Complete
CHECKING ECC-CODE
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,

After these 8 tiny blobs in place:
T_4.1_NAND.png
 
@defragster
Your soldering job is better than mine.

The results you show are exactly what I am seeing. Not what I am doing wrong here. Made a few more changes as a test but same results. Thinks it may be with my addressing.

Pretty much ready to give up unless you all can figure it out. I will push my latest changes that dont even use PINS4 just PINS1. Gives same results.

EDIT: OK latest changes pushed - your all's turn to figure it out.
 
@defragster
Your soldering job is better than mine.
...

:) Did first pass and looked and there were pulled solder hairs on all four pad far ends - using newer finer wire solder.

Made a 30 gauge wire solder paste applicator and touched a tad on each and then solder pulled to balls with reheat. Repeated on the other side. As seen the one got a little light on solder - but figured adding a 'bit more' would be a mess.

Then one wet 90% Iso brush and hair dryer heating..
 
@mjs513 - updated from github. Looked through the code assuming low level stuff right - found some few questions ...

Just browsing lines. The buffer[] to write is zeroed then values set 0x19, then that pointer isn't sent?

Should line ~29 the first read like the second?

Code:
    w25n01g_programDataLoad(W25N01G_LINEAR_TO_COLUMN(0), [B][U]xData[/U][/B], 16);
    w25n01g_programDataLoad(W25N01G_LINEAR_TO_COLUMN(0), [B]buffer[/B], 16);

Also - KByte count is write hex value - why doubled here?
Code:
    FLEXSPI2_FLSHA2CR0 = 0x1F400 * 2;          //Flash Size in KByte, 1F400

... Doesn't change anything

I don't see this as accounting for rare millis Wrap math:
Code:
static bool w25n01g_waitForReady()
{
    while (!w25n01g_isReady()) {
        uint32_t now = millis();
        [B]if (now >= timeoutAt)[/B] {
The value in : w25n01g_setTimeout() should just be : timeoutAt = timeoutMillis;
and the above:
Code:
static bool w25n01g_waitForReady()
{
     uint32_t now = millis();
    while (!w25n01g_isReady()) {
        [B]if (millis() - now >= timeoutAt)[/B] {

It isn't failing - as not wrapped of course in sort run. I checked adding:
Code:
#define Dprint(a) Serial.print( a );

....
            Dprint( "wFReady Timeout" );
            return false;
>> That Dprint Macro would allow Debug_print sprinkling to be commented out easily

New output shows Found flash name:
Code:
Begin Init
Found W25N01G Flash Chip
0
Status of reg 0xa0: 
(HEX: ) 0x7C, (Binary: )1111100

18
Status of reg 0xb0: 
(HEX: ) 0x18, (Binary: )11000

w25n01g_programExecute:
Reading Data
-1, 0
Write Page Addr Complete
0, 0
READING DATA START
Command 14 Complete
CHECKING ECC-CODE
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
 
@defragster
Also - KByte count is write hex value - why doubled here?
saw it in an example. If I don't use it doesn't make a difference. Going to ask a couple of questions as a double check because I think I am confusing myself now.

Q. FLSHSZ is defined in kilobytes so 1GBits = 125,000 Kilobytes so FLSHSZ should really be 125000 or 0x1e848. Doing a double check on everything today and probably some clean up.

The value in : w25n01g_setTimeout() should just be : timeoutAt = timeoutMillis;
Yeah - didn't spend too much time on that one just threw it in as a something quick to test with - have to really fix it up. Max timout is 500ms once set by the way.

That Dprint Macro would allow Debug_print sprinkling to be commented out easily
Was thinking about that but didn't have many prints but looks like they are growing :)

Should line ~29 the first read like the second?
Was experimenting - really need to clean up the example.

I am wondering about the AHB settings though at this point if I they are right - have to try and figure that out as well. Other thing is addressing might be wrong since most cases use column addressing. Think I got ok for now with the LUTs - maybe.
 
@mjs513 - Was suggesting more Dprint() just in case there is a surprise failure in the manipulation.
Maybe prints for status or other without too much code clutter:
Code:
#define DHprint( a ) { Serial.print( #a); Serial.println ( (uint32_t)a,HEX ); }

Between Flash operation and commands and then the MCU prep and execution steps - not even sure where to begin to catch up ...
 
Got my new board and making a tiny bit of progress. It seems like FLEXSPI2_IPCR0 always needs to have an address in the range of flashBaseAddr, probably because that's how it determines the chip select to activate.
Code:
• Flash access start address:
Determined by register field IPCR0[SFAR]
• Flash Chip Select:
Determined by flash access address and Flash size setting (FLSHxCR0[FLSHSZ]).
I did verify that you can write to the page buffer and read back without actually programming the page data, so that makes it somewhat simpler to test since there is one less step. However there's also something wrong with the data send in flexspi_ip_write, because even if I hack the address to always be flashBaseAddr I don't get back the correct data, but if I change it to send a constant value I can get that back
Code:
//  FLEXSPI2_IPCR0 = addr;
FLEXSPI2_IPCR0 = flashBaseAddr;

  FLEXSPI2_IPCR1 = FLEXSPI_IPCR1_ISEQID(index) | FLEXSPI_IPCR1_IDATSZ(length);
  src = (const uint8_t *)data;
  FLEXSPI2_IPCMD = FLEXSPI_IPCMD_TRG;
  while (!((n = FLEXSPI2_INTR) & FLEXSPI_INTR_IPCMDDONE)) {
    if (n & FLEXSPI_INTR_IPTXWE) {
      wrlen = length;
      if (wrlen > 8) wrlen = 8;
      if (wrlen > 0) {
        //Serial.print("%");
        //memcpy((void *)&FLEXSPI2_TFDR0, src, wrlen);
        FLEXSPI2_TFDR0 = 0x11111111; FLEXSPI2_TFDR1 = 0x11111111;
        src += wrlen;
        length -= wrlen;
        FLEXSPI2_INTR = FLEXSPI_INTR_IPTXWE;
      }
    }
  }
 
Not sure if this is relevant - had an edit to startup.c to comment out the call to :: configure_external_ram();

Without that done the NANDflash code sample died after : Begin Init

This T_4.1 has only the NAND on it - but the setup to detect the PSRAM - provided some needed prep - maybe partial but not enough or partially contrary for the NAND access?

Editing that func() as below lines up to ~386 are needed - this takes everything after that out and it still works - taking out the three lines shown above "#if 0" stops it from working
Code:
	FLEXSPI2_MCR0 &= ~FLEXSPI_MCR0_MDIS;

	FLEXSPI2_LUTKEY = FLEXSPI_LUTKEY_VALUE;
	FLEXSPI2_LUTCR = FLEXSPI_LUTCR_UNLOCK;
#if 0
	volatile uint32_t *luttable = &FLEXSPI2_LUT0;
	for (int i=0; i < 64; i++) luttable[i] = 0;
[B]...[/B]
		// No PSRAM
	}
#endif
}

#endif // ARDUINO_TEENSY41

Maybe that code was already checked to be right for NAND use in QSPI ... but just in case ...
 
@defragster - @ecurtz
At this point I am pretty much at a loss with the API. Went through it again and made some changes. So now i get all 0's instead of FF's.

I did add CAS = 16 for column addressing:
Code:
    FLEXSPI2_FLSHA2CR1 = FLEXSPI_FLSHCR1_CSINTERVAL(2)  //minimum interval between flash device Chip selection deassertion and flash device Chip selection assertion.
    | [COLOR="#FF0000"]FLEXSPI_FLSHCR1_CAS(16)[/COLOR]
    | FLEXSPI_FLSHCR1_TCSH(3)                           //Serial Flash CS Hold time.
    | FLEXSPI_FLSHCR1_TCSS(3);                          //Serial Flash CS setup time

Hold and setup times come from the data sheet.

Another possible issue is the T41 loader. For extRam the LD has:
Code:
	ERAM (rwx):  ORIGIN = 0x70000000, LENGTH = 16384K
Notice length is 16MB

The other issue is if i calculated Flash Size correctly. 1GB I think converts to 125,000 kBytes?

If you all could check me it would be appreciated.

EDIT:
Forgot something. Did notice this:
Code:
    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.
Not 100% sure what
Code:
AWRSEQID(6)
ARDSEQID(5)
if these are LUT sequences the 6 corresponds to "write QPI" and 5 "read QPI"
 
Last edited:
This is seriously hacked up and I'm only trying to write to the on chip buffer and read it back, not even attempting to store the page, but it's doing a little bit better than the stuff from GitHub. I cannot wrap my head around why I need to write a constant to the TX FIFO but maybe the morning coffee will help.

EDIT:Removed buggy attachment
 
Last edited:
This is seriously hacked up and I'm only trying to write to the on chip buffer and read it back, not even attempting to store the page, but it's doing a little bit better than the stuff from GitHub. I cannot wrap my head around why I need to write a constant to the TX FIFO but maybe the morning coffee will help.
You shouldnt have to. Let me give it a try and see if I can figure anything out. What I did see in play around WREN is 10 on entering but get reset to 8 so maybe thats why you have to go to usding rdrx1.

EDIT: Need something to drink but have to go out and do some errands.

All clues to why its not working - really appreciate you and defragster testing.
 
If you're referring to the write enable bit in the status register it automatically gets reset every time you do a "write" command (which is actually a couple different things) "A write disable state occurs upon power-up or after any of the following instructions: Write Disable, Program Execute, Block Erase, Page Data Read, Program Execute and Bad Block Management for OTP pages."
 
Ok, found a bug in the writeStatusRegister LUT, this is still hacked up, but a big step toward working.
Wasn't sure you needed the add the STOP command. Looking at what we did for W25G128JV the LUTS didn't need the STOP. Not sure why you need so many. But it did fix the issue I was having with 0ing the PROT status.

If thats the case then you need to add the STOP command for all the LUTs is my guess.
 
STOP is different than 0 which is what it's all initialized to, but I'm not sure if that matters - the bug was skipping over a LUT entry, from 40 to 42.
 
STOP is different than 0 which is what it's all initialized to, but I'm not sure if that matters - the bug was skipping over a LUT entry, from 40 to 42.

Thanks missed that, thought I fixed them, wonder if i did that anyplace else. Oh by the way dont need the stop just checked.
 
Ok think I got it all fixed now thanks to your 2 finds. Going to attach it and push it to Github as well for more testing.
 

Attachments

  • NAND_SPI_TEST.zip
    9 KB · Views: 75
updated from github. Not sure it adds anything but after print loop in setup() the following was added. Changed the offset address to 4004 and output the same as with 4000:
Code:
    for(uint16_t i = 0; i < 32; i++) {
      Serial.printf("0x%02x, ",buffer[i]);
    } Serial.println();

[B]const uint8_t beefy[] = "DEADBEEFdeadbeef\n";
   //Serial.println("Loading data");
Dprint( (char *)beefy )
    w25n01g_writeEnable(true);   //sets the WEL in Status Reg to 1 (bit 2)
    w25n01g_programDataLoad(W25N01G_LINEAR_TO_COLUMN(4004), beefy, 16);
    //w25n01g_randomProgramDataLoad(W25N01G_LINEAR_TO_COLUMN(40000), buffer, 16);
    w25n01g_programExecute(W25N01G_LINEAR_TO_PAGE(4004));
    
    Serial.println("Reading Data");
    memset(buffer, 0, 2048);
    w25n01g_writeEnable(false);
    w25n01g_readBytes(W25N01G_LINEAR_TO_COLUMN(4000), buffer, 32);

    for(uint16_t i = 0; i < 20; i++) {
      Serial.printf("0x%02x[%c], ",buffer[i],buffer[i]);
    } Serial.println();[/B]

Output is:
Begin Init
Found W25N01G Flash Chip
0
Status of reg 0xa0:
(HEX: ) 0x00, (Binary: )0

18
Status of reg 0xb0:
(HEX: ) 0x18, (Binary: )11000

w25n01g_programDataLoad
w25n01g_programExecute
Reading Data
-1, 0
Write Page Addr Complete
READING DATA START
Command 14 Complete
CHECKING ECC-CODE
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
DEADBEEFdeadbeef

w25n01g_programDataLoad
w25n01g_programExecute
Reading Data
0, 0
READING DATA START
Command 14 Complete
CHECKING ECC-CODE
0x49, 0xb0[�], 0x01[], 0x8e[�], 0x82[�], 0x7a[z], 0xfa[�], 0x87[�], 0x61[a], 0xf2[�], 0xcc[�], 0x4d[M], 0x24[$], 0x1a[], 0xb2[�], 0x2b[+], 0xff[�], 0xff[�], 0xff[�], 0xff[�],



With offset to 4004 - expected these to return 0x00 again - but output is the same order a
nd value for 4000 or 4004 of 'write'?
0x49, 0xb0[�], 0x01[], 0x8e[�]

But the 16-19 elements are still 0xff?
 
Back
Top