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
|
||||
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
|
||||
|
|
36
bdevice.py
36
bdevice.py
|
@ -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
|
||||
|
|
|
@ -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
|
||||
```
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
Ładowanie…
Reference in New Issue