/* (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 * 2024-02-02 Fixed: Command 'B9' (Calibration Offset = 0,25V) doesn't work Note: Also requires elimination of a in the PC program afterburner.c Added: 10.0V measurement in measureVppValues(() */ #define VERSION "0.5.8" //#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 // pin multiplex: ZIF_PIN <----> ARDUINO PIN or Shift register pin (0b1xxx) #define PIN_ZIF3 2 #define PIN_ZIF4 0b1 #define PIN_ZIF5 0b1000 #define PIN_ZIF6 0b100 #define PIN_ZIF7 0b10 #define PIN_ZIF8 5 #define PIN_ZIF9 6 #define PIN_ZIF10 7 #define PIN_ZIF11 8 #define PIN_ZIF13 12 #define PIN_ZIF14 11 #define PIN_ZIF15 10 #define PIN_ZIF16 9 #define PIN_ZIF20 0b100000 #define PIN_ZIF21 0b10000 #define PIN_ZIF22 4 #define PIN_ZIF23 3 #define PIN_ZIF_GND_CTRL 13 //A0: VPP sense //A3: DIGI_POT CS #define PIN_SHR_EN A1 #define PIN_SHR_CS A2 //clk and dat is shared SPI bus #define PIN_SHR_CLK A4 #define PIN_SHR_DAT A5 #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 COMMAND_MEASURE_VPP 'm' #define COMMAND_CALIBRATE_VPP 'b' #define COMMAND_CALIBRATION_OFFSET 'B' #define COMMAND_JTAG_PLAYER 'j' #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' #define ATMEL750 'C' typedef enum { UNKNOWN, GAL16V8, GAL18V10, GAL20V8, GAL20RA10, GAL20XV10, GAL22V10, GAL26CV12, GAL26V12, GAL6001, GAL6002, ATF16V8B, ATF20V8B, ATF22V10B, ATF22V10C, ATF750C, LAST_GAL_TYPE //dummy } GALTYPE; typedef enum { PINOUT_UNKNOWN, PINOUT_16V8, PINOUT_18V10, PINOUT_20V8, PINOUT_22V10, PINOUT_600, } PINOUT; #define BIT_NONE 0 #define BIT_ZERO 1 #define BIT_ONE 2 // config bit numbers #define CFG_BASE_16 2048 #define CFG_BASE_18 3456 #define CFG_BASE_20 2560 #define CFG_BASE_20RA 3200 #define CFG_BASE_20XV 1600 #define CFG_BASE_22 5808 #define CFG_BASE_26CV 6344 #define CFG_BASE_26V 7800 #define CFG_BASE_600 8154 #define CFG_BASE_750 14364 #define CFG_STROBE_ROW 0 #define CFG_SET_ROW 1 #define CFG_STROBE_ROW2 3 // Atmel power-down row #define CFG_ROW_APD 59 // Naive detection of the board's RAM size - for support of big Fuse map: // PIN_A11 - present on MEGA (8kB) or Leonardo (2.5kB SRAM) // _RENESAS_RA_ - Uno R4 (32kB) #if defined(PIN_A11) || defined(_RENESAS_RA_) #define RAM_BIG #endif // 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[] PROGMEM = { 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[] PROGMEM = { 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 cfg18V10 // starting address: 3456 // total size 20 static const unsigned char cfg18V10[] PROGMEM = { 1, 0, 3, 2, 5, 4, 7, 6, 9, 8, 11, 10, 13, 12, 15, 14, 17, 16, 19, 18 }; // common CFG fuse address map for cfg20RA10 // starting address: 3200 // total size 10 static const unsigned char cfgRA10[] PROGMEM = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; // common CFG fuse address map for cfg20XV10 // starting address: 1600 // total size 31 static const unsigned char cfgXV10[] PROGMEM = { 30, 28, 29, 20, 21, 22, 10, 11, 12, 13, 14, 0, 1, 2, 3, 4, 27, 26, 23, 24, 25, 19, 18, 17, 16, 15, 9, 8, 7, 6, 5 }; // common CFG fuse address map for cfg22V10 // starting address: 5808 // total size 20 static const unsigned char cfgV10[] PROGMEM = { 1,0,3,2,5,4,7,6,9,8,11,10,13,12,15,14,17,16,19,18, }; // common CFG fuse address map for cfg26CV12 // starting address: 6344 // total size 24 static const unsigned char cfg26CV12[] PROGMEM = { 1, 0, 3, 2, 5, 4, 7, 6, 9, 8, 11, 10, 13, 12, 15, 14, 17, 16, 19, 18, 21, 20, 23, 22 }; // common CFG fuse address map for cfg26V12 // starting address: 7800 // total size 48 static const unsigned char cfg26V12[] PROGMEM = { 36, 24, 12, 0, 37, 25, 13, 1, 38, 26, 14, 2, 39, 27, 15, 3, 40, 28, 16, 4, 41, 29, 17, 5, 42, 30, 18, 6, 43, 31, 19, 7, 44, 32, 20, 8, 45, 33, 21, 9, 46, 34, 22, 10, 47, 35, 23, 11 }; // common CFG fuse address map for cfg6001 // starting address: 8154 // total size 68 static const unsigned char cfg6001[] PROGMEM = { 67, 66, 25, 29, 33, 37, 41, 45, 49, 53, 57, 61, 60, 56, 52, 48, 44, 40, 36, 32, 28, 24, 62, 63, 58, 59, 54, 55, 50, 51, 46, 47, 42, 43, 38, 39, 34, 35, 30, 31, 26, 27, 2, 5, 8, 11, 14, 17, 20, 23, 0, 3, 6, 9, 12, 15, 18, 21, 22, 19, 16, 13, 10, 7, 4, 1, 64, 65 }; // common CFG fuse address map for cfg6002 // starting address: 8154 // total size 104 static const unsigned char cfg6002[] PROGMEM = { 103, 102, 25, 29, 33, 37, 41, 45, 49, 53, 57, 61, 60, 56, 52, 48, 44, 40, 36, 32, 28, 24, 62, 63, 58, 59, 54, 55, 50, 51, 46, 47, 42, 43, 38, 39, 34, 35, 30, 31, 26, 27, 101, 100, 99, 98, 97, 96, 95, 94, 93, 92, 91, 90, 89, 88, 87, 86, 85, 84, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 2, 5, 8, 11, 14, 17, 20, 23, 0, 3, 6, 9, 12, 15, 18, 21, 22, 19, 16, 13, 10, 7, 4, 1, 64, 65 }; // TODO: handle those: /* 30, // 75: Security? 135, // 70: Powerdown 136, // 71: PinKeeper 137, // 72: reserved1 138, // 73: reserved2 139, // 74: reserved3 */ static const uint8_t cfgV750[] PROGMEM = { 0, 3, 6, 9, 12, 15, 18, 21, 24, 27, // S0 1, 4, 7, 10, 13, 16, 19, 22, 25, 28, // S1 2, 5, 8, 11, 14, 17, 20, 23, 26, 29, // S2 31, 35, 39, 43, 47, 51, 55, 59, 63, 67, // S3 32, 36, 40, 44, 48, 52, 56, 60, 64, 68, // S4 33, 37, 41, 45, 49, 53, 57, 61, 65, 69, // S5 34, 38, 42, 46, 50, 54, 58, 62, 66, 70 // S6 }; // UES user electronic signature // PES programmer electronic signature (ATF = text string, others = Vendor/Vpp/timing) // cfg configuration bits for OLMCs // GAL info typedef 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 */ PINOUT pinout; } galinfo_t; const static galinfo_t galInfoList[] PROGMEM = { // +fuses +bits +uesbytes +pesrow +cfgbase // | +pins | +uesrow | +eraserow| +pesbytes | +cfg // +-- type + id0 + id1 | | +rows | | +uesfuse | +eraseallrow +cfgrow | | + cfgbits +cfgmethod +pinout // | | | | | | | | | | | | | | | | | | | | {UNKNOWN, 0x00, 0x00, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 0, 0, NULL , 0 , 0 , PINOUT_UNKNOWN}, {GAL16V8, 0x00, 0x1A, 2194, 20, 32, 64, 32, 2056, 8, 63, 62, 58, 8, 60, CFG_BASE_16 , cfgV8AB , sizeof(cfgV8AB) , CFG_STROBE_ROW, PINOUT_16V8 }, {GAL18V10, 0x50, 0x51, 3540, 20, 36, 96, 44, 3476, 8, 61, 60, 58, 10, 16, CFG_BASE_18 , cfg18V10 , sizeof(cfg18V10) , CFG_SET_ROW , PINOUT_18V10 }, {GAL20V8, 0x20, 0x3A, 2706, 24, 40, 64, 40, 2568, 8, 63, 62, 58, 8, 60, CFG_BASE_20 , cfgV8AB , sizeof(cfgV8AB) , CFG_STROBE_ROW, PINOUT_20V8 }, {GAL20RA10, 0x60, 0x61, 3274, 24, 40, 80, 40, 3210, 8, 61, 60, 58, 10, 16, CFG_BASE_20RA, cfgRA10 , sizeof(cfgRA10) , CFG_SET_ROW , PINOUT_22V10 }, {GAL20XV10, 0x65, 0x66, 1671, 24, 40, 40, 44, 1631, 5, 61, 60, 58, 5, 16, CFG_BASE_20XV, cfgXV10 , sizeof(cfgXV10) , CFG_SET_ROW , PINOUT_22V10 }, {GAL22V10, 0x48, 0x49, 5892, 24, 44, 132, 44, 5828, 8, 61, 62, 58, 10, 16, CFG_BASE_22 , cfgV10 , sizeof(cfgV10) , CFG_SET_ROW , PINOUT_22V10 }, {GAL26CV12, 0x58, 0x59, 6432, 28, 52, 122, 52, 6368, 8, 61, 60, 58, 12, 16, CFG_BASE_26CV, cfg26CV12, sizeof(cfg26CV12), CFG_SET_ROW , PINOUT_22V10 }, {GAL26V12, 0x5D, 0x5D, 7912, 28, 52, 150, 52, 7848, 8, 61, 60, 58, 12, 16, CFG_BASE_26V , cfg26V12 , sizeof(cfg26V12) , CFG_SET_ROW , PINOUT_22V10 }, {GAL6001, 0x40, 0x41, 8294, 24, 78, 75, 97, 8222, 9, 63, 62, 96, 8, 8, CFG_BASE_600 , cfg6001 , sizeof(cfg6001) , CFG_SET_ROW , PINOUT_600 }, {GAL6002, 0x44, 0x44, 8330, 24, 78, 75, 97, 8258, 9, 63, 62, 96, 8, 8, CFG_BASE_600 , cfg6002 , sizeof(cfg6002) , CFG_SET_ROW , PINOUT_600 }, {ATF16V8B, 0x00, 0x00, 2194, 20, 32, 64, 32, 2056, 8, 63, 62, 58, 8, 60, CFG_BASE_16 , cfgV8AB , sizeof(cfgV8AB) , CFG_STROBE_ROW, PINOUT_16V8 }, {ATF20V8B, 0x00, 0x00, 2706, 24, 40, 64, 40, 2568, 8, 63, 62, 58, 8, 60, CFG_BASE_20 , cfgV8AB , sizeof(cfgV8AB) , CFG_STROBE_ROW, PINOUT_20V8 }, {ATF22V10B, 0x00, 0x00, 5892, 24, 44, 132, 44, 5828, 8, 61, 62, 58, 10, 16, CFG_BASE_22 , cfgV10 , sizeof(cfgV10) , CFG_SET_ROW , PINOUT_22V10 }, {ATF22V10C, 0x00, 0x00, 5892, 24, 44, 132, 44, 5828, 8, 61, 62, 58, 10, 16, CFG_BASE_22 , cfgV10 , sizeof(cfgV10) , CFG_SET_ROW , PINOUT_22V10 }, {ATF750C, 0x00, 0x00, 14499, 24, 84, 171, 84,14435, 8, 61, 60,127, 10, 16, CFG_BASE_750 , cfgV750 , sizeof(cfgV750) , CFG_STROBE_ROW2, PINOUT_22V10 }, // TODO: not all numbers are clear }; galinfo_t galinfo __attribute__ ((section (".noinit"))); //preserve data between resets #ifdef RAM_BIG // for ATF750C // MAXFUSES = (((171 * 84 bits) + uesbits + (10*3 + 1 + 10*4 + 5)) + 7) / 8 // (14504 + 7) / 8 = 1813 #define MAXFUSES 1813 #else // Boards with small RAM (< 2.5kB) do not support ATF750C // MAXFUSES calculated as the biggest required space to hold the fuse bitmap // MAXFUSES = GAL6002 8330 bits = 8330/8 = 1041.25 bytes rounded up to 1042 bytes //#define MAXFUSES 1042 //extra space added for sparse fusemap (just enough to fit erased ATF750C) #define MAXFUSES 1332 #define USE_SPARSE_FUSEMAP #endif GALTYPE gal __attribute__ ((section (".noinit"))); //the gal device index pointing to galInfoList, value is preserved between resets static short erasetime = 100, progtime = 100; static uint8_t vpp = 0; char echoEnabled; unsigned char pes[12]; char line[32]; short lineIndex; char endOfLine; char mapUploaded; char isUploading; char uploadError; unsigned char fusemap[MAXFUSES]; unsigned char flagBits; char varVppExists; uint8_t lastShiftRegVal = 0; 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) ; #include "aftb_vpp.h" #include "aftb_sparse.h" // share fusemap buffer with jtag #define XSVF_HEAP fusemap #include "jtag_xsvf_player.h" // print some help on the serial console void printHelp(char full) { Serial.println(F("AFTerburner v." VERSION)); // indication for PC software that the new board desgin is used if (varVppExists) { Serial.println(F(" varVpp ")); } #ifdef RAM_BIG Serial.println(F(" RAM-BIG ")); #endif 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 & set VPP")); Serial.println(F(" b - calibrate VPP")); Serial.println(F(" m - measure VPP")); } static void setFlagBit(uint8_t flag, uint8_t value) { if (value) { flagBits |= flag; } else { flagBits &= ~flag; } } static void setPinMuxUnused(uint8_t pin, uint8_t pm) { // set to OUTPUT during active GAL operation and to INPUT when GAL is inactive pinMode(pin, pm); digitalWrite(pin, LOW); } static void setPinMux(uint8_t pm) { // ensure pull-up is enabled during reading and disabled when inactive on DOUT pin uint8_t doutMode = pm == OUTPUT ? INPUT_PULLUP: INPUT; switch (gal) { case GAL16V8: case ATF16V8B: pinMode(PIN_ZIF10, INPUT); //GND via MOSFET pinMode(PIN_ZIF11, INPUT); pinMode(PIN_ZIF13, INPUT); pinMode(PIN_ZIF14, INPUT); pinMode(PIN_ZIF16, doutMode); //DOUT // ensure ZIF10 is Grounded via transistor digitalWrite(PIN_ZIF_GND_CTRL, pm == OUTPUT ? HIGH: LOW); break; case GAL18V10: pinMode(PIN_ZIF10, INPUT); //GND via MOSFET pinMode(PIN_ZIF11, INPUT); pinMode(PIN_ZIF13, INPUT); pinMode(PIN_ZIF14, INPUT); pinMode(PIN_ZIF9, doutMode); //DOUT // ensure ZIF10 is Grounded via transistor digitalWrite(PIN_ZIF_GND_CTRL, pm == OUTPUT ? HIGH: LOW); //pull down unused pins when active setPinMuxUnused(PIN_ZIF15, pm); setPinMuxUnused(PIN_ZIF16, pm); break; case GAL20V8: case ATF20V8B: pinMode(PIN_ZIF10, pm); pinMode(PIN_ZIF11, pm); pinMode(PIN_ZIF13, pm); pinMode(PIN_ZIF14, pm); pinMode(PIN_ZIF15, doutMode); //DOUT pinMode(PIN_ZIF16, pm); // ensure ZIF10 GND pull is disabled digitalWrite(PIN_ZIF_GND_CTRL, LOW); //pull down unused pins when active setPinMuxUnused(PIN_ZIF14, pm); setPinMuxUnused(PIN_ZIF16, pm); setPinMuxUnused(PIN_ZIF23, pm); break; case GAL20RA10: case GAL20XV10: case GAL22V10: case GAL26CV12: case GAL26V12: case ATF22V10B: case ATF22V10C: case ATF750C: pinMode(PIN_ZIF10, pm); pinMode(PIN_ZIF11, pm); pinMode(PIN_ZIF13, pm); pinMode(PIN_ZIF14, doutMode); //DOUT pinMode(PIN_ZIF15, pm); pinMode(PIN_ZIF16, pm); // ensure ZIF10 GND pull is disabled digitalWrite(PIN_ZIF_GND_CTRL, LOW); //pull down unused pins when active setPinMuxUnused(PIN_ZIF15, pm); setPinMuxUnused(PIN_ZIF16, pm); setPinMuxUnused(PIN_ZIF22, pm); setPinMuxUnused(PIN_ZIF23, pm); break; case GAL6001: case GAL6002: pinMode(PIN_ZIF10, pm); pinMode(PIN_ZIF11, pm); pinMode(PIN_ZIF13, pm); pinMode(PIN_ZIF14, doutMode); //DOUT pinMode(PIN_ZIF15, pm); pinMode(PIN_ZIF16, pm); // ensure ZIF10 GND pull is disabled digitalWrite(PIN_ZIF_GND_CTRL, LOW); //pull down unused pins when active setPinMuxUnused(PIN_ZIF3, pm); setPinMuxUnused(PIN_ZIF15, pm); setPinMuxUnused(PIN_ZIF16, pm); setPinMuxUnused(PIN_ZIF22, pm); break; } } 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); if (varVppExists) { pinMode(PIN_ZIF_GND_CTRL, OUTPUT); //disconnect shift register pins (High Z) when pm == Input digitalWrite(PIN_SHR_EN, pm == INPUT ? HIGH : LOW); setPinMux(pm); } } #define SHR_SET_BIT(X) digitalWrite(PIN_SHR_CLK, 0); \ digitalWrite(PIN_SHR_DAT, (X) ? HIGH : LOW); \ digitalWrite(PIN_SHR_CLK, 1) static void setShiftReg(uint8_t val) { lastShiftRegVal = val; //assume CS is high //ensure CLK is high (might be set low by other SPI devices) digitalWrite(PIN_SHR_CLK, 1); // set CS low digitalWrite(PIN_SHR_CS, 0); SHR_SET_BIT(val & 0b10000000); SHR_SET_BIT(val & 0b1000000); SHR_SET_BIT(val & 0b100000); SHR_SET_BIT(val & 0b10000); SHR_SET_BIT(val & 0b1000); SHR_SET_BIT(val & 0b100); SHR_SET_BIT(val & 0b10); SHR_SET_BIT(val & 0b1); digitalWrite(PIN_SHR_CS, 1); } // 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 //check & initialise variable voltage (old / new board design) varVppExists = varVppInit(); // shift register pinMode(PIN_SHR_EN, OUTPUT); // 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); if (varVppExists) { // reads the calibration values if (varVppCheckCalibration()) { Serial.println(F("I: VPP calib. OK")); } // set shift reg Chip select pinMode(PIN_SHR_CS, OUTPUT); digitalWrite(PIN_SHR_CS, 1); //unselect the POT's SPI bus } Serial.println(">"); } static void sparseSetup(char clearArray){ // Note: Sparse fuse map is ignored on MCUs with big SRAM if (gal == ATF750C) { sparseInit(clearArray); } else { sparseDisable(); } } //copy galinfo item from the flash array into RAM backed struct static void copyGalInfo(void) { memcpy_P(&galinfo, &galInfoList[gal], sizeof(galinfo_t)); sparseSetup(0); } // 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 >= sizeof(line)- 2) { lineIndex = 0; readGarbage(); Serial.println(); Serial.println(F("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 != '#') { // prevent 2 character commands from being flagged as invalid if (!(c == COMMAND_SET_GAL_TYPE || c == COMMAND_CALIBRATION_OFFSET || c == COMMAND_JTAG_PLAYER)) { 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 or 5 digits. // Returns the number value. unsigned short parse45dec(char i, char five) { unsigned short v = 0; if (five) { //containts 5 digits v = (line[i++] - '0') * 10000; } 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) { unsigned short v; if (line[i] == '\r' || line[i] == 0 || line[i] == ' ') { return -1; } v = toHex(line[i++]); v <<= 4; v |= toHex(line[i++]); v <<= 4; v |= toHex(line[i++]); v <<= 4; v |= toHex(line[i]); return v ; } // 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; copyGalInfo(); 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; short v; char fiveDigitAddr = (line[7] != ' ') ? 1 : 0; addr = parse45dec(3, fiveDigitAddr); i += fiveDigitAddr; 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.fuses + apdFuse); if (cs == val) { Serial.println(F("OK checksum matches")); // Conditioning jed files might not have any fuse set, so as long as // they supply empty checksum (C0000) the upload is OK. mapUploaded = 1; } 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) { // new board desgin if (varVppExists) { uint8_t v = VPP_11V0; // when PES is read the VPP is not determined via PES if (on == READPES) { if (gal == ATF16V8B || gal == ATF20V8B || gal == ATF22V10B || gal == ATF22V10C || gal == ATF750C) { v = VPP_10V5; } else { v = VPP_11V5; } #if 0 Serial.print(F("VPP index=")); Serial.println(v); #endif } else { //safety check if (vpp < 36) { vpp = 36; //9V } else if (vpp > 66) { vpp = 48; //12V } v = (vpp >> 1) - 18; // 18: 2 * 9V, resolution 0.5V (not 0.25V) hence 'vpp >> 1' #if 0 Serial.print(F("setVPP ")); Serial.print(vpp); Serial.print(F(" index=")); Serial.println(v); #endif } varVppSet(on ? v : VPP_5V0); delay(50); //settle the voltage } // old board design else { //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) { if (varVppExists) { const PINOUT p = galinfo.pinout; uint8_t pin = PIN_ZIF13; if (p == PINOUT_16V8) { pin = PIN_ZIF15; } else if (p == PINOUT_18V10) { pin = PIN_ZIF8; } digitalWrite(pin, on ? 1:0); } else { digitalWrite(PIN_STROBE, on ? 1:0); } } static void setPV(char on) { if (varVppExists) { const PINOUT p = galinfo.pinout; uint8_t pin = PIN_ZIF23; if (p == PINOUT_22V10) { pin = PIN_ZIF3; } else if (p == PINOUT_20V8) { pin = PIN_ZIF22; } digitalWrite(pin, on ? 1:0); } else { digitalWrite(PIN_PV, on ? 1:0); } } static void setSDIN(char on) { if (varVppExists) { const PINOUT p = galinfo.pinout; if (p == PINOUT_18V10) { if (on) { lastShiftRegVal |= PIN_ZIF7; } else { lastShiftRegVal &= ~PIN_ZIF7; } setShiftReg(lastShiftRegVal); } else { const uint8_t pin = (p == PINOUT_16V8) ? PIN_ZIF9 : PIN_ZIF11; digitalWrite(pin, on ? 1:0); } } else { digitalWrite(PIN_SDIN, on ? 1:0); } } static void setSCLK(char on){ if (varVppExists) { const PINOUT p = galinfo.pinout; if (p == PINOUT_18V10) { if (on) { lastShiftRegVal |= PIN_ZIF6; } else { lastShiftRegVal &= ~PIN_ZIF6; } setShiftReg(lastShiftRegVal); } else { uint8_t pin = (p == PINOUT_16V8) ? PIN_ZIF8 : PIN_ZIF10; digitalWrite(pin, on ? 1:0); } } else { digitalWrite(PIN_SCLK, on ? 1:0); } } // output row address (RA0-5) static void setRow(char row) { if (varVppExists) { uint8_t srval = 0; const PINOUT p = galinfo.pinout; if (p == PINOUT_16V8) { digitalWrite(PIN_ZIF22, (row & 0x1)); //RA0 digitalWrite(PIN_ZIF3 , (row & 0x2)); //RA1 if (row & 0x4) srval |= PIN_ZIF4; //RA2 if (row & 0x8) srval |= PIN_ZIF5; //RA3 if (row & 0x10) srval |= PIN_ZIF6; //RA4 if (row & 0x20) srval |= PIN_ZIF7; //RA5 } else if (p == PINOUT_18V10) { digitalWrite(PIN_ZIF22, (row & 0x1)); //RA0 if (row & 0x2) srval |= PIN_ZIF21; //RA1 if (row & 0x4) srval |= PIN_ZIF20; //RA2 digitalWrite(PIN_ZIF3 , (row & 0x8)); //RA3 if (row & 0x10) srval |= PIN_ZIF4; //RA4 if (row & 0x20) srval |= PIN_ZIF5; //RA5 } else if (p == PINOUT_22V10 || p == PINOUT_600) { if (row & 0x1) srval |= PIN_ZIF4; //RA0 if (row & 0x2) srval |= PIN_ZIF5; //RA1 if (row & 0x4) srval |= PIN_ZIF6; //RA2 if (row & 0x8) srval |= PIN_ZIF7; //RA3 digitalWrite(PIN_ZIF8, (row & 0x10)); //RA4 digitalWrite(PIN_ZIF9, (row & 0x20)); //RA5 } else { //PINOUT_20V8 if (row & 0x1) srval |= PIN_ZIF21; //RA0 digitalWrite(PIN_ZIF3 , (row & 0x2)); //RA1 if (row & 0x4) srval |= PIN_ZIF4; //RA2 if (row & 0x8) srval |= PIN_ZIF5; //RA3 digitalWrite(PIN_ZIF8, (row & 0x10)); //RA4 digitalWrite(PIN_ZIF9, (row & 0x20)); //RA5 } setShiftReg(srval); } else { 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) { if (varVppExists) { const PINOUT p = galinfo.pinout; uint8_t pin = PIN_ZIF16; if (p == PINOUT_22V10 || p == PINOUT_600) { pin = PIN_ZIF14; } else if (p == PINOUT_20V8) { pin = PIN_ZIF15; } else if (p == PINOUT_18V10) { pin = PIN_ZIF9; } return digitalRead(pin) != 0; } else { 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 == READPES) { mode = 2; } else if ( mode == WRITEGAL || mode == ERASEGAL || mode == ERASEALL || mode == BURNSECURITY || mode == WRITEPES || mode == VPPTEST || 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); // For some reason ATF20V8B needs a slower clock if (gal == ATF20V8B) { delay(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; case ATF750C: while (n-- > 1) { sendBit(row & 1); // clock in row number bits 0-5 row >>= 1; } setSDIN(row & 1); // SDIN = row number bit 6 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) { unsigned char nBits = 6; switch(gal) { case GAL16V8: case GAL20V8: case ATF20V8B: case ATF16V8B: setRow(row); // set RA0-5 to row number if (setBit) { sendBits(1, setBit - 1); } strobe(2); // pulse /STB for 2ms break; case ATF750C: nBits = 7; //fall through case GAL18V10: case GAL20RA10: case GAL20XV10: case GAL22V10: case GAL26CV12: case GAL26V12: case ATF22V10B: case ATF22V10C: setRow(0); // set RA0-5 low sendAddress(nBits, row); // send row number (6 or 7 bits) setSTB(0); setSTB(1); // pulse /STB setSDIN(0); // SDIN low break; case GAL6001: case GAL6002: setRow(0); sendBits(95, 0); sendBit(1); sendAddress(7, row); sendBits(16, 0); strobe(2); // pulse /STB for 2ms break; } } static void strobeConfigRow(char row) { switch(gal) { case ATF750C: setRow(0); // set RA0-5 low setRow(galinfo.cfgrow); sendAddress(7, row); // send row number (6 bits) setSDIN(1); // SDIN high setSTB(0); setSTB(1); // pulse /STB break; } } // 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.pesrow); if (gal == ATF16V8B) { setPV(1); //Required for ATF16V8C } if (gal == GAL6001 || gal == GAL6002) { discardBits(20); } for(byteIndex = 0; byteIndex < galinfo.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 == ATF20V8B || gal == ATF22V10B || gal == ATF22V10C) { Serial.println(F("ER write PES not supported")); return; } turnOn(WRITEPES); setPV(1); switch(gal) { case GAL6001: case GAL6002: setRow(0); sendBits(20, 0); for (rbit = 0; rbit < 64; rbit++) { b = pes[rbit >> 3]; p = b & (1 << (rbit & 0b111)); sendBit(p); } sendBits(11, 0); sendBit(1); sendAddress(7, galinfo.pesrow); sendBits(16, 0); setSDIN(0); break; default: setRow(galinfo.pesrow); for (rbit = 0; rbit < 64; rbit++) { b = pes[rbit >> 3]; p = b & (1 << (rbit & 0b111)); sendBit(p); } break; } strobe(progtime); turnOff(); } static const unsigned char PROGMEM duration[] = { 1, 2, 5, 10, 20, 30, 40, 50, 60, 70, 80, 90, 100, 200 }; static unsigned char getDuration(unsigned char index) { if (index > 13) { return 0; } return pgm_read_byte(&duration[index]); } static void setGalDefaults(void) { if (gal == ATF16V8B || gal == ATF20V8B || gal == ATF22V10B || gal == ATF22V10C || gal == ATF750C) { progtime = 20; erasetime = 100; vpp = 42; /* 10.5V */ } else { progtime = 80; erasetime = 80; vpp = 48; /* 12V */ } } void parsePes(char type) { unsigned char algo; if (UNKNOWN == type) { type = gal; } #ifdef DEBUG_PES Serial.print(F("Parse pes. gal=")); Serial.println(type, DEC); #endif switch (type) { case ATF16V8B: case ATF20V8B: case ATF22V10B: case ATF22V10C: case ATF750C: 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; goto more; case GAL6001: case GAL6002: erasetime=50; more: 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 1V vpp -= 4; // -1V } // print PES information void printPes(char type) { Serial.print(F("PES info: ")); //voltage if (pes[3] == ATMEL16 || pes[3] == ATMEL22 || pes[3] == ATMEL750) { //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 ATMEL750: 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 GAL18V10: Serial.print(F("GAL18V10 ")); break; case GAL20V8: Serial.print(F("GAL20V8 ")); break; case GAL20RA10: Serial.print(F("GAL20RA10 ")); break; case GAL20XV10: Serial.print(F("GAL20XV10 ")); break; case GAL22V10: Serial.print(F("GAL22V10 ")); break; case GAL26CV12: Serial.print(F("GAL26CV12 ")); break; case GAL26V12: Serial.print(F("GAL26V12 ")); break; case GAL6001: Serial.print(F("GAL6001 ")); break; case GAL6002: Serial.print(F("GAL6002 ")); break; case ATF16V8B: Serial.print(0 == (flagBits & FLAG_BIT_ATF16V8C) ? F("ATF16V8B "): F("ATF16V8C ")); break; case ATF20V8B: Serial.print(F("ATF20V8B ")); break; case ATF22V10B: Serial.print(F("ATF22V10B ")); break; case ATF22V10C: Serial.print(F("ATF22V10C ")); break; case ATF750C: Serial.print(F("ATF750C ")); 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) { uint16_t pos; if (sparseFusemapStat) { pos = sparseSetFuseBit(bitPos); } else { pos = bitPos >> 3; //divide the bit position by 8 to get the byte position } fusemap[pos] |= (1 << (bitPos & 7)); } // gets a fuse bit from specific fuse position static char getFuseBit(unsigned short bitPos) { uint16_t pos; if (sparseFusemapStat) { pos = sparseGetFuseBit(bitPos); if (pos >= 0xFF00) { return pos & 0x1; } } else { pos = bitPos >> 3; } return (fusemap[pos] & (1 << (bitPos & 7))) ? 1 : 0; } static void setFuseBitVal(unsigned short bitPos, char val) { if (val) { setFuseBit(bitPos); } } // 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.cfgbase; unsigned short row, bit; unsigned short addr; if (flagBits & FLAG_BIT_ATF16V8C) { setPV(0); } for(row = 0; row < galinfo.rows; row++) { strobeRow(row); //set address of the row if (flagBits & FLAG_BIT_ATF16V8C) { setSDIN(0); setPV(1); } for(bit = 0; bit < galinfo.bits; bit++) { // check the received bit is 1 and if so then set the fuse map if (receiveBit()) { addr = galinfo.rows; addr *= bit; addr += row; setFuseBit(addr); } } if (useDelay) { delay(useDelay); } if (flagBits & FLAG_BIT_ATF16V8C) { setPV(0); } } // read UES strobeRow(galinfo.uesrow); if (flagBits & FLAG_BIT_ATF16V8C) { setSDIN(0); setPV(1); } if (doDiscardBits) { discardBits(doDiscardBits); } for(bit = 0; bit < galinfo.uesbytes * 8; bit++) { if (receiveBit()) { addr = galinfo.uesfuse; addr += bit; setFuseBit(addr); } } if (useDelay) { delay(useDelay); } if (flagBits & FLAG_BIT_ATF16V8C) { setPV(0); } // read CFG if (galinfo.cfgmethod == CFG_STROBE_ROW2) { //ATF750C const uint8_t cfgstroberow = 96; const uint8_t cfgrowlen = 10; const uint8_t cfgrowcount = (galinfo.cfgbits + (cfgrowlen - 1)) /cfgrowlen; uint8_t i; for(i = 0; i < cfgrowcount; i++) { strobeConfigRow(cfgstroberow + i); for(bit = 0; bit < cfgrowlen; bit++) { uint8_t absBit = (cfgrowlen * i) + bit; if (absBit >= galinfo.cfgbits) break; if (receiveBit()) { unsigned char cfgOffset = pgm_read_byte(&cfgArray[absBit]); setFuseBit(cfgAddr + cfgOffset); } } if (useDelay) { delay(useDelay); } } } else { if (galinfo.cfgmethod == CFG_STROBE_ROW) { strobeRow(galinfo.cfgrow); if (flagBits & FLAG_BIT_ATF16V8C) { setSDIN(0); setPV(1); } } else { setRow(galinfo.cfgrow); strobe(1); } for(bit = 0; bit < galinfo.cfgbits; bit++) { if (receiveBit()) { unsigned char cfgOffset = pgm_read_byte(&cfgArray[bit]); //read array byte flom flash setFuseBit(cfgAddr + cfgOffset); } } } //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()); } #if 0 if (sparseFusemapStat) { sparsePrintStat(); } #endif } static void readGalFuseMap600(const unsigned char* cfgArray) { unsigned short row, bit; unsigned short addr; for (row = 0; row < 78; row++) { strobeRow(row); discardBits(20); for (bit = 0; bit < 11; bit++) setFuseBitVal(7296 + 78 * bit + row, receiveBit()); for (bit = 0; bit < 64; bit++) setFuseBitVal(114 * bit + row, receiveBit()); discardBits(24); } for (row = 0; row < 64; row++) { sendBits(31, 0); for (bit = 0; bit < 64; bit++) sendBit(bit != row); sendBits(24, 0); setSDIN(0); strobe(2); for (bit = 0; bit < 20; bit++) setFuseBitVal(78 + 114 * row + bit, receiveBit()); discardBits(83); for (bit = 0; bit < 16; bit++) setFuseBitVal(98 + 114 * row + bit, receiveBit()); } // UES strobeRow(galinfo.uesrow); discardBits(20); addr = galinfo.uesfuse; for (bit = 0; bit < 72; bit++) setFuseBitVal(addr + bit, receiveBit()); // CFG setRow(galinfo.cfgrow); strobe(2); addr = galinfo.cfgbase; for (bit = 0; bit < galinfo.cfgbits; bit++) { unsigned char cfgOffset = pgm_read_byte(&cfgArray[bit]); //read array byte flom flash setFuseBitVal(addr + cfgOffset, 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.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; #ifdef DEBUG_VERIFY Serial.print(F("rot f:")); Serial.println(rotatedFuseMap, DEC); #endif if (flagBits & FLAG_BIT_ATF16V8C) { setPV(0); } // read fuse rows for(row = 0; row < galinfo.rows; row++) { strobeRow(row); if (flagBits & FLAG_BIT_ATF16V8C) { setSDIN(0); setPV(1); } for(bit = 0; bit < galinfo.bits; bit++) { addr = galinfo.rows; addr *= bit; addr += row; mapBit = getFuseBit(addr); //bit from RAM fuseBit = receiveBit(); // read from GAL if (mapBit != fuseBit) { #ifdef DEBUG_VERIFY Serial.print(F("f a=")); Serial.print(addr, DEC); #endif errors++; } } if (useDelay) { delay(useDelay); } if (flagBits & FLAG_BIT_ATF16V8C) { setPV(0); } } // read UES strobeRow(galinfo.uesrow); if (flagBits & FLAG_BIT_ATF16V8C) { setSDIN(0); setPV(1); } if (doDiscardBits) { discardBits(doDiscardBits); } for(bit = 0; bit < galinfo.uesbytes * 8; bit++) { addr = galinfo.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); } if (galinfo.cfgmethod == CFG_STROBE_ROW2) { //ATF750C const uint8_t cfgstroberow = 96; const uint8_t cfgrowlen = 10; const uint8_t cfgrowcount = (galinfo.cfgbits + (cfgrowlen - 1)) /cfgrowlen; uint8_t i; for(i = 0; i < cfgrowcount; i++) { strobeConfigRow(cfgstroberow + i); for(bit = 0; bit < cfgrowlen; bit++) { uint8_t absBit = (cfgrowlen * i) + bit; if (absBit >= galinfo.cfgbits) { break; } mapBit = getFuseBit(cfgAddr + pgm_read_byte(&cfgArray[absBit])); // cfgAddr + cfgOffset fuseBit = receiveBit(); if (mapBit != fuseBit) { #ifdef DEBUG_VERIFY Serial.print(F("C a=")); Serial.println(absBit, DEC); #endif errors++; } } if (useDelay) { delay(useDelay); } } } else { if (galinfo.cfgmethod == CFG_STROBE_ROW) { strobeRow(galinfo.cfgrow); if (flagBits & FLAG_BIT_ATF16V8C) { setSDIN(0); setPV(1); } } else { setRow(galinfo.cfgrow); strobe(1); } for(bit = 0; bit < galinfo.cfgbits; bit++) { unsigned char cfgOffset = pgm_read_byte(&cfgArray[bit]); //read array byte flom flash mapBit = getFuseBit(cfgAddr + cfgOffset); 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; } static unsigned short verifyGalFuseMap600(const unsigned char* cfgArray) { 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; for (row = 0; row < 78; row++) { strobeRow(row); discardBits(20); for (bit = 0; bit < 11; bit++) { mapBit = getFuseBit(7296 + 78 * bit + row); fuseBit = receiveBit(); if (mapBit != fuseBit) { #ifdef DEBUG_VERIFY Serial.print(F("f a=")); Serial.println(7296 + 78 * bit + row, DEC); #endif errors++; } } for (bit = 0; bit < 64; bit++) { mapBit = getFuseBit(114 * bit + row); fuseBit = receiveBit(); if (mapBit != fuseBit) { #ifdef DEBUG_VERIFY Serial.print(F("f a=")); Serial.println(114 * bit + row, DEC); #endif errors++; } } discardBits(24); } for (row = 0; row < 64; row++) { sendBits(31, 0); for (bit = 0; bit < 64; bit++) sendBit(bit != row); sendBits(24, 0); setSDIN(0); strobe(2); for (bit = 0; bit < 20; bit++) { mapBit = getFuseBit(78 + 114 * row + bit); fuseBit = receiveBit(); if (mapBit != fuseBit) { #ifdef DEBUG_VERIFY Serial.print(F("f a=")); Serial.println(78 + 114 * row + bit, DEC); #endif errors++; } } discardBits(83); for (bit = 0; bit < 16; bit++) { mapBit = getFuseBit(98 + 114 * row + bit); fuseBit = receiveBit(); if (mapBit != fuseBit) { #ifdef DEBUG_VERIFY Serial.print(F("f a=")); Serial.println(98 + 114 * row + bit, DEC); #endif errors++; } } } // UES strobeRow(galinfo.uesrow); discardBits(20); addr = galinfo.uesfuse; for (bit = 0; bit < 72; bit++) { mapBit = getFuseBit(addr + bit); fuseBit = receiveBit(); if (mapBit != fuseBit) { #ifdef DEBUG_VERIFY Serial.print(F("f a=")); Serial.println(addr + bit, DEC); #endif errors++; } } // CFG setRow(galinfo.cfgrow); strobe(2); addr = galinfo.cfgbase; for (bit = 0; bit < galinfo.cfgbits; bit++) { unsigned char cfgOffset = pgm_read_byte(&cfgArray[bit]); //read array byte flom flash mapBit = getFuseBit(addr + cfgOffset); fuseBit = receiveBit(); if (mapBit != fuseBit) { #ifdef DEBUG_VERIFY Serial.print(F("f a=")); Serial.println(addr + cfgOffset, DEC); #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; } sparseSetup(1); } 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: case ATF20V8B: case GAL18V10: case GAL20RA10: case GAL20XV10: case GAL26V12: case GAL26CV12: cfgArray = (unsigned char*) galinfo.cfg; //read without delay, no discard if (verify) { i = verifyGalFuseMap(cfgArray, 0, 0); } else { readGalFuseMap(cfgArray, 0, 0); } break; case GAL6001: case GAL6002: cfgArray = (gal == GAL6001) ? (unsigned char*) cfg6001 : (unsigned char*) cfg6002; //read without delay, no discard if (verify) { i = verifyGalFuseMap600(cfgArray); } else { readGalFuseMap600(cfgArray); } 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; case ATF750C: //read with delay 1 ms, discard 107 bits on ATF750C if (verify) { i = verifyGalFuseMap(galinfo.cfg, 1, galinfo.bits - 8 * galinfo.uesbytes - 1); } else { readGalFuseMap(galinfo.cfg, 1, galinfo.bits - 8 * galinfo.uesbytes - 1); } } 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.cfgbase; unsigned char row, rbit; unsigned short addr; unsigned char rbitMax = galinfo.bits; const unsigned char skipLastClk = (flagBits & FLAG_BIT_ATF16V8C) ? 1 : 0; setPV(1); // write fuse rows for (row = 0; row < galinfo.rows; row++) { setRow(row); for(rbit = 0; rbit < rbitMax; rbit++) { addr = galinfo.rows; addr *= rbit; addr += row; sendBit(getFuseBit(addr), rbit == rbitMax - 1 ? skipLastClk : 0); } strobe(progtime); } // write UES setRow(galinfo.uesrow); for (rbit = 0; rbit < 64; rbit++) { addr = galinfo.uesfuse; addr += rbit; sendBit(getFuseBit(addr), rbit == 63 ? skipLastClk : 0); } strobe(progtime); // write CFG (all ICs use setRow) rbitMax = galinfo.cfgbits; setRow(galinfo.cfgrow); for(rbit = 0; rbit < rbitMax; rbit++) { unsigned char cfgOffset = pgm_read_byte(&cfgArray[rbit]); //read array byte flom flash sendBit(getFuseBit(cfgAddr + cfgOffset), 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.cfgbase; unsigned char row, bit; unsigned short addr; unsigned short uesFill = galinfo.bits - galinfo.uesbytes * 8; setRow(0); //RA0-5 low // write fuse rows for (row = 0; row < galinfo.rows; row++) { for (bit = 0; bit < galinfo.bits; bit++) { addr = galinfo.rows; addr *= bit; addr += row; sendBit(getFuseBit(addr)); } sendAddress(6, row); setPV(1); strobe(progtime); setPV(0); } // write UES if (fillUesStart) { sendBits(uesFill, 1); } for (bit = 0; bit < galinfo.uesbytes * 8; bit++) { addr = galinfo.uesfuse; addr += bit; sendBit(getFuseBit(addr)); } if (!fillUesStart) { sendBits(uesFill, 1); } sendAddress(6, galinfo.uesrow); setPV(1); strobe(progtime); setPV(0); // write CFG setRow(galinfo.cfgrow); for(bit = 0; bit < galinfo.cfgbits - useSdin; bit++) { unsigned char cfgOffset = pgm_read_byte(&cfgArray[bit]); //read array byte flom flash sendBit(getFuseBit(cfgAddr + cfgOffset)); } if (useSdin) { unsigned char cfgOffset = pgm_read_byte(&cfgArray[19]); //read array byte flom flash setSDIN(getFuseBit(cfgAddr + cfgOffset)); } 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); } } // fuse-map writing function for ATF750C chips static void writeGalFuseMapV750(const unsigned char* cfgArray) { unsigned short cfgAddr = galinfo.cfgbase; unsigned char row, bit; unsigned short addr; unsigned short uesFill = galinfo.bits - (galinfo.uesbytes * 8) - 1; uint8_t cfgRowLen = 10; //ATF750C uint8_t cfgStrobeRow = 96; //ATF750C // write fuse rows setRow(0); //RA0-5 low delayMicroseconds(20); for(row = 0; row < galinfo.rows; row++) { for (bit = 0; bit < galinfo.bits; bit++) { addr = galinfo.rows; addr *= bit; addr += row; sendBit(getFuseBit(addr)); } sendAddress(7, row); setPV(1); delayMicroseconds(20); strobe(progtime); delayMicroseconds(100); setPV(0); delayMicroseconds(12); } setRow(0); //RA0-5 low sendBits(uesFill, 0); //send X number of 0 bits between fuse rows and UES data //write UES for (bit = 0; bit < (8 * galinfo.uesbytes); bit++) { addr = galinfo.uesfuse; addr += bit; sendBit(getFuseBit(addr)); } //set 1 bit after UES to 0 sendBits(1, 0); row = galinfo.uesrow; sendAddress(7, row); setPV(1); strobe(progtime); setPV(0); delay(progtime); // write CFG uint8_t cfgrowcount = (galinfo.cfgbits + (cfgRowLen - 1)) / cfgRowLen; for(uint8_t i = 0; i < cfgrowcount; i++) { setRow(0); delayMicroseconds(10); setRow(galinfo.cfgrow); for(bit = 0; bit < cfgRowLen; bit++) { uint8_t absBit = bit + (i * cfgRowLen); //addr = galinfo.cfgbase - (galinfo[gal].bits * rangeStartRow) + cfgArray[absBit]; addr = galinfo.cfgbase + pgm_read_byte(&cfgArray[absBit]); uint8_t v = getFuseBit(addr); sendBit(v); } sendAddress(7, i + cfgStrobeRow); delayMicroseconds(10); setPV(1); delayMicroseconds(18); strobe(progtime); // 20ms delayMicroseconds(32); setPV(0); delayMicroseconds(12); setRow(0); delayMicroseconds(12); } //TODO - read the power down fuse bit state from the fuse map and set it only if needed if (1) { // disable power-down feature (JEDEC bit #5892) setRow(0); sendAddress(7, 125); setPV(1); strobe(progtime); setPV(0); delay(progtime); } } // fuse-map writing function for 600x GAL chips static void writeGalFuseMap600(const unsigned char* cfgArray) { unsigned short cfgAddr = galinfo.cfgbase; unsigned char row, bit; unsigned short addr; setRow(0); for (row = 0; row < 78; row++) { sendBits(20, 0); for (bit = 0; bit < 11; bit++) sendBit(getFuseBit(7296 + 78 * bit + row)); for (bit = 0; bit < 64; bit++) sendBit(getFuseBit(114 * bit + row)); sendBit(1); sendAddress(7, row); sendBits(16, 0); setSDIN(0); setPV(1); strobe(progtime); setPV(0); } for (row = 0; row < 64; row++) { for (bit = 0; bit < 20; bit++) sendBit(getFuseBit(78 + 114 * row + bit)); sendBits(11, 0); for (bit = 0; bit < 64; bit++) sendBit(bit != row); sendBits(8, 0); for (bit = 0; bit < 16; bit++) sendBit(getFuseBit(98 + 114 * row + bit)); setSDIN(0); setPV(1); strobe(progtime); setPV(0); } // UES sendBits(20, 0); addr = galinfo.uesfuse; for (bit = 0; bit < 72; bit++) sendBit(getFuseBit(addr + bit)); sendBits(3, 0); sendBit(1); sendAddress(7, galinfo.uesrow); sendBits(16, 0); setSDIN(0); setPV(1); strobe(progtime); setPV(0); // CFG setRow(galinfo.cfgrow); for (bit = 0; bit < galinfo.cfgbits; bit++) { unsigned char cfgOffset = pgm_read_byte(&cfgArray[bit]); //read array byte flom flash sendBit(getFuseBit(cfgAddr + cfgOffset)); } setSDIN(0); 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: case ATF20V8B: writeGalFuseMapV8(cfgV8AB); break; case GAL6001: case GAL6002: cfgArray = (unsigned char*) galinfo.cfg; writeGalFuseMap600(cfgArray); break; case GAL18V10: case GAL20RA10: case GAL20XV10: case GAL26CV12: case GAL26V12: cfgArray = (unsigned char*) galinfo.cfg; writeGalFuseMapV10(cfgArray, 0, 0); break; case GAL22V10: case ATF22V10B: case ATF22V10C: writeGalFuseMapV10(cfgV10, (gal == GAL22V10) ? 0 : 1, (gal == ATF22V10C) ? 1 : 0); break; case ATF750C: writeGalFuseMapV750(cfgV750); } turnOff(); } // erases fuse-map in the GAL static void eraseGAL(char eraseAll) { turnOn(ERASEGAL); setPV(1); setRow(eraseAll ? galinfo.eraseallrow : galinfo.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] == '2' && pes[4]== '0' && pes[3] == 'V' && pes[2]=='8' && pes[1] == 'B') { type = ATF20V8B; } 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[8] == 'F' && pes[7] == 'V' && pes[6] == '7' && pes[5] == '5' && pes[4] == '0' && pes[3] =='C') { // complete string at beginning of row 127: "300C057VF100" type = ATF750C; } else if (pes[2] != 0x00 && pes[2] != 0xFF) { for (type = (sizeof(galInfoList) / sizeof(galinfo_t)) - 1; type; type--) { uint8_t id0 = pgm_read_byte(&galInfoList[type].id0); uint8_t id1 = pgm_read_byte(&galInfoList[type].id1); if (pes[2] == id0 || pes[2] == id1) break; } } else if (pes[3] == SGSTHOMSON && pes[2] == 0x00) { type = GAL16V8; } 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 GAL18V10: Serial.println(F("GAL18V10")); break; case GAL20V8: Serial.println(F("GAL20V8")); break; case GAL20RA10: Serial.println(F("GAL20RA10")); break; case GAL20XV10: Serial.println(F("GAL20XV10")); break; case GAL22V10: Serial.println(F("GAL22V10")); break; case GAL26CV12: Serial.println(F("GAL26CV12")); break; case GAL26V12: Serial.println(F("GAL26V12")); break; case GAL6001: Serial.println(F("GAL6001")); break; case GAL6002: Serial.println(F("GAL6002")); break; case ATF16V8B: if (flagBits & FLAG_BIT_ATF16V8C) { Serial.println(F("ATF16V8C")); } else { Serial.println(F("ATF16V8B")); } break; case ATF20V8B: Serial.println(F("ATF20V8B")); break; case ATF22V10B: Serial.println(F("ATF22V10B")); break; case ATF22V10C: Serial.println(F("ATF22V10C")); break; case ATF750C: Serial.println(F("ATF750C")); break; default: Serial.println(F("GAL")); break; } } static unsigned printJedecBlock(unsigned short k, unsigned short bits, unsigned short rows) { unsigned short i, j; unsigned char unused; for (i = 0; i < bits; i++) { unused = 1; for (j = 0; j < rows; j++) { unused &= !getFuseBit(k + j); } if (unused) { k += rows; continue; } Serial.print('L'); printFormatedNumberDec4(k); Serial.print(' '); for (j = 0; j < rows; j++, k++) { if (getFuseBit(k)) { unused = 0; Serial.print('1'); } else { Serial.print('0'); } } Serial.println('*'); } return k; } // 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.pins, DEC); Serial.print(F("*QF")); Serial.print(galinfo.fuses + apdFuse, DEC); Serial.println(F("*QV0*F0*G0*X0*")); k = 0; if (gal == GAL6001 || gal == GAL6002) { k = printJedecBlock(k, 64, 114); k = printJedecBlock(k, 11, 78); } else { k = printJedecBlock(k, galinfo.bits, galinfo.rows); } if( k < galinfo.uesfuse) { Serial.print('L'); printFormatedNumberDec4(k); Serial.print(' '); while(k < galinfo.uesfuse) { if (getFuseBit(k)) { unused = 0; Serial.print('1'); } else { Serial.print('0'); } k++; } Serial.println('*'); } // UES in byte form Serial.print(F("N UES")); for (j = 0;j < galinfo.uesbytes; j++) { n = 0; for (i = 0; i < 8; i++) { if (getFuseBit(k + 8 * j + i)) { if (gal == ATF22V10C || gal == ATF750C) { n |= 1 << (7 - i); // big-endian } else { n |= 1 << i; // little-endian } } } Serial.print(' '); printFormatedNumberHex2(n); } Serial.println('*'); // UES in bit form Serial.print('L'); printFormatedNumberDec4(k); Serial.print(' '); for(j = 0; j < 8 * galinfo.uesbytes; j++) { if (getFuseBit(k++)) { Serial.print('1'); } else { Serial.print('0'); } } Serial.println('*'); // CFG bits if (k < galinfo.fuses) { Serial.print('L'); printFormatedNumberDec4(k); Serial.print(' '); while( k < galinfo.fuses) { if (getFuseBit(k++)) { Serial.print('1'); } else { Serial.print('0'); } } //ATF16V8C if (apdFuse) { Serial.print('1'); setFuseBit(k); // set for correct check-sum calculation } Serial.println('*'); } else if (apdFuse) { //ATF22V10C Serial.print('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.pesbytes; i++) { Serial.print(' '); printFormatedNumberHex2(pes[i]); } Serial.println('*'); Serial.print('C'); printFormatedNumberHex4(checkSum(galinfo.fuses + apdFuse)); Serial.println(); Serial.println('*'); } // 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; // New board design: set VPP to 16.5V and measure values // on analogue pin A1 if (varVppExists) { int16_t v; uint8_t okCnt = 0; varVppSetMax(); for (i = 0 ; i < seconds; i++) { delay(1000); v = varVppMeasureVpp(1); //measure and print if (v >= 1640 && v <= 1664) { okCnt++; // stop early if the VPP is set correctly (still allow time for POT fine-tuning) if (okCnt > 3) { Serial.println(F("VPP OK")); i = seconds; } } else { okCnt = 0; } } varVppSet(VPP_5V0); } // Legacy board design: set the VPP_EN pin "On" and check // with multimeter the desired VPP voltage specific for GAL chip. else { 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) { setGalDefaults(); return 1; // no need to do type check } readPes(); parsePes(UNKNOWN); return testProperGAL(); } static void measureVpp(uint8_t index) { varVppSet(index); delay(150); varVppMeasureVpp(1); //print measured value delay(5000); } static void measureVppValues(void) { if (!varVppExists) { Serial.println(F("ER variable VPP not supported")); return; } Serial.print(F("VPP calib. offset: ")); Serial.println(calOffset); Serial.print(F("VPP: 4.2 - 5.0V : ")); measureVpp(VPP_5V0); Serial.print(F("VPP: 9.0V : ")); measureVpp(VPP_9V0); Serial.print(F("VPP: 10.0V : ")); measureVpp(VPP_10V0); Serial.print(F("VPP: 12.0V : ")); measureVpp(VPP_12V0); Serial.print(F("VPP: 14.0V : ")); measureVpp(VPP_14V0); Serial.print(F("VPP: 16.0V : ")); measureVpp(VPP_16V0); varVppSet(VPP_5V0); } static void calibrateVpp(void) { if (!varVppExists) { Serial.println(F("ER variable VPP not supported")); return; } if (varVppCalibrate()) { Serial.println(F("Calibration OK")); } } static void startJtagPlayer(uint8_t vpp) { jtag_port_t jport; //assign jtag pins jport.tms = 12; jport.tdi = 2; jport.tdo = 4; jport.tck = 3; jport.vref = 10; //Serial.println(vpp ? F("JTAG VPP 1"): F("JTAG VPP 0")); // ensure PC app is ready delay(200); // set VPP if required if (varVppExists) { varVppSet(vpp ? VPP_11V0 : VPP_5V0); } // start XSVF player / processor jtag_play_xsvf(&jport); // unset VPP if (varVppExists) { varVppSet(VPP_5V0); } } // 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; } sparseSetup(1); 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(); //security is handled by COMMAND_ENABLE_SECURITY command } } 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; copyGalInfo(); if (0 == flagBits & FLAG_BIT_TYPE_CHECK) { //no type check requested setGalDefaults(); } } else { Serial.print(F("ER Unknown gal type ")); Serial.println(type, DEC); } } break; case COMMAND_ENABLE_CHECK_TYPE: { setFlagBit(FLAG_BIT_TYPE_CHECK, 1); } break; case COMMAND_DISABLE_CHECK_TYPE: { int i = 0; while(i < 12){ pes[i++] = 0; } setFlagBit(FLAG_BIT_TYPE_CHECK, 0); } break; case COMMAND_MEASURE_VPP: { measureVppValues(); } break; // calibration offset helps to offset the resistor tolerances in voltage dividers and also // small differences in analog ref which is ~3.3 V derived from LDO. case COMMAND_CALIBRATION_OFFSET: { int8_t offset = line[1] - '0'; if (offset >=0 && offset <= 64) { //0:-0.32V 1:-0.31V 2: -0.30V ... 32:0V 33:0.01V 34: 0.02V ... 64:0.32V calOffset = offset - 32; Serial.print(F("Using cal offset: ")); Serial.println(calOffset); } else { Serial.println(F("ER: cal offset failed")); } } break; case COMMAND_CALIBRATE_VPP: { calibrateVpp(); } break; case COMMAND_JTAG_PLAYER: { startJtagPlayer(line[1] == '1'); //flush the serial line in case the player ended abruptly readGarbage(); } 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! }