eblot / pyspiflash

SPI data flash device drivers (pure Python)
MIT License
39 stars 30 forks source link

Does it support only NOR flash? #16

Open asmitaj08 opened 4 years ago

asmitaj08 commented 4 years ago

Hello, I am currently working on nand flash chip . Datasheet - http://pro0fc108.hkpic1.websiteonline.cn/upload/PN26Q01AWSIUG.pdf

It would be of great help if you can suggest in what way should I proceed for SPI nand flash

Thanks & Regards Asmita

eblot commented 4 years ago

It seems this flash is somewhat compliant with SPI NOR data flash (25 protocol).

You need to check how this flash compares to existing flash implementations, and create a new class and implements the special calls for this flash.

asmitaj08 commented 4 years ago

Hi, Thanks for your response. I found FTDI library for MPSSE - https://www.ftdichip.com/Support/Documents/AppNotes/AN_178_User%20Guide%20for%20LibMPSSE-SPI.pdf .

I think, I should proceed with this.

Thanks and Regards Asmita

eblot commented 4 years ago

As explained in PyFtdi ticket, it should be quite easy to add support for this type of flash device, as there is nothing NAND specific to support this device, only two-stage read accesses.

Note that the supported AT45 NOR flash use a similar two-stage management, but for write accesses:

  1. transfer to RAM buffer
  2. prog buffer into flash cells.
asmitaj08 commented 4 years ago

Hello @eblot Thanks for the response. I tried , I am able to read the data from flash. But only the partial data is correct. I am bit confused in section 7.6.2 of the datasheet. Selection_023

Let say that I want to read complete flash i.e. all 64 pages in each 1024 blocks. So my approach is : (1) page read command 0x13 followed by block/page address (2) check for read status and error bit (3) read from cache command 0x03 followed by byte address (0) with wrap length of 2176 (4) next page read command (0x31) I am confused that since there are 1024 blocks and 64 pages for each block, Then do I need to put while loop like : while blockNum <= 1023: (1) (2) (3) while pageNum <= 63: (4) pageNum +=1 blockNum += 1

or the other way :

while blockNum <= 1023 while pageNum <= 63 (1) (2) (3)

Kindly suggest.

Thanks & Regards Asmita

eblot commented 4 years ago

You really need to use markdown syntax, it is quite unreadable as plain text. Moreover, please copy/paste text as text, not as static images.

The PN25Q datasheet is a pure piece of sh*t, I think it is one of the worst flash datasheet I read, it does not help to understand how the flash works.

If I'd ever have to work with this product, I would contact their customer support and report how many typos, contradictory wordings, references to undocumented commands, lack of definitions, inconsistent wordings for the same command, etc. plague the rubbish they dare to call a datasheet. They should call this a datashite...

AFAIU, you need to write this kind of loop:

# 1. initiate read from flash, starting @ address 0
0x13 0x00 0x00 0x00
  # and create an empty Python output buffer

# 2. poll status ("get features")
0x0f ..
# - loop till not busy
# - abort if ECC fails
# - break the loop here if you read all the data you wanted
#   i.e. the whole flash size for example.
#   return the Python output buffer

# 3.: this is either 3A (loop=1), 3B (loop>1), 3C (loop=last):

 # 3A. read from cache, first block, first time in the loop
   0x0b H L 0x00 0x00
   # H, L: use 2048 wrap length (you do not want ECC data)
   # 0x00: always zero
   # 0x00: output an extra dummy byte to match command behaviour

   # 3B: read next page, all other loops
   0x31

   # 3C: read last page, undocumented...
   # for now, use 0x31, once it works, optimize using this opcode
   0x3F

# 4. read 2048 bytes
#  append the 2KiB block to the Python output buffer

# 5. jump to 2.

Notes: I do not think you should use a 2176 (2048+128) wrapping, as this is likely to include the 128 ECC data bits you do not care about as part of the actual data. That would require to discard the 128 last bytes of every read output: more complex, less effective. You only need to check the ECC status every time you read a new page from flash to data buffer.

asmitaj08 commented 4 years ago

