Testing complete.

pull/1/head
Peter Hinch 2020-01-10 18:46:57 +00:00
rodzic cdef377f38
commit bc8b29ea84
5 zmienionych plików z 28 dodań i 60 usunięć

Wyświetl plik

@ -34,7 +34,7 @@ respectively. In the case of the FAT filing system 1M page writes probably
corresponds to 1M filesystem writes because FAT repeatedly updates the corresponds to 1M filesystem writes because FAT repeatedly updates the
allocation tables in the low numbered sectors. Under `littlefs` I would expect allocation tables in the low numbered sectors. Under `littlefs` I would expect
the endurance to be substantially better owing to its wear levelling the endurance to be substantially better owing to its wear levelling
architecture. architecture; over-provisioning should enhance this.
## 1.3 Organisation of this repo ## 1.3 Organisation of this repo
@ -69,9 +69,6 @@ In the table below the Interface column includes page size in bytes.
| Microchip | 24xx64 | I2C 128 | 8KiB | EEPROM | [I2C.md](./eeprom/i2c/I2C.md) | | Microchip | 24xx64 | I2C 128 | 8KiB | EEPROM | [I2C.md](./eeprom/i2c/I2C.md) |
| Adafruit | 1895 | I2C n/a | 32KiB | FRAM | [FRAM.md](./fram/FRAM.md) | | Adafruit | 1895 | I2C n/a | 32KiB | FRAM | [FRAM.md](./fram/FRAM.md) |
Note that the flash driver is **under development** and has a possible issue
discussed in [FLASH.md](./flash/FLASH.md).
## 1.5 Performance ## 1.5 Performance
FRAM is truly byte-addressable: its speed is limited only by the speed of the FRAM is truly byte-addressable: its speed is limited only by the speed of the
@ -86,9 +83,9 @@ The drivers provide the benefit of page writing in a way which is transparent.
If you write a block of data to an arbitrary address, page writes will be used If you write a block of data to an arbitrary address, page writes will be used
to minimise total time. to minimise total time.
In the case of flash memory page writing is mandatory: a sector is written by In the case of flash, page writing is mandatory: a sector is written by first
first erasing it, a process which is slow. This physical limitation means that erasing it, a process which is slow. This physical limitation means that the
the driver must buffer an entire 4096 byte sector. This contrasts with FRAM and driver must buffer an entire 4096 byte sector. This contrasts with FRAM and
EEPROM drivers where the buffering comprises a few bytes. EEPROM drivers where the buffering comprises a few bytes.
# 2. Choice of interface # 2. Choice of interface

Wyświetl plik

