diff --git a/.gitignore b/.gitignore index a2ac9cd..9f8c55b 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ .vscode/ .DS_Store +wip/ diff --git a/TommyPROM/PromDevice.h b/TommyPROM/PromDevice.h index 39e2e2e..ea9f3a6 100644 --- a/TommyPROM/PromDevice.h +++ b/TommyPROM/PromDevice.h @@ -14,6 +14,13 @@ * Block writes are supported on compatible devices by specifying a blockSize * in the constructor. Use zero for devices that only support byte writes. */ + +enum ERET { + RET_OK, + RET_FAIL, + RET_NOT_SUPPORT +}; + class PromDevice { public: @@ -25,9 +32,9 @@ class PromDevice virtual void begin() = 0; virtual const char * getName() = 0; - virtual void disableSoftwareWriteProtect() {} - virtual void enableSoftwareWriteProtect() {} - virtual bool erase(uint32_t start, uint32_t end) { return false; } + virtual ERET disableSoftwareWriteProtect() { return RET_NOT_SUPPORT; } + virtual ERET enableSoftwareWriteProtect() { return RET_NOT_SUPPORT; } + virtual ERET erase(uint32_t start, uint32_t end) { return RET_NOT_SUPPORT; } uint32_t debugBlockWrites; // Number of block write operations uint32_t debugLastAddress; // Last address with an issue diff --git a/TommyPROM/PromDevice27.cpp b/TommyPROM/PromDevice27.cpp index 1cf7e2b..0960e67 100644 --- a/TommyPROM/PromDevice27.cpp +++ b/TommyPROM/PromDevice27.cpp @@ -21,9 +21,10 @@ static void enableWrite() { digitalWrite(WE, LOW); } static void disableWrite() { digitalWrite(WE, HIGH);} -PromDevice27::PromDevice27(uint32_t size, unsigned long pulseWidthUsec, +PromDevice27::PromDevice27(uint32_t size, E27C_PGM pgmType, unsigned long pulseWidthUsec, unsigned writeAttempts, unsigned overwriteMultiplier) : PromDevice(size, 0, 0, false), + mPgmType(pgmType), mPulseWidthUsec(pulseWidthUsec), mWriteAttempts(writeAttempts), mOverwriteMultiplier(overwriteMultiplier) @@ -81,6 +82,19 @@ byte PromDevice27::readByte(uint32_t address) // Burn a byte to the chip and verify that it was written. bool PromDevice27::burnByte(byte value, uint32_t address) +{ + switch(mPgmType) { + case E27C_PGM_WE: return burnByteWE(value, address); break; + case E27C_PGM_CE: return burnByteCE(value, address); break; + default: return false; // PGM_D13 not implemented yet + } +} + + +// Burn a byte to the chip and verify that it was written. +// This uses a dedicated WE or PGM chip that operates on TTL levels and is active LOW. +// Overwrite burning is supported. +bool PromDevice27::burnByteWE(byte value, uint32_t address) { bool status = false; unsigned writeCount = 0; @@ -123,6 +137,91 @@ bool PromDevice27::burnByte(byte value, uint32_t address) return status; } + +// Burn a byte to the chip and verify that it was written. +// This uses an active LOW program pulse on the CE line and a verify operation with CE +// HIGH. Overwrite is not supported, but could be added is a chip is found that needs it. +// Chips that use this mode require a programming voltage on the PGM pin and possibly on +// other pins as well. +// +// VCC may also have a non-standard voltage in program mode. Be sure to separate the +// PROM's VCC line from system VCC if a non-standard voltage is used. +bool PromDevice27::burnByteCE(byte value, uint32_t address) +{ + bool status = false; + unsigned writeCount = 0; + + byte data = 0; + disableOutput(); + disableWrite(); + disableChip(); + setAddress(address); + + while (!status && (writeCount < mWriteAttempts)) + { + setDataBusMode(OUTPUT); + writeDataBus(value); + delayMicroseconds(2); + enableChip(); + myDelay(mPulseWidthUsec); + disableChip(); + delayMicroseconds(2); + ++writeCount; + + setDataBusMode(INPUT); + enableOutput(); + data = readDataBus(); + disableOutput(); + status = (readDataBus() == value); + } + + return status; +} + + +ERET PromDevice27::erase(uint32_t start, uint32_t end) +{ + if (mPgmType != E27C_PGM_CE) return RET_NOT_SUPPORT; + + // Erase code for the 27E257 and 27C257. The Vpp and A9 pins are held at 14V for the + // erase and verify cycle. This erases the entire chip, so the start and end address + // parameters are ignored. + disableChip(); + disableOutput(); + setAddress(0); + setDataBusMode(OUTPUT); + writeDataBus(0xff); + delayMicroseconds(2); + + unsigned writeCount = 0; + ERET status = RET_FAIL; + while ((status == RET_FAIL) && (writeCount < mWriteAttempts)) { + setAddress(0); + setDataBusMode(OUTPUT); + writeDataBus(0xff); + delayMicroseconds(2); + enableChip(); + delay(100); + disableChip(); + delayMicroseconds(2); + + // Read back the data to verify all cells are erased. Note That this is done + // while CE is HIGH. This is the erase verify mode. + setDataBusMode(INPUT); + for (uint32_t address = 0; (address < mSize); address++) { + setAddress(address); + enableOutput(); + uint8_t b = readDataBus(); + disableOutput(); + if (b != 0xff) break; + } + status = RET_OK; + } + + return status; +} + + void PromDevice27::myDelay(unsigned int us) { if (us > 16000) @@ -140,4 +239,3 @@ void PromDevice27::myDelay(unsigned int us) #endif // #if defined(PROM_IS_27) - diff --git a/TommyPROM/PromDevice27.h b/TommyPROM/PromDevice27.h index c12ea36..cb9a8b1 100644 --- a/TommyPROM/PromDevice27.h +++ b/TommyPROM/PromDevice27.h @@ -25,24 +25,35 @@ * See the constructor definition for an explanation of the parameters that * control programming. */ + +enum E27C_PGM { + E27C_PGM_WE, // Dedicated WE or PGM pin that is active LOW + E27C_PGM_CE, // CE is pulsed low to program, CE HIGH for verify + E27C_PGM_D13 // Program voltage pulse switched using Arduino D13 pin +}; + class PromDevice27 : public PromDevice { public: - PromDevice27(uint32_t size, unsigned long mPulseWidthUsec, unsigned writeAttempts, unsigned overwriteMultiplier); + PromDevice27(uint32_t size, E27C_PGM pgmType, unsigned long pulseWidthUsec, + unsigned writeAttempts, unsigned overwriteMultiplier); void begin(); const char * getName() { return "27 series EPROM"; } + 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 burnByteWE(byte value, uint32_t address); + bool burnByteCE(byte value, uint32_t address); void myDelay(unsigned int us); + E27C_PGM mPgmType; unsigned long mPulseWidthUsec; unsigned mWriteAttempts; unsigned mOverwriteMultiplier; }; #endif // #define INCLUDE_PROM_DEVICE_27_H - diff --git a/TommyPROM/PromDevice28C.cpp b/TommyPROM/PromDevice28C.cpp index 02c2b2e..47938ea 100644 --- a/TommyPROM/PromDevice28C.cpp +++ b/TommyPROM/PromDevice28C.cpp @@ -45,7 +45,7 @@ void PromDevice28C::begin() // Write the special six-byte code to turn off Software Data Protection. -void PromDevice28C::disableSoftwareWriteProtect() +ERET PromDevice28C::disableSoftwareWriteProtect() { disableOutput(); disableWrite(); @@ -61,11 +61,13 @@ void PromDevice28C::disableSoftwareWriteProtect() setDataBusMode(INPUT); disableChip(); + + return RET_OK; } // Write the special three-byte code to turn on Software Data Protection. -void PromDevice28C::enableSoftwareWriteProtect() +ERET PromDevice28C::enableSoftwareWriteProtect() { disableOutput(); disableWrite(); @@ -78,6 +80,8 @@ void PromDevice28C::enableSoftwareWriteProtect() setDataBusMode(INPUT); disableChip(); + + return RET_OK; } diff --git a/TommyPROM/PromDevice28C.h b/TommyPROM/PromDevice28C.h index c6fce5d..769d6d9 100644 --- a/TommyPROM/PromDevice28C.h +++ b/TommyPROM/PromDevice28C.h @@ -21,8 +21,8 @@ class PromDevice28C : public PromDevice PromDevice28C(uint32_t size, word blockSize, unsigned maxWriteTime, bool polling); void begin(); const char * getName() { return "28C series EEPROM"; } - void disableSoftwareWriteProtect(); - void enableSoftwareWriteProtect(); + ERET disableSoftwareWriteProtect(); + ERET enableSoftwareWriteProtect(); protected: void setAddress(uint32_t address); @@ -34,4 +34,3 @@ class PromDevice28C : public PromDevice }; #endif // #define INCLUDE_PROM_DEVICE_28C_H - diff --git a/TommyPROM/PromDeviceSST39SF.cpp b/TommyPROM/PromDeviceSST39SF.cpp index 8af4dad..af530d7 100644 --- a/TommyPROM/PromDeviceSST39SF.cpp +++ b/TommyPROM/PromDeviceSST39SF.cpp @@ -46,7 +46,7 @@ void PromDeviceSST39SF::begin() // Erase all sectors containing the specified address range. -bool PromDeviceSST39SF::erase(uint32_t start, uint32_t end) +ERET PromDeviceSST39SF::erase(uint32_t start, uint32_t end) { start >>= 12; end >>= 12; @@ -55,7 +55,7 @@ bool PromDeviceSST39SF::erase(uint32_t start, uint32_t end) eraseSector(sector << 12); } - return true; + return RET_OK; } diff --git a/TommyPROM/PromDeviceSST39SF.h b/TommyPROM/PromDeviceSST39SF.h index c77cd2a..c5beb9c 100644 --- a/TommyPROM/PromDeviceSST39SF.h +++ b/TommyPROM/PromDeviceSST39SF.h @@ -21,7 +21,7 @@ class PromDeviceSST39SF : public PromDevice PromDeviceSST39SF(uint32_t size, word unsigned maxWriteTime, bool polling); void begin(); const char * getName() { return "SST39SF series NOR Flash"; } - bool erase(uint32_t start, uint32_t end); + ERET erase(uint32_t start, uint32_t end); protected: diff --git a/TommyPROM/TommyPROM.ino b/TommyPROM/TommyPROM.ino index 92fac22..2f4866d 100644 --- a/TommyPROM/TommyPROM.ino +++ b/TommyPROM/TommyPROM.ino @@ -19,7 +19,7 @@ #include "XModem.h" -static const char * MY_VERSION = "3.0"; +static const char * MY_VERSION = "3.1"; // Global status @@ -35,17 +35,20 @@ CmdStatus cmdStatus; // 10ms max write time // Data polling supported PromDevice28C prom(32 * 1024L, 64, 10, true); +//PromDevice28C prom(8 * 1024L, 0, 10, true); // 28C64 with no page writes #elif defined(PROM_IS_27) // Define a device for a 2764 EPROM with the following parameters: // 8K byte device capacity +// PGM pin pulses active LOW // 1000us (1ms) write pulse // 15 write attempts // 4x overwrite pulse -PromDevice27 prom(8 * 1024L, 1000L, 15, 4); // 2764 with SEEQ intelligent programming -//PromDevice27 prom(32 * 1024L, 1000L, 25, 3); // 27C256 with SEEQ intelligent programming -//PromDevice27 prom(2 * 1024L, 50000L, 1, 0); // 2716 with single 50ms write -//PromDevice27 prom(64 * 1024L, 100L, 11, 0); // 27C040 with Atmel rapid programming +//PromDevice27 prom(8 * 1024L, E27C_PGM_WE, 1000L, 15, 4); // 2764 with SEEQ intelligent programming +//PromDevice27 prom(32 * 1024L, E27C_PGM_WE, 1000L, 25, 3); // 27C256 with SEEQ intelligent programming +//PromDevice27 prom(2 * 1024L, E27C_PGM_WE, 50000L, 1, 0); // 2716 with single 50ms write +//PromDevice27 prom(64 * 1024L, E27C_PGM_WE, 100L, 11, 0); // 27C040 with Atmel rapid programming +PromDevice27 prom(32 * 1024L, E27C_PGM_CE, 100L, 25, 0); // 27C257/27E257 with 100uS program pulse on CE #elif defined(PROM_IS_SST39SF) // Define a device for anSST39SF Flash with the following parameters: @@ -493,7 +496,13 @@ void pokeBytes(char * pCursor) cmdStatus.info("Poke successful"); } - +void printRetStatus(ERET status) +{ + switch (status) { + case RET_FAIL: Serial.println(F("FAILED")); break; + case RET_NOT_SUPPORT: Serial.println(F("NOT SUPPORTED")); break; + } +} #ifdef ENABLE_DEBUG_COMMANDS /** @@ -686,7 +695,7 @@ void loop() break; case CMD_ERASE: - prom.erase(start, end); + printRetStatus(prom.erase(start, end)); break; case CMD_FILL: @@ -695,8 +704,8 @@ void loop() break; case CMD_LOCK: - Serial.println(F("Writing the lock code to enable Software Write Protect mode.")); - prom.enableSoftwareWriteProtect(); + Serial.print(F("Writing the lock code to enable Software Write Protect mode: ")); + printRetStatus(prom.enableSoftwareWriteProtect()); break; case CMD_POKE: @@ -713,8 +722,8 @@ void loop() break; case CMD_UNLOCK: - Serial.println(F("Writing the unlock code to disable Software Write Protect mode.")); - prom.disableSoftwareWriteProtect(); + Serial.print(F("Writing the unlock code to disable Software Write Protect mode: ")); + printRetStatus(prom.disableSoftwareWriteProtect()); break; case CMD_WRITE: diff --git a/docs/prom-families.md b/docs/prom-families.md index 3d23f98..95de1fc 100644 --- a/docs/prom-families.md +++ b/docs/prom-families.md @@ -21,11 +21,12 @@ support for programming. ## EEPROM - Electrically Erasable Programmable Read-only Memory -EEPROMs are the easiest PROMs to use. They usually can be erased and reprogrammed +EEPROMs are the easiest PROMs to use. Modern EEPROMs usually can be erased and reprogrammed electrically at the individual byte level. This makes them appear similar to a slower static RAM. All of the interactive features of TommyPROM work well with EEPROMs. Due to their complexity, EEPROMs typically come in smaller sizes than other technologies. -The largest EEPROM in the 28C family is 32K bytes. +The largest EEPROM in the 28C family is 32K bytes. Some older EEPROMs cannot be reprogrammed at the individual byte level and are instead bulk erased before a new write +operation. Programming and erasing for these chips usually requires voltages higher than 5V. ## Flash ROM @@ -144,3 +145,13 @@ address lines. The Arduino has enough pins to drive all of these directly, with need for shift registers to create address lines. The 8755 build of TommyPROM also has a circuit to control the 25V programming pulses. + +# Verified Chips + +|Model |Manufacturer |Type |Module |Notes| +|:--- |:--- |:--- |:--- |:--- | +|28C256 |Atmel, others|EEPROM |28C |Fully supported| +|SST39SF040|Microchip |Flash |SST39SF|All SST39SF0x0 supported| +|27E257 | |EEPROM |27 |Continual 12V or 14V for program/erase| +|29C010 | |Flash |28C |Only with 128 byte or less sector size| +|8755A |Intel |EPROM |8755A |Requires 25V pulses to program|