Hello @eblot Thank you so much for detailed explanation. True, it's really difficult to understand this datasheet because of so many errors inside it. I tried to read the complete flash in two ways (code snippet mentioned below ) but was unsuccessful.

### Method 1

`

def _reset_device(self):
     reset_cmd = bytes(self.CMD_RESET)    
     self._spi.exchange(reset_cmd) 

 def _read_page(self)  :
     page_read_cmd = bytes((self.CMD_PAGE_READ,0x00, 0x00, 0x00)) #PageRead 0x13
     self._spi.exchange(page_read_cmd)

 def _check_busy_status(self): 
     val = self._get_featureC()[0]
     if((val & (0x01) == 1)) :
         return False
     else :
         return True

 def _check_eccFail_status(self): 
     val = self._get_featureC()[0]
     if(((val) & (0x30))== (0x20)) :
        print("ECC Failed")
         return False
     else :
         return True 

 def _read_from_cache(self) -> bytes:
     cache_read_cmd = bytes((self.CMD_READ_FROM_CACHE, 0x40, 0x00,0x00))   # # CMD_READ_FROM_CACHE = 0x0B
     self._spi.exchange(cache_read_cmd, 2048)
     #return self._spi.exchange(cache_read_cmd, 2048)

 def _read_next_page(self) :
     next_page_read_cmd = bytes((self.CMD_NEXT_PAGE_READ))
     self._spi.exchange(next_page_read_cmd)

 def _read_last_page(self) :
     last_page_read_cmd = bytes((self.CMD_LAST_PAGE_READ))
     self._spi.exchange(last_page_read_cmd, 2048)

 def read(self) -> bytes:
     buf = bytearray()
     self._reset_device()
     # Set ECC_EN
     self._set_featureB(0x10)
     print("\nFeature A : %s, Feature B : %s, Feature C : %s" % (self._get_featureA(), self._get_featureB(), self._get_featureC()))
     blockCount = 0 
     self._read_page()         ## PageRead 0x13 0x00 0x00 0x00
     while blockCount < 1024 : ## To read whole flash i.e. 1024 blocks
         pageCount = 0
         print(blockCount)
         while self._check_busy_status() != True :
             if self._check_eccFail_status() == False : 
                 break
         blockCount+=1 
        for pageCountLoop in range(0,64) :  ## Each block has 64 pages
             if pageCountLoop == 0 :
                 self._read_from_cache()      ## First page of the block
            else :
                 self._read_next_page()       ## Next 63 pages

             dataV = self._spi.exchange('', 2048)
             buf.extend(dataV)
     return bytes(buf)

`

### Method 2

`
 def _read_page(self, address: int)  :
        page_read_cmd = bytes((self.CMD_PAGE_READ,
                          0x00, (address >> 2) & 0xff,(address<<6) & 0xC0))
        self._spi.exchange(page_read_cmd)

  def read(self) -> bytes:
        buf = bytearray()
        self._reset_device()
        # Set ECC_EN
        self._set_featureB(0x10)
        print("\nFeature A : %s, Feature B : %s, Feature C : %s" % (self._get_featureA(),              self._get_featureB(), self._get_featureC()))
        blockCount = 0
        while blockCount < 1024 :
            print(blockCount)
            self._read_page(blockCount)               # Whole flash i.e. 1024 blocks
            while self._check_busy_status() != True :
                if self._check_eccFail_status() == False :
                    break
            blockCount+=1
            for pageCountLoop in range(0,64) :  # Each block has 64 pages
                if pageCountLoop == 0 :
                    self._read_from_cache()      # First page of the block
                else :
                    self._read_next_page()       # Next 63 pages
                dataV = self._spi.exchange('', 2048)
                buf.extend(dataV)
        return bytes(buf)
`
eblot commented 4 years ago

I would write something like:

CMD_GET_FEATURES = 0x0F
CMD_SET_FEATURES = 0x1F
CMD_PAGE_READ_ADDRESS = 0x13
CMD_RESET = 0xFF
CMD_READ_FROM_CACHE = 0x0B
CMD_READ_NEXT_PAGE = 0x31
CMD_READ_LAST_PAGE = 0x3F

