/* (banner font: aciiart.eu) _____________________________________________________________ | _ __ _ _ \ | / \ / _| |_ ___ _ _| |__ _ _ _ __ ___ ___ _ _ |\ | / _ \| |_| '_/ _ \| '_/ '_ \| | | | '_/ _ \/ _ \| '_/ || | / ___ \ _| |_| __/| | | |_) | |_| | | | | | | __/| | || | /_/ \_\| \__\___||_| |____/\___,_|_| |_| |_|___||_| || \_____________________________________________________________|| '------------------------------------------------------------' Afterburner: GAL IC Programmer for Arduino by -= olin =- Based on ATFblast 3.1 by Bruce Abbott http://www.bhabbott.net.nz/atfblast.html Based on GALBLAST by Manfred Winterhoff http://www.armory.com/%7Erstevew/Public/Pgmrs/GAL/_ClikMe1st.htm Based on GALmate by Yorck Thiele https://www.ythiee.com/2021/06/06/galmate-hardware/ Supports: * National GAL16V8 * Lattice GAL16V8A, GAL16V8B, GAL16V8D * Lattice GAL22V10B * Lattice GAL20V8 * Atmel ATF16V8B, ATF16V8C, ATF22V10B, ATF22V10CQZ Requires: * afterburner PC program to upload JED fuse map, erase, read etc. * simple programming circuit. See: https://github.com/ole00/afterburner */ #define VERSION "0.4.2" //#define DEBUG_PES //#define DEBUG_VERIFY //ARDUINO UNO pin mapping // GAL PIN NAME | ARDUINO UNO PIN NUMBER //programing voltage control pin #define PIN_VPP 11 #define PIN_SDOUT 12 #define PIN_STROBE 13 #define PIN_PV 9 #define PIN_SDIN 8 #define PIN_RA0 10 #define PIN_RA1 2 #define PIN_RA2 3 #define PIN_RA3 4 #define PIN_RA4 5 #define PIN_RA5 6 #define PIN_SCLK 7 #define COMMAND_NONE 0 #define COMMAND_UNKNOWN 1 #define COMMAND_IDENTIFY_PROGRAMMER '*' #define COMMAND_HELP 'h' #define COMMAND_UPLOAD 'u' #define COMMAND_DEBUG 'd' #define COMMAND_READ_PES 'p' #define COMMAND_WRITE_PES 'P' #define COMMAND_READ_FUSES 'r' #define COMMAND_WRITE_FUSES 'w' #define COMMAND_VERIFY_FUSES 'v' #define COMMAND_ERASE_GAL 'c' #define COMMAND_ERASE_GAL_ALL '~' #define COMMAND_UTX '#' #define COMMAND_ECHO 'e' #define COMMAND_TEST_VOLTAGE 't' #define COMMAND_SET_GAL_TYPE 'g' #define COMMAND_ENABLE_CHECK_TYPE 'f' #define COMMAND_DISABLE_CHECK_TYPE 'F' #define COMMAND_ENABLE_SECURITY 's' #define COMMAND_ENABLE_APD 'z' #define COMMAND_DISABLE_APD 'Z' #define READGAL 0 #define VERIFYGAL 1 #define READPES 2 #define SCLKTEST 3 #define WRITEGAL 4 #define ERASEGAL 5 #define ERASEALL 6 #define BURNSECURITY 7 #define WRITEPES 8 #define VPPTEST 9 #define INIT 100 //check GAL type before starting an operation #define FLAG_BIT_TYPE_CHECK (1 << 0) // ATF16V8C flavour #define FLAG_BIT_ATF16V8C (1 << 1) // Keep the power-down feature enabled for ATF C GALs #define FLAG_BIT_APD (1 << 2) // contents of pes[3] // Atmel PES is text string eg. 1B8V61F1 or 3Z01V22F1 // ^ ^ #define LATTICE 0xA1 #define NATIONAL 0x8F #define SGSTHOMSON 0x20 #define ATMEL16 'V' #define ATMEL22 '1' typedef enum { UNKNOWN, GAL16V8, GAL20V8, GAL22V10, ATF16V8B, ATF22V10B, ATF22V10C, LAST_GAL_TYPE //dummy } GALTYPE; #define BIT_NONE 0 #define BIT_ZERO 1 #define BIT_ONE 2 // config bit numbers #define CFG_BASE_16 2048 #define CFG_BASE_20 2560 #define CFG_BASE_22 5808 #define CFG_STROBE_ROW 0 #define CFG_SET_ROW 1 // Atmel power-down row #define CFG_ROW_APD 59 // common CFG fuse address map for cfg16V8 and cfg20V8 // the only difference is the starting address: 2048 for cfg16V8 and 2560 for cfg20V8 // total size: 82 static const unsigned char cfgV8[]= { 80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111, 0,1,2,3, 145, 72,73,74,75,76,77,78,79, 144, 4,5,6,7, 112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143, }; // common CFG fuse address map for cfg16V8AB and cfg20V8AB // the only difference is the starting address: 2048 for cfg16V8AB and 2560 for cfg20V8AB // total size: 82 static const unsigned char cfgV8AB[]= { 0,1,2,3, 145, 72,73,74,75, 80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143, 76,77,78,79, 144, 4,5,6,7, }; // common CFG fuse address map for cfg22V10 // starting address: 5808 // total size 20 static const unsigned char cfgV10[]= { 1,0,3,2,5,4,7,6,9,8,11,10,13,12,15,14,17,16,19,18, }; // UES user electronic signature // PES programmer electronic signature (ATF = text string, others = Vendor/Vpp/timing) // cfg configuration bits for OLMCs // GAL info static struct { GALTYPE type; unsigned char id0,id1; /* variant 1, variant 2 (eg. 16V8=0x00, 16V8A+=0x1A)*/ short fuses; /* total number of fuses */ char pins; /* number of pins on chip */ char rows; /* number of fuse rows */ unsigned char bits; /* number of fuses per row */ char uesrow; /* UES row number */ short uesfuse; /* first UES fuse number */ char uesbytes; /* number of UES bytes */ char eraserow; /* row adddeess for erase */ char eraseallrow; /* row address for erase all (also PES) */ char pesrow; /* row address for PES read/write */ char pesbytes; /* number of PES bytes */ char cfgrow; /* row address of config bits (ACW) */ unsigned short cfgbase; /* base address of the config bit numbers */ const unsigned char *cfg; /* pointer to config bit numbers */ unsigned char cfgbits; /* number of config bits */ unsigned char cfgmethod; /* strobe or set row for reading config */ } galinfo[]= { // + fuses + bits +uesbytes +pesrow +cfgbase // | +pins | +uesrow | +eraserow| +pesbytes | +cfg // +-- type + id0 + id1 | | +rows | | +uesfuse | +eraseallrow +cfgrow | | + cfgbits +cfgmethod // | | | | | | | | | | | | | | | | | | | {UNKNOWN, 0x00, 0x00, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 0, 0, NULL, 0 , 0}, {GAL16V8, 0x00, 0x1A, 2194, 20, 32, 64, 32, 2056, 8, 63, 62, 58, 8, 60, CFG_BASE_16, cfgV8AB, sizeof(cfgV8AB), CFG_STROBE_ROW}, {GAL20V8, 0x20, 0x3A, 2706, 24, 40, 64, 40, 2568, 8, 63, 62, 58, 8, 60, CFG_BASE_20, cfgV8AB, sizeof(cfgV8AB), CFG_STROBE_ROW}, {GAL22V10, 0x48, 0x49, 5892, 24, 44, 132, 44, 5828, 8, 61, 62, 58, 10, 16, CFG_BASE_22, cfgV10, sizeof(cfgV10) , CFG_SET_ROW }, {ATF16V8B, 0x00, 0x00, 2194, 20, 32, 64, 32, 2056, 8, 63, 62, 58, 8, 60, CFG_BASE_16, cfgV8AB, sizeof(cfgV8AB), CFG_STROBE_ROW}, {ATF22V10B, 0x00, 0x00, 5892, 24, 44, 132, 44, 5828, 8, 61, 62, 58, 10, 16, CFG_BASE_22, cfgV10, sizeof(cfgV10) , CFG_SET_ROW }, {ATF22V10C, 0x00, 0x00, 5892, 24, 44, 132, 44, 5828, 8, 61, 62, 58, 10, 16, CFG_BASE_22, cfgV10, sizeof(cfgV10) , CFG_SET_ROW }, }; // MAXFUSES calculated as the biggest required space to hold the fuse bitmap + UES bitmap + CFG bitmap // MAXFUSES = ((132 * 44 bits) / 8) + uesbytes + ((20 + 1) / 8) // +1 is the power-down extra fuse // 726 + 8 + 3 #define MAXFUSES 737 GALTYPE gal __attribute__ ((section (".noinit"))); //the gal device index pointing to galinfo, value is preserved between resets static short security = 0, erasetime = 100, progtime = 100, vpp = 0; char echoEnabled; unsigned char pes[12]; char line[64]; short lineIndex; char endOfLine; char mapUploaded; char isUploading; char uploadError; unsigned char fusemap[MAXFUSES]; unsigned char flagBits; static void setFuseBit(unsigned short bitPos); static unsigned short checkSum(unsigned short n); static char checkGalTypeViaPes(void); static void turnOff(void); static void printFormatedNumberHex2(unsigned char num) ; // print some help on the serial console void printHelp(char full) { Serial.println(F("AFTerburner v." VERSION)); if (!full) { Serial.println(F("type 'h' for help")); return; } Serial.println(F("commands:")); Serial.println(F(" h - print help")); Serial.println(F(" e - toggle echo")); Serial.println(F(" p - read & print PES")); Serial.println(F(" r - read & print fuses")); Serial.println(F(" u - upload fuses")); Serial.println(F(" w - write uploaded fuses")); Serial.println(F(" v - verify fuses")); Serial.println(F(" c - erase chip")); Serial.println(F(" t - test VPP")); } static void setFlagBit(uint8_t flag, uint8_t value) { if (value) { flagBits |= flag; } else { flagBits &= ~flag; } } static void setupGpios(uint8_t pm) { // Serial input of the GAL chip, output from Arduino pinMode(PIN_SDIN, pm); pinMode(PIN_STROBE, pm); pinMode(PIN_PV, pm); pinMode(PIN_RA0, pm); pinMode(PIN_RA1, pm); pinMode(PIN_RA2, pm); pinMode(PIN_RA3, pm); pinMode(PIN_RA4, pm); pinMode(PIN_RA5, pm); pinMode(PIN_SCLK, pm); pinMode(PIN_VPP, pm); } // setup the Arduino board void setup() { // initialize serial: Serial.begin(57600); isUploading = 0; endOfLine = 0; echoEnabled = 0; mapUploaded = 0; lineIndex = 0; setFlagBit(FLAG_BIT_TYPE_CHECK, 1); //do type check // Serial output from the GAL chip, input for Arduino pinMode(PIN_SDOUT, INPUT); // Set all GPIO pins to Input to prevent accidents when // inserting the GAL IC into socket. setupGpios(INPUT); printHelp(0); Serial.println(">"); } // read from serial line and discard the data void readGarbage() { while (Serial.available() > 0) { Serial.read(); } } // Reads input from the serial terminal and returns the command // which is the first character of the entered text. char handleTerminalCommands() { char c; while (Serial.available() > 0) { c = Serial.read(); line[lineIndex] = c; if (c == '\n' || c == '\r') { endOfLine = 1; } //echo input to output else { if (!isUploading && echoEnabled) { Serial.print(c); } } if (lineIndex >= 62) { lineIndex = 0; readGarbage(); Serial.println(); Serial.println("Error: line too long."); } else { lineIndex++; } } if (endOfLine) { c = COMMAND_NONE; //single letter command entered if (lineIndex == 2) { c = line[0]; } else if (lineIndex > 2) { c = line[0]; if (!isUploading || c != '#') { if (c != COMMAND_SET_GAL_TYPE) { c = COMMAND_UNKNOWN; } } } if (!isUploading) { Serial.println(); line[lineIndex] = 0; lineIndex = 0; } endOfLine = 0; return c; } return COMMAND_NONE; } // Parses decimal integer number typed as 4 digit. // Returns the number value. unsigned short parse4dec(char i) { unsigned short v = (line[i++] - '0') * 1000; v += (line[i++] - '0') * 100; v += (line[i++] - '0') * 10; v += line[i] - '0'; return v; } // Converts textual hex value 0-9, A-F to a number. unsigned char toHex(char c) { if (c >= '0' && c <= '9') return c - '0'; if (c >= 'A' && c <= 'F') return c - 'A' + 10; if (c >= 'a' && c <= 'f') return c - 'a' + 10; return 0; } // Parses hexdecimal integer number typed as 2 digit. // Returns the number value. unsigned short parse2hex(char i) { if (line[i] == '\r' || line[i] == 0 || line[i] == ' ') { return -1; } unsigned short v = toHex(line[i++]) << 4; return v + toHex(line[i]); } // Parses hexdecimal integer number typed as 4 digit. // Returns the number value. unsigned short parse4hex(char i) { if (line[i] == '\r' || line[i] == 0 || line[i] == ' ') { return -1; } unsigned short v = ((unsigned short)toHex(line[i++])) << 12; v |= ((unsigned short)toHex(line[i++])) << 8; v |= toHex(line[i++]) << 4; return v + toHex(line[i]); } // Parses a line fed by the serial connection. // This hnadles a primitive upload protocol that // expects a programatic data feed - not suitable // for human interaction. // Data: each command on its own line // line starts with '#' character followed by a command // and a space. Then a command specific data follow. // Commands: // t : gal type index to the GALTYPEE enum // f : row of fuse-map data starting on fuse bit index // c : checksum of the whole fuse map // e : end ofthe upload transfer - returns to terminal void parseUploadLine() { switch (line[1]) { case 'e': { if (uploadError) { Serial.print(F("ER upload failed")); } else { Serial.print(F("OK upload finished")); } isUploading = 0; } break; // gal type case 't': { short v = line[3] - '0'; if (v > 0 && v < LAST_GAL_TYPE) { gal = (GALTYPE) v; Serial.print(F("OK gal set: ")); Serial.println((short) gal, DEC); } else { Serial.println(F("ER unknown gal index")); uploadError = 1; } } break; //fusemap data case 'f': { char i = 8; char j; unsigned short addr = parse4dec(3); short v; do { v = parse2hex(i); if (v >= 0) { for (j = 0; j < 8; j++) { // if fuse bit is set -> then change the fusemap if (v & (1 << j)) { setFuseBit(addr); } addr++; } i += 2; } } while (v >= 0); //any fuse being set is considered as uploaded fuse map mapUploaded = 1; Serial.print(F("OK ")); Serial.println((short) addr, DEC); } break; //checksum case 'c': { unsigned short val = parse4hex(3); unsigned char apdFuse = (flagBits & FLAG_BIT_APD) ? 1 : 0; unsigned short cs = checkSum(galinfo[gal].fuses + apdFuse); if (cs == val) { Serial.println(F("OK checksum matches")); } else { uploadError = 1; Serial.print(F("ER checksum:")); Serial.print(cs, HEX); Serial.print(F(" expected:")); Serial.println(val, HEX); } } break; // PES case 'p': { uint8_t i = 0; uint8_t j = 3; while (i < 8) { pes[i] = parse2hex(j); i++; j+=3; //AB:00:... - 3 characters per one PES byte } } break; default: uploadError = 1; Serial.println(F("ER unknown upload cmd")); } lineIndex = 0; } // ********************************************************* // set/reset individual pins of GAL static void setVCC(char on) { //no control for turning the voltage on of //it is assumed the voltage is always on } static void setVPP(char on) { //programming voltage is controlled by VPP_PIN, //but the programming voltage must be set manually by user turning a Pot digitalWrite(PIN_VPP, on ? 1 : 0); //Serial.print(F("VPP set to:")); //Serial.println( on ? "12V": "5V"); delay(10); } static void setSTB(char on) { digitalWrite(PIN_STROBE, on ? 1:0); } static void setPV(char on) { digitalWrite(PIN_PV, on ? 1:0); } static void setSDIN(char on) { digitalWrite(PIN_SDIN, on ? 1:0); } static void setSCLK(char on){ digitalWrite(PIN_SCLK, on ? 1:0); } // output row address (RA0-5) static void setRow(char row) { digitalWrite(PIN_RA0, (row & 0x1)); digitalWrite(PIN_RA1, ((row & 0x2) ? 1:0)); digitalWrite(PIN_RA2, ((row & 0x4) ? 1:0)); digitalWrite(PIN_RA3, ((row & 0x8) ? 1:0)); digitalWrite(PIN_RA4, ((row & 0x10) ? 1:0)); digitalWrite(PIN_RA5, ((row & 0x20) ? 1:0)); } // serial data out form the GAL chip -> received by Arduino static char getSDOUT(void) { return digitalRead(PIN_SDOUT) != 0; } // GAL finish sequence static void turnOff(void) { delay(100); setPV(0); // P/V- low setRow(0x3F);// RA0-5 high setSDIN(1); // SDIN high setVPP(0); // Vpp off (+12V) setPV(1); // P/V- high delay(2); setVCC(0); // turn off VCC (if controlled) setupGpios(INPUT); delay(100); //ensure VPP is low } // GAL init sequence static void turnOn(char mode) { setupGpios(OUTPUT); if ( mode == WRITEGAL || mode == ERASEGAL || mode == ERASEALL || mode == BURNSECURITY || mode == WRITEPES || mode == VPPTEST || mode == READPES || mode == READGAL ) { mode = 1; } else { mode = 0; } // setVPP(mode); setVPP(0); // VPP off setPV(0); // P/V- low setRow(0x3F); // RA0-5 high - erase sequence ? //setRow(0); // RA0-5 low setSDIN(1); // SDIN high setSCLK(1); // SCLK high setSTB(1); // STB high setVCC(1); // turn on VCC (if controlled) delay(100); setSCLK(0); // SCLK low setVPP(mode); delay(20); } // clock and receive a bit in from GAL SDOUT static char receiveBit(void) { char b = getSDOUT(); setSCLK(1); setSCLK(0); return b; } // read n number of bits static void discardBits(short n) { while (n-- > 0) { receiveBit(); } } // clock a bit and send it out to GAL SDIN static void sendBit(char bitValue, char skipClkLow = 0) { setSDIN(bitValue); setSCLK(1); if (!skipClkLow) { setSCLK(0); } } // send n number of bits to GAL static void sendBits(short n, char bitValue) { char skipClkLow = flagBits & FLAG_BIT_ATF16V8C; while (n-- > 0) { sendBit(bitValue, skipClkLow && n == 0); } } // send row address bits to SDIN // ATF22V10C MSb first, other 22V10 LSb first static void sendAddress(unsigned char n, unsigned char row) { switch (gal) { case ATF22V10C: while (n-- > 1) { sendBit(row & 32); // clock in row number bits 5-1 row <<= 1; } setSDIN(row & 32); // SDIN = row number bit 0 break; default: while (n-- > 0) { sendBit(row & 1); // clock in row number bits 0-5 row >>= 1; } setSDIN(0); // SDIN = low } } // pulse STB pin low for some milliseconds static void strobe(unsigned short msec) { setSTB(0); delay(msec); setSTB(1); } // 16V8, 20V8 RA0-5 = row address, strobe. // 22V10 RA0-5 = 0, send row address (6 bits), strobe. // setBit: 0 - do not set bit, 1- set bit value 0, 2 - set bit value 1 static void strobeRow(char row, char setBit = BIT_NONE) { switch(gal) { case GAL16V8: case GAL20V8: case ATF16V8B: setRow(row); // set RA0-5 to row number if (setBit) { sendBits(1, setBit - 1); } strobe(2); // pulse /STB for 2ms break; case GAL22V10: case ATF22V10B: case ATF22V10C: setRow(0); // set RA0-5 low sendAddress(6,row); // send row number (6 bits) setSTB(0); setSTB(1); // pulse /STB setSDIN(0); // SDIN low } } // read PES: programmer electronic signature (ATF = text string, others = Vendor/Vpp/timing) static void readPes(void) { unsigned short bitmask; short byteIndex; #ifdef DEBUG_PES Serial.print(F("testing gal ")); Serial.print(gal, DEC); Serial.println(); #endif turnOn(READPES); strobeRow(galinfo[gal].pesrow); if (gal == ATF16V8B) { setPV(1); //Required for ATF16V8C } for(byteIndex = 0; byteIndex < galinfo[gal].pesbytes; byteIndex++) { unsigned char value = 0; for (bitmask = 0x1; bitmask <= 0x80; bitmask <<= 1) { if (receiveBit()) { value |= bitmask; } } pes[byteIndex] = value; } turnOff(); } static void writePes(void) { uint8_t rbit; uint8_t b, p; if (gal == ATF16V8B || gal == ATF22V10B || gal == ATF22V10C) { Serial.println(F("ER write PES not supported")); return; } turnOn(WRITEPES); setPV(1); setRow(galinfo[gal].pesrow); for (rbit = 0; rbit < 64; rbit++) { b = pes[rbit >> 3]; p = b & (1 << (rbit & 0b111)); sendBit(p); } strobe(progtime); turnOff(); } static unsigned char getDuration(unsigned char index) { switch (index) { case 0: return 1; case 1: return 2; case 2: return 5; case 3: return 10; case 4: return 20; case 5: return 30; case 6: return 40; case 7: return 50; case 8: return 60; case 9: return 70; case 10: return 80; case 11: return 90; case 12: return 100; case 13: return 200; default: return 0; } } void parsePes(char type) { unsigned char algo; if (UNKNOWN == type) { type = gal; } switch (type) { case ATF16V8B: case ATF22V10B: case ATF22V10C: progtime = 20; erasetime = 100; vpp = 48; /* 12.0V */ break; default: algo = pes[1] & 0x0F; if (algo == 5) { erasetime = (25 << ((pes[4] >> 2) &7)) / 2; progtime = getDuration(((((unsigned short)pes[5] << 8)| pes[4]) >> 5) & 15); vpp = 2 * ((pes[5] >> 1) & 31) + 20; } else switch(type) { case GAL16V8: case GAL20V8: erasetime=100; switch(algo) { case 0: vpp = 63; // 15.75V progtime = 100; break; case 1: vpp = 63; // 15.75V progtime = 80; break; case 2: vpp = 66; // 16.5V progtime = 10; break; case 3: vpp = (pes[3] == NATIONAL) ? 60 : 58; // 15.0V or 14.5V progtime = 40; break; case 4: vpp = 56; // 14V progtime = 100; break; } break; default: erasetime = (pes[3] == NATIONAL) ? 50 : 100; switch(algo) { case 0: vpp = 66; // 16.5V progtime = 10; break; case 1: vpp = 63; // 15.75V progtime = 100; break; case 2: vpp = (pes[3] == NATIONAL) ? 60 : 58; // 15.0V or 14.5V progtime = 40; break; case 3: vpp = 56; // 14V progtime = 100; break; } } } //Afterburnes seems to work with programming voltages reduced by 2V vpp -= 8; // -2V } static void setGalDefaults(void) { if (gal == ATF16V8B || gal == ATF22V10B || gal == ATF22V10C) { progtime = 20; erasetime = 100; vpp = 40; /* 10V */ } else { progtime = 80; erasetime = 80; vpp = 44; /* 11V */ } } // print PES information void printPes(char type) { Serial.print(F("PES info: ")); //voltage if (pes[3] == ATMEL16 || pes[3] == ATMEL22) { //Serial.print(" "); } else { if (pes[1] & 0x10) { Serial.print(F("3.3V ")); } else { Serial.print(F("5V ")); } } //manufacturer switch (pes[3]) { case LATTICE: Serial.print(F("Lattice ")); break; case NATIONAL: Serial.print(F("National ")); break; case SGSTHOMSON: Serial.print(F("ST Microsystems ")); break; case ATMEL16: case ATMEL22: Serial.print(F("Atmel ")); break; default: Serial.print(F("Unknown GAL, ")); } // GAL type switch (type) { case GAL16V8: Serial.print(F("GAL16V8 ")); break; case GAL20V8: Serial.print(F("GAL20V8 ")); break; case GAL22V10: Serial.print(F("GAL20V10 ")); break; case ATF16V8B: Serial.print(0 == (flagBits & FLAG_BIT_ATF16V8C) ? F("ATF16V8B "): F("ATF16V8C ")); break; case ATF22V10B: Serial.print(F("ATF22V10B ")); break; case ATF22V10C: Serial.print(F("ATF22V10C ")); break; } //programming info if (UNKNOWN != type) { Serial.print(F(" VPP=")); Serial.print(vpp >> 2, DEC); Serial.print(F(".")); Serial.print((vpp & 3) * 25, DEC); Serial.print(F(" Timing: prog=")); Serial.print(progtime, DEC); Serial.print(F(" erase=")); Serial.print(erasetime / 4, DEC); } else { Serial.print(F(" try VPP=10..14 in 1V steps")); } Serial.println(); } // sets a fuse bit on particular position // expects that the fusemap was cleared (set to zero) beforehand static void setFuseBit(unsigned short bitPos) { fusemap[bitPos >> 3] |= (1 << (bitPos & 7)); } // gets a fuse bit from specific fuse position static char getFuseBit(unsigned short bitPos) { return (fusemap[bitPos >> 3] & (1 << (bitPos & 7))) ? 1 : 0; } // generic fuse-map reading, fuse-map bits are stored in fusemap array static void readGalFuseMap(const unsigned char* cfgArray, char useDelay, char doDiscardBits) { unsigned short cfgAddr = galinfo[gal].cfgbase; unsigned short row, bit; unsigned short addr; if (flagBits & FLAG_BIT_ATF16V8C) { setPV(0); } // read fuse rows for(row = 0; row < galinfo[gal].rows; row++) { strobeRow(row); //set address of the row if (flagBits & FLAG_BIT_ATF16V8C) { setSDIN(0); setPV(1); } for(bit = 0; bit < galinfo[gal].bits; bit++) { // check the received bit is 1 and if so then set the fuse map if (receiveBit()) { addr = galinfo[gal].rows; addr *= bit; addr += row; setFuseBit(addr); } } if (useDelay) { delay(useDelay); } if (flagBits & FLAG_BIT_ATF16V8C) { setPV(0); } } // read UES strobeRow(galinfo[gal].uesrow); if (flagBits & FLAG_BIT_ATF16V8C) { setSDIN(0); setPV(1); } if (doDiscardBits) { discardBits(doDiscardBits); } for(bit = 0; bit < 64; bit++) { if (receiveBit()) { addr = galinfo[gal].uesfuse; addr += bit; setFuseBit(addr); } } if (useDelay) { delay(useDelay); } if (flagBits & FLAG_BIT_ATF16V8C) { setPV(0); } // read CFG if (galinfo[gal].cfgmethod == CFG_STROBE_ROW) { strobeRow(galinfo[gal].cfgrow); if (flagBits & FLAG_BIT_ATF16V8C) { setSDIN(0); setPV(1); } } else { setRow(galinfo[gal].cfgrow); strobe(1); } for(bit = 0; bit < galinfo[gal].cfgbits; bit++) { if (receiveBit()) { setFuseBit(cfgAddr + cfgArray[bit]); } } //check APD fuse bit - only for ATF16V8C or ATF22V10C if ((flagBits & FLAG_BIT_ATF16V8C) || gal == ATF22V10C) { setPV(0); if (gal == ATF22V10C) { setRow(0); sendAddress(6, CFG_ROW_APD); strobe(1); } else { //ATF16V8C setRow(CFG_ROW_APD); strobe(1); setPV(1); } setFlagBit(FLAG_BIT_APD, receiveBit()); } } // generic fuse-map verification, fuse map bits are compared against read bits static unsigned short verifyGalFuseMap(const unsigned char* cfgArray, char useDelay, char doDiscardBits) { unsigned short cfgAddr = galinfo[gal].cfgbase; unsigned short row, bit; unsigned short addr; char fuseBit; // fuse bit received from GAL char mapBit; // fuse bit stored in RAM unsigned short errors = 0; if (flagBits & FLAG_BIT_ATF16V8C) { setPV(0); } // read fuse rows for(row = 0; row < galinfo[gal].rows; row++) { strobeRow(row); if (flagBits & FLAG_BIT_ATF16V8C) { setSDIN(0); setPV(1); } for(bit = 0; bit < galinfo[gal].bits; bit++) { addr = galinfo[gal].rows; addr *= bit; addr += row; mapBit = getFuseBit(addr); fuseBit = receiveBit(); if (mapBit != fuseBit) { #ifdef DEBUG_VERIFY Serial.print(F("f a=")); Serial.println((row * galinfo[gal].bits) + bit, DEC); #endif errors++; } } if (useDelay) { delay(useDelay); } if (flagBits & FLAG_BIT_ATF16V8C) { setPV(0); } } // read UES strobeRow(galinfo[gal].uesrow); if (flagBits & FLAG_BIT_ATF16V8C) { setSDIN(0); setPV(1); } if (doDiscardBits) { discardBits(doDiscardBits); } for(bit = 0; bit < 64; bit++) { addr = galinfo[gal].uesfuse; addr += bit; mapBit = getFuseBit(addr); fuseBit = receiveBit(); if (mapBit != fuseBit) { #ifdef DEBUG_VERIFY Serial.print(F("U a=")); Serial.println(bit, DEC); #endif errors++; } } if (useDelay) { delay(useDelay); } if (flagBits & FLAG_BIT_ATF16V8C) { setPV(0); } // read CFG if (galinfo[gal].cfgmethod == CFG_STROBE_ROW) { strobeRow(galinfo[gal].cfgrow); if (flagBits & FLAG_BIT_ATF16V8C) { setSDIN(0); setPV(1); } } else { setRow(galinfo[gal].cfgrow); strobe(1); } for(bit = 0; bit < galinfo[gal].cfgbits; bit++) { mapBit = getFuseBit(cfgAddr + cfgArray[bit]); fuseBit = receiveBit(); if (mapBit != fuseBit) { #ifdef DEBUG_VERIFY Serial.print(F("C a=")); Serial.println(bit, DEC); #endif errors++; } } //verify PD fuse on Atmel's C GALs if ((flagBits & FLAG_BIT_ATF16V8C) || gal == ATF22V10C) { setPV(0); if (gal == ATF22V10C) { setRow(0); sendAddress(6, CFG_ROW_APD); strobe(1); } else { //ATF16V8C setRow(CFG_ROW_APD); strobe(1); setPV(1); } mapBit = (flagBits & FLAG_BIT_APD)? 1 : 0; fuseBit = receiveBit(); if (mapBit != fuseBit) { #ifdef DEBUG_VERIFY Serial.println(F("C pd")); #endif errors++; } } return errors; } // main fuse-map reading and verification function // READING: reads fuse rows, UES, CFG from GAL and stores into fusemap bit array RAM. // VERIFY: reads fuse rows, UES, CFG from GAL and compares with fusemap bit array in RAM. static void readOrVerifyGal(char verify) { unsigned short i; unsigned char* cfgArray = (unsigned char*) cfgV8; //ensure fusemap is cleared before READ operation, keep it for VERIFY operation. if (!verify) { for (i = 0; i < MAXFUSES; i++) { fusemap[i] = 0; } } turnOn(READGAL); switch(gal) { case GAL16V8: case GAL20V8: if (pes[2] == 0x1A || pes[2] == 0x3A) { cfgArray = (unsigned char*) cfgV8AB; } //read without delay, no discard if (verify) { i = verifyGalFuseMap(cfgArray, 0, 0); } else { readGalFuseMap(cfgArray, 0, 0); } break; case ATF16V8B: //read without delay, no discard if (verify) { i = verifyGalFuseMap(cfgV8AB, 0, 0); } else { readGalFuseMap(cfgV8AB, 0, 0); } break; case GAL22V10: case ATF22V10B: case ATF22V10C: //read with delay 1 ms, discard 68 cfg bits on ATFxx if (verify) { i = verifyGalFuseMap(cfgV10, 1, (gal == GAL22V10) ? 0 : 68); } else { readGalFuseMap(cfgV10, 1, (gal == GAL22V10) ? 0 : 68); } break; } turnOff(); if (verify && i > 0) { Serial.print(F("ER verify failed. Bit errors: ")); Serial.println(i, DEC); } } // fuse-map writing function for V8 GAL chips static void writeGalFuseMapV8(const unsigned char* cfgArray) { unsigned short cfgAddr = galinfo[gal].cfgbase; unsigned char row, rbit; unsigned short addr; unsigned char rbitMax = galinfo[gal].bits; const unsigned char skipLastClk = (flagBits & FLAG_BIT_ATF16V8C) ? 1 : 0; setPV(1); // write fuse rows for (row = 0; row < galinfo[gal].rows; row++) { setRow(row); for(rbit = 0; rbit < rbitMax; rbit++) { addr = galinfo[gal].rows; addr *= rbit; addr += row; sendBit(getFuseBit(addr), rbit == rbitMax - 1 ? skipLastClk : 0); } strobe(progtime); } // write UES setRow(galinfo[gal].uesrow); for (rbit = 0; rbit < 64; rbit++) { addr = galinfo[gal].uesfuse; addr += rbit; sendBit(getFuseBit(addr), rbit == 63 ? skipLastClk : 0); } strobe(progtime); // write CFG (all ICs use setRow) rbitMax = galinfo[gal].cfgbits; setRow(galinfo[gal].cfgrow); for(rbit = 0; rbit < rbitMax; rbit++) { sendBit(getFuseBit(cfgAddr + cfgArray[rbit]), rbit == rbitMax - 1 ? skipLastClk : 0); } strobe(progtime); setPV(0); // disable power-down if the APD flag is not set (only for ATF16V8C) if (skipLastClk && (flagBits & FLAG_BIT_APD) == 0) { setPV(1); strobeRow(CFG_ROW_APD, BIT_ZERO); // strobe row and send one bit with value 0 setPV(0); } } // fuse-map writing function for V10 GAL chips static void writeGalFuseMapV10(const unsigned char* cfgArray, char fillUesStart, char useSdin) { unsigned short cfgAddr = galinfo[gal].cfgbase; unsigned char row, bit; unsigned short addr; setRow(0); //RA0-5 low // write fuse rows for (row = 0; row < galinfo[gal].rows; row++) { for (bit = 0; bit < galinfo[gal].bits; bit++) { addr = galinfo[gal].rows; addr *= bit; addr += row; sendBit(getFuseBit(addr)); } sendAddress(6, row); setPV(1); strobe(progtime); setPV(0); } // write UES if (fillUesStart) { sendBits(68, 1); } for (bit = 0; bit < 64; bit++) { addr = galinfo[gal].uesfuse; addr += bit; sendBit(getFuseBit(addr)); } if (!fillUesStart) { sendBits(68, 1); } sendAddress(6, galinfo[gal].uesrow); setPV(1); strobe(progtime); setPV(0); // write CFG setRow(galinfo[gal].cfgrow); for(bit = 0; bit < galinfo[gal].cfgbits - useSdin; bit++) { sendBit(getFuseBit(cfgAddr + cfgArray[bit])); } if (useSdin) { setSDIN(getFuseBit(cfgAddr + cfgArray[19])); } setPV(1); strobe(progtime); setPV(0); if (useSdin && (flagBits & FLAG_BIT_APD) == 0) { // disable power-down feature (JEDEC bit #5892) setRow(0); sendAddress(6, CFG_ROW_APD); setPV(1); strobe(progtime); setPV(0); } } // main fuse-map writing function static void writeGal() { unsigned short i; unsigned char* cfgArray = (unsigned char*) cfgV8; turnOn(WRITEGAL); switch(gal) { case GAL16V8: case GAL20V8: if (pes[2] == 0x1A || pes[2] == 0x3A) { cfgArray = (unsigned char*) cfgV8AB; } writeGalFuseMapV8(cfgArray); break; case ATF16V8B: writeGalFuseMapV8(cfgV8AB); break; case GAL22V10: case ATF22V10B: case ATF22V10C: writeGalFuseMapV10(cfgV10, (gal == GAL22V10) ? 0 : 1, (gal == ATF22V10C) ? 1 : 0); break; } turnOff(); } // erases fuse-map in the GAL static void eraseGAL(char eraseAll) { turnOn(ERASEGAL); setPV(1); setRow(eraseAll ? galinfo[gal].eraseallrow : galinfo[gal].eraserow); if (gal == GAL16V8 || gal == ATF16V8B || gal==GAL20V8) { sendBit(1); } strobe(erasetime); setPV(0); turnOff(); } // sets security bit - disables fuse reading static void secureGAL(void) { turnOn(WRITEGAL); setPV(1); strobeRow(61, BIT_ONE); // strobe row and send one bit with value 1 setPV(0); turnOff(); } static char checkGalTypeViaPes(void) { char type = UNKNOWN; #ifdef DEBUG_PES char i; Serial.println(F("PES raw bytes:")); for (i = 0; i < 10; i++) { printFormatedNumberHex2(pes[i]); Serial.print(F(" ")); } Serial.println(); #endif setFlagBit(FLAG_BIT_ATF16V8C, 0); if (pes[7] == 'F' && pes[6]== '2' && pes[5]== '2' && (pes[4]== 'V' || pes[4]=='L') && pes[3]== '1' && pes[2]=='0') { if (pes[1] == 'B') { type = ATF22V10B; } else { type = ATF22V10C; } } else if (pes[6] == 'F' && pes[5] == '1' && pes[4]== '6' && pes[3] == 'V' && pes[2]=='8') { type = ATF16V8B; if (pes[1] == 'C' || pes[1] == 'Z') { // ATF16V8C, ATF16V8CZ setFlagBit(FLAG_BIT_ATF16V8C, 1); } } else if (pes[2] != 0x00 && pes[2] != 0xFF) { for (type = (sizeof(galinfo) / sizeof(galinfo[0])) - 1; type; type--) { if (pes[2] == galinfo[type].id0 || pes[2] == galinfo[type].id1) break; } } return type; } // checks whether gal type corresponds to PES information on the IC // note: PES must be read beforehand static char testProperGAL(void) { char type = checkGalTypeViaPes(); if (type == 0) { //Unknown or illegal PES, goto error; } else if (type != gal) { //PES indicates a different GAL type than selected. Change to detected GAL type? goto error; } return 1; error: Serial.println(F("ER unknown or wrong GAL type (check Power ON)")); return 0; } // prints a hexadecimal number - 2 digits with a leading zero static void printFormatedNumberHex2(unsigned char num) { if (num < 16) { Serial.print(F("0")); } Serial.print(num, HEX); } // prints a hexadecimal number - 4 digits with a leading zero static void printFormatedNumberHex4(unsigned short num) { if (num < 0x10) { Serial.print(F("000")); } else if (num < 0x100) { Serial.print(F("00")); } else if (num < 0x1000) { Serial.print(F("0")); } Serial.print(num, HEX); } // prints a decimal number - 4 digits with a leading zero static void printFormatedNumberDec4(unsigned short num) { if (num < 1) { Serial.print(F("0000")); return; } if (num < 10) { Serial.print(F("000")); } else if (num < 100) { Serial.print(F("00")); } else if (num < 1000) { Serial.print(F("0")); } Serial.print(num, DEC); } // adds a formated decimal number with a leading zero to a line buffer at position 'i' static unsigned char addFormatedNumberDec4(unsigned short num, unsigned char i) { char cnt = 3; while (cnt >= 0) { line[i + cnt] = '0' + (num % 10); num /= 10; cnt--; } return i + 4; } // calculates fuse-map checksum and returns it static unsigned short checkSum(unsigned short n) { unsigned short c, e, i; unsigned long a; c = e= 0; a = 0; for (i = 0; i < n; i++) { e++; if (e == 9) { e = 1; a += c; c = 0; } c >>= 1; if (getFuseBit(i)) { c += 0x80; } } return (unsigned short)((c >> (8 - e)) + a); } static void printGalName() { switch (gal) { case GAL16V8: Serial.println(F("GAL16V8")); break; case GAL20V8: Serial.println(F("GAL20V8")); break; case GAL22V10: Serial.println(F("GAL22V10")); break; case ATF16V8B: if (flagBits & FLAG_BIT_ATF16V8C) { Serial.println(F("ATF16V8C")); } else { Serial.println(F("ATF16V8B")); } break; case ATF22V10B: Serial.println(F("ATF22V10B")); break; case ATF22V10C: Serial.println(F("ATF22V10C")); break; default: Serial.println(F("GAL")); break; } } // prints the contents of fuse-map array in the form of JEDEC text file static void printJedec() { unsigned short i, j, k, n; unsigned char unused, start; uint8_t apdFuse = (flagBits & FLAG_BIT_APD) ? 1 : 0; Serial.print(F("JEDEC file for ")); printGalName(); Serial.print(F("*QP")); Serial.print(galinfo[gal].pins, DEC); Serial.print(F("*QF")); Serial.print(galinfo[gal].fuses + apdFuse, DEC); Serial.println(F("*QV0*F0*G0*X0*")); for( i = k = 0; i < galinfo[gal].bits; i++) { unused = 1; n = 0; line[n++] = 'L'; n = addFormatedNumberDec4(k, n); line[n++] = ' '; for(j= 0; j < galinfo[gal].rows; j++, k++) { if (getFuseBit(k)) { unused = 0; line[n++] = '1'; } else { line[n++] = '0'; } } line[n++] = '*'; line[n++] = 0; if (!unused) { Serial.println(line); } } if( k < galinfo[gal].uesfuse) { unused = 1; n = 0; line[n++] = 'L'; n = addFormatedNumberDec4(k, n); line[n++] = ' '; while(k < galinfo[gal].uesfuse) { if (getFuseBit(k)) { unused = 0; line[n++] = '1'; } else { line[n++] = '0'; } k++; } line[n++] = '*'; line[n++] = 0; if (!unused) { Serial.println(line); } } line[0] = 0; // UES in byte form Serial.print(F("N UES")); for (j = 0;j < galinfo[gal].uesbytes; j++) { n = 0; for (i = 0; i < 8; i++) { if (getFuseBit(k + 8 * j + i)) { if (gal == ATF22V10C) { n |= 1 << (7 - i); // big-endian } else { n |= 1 << i; // little-endian } } } Serial.print(F(" ")); printFormatedNumberHex2(n); } Serial.println(F("*")); // UES in bit form Serial.print(F("L")); printFormatedNumberDec4(k); Serial.print(F(" ")); for(j = 0; j < 8 * galinfo[gal].uesbytes; j++) { if (getFuseBit(k++)) { Serial.print(F("1")); } else { Serial.print(F("0")); } } Serial.println(F("*")); // CFG bits if (k < galinfo[gal].fuses) { Serial.print(F("L")); printFormatedNumberDec4(k); Serial.print(F(" ")); while( k < galinfo[gal].fuses) { if (getFuseBit(k++)) { Serial.print(F("1")); } else { Serial.print(F("0")); } } //ATF16V8C if (apdFuse) { Serial.print(F("1")); setFuseBit(k); // set for correct check-sum calculation } Serial.println(F("*")); } else if (apdFuse) { //ATF22V10C Serial.print(F("L")); printFormatedNumberDec4(k); Serial.println(F(" 1*")); setFuseBit(k); // set for correct check-sum calculation } Serial.print(F("N PES")); for(i = 0; i < galinfo[gal].pesbytes; i++) { Serial.print(F(" ")); printFormatedNumberHex2(pes[i]); } Serial.println(F("*")); Serial.print(F("C")); printFormatedNumberHex4(checkSum(galinfo[gal].fuses + apdFuse)); Serial.println(); Serial.println(F("*")); } // helper print function to save RAM space static void printNoFusesError() { Serial.println(F("ER fuse map not uploaded")); } static void testVoltage(int seconds) { int i; pinMode(PIN_VPP, OUTPUT); setVPP(1); for (i = 0 ; i < seconds; i++) { delay(1000); } setVPP(0); pinMode(PIN_VPP, INPUT); } // returns 1 if type check if OK, 0 if gal type does not match the type read from PES static char doTypeCheck(void) { if (0 == flagBits & FLAG_BIT_TYPE_CHECK) { return 1; // no need to do type check } readPes(); parsePes(UNKNOWN); return testProperGAL(); } // Arduino main loop void loop() { // read a command from serial terminal or COMMAND_NONE if nothing is received from serial char command = handleTerminalCommands(); // any unexpected input when uploading fuse map terminates the upload process if (isUploading && command != COMMAND_UTX && command != COMMAND_NONE) { Serial.println(F("ER upload aborted")); isUploading = 0; lineIndex = 0; } // handle commands received from the serial terminal switch (command) { // print some help case COMMAND_HELP: { printHelp(1); } break; case COMMAND_IDENTIFY_PROGRAMMER : { printHelp(0); } break; // verify fuse-map bits and bits read from the GAL chip case COMMAND_VERIFY_FUSES: { if (mapUploaded) { if (doTypeCheck()) { readOrVerifyGal(1); //just verify, do not overwrite fusemap } } else { printNoFusesError(); } } break; // handle upload command - start the download of fuse-map case COMMAND_UPLOAD: { short i; // clean fuses for (i = 0; i < MAXFUSES; i++) { fusemap[i] = 0; } isUploading = 1; uploadError = 0; } break; // command of the upload protocol case COMMAND_UTX : { parseUploadLine(); } break; // read and print the PES case COMMAND_READ_PES : { char type; readPes(); type = checkGalTypeViaPes(); parsePes(type); printPes(type); } break; case COMMAND_WRITE_PES : { char type; type = checkGalTypeViaPes(); parsePes(type); writePes(); } break; // read fuse-map from the GAL and print it in the JEDEC form case COMMAND_READ_FUSES : { if (doTypeCheck()) { readOrVerifyGal(0); //just read, no verification printJedec(); } } break; // write current fuse-map to the GAL chip case COMMAND_WRITE_FUSES : { if (mapUploaded) { if (doTypeCheck()) { writeGal(); //TODO security } } else { printNoFusesError(); } } break; // erases the fuse-map on the GAL chip case COMMAND_ERASE_GAL: { if (doTypeCheck()) { eraseGAL(0); } } break; // erases PES and the fuse-map on the GAL chip case COMMAND_ERASE_GAL_ALL: { if (doTypeCheck()) { eraseGAL(1); } } break; // sets the security bit case COMMAND_ENABLE_SECURITY: { if (doTypeCheck()) { secureGAL(); } } break; // keep atmel power-down feature enabled during write case COMMAND_ENABLE_APD: { setFlagBit(FLAG_BIT_APD, 1); Serial.println(F("OK APD set")); } break; case COMMAND_DISABLE_APD: { setFlagBit(FLAG_BIT_APD, 0); Serial.println(F("OK APD cleared")); } break; // toggles terminal echo case COMMAND_ECHO : { echoEnabled = 1 - echoEnabled; } break; case COMMAND_TEST_VOLTAGE : { testVoltage(20); } break; case COMMAND_SET_GAL_TYPE : { char type = line[1] - '0'; if (type >= 1 && type < LAST_GAL_TYPE) { gal = (GALTYPE) type; if (0 == flagBits & FLAG_BIT_TYPE_CHECK) { //no type check requested setGalDefaults(); } } else { Serial.println(F("ER Unknown gal type")); } } break; case COMMAND_ENABLE_CHECK_TYPE: { setFlagBit(FLAG_BIT_TYPE_CHECK, 1); } break; case COMMAND_DISABLE_CHECK_TYPE: { setFlagBit(FLAG_BIT_TYPE_CHECK, 0); } break; default: { if (command != COMMAND_NONE) { Serial.print(F("ER Unknown command: ")); Serial.println(line); } } } // display prompt character - important for the PC program to check that Arduino // finished the desired operation if (command != COMMAND_NONE) { Serial.println(F(">")); } // and that's it! }