flash_spi: Generalize to support chips with 24-bit addresses

Currently flash_spi.py is hardcoded for a small set of devices, all of
which can use 4-byte addressing. Fix this by allowing the command set and
address generation to be switched dynamically.

We also remove the whitelist of tested devices since the interfaces used
is very common amoung different flash devices. However as a safety measure
the user can provide the expected flash size and this will be checked
against the density field.
pull/1/head
Daniel Thompson 2020-02-09 16:51:01 +00:00
rodzic 9466ad2061
commit 8105da265b
1 zmienionych plików z 42 dodań i 27 usunięć

Wyświetl plik

@ -1,5 +1,4 @@
# flash_spi.py MicroPython driver for Cypress S25FL128L 16MiB and S25FL256L 32MiB # flash_spi.py MicroPython driver for SPI NOR flash devices.
# flash devices.
# Released under the MIT License (MIT). See LICENSE. # Released under the MIT License (MIT). See LICENSE.
# Copyright (c) 2019 Peter Hinch # Copyright (c) 2019 Peter Hinch
@ -9,10 +8,12 @@ from micropython import const
from bdevice import FlashDevice from bdevice import FlashDevice
# Supported instruction set: # Supported instruction set:
# 4 byte address commands # 3 and 4 byte address commands
_READ = const(0x13) _READ = const(0)
_PP = const(0x12) # Page program _PP = const(1)
_SE = const(0x21) # Sector erase _SE = const(2)
_CMDS3BA = b'\x03\x02\x20'
_CMDS4BA = b'\x13\x12\x21'
# No address # No address
_WREN = const(6) # Write enable _WREN = const(6) # Write enable
_RDSR1 = const(5) # Read status register 1 _RDSR1 = const(5) # Read status register 1
@ -25,23 +26,31 @@ _SEC_SIZE = const(4096) # Flash sector size 0x1000
# Logical Flash device comprising one or more physical chips sharing an SPI bus. # Logical Flash device comprising one or more physical chips sharing an SPI bus.
class FLASH(FlashDevice): class FLASH(FlashDevice):
def __init__(self, spi, cspins, size=16384, verbose=True, sec_size=_SEC_SIZE, block_size=9): def __init__(self, spi, cspins, size=None, verbose=True, sec_size=_SEC_SIZE, block_size=9):
# args: virtual block size in bits, no. of chips, bytes in each chip # args: virtual block size in bits, no. of chips, bytes in each chip
if size not in (16384, 32768):
raise ValueError('Valid sizes: 16384 or 32768KiB')
super().__init__(block_size, len(cspins), size * 1024, sec_size)
self._spi = spi self._spi = spi
self._cspins = cspins self._cspins = cspins
self._ccs = None # Chip select Pin object for current chip self._ccs = None # Chip select Pin object for current chip
self._bufp = bytearray(6) # instruction + 4 byte address + 1 byte value self._bufp = bytearray(6) # instruction + 4 byte address + 1 byte value
self._mvp = memoryview(self._bufp) # cost-free slicing self._mvp = memoryview(self._bufp) # cost-free slicing
self._page_size = 256 # Write uses 256 byte pages. self._page_size = 256 # Write uses 256 byte pages.
self.scan(verbose)
size = self.scan(verbose, size)
super().__init__(block_size, len(cspins), size * 1024, sec_size)
# Select the correct command set
if size <= 4096:
self._cmds = _CMDS3BA
self._cmdlen = 4
else:
self._cmds = _CMDS4BA
self._cmdlen = 5
self.initialise() # Initially cache sector 0 self.initialise() # Initially cache sector 0
# **** API SPECIAL METHODS **** # **** API SPECIAL METHODS ****
# Scan: read manf ID # Scan: read manf ID
def scan(self, verbose): def scan(self, verbose, size):
mvp = self._mvp mvp = self._mvp
for n, cs in enumerate(self._cspins): for n, cs in enumerate(self._cspins):
mvp[:] = b'\0\0\0\0\0\0' mvp[:] = b'\0\0\0\0\0\0'
@ -49,12 +58,16 @@ class FLASH(FlashDevice):
cs(0) cs(0)
self._spi.write_readinto(mvp[:4], mvp[:4]) self._spi.write_readinto(mvp[:4], mvp[:4])
cs(1) cs(1)
if mvp[1] != 1 or mvp[2] != 0x60 or not (mvp[3] == 0x18 or mvp[3] == 0x19): scansize = 1 << (mvp[3] - 10)
raise RuntimeError('Flash not found at cs[{}].'.format(n)) if not size:
size = scansize
if size != scansize:
raise ValueError('Flash size mismatch: expected {}KiB, found {}KiB'.format(size, scansize))
if verbose: if verbose:
s = '{} chips detected. Total flash size {}MiB.' s = '{} chips detected. Total flash size {}MiB.'
print(s.format(n + 1, self._a_bytes // (1024 * 1024))) n += 1
return n print(s.format(n, (n * size) // 1024))
return size
# Chip erase. Can take minutes. # Chip erase. Can take minutes.
def erase(self): def erase(self):
@ -86,9 +99,9 @@ class FLASH(FlashDevice):
cs(0) cs(0)
self._spi.write(mvp[:1]) # Enable write self._spi.write(mvp[:1]) # Enable write
cs(1) cs(1)
mvp[0] = _PP mvp[0] = self._cmds[_PP]
cs(0) cs(0)
self._spi.write(mvp[:5]) # Start write self._spi.write(mvp[:self._cmdlen]) # Start write
self._spi.write(cache[start : start + ps]) self._spi.write(cache[start : start + ps])
cs(1) cs(1)
self._wait_rdy() # Wait for write to complete self._wait_rdy() # Wait for write to complete
@ -104,9 +117,9 @@ class FLASH(FlashDevice):
while nbytes > 0: while nbytes > 0:
npage = self._getaddr(addr, nbytes) # No. of bytes in current chip npage = self._getaddr(addr, nbytes) # No. of bytes in current chip
cs = self._ccs cs = self._ccs
mvp[0] = _READ mvp[0] = self._cmds[_READ]
cs(0) cs(0)
self._spi.write(mvp[:5]) self._spi.write(mvp[:self._cmdlen])
self._spi.readinto(mvb[start : start + npage]) self._spi.readinto(mvb[start : start + npage])
cs(1) cs(1)
# print('addr {} npage {} data {}'.format(addr, npage, mvb[start])) # print('addr {} npage {} data {}'.format(addr, npage, mvb[start]))
@ -141,11 +154,13 @@ class FLASH(FlashDevice):
raise RuntimeError("Flash Address is out of range") raise RuntimeError("Flash Address is out of range")
ca, la = divmod(addr, self._c_bytes) # ca == chip no, la == offset into chip ca, la = divmod(addr, self._c_bytes) # ca == chip no, la == offset into chip
self._ccs = self._cspins[ca] # Current chip select self._ccs = self._cspins[ca] # Current chip select
mvp = self._mvp cmdlen = self._cmdlen
mvp[1] = la >> 24 mvp = self._mvp[:cmdlen]
mvp[2] = la >> 16 & 0xff if cmdlen > 3:
mvp[3] = (la >> 8) & 0xff mvp[-4] = la >> 24
mvp[4] = la & 0xff mvp[-3] = la >> 16 & 0xff
mvp[-2] = (la >> 8) & 0xff
mvp[-1] = la & 0xff
pe = (addr & -self._c_bytes) + self._c_bytes # Byte 0 of next chip pe = (addr & -self._c_bytes) + self._c_bytes # Byte 0 of next chip
return min(nbytes, pe - la) return min(nbytes, pe - la)
@ -160,8 +175,8 @@ class FLASH(FlashDevice):
cs(0) cs(0)
self._spi.write(mvp[:1]) # Enable write self._spi.write(mvp[:1]) # Enable write
cs(1) cs(1)
mvp[0] = _SE mvp[0] = self._cmds[_SE]
cs(0) cs(0)
self._spi.write(mvp[:5]) # Start erase self._spi.write(mvp[:self._cmdlen]) # Start erase
cs(1) cs(1)
self._wait_rdy() # Wait for erase to complete self._wait_rdy() # Wait for erase to complete