FEATURE_LOCK = 0xA0
FEATURE_FEATURE = 0xB0
FEATURE_STATUS = 0xC0

STATUS_OIP = 1 << 0
STATUS_ECCS0 = 1 << 4
STATUS_ECCS1 = 1 << 5

FEATURE_ECC = 1 << 4

PAGE_SIZE = 2048
BLOCK_COUNT = 1024
PAGE_PER_BLOCK = 64
FLASH_SIZE = BLOCK_COUNT*PAGE_PER_BLOCK*PAGE_SIZE

def _reset_device(self) -> None:
     self._spi.write([self.CMD_RESET])

def _check_busy(self) -> Bool:
    status = self._spi.exchange([self.CMD_GET_FEATURES, self.FEATURE_STATUS],
                                1)
    return bool(status[0] & self.STATUS_OIP)

def _enable_ecc(self) -> None:
    feature = self._spi.exchange([self.CMD_GET_FEATURES, self.FEATURE_FEATURE],
                                 1)
    feature[0] |= self.FEATURE_ECC
    self._spi.write([self.CMD_SET_FEATURES, feature[0]])

def _check_ecc(self) -> bool:
    status = self._spi.exchange([self.CMD_GET_FEATURES, self.FEATURE_STATUS],
                                1)
    ecc = status[0] & (self.STATUS_ECCS0 | STATUS_ECCS1)
    return not (status[0] == STATUS_ECCS1)

def _set_read_address(self, address: int) -> None:
    addrs = struct.pack('>I', address)
    self._spi.write([self.CMD_PAGE_READ_ADDRESS, addrs[2], addrs[1], addrs[0]])

def _read_first_page(self, address) -> bytes:
    return self._spi.exchange([self.CMD_READ_FROM_CACHE, 0x40, 0x00, 0x00],
                              self.PAGE_SIZE)

def _read_next_page(self) -> bytes:
    return self._spi.exchange([self.CMD_READ_NEXT_PAGE], self.PAGE_SIZE)

def _read_last_page(self) -> bytes:
    return self._spi.exchange([self.CMD_READ_LAST_PAGE], self.PAGE_SIZE)

def read_all(self) -> bytes:
    buf = bytearray()
    self._reset_device()
    self._enable_ecc()
    self._set_read_address(0)
    while len(buf) < self.FLASH_SIZE:
        while self._check_busy():
            pass  # tRD is too short (~240 us) to be worth calling time.sleep()
        if self._check_ecc():
            raise IOError('Corrupted, non recoverable flash content')
        if not buf:
            data = self._read_first_page()
        elif len(buf) < FLASH_SIZE-2*PAGE_SIZE:
            data = self._read_next_page()
        else:
            data = self._read_last_page()
        buf.extend(data)
    return bytes(buf)
asmitaj08 commented 4 years ago

Hello, Thank you so much. I tried but not successful. I have few queries. In give part of the code

def _check_ecc(self) -> bool:
    status = self._spi.exchange([self.CMD_GET_FEATURES, self.FEATURE_STATUS],
                                1)
    ecc = status[0] & (self.STATUS_ECCS0 | STATUS_ECCS1)
    return not (status[0] == STATUS_ECCS1)

As per the doc,

ECCS provides ECC status as follows: 00b = No bit errors were detected during the previous read algorithm. 01b = bit error was detected and corrected, error bit number = 1~7. 10b = bit error was detected and not corrected. 11b = bit error was detected and corrected, error bit number = 8. ECCS is set to 00b either following a RESET, or at the beginning of the READ. It is then updated after the device completes a valid READ operation. ECCS is invalid if internal ECC is disabled (via a SET FEATURES command to reset ECC_EN).

That is , _checkecc should fail at ECCS1:ECCS0 10

i.e. I think it should be

def _check_ecc(self) -> bool:
    status = self._spi.exchange([self.CMD_GET_FEATURES, self.FEATURE_STATUS],
                                1)
    ecc = status[0] & (self.STATUS_ECCS0 | STATUS_ECCS1)
    return (ecc == STATUS_ECCS1)

Another query

