2019-03-16 10:53:19 +00:00
|
|
|
/*
|
|
|
|
(banner font: aciiart.eu)
|
|
|
|
_____________________________________________________________
|
|
|
|
| _ __ _ _ \
|
|
|
|
| / \ / _| |_ ___ _ _| |__ _ _ _ __ ___ ___ _ _ |\
|
|
|
|
| / _ \| |_| '_/ _ \| '_/ '_ \| | | | '_/ _ \/ _ \| '_/ ||
|
|
|
|
| / ___ \ _| |_| __/| | | |_) | |_| | | | | | | __/| | ||
|
|
|
|
| /_/ \_\| \__\___||_| |____/\___,_|_| |_| |_|___||_| ||
|
|
|
|
\_____________________________________________________________||
|
|
|
|
'------------------------------------------------------------'
|
|
|
|
|
|
|
|
Afterburner: GAL IC Programmer for Arduino by -= olin =-
|
|
|
|
http://molej.cz/index_aft.html
|
|
|
|
|
|
|
|
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
|
|
|
|
|
|
|
|
Supports:
|
|
|
|
* National GAL16V8
|
|
|
|
* Lattice GAL16V8A, GAL16V8B, GAL16V8D
|
|
|
|
* Lattice GAL22V10B
|
|
|
|
* Atmel ATF16V8B, ATF22V10B, ATF22V10CQZ
|
|
|
|
|
|
|
|
Requires:
|
|
|
|
* Arduino UNO with Afterburner sketch uploaded.
|
|
|
|
* simple programming circuit.
|
|
|
|
|
|
|
|
Changelog:
|
|
|
|
* 2019.02.02 - initial version 0.1
|
2019-03-24 00:08:17 +00:00
|
|
|
* 2019.03.24 - version 0.2
|
|
|
|
- added support for Win32 and Win64 builds
|
|
|
|
- fixed serial port setup for Mac OSX
|
2019-04-09 18:56:49 +00:00
|
|
|
* 2019.04.09 - version 0.3
|
|
|
|
- fixed error detection
|
|
|
|
- 'i' command now requires GAL type to be passed
|
|
|
|
on the command line
|
2019-03-16 10:53:19 +00:00
|
|
|
|
|
|
|
This is the PC part that communicates with Arduino UNO by serial line.
|
|
|
|
To compile: gcc -g3 -O0 afterburner afterburner.c
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
#include <unistd.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include <errno.h>
|
|
|
|
|
2019-03-24 00:08:17 +00:00
|
|
|
#include "serial_port.h"
|
|
|
|
|
2023-03-21 23:23:24 +00:00
|
|
|
#define VERSION "v.0.4.1"
|
2019-03-16 10:53:19 +00:00
|
|
|
|
|
|
|
|
|
|
|
#define MAX_LINE 200
|
|
|
|
|
|
|
|
#define MAXFUSES 10000
|
|
|
|
#define GALBUFSIZE 16384
|
|
|
|
|
|
|
|
typedef enum {
|
|
|
|
UNKNOWN,
|
|
|
|
GAL16V8,
|
|
|
|
GAL20V8,
|
|
|
|
GAL22V10,
|
|
|
|
ATF16V8B,
|
|
|
|
ATF22V10B,
|
|
|
|
ATF22V10C
|
|
|
|
} Galtype;
|
|
|
|
|
|
|
|
|
|
|
|
/* GAL info */
|
|
|
|
static struct {
|
|
|
|
Galtype type;
|
|
|
|
unsigned char id0, id1; /* variant 1, variant 2 (eg. 16V8=0x00, 16V8A+=0x1A)*/
|
|
|
|
char *name; /* pointer to chip name */
|
|
|
|
int fuses; /* total number of fuses */
|
|
|
|
int pins; /* number of pins on chip */
|
|
|
|
int rows; /* number of fuse rows */
|
|
|
|
int bits; /* number of fuses per row */
|
|
|
|
int uesrow; /* UES row number */
|
|
|
|
int uesfuse; /* first UES fuse number */
|
|
|
|
int uesbytes; /* number of UES bytes */
|
|
|
|
int eraserow; /* row adddeess for erase */
|
|
|
|
int eraseallrow; /* row address for erase all */
|
|
|
|
int pesrow; /* row address for PES read/write */
|
|
|
|
int pesbytes; /* number of PES bytes */
|
|
|
|
int cfgrow; /* row address of config bits */
|
|
|
|
int cfgbits; /* number of config bits */
|
|
|
|
}
|
|
|
|
galinfo[] = {
|
|
|
|
{UNKNOWN, 0x00, 0x00, "unknown", 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 0, 0},
|
|
|
|
{GAL16V8, 0x00, 0x1A, "GAL16V8", 2194, 20, 32, 64, 32, 2056, 8, 63, 54, 58, 8, 60, 82},
|
|
|
|
{GAL20V8, 0x20, 0x3A, "GAL20V8", 2706, 24, 40, 64, 40, 2568, 8, 63, 59, 58, 8, 60, 82},
|
|
|
|
{GAL22V10, 0x48, 0x49, "GAL22V10", 5892, 24, 44, 132, 44, 5828, 8, 61, 60, 58, 10, 16, 20},
|
|
|
|
{ATF16V8B, 0x00, 0x00, "ATF16V8B", 2194, 20, 32, 64, 32, 2056, 8, 63, 54, 58, 8, 60, 82},
|
|
|
|
{ATF22V10B, 0x00, 0x00, "ATF22V10B", 5892, 24, 44, 132, 44, 5828, 8, 61, 60, 58, 10, 16, 20},
|
|
|
|
{ATF22V10C, 0x00, 0x00, "ATF22V10C", 5892, 24, 44, 132, 44, 5828, 8, 61, 60, 58, 10, 16, 20},
|
|
|
|
};
|
|
|
|
|
|
|
|
char verbose = 0;
|
|
|
|
char* filename = 0;
|
|
|
|
char* deviceName = 0;
|
|
|
|
|
2019-03-24 00:08:17 +00:00
|
|
|
SerialDeviceHandle serialF = INVALID_HANDLE;
|
2019-03-16 10:53:19 +00:00
|
|
|
Galtype gal;
|
|
|
|
int security = 0;
|
|
|
|
unsigned short checksum;
|
|
|
|
char galbuffer[GALBUFSIZE];
|
|
|
|
char fusemap[MAXFUSES];
|
2019-04-09 18:56:49 +00:00
|
|
|
char noGalCheck = 0;
|
2019-03-16 10:53:19 +00:00
|
|
|
|
|
|
|
char opRead = 0;
|
|
|
|
char opWrite = 0;
|
|
|
|
char opErase = 0;
|
|
|
|
char opInfo = 0;
|
|
|
|
char opVerify = 0;
|
|
|
|
char opTestVPP = 0;
|
|
|
|
|
|
|
|
|
|
|
|
static int waitForSerialPrompt(char* buf, int bufSize, int maxDelay);
|
2019-04-09 18:56:49 +00:00
|
|
|
static char sendGenericCommand(const char* command, const char* errorText, int maxDelay, char printResult);
|
2019-03-16 10:53:19 +00:00
|
|
|
|
|
|
|
|
|
|
|
static void printHelp() {
|
|
|
|
printf("Afterburner " VERSION " a GAL programming tool for Arduino based programmer\n");
|
2022-04-22 18:54:32 +00:00
|
|
|
printf("more info: https://github.com/ole00/afterburner\n");
|
2019-03-16 10:53:19 +00:00
|
|
|
printf("usage: afterburner command(s) [options]\n");
|
|
|
|
printf("commands: ierwvs\n");
|
|
|
|
printf(" i : read device info and programming voltage\n");
|
|
|
|
printf(" r : read fuse map from the GAL chip and display it, -t option must be set\n");
|
|
|
|
printf(" w : write fuse map, -f and -t options must be set\n");
|
|
|
|
printf(" v : verify fuse map, -f and -t options must be set\n");
|
|
|
|
printf(" e : erase the GAL chip, -t options must be set\n");
|
|
|
|
printf(" s : sets VPP ON to check the programming voltage. Ensure the GAL is NOT inserted.\n");
|
|
|
|
printf("options:\n");
|
|
|
|
printf(" -v : verbose mode\n");
|
|
|
|
printf(" -t <gal_type> : the GAL type. use GAL16V8 GAL20V8 GAL22V10 ATF16V8B ATF22V10B ATF22V10C\n");
|
|
|
|
printf(" -f <file> : JEDEC fuse map file\n");
|
2019-03-24 00:08:17 +00:00
|
|
|
printf(" -d <serial_device> : name of the serial device. Default is: %s\n", DEFAULT_SERIAL_DEVICE_NAME);
|
2019-03-16 10:53:19 +00:00
|
|
|
printf(" serial params are: 38400, 8N1\n");
|
2019-04-09 18:56:49 +00:00
|
|
|
printf(" -nc : do not check device GAL type before operation: force the GAL type set on command line\n");
|
2019-03-16 10:53:19 +00:00
|
|
|
printf("examples:\n");
|
2019-04-09 18:56:49 +00:00
|
|
|
printf(" afterburner i -t ATF16V8B : reads and prints the device info\n");
|
2019-03-16 10:53:19 +00:00
|
|
|
printf(" afterburner r -t ATF16V8B : reads the fuse map from the GAL chip and displays it\n");
|
|
|
|
printf(" afterburner wv -f fuses.jed -t ATF16V8B : reads fuse map from file and writes it to \n");
|
|
|
|
printf(" the GAL chip. Does the fuse map verification at the end.\n");
|
|
|
|
printf("hints:\n");
|
|
|
|
printf(" - use the 'i' command first to check and set the right programming voltage (VPP)\n");
|
2019-04-09 18:56:49 +00:00
|
|
|
printf(" of the chip. If the programing voltage is unknown use 10V.\n");
|
|
|
|
printf(" - known VPP voltages as tested on Afterburner with Arduino UNO: \n");
|
|
|
|
printf(" Lattice GAL16V8D, GAL22V10D: 12V \n");
|
|
|
|
printf(" Atmel ATF16V8D, ATF22V10C: 10V \n");
|
2019-03-16 10:53:19 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static char checkArgs(int argc, char** argv) {
|
|
|
|
int i;
|
|
|
|
char* type = 0;
|
|
|
|
char* modes = 0;
|
|
|
|
|
|
|
|
gal = UNKNOWN;
|
|
|
|
|
|
|
|
for (i = 1; i < argc; i++) {
|
|
|
|
char* param = argv[i];
|
|
|
|
if (strcmp("-t", param) == 0) {
|
|
|
|
i++;
|
|
|
|
type = argv[i];
|
|
|
|
} else if (strcmp("-v", param) == 0) {
|
|
|
|
verbose = 1;
|
|
|
|
} else if (strcmp("-f", param) == 0) {
|
|
|
|
i++;
|
|
|
|
filename = argv[i];
|
|
|
|
} else if (strcmp("-d", param) == 0) {
|
|
|
|
i++;
|
|
|
|
deviceName = argv[i];
|
2019-04-09 18:56:49 +00:00
|
|
|
} else if (strcmp("-nc", param) == 0) {
|
|
|
|
noGalCheck = 1;
|
2019-03-16 10:53:19 +00:00
|
|
|
} else if (param[0] != '-') {
|
|
|
|
modes = param;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
i = 0;
|
|
|
|
while (modes != 0 && modes[i] != 0) {
|
|
|
|
switch (modes[i]) {
|
|
|
|
case 'r':
|
|
|
|
opRead = 1;
|
|
|
|
break;
|
|
|
|
case 'w':
|
|
|
|
opWrite = 1;
|
|
|
|
break;
|
|
|
|
case 'v':
|
|
|
|
opVerify = 1;
|
|
|
|
break;
|
|
|
|
case 'e':
|
|
|
|
opErase = 1;
|
|
|
|
break;
|
|
|
|
case 'i':
|
|
|
|
opInfo = 1;
|
|
|
|
break;
|
|
|
|
case 's':
|
|
|
|
opTestVPP = 1;
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
printf("Error: unknown operation '%c' \n", modes[i]);
|
|
|
|
}
|
|
|
|
i++;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (opRead == 0 && opWrite == 0 && opErase == 0 && opInfo == 0 && opVerify == 0 && opTestVPP == 0) {
|
|
|
|
printHelp();
|
|
|
|
printf("Error: no command specified.\n");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (0 == filename && (opWrite == 1 || opVerify == 1)) {
|
|
|
|
printf("Error: missing JED filename\n");
|
|
|
|
return -1;
|
|
|
|
}
|
2019-04-09 18:56:49 +00:00
|
|
|
if (0 == type && (opWrite || opRead || opErase || opVerify || opInfo)) {
|
2019-03-16 10:53:19 +00:00
|
|
|
printf("Error: missing GAL type. Use -t <type> to specify.\n");
|
|
|
|
return -1;
|
|
|
|
} else if (0 != type) {
|
|
|
|
if (strcmp("GAL16V8", type) == 0) {
|
|
|
|
gal = GAL16V8;
|
|
|
|
}
|
|
|
|
if (strcmp("GAL20V8", type) == 0) {
|
|
|
|
gal = GAL20V8;
|
|
|
|
}
|
|
|
|
if (strcmp("GAL22V10", type) == 0) {
|
|
|
|
gal = GAL22V10;
|
|
|
|
}
|
|
|
|
if (strcmp("ATF16V8B", type) == 0) {
|
|
|
|
gal = ATF16V8B;
|
|
|
|
}
|
|
|
|
if (strcmp("ATF22V10B", type) == 0) {
|
|
|
|
gal = ATF22V10B;
|
|
|
|
}
|
|
|
|
if (strcmp("ATF22V10C", type) == 0) {
|
|
|
|
gal = ATF22V10C;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (UNKNOWN == gal) {
|
|
|
|
printf("Error: unknow GAL type. Types: GAL16V8 GAL20V8 GAL22V10 ATF16V8B ATF22V10B ATF22V10C\n");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
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 (fusemap[i]) {
|
|
|
|
c += 0x80;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return (unsigned short)((c >> (8 - e)) + a);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int parseFuseMap(char *ptr) {
|
|
|
|
int i, n, type, checksumpos, address, pins, lastfuse;
|
|
|
|
int state = 0; // 0=outside JEDEC, 1=skipping comment or unknown, 2=read command
|
|
|
|
|
|
|
|
security = 0;
|
|
|
|
checksum = 0;
|
|
|
|
checksumpos = 0;
|
|
|
|
pins = 0;
|
|
|
|
lastfuse = 0;
|
|
|
|
|
|
|
|
for (n = 0; ptr[n]; n++) {
|
|
|
|
if (ptr[n] == '*') {
|
|
|
|
state = 2;
|
|
|
|
} else
|
|
|
|
switch (state) {
|
|
|
|
case 2:
|
|
|
|
if (!isspace(ptr[n]))
|
|
|
|
switch (ptr[n]) {
|
|
|
|
case 'L':
|
|
|
|
address = 0;
|
|
|
|
state = 3;
|
|
|
|
break;
|
|
|
|
case 'F':
|
|
|
|
state = 5;
|
|
|
|
break;
|
|
|
|
case 'G':
|
|
|
|
state = 13;
|
|
|
|
break;
|
|
|
|
case 'Q':
|
|
|
|
state = 7;
|
|
|
|
break;
|
|
|
|
case 'C':
|
|
|
|
checksumpos = n;
|
|
|
|
state = 14;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
state = 1;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 3:
|
|
|
|
if (!isdigit(ptr[n])) {
|
|
|
|
return n;
|
|
|
|
}
|
|
|
|
address = ptr[n] - '0';
|
|
|
|
state = 4;
|
|
|
|
break;
|
|
|
|
case 4:
|
|
|
|
if (isspace(ptr[n])) {
|
|
|
|
state = 6;
|
|
|
|
} else if (isdigit(ptr[n])) {
|
|
|
|
address = 10 * address + (ptr[n] - '0');
|
|
|
|
} else {
|
|
|
|
return n;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 5:
|
|
|
|
if (isspace(ptr[n])) break; // ignored
|
|
|
|
if (ptr[n] == '0' || ptr[n] == '1') {
|
|
|
|
memset(fusemap, ptr[n] - '0', sizeof(fusemap));
|
|
|
|
} else {
|
|
|
|
return n;
|
|
|
|
}
|
|
|
|
state = 1;
|
|
|
|
break;
|
|
|
|
case 6:
|
|
|
|
if (isspace(ptr[n])) break; // ignored
|
|
|
|
if (ptr[n] == '0' || ptr[n] == '1') {
|
|
|
|
fusemap[address++] = ptr[n] - '0';
|
|
|
|
} else {
|
|
|
|
return n;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 7:
|
|
|
|
if (isspace(ptr[n])) break; // ignored
|
|
|
|
if (ptr[n] == 'P') {
|
|
|
|
pins = 0;
|
|
|
|
state = 8;
|
|
|
|
} else if (ptr[n] == 'F') {
|
|
|
|
lastfuse = 0;
|
|
|
|
state = 9;
|
|
|
|
} else {
|
|
|
|
state = 2;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 8:
|
|
|
|
if (isspace(ptr[n])) break; // ignored
|
|
|
|
if (!isdigit(ptr[n])) return n;
|
|
|
|
pins = ptr[n] - '0';
|
|
|
|
state = 10;
|
|
|
|
break;
|
|
|
|
case 9:
|
|
|
|
if (isspace(ptr[n])) break; // ignored
|
|
|
|
if (!isdigit(ptr[n])) return n;
|
|
|
|
lastfuse = ptr[n] - '0';
|
|
|
|
state = 11;
|
|
|
|
break;
|
|
|
|
case 10:
|
|
|
|
if (isdigit(ptr[n])) {
|
|
|
|
pins = 10 * pins + (ptr[n] - '0');
|
|
|
|
} else if (isspace(ptr[n])) {
|
|
|
|
state = 12;
|
|
|
|
} else {
|
|
|
|
return n;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 11:
|
|
|
|
if (isdigit(ptr[n])) {
|
|
|
|
lastfuse = 10 * lastfuse + (ptr[n] - '0');
|
|
|
|
} else if (isspace(ptr[n])) {
|
|
|
|
state = 12;
|
|
|
|
} else {
|
|
|
|
return n;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 12:
|
|
|
|
if (!isspace(ptr[n])) {
|
|
|
|
return n;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 13:
|
|
|
|
if (isspace(ptr[n])) break; // ignored
|
|
|
|
if (ptr[n] == '0' || ptr[n] == '1') {
|
|
|
|
security = ptr[n] - '0';
|
|
|
|
} else {
|
|
|
|
return n;
|
|
|
|
}
|
|
|
|
state = 1;
|
|
|
|
break;
|
|
|
|
case 14:
|
|
|
|
if (isspace(ptr[n])) break; // ignored
|
|
|
|
if (isdigit(ptr[n])) {
|
|
|
|
checksum = ptr[n] - '0';
|
|
|
|
} else if (toupper(ptr[n]) >= 'A' && toupper(ptr[n]) <= 'F') {
|
|
|
|
checksum = toupper(ptr[n]) - 'A' + 10;
|
|
|
|
} else return n;
|
|
|
|
state = 15;
|
|
|
|
break;
|
|
|
|
case 15:
|
|
|
|
if (isdigit(ptr[n])) {
|
|
|
|
checksum = 16 * checksum + ptr[n] - '0';
|
|
|
|
} else if (toupper(ptr[n]) >= 'A' && toupper(ptr[n]) <= 'F') {
|
|
|
|
checksum = 16 * checksum + toupper(ptr[n]) - 'A' + 10;
|
|
|
|
} else if (isspace(ptr[n])) {
|
|
|
|
state = 2;
|
|
|
|
} else return n;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (lastfuse || pins) {
|
|
|
|
int cs = checkSum(lastfuse);
|
|
|
|
if (checksum && checksum != cs) {
|
2019-04-09 18:56:49 +00:00
|
|
|
printf("Checksum does not match! given=0x%04X calculated=0x%04X last fuse=%i\n", checksum, cs, lastfuse);
|
2019-03-16 10:53:19 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
for (type = 0, i = 1; i < sizeof(galinfo) / sizeof(galinfo[0]); i++) {
|
|
|
|
if (
|
|
|
|
(lastfuse == 0 ||
|
|
|
|
galinfo[i].fuses == lastfuse ||
|
|
|
|
galinfo[i].uesfuse == lastfuse && galinfo[i].uesfuse + 8 * galinfo[i].uesbytes == galinfo[i].fuses)
|
|
|
|
&&
|
|
|
|
(pins == 0 ||
|
|
|
|
galinfo[i].pins == pins ||
|
|
|
|
galinfo[i].pins == 24 && pins == 28)
|
|
|
|
) {
|
|
|
|
if (gal == 0) {
|
|
|
|
type = i;
|
|
|
|
break;
|
|
|
|
} else if (!type) {
|
|
|
|
type = i;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return n;
|
|
|
|
}
|
|
|
|
|
|
|
|
static char readJedec(void) {
|
|
|
|
FILE* f;
|
|
|
|
int size;
|
|
|
|
|
|
|
|
f = fopen(filename, "r");
|
|
|
|
if (f) {
|
|
|
|
size = fread(galbuffer, 1, GALBUFSIZE, f);
|
|
|
|
fclose(f);
|
|
|
|
galbuffer[size] = 0;
|
|
|
|
} else {
|
|
|
|
printf("Error: failed to open file: %s\n", filename);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int openSerial(void) {
|
2022-04-01 21:15:43 +00:00
|
|
|
char buf[512] = {0};
|
2019-03-24 00:08:17 +00:00
|
|
|
char devName[256] = {0};
|
2019-03-16 10:53:19 +00:00
|
|
|
int total;
|
|
|
|
int labelPos;
|
|
|
|
|
|
|
|
|
|
|
|
//open device name
|
2019-03-24 00:08:17 +00:00
|
|
|
snprintf(devName, sizeof(devName), "%s", (deviceName == 0) ? DEFAULT_SERIAL_DEVICE_NAME : deviceName);
|
|
|
|
serialDeviceCheckName(devName, sizeof(devName));
|
2019-03-16 10:53:19 +00:00
|
|
|
|
|
|
|
if (verbose) {
|
|
|
|
printf("opening serial: %s\n", devName);
|
|
|
|
}
|
|
|
|
|
2019-03-24 00:08:17 +00:00
|
|
|
serialF = serialDeviceOpen(devName);
|
|
|
|
if (serialF == INVALID_HANDLE) {
|
2019-03-16 10:53:19 +00:00
|
|
|
printf("Error: failed to open serial device: %s\n", devName);
|
|
|
|
return -2;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// prod the programmer to output it's identification
|
|
|
|
sprintf(buf, "*\r");
|
2019-03-24 00:08:17 +00:00
|
|
|
serialDeviceWrite(serialF, buf, 2);
|
2019-03-16 10:53:19 +00:00
|
|
|
|
|
|
|
//read programmer's message
|
|
|
|
total = waitForSerialPrompt(buf, 512, 3000);
|
|
|
|
buf[total] = 0;
|
|
|
|
|
|
|
|
//check we are communicating with Afterburner programmer
|
|
|
|
labelPos = strstr(buf, "AFTerburner v.") - buf;
|
|
|
|
|
|
|
|
if (labelPos >= 0 && labelPos < 500 && buf[total - 3] == '>') {
|
|
|
|
//all OK
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
if (verbose) {
|
|
|
|
printf("Output from programmer not recognised: %s\n", buf);
|
|
|
|
}
|
2019-03-24 00:08:17 +00:00
|
|
|
serialDeviceClose(serialF);
|
|
|
|
serialF = INVALID_HANDLE;
|
2019-03-16 10:53:19 +00:00
|
|
|
return -4;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void closeSerial(void) {
|
2019-03-24 00:08:17 +00:00
|
|
|
if (INVALID_HANDLE == serialF) {
|
2019-03-16 10:53:19 +00:00
|
|
|
return;
|
|
|
|
}
|
2019-03-24 00:08:17 +00:00
|
|
|
serialDeviceClose(serialF);
|
|
|
|
serialF = INVALID_HANDLE;
|
2019-03-16 10:53:19 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int checkPromptExists(char* buf, int bufSize) {
|
|
|
|
int i;
|
|
|
|
for (i = 0; i < bufSize && buf[i] != 0; i++) {
|
|
|
|
if (buf[i] == '>') {
|
|
|
|
return i;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
static char* stripPrompt(char* buf) {
|
|
|
|
int len;
|
|
|
|
int i;
|
|
|
|
if (buf == 0) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
len = strlen(buf);
|
|
|
|
i = checkPromptExists(buf, len);
|
|
|
|
if (i >= 0) {
|
|
|
|
buf[i] = 0;
|
|
|
|
len = i;
|
|
|
|
}
|
|
|
|
|
|
|
|
//strp rear new line characters
|
|
|
|
for (i = len - 1; i >= 0; i--) {
|
|
|
|
if (buf[i] != '\r' && buf[i] != '\n') {
|
|
|
|
break;
|
|
|
|
} else {
|
|
|
|
buf[i] = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//strip frontal new line characters
|
|
|
|
for (i = 0; buf[i] != 0; i++) {
|
|
|
|
if (buf[0] == '\r' || buf[0] == '\n') {
|
|
|
|
buf++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return buf;
|
|
|
|
}
|
|
|
|
|
2019-04-09 18:56:49 +00:00
|
|
|
//finds beginnig of the last line
|
|
|
|
static char* findLastLine(char* buf) {
|
|
|
|
int i;
|
|
|
|
char* result = buf;
|
|
|
|
|
|
|
|
if (buf == 0) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
for (i = 0; buf[i] != 0; i++) {
|
|
|
|
if (buf[i] == '\r' || buf[i] == '\n') {
|
|
|
|
result = buf + i + 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2019-03-16 10:53:19 +00:00
|
|
|
static int waitForSerialPrompt(char* buf, int bufSize, int maxDelay) {
|
|
|
|
char* bufStart = buf;
|
|
|
|
int bufTotal = bufSize;
|
|
|
|
int bufPos = 0;
|
|
|
|
int readSize;
|
|
|
|
|
|
|
|
memset(buf, 0, bufSize);
|
|
|
|
|
|
|
|
while (maxDelay > 0) {
|
2019-03-24 00:08:17 +00:00
|
|
|
readSize = serialDeviceRead(serialF, buf, bufSize);
|
2019-03-16 10:53:19 +00:00
|
|
|
if (readSize > 0) {
|
|
|
|
bufPos += readSize;
|
|
|
|
if (checkPromptExists(bufStart, bufTotal) >= 0) {
|
2022-04-01 16:48:20 +00:00
|
|
|
maxDelay = 4; //force exit, but read the rest of the line
|
2019-03-16 10:53:19 +00:00
|
|
|
} else {
|
|
|
|
buf += readSize;
|
|
|
|
bufSize -= readSize;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (maxDelay > 0) {
|
2022-04-01 21:15:43 +00:00
|
|
|
/* WIN_API handles timeout itself */
|
|
|
|
#ifndef _USE_WIN_API_
|
2023-03-21 23:23:24 +00:00
|
|
|
usleep(10 * 1000);
|
|
|
|
maxDelay -= 10;
|
2022-04-01 21:15:43 +00:00
|
|
|
#else
|
|
|
|
maxDelay -= 30;
|
|
|
|
#endif
|
2019-03-16 10:53:19 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return bufPos;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int sendLine(char* buf, int bufSize, int maxDelay) {
|
|
|
|
int total;
|
|
|
|
int writeSize;
|
|
|
|
char* obuf = buf;
|
|
|
|
|
2019-03-24 00:08:17 +00:00
|
|
|
if (serialF == INVALID_HANDLE) {
|
2019-03-16 10:53:19 +00:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (buf == 0) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
total = strlen(buf);
|
|
|
|
// write the query into the modem's file
|
|
|
|
// file is opened non blocking so we have to ensure all contents is written
|
|
|
|
while (total > 0) {
|
2019-03-24 00:08:17 +00:00
|
|
|
writeSize = serialDeviceWrite(serialF, buf, total);
|
2019-03-16 10:53:19 +00:00
|
|
|
if (writeSize < 0) {
|
2019-03-24 00:08:17 +00:00
|
|
|
printf("ERROR: written: %i (%s)\n", writeSize, strerror(errno));
|
2019-03-16 10:53:19 +00:00
|
|
|
return -4;
|
|
|
|
}
|
|
|
|
buf += writeSize;
|
|
|
|
total -= writeSize;
|
|
|
|
}
|
|
|
|
|
|
|
|
total = waitForSerialPrompt(obuf, bufSize, (maxDelay < 0) ? 6 : maxDelay);
|
|
|
|
obuf[total] = 0;
|
|
|
|
obuf = stripPrompt(obuf);
|
|
|
|
if (verbose) {
|
|
|
|
printf("read: %i '%s'\n", total, obuf);
|
|
|
|
}
|
|
|
|
|
|
|
|
return total;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Upload fusemap in byte format (as opposed to bit format used in JEDEC file).
|
|
|
|
static char upload() {
|
|
|
|
char fuseSet;
|
|
|
|
char buf[MAX_LINE];
|
|
|
|
char line[64];
|
2019-04-09 18:56:49 +00:00
|
|
|
unsigned int i, j, n;
|
2019-03-16 10:53:19 +00:00
|
|
|
|
|
|
|
if (openSerial() != 0) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Start upload
|
|
|
|
sprintf(buf, "u\r");
|
|
|
|
sendLine(buf, MAX_LINE, 20);
|
|
|
|
|
|
|
|
//device type
|
|
|
|
sprintf(buf, "#t %i %s\r", (int) gal, galinfo[gal].name);
|
|
|
|
sendLine(buf, MAX_LINE, 300);
|
|
|
|
|
|
|
|
//fuse map
|
|
|
|
buf[0] = 0;
|
|
|
|
fuseSet = 0;
|
2019-04-09 18:56:49 +00:00
|
|
|
for (i = 0; i < galinfo[gal].fuses;) {
|
2019-03-16 10:53:19 +00:00
|
|
|
unsigned char f = 0;
|
|
|
|
if (i % 32 == 0) {
|
|
|
|
if (i != 0) {
|
|
|
|
strcat(buf, "\r");
|
|
|
|
//the buffer contains at least one fuse set to 1
|
|
|
|
if (fuseSet) {
|
2019-04-09 18:56:49 +00:00
|
|
|
#ifdef DEBUG_UPLOAD
|
|
|
|
printf("%s\n", buf);
|
|
|
|
#endif
|
2019-03-16 10:53:19 +00:00
|
|
|
sendLine(buf, MAX_LINE, 100);
|
2019-04-09 18:56:49 +00:00
|
|
|
buf[0] = 0;
|
2019-03-16 10:53:19 +00:00
|
|
|
}
|
|
|
|
fuseSet = 0;
|
|
|
|
}
|
2019-04-09 18:56:49 +00:00
|
|
|
sprintf(buf, "#f %04i ", i);
|
2019-03-16 10:53:19 +00:00
|
|
|
}
|
2019-04-09 18:56:49 +00:00
|
|
|
f = 0;
|
|
|
|
for (j = 0; j < 8 && i < galinfo[gal].fuses; j++,i++) {
|
2019-03-16 10:53:19 +00:00
|
|
|
if (fusemap[i]) {
|
|
|
|
f |= (1 << j);
|
|
|
|
fuseSet = 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
sprintf(line, "%02X", f);
|
|
|
|
strcat(buf, line);
|
2019-04-09 18:56:49 +00:00
|
|
|
|
2019-03-16 10:53:19 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// send last unfinished fuse line
|
2019-04-09 18:56:49 +00:00
|
|
|
if (i % 32 && fuseSet) {
|
2019-03-16 10:53:19 +00:00
|
|
|
strcat(buf, "\r");
|
2019-04-09 18:56:49 +00:00
|
|
|
#ifdef DEBUG_UPLOAD
|
|
|
|
printf("%s\n", buf);
|
|
|
|
#endif
|
2019-03-16 10:53:19 +00:00
|
|
|
sendLine(buf, MAX_LINE, 100);
|
|
|
|
}
|
|
|
|
|
|
|
|
//checksum
|
|
|
|
if (verbose) {
|
|
|
|
printf("sending csum: %04X\n", checkSum(galinfo[gal].fuses));
|
|
|
|
}
|
|
|
|
sprintf(buf, "#c %04X\r", checkSum(galinfo[gal].fuses));
|
|
|
|
sendLine(buf, MAX_LINE, 300);
|
|
|
|
|
|
|
|
//end of upload
|
2019-04-09 18:56:49 +00:00
|
|
|
return sendGenericCommand("#e\r", "Upload failed", 300, 0);
|
2019-03-16 10:53:19 +00:00
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
static char sendGenericCommand(const char* command, const char* errorText, int maxDelay, char printResult) {
|
|
|
|
char buf[MAX_LINE];
|
|
|
|
int readSize;
|
|
|
|
|
|
|
|
sprintf(buf, "%s", command);
|
|
|
|
readSize = sendLine(buf, MAX_LINE, maxDelay);
|
|
|
|
if (readSize < 0) {
|
|
|
|
if (verbose) {
|
|
|
|
printf("%s\n", errorText);
|
|
|
|
}
|
|
|
|
return -1;
|
|
|
|
} else {
|
|
|
|
char* response = stripPrompt(buf);
|
2019-04-09 18:56:49 +00:00
|
|
|
char* lastLine = findLastLine(response);
|
|
|
|
if (lastLine == 0 || (lastLine[0] == 'E' && lastLine[1] == 'R')) {
|
2019-03-16 10:53:19 +00:00
|
|
|
printf("%s\n", response);
|
|
|
|
return -1;
|
|
|
|
} else if (printResult) {
|
|
|
|
printf("%s\n", response);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static char operationWriteOrVerify(char doWrite) {
|
|
|
|
char buf[MAX_LINE];
|
|
|
|
int readSize;
|
|
|
|
|
|
|
|
char result;
|
|
|
|
|
|
|
|
if (readJedec()) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
result = parseFuseMap(galbuffer);
|
|
|
|
if (verbose) {
|
|
|
|
printf("parse result=%i\n", result);
|
|
|
|
}
|
|
|
|
result = upload();
|
|
|
|
if (result) {
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
// write command
|
|
|
|
if (doWrite) {
|
|
|
|
result = sendGenericCommand("w\r", "write failed ?", 4000, 0);
|
|
|
|
if (result) {
|
|
|
|
goto finish;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// verify command
|
|
|
|
if (opVerify) {
|
|
|
|
result = sendGenericCommand("v\r", "verify failed ?", 4000, 0);
|
|
|
|
}
|
|
|
|
finish:
|
|
|
|
closeSerial();
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static char operationReadInfo(void) {
|
|
|
|
|
|
|
|
char result;
|
|
|
|
|
|
|
|
if (openSerial() != 0) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (verbose) {
|
|
|
|
printf("sending 'p' command...\n");
|
|
|
|
}
|
|
|
|
result = sendGenericCommand("p\r", "info failed ?", 4000, 1);
|
|
|
|
|
|
|
|
closeSerial();
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Test of programming voltage. Most chips require +12V to start prograaming.
|
|
|
|
// This test function turns ON the ENable pin so the Programming voltage is set.
|
|
|
|
// After 20 seconds the ENable pin is turned OFF. This gives you time to turn the
|
|
|
|
// pot on the MT3608 module and calibrate the right voltage for the GAL chip.
|
|
|
|
static char operationTestVpp(void) {
|
|
|
|
|
|
|
|
char result;
|
|
|
|
|
|
|
|
if (openSerial() != 0) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (verbose) {
|
|
|
|
printf("sending 't' command...\n");
|
|
|
|
}
|
|
|
|
printf("Turn the Pot on the MT3608 module to check / set the VPP\n");
|
|
|
|
//Voltage testing takes ~20 seconds
|
|
|
|
result = sendGenericCommand("t\r", "info failed ?", 22000, 1);
|
|
|
|
|
|
|
|
closeSerial();
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2019-04-09 18:56:49 +00:00
|
|
|
static char operationSetGalCheck(void) {
|
|
|
|
int readSize;
|
|
|
|
char result;
|
|
|
|
|
|
|
|
if (openSerial() != 0) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
result = sendGenericCommand(noGalCheck ? "F\r" : "f\r", "noGalCheck failed ?", 4000, 0);
|
|
|
|
closeSerial();
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
static char operationSetGalType(Galtype type) {
|
|
|
|
char buf[MAX_LINE];
|
|
|
|
int readSize;
|
|
|
|
char result;
|
|
|
|
|
|
|
|
if (openSerial() != 0) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
sprintf(buf, "g%i\r", (int)type);
|
|
|
|
result = sendGenericCommand(buf, "setGalType failed ?", 4000, 0);
|
|
|
|
closeSerial();
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2019-03-16 10:53:19 +00:00
|
|
|
static char operationEraseGal(void) {
|
|
|
|
char buf[MAX_LINE];
|
|
|
|
int readSize;
|
|
|
|
char result;
|
|
|
|
|
|
|
|
if (openSerial() != 0) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
//Switch to upload mode to specify GAL
|
|
|
|
sprintf(buf, "u\r");
|
|
|
|
sendLine(buf, MAX_LINE, 300);
|
|
|
|
|
|
|
|
//set GAL type
|
|
|
|
sprintf(buf, "#t %i\r", (int) gal);
|
|
|
|
sendLine(buf, MAX_LINE, 300);
|
|
|
|
|
|
|
|
//Exit upload mode (ensure the return texts are discarded by waiting 100 ms)
|
|
|
|
sprintf(buf, "#e\r");
|
|
|
|
sendLine(buf, MAX_LINE, 100);
|
|
|
|
|
|
|
|
result = sendGenericCommand("c\r", "erase failed ?", 4000, 0);
|
|
|
|
|
|
|
|
closeSerial();
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
static char operationReadFuses(void) {
|
|
|
|
char* response;
|
|
|
|
char* buf = galbuffer;
|
|
|
|
int readSize;
|
|
|
|
|
|
|
|
if (openSerial() != 0) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
//Switch to upload mode to specify GAL
|
|
|
|
sprintf(buf, "u\r");
|
|
|
|
sendLine(buf, MAX_LINE, 100);
|
|
|
|
|
|
|
|
//set GAL type
|
|
|
|
sprintf(buf, "#t %i\r", (int) gal);
|
|
|
|
sendLine(buf, MAX_LINE, 100);
|
|
|
|
|
|
|
|
//Exit upload mode (ensure the texts are discarded by waiting 100 ms)
|
|
|
|
sprintf(buf, "#e\r");
|
|
|
|
sendLine(buf, MAX_LINE, 1000);
|
|
|
|
|
|
|
|
//READ_FUSE command
|
|
|
|
sprintf(buf, "r\r");
|
|
|
|
readSize = sendLine(buf, GALBUFSIZE, 5000);
|
|
|
|
if (readSize < 0) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
response = stripPrompt(buf);
|
|
|
|
printf("%s\n", response);
|
|
|
|
|
|
|
|
closeSerial();
|
|
|
|
|
|
|
|
if (response[0] == 'E' && response[1] == 'R') {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int main(int argc, char** argv) {
|
|
|
|
char result = 0;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
result = checkArgs(argc, argv);
|
|
|
|
if (result) {
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
if (verbose) {
|
|
|
|
printf("Afterburner " VERSION " \n");
|
|
|
|
}
|
|
|
|
|
2019-04-09 18:56:49 +00:00
|
|
|
result = operationSetGalCheck();
|
|
|
|
|
|
|
|
if (gal != UNKNOWN && 0 == result) {
|
|
|
|
result = operationSetGalType(gal);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (opErase && 0 == result) {
|
2019-03-16 10:53:19 +00:00
|
|
|
result = operationEraseGal();
|
|
|
|
}
|
|
|
|
|
|
|
|
if (0 == result) {
|
|
|
|
if (opWrite) {
|
|
|
|
// writing fuses and optionally verification
|
|
|
|
result = operationWriteOrVerify(1);
|
|
|
|
} else if (opInfo) {
|
|
|
|
result = operationReadInfo();
|
|
|
|
} else if (opRead) {
|
|
|
|
result = operationReadFuses();
|
|
|
|
} else if (opVerify) {
|
|
|
|
// verification without writing
|
|
|
|
result = operationWriteOrVerify(0);
|
|
|
|
} else if (opTestVPP) {
|
|
|
|
result = operationTestVpp();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (verbose) {
|
|
|
|
printf("result=%i\n", (char)result);
|
|
|
|
}
|
|
|
|
return result;
|
|
|
|
}
|