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
allocation tables in the low numbered sectors. Under `littlefs` I would expect
the endurance to be substantially better owing to its wear levelling
architecture.
architecture; over-provisioning should enhance this.
## 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) |
| 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
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
to minimise total time.
In the case of flash memory page writing is mandatory: a sector is written by
first erasing it, a process which is slow. This physical limitation means that
the driver must buffer an entire 4096 byte sector. This contrasts with FRAM and
In the case of flash, page writing is mandatory: a sector is written by first
erasing it, a process which is slow. This physical limitation means that the
driver must buffer an entire 4096 byte sector. This contrasts with FRAM and
EEPROM drivers where the buffering comprises a few bytes.
# 2. Choice of interface

Wyświetl plik

@ -1,20 +1,14 @@
# bdevice.py Hardware-agnostic base classes.
# BlockDevice Base class for general block devices e.g. EEPROM, FRAM.
# FlashDevice Base class for generic Flash memory (subclass of BlockDevice).
# Documentation in BASE_CLASSES.md
# Released under the MIT License (MIT). See LICENSE.
# 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
class BlockDevice:
def __init__(self, nbits, nchips, chip_size):
@ -66,34 +60,26 @@ class BlockDevice:
# IOCTL protocol.
def sync(self): # Nothing to do for unbuffered devices. Subclass overrides.
return 0
return
def readblocks(self, blocknum, buf, offset=0):
self.readwrite(offset + (blocknum << self._nbits), buf, True)
def writeblocks(self, blocknum, buf, offset=None):
offset = 0 if offset is None else offset
def writeblocks(self, blocknum, buf, offset=0):
self.readwrite(offset + (blocknum << self._nbits), buf, False)
def ioctl(self, op, arg): # ioctl calls: see extmod/vfs.h
if op == 3: # SYNCHRONISE
return self.sync()
self.sync()
return
if op == 4: # BP_IOCTL_SEC_COUNT
return self._a_bytes >> self._nbits
if op == 5: # BP_IOCTL_SEC_SIZE
return self._block_size
if op == 6: # Erase
if op == 6: # ERASE
return 0
# Hardware agnostic base class for flash memory, where a single sector is cached.
# 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.
# Hardware agnostic base class for flash memory.
_RDBUFSIZE = const(32) # Size of read buffer for erasure test
@ -172,14 +158,14 @@ class FlashDevice(BlockDevice):
def initialise(self):
self._fill_cache(0)
# Return True if a sector is erased. Assumes erased == ff.
def is_empty(self, addr):
# Return True if a sector is erased.
def is_empty(self, addr, ev=0xff):
mvb = self._mvbuf
erased = True
nbufs = self.sec_size // _RDBUFSIZE # Read buffers per sector
for _ in range(nbufs):
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
break
addr += _RDBUFSIZE

Wyświetl plik

@ -2,7 +2,8 @@
This driver supports the Cypress S25FL256L and S25FL128L chips, providing 64MiB
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
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
its resilience and wear levelling characteristics.
Byte level access on such large devices probably has few use cases other than
for facilitating effective hardware tests and 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.
Arguably byte level access on such large devices has few use cases other than
for facilitating effective hardware tests and for diagnostics.
##### [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
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
otherwise erratic results occurred. This was probably because of my breadboard
test setup as described above. SPI bus wiring should be short and direct.
not support such high rates. The test programs specify 20MHz, but in practice
the Pyboard D delivers 15MHz. Testing was done at this rate. In testing a
"lashup" breadboard was unsatisfactory: a problem entirely fixed with a PCB.
Bus lines should be short and direct.
# 3. Files
@ -109,7 +96,7 @@ import os
from machine import SPI, Pin
from flash_spi import FLASH
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
os.VfsLfs2.mkfs(flash) # Omit this to mount an existing filesystem
os.mount(flash,'/fl_ext')
@ -172,7 +159,7 @@ of single byte access:
from machine import SPI, Pin
from flash_spi import FLASH
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
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 flash_spi import FLASH
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))
print(flash[2000:2002]) # Returns a bytearray
```

Wyświetl plik

@ -109,6 +109,7 @@ class FLASH(FlashDevice):
self._spi.write(mvp[:5])
self._spi.readinto(mvb[start : start + npage])
cs(1)
# print('addr {} npage {} data {}'.format(addr, npage, mvb[start]))
nbytes -= npage
start += npage
addr += npage

Wyświetl plik

@ -16,7 +16,7 @@ def get_device():
Pin.board.EN_3V3.value(1)
# 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))
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')
return flash
@ -157,6 +157,3 @@ def full_test(count=10):
if g != data[n]:
print('{} {:2x} {:2x} {:2x}'.format(n, data[n], g, got1[n]))
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