In code section,

      if not buf:
            data = self._read_first_page()
        elif len(buf) < FLASH_SIZE-2*PAGE_SIZE:
            data = self._read_next_page()
        else:
            data = self._read_last_page()

why is that len(buf) condition check with FLASH_SIZE-2*PAGE_SIZE

And another query is as per doc,in instruction table, command 0x31 and 0x3F are not returning anything. And read_page command 0x13 asks for block address, do we need to pass it everytime, here we have set it as 0 in start. I am confused her. Kindly suggest

Thanks & Regards Asmita

eblot commented 4 years ago

(fix block quote)

eblot commented 4 years ago

_check_ecc() is correct, but the test is not, it should be read as

if not _check_ecc():
    raise IOError(...)
asmitaj08 commented 4 years ago

Ya, I later did changes by using

if not _check_ecc():
    raise IOError(...)

But no success.

eblot commented 4 years ago

why is that len(buf) condition check with FLASH_SIZE-2*PAGE_SIZE

_read_last_page() should be only called once, for the last PAGE_SIZE read chunk. However, as we test of the length of the buffer before appending a new chunk, so we must account for the chunk of data that is about to be appended.

You are right, I misread the doc, it should be:

CMD_GET_FEATURES = 0x0F
CMD_SET_FEATURES = 0x1F
CMD_PAGE_READ_ADDRESS = 0x13
CMD_RESET = 0xFF
CMD_READ_FROM_CACHE = 0x0B
CMD_READ_NEXT_PAGE = 0x31
CMD_READ_LAST_PAGE = 0x3F

FEATURE_LOCK = 0xA0
FEATURE_FEATURE = 0xB0
FEATURE_STATUS = 0xC0

STATUS_OIP = 1 << 0
STATUS_ECCS0 = 1 << 4
STATUS_ECCS1 = 1 << 5

FEATURE_ECC = 1 << 4

PAGE_SIZE = 2048
BLOCK_COUNT = 1024
PAGE_PER_BLOCK = 64
FLASH_SIZE = BLOCK_COUNT*PAGE_PER_BLOCK*PAGE_SIZE

def _reset_device(self) -> None:
     self._spi.write([self.CMD_RESET])

def _check_busy(self) -> Bool:
    status = self._spi.exchange([self.CMD_GET_FEATURES, self.FEATURE_STATUS],
                                1)
    return bool(status[0] & self.STATUS_OIP)

def _enable_ecc(self) -> None:
    feature = self._spi.exchange([self.CMD_GET_FEATURES, self.FEATURE_FEATURE],
                                 1)
    feature[0] |= self.FEATURE_ECC
    self._spi.write([self.CMD_SET_FEATURES, feature[0]])

def _check_ecc(self) -> bool:
    status = self._spi.exchange([self.CMD_GET_FEATURES, self.FEATURE_STATUS],
                                1)
    ecc = status[0] & (self.STATUS_ECCS0 | STATUS_ECCS1)
    return not (status[0] == STATUS_ECCS1)

def _set_first_page(self, address: int) -> None:
    addrs = struct.pack('>I', address)
    self._spi.write([self.CMD_PAGE_READ_ADDRESS, addrs[2], addrs[1], addrs[0]])

def _set_next_page(self) -> bytes:
    return self._spi.exchange([self.CMD_READ_NEXT_PAGE], self.PAGE_SIZE)

def _set_last_page(self) -> bytes:
    return self._spi.exchange([self.CMD_READ_LAST_PAGE], self.PAGE_SIZE)

def _read_cache(self, address) -> bytes:
    return self._spi.exchange([self.CMD_READ_FROM_CACHE, 0x40, 0x00, 0x00],
                              self.PAGE_SIZE)

def read_all(self) -> bytes:
    buf = bytearray()
    self._reset_device()
    self._enable_ecc()
    while len(buf) < self.FLASH_SIZE:
        if not buf:
            self._set_first_page()
        elif len(buf) >= FLASH_SIZE-PAGE_SIZE:
            self._set_last_page()
        else:
            self._set_next_page()
        while self._check_busy():
            pass  # tRD is too short (~240 us) to be worth calling time.sleep()
        if not self._check_ecc():
            raise IOError('Corrupted, non recoverable flash content')
        data = self._read_cache()
        buf.extend(data)
    return bytes(buf)
