From fb877244c1fda7e784a5227cb28321c2e1b9cbaf Mon Sep 17 00:00:00 2001 From: Tom Nisbet Date: Fri, 12 Aug 2022 23:34:46 -0400 Subject: [PATCH] Add preliminary support for SST39SF flash --- TommyPROM/Configure.h | 3 + TommyPROM/PromDevice.h | 1 + TommyPROM/PromDeviceSST39SF.cpp | 252 ++++++++++++++++++++++++++++++++ TommyPROM/PromDeviceSST39SF.h | 41 ++++++ TommyPROM/TommyPROM.ino | 26 +++- 5 files changed, 317 insertions(+), 6 deletions(-) create mode 100644 TommyPROM/PromDeviceSST39SF.cpp create mode 100644 TommyPROM/PromDeviceSST39SF.h diff --git a/TommyPROM/Configure.h b/TommyPROM/Configure.h index 563e41c..bc04ef4 100644 --- a/TommyPROM/Configure.h +++ b/TommyPROM/Configure.h @@ -8,6 +8,7 @@ #define PROM_IS_28C //#define PROM_IS_27 +//#define PROM_IS_SST39SF //#define PROM_IS_8755A // Don't change anything below this comment unless you are adding support for a new device type. @@ -15,6 +16,8 @@ #include "PromDevice28C.h" #elif defined(PROM_IS_27) #include "PromDevice27.h" +#elif defined(PROM_IS_SST39SF) +#include "PromDeviceSST39SF.h" #elif defined(PROM_IS_8755A) #include "PromDevice8755A.h" // Additional device support goes here... diff --git a/TommyPROM/PromDevice.h b/TommyPROM/PromDevice.h index 4e1d581..39e2e2e 100644 --- a/TommyPROM/PromDevice.h +++ b/TommyPROM/PromDevice.h @@ -27,6 +27,7 @@ class PromDevice virtual const char * getName() = 0; virtual void disableSoftwareWriteProtect() {} virtual void enableSoftwareWriteProtect() {} + virtual bool erase(uint32_t start, uint32_t end) { return false; } uint32_t debugBlockWrites; // Number of block write operations uint32_t debugLastAddress; // Last address with an issue diff --git a/TommyPROM/PromDeviceSST39SF.cpp b/TommyPROM/PromDeviceSST39SF.cpp new file mode 100644 index 0000000..52a4c21 --- /dev/null +++ b/TommyPROM/PromDeviceSST39SF.cpp @@ -0,0 +1,252 @@ +#include "Configure.h" +#if defined(PROM_IS_SST39SF) + +#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);} + + +PromDeviceSST39SF::PromDeviceSST39SF(uint32_t size, unsigned maxWriteTime, bool polling) + : PromDevice(size, 0, maxWriteTime, polling), + currentSector(0xffffffff) +{ +} + + +void PromDeviceSST39SF::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(); +} + + +// Write the special six-byte code to turn off Software Data Protection. +void PromDeviceSST39SF::disableSoftwareWriteProtect() +{ + disableOutput(); + disableWrite(); + enableChip(); + setDataBusMode(OUTPUT); + + setByte(0xaa, 0x5555); + setByte(0x55, 0x2aaa); + setByte(0x80, 0x5555); + setByte(0xaa, 0x5555); + setByte(0x55, 0x2aaa); + setByte(0x20, 0x5555); + + setDataBusMode(INPUT); + disableChip(); +} + + +// Write the special three-byte code to turn on Software Data Protection. +void PromDeviceSST39SF::enableSoftwareWriteProtect() +{ + disableOutput(); + disableWrite(); + enableChip(); + setDataBusMode(OUTPUT); + + setByte(0xaa, 0x5555); + setByte(0x55, 0x2aaa); + setByte(0xa0, 0x5555); + + setDataBusMode(INPUT); + disableChip(); +} + + +// Erase all sectors containing the specified address range. +bool PromDeviceSST39SF::erase(uint32_t start, uint32_t end) +{ + start >>= 12; + end >>= 12; + for (uint32_t sector = start; (start <= end); start++) + { + eraseSector(sector << 12); + } + + return true; +} + + + + +// BEGIN PRIVATE METHODS +// + +// Use the PromAddressDriver to set an address in the two address shift registers. +void PromDeviceSST39SF::setAddress(uint32_t address) +{ + PromAddressDriver::setAddress(address); +} + + +// Read a byte from a given address +byte PromDeviceSST39SF::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 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. + // 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) + { + eraseSector(address); + currentSector = address & 0xfffff000; + } + disableOutput(); + disableWrite(); + + setDataBusMode(OUTPUT); + enableChip(); + setByte(0xaa, 0x5555); + setByte(0x55, 0x2aaa); + setByte(0xa0, 0x5555); + + + setAddress(address); + setDataBusMode(OUTPUT); + writeDataBus(value); + + //enableChip(); + delayMicroseconds(1); + enableWrite(); + delayMicroseconds(1); + disableWrite(); + + status = waitForWriteCycleEnd(value); + + disableChip(); + + return status; +} + + +bool PromDeviceSST39SF::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 (int 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 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. +void PromDeviceSST39SF::setByte(byte value, uint32_t address) +{ + setAddress(address); + writeDataBus(value); + + delayMicroseconds(1); + enableWrite(); + delayMicroseconds(1); + disableWrite(); +} + +void PromDeviceSST39SF::eraseSector(uint32_t addr) +{ + disableOutput(); + disableWrite(); + + setDataBusMode(OUTPUT); + enableChip(); + setByte(0xaa, 0x5555); + setByte(0x55, 0x2aaa); + setByte(0x80, 0x5555); + setByte(0xaa, 0x5555); + setByte(0x55, 0x2aaa); + setByte(0x30, addr & 0xfffff000); + delay(25); + disableChip(); +} + +#endif // #if defined(PROM_IS_SST39SF) diff --git a/TommyPROM/PromDeviceSST39SF.h b/TommyPROM/PromDeviceSST39SF.h new file mode 100644 index 0000000..43bc484 --- /dev/null +++ b/TommyPROM/PromDeviceSST39SF.h @@ -0,0 +1,41 @@ +#ifndef INCLUDE_PROM_DEVICE_SST39SF_H +#define INCLUDE_PROM_DEVICE_SST39SF_H + +#include "Arduino.h" +#include "PromDevice.h" + +/*****************************************************************************/ +/*****************************************************************************/ +/** + * PromDeviceSST39SF class + * + * 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 +{ + public: + PromDeviceSST39SF(uint32_t size, word unsigned maxWriteTime, bool polling); + void begin(); + const char * getName() { return "SST39SF series NOR Flash"; } + void disableSoftwareWriteProtect(); + void enableSoftwareWriteProtect(); + bool 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 eraseSector(uint32_t addr); + + uint32_t currentSector; + +}; + +#endif // #define INCLUDE_PROM_DEVICE_SST39SF_H diff --git a/TommyPROM/TommyPROM.ino b/TommyPROM/TommyPROM.ino index 07889cd..350e59b 100644 --- a/TommyPROM/TommyPROM.ino +++ b/TommyPROM/TommyPROM.ino @@ -19,7 +19,7 @@ #include "XModem.h" -static const char * MY_VERSION = "2.7"; +static const char * MY_VERSION = "2.8"; // Global status @@ -47,6 +47,13 @@ PromDevice27 prom(8 * 1024L, 1000L, 15, 4); // 2764 with SEEQ intelligent prog //PromDevice27 prom(2 * 1024L, 50000L, 1, 0); // 2716 with single 50ms write //PromDevice27 prom(64 * 1024L, 100L, 11, 0); // 27C040 with Atmel rapid programming +#elif defined(PROM_IS_SST39SF) +// Define a device for anSST39SF Flash with the following parameters: +// 512K byte device capacity +// 10ms max write time +// Data polling supported +PromDeviceSST39SF prom(512 * 1024L, 10, 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); @@ -74,9 +81,10 @@ const char hex[] = "0123456789abcdef"; enum { // CLI Commands CMD_INVALID, + CMD_BLANK, CMD_CHECKSUM, CMD_DUMP, - CMD_ERASED, + CMD_ERASE, CMD_FILL, CMD_LOCK, CMD_POKE, @@ -133,9 +141,10 @@ byte parseCommand(char c) switch (c) { + case 'b': cmd = CMD_BLANK; break; case 'c': cmd = CMD_CHECKSUM; break; case 'd': cmd = CMD_DUMP; break; - case 'e': cmd = CMD_ERASED; break; + case 'e': cmd = CMD_ERASE; break; case 'f': cmd = CMD_FILL; break; case 'l': cmd = CMD_LOCK; break; case 'p': cmd = CMD_POKE; break; @@ -657,6 +666,10 @@ void loop() switch (cmd) { + case CMD_BLANK: + erasedBlockCheck(start, end); + break; + case CMD_CHECKSUM: w = checksumBlock(start, end); Serial.print(F("Checksum ")); @@ -672,8 +685,8 @@ void loop() dumpBlock(start, end); break; - case CMD_ERASED: - erasedBlockCheck(start, end); + case CMD_ERASE: + prom.erase(start, end); break; case CMD_FILL: @@ -748,9 +761,10 @@ void loop() Serial.println(prom.getName()); Serial.println(); Serial.println(F("Valid commands are:")); + Serial.println(F(" Bsssss eeeee - Check to see if device range is Blank/erased (all FF)")); Serial.println(F(" Csssss eeeee - Compute checksum from device")); Serial.println(F(" Dsssss eeeee - Dump bytes from device to terminal")); - Serial.println(F(" Esssss eeeee - Check to see if device range is Erased (all FF)")); + Serial.println(F(" Esssss eeeee - Erase address range on device (needed for some Flash)")); Serial.println(F(" Fsssss eeeee dd - Fill block on device with fixed value")); Serial.println(F(" L - Lock (enable) device Software Data Protection")); Serial.println(F(" Psssss dd dd... - Poke (write) values to device (up to 32 values)"));