afterburner/src_pc/afterburner.c

1580 lines
45 KiB
C
Raw Normal View History

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:
* use 'git log'
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 -o afterburner afterburner.c
2019-03-16 10:53:19 +00:00
* 2024-02-02 Fixed: Command 'B9' (Calibration Offset = 0,25V) doesn't work
Note: Also requires elimination of a bug in the PC program afterburner.ino
Added: Sending B4, if b /wo -co is executed
*/
2019-03-16 10:53:19 +00:00
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <stdint.h>
2019-03-16 10:53:19 +00:00
#include "serial_port.h"
2024-03-17 21:09:20 +00:00
#define VERSION "v.0.5.8"
2019-03-16 10:53:19 +00:00
#ifdef GCOM
#define VERSION_EXTENDED VERSION "-" GCOM
#else
#define VERSION_EXTENDED VERSION
#endif
#define MAX_LINE (16*1024)
2019-03-16 10:53:19 +00:00
#define MAXFUSES 30000
#define GALBUFSIZE (256 * 1024)
#define JTAG_ID 0xFF
2019-03-16 10:53:19 +00:00
typedef enum {
UNKNOWN,
GAL16V8,
2023-09-17 04:55:40 +00:00
GAL18V10,
2019-03-16 10:53:19 +00:00
GAL20V8,
2023-11-22 03:48:09 +00:00
GAL20RA10,
2023-09-16 06:20:12 +00:00
GAL20XV10,
2019-03-16 10:53:19 +00:00
GAL22V10,
2023-10-30 13:10:28 +00:00
GAL26CV12,
GAL26V12,
2023-09-22 15:32:15 +00:00
GAL6001,
GAL6002,
2019-03-16 10:53:19 +00:00
ATF16V8B,
2023-10-30 14:13:25 +00:00
ATF20V8B,
2019-03-16 10:53:19 +00:00
ATF22V10B,
ATF22V10C,
ATF750C,
//jtag based PLDs at the end: they do not have a gal type in MCU software
ATF1502AS,
ATF1504AS,
2019-03-16 10:53:19 +00:00
} 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},
2023-09-17 04:55:40 +00:00
{GAL18V10, 0x50, 0x51, "GAL18V10", 3540, 20, 36, 96, 36, 3476, 8, 61, 60, 58, 10, 16, 20},
2019-03-16 10:53:19 +00:00
{GAL20V8, 0x20, 0x3A, "GAL20V8", 2706, 24, 40, 64, 40, 2568, 8, 63, 59, 58, 8, 60, 82},
2023-11-22 03:48:09 +00:00
{GAL20RA10, 0x60, 0x61, "GAL20RA10", 3274, 24, 40, 80, 40, 3210, 8, 61, 60, 58, 10, 16, 10},
2023-09-16 06:20:12 +00:00
{GAL20XV10, 0x65, 0x66, "GAL20XV10", 1671, 24, 40, 40, 44, 1631, 5, 61, 60, 58, 5, 16, 31},
2019-03-16 10:53:19 +00:00
{GAL22V10, 0x48, 0x49, "GAL22V10", 5892, 24, 44, 132, 44, 5828, 8, 61, 60, 58, 10, 16, 20},
2023-10-30 13:10:28 +00:00
{GAL26CV12, 0x58, 0x59, "GAL26CV12", 6432, 28, 52, 122, 52, 6368, 8, 61, 60, 58, 12, 16},
2023-10-30 13:25:08 +00:00
{GAL26V12, 0x5D, 0x5D, "GAL26V12", 7912, 28, 52, 150, 52, 7848, 8, 61, 60, 58, 12, 16},
2023-09-22 15:32:15 +00:00
{GAL6001, 0x40, 0x41, "GAL6001", 8294, 24, 78, 75, 97, 8222, 9, 63, 62, 96, 8, 8, 68},
{GAL6002, 0x44, 0x44, "GAL6002", 8330, 24, 78, 75, 97, 8258, 9, 63, 62, 96, 8, 8, 104},
2019-03-16 10:53:19 +00:00
{ATF16V8B, 0x00, 0x00, "ATF16V8B", 2194, 20, 32, 64, 32, 2056, 8, 63, 54, 58, 8, 60, 82},
2023-10-30 14:13:25 +00:00
{ATF20V8B, 0x00, 0x00, "ATF20V8B", 2706, 24, 40, 64, 40, 2568, 8, 63, 59, 58, 8, 60, 82},
2019-03-16 10:53:19 +00:00
{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},
{ATF750C, 0x00, 0x00, "ATF750C", 14499, 24, 84, 171, 84, 14435, 8, 61, 60, 127, 10, 16, 71},
{ATF1502AS, JTAG_ID, JTAG_ID, "ATF1502AS", 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 0, 0},
{ATF1504AS, JTAG_ID, JTAG_ID, "ATF1504AS", 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 0, 0},
2019-03-16 10:53:19 +00:00
};
char verbose = 0;
char* filename = 0;
char* deviceName = 0;
char* pesString = NULL;
2019-03-16 10:53:19 +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];
char noGalCheck = 0;
char varVppExists = 0;
char printSerialWhileWaiting = 0;
int calOffset = 0; //no calibration offset is applied
char enableSecurity = 0;
char bigRam = 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;
char opCalibrateVPP = 0;
char opMeasureVPP = 0;
char opSecureGal = 0;
char opWritePes = 0;
char flagEnableApd = 0;
char flagEraseAll = 0;
2019-03-16 10:53:19 +00:00
static int waitForSerialPrompt(char* buf, int bufSize, int maxDelay);
static char sendGenericCommand(const char* command, const char* errorText, int maxDelay, char printResult);
2019-03-16 10:53:19 +00:00
2023-09-16 06:20:12 +00:00
static void printGalTypes() {
2023-10-02 21:40:18 +00:00
int i;
for (i = 1; i < sizeof(galinfo) / sizeof(galinfo[0]); i++) {
if (i % 8 == 1) {
printf("\n\t");
} else
2023-09-16 06:20:12 +00:00
if (i > 1) {
printf(" ");
}
printf("%s", galinfo[i].name);
}
}
2019-03-16 10:53:19 +00:00
static void printHelp() {
printf("Afterburner " VERSION_EXTENDED " 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: ierwvsbm\n");
2019-03-16 10:53:19 +00:00
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 option must be set. Optionally '-all' can be set.\n");
printf(" p : write PES. -t and -pes options must be set. GAL must be erased with '-all' option.\n");
printf(" s : set VPP ON to check the programming voltage. Ensure the GAL is NOT inserted.\n");
printf(" b : calibrate variable VPP on new board designs. Ensure the GAL is NOT inserted.\n");
printf(" m : measure variable VPP on new board designs. Ensure the GAL is NOT inserted.\n");
printf("options:\n");
2019-03-16 10:53:19 +00:00
printf(" -v : verbose mode\n");
2023-09-16 06:20:12 +00:00
printf(" -t <gal_type> : the GAL type. use ");
printGalTypes();
printf("\n");
2019-03-16 10:53:19 +00:00
printf(" -f <file> : JEDEC fuse map file\n");
printf(" -d <serial_device> : name of the serial device. Without this option the device is guessed.\n");
printf(" serial params are: 57600, 8N1\n");
printf(" -nc : do not check device GAL type before operation: force the GAL type set on command line\n");
printf(" -sec: enable security - protect the chip. Use with 'w' or 'v' commands.\n");
printf(" -co <offset>: Set calibration offset. Use with 'b' command. Value: -20 (-0.2V) to 25 (+0.25V)\n");
printf(" -all: use with 'e' command to erase all data including PES.\n");
printf(" -pes <PES> : use with 'p' command to specify new PES. PES format is 8 hex bytes with a delimiter.\n");
printf(" For example 00:03:3A:A1:00:00:00:90\n");
2019-03-16 10:53:19 +00:00
printf("examples:\n");
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(" afterburner ep -t GAL20V8 -all -pes 00:03:3A:A1:00:00:00:90 Fully erases the GAL chip\n");
printf(" and writes new PES. Does not work with Atmel chips.\n");
2019-03-16 10:53:19 +00:00
printf("hints:\n");
printf(" - use the 'i' command first to check and set the right programming voltage (VPP)\n");
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, GAL20V8B, GAL22V10D: 12V \n");
2024-03-19 18:57:56 +00:00
printf(" Atmel ATF16V8B, ATF16V8C, ATF22V10C: 11V \n");
2019-03-16 10:53:19 +00:00
}
static int8_t verifyArgs(char* type) {
if (!opRead && !opWrite && !opErase && !opInfo && !opVerify && !opTestVPP && !opCalibrateVPP && !opMeasureVPP && !opWritePes) {
printHelp();
printf("Error: no command specified.\n");
return -1;
}
if (opWritePes && (NULL == pesString || strlen(pesString) != 23)) {
printf("Error: invalid or no PES specified.\n");
return -1;
}
if ((opRead || opWrite || opVerify) && opErase && flagEraseAll) {
printf("Error: invalid command combination. Use 'Erase all' in a separate step\n");
return -1;
}
if ((opRead || opWrite || opVerify) && (opTestVPP || opCalibrateVPP || opMeasureVPP)) {
printf("Error: VPP functions can not be conbined with read/write/verify operations\n");
return -1;
}
if (0 == type && (opWrite || opRead || opErase || opVerify || opInfo || opWritePes)) {
printf("Error: missing GAL type. Use -t <type> to specify.\n");
return -1;
} else if (0 != type) {
2023-10-02 21:40:18 +00:00
int i;
for (i = 1; i < sizeof(galinfo) / sizeof(galinfo[0]); i++) {
2023-09-16 06:20:12 +00:00
if (strcmp(type, galinfo[i].name) == 0) {
gal = galinfo[i].type;
break;
}
}
if (UNKNOWN == gal) {
2023-09-16 06:20:12 +00:00
printf("Error: unknown GAL type. Types: ");
printGalTypes();
printf("\n");
return -1;
}
}
if (0 == filename && (opWrite == 1 || opVerify == 1)) {
printf("Error: missing %s filename (param: -f fname)\n", galinfo[gal].id0 == JTAG_ID ? ".xsvf" : ".jed");
return -1;
}
return 0;
}
static int8_t checkArgs(int argc, char** argv) {
2019-03-16 10:53:19 +00:00
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];
} else if (strcmp("-nc", param) == 0) {
noGalCheck = 1;
} else if (strcmp("-sec", param) == 0) {
opSecureGal = 1;
} else if (strcmp("-all", param) == 0) {
flagEraseAll = 1;
} else if (strcmp("-pes", param) == 0) {
i++;
pesString = argv[i];
} else if (strcmp("-co", param) == 0) {
i++;
calOffset = atoi(argv[i]);
if (calOffset < -32 || calOffset > 32) {
printf("Calibration offset out of range (-32..32 inclusive).\n");
}
if (calOffset < -32) {
calOffset = -32;
} else if (calOffset > 32) {
calOffset = 32;
}
}
else if (param[0] != '-') {
2019-03-16 10:53:19 +00:00
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;
case 'b':
opCalibrateVPP = 1;
break;
case 'm':
opMeasureVPP = 1;
break;
case 'p':
opWritePes = 1;
break;
2019-03-16 10:53:19 +00:00
default:
printf("Error: unknown operation '%c' \n", modes[i]);
}
i++;
}
if (verifyArgs(type)) {
2019-03-16 10:53:19 +00:00
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) {
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))
2019-03-16 10:53:19 +00:00
&&
(pins == 0 ||
galinfo[i].pins == pins ||
(galinfo[i].pins == 24 && pins == 28))
2019-03-16 10:53:19 +00:00
) {
if (gal == 0) {
type = i;
break;
} else if (!type) {
type = i;
}
}
}
}
if (lastfuse == 2195 && gal == ATF16V8B) {
flagEnableApd = fusemap[2194];
if (verbose) {
printf("PD fuse detected: %i\n", fusemap[2194]);
}
}
if (lastfuse == 5893 && gal == ATF22V10C) {
flagEnableApd = fusemap[5892];
if (verbose) {
printf("PD fuse detected: %i\n", fusemap[5892]);
}
}
2019-03-16 10:53:19 +00:00
return n;
}
static char readFile(int* fileSize) {
2019-03-16 10:53:19 +00:00
FILE* f;
int size;
if (verbose) {
printf("opening file: '%s'\n", filename);
}
f = fopen(filename, "rb");
2019-03-16 10:53:19 +00:00
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;
}
if (fileSize != NULL) {
*fileSize = size;
}
2019-03-16 10:53:19 +00:00
return 0;
}
static char checkForString(char* buf, int start, const char* key) {
int labelPos = strstr(buf + start, key) - buf;
return (labelPos > 0 && labelPos < 500) ? 1 : 0;
}
2019-03-16 10:53:19 +00:00
static int openSerial(void) {
char buf[512] = {0};
char devName[256] = {0};
2019-03-16 10:53:19 +00:00
int total;
int labelPos;
//open device name
if (deviceName == 0) {
serialDeviceGuessName(&deviceName);
}
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);
}
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");
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;
bigRam = 0;
2019-03-16 10:53:19 +00:00
if (labelPos >= 0 && labelPos < 500 && buf[total - 3] == '>') {
// check for new board desgin: variable VPP
varVppExists = checkForString(buf, labelPos, " varVpp ");
if (verbose && varVppExists) {
printf("variable VPP board detected\n");
}
// check for Big Ram
bigRam = checkForString(buf, labelPos, " RAM-BIG");
if (verbose & bigRam) {
printf("MCU Big RAM detected\n");
}
2019-03-16 10:53:19 +00:00
//all OK
return 0;
}
if (verbose) {
printf("Output from programmer not recognised: %s\n", buf);
}
serialDeviceClose(serialF);
serialF = INVALID_HANDLE;
2019-03-16 10:53:19 +00:00
return -4;
}
static void closeSerial(void) {
if (INVALID_HANDLE == serialF) {
2019-03-16 10:53:19 +00:00
return;
}
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 - 2 && buf[i] != 0; i++) {
if (buf[i] == '>' && buf[i+1] == '\r' && buf[i+2] == '\n') {
2019-03-16 10:53:19 +00:00
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;
}
//strip rear new line characters
2019-03-16 10:53:19 +00:00
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;
}
//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;
}
static char* printBuffer(char* bufPrint, int readSize) {
int i;
char doPrint = 1;
for (i = 0; i < readSize;i++) {
if (*bufPrint == '>') {
doPrint = 0;
}
if (doPrint) {
printf("%c", *bufPrint);
if (*bufPrint == '\n' || *bufPrint == '\r') {
fflush(stdout);
}
bufPrint++;
}
}
return bufPrint;
}
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;
char* bufPrint = buf;
char doPrint = printSerialWhileWaiting;
2019-03-16 10:53:19 +00:00
memset(buf, 0, bufSize);
while (maxDelay > 0) {
readSize = serialDeviceRead(serialF, buf, bufSize);
2019-03-16 10:53:19 +00:00
if (readSize > 0) {
bufPos += readSize;
if (checkPromptExists(bufStart, bufTotal) >= 0) {
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 (bufSize <= 0) {
printf("ERROR: serial port read buffer is too small!\nAre you dumping large amount of data?\n");
return -1;
}
2019-03-16 10:53:19 +00:00
}
if (printSerialWhileWaiting) {
bufPrint = printBuffer(bufPrint, readSize);
}
2019-03-16 10:53:19 +00:00
}
if (maxDelay > 0) {
/* WIN_API handles timeout itself */
#ifndef _USE_WIN_API_
usleep(10 * 1000);
maxDelay -= 10;
#else
maxDelay -= 30;
#endif
if(maxDelay <= 0 && verbose) {
printf("waitForSerialPrompt timed out\n");
}
2019-03-16 10:53:19 +00:00
}
}
return bufPos;
}
static int sendBuffer(char* buf) {
2019-03-16 10:53:19 +00:00
int total;
int writeSize;
if (buf == 0) {
return -1;
}
total = strlen(buf);
// write the query into the serial port's file
2019-03-16 10:53:19 +00:00
// file is opened non blocking so we have to ensure all contents is written
while (total > 0) {
writeSize = serialDeviceWrite(serialF, buf, total);
2019-03-16 10:53:19 +00:00
if (writeSize < 0) {
printf("ERROR: written: %i (%s)\n", writeSize, strerror(errno));
2019-03-16 10:53:19 +00:00
return -4;
}
buf += writeSize;
total -= writeSize;
}
return 0;
}
static int sendLine(char* buf, int bufSize, int maxDelay) {
int total;
char* obuf = buf;
if (serialF == INVALID_HANDLE) {
return -1;
}
total = sendBuffer(buf);
if (total) {
return total;
}
2019-03-16 10:53:19 +00:00
total = waitForSerialPrompt(obuf, bufSize, (maxDelay < 0) ? 6 : maxDelay);
if (total < 0) {
return total;
}
2019-03-16 10:53:19 +00:00
obuf[total] = 0;
obuf = stripPrompt(obuf);
if (verbose) {
printf("read: %i '%s'\n", total, obuf);
}
return total;
}
static void updateProgressBar(char* label, int current, int total) {
int done = ((current + 1) * 40) / total;
if (current >= total) {
printf("%s%5d/%5d |########################################|\n", label, total, total);
} else {
printf("%s%5d/%5d |", label, current, total);
printf("%.*s%*s|\r", done, "########################################", 40 - done, "");
fflush(stdout); //flush the text out so that the animation of the progress bar looks smooth
}
}
2019-03-16 10:53:19 +00:00
// 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];
unsigned int i, j, n;
unsigned short csum;
int apdFuse = flagEnableApd;
int totalFuses = galinfo[gal].fuses;
2019-03-16 10:53:19 +00:00
if (apdFuse) {
totalFuses++;
2019-03-16 10:53:19 +00:00
}
// Start upload
sprintf(buf, "u\r");
sendLine(buf, MAX_LINE, 20);
//device type
2023-11-04 14:31:56 +00:00
sprintf(buf, "#t %c %s\r", '0' + (int) gal, galinfo[gal].name);
2019-03-16 10:53:19 +00:00
sendLine(buf, MAX_LINE, 300);
//fuse map
buf[0] = 0;
fuseSet = 0;
2023-10-01 16:03:49 +00:00
printf("Uploading fuse map...\n");
for (i = 0; i < totalFuses;) {
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) {
#ifdef DEBUG_UPLOAD
printf("%s\n", buf);
#endif
2019-03-16 10:53:19 +00:00
sendLine(buf, MAX_LINE, 100);
buf[0] = 0;
2019-03-16 10:53:19 +00:00
}
fuseSet = 0;
}
sprintf(buf, "#f %04i ", i);
2019-03-16 10:53:19 +00:00
}
f = 0;
for (j = 0; j < 8 && i < totalFuses; 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);
2023-10-01 16:03:49 +00:00
updateProgressBar("", i, totalFuses);
2019-03-16 10:53:19 +00:00
}
updateProgressBar("", totalFuses, totalFuses);
2019-03-16 10:53:19 +00:00
// send last unfinished fuse line
2023-11-21 08:33:31 +00:00
if (fuseSet) {
2019-03-16 10:53:19 +00:00
strcat(buf, "\r");
#ifdef DEBUG_UPLOAD
printf("%s\n", buf);
#endif
2019-03-16 10:53:19 +00:00
sendLine(buf, MAX_LINE, 100);
}
//checksum
csum = checkSum(totalFuses);
2019-03-16 10:53:19 +00:00
if (verbose) {
printf("sending csum: %04X\n", csum);
2019-03-16 10:53:19 +00:00
}
sprintf(buf, "#c %04X\r", csum);
2019-03-16 10:53:19 +00:00
sendLine(buf, MAX_LINE, 300);
//end of upload
return sendGenericCommand("#e\r", "Upload failed", 300, 0);
2019-03-16 10:53:19 +00:00
}
//returns 0 on success
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);
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 && printSerialWhileWaiting == 0) {
2019-03-16 10:53:19 +00:00
printf("%s\n", response);
}
}
return 0;
}
static char operationWriteOrVerify(char doWrite) {
char buf[MAX_LINE];
int readSize;
char result;
if (readFile(NULL)) {
2019-03-16 10:53:19 +00:00
return -1;
}
result = parseFuseMap(galbuffer);
if (verbose) {
printf("parse result=%i\n", result);
}
if (openSerial() != 0) {
return -1;
}
// set power-down fuse bit (do it before upload to correctly calculate check-sum)
result = sendGenericCommand(flagEnableApd ? "z\r" : "Z\r", "APD set failed ?", 4000, 0);
if (result) {
goto finish;
}
2019-03-16 10:53:19 +00:00
result = upload();
if (result) {
return result;
}
// write command
if (doWrite) {
result = sendGenericCommand("w\r", "write failed ?", 8000, 0);
2019-03-16 10:53:19 +00:00
if (result) {
goto finish;
}
}
// verify command
if (opVerify) {
result = sendGenericCommand("v\r", "verify failed ?", 8000, 0);
2019-03-16 10:53:19 +00:00
}
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");
}
if (varVppExists) {
printf("Turn the Pot on the MT3608 module to set the VPP to 16.5V (+/- 0.05V)\n");
} else {
printf("Turn the Pot on the MT3608 module to check / set the VPP\n");
}
//print the measured voltages if the feature is available
printSerialWhileWaiting = 1;
2019-03-16 10:53:19 +00:00
//Voltage testing takes ~20 seconds
result = sendGenericCommand("t\r", "info failed ?", 22000, 1);
printSerialWhileWaiting = 0;
2019-03-16 10:53:19 +00:00
closeSerial();
return result;
}
static char operationCalibrateVpp(void) {
char result;
char cmd [8] = {0};
char val = (char)('0' + (calOffset + 32));
if (openSerial() != 0) {
return -1;
}
sprintf(cmd, "B%c\r", val);
if (verbose) {
printf("sending 'B%c' command...\n", val);
}
result = sendGenericCommand(cmd, "VPP cal. offset failed", 4000, 1);
if (verbose) {
printf("sending 'b' command...\n");
}
printf("VPP voltages are scanned - this might take a while...\n");
printSerialWhileWaiting = 1;
result = sendGenericCommand("b\r", "VPP calibration failed", 34000, 1);
printSerialWhileWaiting = 0;
closeSerial();
return result;
}
static char operationMeasureVpp(void) {
char result;
if (openSerial() != 0) {
return -1;
}
if (verbose) {
printf("sending 'm' command...\n");
}
//print the measured voltages if the feature is available
printSerialWhileWaiting = 1;
result = sendGenericCommand("m\r", "VPP measurement failed", 40000, 1);
printSerialWhileWaiting = 0;
closeSerial();
return result;
}
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;
}
if (verbose) {
printf("sending 'g' command type=%i\n", type);
}
sprintf(buf, "g%c\r", '0' + (int)type);
result = sendGenericCommand(buf, "setGalType failed ?", 4000, 0);
closeSerial();
return result;
}
static char operationSecureGal() {
int readSize;
char result;
if (openSerial() != 0) {
return -1;
}
if (verbose) {
printf("sending 's' command...\n");
}
result = sendGenericCommand("s\r", "secure GAL failed ?", 4000, 0);
closeSerial();
return result;
}
static char operationWritePes(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 %c\r", '0' + (int) gal);
sendLine(buf, MAX_LINE, 300);
//set new PES
sprintf(buf, "#p %s\r", pesString);
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);
if (verbose) {
printf("sending 'P' command...\n");
}
result = sendGenericCommand("P\r", "write PES 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 %c\r", '0' + (int) gal);
2019-03-16 10:53:19 +00:00
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);
if (flagEraseAll) {
result = sendGenericCommand("~\r", "erase all failed ?", 4000, 0);
} else {
result = sendGenericCommand("c\r", "erase failed ?", 4000, 0);
}
2019-03-16 10:53:19 +00:00
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 %c\r", '0' + (int) gal);
2019-03-16 10:53:19 +00:00
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, 12000);
2019-03-16 10:53:19 +00:00
if (readSize < 0) {
return -1;
}
response = stripPrompt(buf);
printf("%s\n", response);
closeSerial();
if (response[0] == 'E' && response[1] == 'R') {
return -1;
}
return 0;
}
static int readJtagSerialLine(char* buf, int bufSize, int maxDelay, int* feedRequest) {
char* bufStart = buf;
int readSize;
int bufPos = 0;
memset(buf, 0, bufSize);
while (maxDelay > 0) {
readSize = serialDeviceRead(serialF, buf, 1);
if (readSize > 0) {
bufPos += readSize;
buf[1] = 0;
//handle the feed request
if (buf[0] == '$') {
char tmp[5];
bufPos -= readSize;
buf[0] = 0;
//extra 5 bytes should be present: 3 bytes of size, 2 new line chars
readSize = serialDeviceRead(serialF, tmp, 5);
if (readSize == 5) {
tmp[3] = 0;
*feedRequest = atoi(tmp);
maxDelay = 0; //force exit
} else {
printf("Warning: corrupted feed request!\n");
}
//printf("***\n");
} else
if (buf[0] == '\r') {
readSize = serialDeviceRead(serialF, buf, 1); // read \n coming from Arduino
//printf("-%c-\n", buf[0] == '\n' ? 'n' : 'r');
buf[0] = 0;
bufPos++;
maxDelay = 0; //force exit
} else {
//printf("(0x%02x %d) \n", buf[0], (int) buf[0]);
buf += readSize;
if (bufPos == bufSize) {
printf("ERROR: serial port read buffer is too small!\nAre you dumping large amount of data?\n");
return -1;
}
}
}
if (maxDelay > 0) {
/* WIN_API handles timeout itself */
#ifndef _USE_WIN_API_
usleep(1 * 1000);
maxDelay -= 10;
#else
maxDelay -= 30;
#endif
}
}
return bufPos;
}
static int playJtagFile(char* label, int fSize, int vpp, int showProgress) {
char buf[MAX_LINE] = {0};
int sendPos = 0;
char ready = 0;
int result = 0;
unsigned int csum = 0;
int feedRequest = 0;
// support for XCOMMENT messages which might be interrupted by a feed request
int continuePrinting = 0;
if (openSerial() != 0) {
return -1;
}
//compute check sum
if (verbose) {
int i;
for (i = 0; i < fSize; i++) {
csum += (unsigned char) galbuffer[i];
}
}
// send start-JTAG-player command
sprintf(buf, "j%d\r", vpp ? 1: 0);
sendBuffer(buf);
// read response from MCU and feed the XSVF player with data
while(1) {
int readBytes;
feedRequest = 0;
buf[0] = 0;
readBytes = readJtagSerialLine(buf, MAX_LINE, 3000, &feedRequest);
//printf(">> read %d len=%d cp=%d '%s'\n", readBytes, (int) strlen(buf), continuePrinting, buf);
//request to send more data was received
if (feedRequest > 0) {
if (ready) {
int chunkSize = fSize - sendPos;
if (chunkSize > feedRequest) {
chunkSize = feedRequest;
// make the initial chunk big so the data are buffered by the OS
if (sendPos == 0) {
chunkSize *= 4;
if (chunkSize > fSize) {
chunkSize = fSize;
}
}
}
if (chunkSize > 0) {
// send the data over serial line
int w = serialDeviceWrite(serialF, galbuffer + sendPos, chunkSize);
sendPos += w;
// print progress / file position
if (showProgress && (sendPos % 1024 == 0 || sendPos == fSize)) {
updateProgressBar(label, sendPos, fSize);
}
}
}
if (readBytes > 2) {
continuePrinting = 1;
}
}
// when the feed request was detected, there might be still some data in the buffer
if (buf[0] != 0) {
//prevous line had a feed request - this is a continuation
if (feedRequest == 0 && continuePrinting) {
continuePrinting = 0;
printf("%s\n", buf);
} else
//print debug messages
if (buf[0] == 'D') {
if (feedRequest) { // the rest of the message will follow
printf("%s", buf + 1);
} else {
printf("%s\n", buf + 1);
}
}
// quit
if (buf[0] == 'Q') {
result = atoi(buf + 1);
//print error result
if (result != 0) {
printf("%s\n", buf + 1);
} else
// when all is OK and verbose mode is on, then print the checksum for comparison
if (verbose) {
printf("PC : 0x%08X\n", csum);
}
break;
} else
// ready to receive anouncement
if (strcmp("RXSVF", buf) == 0) {
ready = 1;
} else
// print important messages
if (buf[0] == '!') {
// in verbose mode print all messages, otherwise print only success or fail messages
if (verbose || 0 == strcmp("!Success", buf) || 0 == strcmp("!Fail", buf)) {
printf("%s\n", buf + 1);
}
}
#if 0
//print all the rest
else if (verbose) {
printf("'%s'\n", buf);
}
#endif
} else
// the buffer is empty but there was a feed request just before - print a new line
if (readBytes > 0 && continuePrinting) {
printf("\n");
continuePrinting = 0;
}
}
readJtagSerialLine(buf, MAX_LINE, 1000, &feedRequest);
return result;
}
static int processJtagInfo(void) {
int result;
int fSize = 0;
char tmp[256];
if (!opInfo) {
return 0;
}
if (!(gal == ATF1502AS || gal == ATF1504AS)) {
printf("error: infor command is unsupported");
return 1;
}
// Use default .xsvf file for erase if no file is provided.
// if the file is provided while write operation is also requested
// then the file is specified for writing -> do not use it for erasing
sprintf(tmp, "xsvf/id_ATF150X.xsvf");
filename = tmp;
result = readFile(&fSize);
if (result) {
return result;
}
//play the info file and use high VPP
return playJtagFile("", fSize, 1, 0);
}
static int processJtagErase(void) {
int result;
int fSize = 0;
char tmp[256];
char* originalFname = filename;
if (!opErase) {
return 0;
}
// Use default .xsvf file for erase.
sprintf(tmp, "xsvf/erase_%s.xsvf", galinfo[gal].name);
filename = tmp;
result = readFile(&fSize);
if (result) {
filename = originalFname;
return result;
}
filename = originalFname;
//play the erase file and use high VPP
return playJtagFile("erase ", fSize, 1, 1);
}
static int processJtagWrite(void) {
int result;
int fSize = 0;
if (!opWrite) {
return 0;
}
// paranoid: this condition should be already checked during argument's check
if (0 == filename) {
return -1;
}
result = readFile(&fSize);
if (result) {
return result;
}
//play the file and use low VPP
return playJtagFile("write ", fSize, 0, 1);
}
static int processJtag(void) {
int result;
if (verbose) {
printf("JTAG\n");
}
if ((gal == ATF1502AS || gal == ATF1504AS) && (opRead || opVerify)) {
printf("error: read and verify operation is not supported\n");
return 1;
}
result = processJtagInfo();
if (result) {
return result;
}
result = processJtagErase();
if (result) {
return result;
}
result = processJtagWrite();
if (result) {
return result;
}
return 0;
}
2019-03-16 10:53:19 +00:00
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");
}
// process JTAG operations
if (gal != 0 && galinfo[gal].id0 == JTAG_ID && galinfo[gal].id1 == JTAG_ID) {
result = processJtag();
goto finish;
}
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();
} else if (opWritePes) {
result = operationWritePes();
2019-03-16 10:53:19 +00:00
}
if (0 == result && (opWrite || opVerify)) {
if (opSecureGal) {
operationSecureGal();
}
}
//variable VPP functions (for new board designs)
if (varVppExists) {
if (0 == result && opCalibrateVPP) {
result = operationCalibrateVpp();
}
if (0 == result && opMeasureVPP) {
result = operationMeasureVpp();
}
}
2019-03-16 10:53:19 +00:00
}
finish:
2019-03-16 10:53:19 +00:00
if (verbose) {
printf("result=%i\n", (char)result);
}
return result;
}