Add support for SST28SF SuperFlash

This commit is contained in:
Tom Nisbet 2022-12-13 17:15:20 -05:00
parent 947dc24adf
commit 035b3412af
7 changed files with 334 additions and 16 deletions

View File

@ -9,6 +9,7 @@
#define PROM_IS_28C #define PROM_IS_28C
//#define PROM_IS_27 //#define PROM_IS_27
//#define PROM_IS_SST39SF //#define PROM_IS_SST39SF
//#define PROM_IS_SST28SF
//#define PROM_IS_8755A //#define PROM_IS_8755A
// Don't change anything below this comment unless you are adding support for a new device type. // Don't change anything below this comment unless you are adding support for a new device type.
@ -18,6 +19,8 @@
#include "PromDevice27.h" #include "PromDevice27.h"
#elif defined(PROM_IS_SST39SF) #elif defined(PROM_IS_SST39SF)
#include "PromDeviceSST39SF.h" #include "PromDeviceSST39SF.h"
#elif defined(PROM_IS_SST28SF)
#include "PromDeviceSST28SF.h"
#elif defined(PROM_IS_8755A) #elif defined(PROM_IS_8755A)
#include "PromDevice8755A.h" #include "PromDevice8755A.h"
// Additional device support goes here... // Additional device support goes here...

View File

@ -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)

View File

@ -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

View File

@ -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) ERET PromDeviceSST39SF::erase(uint32_t start, uint32_t end)
{ {
start >>= 12; start >>= 12;
end >>= 12; end >>= 12;
for (uint32_t sector = start; (start <= end); start++) for (uint32_t sector = start; (sector <= end); sector++)
{ {
eraseSector(sector << 12); eraseSector(sector << 12);
} }
@ -95,7 +95,7 @@ bool PromDeviceSST39SF::burnByte(byte value, uint32_t address)
bool status = false; bool status = false;
// Erase a sector before writing any new data to it. Note that multiple // 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 // If multiple burn calls will be needed for the same address, it is up to the
// caller to erase the sector before writing. // caller to erase the sector before writing.
if ((address & 0xfffff000) != currentSector) 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 // Set an address and data value and toggle the write control. This is used to write
// to write control sequences, like the software write protect. This is not a // control sequences, like the sector erase. This is not a complete byte write function
// complete byte write function because it does not set the chip enable or the // because it does not set the chip enable or the mode of the data bus.
// mode of the data bus.
void PromDeviceSST39SF::setByte(byte value, uint32_t address) void PromDeviceSST39SF::setByte(byte value, uint32_t address)
{ {
setAddress(address); setAddress(address);

View File

@ -11,9 +11,6 @@
* *
* Provides the device-specific interface to read and write data from an * Provides the device-specific interface to read and write data from an
* SST39SF series parallel NOR Flash using the Arduino. * 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 class PromDeviceSST39SF : public PromDevice
{ {

View File

@ -19,7 +19,7 @@
#include "XModem.h" #include "XModem.h"
static const char * MY_VERSION = "3.1"; static const char * MY_VERSION = "3.2";
// Global status // Global status
@ -53,10 +53,17 @@ PromDevice27 prom(32 * 1024L, E27C_PGM_CE, 100L, 25, 0); // 27C257/27E257 with
#elif defined(PROM_IS_SST39SF) #elif defined(PROM_IS_SST39SF)
// Define a device for anSST39SF Flash with the following parameters: // Define a device for anSST39SF Flash with the following parameters:
// 512K byte device capacity // 512K byte device capacity
// 10ms max write time // 10us max write time
// Data polling supported // Data polling supported
PromDeviceSST39SF prom(512 * 1024L, 10, true); 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) #elif defined(PROM_IS_8755A)
// Define a device for an Intel 8755A with a fixed size of 2K and no other parameters. // Define a device for an Intel 8755A with a fixed size of 2K and no other parameters.
PromDevice8755A prom(2 * 1024L); PromDevice8755A prom(2 * 1024L);
@ -499,6 +506,7 @@ void pokeBytes(char * pCursor)
void printRetStatus(ERET status) void printRetStatus(ERET status)
{ {
switch (status) { switch (status) {
case RET_OK: Serial.println(F("OK")); break;
case RET_FAIL: Serial.println(F("FAILED")); break; case RET_FAIL: Serial.println(F("FAILED")); break;
case RET_NOT_SUPPORT: Serial.println(F("NOT SUPPORTED")); break; case RET_NOT_SUPPORT: Serial.println(F("NOT SUPPORTED")); break;
} }

View File

@ -71,12 +71,13 @@ a version of the programer that supports these chips.
## Atmel SST39FS Flash ## 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 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 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 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 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. 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 This is only needed if data will be rewritten to the same location after a previous write
to that sector. 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 ## Misc Flash
#### 29C Series #### 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| |AT28C256 |Atmel, others|EEPROM |28C |Fully supported|
|SST39SF040|Microchip |Flash |SST39SF|All SST39SF0x0 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| |WE27C257 |Winbond |EEPROM |27 |Continual 12V or 14V for program/erase|
|AT29C010 |Atmel |Flash |28C |Only with 128 byte or less sector size| |AT29C010 |Atmel |Flash |28C |Only with 128 byte or less sector size|
|8755A |Intel |EPROM |8755A |Requires 25V pulses to program| |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 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. 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 #### 27C257
The Winbond WE27C257 and WE27E257 appear to be identical 32Kx8 EEPROMs. The 27C version 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| |M27C4001 |ST Micro |EEPROM | |VCC=6.5V, VPP=12.75V to pgm|
|SST27SF020|SST |Flash | |12V continuous for pgm/erase| |SST27SF020|SST |Flash | |12V continuous for pgm/erase|
|SST28SF040|SST |Flash | |5V with cmds|