diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..d06fbd5 --- /dev/null +++ b/.gitignore @@ -0,0 +1,10 @@ +app_*/.build_*/ +app_*/bin/ +doc/.doxygen/ +doc/.linked_dirs/ +doc/.sources/ +doc/Doxyfile-e +doc/_build/ +doc/index.pdf +module_flash/.build_flash_dev/ +module_flash/lib/ diff --git a/app_example_flash_small/Makefile b/app_example_flash_small/Makefile new file mode 100644 index 0000000..5b9c698 --- /dev/null +++ b/app_example_flash_small/Makefile @@ -0,0 +1,9 @@ +TARGET = XK-1 +APP_NAME = +USED_MODULES = module_flash_small +XCC_FLAGS_Debug = +XCC_FLAGS_Release = -g +VERBOSE = 0 + +XMOS_MAKE_PATH ?= ../.. +-include $(XMOS_MAKE_PATH)/xcommon/module_xcommon/build/Makefile.common diff --git a/app_example_flash_small/src/main.xc b/app_example_flash_small/src/main.xc new file mode 100644 index 0000000..c316c34 --- /dev/null +++ b/app_example_flash_small/src/main.xc @@ -0,0 +1,109 @@ +#include +#include + + +//::program that tests erase sector + +testBytes(int start, int end, int value) { + char data[1]; + printf("Testing that bytes %d..%d are 0x%02x\n", start, end, value); + for(int i = start; i < end; i++) { + spiFlashRead(i, data, 1); + if (data[0] != value) { + printf("Byte %d is 0x%02x rather than 0x%02x- sector size/erase command are not consistent\n", i, data[0] & 0xff, value); + } + } +} + +testErase() { + char data[256]; + + spiInit(); + printf("Erasing %d bytes (2 sectors)\n", SPI_SECTOR_SIZE*2); + spiFlashErase(0, SPI_SECTOR_SIZE*2); + testBytes(0, SPI_SECTOR_SIZE*2, 0xff); + for(int i = 0; i < 256; i++) { + data[i] = 0; + } + printf("Setting 2 sectors to 0x00\n"); + for(int i = 0; i < SPI_SECTOR_SIZE*2; i+=256) { + spiFlashWriteSmall(i, data, 256); + } + testBytes(0, SPI_SECTOR_SIZE*2, 0x00); + printf("Erasing %d bytes (1 sectorx)\n", SPI_SECTOR_SIZE); + spiFlashErase(0, SPI_SECTOR_SIZE); + testBytes(0, SPI_SECTOR_SIZE, 0xff); + testBytes(SPI_SECTOR_SIZE, SPI_SECTOR_SIZE*2, 0x00); +} +//:: + +#define spiInit() ; + +//::spi program +#include + +useSPI() { + spiInit(); + printf("ID4: %08x\n",spiCommandStatus(SPI_CMD_READID, 4)); +} +//:: + +//::flash program +#include + +useFlash() { + char data[256]; + int addr = 0x18800; + spiInit(); + + spiFlashRead(addr, data, 32); + for(int i = 0; i < 32; i++) { + printf(" %02x", data[i]); + } + printf("\n"); + spiFlashErase(addr, 32); + spiFlashRead(addr, data, 32); + for(int i = 0; i < 32; i++) { + printf(" %02x", data[i]); + } + printf("\n"); + for(int i = 0; i < 32; i++) { + data[i] = i; + } + printf("\n"); + spiFlashWriteSmall(addr, data, 32); + spiFlashRead(addr, data, 32); + for(int i = 0; i < 32; i++) { + printf(" %02x", data[i]); + } + printf("\n"); +} +//:: + +//::persistence program +#include + +char state[15]; + +usePersistence() { + int valid; + spiInit(); + + valid = spiFlashPersistentStateRead(state); + if (!valid) { + // fill state[] with factory default + } + + //... + // update state + spiFlashPersistentStateWrite(state); + //... +} +//:: + +main() { + testErase(); + useSPI(); + useFlash(); + usePersistence(); +} diff --git a/app_test_flash_small/Makefile b/app_test_flash_small/Makefile new file mode 100644 index 0000000..01f557d --- /dev/null +++ b/app_test_flash_small/Makefile @@ -0,0 +1,9 @@ +TARGET = XK-1 +APP_NAME = +USED_MODULES = module_flash_small +XCC_FLAGS_Debug = +XCC_FLAGS_Release = -g -O2 +VERBOSE = 0 + +XMOS_MAKE_PATH ?= ../.. +-include $(XMOS_MAKE_PATH)/xcommon/module_xcommon/build/Makefile.common diff --git a/app_test_flash_small/src/main.xc b/app_test_flash_small/src/main.xc new file mode 100644 index 0000000..5c26593 --- /dev/null +++ b/app_test_flash_small/src/main.xc @@ -0,0 +1,43 @@ +#include +#include + +int errors = 0; + +void testBytes(int start, int end, int value) { + char data[1]; +// printf("Testing that bytes %d..%d are 0x%02x\n", start, end, value); + for(int i = start; i < end; i++) { + spiFlashRead(i, data, 1); + if (data[0] != value) { + printf("Byte %d is 0x%02x rather than 0x%02x- sector size/erase command are not consistent\n", i, data[0] & 0xff, value); + errors++; + } + } +} + +int main(void) { + char data[256]; + + spiInit(); +// printf("Erasing %d bytes (2 sectors)\n", SPI_SECTOR_SIZE*2); + spiFlashErase(0, SPI_SECTOR_SIZE*2); + testBytes(0, SPI_SECTOR_SIZE*2, 0xff); + for(int i = 0; i < 256; i++) { + data[i] = 0; + } +// printf("Setting 2 sectors to 0x00\n"); + for(int i = 0; i < SPI_SECTOR_SIZE*2; i+=256) { + spiFlashWriteSmall(i, data, 256); + } + testBytes(0, SPI_SECTOR_SIZE*2, 0x00); +// printf("Erasing %d bytes (1 sectorx)\n", SPI_SECTOR_SIZE); + spiFlashErase(0, SPI_SECTOR_SIZE); + testBytes(0, SPI_SECTOR_SIZE, 0xff); + testBytes(SPI_SECTOR_SIZE, SPI_SECTOR_SIZE*2, 0x00); + printf("Erase code 0x%02x and sector size %d ", SPI_CMD_ERASE, SPI_SECTOR_SIZE); + if (errors == 0) { + printf("appear to work for this device\n"); + } else { + printf("appear to be broken for this device\n"); + } +} diff --git a/app_test_flash_small/src/spi_conf.h b/app_test_flash_small/src/spi_conf.h new file mode 100644 index 0000000..1d8f943 --- /dev/null +++ b/app_test_flash_small/src/spi_conf.h @@ -0,0 +1,3 @@ +#define SPI_CLK_MHZ 25 +#define SPI_CMD_ERASE 0xD8 +#define SPI_SECTOR_SIZE 32768 diff --git a/doc/Makefile b/doc/Makefile new file mode 100644 index 0000000..c554c96 --- /dev/null +++ b/doc/Makefile @@ -0,0 +1,9 @@ +REPO=../../sc_flash +VERSION=2v0 +DOXYGEN_DIRS=$(REPO)/module_flash_small +SOURCE_INCLUDE_DIRS=$(REPO) +XDOC_DIR ?= ../../xdoc +include $(XDOC_DIR)/Makefile.inc + +all: html pdf + @if [ ! -d $(REPO)_gh_pages ] ; then echo '**** no gh_pages checked out ****'; exit 0; else cp -r _build/html/* $(REPO)_gh_pages/; cp -r _build/html/.doctrees $(REPO)_gh_pages/; echo 'HTML files copied to $(REPO)_gh_pages'; echo 'Now go to $(REPO)_gh_pages, add, commit, and push to publish the documentation'; fi diff --git a/doc/api-small.rst b/doc/api-small.rst new file mode 100644 index 0000000..46634aa --- /dev/null +++ b/doc/api-small.rst @@ -0,0 +1,211 @@ +module_flash_small +'''''''''''''''''' + +This module implements a simple flash library, sufficient for DFU and +similar applications. It has a number of restrictions compared to the +flash-library in module_flash``: + +* No support for changing write-protection (but any protection that is is + set-up will be honoured). + +* Sector size, speed and SPI commands are compiled-in, and hence only + binary-compatible devices are supported once you have created your binary. + +* Only a single instance is supported in a program (ie, you cannot use two + separate flash devices simultaneously) + +The library is configured using compile-time defines. The ports are +normally defined in the XN file. Any other define should be placed in a +source file ``spi_conf.h`` that will be picked up by the build-system. + + +SPI API +======= + +Defines +------- + +There are two defines to deal with flash interoperability. There is no need +to worry about these if you only read flash. If you need to write to flash, +you must check that these are compatible with *all* flash devices that you +intend to support. See the section on interoperability for an +explanation. + +**SPI_CLK_MHZ** + + The maximum speed of the flash device in MHz. The default value is 25, + the only other value supported is 13 (resulting in a 12.5 MHz SPI clock). + +**SPI_CMD_ERASE** + + The command byte that erases a sector in SPI. The default value 0x20, + works for about half the SPI flash devices. + +**SPI_SECTOR_SIZE** + + The number of bytes that a sector-erase erases. The default value, 4096 + bytes, is compatible with about half of the flash devices. + +**PORT_SPI_SS** + + The port to which the SPI slave select signal is connected. Normally + defined in the XN file. + +**PORT_SPI_CLK** + + The port to which the SPI clock signal is connected. Normally + defined in the XN file. + +**PORT_SPI_MISO** + + The port to which the SPI master-in slave-out is connected. Normally + defined in the XN file. + +**PORT_SPI_MOSI** + + The port to which the SPI master-out slave-in is connected. Normally + defined in the XN file. + +Functions +--------- + +These functions are defined in ``spi.h``. + +.. doxygenfunction:: spiInit +.. doxygenfunction:: spiCommandStatus +.. doxygenfunction:: spiCommandAddressStatus + +Flash API +========= + +Defines +------- + +There are three defines that can be tuned to change the persistent state +handling - only important if spiFlashPersistentStateRead() and +spiFlashPersistentStateWrite() are used. + +**FLASH_PERSISTENT_SIZE** + + The size of persistent data in bytes. When using the functions + spiFlashPersistentStateRead() and spiFlashPersistentStateWrite() this + indicates how much data is read or written. The default is 15 bytes; the + number of bytes plus one must be a power of 2. + +**FLASH_PERSISTENT_BASE** + + The based address of the persistent data in flash. This must be a sector + boundary. The default is 65536. + + +**FLASH_PERSISTENT_SEGMENT_SIZE** + + The size of the persistent data segment in bytes. The persistent data + segment must be at least two sectors long; longer segments take more + space but wear out less quickly. The default is 2 sectors. + +Functions +--------- + +These functions are defined in ``flash.h``. + +.. doxygenfunction:: spiFlashRead +.. doxygenfunction:: spiFlashWriteSmall +.. doxygenfunction:: spiFlashWrite +.. doxygenfunction:: spiFlashErase +.. doxygenfunction:: spiFlashPersistentStateRead +.. doxygenfunction:: spiFlashPersistentStateWrite + + +Compatibility +============= + +Not all SPI flash devices are the same, and there is considerable variation +in how to erase part of the flash. The two parameters to be set are the +'Erase command' and the 'Erase sector size'. The default settings are 0x20 +and 4K, and all devices marked with an 'X' in this column should be +compatible. + +Please note that it is that once the sector and erase code are compiled in, +then the binary is only compatible with that set of devices. If greater +compatibility is required, the full flash library should be used instead. +You must consult the datasheets of the flash-chips that you intend to +employ and verify that all of those are compatible. + +A test application is provided in ``app_test_flash_small/`` which tests +whether an erase code and erase sector size work on a particular piece of +hardware. + ++-------------------------+------+------+------+------+------+ +| Erase code | 0x20 | 0x52 | 0xD8 | 0xD8 | Size | ++-------------------------+------+------+------+------+------+ +| Erase sector size | 4K | 32K | 64K | 32K | bytes| ++----------+--------------+------+------+------+------+------+ +| ALTERA | EPCS1 | | | | X | 128K | +| +--------------+------+------+------+------+------+ +| | EPCS4/16/64 | | | X | | | ++----------+--------------+------+------+------+------+------+ +| AMIC | A25L016 | X | | | | 2M | +| +--------------+------+------+------+------+------+ +| | A25L40 | | | X | | 512K | +| +--------------+------+------+------+------+------+ +| | A25L80 | | | X | | 1M | ++----------+--------------+------+------+------+------+------+ +| ATMEL | AT25DF021 | X | | X | | 256K | +| +--------------+------+------+------+------+------+ +| | AT25DF041A | X | | X | | 512K | +| +--------------+------+------+------+------+------+ +| | AT25F512/1024| | X | | | | +| +--------------+------+------+------+------+------+ +| | AT25FS010 | X | | | X | 128K | ++----------+--------------+------+------+------+------+------+ +| ESMT | F25L004A | X | | X | | 512K | ++----------+--------------+------+------+------+------+------+ +| MACRONIX | MX25L1005C | X | | X | | 128K | ++----------+--------------+------+------+------+------+------+ +| NUMONYX | M25P10 | | | | X | 128K | +| +--------------+------+------+------+------+------+ +| MICRON | M25P16 | | | X | | 2M | +| +--------------+------+------+------+------+------+ +| | M25PE10 | X | | X | | 128K | +| +--------------+------+------+------+------+------+ +| | M45PE10 | | | X | | | ++----------+--------------+------+------+------+------+------+ +| SST | SST25VF010 | X | X | | X | 128K | +| +--------------+------+------+------+------+------+ +| | SST25VF016 | X | X | X | | 2M | +| +--------------+------+------+------+------+------+ +| | SST25VF040 | X | X | X | | 512K | ++----------+--------------+------+------+------+------+------+ +| ST | M25PE10 | | | X | | 128K | +| +--------------+------+------+------+------+------+ +| | M25PE20 | | | X | | 256K | ++----------+--------------+------+------+------+------+------+ +| WINBOND | W25X10 | X | X | X | | 128K | +| +--------------+------+------+------+------+------+ +| | W25X20 | X | X | X | | 256K | +| +--------------+------+------+------+------+------+ +| | W25X40 | X | X | X | | 512K | ++----------+--------------+------+------+------+------+------+ + +Example +======= + +An example that uses the SPI library to read the ID: + +.. literalinclude:: app_example_flash_small/src/main.xc + :start-after: //::spi program + :end-before: //:: + +An example that reads, erases and writes data: + +.. literalinclude:: app_example_flash_small/src/main.xc + :start-after: //::flash program + :end-before: //:: + +An example that uses the flash library to store some persistent data (such +as volume, or mixer settings) with some simple wear levelling: + +.. literalinclude:: app_example_flash_small/src/main.xc + :start-after: //::persistence program + :end-before: //:: diff --git a/doc/index.rst b/doc/index.rst new file mode 100644 index 0000000..daf1fa8 --- /dev/null +++ b/doc/index.rst @@ -0,0 +1,10 @@ +Documentation +------------- + +.. toctree:: + + summary.rst + api-small.rst + + + diff --git a/doc/summary.rst b/doc/summary.rst new file mode 100644 index 0000000..43005ad --- /dev/null +++ b/doc/summary.rst @@ -0,0 +1,45 @@ +SPI Flash library +================= + +The SPI flash library interfaces to SPI flash (sometimes known as serial +flash), a loosely defined standard to talk to non volatile memory using 4 +wires: Slave-Select (SS), Master-In-Slave-Out (MISO), Master-Out-Slave-In +(MOSI), and clock (CLK). SPI flashes are made by many manufacturers in +sizes from a few tens of kilobytes to hundreds of kilobytes, in small 8-pin +SMD packages. + +Most SPI flash implement a small set of standard commands that enable cross +platform read and write functions. Erase and protect functions are less +portable. + + + +module_flash +------------ + +This module supports a general purpose flash module that supports different +flash models by means of specificaion files. The module is designed to be +inter operable with many different sorts of flash memory, and support all +operations. + + +module_flash_small +------------------ + +This module supports only few operations, and is designed to be small. Most +parameters must be compiled in (rather than run-time variable). + ++----------------------------------+------------------------+------------------------+ +| Functionality provided | Resources required | Status | +| +-----------+------------+ | +| | ports | Memory | | ++----------------------------------+-----------+------------+------------------------+ +| Read, Erase, Write | 4 | 550 bytes | Implemented | ++----------------------------------+-----------+------------+------------------------+ +| Read, Erase, Write, Persistency | 4 | 930 bytes | Implemented | ++----------------------------------+-----------+------------+------------------------+ + +The interface comprises functions for reading and writing bytes (up to a +page), and optionally for reading and writing small a small section of +persistent data in a manner that does not wear out flash. + diff --git a/module_flash_small/module_build_info b/module_flash_small/module_build_info new file mode 100644 index 0000000..e46dbf0 --- /dev/null +++ b/module_flash_small/module_build_info @@ -0,0 +1,3 @@ +OPTIONAL_HEADERS += spi_conf.h + +MODULE_XCC_FLAGS =-O2 -g diff --git a/module_flash_small/src/flash.h b/module_flash_small/src/flash.h new file mode 100644 index 0000000..399ada4 --- /dev/null +++ b/module_flash_small/src/flash.h @@ -0,0 +1,118 @@ +#ifndef FLASH_H +#define FLASH_H + +#include "spi.h" + +/** This function reads a block of data from the flash at the given + * address. + + * \param address the address to send to the SPI device. Only the least + * significant 24 bits are used. + * + * \param data an array of data to which data is to be written from + * the flash device. + * + * \param bytes The number of bytes that are to be read from the device + * into ``data``. + * + */ +void spiFlashRead(int address, char data[],int bytes); +#define spiFlashRead(address,data,bytes) spiCommandAddressStatus(SPI_CMD_READ,address,data,bytes) + +/** This function writes a small block of data to the flash at the given + * address. "Small" means that all writes must happen in the same 256 byte + * page. In other words, (address + data & 0xff) must equal (address & + * 0xff). A call can be made to spiFlashWrite() to write arbitrary size + * data to arbitrary places. A write to flash can only change bits form '1' + * to '0'. A call can be made to spiFlashErase() to set a whole sector of + * the flash to all '1'. + + * \param address the address to send to the SPI device. Only the least + * significant 24 bits are used. + * + * \param data an array of data that contains the data to be written to + * the flash device. + * + * \param bytes The number of bytes that are to be written to the device + * from ``data``. + * + */ +void spiFlashWriteSmall(int address, char data[],int bytes); + +/** This function writes a block of data to the flash at the given address. + * A write to flash can only change bits form '1' to '0'. A spiFlashErase() + * call can be made to set a whole sector of the flash to all '1'. + * + * \param address the address to send to the SPI device. Only the least + * significant 24 bits are used. + * + * \param data an array of data that contains the data to be written to + * the flash device. + * + * \param bytes The number of bytes that are to be written to the device + * from ``data``. + * + */ +void spiFlashWrite(int address, char data[],int bytes); + +/** This function erases a block of data in the flash at the given address. + * This will replace the block with all '1' bits. The address should be + * aligned on a SPI_SECTOR_SIZE boundary, and the length should be a whole + * number of SPI_SECTOR_SIZE bytes. + * + * \param address the address to send to the SPI device. Only the least + * significant 24 bits are used. + * + * \param bytes The number of bytes that are to be erased. + * + */ +void spiFlashErase(int address, int bytes); + +/** This function reads persistent data from flash memory. The size of hte + * persistent data, the base address and the segment size are all compile + * time constants (defined earlier). A not-so-small library can be made + * that provides generic functionality. + * + * This function may take some time to complete - in the worst case it will + * search the whole persistent segment for valid data. The time taken + * depends on the persisten segment size and flash speed, but may well be + * milliseconds. Note that the read position is cached, so subsequent calls + * to this function will be fast. + * + * \param data array in which the persistent data is to be stored. + * + * \returns 0 if no valid data could be found. A factory default should be + * used in this case. + */ +int spiFlashPersistentStateRead(char data[]); + +/** This function writes persistent data to flash memory. The size of hte + * persistent data, the base address and the segment size are all compile + * time constants (defined earlier). If this function returns, then the + * next call to spiFlashPersistentStateRead() will return the data that is + * written in this call. + * + * This function may take some time to complete - it will wait for data to + * be written, and it may have to erase a sector of data. The time taken + * depends mostly on flash speed, but may well be tens of milliseconds when + * a sector needs to be erased. A write operation may take a few hundred + * microseconds, and if spiFlashPersistentStateRead() has not been called + * then a full search of flash may be necessary. + * + * Many calls to this function will wear out the flash memory. A flash + * memory may wear out after as little as 100,000 cycles, so if this + * function is called every 200 us, and the segment is 8192 bytes for + * 15-bytes of data, then the memory will wear out in (8192/(15+1)) * + * 100,000 * 0.0002 seconds, which is about 3 hours. Calls to this function + * should therefore be limited (for example to at most once every few + * seconds), or the persistent segment should be made much larger. + * + * Note that there are hypothetical cases where valid data may be found if + * the erase function is interrupted by a power failure. A CRC check shall + * be performed on the data if this is an issue for the application. + * + * \param data array in which the persistent data to be written is stored. + */ +void spiFlashPersistentStateWrite(char data[]); + +#endif diff --git a/module_flash_small/src/flash.xc b/module_flash_small/src/flash.xc new file mode 100644 index 0000000..4d33896 --- /dev/null +++ b/module_flash_small/src/flash.xc @@ -0,0 +1,122 @@ +#include +#include "flash.h" +#include "flashDefaults.h" + +void spiFlashWriteSmall(int address, char data[],int bytes) { + spiCommandStatus(SPI_CMD_WRITE_ENABLE, 0); + spiCommandAddressStatus(SPI_CMD_WRITE,address,data,-(bytes)); + while(spiCommandStatus(SPI_CMD_READSR, 1) & 1) { + ; + } + spiCommandStatus(SPI_CMD_WRITE_DISABLE, 0); +} + +void spiFlashWrite(int address, char data[],int bytes) { + printf("Not supported yet - needs array slicing...\n"); + while (bytes != 0) { + int len = (address & 0xff) == ((address + bytes) & 0xff) ? bytes : 256-(address & 0xff); + spiFlashWriteSmall(address, data, len); + bytes -= len; + address += len; + } +} + +void spiFlashErase(int address, int bytes) { + char data[1]; + while (bytes > 0) { + spiCommandStatus(SPI_CMD_WRITE_ENABLE, 0); + spiCommandAddressStatus(SPI_CMD_ERASE, address, data, 0); + bytes -= SPI_SECTOR_SIZE; + address += SPI_SECTOR_SIZE; + while(spiCommandStatus(SPI_CMD_READSR, 1) & 1) { + ; + } + } + spiCommandStatus(SPI_CMD_WRITE_DISABLE, 0); +} + +static int flashStartAddress = 0; + +#define VALID 0x0F +#define INVALID 0x00 + +/* + * Persistent data is stored in flash between addresses + * FLASH_PERSISTENT_BASE and FLASH_PERSISTENT_BASE + + * FLASH_PERSISTENT_SEGMENT_SIZE. Each data item is always a power of 2 + * long, and the last byte is a guard byte which is one of 0xFF (unused), + * 0x0F (valid) or 0x00 (no longer valid). + * + * This function finds the first valid persistent data in flash. This is + * probably the most recent state, unless the syste got switched off prior + * to it being made invalid, in which case this data is still valid but + * just one step old. + */ +int spiFlashPersistentStateRead(char data[]) { + char guard[1]; + for(int i = flashStartAddress; + i < flashStartAddress + FLASH_PERSISTENT_SEGMENT_SIZE; + i+= FLASH_PERSISTENT_SIZE + 1) { + int index = (i&(FLASH_PERSISTENT_SEGMENT_SIZE-1)); + spiFlashRead(FLASH_PERSISTENT_BASE + index + FLASH_PERSISTENT_SIZE, guard, 1); + if (guard[0] == VALID) { + spiFlashRead(FLASH_PERSISTENT_BASE + index, data, FLASH_PERSISTENT_SIZE); + flashStartAddress = index; + return 1; + } + } + return 0; +} + +/* + * This function writes persistent data by first finding valid persistent + * data. If it cannot find any it will commence writing from the start + * (default writeIndex is 0), otherwise writeIndex is set to be immediately + * after the valid block. If the writeIndex is at the start of a sector, + * then the sector is erased. The data is written, the guard byte for the + * data is set to valid (in a separate write, that guarantees that the data + * write has completed before the guard byte write is completed), and + * finally the old guard byte is cleared. + * + * This process can be interrupted at the following places without disruption: + * + * After erase - it will find the old block (there are always at least + * two sectors) + * + * After write of data - the guard will not be written, so it will not + * find the next one valid + * + * After the guard is written - a read will still find the old data, + * leading to this being erased again + * + * After the old guard is cleared - this will now have progressed the + * state. + * + * If interrupt during erase, there is a chance that any guard is set to + * 'valid'. This code cannot deal with it, but a full and large version + * would put a CRC over the data to validate the guard. + */ +void spiFlashPersistentStateWrite(char data[]) { + char guard[1]; + int i, writeIndex = 0, clearIndex = FLASH_PERSISTENT_SEGMENT_SIZE - FLASH_PERSISTENT_SIZE; + for(i = flashStartAddress; + i < flashStartAddress + FLASH_PERSISTENT_SEGMENT_SIZE; + i+= FLASH_PERSISTENT_SIZE + 1) { + int readIndex = (i&(FLASH_PERSISTENT_SEGMENT_SIZE-1)); + spiFlashRead(FLASH_PERSISTENT_BASE + readIndex + FLASH_PERSISTENT_SIZE, guard, 1); + if (guard[0] == VALID) { + clearIndex = readIndex; + writeIndex = (readIndex+FLASH_PERSISTENT_SIZE+1)&(FLASH_PERSISTENT_SEGMENT_SIZE-1); + break; + } + } + if ((writeIndex & (FLASH_PERSISTENT_SECTOR_SIZE -1)) == 0) { + spiFlashErase(FLASH_PERSISTENT_BASE + writeIndex, FLASH_PERSISTENT_SECTOR_SIZE); + } + spiFlashWriteSmall(FLASH_PERSISTENT_BASE + writeIndex, data, 15); + guard[0] = VALID; + spiFlashWriteSmall(FLASH_PERSISTENT_BASE + writeIndex + FLASH_PERSISTENT_SIZE, guard, 1); + guard[0] = INVALID; + spiFlashWriteSmall(FLASH_PERSISTENT_BASE + clearIndex + FLASH_PERSISTENT_SIZE, guard, 1); + flashStartAddress = writeIndex; +} diff --git a/module_flash_small/src/flashDefaults.h b/module_flash_small/src/flashDefaults.h new file mode 100644 index 0000000..252272e --- /dev/null +++ b/module_flash_small/src/flashDefaults.h @@ -0,0 +1,33 @@ +#ifndef FLASH_PERSISTENT_SIZE +#define FLASH_PERSISTENT_SIZE 15 +#endif + +#ifndef FLASH_PERSISTENT_BASE +#define FLASH_PERSISTENT_BASE 65536 +#endif + +#ifndef FLASH_PERSISTENT_SEGMENT_SIZE +#define FLASH_PERSISTENT_SEGMENT_SIZE (2*SPI_SECTOR_SIZE) +#endif + +#define FLASH_PERSISTENT_SECTOR_SIZE SPI_SECTOR_SIZE + +#if ((FLASH_PERSISTENT_SIZE & (FLASH_PERSISTENT_SIZE+1)) != 0) +#error "FLASH_PERSISTENT_SIZE should be a power of 2 minus one" +#endif + +#if ((FLASH_PERSISTENT_SEGMENT_SIZE & (FLASH_PERSISTENT_SEGMENT_SIZE-1)) != 0) +#error "FLASH_PERSISTENT_SEGMENT_SIZE should be a power of 2" +#endif + +#if ((FLASH_PERSISTENT_SECTOR_SIZE & (FLASH_PERSISTENT_SECTOR_SIZE-1)) != 0) +#error "FLASH_PERSISTENT_SEGMENT_SIZE should be a power of 2" +#endif + +#if (FLASH_PERSISTENT_SECTOR_SIZE < 4096) +#error "FLASH_PERSISTENT_SECTOR_SIZE should not be less than 4096" +#endif + +#if (FLASH_PERSISTENT_SEGMENT_SIZE < FLASH_PERSISTENT_SECTOR_SIZE * 2) +#error "FLASH_PERSISTENT_SEGMENT_SIZE should not be less than 2 * FLASH_PERSISTENT_SECTOR_SIZE" +#endif diff --git a/module_flash_small/src/spi.h b/module_flash_small/src/spi.h new file mode 100644 index 0000000..f258518 --- /dev/null +++ b/module_flash_small/src/spi.h @@ -0,0 +1,67 @@ +#ifndef SPI_H +#define SPI_H + +#ifdef __spi_conf_h_exists__ +#include "spi_conf.h" +#endif + +#define SPI_CMD_WRITE_ENABLE 0x06 /**< SPI command to enable write and erase operations */ +#define SPI_CMD_WRITE_DISABLE 0x04 +#define SPI_CMD_WRITE 0x02 +#define SPI_CMD_READ 0x03 +#define SPI_CMD_READSR 0x05 +#define SPI_CMD_READID 0x9f + +#ifndef SPI_CMD_ERASE +#define SPI_CMD_ERASE 0x20 +#endif + +#ifndef SPI_SECTOR_SIZE +#define SPI_SECTOR_SIZE 4096 +#endif + +#ifndef SPI_CLK_MHZ +#define SPI_CLK_MHZ 25 +#endif + +/** This function is to be called prior to any SPI operation. It initialises the ports. + */ +void spiInit(); + +/** This function issues a single command without parameters to the SPI, + * and reads up to 4 bytes status value from the device. + * + * \param cmd command value - listed above + * + * \param returnBytes The number of bytes that are to be read from the + * device after the command is issued. 0 means no bytes + * will be read. + * + * \returns the read bytes, or zero if no bytes were requested. If multiple + * bytes are requested, then the last byte read is in the least-significant + * byte of the return value. + */ +int spiCommandStatus(int cmd, int returnBytes); + +/** This function issues a single command with a 3-byte address parameter + * and an optional data-set to be output to or input form the device. + * + * \param cmd command value - listed above + * + * \param address the address to send to the SPI device. Only the least + * significant 24 bits are used. + * + * \param data an array of data that contains either data to be written to + * the device, or which is used to store that that is + * read from the device. + * + * \param returnBytes If positive, then this is the number of bytes that + * are to be read from the device, into ``data``. If + * negative, then this is (minus) the number of bytes to + * be written to the device from ``data``. 0 means no + * bytes will be read or written. + * + */ +void spiCommandAddressStatus(int cmd, int address, char data[], int returnBytes); + +#endif diff --git a/module_flash_small/src/spi.xc b/module_flash_small/src/spi.xc new file mode 100644 index 0000000..3a0d2d1 --- /dev/null +++ b/module_flash_small/src/spi.xc @@ -0,0 +1,75 @@ +#include +#include +#include "spi.h" +#include +#include + +out port spiSS = PORT_SPI_SS; +buffered out port:32 spiCLK = PORT_SPI_CLK; +buffered in port:8 spiMISO = PORT_SPI_MISO; +buffered out port:8 spiMOSI = PORT_SPI_MOSI; + +clock SPIclock = XS1_CLKBLK_1; + +void spiInit() { + spiSS <: ~0; + spiCLK <: ~0; + + configure_clock_src(SPIclock, spiCLK); + start_clock(SPIclock); + + configure_out_port(spiMOSI, SPIclock, 0); + configure_in_port(spiMISO, SPIclock); +} + +#if (SPI_CLK_MHZ == 25) +#define eightPulses(clk) { clk <: 0xCCCCCCCC;} +#elif (SPI_CLK_MHZ == 13) +#define eightPulses(clk) { clk <: 0xF0F0F0F0; clk <: 0xF0F0F0F0;} +#else +#error "Undefined SPI_CLK_MHZ speed - must be one of 25 or 13" +#endif + +static void spiCmd(int cmd) { + spiSS <: 0; + clearbuf(spiMOSI); + spiMOSI <: bitrev(cmd<<24); + eightPulses(spiCLK); + sync(spiCLK); +} + +int spiCommandStatus(int cmd, int returnBytes) { + int data = 0; + spiCmd(cmd); + clearbuf(spiMISO); + while(returnBytes--) { + eightPulses(spiCLK); + spiMISO :> >> data; + } + spiSS <: 1; + return bitrev(data); +} + +void spiCommandAddressStatus(int cmd, int addr, char data[], int returnBytes) { + spiCmd(cmd); + addr = bitrev(addr << 8); + spiMOSI <: >> addr; + eightPulses(spiCLK); + spiMOSI <: >> addr; + eightPulses(spiCLK); + spiMOSI <: >> addr; + eightPulses(spiCLK); + for(int i = 0; i < -returnBytes; i++) { + spiMOSI <: bitrev(data[i]<<24); + eightPulses(spiCLK); + } + sync(spiCLK); + clearbuf(spiMISO); + for(int i = 0; i < returnBytes; i++) { + int x; + eightPulses(spiCLK); + spiMISO :> x; + data[i] = bitrev(x<<24); + } + spiSS <: 1; +}