kopia lustrzana https://github.com/peterhinch/micropython_eeprom
Testing complete.
rodzic
cdef377f38
commit
bc8b29ea84
11
README.md
11
README.md
|
@ -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
|
||||||
|
|
36
bdevice.py
36
bdevice.py
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
```
|
```
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
|
||||||
|
|
Ładowanie…
Reference in New Issue