aboutsummaryrefslogtreecommitdiffstats
path: root/iceprog
diff options
context:
space:
mode:
authorClifford Wolf <clifford@clifford.at>2015-07-18 13:07:39 +0200
committerClifford Wolf <clifford@clifford.at>2015-07-18 13:07:39 +0200
commit13e63e6b65e044e348356731b55610d02cb308b9 (patch)
tree00274273090bec68fc31022b6daef4115290efbb /iceprog
parentc41701ca3a2046e9cabe303cff0aa203215d70c1 (diff)
downloadicestorm-13e63e6b65e044e348356731b55610d02cb308b9.tar.gz
icestorm-13e63e6b65e044e348356731b55610d02cb308b9.tar.bz2
icestorm-13e63e6b65e044e348356731b55610d02cb308b9.zip
Import of icestorm-snapshot-150526.zip
Diffstat (limited to 'iceprog')
-rw-r--r--iceprog/Makefile22
-rw-r--r--iceprog/iceprog.c598
2 files changed, 620 insertions, 0 deletions
diff --git a/iceprog/Makefile b/iceprog/Makefile
new file mode 100644
index 0000000..b0d735a
--- /dev/null
+++ b/iceprog/Makefile
@@ -0,0 +1,22 @@
+# CC = clang
+LDLIBS = -lftdi -lm
+CFLAGS = -MD -O0 -ggdb -Wall -std=c99
+
+all: iceprog
+
+iceprog: iceprog.o
+
+install: all
+ cp iceprog /usr/local/bin/iceprog
+
+uninstall:
+ rm -f /usr/local/bin/iceprog
+
+clean:
+ rm -f iceprog
+ rm -f *.o *.d
+
+-include *.d
+
+.PHONY: all install uninstall clean
+
diff --git a/iceprog/iceprog.c b/iceprog/iceprog.c
new file mode 100644
index 0000000..5d03dc9
--- /dev/null
+++ b/iceprog/iceprog.c
@@ -0,0 +1,598 @@
+/*
+ * iceprog -- simple programming tool for FTDI-based Lattice iCE programmers
+ *
+ * Copyright (C) 2015 Clifford Wolf <clifford@clifford.at>
+ *
+ * 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 <ftdi.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+
+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;
+ printf("unexpected rx byte: %02X\n", data);
+ }
+}
+
+void error()
+{
+ check_rx();
+ printf("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) {
+ printf("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) {
+ printf("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) {
+ printf("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) {
+ printf("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()
+{
+ // printf("read flash ID..\n");
+
+ uint8_t data[21] = { 0x9E };
+ set_gpio(0, 0);
+ xfer_spi(data, 21);
+ set_gpio(1, 0);
+
+ printf("flash ID:");
+ for (int i = 1; i < 21; i++)
+ printf(" 0x%02X", data[i]);
+ printf("\n");
+}
+
+void flash_write_enable()
+{
+ if (verbose)
+ printf("write enable..\n");
+
+ uint8_t data[1] = { 0x06 };
+ set_gpio(0, 0);
+ xfer_spi(data, 1);
+ set_gpio(1, 0);
+}
+
+void flash_bulk_erase()
+{
+ printf("bulk erase..\n");
+
+ uint8_t data[1] = { 0xc7 };
+ set_gpio(0, 0);
+ xfer_spi(data, 1);
+ set_gpio(1, 0);
+}
+
+void flash_sector_erase(int addr)
+{
+ printf("sector erase 0x%06X..\n", addr);
+
+ uint8_t command[4] = { 0xd8, addr >> 16, addr >> 8, 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)
+ printf("prog 0x%06X +0x%03X..\n", addr, n);
+
+ uint8_t command[4] = { 0x02, addr >> 16, addr >> 8, 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++)
+ printf("%02x%c", data[i], i == n-1 || i % 32 == 31 ? '\n' : ' ');
+}
+
+void flash_read(int addr, uint8_t *data, int n)
+{
+ if (verbose)
+ printf("read 0x%06X +0x%03X..\n", addr, n);
+
+ uint8_t command[4] = { 0x03, addr >> 16, addr >> 8, 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++)
+ printf("%02x%c", data[i], i == n-1 || i % 32 == 31 ? '\n' : ' ');
+}
+
+void flash_wait()
+{
+ if (verbose)
+ printf("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) {
+ printf(".");
+ fflush(stdout);
+ }
+ usleep(250000);
+ }
+
+ if (verbose)
+ printf("\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] <filename>\n", progname);
+ fprintf(stderr, "\n");
+ fprintf(stderr, " -d <device-string>\n");
+ fprintf(stderr, " use the specified USB device:\n");
+ fprintf(stderr, "\n");
+ fprintf(stderr, " d:<devicenode> (e.g. d:002/005)\n");
+ fprintf(stderr, " i:<vendor>:<product> (e.g. i:0x0403:0x6010)\n");
+ fprintf(stderr, " i:<vendor>:<product>:<index> (e.g. i:0x0403:0x6010:0)\n");
+ fprintf(stderr, " s:<vendor>:<product>:<serial-string>\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, " -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;
+ const char *filename = NULL;
+ const char *devstr = NULL;
+
+ int opt;
+ while ((opt = getopt(argc, argv, "d:rRcbnSv")) != -1)
+ {
+ switch (opt)
+ {
+ case 'd':
+ devstr = optarg;
+ 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 'v':
+ verbose = true;
+ break;
+ default:
+ help(argv[0]);
+ }
+ }
+
+ if (read_mode && check_mode)
+ help(argv[0]);
+
+ if (bulk_erase && dont_erase)
+ help(argv[0]);
+
+ if (optind+1 != argc)
+ help(argv[0]);
+
+ filename = argv[optind];
+
+ // ---------------------------------------------------------
+ // Initialize USB connection to FT2232H
+ // ---------------------------------------------------------
+
+ printf("init..\n");
+
+ ftdi_init(&ftdic);
+ ftdi_set_interface(&ftdic, INTERFACE_A);
+
+ if (devstr != NULL) {
+ if (ftdi_usb_open_string(&ftdic, devstr)) {
+ printf("Can't find iCE FTDI USB device (device string %s).\n", devstr);
+ error();
+ }
+ } else {
+ if (ftdi_usb_open(&ftdic, 0x0403, 0x6010)) {
+ printf("Can't find iCE FTDI USB device (vedor_id 0x0403, device_id 0x6010).\n");
+ error();
+ }
+ }
+
+ ftdic_open = true;
+
+ if (ftdi_usb_reset(&ftdic)) {
+ printf("Failed to reset iCE FTDI USB device.\n");
+ error();
+ }
+
+ if (ftdi_usb_purge_buffers(&ftdic)) {
+ printf("Failed to purge buffers on iCE FTDI USB device.\n");
+ error();
+ }
+
+ if (ftdi_set_bitmode(&ftdic, 0xff, BITMODE_MPSSE) < 0) {
+ printf("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);
+
+ printf("cdone: %s\n", get_cdone() ? "high" : "low");
+
+ set_gpio(1, 1);
+ usleep(100000);
+
+
+ if (prog_sram)
+ {
+ // ---------------------------------------------------------
+ // Reset
+ // ---------------------------------------------------------
+
+ printf("reset..\n");
+
+ set_gpio(0, 0);
+ usleep(100);
+
+ set_gpio(0, 1);
+ usleep(2000);
+
+ printf("cdone: %s\n", get_cdone() ? "high" : "low");
+
+
+ // ---------------------------------------------------------
+ // Program
+ // ---------------------------------------------------------
+
+ FILE *f = fopen(filename, "r");
+ if (f == NULL) {
+ fprintf(stderr, "Error: Can't open '%s' for reading: %s\n", filename, strerror(errno));
+ error();
+ }
+
+ printf("programming..\n");
+ while (1)
+ {
+ static unsigned char buffer[4096];
+ int rc = fread(buffer, 1, 4096, f);
+ if (rc <= 0) break;
+ if (verbose)
+ printf("sending %d bytes.\n", rc);
+ send_spi(buffer, rc);
+ }
+
+ 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);
+
+ printf("cdone: %s\n", get_cdone() ? "high" : "low");
+ }
+ else
+ {
+ // ---------------------------------------------------------
+ // Reset
+ // ---------------------------------------------------------
+
+ printf("reset..\n");
+
+ set_gpio(1, 0);
+ usleep(250000);
+
+ printf("cdone: %s\n", get_cdone() ? "high" : "low");
+
+ flash_read_id();
+
+
+ // ---------------------------------------------------------
+ // Program
+ // ---------------------------------------------------------
+
+ if (!read_mode && !check_mode)
+ {
+ FILE *f = 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
+ {
+ fseek(f, SEEK_END, 0);
+ int file_size = ftell(f);
+ rewind(f);
+
+ for (int addr = 0; addr < file_size; addr += 0x1000) {
+ flash_write_enable();
+ flash_sector_erase(addr);
+ flash_wait();
+ }
+ }
+ }
+
+ printf("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();
+ }
+
+ fclose(f);
+ }
+
+
+ // ---------------------------------------------------------
+ // Read/Verify
+ // ---------------------------------------------------------
+
+ if (read_mode)
+ {
+ FILE *f = fopen(filename, "w");
+ if (f == NULL) {
+ fprintf(stderr, "Error: Can't open '%s' for writing: %s\n", filename, strerror(errno));
+ error();
+ }
+
+ printf("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);
+ }
+
+ fclose(f);
+ }
+ else
+ {
+ FILE *f = fopen(filename, "r");
+ if (f == NULL) {
+ fprintf(stderr, "Error: Can't open '%s' for reading: %s\n", filename, strerror(errno));
+ error();
+ }
+
+ printf("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();
+ }
+ }
+
+ printf("VERIFY OK\n");
+
+ fclose(f);
+ }
+
+
+ // ---------------------------------------------------------
+ // Reset
+ // ---------------------------------------------------------
+
+ set_gpio(1, 1);
+ usleep(250000);
+
+ printf("cdone: %s\n", get_cdone() ? "high" : "low");
+ }
+
+
+ // ---------------------------------------------------------
+ // Exit
+ // ---------------------------------------------------------
+
+ printf("Bye.\n");
+ ftdi_disable_bitbang(&ftdic);
+ ftdi_usb_close(&ftdic);
+ ftdi_deinit(&ftdic);
+ return 0;
+}
+