I've finally completed the code for programming my flash ROMs, and successfully tested all functions for the first time. The code is an absolute mess, but I'm posting it here in case anyone else might find it useful. I've broken out the functions specific to working with the flash into a library. This is my first experiment with classes in C++, so forgive me if its implementation is non-standard.
The Arduino sketch posted below will present a prompt and menu for working with the chip. The Read function will read the specified address range and output Intel Hex format. The Write function parses Hex format and programs the chip with it. Extended Linear Address entries are used to select the sectors on the chip (0-7, for 8 64k sectors). Verify checks the lock bits for the specified sector, and scans through the sector looking for any bytes that are programmed (not reading $FF).
I did run into a few problems while testing the programmer. The first was I noticed that between reads of the same area on the flash chip I was testing with, the value returned was inconsistent. Sometimes it would be just a single bit different on one read out of three ($FF $7F $FF), but sometimes it would be three very different reads ($7F $1F $60). My first thought was perhaps I needed decoupling capacitors, so I broke out the soldering iron. No change. Maybe I'm not accounting for setup time, so I added a delay, and read the byte three times in a row before bringing OE back up. No change. Finally, I threw together a quick sketch that would scan through the entire address range of the ROM and read each byte sixteen times, and report any that were inconsistent.
Clearly, this chip has a bad sector. I pulled these chips out of a wall-mount touch panel that is at least 15 years old, so a bad sector is not surprising. Of the four, two have bad sectors.
The other big problem I ran into while testing was none of the flash chips wanted to respond to any of the write commands. I pored over the datasheet for hours, checking, rechecking the commands, the timing diagrams, everything I could think of. Nothing seemed out of place, and I know I had it working before I moved from the breadboard. ... Which of course means that must be where it went bad. Sure enough, another quick sketch to step through the address pins one-by-one, and I had swapped A14 and A15 when building the board. Trying to desolder from the cheap import board resulted in completely removing the copper pad as well, but I got them connected the right way in the end. It was pretty exciting to see the byte write command complete successfully, and be able to read back in the data I had written.
Programmer library and Arduino sketch below:
AM29_Flash.h
/* AM29F040 Flash memory library
* This library was designed for the 512KB AMD AM29F040 chip
* to be programmed with an Arduino Mega.
*
* Modification will be required to work with anything else
* I make no guarantees this code will work for you, and provide
* it here as an example of what worked for me.
*
* To use, first edit this header to fill in the proper ports
* for the high and low address bytes, and the DQ byte.
* I used Port A for address low, Port C for address high,
* and Port K for DQ. The constructor for AM29 will set which
* Arduino pins are used for A16-A18, and for the control signals.
* Calling the setup() method will set all pins as input/output
* as necessary, disable pullups, and set initial values.
*/
#include <stdint.h>
#ifndef __JA__AM29__FLASH__INCLUDED__
#define __JA__AM29__FLASH__INCLUDED__
#define AM29ADDRLOPORT PORTA
#define AM29ADDRLODDR DDRA
#define AM29ADDRHIPORT PORTC
#define AM29ADDRHIDDR DDRC
#define AM29IOPORT PORTK
#define AM29IODDR DDRK
#define AM29IOPIN PINK
#define NOP() do { __asm__ __volatile__ ("nop"); } while(0);
#define cSECTORLIMIT 7
class AM29{
private:
int A16;
int A17;
int A18;
int CE;
int OE;
int WE;
public:
AM29(int,int,int,int,int,int);
void setup();
void setAddr(uint32_t addr);
void setAddr(uint8_t sector, uint16_t addr);
uint8_t readByte(uint32_t addr);
uint8_t readByte(uint8_t sector, uint16_t addr);
int verifySector(uint8_t sector);
int progressCheck(uint8_t data, uint32_t addr);
void byteWrite(uint8_t data, uint32_t addr);
int programByte(uint8_t data, uint8_t sector, uint16_t addr);
int programByte(uint8_t data, uint32_t addr);
void autoSelect();
void readReset();
int eraseSector(uint8_t sector);
int eraseChip();
};
#endif
AM29_Flash.cpp
#include "pins_arduino.h"
#include "wiring_private.h"
#include <avr/pgmspace.h>
#include <stdint.h>
#include "AM29_Flash.h"
AM29::AM29 (int inA16, int inA17, int inA18, int inCE, int inOE, int inWE){
A16 = inA16;
A17 = inA17;
A18 = inA18;
CE = inCE;
OE = inOE;
WE = inWE;
}
void AM29::setup(){
//call to set up proper port directions and initialize
AM29ADDRLODDR = 0xFF; //lo addr output
AM29ADDRLOPORT = 0;
AM29ADDRHIDDR = 0xFF; //hi addr output
AM29ADDRHIPORT = 0;
//Sector addr outputs
pinMode(A16, OUTPUT);
digitalWrite(A16, LOW);
pinMode(A17, OUTPUT);
digitalWrite(A17, LOW);
pinMode(A18, OUTPUT);
digitalWrite(A18, LOW);
AM29IODDR = 0; //io input
AM29IOPORT = 0; //io disable pullup
//Control signal outputs (Active low)
pinMode(CE, OUTPUT);
digitalWrite(CE,HIGH);
pinMode(OE, OUTPUT);
digitalWrite(OE,HIGH);
pinMode(WE, OUTPUT);
digitalWrite(WE,HIGH);
}
//set the address pins to the given address
void AM29::setAddr(uint32_t addr){
setAddr( (uint8_t)(addr>>16), (uint16_t)addr);
}
void AM29::setAddr(uint8_t sector, uint16_t addr){
AM29ADDRLOPORT = (uint8_t)addr;
AM29ADDRHIPORT = (uint8_t)(addr>>8);
digitalWrite(A16, (sector & (1<<0)));
digitalWrite(A17, (sector & (1<<1)));
digitalWrite(A18, (sector & (1<<2)));
}
//read in uint8_t at given address
uint8_t AM29::readByte(uint32_t addr){
return(readByte( (uint8_t)(addr>>16), (uint16_t)addr));
}
uint8_t AM29::readByte(uint8_t sector, uint16_t addr){
boolean errorFound = false;
digitalWrite(CE, LOW);
setAddr(sector, addr);
digitalWrite(OE, LOW);
NOP();
NOP();
uint8_t b = AM29IOPIN;
digitalWrite(OE, HIGH);
digitalWrite(CE, HIGH);
setAddr(0);
return(b);
}
int AM29::verifySector(uint8_t sector){
//return 0 for sector erased
//return -1 for error
//return 1 for sector programmed
//return 2 for sector erased but locked
//return 3 for sector programmed and locked
int retval = 0;
bool lock = false;
if(sector > cSECTORLIMIT) return(-1);
uint32_t startA = sector;
startA = startA << 16;
uint32_t endA = startA;
endA |= 0xFFFF;
//check sector lock
autoSelect();
uint8_t b = readByte(startA + 2);
readReset();
if(b&1) lock = true;
//check sector data
do{
if(readByte(endA) != 0xFF){
retval = 1;
break;
}
endA--;
}while(endA > startA);
if(lock) return(retval+2);
else return(retval);
}
int AM29::progressCheck(uint8_t data, uint32_t addr){
//returns -1 for error
//returns 0 for operation in progress
//returns 1 for valid
//this function based on flow chart in datasheet
uint8_t b, c;
b = readByte(addr);
c = readByte(addr);
if((b&(1<<6)) != (c&(1<<6))){
//toggle bit has toggled
if((c&(1<<5)) == (1<<5)){
//possible error
b=readByte(addr);
if((b&(1<<6)) == (c&(1<<6))){
//toggling has stopped
if(b == data) return(1);
else return(-1);
}else{
return(-1);
}
}else{
return(0);
}
}else{
if(b == data) return(1);
else return(-1);
}
}
//output given byte at given address
void AM29::byteWrite(uint8_t data, uint32_t addr){
digitalWrite(CE, LOW);
digitalWrite(OE, HIGH);
setAddr(addr);
AM29IODDR = 0xFF;
AM29IOPORT = data;
digitalWrite(WE, LOW);
delayMicroseconds(60);
digitalWrite(WE, HIGH);
delayMicroseconds(60);
digitalWrite(CE, HIGH);
AM29IOPORT = 0;
AM29IODDR = 0;
}
//program uint8_t at given address, including setup commands
//returns -2 if programmed, -1 if verification error, 1 if ok
int AM29::programByte(uint8_t data, uint8_t sector, uint16_t addr){
uint32_t fullAddr = (sector << 16);
fullAddr += addr;
return(programByte(data, fullAddr));
}
int AM29::programByte(uint8_t data, uint32_t addr){
if(readByte(addr) != 0xFF) return(-2);
autoSelect();
byteWrite(0xAA, 0x05555);
byteWrite(0x55, 0x02AAA);
byteWrite(0xA0, 0x05555);
byteWrite(data, addr);
readReset();
//verify
int check=0;
do{
check = progressCheck(data, addr);
Serial.print(".");
}while(check==0);
return(check);
}
//Send the autoSelect command
void AM29::autoSelect(){
byteWrite(0xAA, 0x05555);
byteWrite(0x55, 0x02AAA);
byteWrite(0x90, 0x05555);
uint8_t b = readByte(0);
}
//Send the readReset command
void AM29::readReset(){
byteWrite(0xF0, 0);
}
//Erase the given sector
int AM29::eraseSector(uint8_t sector){
uint32_t addr = sector;
addr = addr << 16;
Serial.println(String(addr,HEX));
byteWrite(0xAA, 0x05555);
byteWrite(0x55, 0x02AAA);
byteWrite(0x80, 0x05555);
byteWrite(0xAA, 0x05555);
byteWrite(0x55, 0x02AAA);
byteWrite(0x30, addr);
delayMicroseconds(80);
int check = 0;
do{
check = progressCheck(0xFF, addr);
Serial.print(".");
}while(check==0);
return(check);
}
//Erase entire chip
int AM29::eraseChip(){
byteWrite(0xAA, 0x05555);
byteWrite(0x55, 0x02AAA);
byteWrite(0x80, 0x05555);
byteWrite(0xAA, 0x05555);
byteWrite(0x55, 0x02AAA);
byteWrite(0x10, 0x05555);
int check = 0;
do{
check = progressCheck(0xFF, 0);
Serial.print(".");
}while(check==0);
return(check);
}
And the Arduino sketch with a serial menu using the above functions:
#include <AM29_Flash.h>
#define A16 38
#define A17 39
#define A18 40
#define CE 56
#define OE 57
#define WE 58
AM29 flash = AM29(A16, A17, A18, CE, OE, WE);
void setup() {
//setup code
flash.setup();
Serial.begin(115200);
Serial.println(F("\nFlash Memory Manager"));
Serial.println(F("(C)2015 techav"));
Serial.println();
}
void loop() {
mainMenu();
}
void printHex8(uint8_t in){
Serial.print(String((in>>4) & 0x0F,HEX));
Serial.print(String((in & 0x0F),HEX));
}
void printHex16(uint16_t in){
Serial.print(String((in>>12) & 0x000F, HEX));
Serial.print(String((in>>8) & 0x000F, HEX));
Serial.print(String((in>>4) & 0x000F, HEX));
Serial.print(String((in & 0x000F), HEX));
}
void writeFlashMenu() {
boolean exit = false;
uint8_t sector = 0;
uint16_t addr = 0;
uint8_t writeCount;
do{
String input;
Serial.print(F("?"));
input = readLineConsole();
uint8_t inBuf[255];
int e = input.length()-1;
int bufLoc = 0;
uint8_t checksum = 0;
uint8_t readSum = 0;
for(int i=1; i<e; i+=2){
uint8_t b = strtoul(input.substring(i,i+2).c_str(),NULL,16);
inBuf[bufLoc++] = b;
}
bufLoc--;
for(int i=0; i<bufLoc; i++){
checksum += (uint8_t)inBuf[i];
}
checksum = (~checksum)+1;
bufLoc++;
readSum = (uint8_t)strtoul(input.substring(e-1).c_str(),NULL,16);
if(checksum != readSum){
Serial.println(F("Invalid checksum!"));
return;
}
switch(inBuf[3]){
case(0):
//data
writeCount = 0;
addr = inBuf[1];
addr = addr << 8;
addr += inBuf[2];
Serial.print(F("Starting at address "));
printHex16(addr);
Serial.println();
do{
int r = flash.programByte(inBuf[writeCount+4],sector,addr);
switch(r){
case(1):
Serial.println();
break;
case(-2):
Serial.println(F("Sector is programmed. Erase and try again. Exiting."));
return;
break;
case(-1):
default:
Serial.print(F("Unkown error programming uint8_t "));
printHex8(inBuf[writeCount+4]);
Serial.print(F(" to address "));
printHex16(addr);
Serial.println();
break;
}
addr++;
writeCount++;
}while(writeCount<inBuf[0]);
break;
case(1):
//end of file
Serial.println(F("End of file reached. Exiting."));
exit=true;
break;
case(4):
//sector
sector = inBuf[5];
Serial.print(F("Jumping to sector "));
printHex8(sector);
Serial.println();
break;
default:
Serial.print(F("Record type "));
printHex8(inBuf[3]);
Serial.println(F(" not supported at this time."));
}
}while(!exit);
}
void verifyFlashMenu() {
String input;
uint8_t sector = 0;
bool valid = false;
do{
Serial.print(F("Select Sector to verify (1-8): "));
input = readLineConsole();
sector = (uint8_t)input.toInt();
if(sector > 0 && sector < 9){
valid = true;
sector--;
}
}while(!valid);
int r = flash.verifySector(sector);
switch(r){
case(3):
Serial.println(F("Sector is Locked"));
case(1):
Serial.println(F("Sector has data"));
break;
case(2):
Serial.println(F("Sector is Locked"));
case(0):
Serial.println(F("Sector is erased"));
break;
default:
Serial.println(F("An unknown error occurred"));
}
}
void eraseFlashMenu() {
String input;
uint8_t sector=0;
bool valid = false;
Serial.print(F("Erase CHIP or SECTOR? "));
input = readLineConsole();
if(input == F("CHIP")){
//Chip erase
Serial.print(F("Type YES to confirm chip erase: "));
input = readLineConsole();
if(input == F("YES")){
int r = flash.eraseChip();
if(r==1){
Serial.println(F("Chip erase successful"));
}else{
Serial.println(F("An error occurred. Please verify Flash contents"));
}
}else{
Serial.println(F("Operation cancelled."));
}
}else if(input == F("SECTOR")){
//Sector erase
do{
Serial.print(F("Select Sector to erase (1-8): "));
input = readLineConsole();
sector = (uint8_t)input.toInt();
if(sector > 0 && sector < 9){
valid = true;
sector--;
}
}while(!valid);
Serial.print(F("Type YES to confirm erase sector "));
Serial.print(sector + 1);
Serial.print(F(": "));
input = readLineConsole();
if(input == F("YES")){
int r = flash.eraseSector(sector);
if(r==1){
Serial.println(F("Chip erase successful"));
}else{
Serial.println(F("An error occurred. Please verify Flash contents"));
}
}
}else{
Serial.println(F("Invalid selection. Aborting"));
}
}
void readFlashMenu() {
String input;
uint8_t sector;
uint32_t addr, count;
bool valid = false;
do{
Serial.print(F("Select Sector (1-8): "));
input = readLineConsole();
sector = (uint8_t)input.toInt();
if(sector > 0 && sector < 9){
valid = true;
sector--;
}
}while(!valid);
//Serial.println(sector);
valid = false;
do{
Serial.print(F("Start address for read ($0000-$FFFF): $"));
input = readLineConsole();
addr = strtol(input.c_str(),NULL,16);
if(addr >= 0 && addr < 0xFFFF) valid = true;
}while(!valid);
//Serial.println(String(addr,HEX));
valid = false;
do{
Serial.print(F("End address for read ($0000-$FFFF): $"));
input = readLineConsole();
count = strtol(input.c_str(),NULL,16);
if(count > addr && count <= 0xFFFF) valid = true;
}while(!valid);
//Serial.println(String(count,HEX));
//start with printing Extended Address record
Serial.print(F(":0200000400"));
printHex8(sector);
printHex8((~(6+sector))+1);
Serial.println();
//read and output Flash data
do {
int a;
uint8_t checksum = 0;
if((count-addr)>=16){
a = 16;
}else{
a = count-addr+1;
}
Serial.print(":");
printHex8(a); //uint8_t count
printHex16(addr); //start address
printHex8(0); //field type
checksum += a;
checksum += (uint8_t)(addr>>8);
checksum += (uint8_t)(addr);
for(a; a>0; a--){
uint8_t b = flash.readByte(sector, addr);
checksum += (uint8_t)b;
printHex8(b);
addr++;
}
checksum = (~checksum)+1;
printHex8(checksum);
Serial.println();
}while(addr < count);
Serial.println(F(":00000001FF"));
}
String readLineConsole() {
String rx;
bool lineTerm = false;
do{
if(Serial.available()){
char c = Serial.read();
if(c >= 'a' && c <= 'z'){
c -= 0x20;
}
if(c == 0x0A){
lineTerm = true;
}else if(c != 0x0D){
rx += c;
}
}
}while(!lineTerm);
//flush out anything else that may be in the buffer
//after the line termination
while(Serial.available()){
char c = Serial.read();
}
Serial.println(rx);
return(rx);
}
void mainMenu() {
Serial.print(F("> "));
String rx = readLineConsole();
if(rx == F("READ")){
readFlashMenu();
} else if(rx == F("WRITE")){
//
writeFlashMenu();
} else if(rx == F("VERIFY")){
verifyFlashMenu();
} else if(rx == F("ERASE")){
eraseFlashMenu();
} else {
//show help
Serial.println(F("Command List:"));
Serial.println(F("Help - (This List)"));
Serial.println(F("Read - Read data from Flash"));
Serial.println(F("Write - Write data to Flash"));
Serial.println(F("Erase - Erase data from Flash"));
Serial.println(F("Verify - Check Flash sector status"));
}
}
In hindsight, I suppose I could have made this a project all its own, but here it is, as part of my Z80 project.
Discussions
Become a Hackaday.io Member
Create an account to leave a comment. Already have an account? Log In.
Hello. your project is very interesting. I am just trying to replicate the flash recorder part and there is something I don't understand. How comes that in your arduino sketch you define pins 56,57 and 58 for CE, OE and WE when mega doesn't have those pins ( it goes till 53. ) . When I checked your photo of the protoboard it seems you used ports 3,4 and 5 PWM , The point is that I believe the code as written won't work Iike this. Thank you
Are you sure? yes | no
Dear friend,
What software is used to connect to a PC?
Are you sure? yes | no