@ -1,20 +1,14 @@
# bdevice.py Hardware-agnostic base classes. # bdevice.py Hardware-agnostic base classes.
# BlockDevice Base class for general block devices e.g. EEPROM, FRAM. # BlockDevice Base class for general block devices e.g. EEPROM, FRAM.
# FlashDevice Base class for generic Flash memory (subclass of BlockDevice). # FlashDevice Base class for generic Flash memory (subclass of BlockDevice).
# Documentation in BASE_CLASSES.md
# 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
# BlockDevice: hardware-independent class implementing the uos.AbstractBlockDev
# protocol with extended interface. It supports littlefs.
# http://docs.micropython.org/en/latest/reference/filesystem.html#custom-block-devices
# The subclass must implement .readwrite which can read or write arbitrary amounts
# of data to arbitrary addresses. IOW .readwrite handles physical block structure
# while ioctl supports a virtual block size.
from micropython import const from micropython import const
class BlockDevice: class BlockDevice:
def __init__(self, nbits, nchips, chip_size): def __init__(self, nbits, nchips, chip_size):
@ -66,34 +60,26 @@ class BlockDevice:
# IOCTL protocol. # IOCTL protocol.
def sync(self): # Nothing to do for unbuffered devices. Subclass overrides. def sync(self): # Nothing to do for unbuffered devices. Subclass overrides.
return 0 return
def readblocks(self, blocknum, buf, offset=0): def readblocks(self, blocknum, buf, offset=0):
self.readwrite(offset + (blocknum << self._nbits), buf, True) self.readwrite(offset + (blocknum << self._nbits), buf, True)
def writeblocks(self, blocknum, buf, offset=None): def writeblocks(self, blocknum, buf, offset=0):
offset = 0 if offset is None else offset
self.readwrite(offset + (blocknum << self._nbits), buf, False) self.readwrite(offset + (blocknum << self._nbits), buf, False)
def ioctl(self, op, arg): # ioctl calls: see extmod/vfs.h def ioctl(self, op, arg): # ioctl calls: see extmod/vfs.h
if op == 3: # SYNCHRONISE if op == 3: # SYNCHRONISE
return self.sync() self.sync()
return
if op == 4: # BP_IOCTL_SEC_COUNT if op == 4: # BP_IOCTL_SEC_COUNT
return self._a_bytes >> self._nbits return self._a_bytes >> self._nbits
if op == 5: # BP_IOCTL_SEC_SIZE if op == 5: # BP_IOCTL_SEC_SIZE
return self._block_size return self._block_size
if op == 6: # Erase if op == 6: # ERASE
return 0 return 0
# Hardware agnostic base class for flash memory, where a single sector is cached. # Hardware agnostic base class for flash memory.
# This minimises RAM usage. Under FAT wear is reduced if you cache at least two
# sectors. This driver is primarily intended for littlefs which has no such issue.
# Class assumes erased state is 0xff.
# Subclass must provide these hardware-dependent methods:
# .rdchip(addr, mvb) Read from chip into memoryview: data guaranteed not to be cached.
# .flush(cache, addr) Erase physical sector and write out an entire cached sector.
# .readwrite As per base class.
_RDBUFSIZE = const(32) # Size of read buffer for erasure test _RDBUFSIZE = const(32) # Size of read buffer for erasure test
@ -172,14 +158,14 @@ class FlashDevice(BlockDevice):
def initialise(self): def initialise(self):
self._fill_cache(0) self._fill_cache(0)
# Return True if a sector is erased. Assumes erased == ff. # Return True if a sector is erased.
def is_empty(self, addr): def is_empty(self, addr, ev=0xff):
mvb = self._mvbuf mvb = self._mvbuf
erased = True erased = True
nbufs = self.sec_size // _RDBUFSIZE # Read buffers per sector nbufs = self.sec_size // _RDBUFSIZE # Read buffers per sector
for _ in range(nbufs): for _ in range(nbufs):
self.rdchip(addr, mvb) self.rdchip(addr, mvb)
if any(True for x in mvb if x != 0xff): if any(True for x in mvb if x != ev):
erased = False erased = False
break break
addr += _RDBUFSIZE addr += _RDBUFSIZE

Wyświetl plik

