/*
* JR-IDE Project
* - (c) 2017 Alan Hightower
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "machxo2_internal.h"
#define STX 0x02
#define ETX 0x03
#define LOGERR(fmt, ...) fprintf(stderr, "\nERROR: " fmt "\n\n", ## __VA_ARGS__)
enum
{
COMMENT,
FUSE_CHECKSUM,
FUSE_DATA,
END_DATA,
FUSE_LIST,
SECURITY_FUSE,
FUSE_DEFAULT,
FUSE_SIZE,
USER_CODE,
FEATURE_ROW,
DONE
};
static struct _st_machxo2_ctx
{
machxo2_hdr_t hdr;
uint8_t *page;
int page_count;
int page_total;
} ctx;
uint32_t _binfield (char *p)
{
uint32_t val = 0, i = 0;
while ((*p != '\0') && (i < 32))
{
val = (val<<1) | (*p - '0');
++i;
}
return val;
}
static void removeLastStar (char *p)
{
int i = 0;
while (*p != '\0')
{
++i;
++p;
}
if (i == 0)
return; // empty string
while ((*p != '*') && i)
{
--p;
--i;
}
if (i)
*p = '\0'; // replace last '*' with NULL, end line at that spot
}
static int page_count = 0;
static int fuse_offset = 0;
static int fuse_state = 0;
static int fuse_total = 0;
static void _fuse2hex (char *p)
{
uint8_t val;
int i, j;
for (i = 0; i < XO2_PAGE_SIZE; i++)
{
val = 0;
for (j = 0; j < 8; j++, p++)
val = (val << 1) | (*p - '0');
ctx.page[ctx.page_count++] = val;
}
page_count++;
return;
}
static uint8_t *_hexrow (FILE *handle, uint8_t *ptr, int len)
{
int i;
for (i = 0; i < len; i++, ptr++)
fprintf (handle, " 0x%02x,", *ptr);
fprintf (handle, "\n");
return ptr;
}
/**
* Convert the E Field (Feature Row) into a list of C byte values in an array.
* This function is specific to the MachXO2 Feature Row format.
* The number of bytes in a row is passed in.
* the bits are actually in reverse order so we need to work backwards through
* the string.
*
* JEDEC file format is lsb first so shift into byte from top down.
*/
static void convertFeatureRowToHexArray (char *p, uint8_t *out, int cnt)
{
uint8_t val;
int i, j;
// start at last char in string and work backwards
p = p + ((8 * cnt) - 1);
for (i = 0; i < cnt; i++)
{
val = 0;
for (j = 0; j < 8; j++)
{
val = (val<<1) | (*p - '0');
--p;
}
(*out++) = val;
}
return;
}
MachXO2_DEVICES _lookup_type (char *text)
{
if (strstr (text, "LCMXO2-256") != NULL)
return MachXO2_256;
if (strstr (text, "LCMXO2-640") != NULL)
return MachXO2_640;
if (strstr (text, "LCMXO2-1200") != NULL)
return MachXO2_1200;
if (strstr (text, "LCMXO2-2000") != NULL)
return MachXO2_2000;
if (strstr (text, "LCMXO2-4000") != NULL)
return MachXO2_4000;
if (strstr (text, "LCMXO2-7000") != NULL)
return MachXO2_7000;
return MachXO2_1200;
}
int main (int argc, char *argv[])
{
char *output = NULL;
char *filename = NULL;
FILE *in_handle;
FILE *out_handle;
int ascii = 0;
argc--; argv++;
while (argc)
{
if (strcmp (argv[0], "-o") == 0)
{
argc--; argv++;
output = strdup (argv[0]);
}
else if (strcmp (argv[0], "-a") == 0)
{
ascii = 1;
}
else if ((strcmp (argv[0], "-?") == 0) ||
(strcmp (argv[0], "--help") == 0))
{
printf ("\nUsage: jed2jpf [-o output] [input]\n\n");
return 0;
}
else
{
if (filename == NULL)
filename = strdup (argv[0]);
else
LOGERR("Unknown argument '%s'", argv[0]);
}
argc--; argv++;
}
if (filename)
{
in_handle = fopen (filename, "r");
if (in_handle == NULL)
{
LOGERR ("Unable to open input file '%s' - %s",
filename, strerror (errno));
return -1;
}
}
else
{
in_handle = stdin;
}
if (output)
{
out_handle = fopen (output, "w");
if (out_handle == NULL)
{
LOGERR ("Unable to open output file '%s' - %s",
output, strerror (errno));
return -1;
}
}
else
{
out_handle = stdout;
}
memset (&ctx, 0, sizeof (ctx));
ctx.hdr.magic = MACHXO2_IMAGE_HDR_MAGIC;
char line[1024];
fgets (line, sizeof (line) - 1, in_handle);
if (line[0] != STX)
{
printf("ERROR! Expected STX as first char!\nAborting.\n");
fclose (in_handle);
fclose (out_handle);
return -2;
}
int state = COMMENT, sts = 0, done = 0;
const xo2_info_t *info = NULL;
while (!feof (in_handle) && !sts && !done)
{
if (fgets (line, sizeof (line) - 1, in_handle) != NULL)
{
removeLastStar (line);
if ((line[0] == '0') || (line[0] == '1'))
state = FUSE_DATA;
else if (strncmp("NOTE", line, 4) == 0)
state = COMMENT;
else if (line[0] == 'G')
state = SECURITY_FUSE;
else if (line[0] == 'L')
state = FUSE_LIST;
else if (line[0] == 'C')
state = FUSE_CHECKSUM;
else if (line[0] == '*')
state = END_DATA;
else if (line[0] == 'D')
state = FUSE_DEFAULT;
else if (line[0] == 'U')
state = USER_CODE;
else if (line[0] == 'E')
state = FEATURE_ROW;
else if (strncmp("QF", line, 2) == 0)
state = FUSE_SIZE;
else if (line[0] == ETX)
state = DONE;
switch (state)
{
case FUSE_DATA:
if (page_count < ctx.page_total)
_fuse2hex (line);
break;
case FUSE_LIST:
{
int offset = -1;
sscanf (&line[1], "%d", &offset);
offset = offset / (8 * XO2_PAGE_SIZE);
switch (fuse_state)
{
case 0: // should be zero
if (offset != 0)
{
LOGERR("Starting offset != 0 (%d)", offset);
sts = -1;
}
break;
case 1:
break;
case 2:
break;
}
fuse_state++;
fuse_offset = offset;
break;
}
case SECURITY_FUSE:
// sscanf (&line[1], "%d", &ctx.hdr.security_fuse);
break;
case FUSE_DEFAULT:
// sscanf (&line[1], "%d", &ctx.hdr.erase_val);
break;
case FUSE_SIZE:
sscanf (&line[2], "%d", &fuse_total);
fuse_total = fuse_total / (8 * XO2_PAGE_SIZE);
#if 0
if (fuse_total > ctx.page_total)
{
LOGERR("Page count mismatch (%d/%d)", fuse_total, ctx.page_total);
sts = -1;
}
#endif
break;
case USER_CODE:
if (line[1] == 'H')
sscanf (&line[2], "%x", &ctx.hdr.user_code);
else
ctx.hdr.user_code = _binfield (&line[1]);
break;
case FEATURE_ROW:
// 2 consectutive rows. 1st starts with E and is 64 bits. 2nd line is 16 bits, ends in *
convertFeatureRowToHexArray (&line[1], ctx.hdr.feature_row, 8);
fgets (line, sizeof (line), in_handle);
removeLastStar (line);
convertFeatureRowToHexArray (&line[0], ctx.hdr.feature_bits, 2);
break;
case DONE:
done = 1;
break;
case FUSE_CHECKSUM:
case COMMENT:
case END_DATA:
default:
// do nothing
break;
}
// Look for specific XO2 Device type and extract
if ((state == COMMENT) && (strncmp("DEVICE NAME:", &line[5], 12) == 0))
{
MachXO2_DEVICES dev_id = _lookup_type (&line[17]);
info = &xo2_device_info[dev_id];
ctx.page_total = info->pages_cfg + info->pages_ufm;
ctx.page = (uint8_t *) calloc (ctx.page_total, XO2_PAGE_SIZE);
// defaults
ctx.hdr.device_id = info->hc_id;
ctx.hdr.cfg_pages = info->pages_cfg;
ctx.hdr.ufm_pages = info->pages_ufm;
}
}
}
fclose (in_handle);
if (sts == 0)
{
int page;
// Find last non-zero config page
for (page = info->pages_cfg - 1; page >= 0; page--)
{
uint8_t *ptr = (uint8_t *) ctx.page + (page * XO2_PAGE_SIZE);
uint8_t agg = 0;
int i;
for (i = 0; i < XO2_PAGE_SIZE; i++)
agg |= ptr[i];
if (agg)
{
page++;
break;
}
}
ctx.hdr.cfg_pages = page;
// Find last non-zero UFM page
for (page = ctx.page_total - 1; page >= info->pages_cfg; page--)
{
uint8_t *ptr = (uint8_t *) ctx.page + (page * XO2_PAGE_SIZE);
uint8_t agg = 0;
int i;
for (i = 0; i < XO2_PAGE_SIZE; i++)
agg |= ptr[i];
if (agg)
{
page++;
break;
}
}
ctx.hdr.ufm_pages = (page >= info->pages_cfg) ? page - info->pages_cfg : 0;
printf (" -JPF- -> Config pages %d / UFM pages %d\n",
ctx.hdr.cfg_pages, ctx.hdr.ufm_pages);
printf (" -JPF- -> Usercode 0x%08x\n",
ctx.hdr.user_code);
if (ascii)
{
fprintf (out_handle, "\n#include \n");
fprintf (out_handle, "\nconst uint8_t _binary_firmware_jpf_start[] = {\n\n");
uint8_t *ptr = (uint8_t *) &ctx.hdr;
fprintf (out_handle, "\t/* magic = 0x%08x */ ", ctx.hdr.magic);
ptr = _hexrow (out_handle, ptr, 4);
fprintf (out_handle, "\t/* device id = 0x%08x */ ", ctx.hdr.device_id);
ptr = _hexrow (out_handle, ptr, 4);
fprintf (out_handle, "\t/* user code = 0x%08x */ ", ctx.hdr.user_code);
ptr = _hexrow (out_handle, ptr, 4);
fprintf (out_handle, "\n\t/* cfg pages = %-5d */ ", ctx.hdr.cfg_pages);
ptr = _hexrow (out_handle, ptr, 4);
fprintf (out_handle, "\t/* ufm pages = %-5d */ ", ctx.hdr.ufm_pages);
ptr = _hexrow (out_handle, ptr, 4);
fprintf (out_handle, "\n\t/* feature row */ ");
ptr = _hexrow (out_handle, ptr, 8);
fprintf (out_handle, "\t/* feature bits */ ");
ptr = _hexrow (out_handle, ptr, 2);
fprintf (out_handle, "\t/* reserved */ ");
ptr = _hexrow (out_handle, ptr, 2);
fprintf (out_handle, "\n");
ptr = (uint8_t *) ctx.page;
for (page = 0; page < (int) ctx.hdr.cfg_pages; page++)
{
fprintf (out_handle, "\t/* cfg page %-5d */ ", page);
ptr = _hexrow (out_handle, ptr, XO2_PAGE_SIZE);
}
if (ctx.hdr.ufm_pages)
{
fprintf (out_handle, "\n");
ptr = (uint8_t *) ctx.page;
ptr += info->pages_cfg * XO2_PAGE_SIZE;
for (page = 0; page < (int) ctx.hdr.ufm_pages; page++)
{
fprintf (out_handle, "\t/* ufm page %-5d */ ", page);
ptr = _hexrow (out_handle, ptr, XO2_PAGE_SIZE);
}
}
fprintf (out_handle, "};\n");
}
else
{
fwrite (&ctx.hdr, sizeof (ctx.hdr), 1, out_handle);
uint8_t *ptr = (uint8_t *) ctx.page;
fwrite (ptr, ctx.hdr.cfg_pages * XO2_PAGE_SIZE, 1, out_handle);
if (ctx.hdr.ufm_pages)
{
ptr += info->pages_cfg * XO2_PAGE_SIZE;
fwrite (ptr, ctx.hdr.ufm_pages * XO2_PAGE_SIZE, 1, out_handle);
}
}
}
fclose (out_handle);
return 0;
}