From c853af991b729e6780a5101db3d4433b266c72dc Mon Sep 17 00:00:00 2001 From: Henk Muller Date: Fri, 6 Jul 2012 18:44:53 +0100 Subject: [PATCH 01/13] Working small flash library - universal read and write at sinle speed to single device --- app_example_flash_small/Makefile | 9 +++ app_example_flash_small/src/main.xc | 32 ++++++++++ module_flash_small/module_build_info | 1 + module_flash_small/src/lld.h | 67 ++++++++++++++++++++ module_flash_small/src/lld.xc | 91 ++++++++++++++++++++++++++++ 5 files changed, 200 insertions(+) create mode 100644 app_example_flash_small/Makefile create mode 100644 app_example_flash_small/src/main.xc create mode 100644 module_flash_small/module_build_info create mode 100644 module_flash_small/src/lld.h create mode 100644 module_flash_small/src/lld.xc 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..4267f97 --- /dev/null +++ b/app_example_flash_small/src/main.xc @@ -0,0 +1,32 @@ +#include +#include + +main() { + char data[256]; + int addr = 0x18800; + spiInit(); + printf("ID4: %08x\n",spiCommandStatus(SPI_CMD_READID, 4)); + printf("ID4: %08x\n",spiCommandStatus(SPI_CMD_READID, 4)); + printf("ID4: %08x\n",spiCommandStatus(SPI_CMD_READID, 4)); + printf("ID4: %08x\n",spiCommandStatus(SPI_CMD_READID, 4)); + printf("SR: %08x\n", spiCommandStatus(SPI_CMD_READSR, 1)); + printf("SR: %08x\n", spiCommandStatus(SPI_CMD_READSR, 1)); + printf("SR: %08x\n", spiCommandStatus(SPI_CMD_READSR, 1)); + printf("SR: %08x\n", spiCommandStatus(SPI_CMD_READSR, 1)); + + for(int i = 0; i < 128; i++) { +// printf("%02x: %02x\n", i, spiCommandStatus(i, 1)); + } + spiCommandAddressStatus(SPI_CMD_READ, addr, data, 32); + for(int i = 0; i < 32; i++) { + printf(" %02x", data[i]); + data[i] &= (1<<(i&7)); + } + printf("\n"); + spiFlashWrite(addr, data, 32); + spiCommandAddressStatus(SPI_CMD_READ, addr, data, 32); + for(int i = 0; i < 32; i++) { + printf(" %02x", data[i]); + } + printf("\n"); +} diff --git a/module_flash_small/module_build_info b/module_flash_small/module_build_info new file mode 100644 index 0000000..d038a2d --- /dev/null +++ b/module_flash_small/module_build_info @@ -0,0 +1 @@ +MODULE_XCC_FLAGS = -O3 -g diff --git a/module_flash_small/src/lld.h b/module_flash_small/src/lld.h new file mode 100644 index 0000000..8536b14 --- /dev/null +++ b/module_flash_small/src/lld.h @@ -0,0 +1,67 @@ +#define SPI_CMD_WRITE_ENABLE 0x06 +#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 + +/** 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); + +#define spiFlashRead(address,data,bytes) spiCommandAddressStatus(SPI_CMD_READ,address,data,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'. Note + * that (address + data & 0xff) must equal (address & 0xff); that is, all + * writes must happen in the same 256 byte page. + * + * \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); diff --git a/module_flash_small/src/lld.xc b/module_flash_small/src/lld.xc new file mode 100644 index 0000000..919aefe --- /dev/null +++ b/module_flash_small/src/lld.xc @@ -0,0 +1,91 @@ +#include +#include +#include "lld.h" +#include +#include + +#define FLASH_25MHz +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 defined(FLASH_50MHz) +#define eightPulses(clk) { clk <: 0xAAAA;} +#elif defined(FLASH_25MHz) +#define eightPulses(clk) { clk <: 0xCCCCCCCC;} +#elif defined(FLASH_12_5MHz) +#define eightPulses(clk) { clk <: 0xF0F0F0F0; clk <: 0xF0F0F0F0;} +#elif defined(FLASH_1_6MHz) +#define eightPulses(clk) { for(int i = 0; i < 8; i++) { clk <: 0; clk <: ~0;}} +#elif defined(FLASH_0_8MHz) +#define eightPulses(clk) { for(int i = 0; i < 8; i++) { clk <: 0; clk <: 0; clk <: ~0; clk <: ~0;}} +#else +#error "Undefined FLASH speed - must be one of 50, 25, or 12_5" +#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; +} + +void spiFlashWrite(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); +} From 6f7b6d0b003a7cd98687635c301654da031e47fe Mon Sep 17 00:00:00 2001 From: Henk Muller Date: Mon, 9 Jul 2012 08:29:21 +0100 Subject: [PATCH 02/13] With erase function --- app_example_flash_small/src/main.xc | 14 ++++++-- module_flash_small/src/flash.h | 51 +++++++++++++++++++++++++++++ module_flash_small/src/flash.xc | 40 ++++++++++++++++++++++ module_flash_small/src/lld.h | 24 ++------------ module_flash_small/src/lld.xc | 9 ----- 5 files changed, 105 insertions(+), 33 deletions(-) create mode 100644 module_flash_small/src/flash.h create mode 100644 module_flash_small/src/flash.xc diff --git a/app_example_flash_small/src/main.xc b/app_example_flash_small/src/main.xc index 4267f97..38a48d7 100644 --- a/app_example_flash_small/src/main.xc +++ b/app_example_flash_small/src/main.xc @@ -1,4 +1,5 @@ #include +#include #include main() { @@ -17,14 +18,21 @@ main() { for(int i = 0; i < 128; i++) { // printf("%02x: %02x\n", i, spiCommandStatus(i, 1)); } - spiCommandAddressStatus(SPI_CMD_READ, addr, data, 32); + spiFlashRead(addr, data, 32); for(int i = 0; i < 32; i++) { printf(" %02x", data[i]); data[i] &= (1<<(i&7)); } printf("\n"); - spiFlashWrite(addr, data, 32); - spiCommandAddressStatus(SPI_CMD_READ, addr, data, 32); + spiFlashErase4K(addr, 32); + spiFlashRead(addr, data, 32); + for(int i = 0; i < 32; i++) { + printf(" %02x", data[i]); + data[i] &= (1<<(i&7)); + } + printf("\n"); + spiFlashWriteSmall(addr, data, 32); + spiFlashRead(addr, data, 32); for(int i = 0; i < 32; i++) { printf(" %02x", data[i]); } diff --git a/module_flash_small/src/flash.h b/module_flash_small/src/flash.h new file mode 100644 index 0000000..b8ee21f --- /dev/null +++ b/module_flash_small/src/flash.h @@ -0,0 +1,51 @@ + +#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 4K boundary, and the length should be a whole number of 4K + * blocks. + * + * \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 spiFlashErase4K(int address, int bytes); diff --git a/module_flash_small/src/flash.xc b/module_flash_small/src/flash.xc new file mode 100644 index 0000000..52e8c60 --- /dev/null +++ b/module_flash_small/src/flash.xc @@ -0,0 +1,40 @@ +#include +#include "flash.h" +#include "lld.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 spiFlashErase4K(int address, int bytes) { + char data[1]; + spiCommandStatus(SPI_CMD_WRITE_ENABLE, 0); + while (bytes > 0) { + spiCommandAddressStatus(SPI_CMD_ERASE_4K, address, data, 0); + bytes -= 4096; + address += 4096; + while(spiCommandStatus(SPI_CMD_READSR, 1) & 1) { + ; + } + } + spiCommandStatus(SPI_CMD_WRITE_DISABLE, 0); +} + +void spiFlashStorePersistentState(int address, char data[], int bytes) { + +} diff --git a/module_flash_small/src/lld.h b/module_flash_small/src/lld.h index 8536b14..36688b9 100644 --- a/module_flash_small/src/lld.h +++ b/module_flash_small/src/lld.h @@ -1,6 +1,9 @@ #define SPI_CMD_WRITE_ENABLE 0x06 #define SPI_CMD_WRITE_DISABLE 0x04 #define SPI_CMD_WRITE 0x02 +#define SPI_CMD_ERASE_4K 0x20 +#define SPI_CMD_ERASE_32K 0x52 +#define SPI_CMD_ERASE_64K 0xD8 #define SPI_CMD_READ 0x03 #define SPI_CMD_READSR 0x05 #define SPI_CMD_READID 0x9f @@ -44,24 +47,3 @@ int spiCommandStatus(int cmd, int returnBytes); * */ void spiCommandAddressStatus(int cmd, int address, char data[], int returnBytes); - -#define spiFlashRead(address,data,bytes) spiCommandAddressStatus(SPI_CMD_READ,address,data,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'. Note - * that (address + data & 0xff) must equal (address & 0xff); that is, all - * writes must happen in the same 256 byte page. - * - * \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); diff --git a/module_flash_small/src/lld.xc b/module_flash_small/src/lld.xc index 919aefe..9e8b954 100644 --- a/module_flash_small/src/lld.xc +++ b/module_flash_small/src/lld.xc @@ -80,12 +80,3 @@ void spiCommandAddressStatus(int cmd, int addr, char data[], int returnBytes) { } spiSS <: 1; } - -void spiFlashWrite(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); -} From e605a49c21de378067ac90d1f95451176474f3d0 Mon Sep 17 00:00:00 2001 From: Henk Muller Date: Mon, 9 Jul 2012 09:33:23 +0100 Subject: [PATCH 03/13] New file for persistent state --- module_flash_small/src/flash.h | 2 + module_flash_small/src/flash.xc | 83 ++++++++++++++++++++++++++++++++- 2 files changed, 83 insertions(+), 2 deletions(-) diff --git a/module_flash_small/src/flash.h b/module_flash_small/src/flash.h index b8ee21f..f297051 100644 --- a/module_flash_small/src/flash.h +++ b/module_flash_small/src/flash.h @@ -49,3 +49,5 @@ void spiFlashWrite(int address, char data[],int bytes); * */ void spiFlashErase4K(int address, int bytes); + +int spiFlashPersistentStateRead(char data[]); diff --git a/module_flash_small/src/flash.xc b/module_flash_small/src/flash.xc index 52e8c60..71451f4 100644 --- a/module_flash_small/src/flash.xc +++ b/module_flash_small/src/flash.xc @@ -1,6 +1,7 @@ #include #include "flash.h" #include "lld.h" +#include "flashDefaults.h" void spiFlashWriteSmall(int address, char data[],int bytes) { spiCommandStatus(SPI_CMD_WRITE_ENABLE, 0); @@ -35,6 +36,84 @@ void spiFlashErase4K(int address, int bytes) { spiCommandStatus(SPI_CMD_WRITE_DISABLE, 0); } -void spiFlashStorePersistentState(int address, char data[], int bytes) { - +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[]) { + 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, data, FLASH_PERSISTENT_SIZE + 1); + if (data[FLASH_PERSISTENT_SIZE] == VALID) { + 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 eb 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. + */ +int spiFlashPersistentStateWrite(char data[]) { + char guard[1]; + int i, readIndex, writeIndex = 0; + for(i = flashStartAddress + FLASH_PERSISTENT_SIZE; + i < flashStartAddress + FLASH_PERSISTENT_SEGMENT_SIZE; + i+= FLASH_PERSISTENT_SIZE + 1) { + readIndex = (i&(FLASH_PERSISTENT_SEGMENT_SIZE-1)); + spiFlashRead(FLASH_PERSISTENT_BASE + readIndex, guard, 1); + if (guard[0] == VALID) { + writeIndex = (readIndex+FLASH_PERSISTENT_SIZE)&(FLASH_PERSISTENT_SEGMENT_SIZE-1); + break; + } + } + if ((writeIndex & (FLASH_PERSISTENT_SECTOR_SIZE -1)) == 0) { + spiFlashErase4K(FLASH_PERSISTENT_BASE + writeIndex, FLASH_PERSISTENT_SECTOR_SIZE); + } + spiFlashWrite(FLASH_PERSISTENT_BASE + writeIndex, data, 15); + spiFlashWrite(FLASH_PERSISTENT_BASE + writeIndex + FLASH_PERSISTENT_SIZE, guard, 1); + guard[0] = INVALID; + spiFlashWrite(FLASH_PERSISTENT_BASE + readIndex, guard, 1); + flashStartAddress = readIndex; } From b3d3dc448d4bf29ff4871c0a8d84749aae011b44 Mon Sep 17 00:00:00 2001 From: Henk Muller Date: Mon, 9 Jul 2012 09:33:45 +0100 Subject: [PATCH 04/13] New file for persistent state --- module_flash_small/src/flashDefaults.h | 35 ++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) create mode 100644 module_flash_small/src/flashDefaults.h diff --git a/module_flash_small/src/flashDefaults.h b/module_flash_small/src/flashDefaults.h new file mode 100644 index 0000000..5e30c78 --- /dev/null +++ b/module_flash_small/src/flashDefaults.h @@ -0,0 +1,35 @@ +#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 32768 +#endif + +#ifndef FLASH_PERSISTENT_SECTOR_SIZE +#define FLASH_PERSISTENT_SECTOR_SIZE 4096 +#endif + +#if ((FLASH_PERSISTENT_SIZE & (FLASH_PERSISTENT_SIZE+1)) != 0) +#error "FLASH_PERSISTENT_SIZE should be a power of 2" +#endif + +#if ((FLASH_PERSISTENT_SEGMENT_SIZE & (FLASH_PERSISTENT_SEGMENT_SIZE+1)) != 0) +#error "FLASH_PERSISTENT_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 From 9a484c6a5d611d61bc251f56c5ce08a2d804cbd2 Mon Sep 17 00:00:00 2001 From: Henk Muller Date: Mon, 9 Jul 2012 11:09:05 +0100 Subject: [PATCH 05/13] Workign persistent state storage and recovery --- app_example_flash_small/src/main.xc | 25 +++++++++++++++++ module_flash_small/src/flash.h | 37 ++++++++++++++++++++++++++ module_flash_small/src/flash.xc | 22 ++++++++------- module_flash_small/src/flashDefaults.h | 10 +++---- 4 files changed, 79 insertions(+), 15 deletions(-) diff --git a/app_example_flash_small/src/main.xc b/app_example_flash_small/src/main.xc index 38a48d7..b053876 100644 --- a/app_example_flash_small/src/main.xc +++ b/app_example_flash_small/src/main.xc @@ -5,6 +5,7 @@ main() { char data[256]; int addr = 0x18800; + int valid; spiInit(); printf("ID4: %08x\n",spiCommandStatus(SPI_CMD_READID, 4)); printf("ID4: %08x\n",spiCommandStatus(SPI_CMD_READID, 4)); @@ -37,4 +38,28 @@ main() { printf(" %02x", data[i]); } printf("\n"); + + for(int i = 0; i < 100; i++) { + timer t; + int t0, t1, t2, t3; + t :> t0; + valid = spiFlashPersistentStateRead(data); + t :> t1; + if (valid) { + for(int i = 0; i < 15; i++) { + printf(" %02x", data[i]); + } + for(int i = 0; i < 15; i++) { + data[i] += i; + } + } else { + for(int i = 0; i < 15; i++) { + data[i] = 1; + } + } + t :> t2; + spiFlashPersistentStateWrite(data); + t :> t3; + printf(" <<< Persistent; read: %d write: %d\n", t1-t0, t3-t2); + } } diff --git a/module_flash_small/src/flash.h b/module_flash_small/src/flash.h index f297051..ea11652 100644 --- a/module_flash_small/src/flash.h +++ b/module_flash_small/src/flash.h @@ -50,4 +50,41 @@ void spiFlashWrite(int address, char data[],int bytes); */ void spiFlashErase4K(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. + * + * 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[]); diff --git a/module_flash_small/src/flash.xc b/module_flash_small/src/flash.xc index 71451f4..eac8574 100644 --- a/module_flash_small/src/flash.xc +++ b/module_flash_small/src/flash.xc @@ -95,25 +95,27 @@ int spiFlashPersistentStateRead(char data[]) { * 'valid'. This code cannot deal with it, but a full and large version * would put a CRC over the data to validate the guard. */ -int spiFlashPersistentStateWrite(char data[]) { +void spiFlashPersistentStateWrite(char data[]) { char guard[1]; - int i, readIndex, writeIndex = 0; - for(i = flashStartAddress + FLASH_PERSISTENT_SIZE; + 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) { - readIndex = (i&(FLASH_PERSISTENT_SEGMENT_SIZE-1)); - spiFlashRead(FLASH_PERSISTENT_BASE + readIndex, guard, 1); + int readIndex = (i&(FLASH_PERSISTENT_SEGMENT_SIZE-1)); + spiFlashRead(FLASH_PERSISTENT_BASE + readIndex + FLASH_PERSISTENT_SIZE, guard, 1); if (guard[0] == VALID) { - writeIndex = (readIndex+FLASH_PERSISTENT_SIZE)&(FLASH_PERSISTENT_SEGMENT_SIZE-1); + clearIndex = readIndex; + writeIndex = (readIndex+FLASH_PERSISTENT_SIZE+1)&(FLASH_PERSISTENT_SEGMENT_SIZE-1); break; } } if ((writeIndex & (FLASH_PERSISTENT_SECTOR_SIZE -1)) == 0) { spiFlashErase4K(FLASH_PERSISTENT_BASE + writeIndex, FLASH_PERSISTENT_SECTOR_SIZE); } - spiFlashWrite(FLASH_PERSISTENT_BASE + writeIndex, data, 15); - spiFlashWrite(FLASH_PERSISTENT_BASE + writeIndex + FLASH_PERSISTENT_SIZE, guard, 1); + spiFlashWriteSmall(FLASH_PERSISTENT_BASE + writeIndex, data, 15); + guard[0] = VALID; + spiFlashWriteSmall(FLASH_PERSISTENT_BASE + writeIndex + FLASH_PERSISTENT_SIZE, guard, 1); guard[0] = INVALID; - spiFlashWrite(FLASH_PERSISTENT_BASE + readIndex, guard, 1); - flashStartAddress = readIndex; + 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 index 5e30c78..91629b3 100644 --- a/module_flash_small/src/flashDefaults.h +++ b/module_flash_small/src/flashDefaults.h @@ -7,7 +7,7 @@ #endif #ifndef FLASH_PERSISTENT_SEGMENT_SIZE -#define FLASH_PERSISTENT_SEGMENT_SIZE 32768 +#define FLASH_PERSISTENT_SEGMENT_SIZE 8192 #endif #ifndef FLASH_PERSISTENT_SECTOR_SIZE @@ -15,14 +15,14 @@ #endif #if ((FLASH_PERSISTENT_SIZE & (FLASH_PERSISTENT_SIZE+1)) != 0) -#error "FLASH_PERSISTENT_SIZE should be a power of 2" +#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_SIZE should be a power of 2" +#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) +#if ((FLASH_PERSISTENT_SECTOR_SIZE & (FLASH_PERSISTENT_SECTOR_SIZE-1)) != 0) #error "FLASH_PERSISTENT_SEGMENT_SIZE should be a power of 2" #endif From 653b3b6e05ad4b3b9893d54ae944dd1947cbb7a1 Mon Sep 17 00:00:00 2001 From: Henk Muller Date: Mon, 9 Jul 2012 11:15:47 +0100 Subject: [PATCH 06/13] Update to documentation of persistent state write --- module_flash_small/src/flash.h | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/module_flash_small/src/flash.h b/module_flash_small/src/flash.h index ea11652..54e29d3 100644 --- a/module_flash_small/src/flash.h +++ b/module_flash_small/src/flash.h @@ -81,6 +81,14 @@ int spiFlashPersistentStateRead(char data[]); * 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. From 96749360888864186ef5a12de8d66a52885edff5 Mon Sep 17 00:00:00 2001 From: Henk Muller Date: Mon, 9 Jul 2012 12:00:09 +0100 Subject: [PATCH 07/13] Documentation - v0 --- doc/Makefile | 9 ++++++ doc/api-small.rst | 31 +++++++++++++++++++ doc/index.rst | 10 +++++++ doc/summary.rst | 45 ++++++++++++++++++++++++++++ module_flash_small/module_build_info | 2 +- 5 files changed, 96 insertions(+), 1 deletion(-) create mode 100644 doc/Makefile create mode 100644 doc/api-small.rst create mode 100644 doc/index.rst create mode 100644 doc/summary.rst 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..006fa98 --- /dev/null +++ b/doc/api-small.rst @@ -0,0 +1,31 @@ +module_flash_small +'''''''''''''''''' + +This module implements a simple flash library, sufficient for DFU and +similar applications. + +API +=== + + +.. doxygenfunction:: spiFlashRead +.. doxygenfunction:: spiFlashWriteSmall +.. doxygenfunction:: spiFlashWrite +.. doxygenfunction:: spiFlashErase4K +.. doxygenfunction:: spiFlashPersistentStateRead +.. doxygenfunction:: spiFlashPersistentStateWrite + + +Example +======= + +To be provided. + +.. literalinclude:: app_example_flash_small/src/main.xc + :start-after: //::declaration + :end-before: //:: + + +.. literalinclude:: app_example_flash_small/src/main.xc + :start-after: //::main 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 index d038a2d..50b4537 100644 --- a/module_flash_small/module_build_info +++ b/module_flash_small/module_build_info @@ -1 +1 @@ -MODULE_XCC_FLAGS = -O3 -g +MODULE_XCC_FLAGS = -fllvm -O2 -g From 98abb1b593217cd0587f9497c6d3fcd02819cc31 Mon Sep 17 00:00:00 2001 From: Henk Muller Date: Mon, 9 Jul 2012 12:03:18 +0100 Subject: [PATCH 08/13] With first version of documentation completed --- module_flash_small/src/flash.h | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/module_flash_small/src/flash.h b/module_flash_small/src/flash.h index 54e29d3..ac78b4c 100644 --- a/module_flash_small/src/flash.h +++ b/module_flash_small/src/flash.h @@ -1,4 +1,17 @@ +/** 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 From 5af1f9c8b1ea4c7310ad457d4cfc054cdcc3af68 Mon Sep 17 00:00:00 2001 From: Henk Muller Date: Mon, 9 Jul 2012 16:31:28 +0100 Subject: [PATCH 09/13] List of compatible flashes --- app_example_flash_small/src/main.xc | 77 ++++++++++++------------ doc/api-small.rst | 92 +++++++++++++++++++++++++++-- module_flash_small/src/flash.h | 7 +++ module_flash_small/src/spi.h | 56 ++++++++++++++++++ module_flash_small/src/spi.xc | 82 +++++++++++++++++++++++++ 5 files changed, 271 insertions(+), 43 deletions(-) create mode 100644 module_flash_small/src/spi.h create mode 100644 module_flash_small/src/spi.xc diff --git a/app_example_flash_small/src/main.xc b/app_example_flash_small/src/main.xc index b053876..5ecaf82 100644 --- a/app_example_flash_small/src/main.xc +++ b/app_example_flash_small/src/main.xc @@ -2,34 +2,36 @@ #include #include -main() { +//::spi program +#include + +useSPI() { + spiInit(); + printf("ID4: %08x\n",spiCommandStatus(SPI_CMD_READID, 4)); +} +//:: + +//::flash program +#include + +useFlash() { char data[256]; int addr = 0x18800; - int valid; spiInit(); - printf("ID4: %08x\n",spiCommandStatus(SPI_CMD_READID, 4)); - printf("ID4: %08x\n",spiCommandStatus(SPI_CMD_READID, 4)); - printf("ID4: %08x\n",spiCommandStatus(SPI_CMD_READID, 4)); - printf("ID4: %08x\n",spiCommandStatus(SPI_CMD_READID, 4)); - printf("SR: %08x\n", spiCommandStatus(SPI_CMD_READSR, 1)); - printf("SR: %08x\n", spiCommandStatus(SPI_CMD_READSR, 1)); - printf("SR: %08x\n", spiCommandStatus(SPI_CMD_READSR, 1)); - printf("SR: %08x\n", spiCommandStatus(SPI_CMD_READSR, 1)); - for(int i = 0; i < 128; i++) { -// printf("%02x: %02x\n", i, spiCommandStatus(i, 1)); - } spiFlashRead(addr, data, 32); for(int i = 0; i < 32; i++) { printf(" %02x", data[i]); - data[i] &= (1<<(i&7)); } printf("\n"); spiFlashErase4K(addr, 32); spiFlashRead(addr, data, 32); for(int i = 0; i < 32; i++) { printf(" %02x", data[i]); - data[i] &= (1<<(i&7)); + } + printf("\n"); + for(int i = 0; i < 32; i++) { + data[i] = i; } printf("\n"); spiFlashWriteSmall(addr, data, 32); @@ -38,28 +40,29 @@ main() { printf(" %02x", data[i]); } printf("\n"); +} +//:: - for(int i = 0; i < 100; i++) { - timer t; - int t0, t1, t2, t3; - t :> t0; - valid = spiFlashPersistentStateRead(data); - t :> t1; - if (valid) { - for(int i = 0; i < 15; i++) { - printf(" %02x", data[i]); - } - for(int i = 0; i < 15; i++) { - data[i] += i; - } - } else { - for(int i = 0; i < 15; i++) { - data[i] = 1; - } - } - t :> t2; - spiFlashPersistentStateWrite(data); - t :> t3; - printf(" <<< Persistent; read: %d write: %d\n", t1-t0, t3-t2); +//::persistence program +#include + +usePersistence() { + char data[16]; + int valid; + spiInit(); + + valid = spiFlashPersistentStateRead(data); + if (!valid) { + // fill data with factory default } + // update state + + spiFlashPersistentStateWrite(data); +} +//:: + +main() { + useSPI(); + useFlash(); + usePersistence(); } diff --git a/doc/api-small.rst b/doc/api-small.rst index 006fa98..2b887cc 100644 --- a/doc/api-small.rst +++ b/doc/api-small.rst @@ -2,11 +2,22 @@ module_flash_small '''''''''''''''''' This module implements a simple flash library, sufficient for DFU and -similar applications. +similar applications. Two files are to be included ``lld.h`` and +``flash.h``. -API -=== +SPI API +======= + +.. doxygenfunction:: spiInit +.. doxygenfunction:: spiCommandStatus +.. doxygenfunction:: spiCommandAddressStatus + +.. doxygendefine:: SPI_CMD_WRITE_ENABLE +.. doxygendefine:: SPI_CMD_WRITE_DISABLE + +Flash API +========= .. doxygenfunction:: spiFlashRead .. doxygenfunction:: spiFlashWriteSmall @@ -16,16 +27,85 @@ API .. 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. + ++-------------------------+------+------+------+------+ +| Erase code | 0x20 | 0x52 | 0xD8 | 0xD8 | ++-------------------------+------+------+------+------+ +| Erase sector size | 4K | 32K | 64K | 32K | ++----------+--------------+------+------+------+------+ +| ALTERA | EPCS1 | | | | X | +| +--------------+------+------+------+------+ +| | EPCS4/16/64 | | | X | | ++----------+--------------+------+------+------+------+ +| AMIC | A25L016 | X | | | | +| +--------------+------+------+------+------+ +| | A25L40 | | | X | | +| +--------------+------+------+------+------+ +| | A25L80 | | | X | | ++----------+--------------+------+------+------+------+ +| ATMEL | AT25DF021 | X | | X | | +| +--------------+------+------+------+------+ +| | AT25DF041A | X | | X | | +| +--------------+------+------+------+------+ +| | AT25F512/1024| | X | | | +| +--------------+------+------+------+------+ +| | AT25FS010 | X | | | X | ++----------+--------------+------+------+------+------+ +| ESMT | F25L004A | X | | X | | ++----------+--------------+------+------+------+------+ +| MACRONIX | MX25L1005C | X | | X | | ++----------+--------------+------+------+------+------+ +| NUMONYX | M25P10 | | | | X | +| +--------------+------+------+------+------+ +| | M25P16 | | | X | | +| +--------------+------+------+------+------+ +| | M25PE10 | X | | X | | +| +--------------+------+------+------+------+ +| | M45PE10 | | | X | | ++----------+--------------+------+------+------+------+ +| SST | SST25VF010 | X | X | | X | +| +--------------+------+------+------+------+ +| | SST25VF016 | X | X | X | | +| +--------------+------+------+------+------+ +| | SST25VF040 | X | X | X | | ++----------+--------------+------+------+------+------+ +| ST | M25PE10/20 | | | X | | ++----------+--------------+------+------+------+------+ +| WINBOND | W25X10/20/40 | X | X | X | | ++----------+--------------+------+------+------+------+ + Example ======= -To be provided. +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 SPI library to read the ID: .. literalinclude:: app_example_flash_small/src/main.xc - :start-after: //::declaration + :start-after: //::spi 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: //::main program + :start-after: //::persistence program :end-before: //:: diff --git a/module_flash_small/src/flash.h b/module_flash_small/src/flash.h index ac78b4c..f25a665 100644 --- a/module_flash_small/src/flash.h +++ b/module_flash_small/src/flash.h @@ -1,3 +1,8 @@ +#ifndef FLASH_H +#define FLASH_H + +#include "spih" + /** This function reads a block of data from the flash at the given * address. @@ -109,3 +114,5 @@ int spiFlashPersistentStateRead(char data[]); * \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/spi.h b/module_flash_small/src/spi.h new file mode 100644 index 0000000..21d26ac --- /dev/null +++ b/module_flash_small/src/spi.h @@ -0,0 +1,56 @@ +#ifndef SPI_H +#define SPI_H + +#define SPI_CMD_WRITE_ENABLE 0x06 /**< SPI command to enable write and erase operations */ + +/** \def SPI command to disable write and erase operations */ +#define SPI_CMD_WRITE_DISABLE 0x04 +#define SPI_CMD_WRITE 0x02 +#define SPI_CMD_ERASE_4K 0x20 +#define SPI_CMD_ERASE_32K 0x52 +#define SPI_CMD_ERASE_64K 0xD8 +#define SPI_CMD_READ 0x03 +#define SPI_CMD_READSR 0x05 +#define SPI_CMD_READID 0x9f + +/** 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..9e8b954 --- /dev/null +++ b/module_flash_small/src/spi.xc @@ -0,0 +1,82 @@ +#include +#include +#include "lld.h" +#include +#include + +#define FLASH_25MHz +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 defined(FLASH_50MHz) +#define eightPulses(clk) { clk <: 0xAAAA;} +#elif defined(FLASH_25MHz) +#define eightPulses(clk) { clk <: 0xCCCCCCCC;} +#elif defined(FLASH_12_5MHz) +#define eightPulses(clk) { clk <: 0xF0F0F0F0; clk <: 0xF0F0F0F0;} +#elif defined(FLASH_1_6MHz) +#define eightPulses(clk) { for(int i = 0; i < 8; i++) { clk <: 0; clk <: ~0;}} +#elif defined(FLASH_0_8MHz) +#define eightPulses(clk) { for(int i = 0; i < 8; i++) { clk <: 0; clk <: 0; clk <: ~0; clk <: ~0;}} +#else +#error "Undefined FLASH speed - must be one of 50, 25, or 12_5" +#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; +} From 3f3cc604d5bd615637914da2ccc668519f6e73dc Mon Sep 17 00:00:00 2001 From: Henk Muller Date: Mon, 9 Jul 2012 17:15:55 +0100 Subject: [PATCH 10/13] Extended documentation, clearer examples --- app_example_flash_small/src/main.xc | 18 ++++--- doc/api-small.rst | 71 ++++++++++++++++++++++---- module_flash_small/src/flash.h | 8 +-- module_flash_small/src/flash.xc | 15 +++--- module_flash_small/src/flashDefaults.h | 6 +-- module_flash_small/src/spi.h | 17 ++++-- module_flash_small/src/spi.xc | 2 +- 7 files changed, 98 insertions(+), 39 deletions(-) diff --git a/app_example_flash_small/src/main.xc b/app_example_flash_small/src/main.xc index 5ecaf82..a1d479f 100644 --- a/app_example_flash_small/src/main.xc +++ b/app_example_flash_small/src/main.xc @@ -1,4 +1,3 @@ -#include #include #include @@ -11,6 +10,8 @@ useSPI() { } //:: +#define spiInit() ; + //::flash program #include @@ -24,7 +25,7 @@ useFlash() { printf(" %02x", data[i]); } printf("\n"); - spiFlashErase4K(addr, 32); + spiFlashErase(addr, 32); spiFlashRead(addr, data, 32); for(int i = 0; i < 32; i++) { printf(" %02x", data[i]); @@ -46,18 +47,21 @@ useFlash() { //::persistence program #include +char state[15]; + usePersistence() { - char data[16]; int valid; spiInit(); - valid = spiFlashPersistentStateRead(data); + valid = spiFlashPersistentStateRead(state); if (!valid) { - // fill data with factory default + // fill state[] with factory default } - // update state - spiFlashPersistentStateWrite(data); + //... + // update state + spiFlashPersistentStateWrite(state); + //... } //:: diff --git a/doc/api-small.rst b/doc/api-small.rst index 2b887cc..6f52692 100644 --- a/doc/api-small.rst +++ b/doc/api-small.rst @@ -2,27 +2,76 @@ module_flash_small '''''''''''''''''' This module implements a simple flash library, sufficient for DFU and -similar applications. Two files are to be included ``lld.h`` and -``flash.h``. - +similar applications. There are two include files: ``spi.h`` which is to be +used if ony low level SPI functions are used, or ``flash.h`` which includes +higher level functions to interact with SPI flash. 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 See the section on interoperability for an +explanation. + +**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. + + +Functions +--------- + .. doxygenfunction:: spiInit .. doxygenfunction:: spiCommandStatus .. doxygenfunction:: spiCommandAddressStatus -.. doxygendefine:: SPI_CMD_WRITE_ENABLE -.. doxygendefine:: SPI_CMD_WRITE_DISABLE - 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 +--------- + .. doxygenfunction:: spiFlashRead .. doxygenfunction:: spiFlashWriteSmall .. doxygenfunction:: spiFlashWrite -.. doxygenfunction:: spiFlashErase4K +.. doxygenfunction:: spiFlashErase .. doxygenfunction:: spiFlashPersistentStateRead .. doxygenfunction:: spiFlashPersistentStateWrite @@ -91,16 +140,16 @@ employ and verify that all of those are compatible. Example ======= -An example that reads, erases and writes data: +An example that uses the SPI library to read the ID: .. literalinclude:: app_example_flash_small/src/main.xc - :start-after: //::flash program + :start-after: //::spi program :end-before: //:: -An example that uses the SPI library to read the ID: +An example that reads, erases and writes data: .. literalinclude:: app_example_flash_small/src/main.xc - :start-after: //::spi program + :start-after: //::flash program :end-before: //:: An example that uses the flash library to store some persistent data (such diff --git a/module_flash_small/src/flash.h b/module_flash_small/src/flash.h index f25a665..399ada4 100644 --- a/module_flash_small/src/flash.h +++ b/module_flash_small/src/flash.h @@ -1,7 +1,7 @@ #ifndef FLASH_H #define FLASH_H -#include "spih" +#include "spi.h" /** This function reads a block of data from the flash at the given * address. @@ -57,8 +57,8 @@ 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 4K boundary, and the length should be a whole number of 4K - * blocks. + * 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. @@ -66,7 +66,7 @@ void spiFlashWrite(int address, char data[],int bytes); * \param bytes The number of bytes that are to be erased. * */ -void spiFlashErase4K(int address, int bytes); +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 diff --git a/module_flash_small/src/flash.xc b/module_flash_small/src/flash.xc index eac8574..afb511b 100644 --- a/module_flash_small/src/flash.xc +++ b/module_flash_small/src/flash.xc @@ -1,6 +1,5 @@ #include #include "flash.h" -#include "lld.h" #include "flashDefaults.h" void spiFlashWriteSmall(int address, char data[],int bytes) { @@ -22,11 +21,11 @@ void spiFlashWrite(int address, char data[],int bytes) { } } -void spiFlashErase4K(int address, int bytes) { +void spiFlashErase(int address, int bytes) { char data[1]; spiCommandStatus(SPI_CMD_WRITE_ENABLE, 0); while (bytes > 0) { - spiCommandAddressStatus(SPI_CMD_ERASE_4K, address, data, 0); + spiCommandAddressStatus(SPI_CMD_ERASE, address, data, 0); bytes -= 4096; address += 4096; while(spiCommandStatus(SPI_CMD_READSR, 1) & 1) { @@ -54,12 +53,14 @@ static int flashStartAddress = 0; * 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, data, FLASH_PERSISTENT_SIZE + 1); - if (data[FLASH_PERSISTENT_SIZE] == VALID) { + 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; } @@ -77,7 +78,7 @@ int spiFlashPersistentStateRead(char data[]) { * write has completed before the guard byte write is completed), and * finally the old guard byte is cleared. * - * This process can eb interrupted at the following places without disruption: + * 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) @@ -110,7 +111,7 @@ void spiFlashPersistentStateWrite(char data[]) { } } if ((writeIndex & (FLASH_PERSISTENT_SECTOR_SIZE -1)) == 0) { - spiFlashErase4K(FLASH_PERSISTENT_BASE + writeIndex, FLASH_PERSISTENT_SECTOR_SIZE); + spiFlashErase(FLASH_PERSISTENT_BASE + writeIndex, FLASH_PERSISTENT_SECTOR_SIZE); } spiFlashWriteSmall(FLASH_PERSISTENT_BASE + writeIndex, data, 15); guard[0] = VALID; diff --git a/module_flash_small/src/flashDefaults.h b/module_flash_small/src/flashDefaults.h index 91629b3..252272e 100644 --- a/module_flash_small/src/flashDefaults.h +++ b/module_flash_small/src/flashDefaults.h @@ -7,12 +7,10 @@ #endif #ifndef FLASH_PERSISTENT_SEGMENT_SIZE -#define FLASH_PERSISTENT_SEGMENT_SIZE 8192 +#define FLASH_PERSISTENT_SEGMENT_SIZE (2*SPI_SECTOR_SIZE) #endif -#ifndef FLASH_PERSISTENT_SECTOR_SIZE -#define FLASH_PERSISTENT_SECTOR_SIZE 4096 -#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" diff --git a/module_flash_small/src/spi.h b/module_flash_small/src/spi.h index 21d26ac..c5a49d9 100644 --- a/module_flash_small/src/spi.h +++ b/module_flash_small/src/spi.h @@ -1,18 +1,25 @@ #ifndef SPI_H #define SPI_H -#define SPI_CMD_WRITE_ENABLE 0x06 /**< SPI command to enable write and erase operations */ +#ifdef __spi_conf_h_exists__ +#include "spi_conf.h" +#endif -/** \def SPI command to disable write and erase operations */ +#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_ERASE_4K 0x20 -#define SPI_CMD_ERASE_32K 0x52 -#define SPI_CMD_ERASE_64K 0xD8 #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 + /** This function is to be called prior to any SPI operation. It initialises the ports. */ void spiInit(); diff --git a/module_flash_small/src/spi.xc b/module_flash_small/src/spi.xc index 9e8b954..b84ca5c 100644 --- a/module_flash_small/src/spi.xc +++ b/module_flash_small/src/spi.xc @@ -1,6 +1,6 @@ #include #include -#include "lld.h" +#include "spi.h" #include #include From c4e2623a4494a8b2a1471a5fd89d67e251997c12 Mon Sep 17 00:00:00 2001 From: Henk Muller Date: Mon, 9 Jul 2012 17:17:56 +0100 Subject: [PATCH 11/13] moved files aroudn, added gitignore --- .gitignore | 10 +++++ module_flash_small/src/lld.h | 49 --------------------- module_flash_small/src/lld.xc | 82 ----------------------------------- 3 files changed, 10 insertions(+), 131 deletions(-) create mode 100644 .gitignore delete mode 100644 module_flash_small/src/lld.h delete mode 100644 module_flash_small/src/lld.xc 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/module_flash_small/src/lld.h b/module_flash_small/src/lld.h deleted file mode 100644 index 36688b9..0000000 --- a/module_flash_small/src/lld.h +++ /dev/null @@ -1,49 +0,0 @@ -#define SPI_CMD_WRITE_ENABLE 0x06 -#define SPI_CMD_WRITE_DISABLE 0x04 -#define SPI_CMD_WRITE 0x02 -#define SPI_CMD_ERASE_4K 0x20 -#define SPI_CMD_ERASE_32K 0x52 -#define SPI_CMD_ERASE_64K 0xD8 -#define SPI_CMD_READ 0x03 -#define SPI_CMD_READSR 0x05 -#define SPI_CMD_READID 0x9f - -/** 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); diff --git a/module_flash_small/src/lld.xc b/module_flash_small/src/lld.xc deleted file mode 100644 index 9e8b954..0000000 --- a/module_flash_small/src/lld.xc +++ /dev/null @@ -1,82 +0,0 @@ -#include -#include -#include "lld.h" -#include -#include - -#define FLASH_25MHz -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 defined(FLASH_50MHz) -#define eightPulses(clk) { clk <: 0xAAAA;} -#elif defined(FLASH_25MHz) -#define eightPulses(clk) { clk <: 0xCCCCCCCC;} -#elif defined(FLASH_12_5MHz) -#define eightPulses(clk) { clk <: 0xF0F0F0F0; clk <: 0xF0F0F0F0;} -#elif defined(FLASH_1_6MHz) -#define eightPulses(clk) { for(int i = 0; i < 8; i++) { clk <: 0; clk <: ~0;}} -#elif defined(FLASH_0_8MHz) -#define eightPulses(clk) { for(int i = 0; i < 8; i++) { clk <: 0; clk <: 0; clk <: ~0; clk <: ~0;}} -#else -#error "Undefined FLASH speed - must be one of 50, 25, or 12_5" -#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; -} From 6b0736857fc16d099816979451c88547e89744e9 Mon Sep 17 00:00:00 2001 From: Henk Muller Date: Tue, 10 Jul 2012 10:07:40 +0100 Subject: [PATCH 12/13] New app for testing, repaired lack of spi_conf.h inclusion in build system --- app_example_flash_small/src/main.xc | 41 ++++++++++- app_test_flash_small/Makefile | 9 +++ app_test_flash_small/src/main.xc | 43 +++++++++++ app_test_flash_small/src/spi_conf.h | 2 + doc/api-small.rst | 105 +++++++++++++++------------ module_flash_small/module_build_info | 4 +- module_flash_small/src/flash.xc | 6 +- 7 files changed, 158 insertions(+), 52 deletions(-) create mode 100644 app_test_flash_small/Makefile create mode 100644 app_test_flash_small/src/main.xc create mode 100644 app_test_flash_small/src/spi_conf.h diff --git a/app_example_flash_small/src/main.xc b/app_example_flash_small/src/main.xc index a1d479f..c316c34 100644 --- a/app_example_flash_small/src/main.xc +++ b/app_example_flash_small/src/main.xc @@ -1,6 +1,44 @@ #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 @@ -10,8 +48,6 @@ useSPI() { } //:: -#define spiInit() ; - //::flash program #include @@ -66,6 +102,7 @@ usePersistence() { //:: 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..34879b5 --- /dev/null +++ b/app_test_flash_small/src/spi_conf.h @@ -0,0 +1,2 @@ +#define SPI_CMD_ERASE 0xD8 +#define SPI_SECTOR_SIZE 32768 diff --git a/doc/api-small.rst b/doc/api-small.rst index 6f52692..859bc8c 100644 --- a/doc/api-small.rst +++ b/doc/api-small.rst @@ -3,9 +3,12 @@ module_flash_small This module implements a simple flash library, sufficient for DFU and similar applications. There are two include files: ``spi.h`` which is to be -used if ony low level SPI functions are used, or ``flash.h`` which includes +used if only low level SPI functions are used, or ``flash.h`` which includes higher level functions to interact with SPI flash. +In order to set one or more of the ``#define`` below, create a source file +``spi_conf.h`` containing all the defines. + SPI API ======= @@ -91,51 +94,61 @@ 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. -+-------------------------+------+------+------+------+ -| Erase code | 0x20 | 0x52 | 0xD8 | 0xD8 | -+-------------------------+------+------+------+------+ -| Erase sector size | 4K | 32K | 64K | 32K | -+----------+--------------+------+------+------+------+ -| ALTERA | EPCS1 | | | | X | -| +--------------+------+------+------+------+ -| | EPCS4/16/64 | | | X | | -+----------+--------------+------+------+------+------+ -| AMIC | A25L016 | X | | | | -| +--------------+------+------+------+------+ -| | A25L40 | | | X | | -| +--------------+------+------+------+------+ -| | A25L80 | | | X | | -+----------+--------------+------+------+------+------+ -| ATMEL | AT25DF021 | X | | X | | -| +--------------+------+------+------+------+ -| | AT25DF041A | X | | X | | -| +--------------+------+------+------+------+ -| | AT25F512/1024| | X | | | -| +--------------+------+------+------+------+ -| | AT25FS010 | X | | | X | -+----------+--------------+------+------+------+------+ -| ESMT | F25L004A | X | | X | | -+----------+--------------+------+------+------+------+ -| MACRONIX | MX25L1005C | X | | X | | -+----------+--------------+------+------+------+------+ -| NUMONYX | M25P10 | | | | X | -| +--------------+------+------+------+------+ -| | M25P16 | | | X | | -| +--------------+------+------+------+------+ -| | M25PE10 | X | | X | | -| +--------------+------+------+------+------+ -| | M45PE10 | | | X | | -+----------+--------------+------+------+------+------+ -| SST | SST25VF010 | X | X | | X | -| +--------------+------+------+------+------+ -| | SST25VF016 | X | X | X | | -| +--------------+------+------+------+------+ -| | SST25VF040 | X | X | X | | -+----------+--------------+------+------+------+------+ -| ST | M25PE10/20 | | | X | | -+----------+--------------+------+------+------+------+ -| WINBOND | W25X10/20/40 | X | X | X | | -+----------+--------------+------+------+------+------+ +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 ======= diff --git a/module_flash_small/module_build_info b/module_flash_small/module_build_info index 50b4537..e46dbf0 100644 --- a/module_flash_small/module_build_info +++ b/module_flash_small/module_build_info @@ -1 +1,3 @@ -MODULE_XCC_FLAGS = -fllvm -O2 -g +OPTIONAL_HEADERS += spi_conf.h + +MODULE_XCC_FLAGS =-O2 -g diff --git a/module_flash_small/src/flash.xc b/module_flash_small/src/flash.xc index afb511b..4d33896 100644 --- a/module_flash_small/src/flash.xc +++ b/module_flash_small/src/flash.xc @@ -23,11 +23,11 @@ void spiFlashWrite(int address, char data[],int bytes) { void spiFlashErase(int address, int bytes) { char data[1]; - spiCommandStatus(SPI_CMD_WRITE_ENABLE, 0); while (bytes > 0) { + spiCommandStatus(SPI_CMD_WRITE_ENABLE, 0); spiCommandAddressStatus(SPI_CMD_ERASE, address, data, 0); - bytes -= 4096; - address += 4096; + bytes -= SPI_SECTOR_SIZE; + address += SPI_SECTOR_SIZE; while(spiCommandStatus(SPI_CMD_READSR, 1) & 1) { ; } From abff0c6ec3d16613329977733b183a030ee1b09f Mon Sep 17 00:00:00 2001 From: Henk Muller Date: Tue, 10 Jul 2012 12:26:11 +0100 Subject: [PATCH 13/13] More documentation, added speed to configuration --- app_test_flash_small/src/spi_conf.h | 3 +- doc/api-small.rst | 50 +++++++++++++++++++++++++---- module_flash_small/src/spi.h | 4 +++ module_flash_small/src/spi.xc | 13 ++------ 4 files changed, 53 insertions(+), 17 deletions(-) diff --git a/app_test_flash_small/src/spi_conf.h b/app_test_flash_small/src/spi_conf.h index 34879b5..1d8f943 100644 --- a/app_test_flash_small/src/spi_conf.h +++ b/app_test_flash_small/src/spi_conf.h @@ -1,2 +1,3 @@ -#define SPI_CMD_ERASE 0xD8 +#define SPI_CLK_MHZ 25 +#define SPI_CMD_ERASE 0xD8 #define SPI_SECTOR_SIZE 32768 diff --git a/doc/api-small.rst b/doc/api-small.rst index 859bc8c..46634aa 100644 --- a/doc/api-small.rst +++ b/doc/api-small.rst @@ -2,12 +2,22 @@ module_flash_small '''''''''''''''''' This module implements a simple flash library, sufficient for DFU and -similar applications. There are two include files: ``spi.h`` which is to be -used if only low level SPI functions are used, or ``flash.h`` which includes -higher level functions to interact with SPI flash. +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. -In order to set one or more of the ``#define`` below, create a source file -``spi_conf.h`` containing all the defines. SPI API ======= @@ -18,9 +28,14 @@ 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 See the section on interoperability for an +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, @@ -31,10 +46,31 @@ explanation. 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 @@ -71,6 +107,8 @@ spiFlashPersistentStateWrite() are used. Functions --------- +These functions are defined in ``flash.h``. + .. doxygenfunction:: spiFlashRead .. doxygenfunction:: spiFlashWriteSmall .. doxygenfunction:: spiFlashWrite diff --git a/module_flash_small/src/spi.h b/module_flash_small/src/spi.h index c5a49d9..f258518 100644 --- a/module_flash_small/src/spi.h +++ b/module_flash_small/src/spi.h @@ -20,6 +20,10 @@ #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(); diff --git a/module_flash_small/src/spi.xc b/module_flash_small/src/spi.xc index b84ca5c..3a0d2d1 100644 --- a/module_flash_small/src/spi.xc +++ b/module_flash_small/src/spi.xc @@ -4,7 +4,6 @@ #include #include -#define FLASH_25MHz out port spiSS = PORT_SPI_SS; buffered out port:32 spiCLK = PORT_SPI_CLK; buffered in port:8 spiMISO = PORT_SPI_MISO; @@ -23,18 +22,12 @@ void spiInit() { configure_in_port(spiMISO, SPIclock); } -#if defined(FLASH_50MHz) -#define eightPulses(clk) { clk <: 0xAAAA;} -#elif defined(FLASH_25MHz) +#if (SPI_CLK_MHZ == 25) #define eightPulses(clk) { clk <: 0xCCCCCCCC;} -#elif defined(FLASH_12_5MHz) +#elif (SPI_CLK_MHZ == 13) #define eightPulses(clk) { clk <: 0xF0F0F0F0; clk <: 0xF0F0F0F0;} -#elif defined(FLASH_1_6MHz) -#define eightPulses(clk) { for(int i = 0; i < 8; i++) { clk <: 0; clk <: ~0;}} -#elif defined(FLASH_0_8MHz) -#define eightPulses(clk) { for(int i = 0; i < 8; i++) { clk <: 0; clk <: 0; clk <: ~0; clk <: ~0;}} #else -#error "Undefined FLASH speed - must be one of 50, 25, or 12_5" +#error "Undefined SPI_CLK_MHZ speed - must be one of 25 or 13" #endif static void spiCmd(int cmd) {