diff --git a/TommyPROM/Configure.h b/TommyPROM/Configure.h index bc04ef4..f5f15cd 100644 --- a/TommyPROM/Configure.h +++ b/TommyPROM/Configure.h @@ -9,6 +9,7 @@ #define PROM_IS_28C //#define PROM_IS_27 //#define PROM_IS_SST39SF +//#define PROM_IS_SST28SF //#define PROM_IS_8755A // Don't change anything below this comment unless you are adding support for a new device type. @@ -18,6 +19,8 @@ #include "PromDevice27.h" #elif defined(PROM_IS_SST39SF) #include "PromDeviceSST39SF.h" +#elif defined(PROM_IS_SST28SF) +#include "PromDeviceSST28SF.h" #elif defined(PROM_IS_8755A) #include "PromDevice8755A.h" // Additional device support goes here... diff --git a/TommyPROM/PromDeviceSST28SF.cpp b/TommyPROM/PromDeviceSST28SF.cpp new file mode 100644 index 0000000..bf725ea --- /dev/null +++ b/TommyPROM/PromDeviceSST28SF.cpp @@ -0,0 +1,254 @@ +#include "Configure.h" +#if defined(PROM_IS_SST28SF) + +#include "PromAddressDriver.h" + +// IO lines for the EEPROM device control +// Pins D2..D9 are used for the data bus. +#define WE A0 +#define CE A1 +#define OE A2 + +// Set the status of the device control pins +static void enableChip() { digitalWrite(CE, LOW); } +static void disableChip() { digitalWrite(CE, HIGH);} +static void enableOutput() { digitalWrite(OE, LOW); } +static void disableOutput() { digitalWrite(OE, HIGH);} +static void enableWrite() { digitalWrite(WE, LOW); } +static void disableWrite() { digitalWrite(WE, HIGH);} + + +PromDeviceSST28SF::PromDeviceSST28SF(uint32_t size, unsigned maxWriteTime, bool polling) + : PromDevice(size, 0, maxWriteTime, polling), + currentSector(0xffffffff) +{ +} + + +void PromDeviceSST28SF::begin() +{ + // Define the data bus as input initially so that it does not put out a + // signal that could collide with output on the data pins of the EEPROM. + setDataBusMode(INPUT); + + // Define the EEPROM control pins as output, making sure they are all + // in the disabled state. + digitalWrite(OE, HIGH); + pinMode(OE, OUTPUT); + digitalWrite(CE, HIGH); + pinMode(CE, OUTPUT); + digitalWrite(WE, HIGH); + pinMode(WE, OUTPUT); + + // This chip uses the shift register hardware for addresses, so initialize that. + PromAddressDriver::begin(); +} + + +// Turn off Software Data Protection. +ERET PromDeviceSST28SF::disableSoftwareWriteProtect() +{ + return sendSdpSequence(0x041a); +} + +// Turn on Software Data Protection. +ERET PromDeviceSST28SF::enableSoftwareWriteProtect() +{ + return sendSdpSequence(0x040a); +} + + +// Erase all 256 byte sectors containing the specified address range. +ERET PromDeviceSST28SF::erase(uint32_t start, uint32_t end) +{ + start >>= 8; + end >>= 8; + for (uint32_t sector = start; (sector <= end); sector++) + { + eraseSector(sector << 8); + } + + return RET_OK; +} + + +// BEGIN PRIVATE METHODS +// + +// Use the PromAddressDriver to set an address in the two address shift registers. +void PromDeviceSST28SF::setAddress(uint32_t address) +{ + PromAddressDriver::setAddress(address); +} + + +// Read a byte from a given address +byte PromDeviceSST28SF::readByte(uint32_t address) +{ + byte data = 0; + setAddress(address); + setDataBusMode(INPUT); + disableOutput(); + disableWrite(); + enableChip(); + enableOutput(); + data = readDataBus(); + disableOutput(); + disableChip(); + + return data; +} + + +// Burn a byte to the chip and verify that it was written. +bool PromDeviceSST28SF::burnByte(byte value, uint32_t address) +{ + bool status = false; + + // Erase a sector before writing any new data to it. Note that multiple + // burbByte calls to the same sector will only do an erase on the first call. + // If multiple burn calls will be needed for the same address, it is up to the + // caller to erase the sector before writing. + if ((address & 0xffffff00) != currentSector) + { + eraseSector(address); + currentSector = address & 0xffffff00; + } + disableOutput(); + disableWrite(); + + setDataBusMode(OUTPUT); + enableChip(); + setByte(0x10, 0x0000); + + setAddress(address); + writeDataBus(value); + + delayMicroseconds(1); + enableWrite(); + delayMicroseconds(1); + disableWrite(); + + status = waitForWriteCycleEnd(value); + + disableChip(); + + return status; +} + + +bool PromDeviceSST28SF::waitForWriteCycleEnd(byte lastValue) +{ + if (mSupportsDataPoll) + { + // Verify programming complete by reading the last value back until it matches the + // value written twice in a row. The D7 bit will read the inverse of last written + // data and the D6 bit will toggle on each read while in programming mode. + // + // This loop code takes about 18uSec to execute. The max readcount is set to the + // device's maxReadTime (in uSecs) divided by ten rather than eighteen to ensure + // that it runs at least as long as the chip's timeout value, even if some code + // optimizations are made later. In actual practice, the loop will terminate much + // earlier because it will detect the end of the write well before the max time. + byte b1=0, b2=0; + setDataBusMode(INPUT); + delayMicroseconds(1); + for (unsigned readCount = 1; (readCount < (mMaxWriteTime * 100)); readCount++) + { + enableChip(); + enableOutput(); + delayMicroseconds(1); + b1 = readDataBus(); + disableOutput(); + disableChip(); + enableChip(); + enableOutput(); + delayMicroseconds(1); + b2 = readDataBus(); + disableOutput(); + disableChip(); + if ((b1 == b2) && (b1 == lastValue)) + { + return true; + } + } + + debugLastExpected = lastValue; + debugLastReadback = b2; + return false; + } + else + { + // No way to detect success. Just wait the max write time. + delayMicroseconds(mMaxWriteTime * 1000L); + return true; + } +} + + +// Set an address and data value and toggle the write control. This is used to write +// control sequences, like the sector erase. This is not a complete byte write function +// because it does not set the chip enable or the mode of the data bus. +void PromDeviceSST28SF::setByte(byte value, uint32_t address) +{ + setAddress(address); + writeDataBus(value); + + delayMicroseconds(1); + enableWrite(); + delayMicroseconds(1); + disableWrite(); +} + +// Set an address value and toggle the read control. This is used for control sequences, +// like the software write protect. This is not a complete byte read function because it +// does not set the chip enable or the mode of the data bus. +void PromDeviceSST28SF::getByte(uint32_t address) +{ + setAddress(address); + enableOutput(); + disableOutput(); +} + +void PromDeviceSST28SF::eraseSector(uint32_t addr) +{ + disableOutput(); + disableWrite(); + setDataBusMode(OUTPUT); + enableChip(); + setByte(0x20, 0x0000); + setByte(0xD0, addr & 0xffffff00); + delay(4); + disableChip(); +} + + +// Software Data protection is enabled and disabled by reading a sequence of seven +// addresses. The sequence only differs by the final address, so this common code is used +// for both commands. +// +// The 2001 SST datasheet is a bit unclear on the sequence. The timing diagrams are +// labeled as "Unprotect Disable" and "Protect Disable", which would seem to be protect +// and unprotect, respectively. However, the softare command summary table references the +// "Protect Disable" timing diagram for the Protect command. +ERET PromDeviceSST28SF::sendSdpSequence(uint16_t lastAddress) +{ + disableOutput(); + disableWrite(); + enableChip(); + setDataBusMode(INPUT); + + getByte(0x1823); + getByte(0x1820); + getByte(0x1822); + getByte(0x0418); + getByte(0x041b); + getByte(0x0419); + getByte(lastAddress); + + disableChip(); + + return RET_OK; +} + +#endif // #if defined(PROM_IS_SST28SF) diff --git a/TommyPROM/PromDeviceSST28SF.h b/TommyPROM/PromDeviceSST28SF.h new file mode 100644 index 0000000..7b64d83 --- /dev/null +++ b/TommyPROM/PromDeviceSST28SF.h @@ -0,0 +1,41 @@ +#ifndef INCLUDE_PROM_DEVICE_SST28SF_H +#define INCLUDE_PROM_DEVICE_SST28SF_H + +#include "Arduino.h" +#include "PromDevice.h" + +/*****************************************************************************/ +/*****************************************************************************/ +/** + * PromDeviceSST28SF class + * + * Provides the device-specific interface to read and write data from an + * SST28SF series parallel SuperFlash using the Arduino. + */ +class PromDeviceSST28SF : public PromDevice +{ + public: + PromDeviceSST28SF(uint32_t size, word unsigned maxWriteTime, bool polling); + void begin(); + const char * getName() { return "SST28SF series SuperFlash"; } + ERET disableSoftwareWriteProtect(); + ERET enableSoftwareWriteProtect(); + ERET erase(uint32_t start, uint32_t end); + + + protected: + void setAddress(uint32_t address); + byte readByte(uint32_t address); + bool burnByte(byte value, uint32_t address); + bool waitForWriteCycleEnd(byte lastValue); + void setByte(byte value, uint32_t address); + void getByte(uint32_t address); + void eraseSector(uint32_t addr); + ERET sendSdpSequence(uint16_t lastAddress); + + + uint32_t currentSector; + +}; + +#endif // #define INCLUDE_PROM_DEVICE_SST28SF_H diff --git a/TommyPROM/PromDeviceSST39SF.cpp b/TommyPROM/PromDeviceSST39SF.cpp index af530d7..e882c64 100644 --- a/TommyPROM/PromDeviceSST39SF.cpp +++ b/TommyPROM/PromDeviceSST39SF.cpp @@ -45,12 +45,12 @@ void PromDeviceSST39SF::begin() } -// Erase all sectors containing the specified address range. +// Erase all 4K byte sectors containing the specified address range. ERET PromDeviceSST39SF::erase(uint32_t start, uint32_t end) { start >>= 12; end >>= 12; - for (uint32_t sector = start; (start <= end); start++) + for (uint32_t sector = start; (sector <= end); sector++) { eraseSector(sector << 12); } @@ -95,7 +95,7 @@ bool PromDeviceSST39SF::burnByte(byte value, uint32_t address) bool status = false; // Erase a sector before writing any new data to it. Note that multiple - // burbByte calls to the same sector will only do an erase on the first call. + // burnByte calls to the same sector will only do an erase on the first call. // If multiple burn calls will be needed for the same address, it is up to the // caller to erase the sector before writing. if ((address & 0xfffff000) != currentSector) @@ -178,10 +178,9 @@ bool PromDeviceSST39SF::waitForWriteCycleEnd(byte lastValue) } -// Set an address and data value and toggle the write control. This is used -// to write control sequences, like the software write protect. This is not a -// complete byte write function because it does not set the chip enable or the -// mode of the data bus. +// Set an address and data value and toggle the write control. This is used to write +// control sequences, like the sector erase. This is not a complete byte write function +// because it does not set the chip enable or the mode of the data bus. void PromDeviceSST39SF::setByte(byte value, uint32_t address) { setAddress(address); diff --git a/TommyPROM/PromDeviceSST39SF.h b/TommyPROM/PromDeviceSST39SF.h index c5beb9c..a4fa192 100644 --- a/TommyPROM/PromDeviceSST39SF.h +++ b/TommyPROM/PromDeviceSST39SF.h @@ -11,9 +11,6 @@ * * Provides the device-specific interface to read and write data from an * SST39SF series parallel NOR Flash using the Arduino. - * - * Block writes are supported on compatible devices by specifying a blockSize - * in the constructor. Use zero for byte writes. */ class PromDeviceSST39SF : public PromDevice { diff --git a/TommyPROM/TommyPROM.ino b/TommyPROM/TommyPROM.ino index 2f4866d..95b15f3 100644 --- a/TommyPROM/TommyPROM.ino +++ b/TommyPROM/TommyPROM.ino @@ -19,7 +19,7 @@ #include "XModem.h" -static const char * MY_VERSION = "3.1"; +static const char * MY_VERSION = "3.2"; // Global status @@ -53,10 +53,17 @@ PromDevice27 prom(32 * 1024L, E27C_PGM_CE, 100L, 25, 0); // 27C257/27E257 with #elif defined(PROM_IS_SST39SF) // Define a device for anSST39SF Flash with the following parameters: // 512K byte device capacity -// 10ms max write time +// 10us max write time // Data polling supported PromDeviceSST39SF prom(512 * 1024L, 10, true); +#elif defined(PROM_IS_SST28SF) +// Define a device for anSST28SF Flash with the following parameters: +// 512K byte device capacity +// 40us max write time +// Data polling supported +PromDeviceSST28SF prom(512 * 1024L, 40, true); + #elif defined(PROM_IS_8755A) // Define a device for an Intel 8755A with a fixed size of 2K and no other parameters. PromDevice8755A prom(2 * 1024L); @@ -499,6 +506,7 @@ void pokeBytes(char * pCursor) void printRetStatus(ERET status) { switch (status) { + case RET_OK: Serial.println(F("OK")); break; case RET_FAIL: Serial.println(F("FAILED")); break; case RET_NOT_SUPPORT: Serial.println(F("NOT SUPPORTED")); break; } diff --git a/docs/prom-families.md b/docs/prom-families.md index ba5c924..016a291 100644 --- a/docs/prom-families.md +++ b/docs/prom-families.md @@ -71,12 +71,13 @@ a version of the programer that supports these chips. ## Atmel SST39FS Flash -TommyPROM has a driver for Atmel SST39FS flash chips. This driver replaces the 28C driver at compile time. See configure.h to enable a different driver. +TommyPROM has a driver for Atmel SST39FS NOR flash chips. This driver replaces the 28C +driver at compile time. See configure.h to enable a different driver. -The SST39FS chips use fixed 2KB sectors that must be manually erased before a new program +The SST39FS chips use fixed 4KB sectors that must be manually erased before a new program operation, but the code manages this transparently. Whenever a write is started to a new segment, the driver first initiates an erase of that sector. A second write to the same -sector will not cause an erase, so is is possible to write to a segment multiple times +sector will not cause an erase, so it is possible to write to a segment multiple times with no additional steps as long as the writes are to different parts of the sector. For example, 256 bytes could be written to the start of a sector from one file and then 512 bytes could be written to the end of the sector from another file. @@ -88,6 +89,14 @@ The SST39FS driver supports a manual erase from the command line using the E com This is only needed if data will be rewritten to the same location after a previous write to that sector. +There is also a driver for the SST28SF0x0 SuperFlash chips. These are an earlier version +of the 39SF chips, using 256-byte sectors. The 28SF and 39SF chips are pin compatible, +but use different command sets for programming and erasing. For read-only applications, +they should be identical, although the 28SF are slower. + +All programming and erase operations for both the 39SF and 28SF chips require only a +single 5V power supply. + ## Misc Flash #### 29C Series @@ -152,6 +161,7 @@ The 8755 build of TommyPROM also has a circuit to control the 25V programming pu |:--- |:--- |:--- |:--- |:--- | |AT28C256 |Atmel, others|EEPROM |28C |Fully supported| |SST39SF040|Microchip |Flash |SST39SF|All SST39SF0x0 supported| +|SST28SF040|SST |Flash | |All SST28SF0x0 supported| |WE27C257 |Winbond |EEPROM |27 |Continual 12V or 14V for program/erase| |AT29C010 |Atmel |Flash |28C |Only with 128 byte or less sector size| |8755A |Intel |EPROM |8755A |Requires 25V pulses to program| @@ -178,6 +188,13 @@ writing new data. The code keeps track of the current sector and will automatic an erase operation whenever a write starts to a new sector. The _Erase_ command is supported, but is not needed unless overwriting new data to a single sector. +#### SST28SF040 + +This is an earlier version of the SST39SF series chips. They are pin compatible with the +39SF series, but use a different command set for programming. Unlike the 39SF, these +flash chips support software data protection. The _Lock_ and _Unlock_ commands can be +used to enable and disable SDP from the command line. + #### 27C257 The Winbond WE27C257 and WE27E257 appear to be identical 32Kx8 EEPROMs. The 27C version @@ -212,4 +229,3 @@ for chips with the 256 byte buffer. |:--- |:--- |:--- |:--- |:--- | |M27C4001 |ST Micro |EEPROM | |VCC=6.5V, VPP=12.75V to pgm| |SST27SF020|SST |Flash | |12V continuous for pgm/erase| -|SST28SF040|SST |Flash | |5V with cmds|