eblot commented 4 years ago

About ECC: are you sure your flash is programmed?

At this point, you need to use a logical analyser or an oscilloscope to trace whatever is exchange with the flash.

eblot commented 4 years ago

I'm not sure how ECC should be interpreted with a blank flash, is it documented?

asmitaj08 commented 4 years ago

I'm not sure how ECC should be interpreted with a blank flash, is it documented?

Yes , it's documented in its so-called datasheet on page 33, section 8. Its screenshot i am attaching below. Selection_025

asmitaj08 commented 4 years ago

About ECC: are you sure your flash is programmed?

At this point, you need to use a logical analyser or an oscilloscope to trace whatever is exchange with the flash.

Yes, flash is programmed

asmitaj08 commented 4 years ago

[edited, no need to duplicate whole message]

I have query in this section of code

if not buf: self._set_first_page() elif len(buf) >= FLASH_SIZE-PAGE_SIZE: self._set_last_page() else: self._set_next_page() while self._check_busy():

understood len(buf) >= FLASH_SIZE-PAGE_SIZE, indicates if all pages of all blocks are read then pass the last page read command. I am confused that is just a single time _set_first_page() would be ok or should we read 64 pages of one block at a time.

Also, I think self._set_last_page() and self._set_first_page() should not return anything.

eblot commented 4 years ago

I see nothing it the doc that says that when the last page of a block is read, the address wraps up to the start of the same block. It looks like other flashes: subsequent read resume on the next block. Block segmentation is mostly for erasure operation. For read operation, the flash seems linear - at least nothing seems to indicate it is not.

Also, I think self._set_last_page() and self._set_first_page() should not return anything.

True I forgot to clear the return statements in the second version of the code

asmitaj08 commented 4 years ago

Hello @eblot , I tried all the possible methods but was able to read the flash using following below script. There are 3 partitions inside flash, I am able to extract complete two partitions but some files in partition 3 are missing, while extracting using ubireader, I am getting error extract_files Fatal: 1624 . But when I am doing Binwalk , I am not at all getting the files of 3rd partition. Any suggestions would be useful, thanks for your support. Once finalized, I would send you the pull request to merge for this NAND flash chip. Similarly I would like to add SPI EEPROM 25LC256 support.

`def _reset_device(self) -> None: self._spi.write([self.CMD_RESET])

def _check_busy(self) -> bool:
    status = self._spi.exchange([self.CMD_GET_FEATURES, self.FEATURE_STATUS],1)
    return bool(status[0] & self.STATUS_OIP)

def _enable_ecc(self) -> None:
    feature = self._spi.exchange([self.CMD_GET_FEATURES, self.FEATURE_FEATURE],1)
    feature[0] |= self.FEATURE_ECC
    self._spi.write([self.CMD_SET_FEATURES, feature[0]])

def _check_ecc(self) -> bool:
    status = self._spi.exchange([self.CMD_GET_FEATURES, self.FEATURE_STATUS],1)
    ecc = status[0] & (self.STATUS_ECCS0 | self.STATUS_ECCS1)
    return not (status[0] == self.STATUS_ECCS1)
    #return (ecc == self.STATUS_ECCS1)

def _read_page(self, blockAddress: int, pageAddress: int) -> bytes :
    return self._spi.exchange([self.CMD_PAGE_READ, 0x00,blockAddress>>2,pageAddress|(blockAddress<<6 & 192)])

def _read_cache(self) -> bytes:
     return self._spi.exchange([self.CMD_READ_FROM_CACHE, 0x40, 0x00, 0x00], self.PAGE_SIZE)

def read_all(self):
    buf = bytearray()
    self._reset_device()
    self._enable_ecc()

    for j in range(0,1024):
        for i in range(0,64):

            dataList = []
            self._read_page(j,i)
            while self._check_busy():
                pass  
            if not self._check_ecc():
                raise IOError('Corrupted, non recoverable flash content')

            dataList = self._read_cache()
            buf.extend(dataList) 
    return bytes(buf)`