/* * iceprog -- simple programming tool for FTDI-based Lattice iCE programmers * * Copyright (C) 2015 Clifford Wolf * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * Relevant Documents: * ------------------- * http://www.latticesemi.com/~/media/Documents/UserManuals/EI/icestickusermanual.pdf * http://www.micron.com/~/media/documents/products/data-sheet/nor-flash/serial-nor/n25q/n25q_32mb_3v_65nm.pdf * http://www.ftdichip.com/Support/Documents/AppNotes/AN_108_Command_Processor_for_MPSSE_and_MCU_Host_Bus_Emulation_Modes.pdf */ #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include struct ftdi_context ftdic; bool ftdic_open = false; bool verbose = false; void check_rx() { while (1) { uint8_t data; int rc = ftdi_read_data(&ftdic, &data, 1); if (rc <= 0) break; fprintf(stderr, "unexpected rx byte: %02X\n", data); } } void error() { check_rx(); fprintf(stderr, "ABORT.\n"); if (ftdic_open) ftdi_usb_close(&ftdic); ftdi_deinit(&ftdic); exit(1); } uint8_t recv_byte() { uint8_t data; while (1) { int rc = ftdi_read_data(&ftdic, &data, 1); if (rc < 0) { fprintf(stderr, "Read error.\n"); error(); } if (rc == 1) break; usleep(100); } return data; } void send_byte(uint8_t data) { int rc = ftdi_write_data(&ftdic, &data, 1); if (rc != 1) { fprintf(stderr, "Write error (single byte, rc=%d, expected %d).\n", rc, 1); error(); } } void send_spi(uint8_t *data, int n) { if (n < 1) return; send_byte(0x11); send_byte(n-1); send_byte((n-1) >> 8); int rc = ftdi_write_data(&ftdic, data, n); if (rc != n) { fprintf(stderr, "Write error (chunk, rc=%d, expected %d).\n", rc, n); error(); } } void xfer_spi(uint8_t *data, int n) { if (n < 1) return; send_byte(0x31); send_byte(n-1); send_byte((n-1) >> 8); int rc = ftdi_write_data(&ftdic, data, n); if (rc != n) { fprintf(stderr, "Write error (chunk, rc=%d, expected %d).\n", rc, n); error(); } for (int i = 0; i < n; i++) data[i] = recv_byte(); } void set_gpio(int slavesel_b, int creset_b) { uint8_t gpio = 1; if (slavesel_b) { // ADBUS4 (GPIOL0) gpio |= 0x10; } if (creset_b) { // ADBUS7 (GPIOL3) gpio |= 0x80; } send_byte(0x80); send_byte(gpio); send_byte(0x93); } int get_cdone() { uint8_t data; send_byte(0x81); data = recv_byte(); // ADBUS6 (GPIOL2) return (data & 0x40) != 0; } void flash_read_id() { // fprintf(stderr, "read flash ID..\n"); uint8_t data[21] = { 0x9F }; set_gpio(0, 0); xfer_spi(data, 21); set_gpio(1, 0); fprintf(stderr, "flash ID:"); for (int i = 1; i < 21; i++) fprintf(stderr, " 0x%02X", data[i]); fprintf(stderr, "\n"); } void flash_power_up() { uint8_t data[1] = { 0xAB }; set_gpio(0, 0); xfer_spi(data, 1); set_gpio(1, 0); } void flash_power_down() { uint8_t data[1] = { 0xB9 }; set_gpio(0, 0); xfer_spi(data, 1); set_gpio(1, 0); } void flash_write_enable() { if (verbose) fprintf(stderr, "write enable..\n"); uint8_t data[1] = { 0x06 }; set_gpio(0, 0); xfer_spi(data, 1); set_gpio(1, 0); } void flash_bulk_erase() { fprintf(stderr, "bulk erase..\n"); uint8_t data[1] = { 0xc7 }; set_gpio(0, 0); xfer_spi(data, 1); set_gpio(1, 0); } void flash_64kB_sector_erase(int addr) { fprintf(stderr, "erase 64kB sector at 0x%06X..\n", addr); uint8_t command[4] = { 0xd8, (uint8_t)(addr >> 16), (uint8_t)(addr >> 8), (uint8_t)addr }; set_gpio(0, 0); send_spi(command, 4); set_gpio(1, 0); } void flash_prog(int addr, uint8_t *data, int n) { if (verbose) fprintf(stderr, "prog 0x%06X +0x%03X..\n", addr, n); uint8_t command[4] = { 0x02, (uint8_t)(addr >> 16), (uint8_t)(addr >> 8), (uint8_t)addr }; set_gpio(0, 0); send_spi(command, 4); send_spi(data, n); set_gpio(1, 0); if (verbose) for (int i = 0; i < n; i++) fprintf(stderr, "%02x%c", data[i], i == n-1 || i % 32 == 31 ? '\n' : ' '); } void flash_read(int addr, uint8_t *data, int n) { if (verbose) fprintf(stderr, "read 0x%06X +0x%03X..\n", addr, n); uint8_t command[4] = { 0x03, (uint8_t)(addr >> 16), (uint8_t)(addr >> 8), (uint8_t)addr }; set_gpio(0, 0); send_spi(command, 4); memset(data, 0, n); xfer_spi(data, n); set_gpio(1, 0); if (verbose) for (int i = 0; i < n; i++) fprintf(stderr, "%02x%c", data[i], i == n-1 || i % 32 == 31 ? '\n' : ' '); } void flash_wait() { if (verbose) fprintf(stderr, "waiting.."); while (1) { uint8_t data[2] = { 0x05 }; set_gpio(0, 0); xfer_spi(data, 2); set_gpio(1, 0); if ((data[1] & 0x01) == 0) break; if (verbose) { fprintf(stderr, "."); fflush(stdout); } usleep(1000); } if (verbose) fprintf(stderr, "\n"); } void help(const char *progname) { fprintf(stderr, "\n"); fprintf(stderr, "iceprog -- simple programming tool for FTDI-based Lattice iCE programmers\n"); fprintf(stderr, "\n"); fprintf(stderr, "\n"); fprintf(stderr, "Notes for iCEstick (iCE40HX-1k devel board):\n"); fprintf(stderr, " An unmodified iCEstick can only be programmed via the serial flash.\n"); fprintf(stderr, " Direct programming of the SRAM is not supported. For direct SRAM\n"); fprintf(stderr, " programming the flash chip and one zero ohm resistor must be desoldered\n"); fprintf(stderr, " and the FT2232H SI pin must be connected to the iCE SPI_SI pin, as shown\n"); fprintf(stderr, " in this picture: http://www.clifford.at/gallery/2014-elektronik/IMG_20141115_183838\n"); fprintf(stderr, "\n"); fprintf(stderr, "\n"); fprintf(stderr, "Notes for the iCE40-HX8K Breakout Board:\n"); fprintf(stderr, " Make sure that the jumper settings on the board match the selected\n"); fprintf(stderr, " mode (SRAM or FLASH). See the iCE40-HX8K user manual for details.\n"); fprintf(stderr, "\n"); fprintf(stderr, "\n"); fprintf(stderr, "Usage: %s [options] \n", progname); fprintf(stderr, "\n"); fprintf(stderr, " -d \n"); fprintf(stderr, " use the specified USB device:\n"); fprintf(stderr, "\n"); fprintf(stderr, " d: (e.g. d:002/005)\n"); fprintf(stderr, " i:: (e.g. i:0x0403:0x6010)\n"); fprintf(stderr, " i::: (e.g. i:0x0403:0x6010:0)\n"); fprintf(stderr, " s:::\n"); fprintf(stderr, "\n"); fprintf(stderr, " -I [ABCD]\n"); fprintf(stderr, " connect to the specified interface on the FTDI chip\n"); fprintf(stderr, "\n"); fprintf(stderr, " -r\n"); fprintf(stderr, " read entire flash (32Mb / 4MB) and write to file\n"); fprintf(stderr, "\n"); fprintf(stderr, " -R\n"); fprintf(stderr, " read first 256 kB from flash and write to file\n"); fprintf(stderr, "\n"); fprintf(stderr, " -c\n"); fprintf(stderr, " do not write flash, only verify (check)\n"); fprintf(stderr, "\n"); fprintf(stderr, " -b\n"); fprintf(stderr, " bulk erase entire flash before writing\n"); fprintf(stderr, "\n"); fprintf(stderr, " -n\n"); fprintf(stderr, " do not erase flash before writing\n"); fprintf(stderr, "\n"); fprintf(stderr, " -S\n"); fprintf(stderr, " perform SRAM programming\n"); fprintf(stderr, "\n"); fprintf(stderr, " -t\n"); fprintf(stderr, " just read the flash ID sequence\n"); fprintf(stderr, "\n"); fprintf(stderr, " -v\n"); fprintf(stderr, " verbose output\n"); fprintf(stderr, "\n"); exit(1); } int main(int argc, char **argv) { int max_read_size = 4 * 1024 * 1024; bool read_mode = false; bool check_mode = false; bool bulk_erase = false; bool dont_erase = false; bool prog_sram = false; bool test_mode = false; const char *filename = NULL; const char *devstr = NULL; enum ftdi_interface ifnum = INTERFACE_A; int opt; while ((opt = getopt(argc, argv, "d:I:rRcbnStv")) != -1) { switch (opt) { case 'd': devstr = optarg; break; case 'I': if (!strcmp(optarg, "A")) ifnum = INTERFACE_A; else if (!strcmp(optarg, "B")) ifnum = INTERFACE_B; else if (!strcmp(optarg, "C")) ifnum = INTERFACE_C; else if (!strcmp(optarg, "D")) ifnum = INTERFACE_D; else help(argv[0]); break; case 'r': read_mode = true; break; case 'R': read_mode = true; max_read_size = 256 * 1024; break; case 'c': check_mode = true; break; case 'b': bulk_erase = true; break; case 'n': dont_erase = true; break; case 'S': prog_sram = true; break; case 't': test_mode = true; break; case 'v': verbose = true; break; default: help(argv[0]); } } if (read_mode + check_mode + prog_sram + test_mode > 1) help(argv[0]); if (bulk_erase && dont_erase) help(argv[0]); if (optind+1 != argc && !test_mode) help(argv[0]); filename = argv[optind]; // --------------------------------------------------------- // Initialize USB connection to FT2232H // --------------------------------------------------------- fprintf(stderr, "init..\n"); ftdi_init(&ftdic); ftdi_set_interface(&ftdic, ifnum); if (devstr != NULL) { if (ftdi_usb_open_string(&ftdic, devstr)) { fprintf(stderr, "Can't find iCE FTDI USB device (device string %s).\n", devstr); error(); } } else { if (ftdi_usb_open(&ftdic, 0x0403, 0x6010)) { fprintf(stderr, "Can't find iCE FTDI USB device (vedor_id 0x0403, device_id 0x6010).\n"); error(); } } ftdic_open = true; if (ftdi_usb_reset(&ftdic)) { fprintf(stderr, "Failed to reset iCE FTDI USB device.\n"); error(); } if (ftdi_usb_purge_buffers(&ftdic)) { fprintf(stderr, "Failed to purge buffers on iCE FTDI USB device.\n"); error(); } if (ftdi_set_bitmode(&ftdic, 0xff, BITMODE_MPSSE) < 0) { fprintf(stderr, "Failed set BITMODE_MPSSE on iCE FTDI USB device.\n"); error(); } // enable clock divide by 5 send_byte(0x8b); // set 6 MHz clock send_byte(0x86); send_byte(0x00); send_byte(0x00); fprintf(stderr, "cdone: %s\n", get_cdone() ? "high" : "low"); set_gpio(1, 1); usleep(100000); if (test_mode) { fprintf(stderr, "reset..\n"); set_gpio(1, 0); usleep(250000); fprintf(stderr, "cdone: %s\n", get_cdone() ? "high" : "low"); flash_power_up(); flash_read_id(); flash_power_down(); set_gpio(1, 1); usleep(250000); fprintf(stderr, "cdone: %s\n", get_cdone() ? "high" : "low"); } else if (prog_sram) { // --------------------------------------------------------- // Reset // --------------------------------------------------------- fprintf(stderr, "reset..\n"); set_gpio(0, 0); usleep(100); set_gpio(0, 1); usleep(2000); fprintf(stderr, "cdone: %s\n", get_cdone() ? "high" : "low"); // --------------------------------------------------------- // Program // --------------------------------------------------------- FILE *f = (strcmp(filename, "-") == 0) ? stdin : fopen(filename, "r"); if (f == NULL) { fprintf(stderr, "Error: Can't open '%s' for reading: %s\n", filename, strerror(errno)); error(); } fprintf(stderr, "programming..\n"); while (1) { static unsigned char buffer[4096]; int rc = fread(buffer, 1, 4096, f); if (rc <= 0) break; if (verbose) fprintf(stderr, "sending %d bytes.\n", rc); send_spi(buffer, rc); } if (f != stdin) fclose(f); // add 48 dummy bits send_byte(0x8f); send_byte(0x05); send_byte(0x00); // add 1 more dummy bit send_byte(0x8e); send_byte(0x00); fprintf(stderr, "cdone: %s\n", get_cdone() ? "high" : "low"); } else { // --------------------------------------------------------- // Reset // --------------------------------------------------------- fprintf(stderr, "reset..\n"); set_gpio(1, 0); usleep(250000); fprintf(stderr, "cdone: %s\n", get_cdone() ? "high" : "low"); flash_power_up(); flash_read_id(); // --------------------------------------------------------- // Program // --------------------------------------------------------- if (!read_mode && !check_mode) { FILE *f = (strcmp(filename, "-") == 0) ? stdin : fopen(filename, "r"); if (f == NULL) { fprintf(stderr, "Error: Can't open '%s' for reading: %s\n", filename, strerror(errno)); error(); } if (!dont_erase) { if (bulk_erase) { flash_write_enable(); flash_bulk_erase(); flash_wait(); } else { struct stat st_buf; if (stat(filename, &st_buf)) { fprintf(stderr, "Error: Can't stat '%s': %s\n", filename, strerror(errno)); error(); } fprintf(stderr, "file size: %d\n", (int)st_buf.st_size); for (int addr = 0; addr < st_buf.st_size; addr += 0x10000) { flash_write_enable(); flash_64kB_sector_erase(addr); flash_wait(); } } } fprintf(stderr, "programming..\n"); for (int addr = 0; true; addr += 256) { uint8_t buffer[256]; int rc = fread(buffer, 1, 256, f); if (rc <= 0) break; flash_write_enable(); flash_prog(addr, buffer, rc); flash_wait(); } if (f != stdin) fclose(f); } // --------------------------------------------------------- // Read/Verify // --------------------------------------------------------- if (read_mode) { FILE *f = (strcmp(filename, "-") == 0) ? stdout : fopen(filename, "w"); if (f == NULL) { fprintf(stderr, "Error: Can't open '%s' for writing: %s\n", filename, strerror(errno)); error(); } fprintf(stderr, "reading..\n"); for (int addr = 0; addr < max_read_size; addr += 256) { uint8_t buffer[256]; flash_read(addr, buffer, 256); fwrite(buffer, 256, 1, f); } if (f != stdout) fclose(f); } else { FILE *f = (strcmp(filename, "-") == 0) ? stdin : fopen(filename, "r"); if (f == NULL) { fprintf(stderr, "Error: Can't open '%s' for reading: %s\n", filename, strerror(errno)); error(); } fprintf(stderr, "reading..\n"); for (int addr = 0; true; addr += 256) { uint8_t buffer_flash[256], buffer_file[256]; int rc = fread(buffer_file, 1, 256, f); if (rc <= 0) break; flash_read(addr, buffer_flash, 256); if (memcmp(buffer_file, buffer_flash, rc)) { fprintf(stderr, "Found difference between flash and file!\n"); error(); } } fprintf(stderr, "VERIFY OK\n"); if (f != stdin) fclose(f); } // --------------------------------------------------------- // Reset // --------------------------------------------------------- flash_power_down(); set_gpio(1, 1); usleep(250000); fprintf(stderr, "cdone: %s\n", get_cdone() ? "high" : "low"); } // --------------------------------------------------------- // Exit // --------------------------------------------------------- fprintf(stderr, "Bye.\n"); ftdi_disable_bitbang(&ftdic); ftdi_usb_close(&ftdic); ftdi_deinit(&ftdic); return 0; }