/* * 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; }