@ -2,7 +2,8 @@
This driver supports the Cypress S25FL256L and S25FL128L chips, providing 64MiB This driver supports the Cypress S25FL256L and S25FL128L chips, providing 64MiB
and 32MiB respectively. These have 100K cycles of write endurance (compared to and 32MiB respectively. These have 100K cycles of write endurance (compared to
10K for Pyboard Flash memory). 10K for Pyboard Flash memory). These were the largest capacity available with a
sector size small enough for microcontroller use.
Multiple chips may be used to construct a single logical nonvolatile memory Multiple chips may be used to construct a single logical nonvolatile memory
module. The driver allows the memory either to be mounted in the target module. The driver allows the memory either to be mounted in the target
@ -22,23 +23,8 @@ an inevitable price for the large capacity of flash chips.
FAT and littlefs filesystems are supported but the latter is preferred owing to FAT and littlefs filesystems are supported but the latter is preferred owing to
its resilience and wear levelling characteristics. its resilience and wear levelling characteristics.
Byte level access on such large devices probably has few use cases other than Arguably byte level access on such large devices has few use cases other than
for facilitating effective hardware tests and diagnostics. for facilitating effective hardware tests and for diagnostics.
## 1.1 Test Status
The driver has been tested with both chip types. Single chip setups work fine.
With pairs of devices I have seen sporadic single bit errors, especially at
high baudrates. I strongly suspect hardware issues with my breadboard lash-up
and am awaiting PCB's in manufacture. The reasons I suspect hardware are:
1. Errors are usually single bit: it's hard to see how the driver could do
this.
2. Errors are not consistent.
3. They are baudrate dependent.
4. The breadboard is truly revoltming.
However until I can test with a PCB the possibility of a bug remains.
##### [Main readme](../README.md) ##### [Main readme](../README.md)
@ -74,9 +60,10 @@ Other platforms may vary but the Cypress chips require a 3.3V supply.
## 2.1 SPI Bus ## 2.1 SPI Bus
The devices support baudrates up to 50MHz. In practice MicroPython targets do The devices support baudrates up to 50MHz. In practice MicroPython targets do
not support such high rates. In testing I found it necessary to specify 5MHz not support such high rates. The test programs specify 20MHz, but in practice
otherwise erratic results occurred. This was probably because of my breadboard the Pyboard D delivers 15MHz. Testing was done at this rate. In testing a
test setup as described above. SPI bus wiring should be short and direct. "lashup" breadboard was unsatisfactory: a problem entirely fixed with a PCB.
Bus lines should be short and direct.
# 3. Files # 3. Files
@ -109,7 +96,7 @@ import os
from machine import SPI, Pin from machine import SPI, Pin
from flash_spi import FLASH from flash_spi import FLASH
cspins = (Pin(Pin.board.Y5, Pin.OUT, value=1), Pin(Pin.board.Y4, Pin.OUT, value=1)) cspins = (Pin(Pin.board.Y5, Pin.OUT, value=1), Pin(Pin.board.Y4, Pin.OUT, value=1))
flash = FLASH(SPI(2, baudrate=5_000_000), cspins) flash = FLASH(SPI(2, baudrate=20_000_000), cspins)
# Format the filesystem # Format the filesystem
os.VfsLfs2.mkfs(flash) # Omit this to mount an existing filesystem os.VfsLfs2.mkfs(flash) # Omit this to mount an existing filesystem
os.mount(flash,'/fl_ext') os.mount(flash,'/fl_ext')
@ -172,7 +159,7 @@ of single byte access:
from machine import SPI, Pin from machine import SPI, Pin
from flash_spi import FLASH from flash_spi import FLASH
cspins = (Pin(Pin.board.Y5, Pin.OUT, value=1), Pin(Pin.board.Y4, Pin.OUT, value=1)) cspins = (Pin(Pin.board.Y5, Pin.OUT, value=1), Pin(Pin.board.Y4, Pin.OUT, value=1))
flash = FLASH(SPI(2, baudrate=5_000_000), cspins) flash = FLASH(SPI(2, baudrate=20_000_000), cspins)
flash[2000] = 42 flash[2000] = 42
print(flash[2000]) # Return an integer print(flash[2000]) # Return an integer
``` ```
@ -182,7 +169,7 @@ writing, the size of the slice must match the length of the buffer:
from machine import SPI, Pin from machine import SPI, Pin
from flash_spi import FLASH from flash_spi import FLASH
cspins = (Pin(Pin.board.Y5, Pin.OUT, value=1), Pin(Pin.board.Y4, Pin.OUT, value=1)) cspins = (Pin(Pin.board.Y5, Pin.OUT, value=1), Pin(Pin.board.Y4, Pin.OUT, value=1))
flash = FLASH(SPI(2, baudrate=5_000_000), cspins) flash = FLASH(SPI(2, baudrate=20_000_000), cspins)
flash[2000:2002] = bytearray((42, 43)) flash[2000:2002] = bytearray((42, 43))
print(flash[2000:2002]) # Returns a bytearray print(flash[2000:2002]) # Returns a bytearray
``` ```

Wyświetl plik

@ -109,6 +109,7 @@ class FLASH(FlashDevice):
self._spi.write(mvp[:5]) self._spi.write(mvp[:5])
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]))
nbytes -= npage nbytes -= npage
start += npage start += npage
addr += npage addr += npage

Wyświetl plik

@ -16,7 +16,7 @@ def get_device():
Pin.board.EN_3V3.value(1) Pin.board.EN_3V3.value(1)
# Adjust to suit number of chips and their wiring. # Adjust to suit number of chips and their wiring.
cspins = (Pin(Pin.board.Y5, Pin.OUT, value=1), Pin(Pin.board.Y4, Pin.OUT, value=1)) cspins = (Pin(Pin.board.Y5, Pin.OUT, value=1), Pin(Pin.board.Y4, Pin.OUT, value=1))
flash = FLASH(SPI(2, baudrate=5_000_000), cspins, size=32768) flash = FLASH(SPI(2, baudrate=20_000_000), cspins, size=32768)
print('Instantiated Flash') print('Instantiated Flash')
return flash return flash
@ -157,6 +157,3 @@ def full_test(count=10):
if g != data[n]: if g != data[n]:
print('{} {:2x} {:2x} {:2x}'.format(n, data[n], g, got1[n])) print('{} {:2x} {:2x} {:2x}'.format(n, data[n], g, got1[n]))
break break
# Fail seems to be on write: two readbacks are always identical.
# Error is on MSB of byte 0: write a 1 and get 0