icestorm/iceprog/iceprog.c

670 lines
15 KiB
C
Raw Normal View History

2015-07-18 13:07:39 +02:00
/*
* 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>
2015-07-18 13:10:40 +02:00
#include <sys/types.h>
#include <sys/stat.h>
2015-07-18 13:07:39 +02:00
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);
2015-07-18 13:07:39 +02:00
}
}
void error()
{
check_rx();
fprintf(stderr, "ABORT.\n");
2015-07-18 13:07:39 +02:00
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");
2015-07-18 13:07:39 +02:00
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);
2015-07-18 13:07:39 +02:00
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);
2015-07-18 13:07:39 +02:00
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);
2015-07-18 13:07:39 +02:00
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");
2015-07-18 13:07:39 +02:00
2015-07-18 13:10:40 +02:00
uint8_t data[21] = { 0x9F };
2015-07-18 13:07:39 +02:00
set_gpio(0, 0);
xfer_spi(data, 21);
set_gpio(1, 0);
fprintf(stderr, "flash ID:");
2015-07-18 13:07:39 +02:00
for (int i = 1; i < 21; i++)
fprintf(stderr, " 0x%02X", data[i]);
fprintf(stderr, "\n");
2015-07-18 13:07:39 +02:00
}
2015-07-18 13:10:40 +02:00
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);
}
2015-07-18 13:07:39 +02:00
void flash_write_enable()
{
if (verbose)
fprintf(stderr, "write enable..\n");
2015-07-18 13:07:39 +02:00
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");
2015-07-18 13:07:39 +02:00
uint8_t data[1] = { 0xc7 };
set_gpio(0, 0);
xfer_spi(data, 1);
set_gpio(1, 0);
}
2015-07-18 13:10:40 +02:00
void flash_64kB_sector_erase(int addr)
2015-07-18 13:07:39 +02:00
{
fprintf(stderr, "erase 64kB sector at 0x%06X..\n", addr);
2015-07-18 13:07:39 +02:00
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)
fprintf(stderr, "prog 0x%06X +0x%03X..\n", addr, n);
2015-07-18 13:07:39 +02:00
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++)
fprintf(stderr, "%02x%c", data[i], i == n-1 || i % 32 == 31 ? '\n' : ' ');
2015-07-18 13:07:39 +02:00
}
void flash_read(int addr, uint8_t *data, int n)
{
if (verbose)
fprintf(stderr, "read 0x%06X +0x%03X..\n", addr, n);
2015-07-18 13:07:39 +02:00
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++)
fprintf(stderr, "%02x%c", data[i], i == n-1 || i % 32 == 31 ? '\n' : ' ');
2015-07-18 13:07:39 +02:00
}
void flash_wait()
{
if (verbose)
fprintf(stderr, "waiting..");
2015-07-18 13:07:39 +02:00
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, ".");
2015-07-18 13:07:39 +02:00
fflush(stdout);
}
usleep(250000);
}
if (verbose)
fprintf(stderr, "\n");
2015-07-18 13:07:39 +02:00
}
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, " -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");
2015-07-18 13:07:39 +02:00
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;
2015-07-18 13:10:40 +02:00
bool test_mode = false;
2015-07-18 13:07:39 +02:00
const char *filename = NULL;
const char *devstr = NULL;
2015-07-18 13:10:40 +02:00
int ifnum = INTERFACE_A;
2015-07-18 13:07:39 +02:00
int opt;
2015-07-18 13:10:40 +02:00
while ((opt = getopt(argc, argv, "d:I:rRcbnStv")) != -1)
2015-07-18 13:07:39 +02:00
{
switch (opt)
{
case 'd':
devstr = optarg;
break;
2015-07-18 13:10:40 +02:00
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;
2015-07-18 13:07:39 +02:00
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;
2015-07-18 13:10:40 +02:00
case 't':
test_mode = true;
break;
2015-07-18 13:07:39 +02:00
case 'v':
verbose = true;
break;
default:
help(argv[0]);
}
}
2015-07-18 13:10:40 +02:00
if (read_mode + check_mode + prog_sram + test_mode > 1)
2015-07-18 13:07:39 +02:00
help(argv[0]);
if (bulk_erase && dont_erase)
help(argv[0]);
2015-07-18 13:10:40 +02:00
if (optind+1 != argc && !test_mode)
2015-07-18 13:07:39 +02:00
help(argv[0]);
filename = argv[optind];
// ---------------------------------------------------------
// Initialize USB connection to FT2232H
// ---------------------------------------------------------
fprintf(stderr, "init..\n");
2015-07-18 13:07:39 +02:00
ftdi_init(&ftdic);
2015-07-18 13:10:40 +02:00
ftdi_set_interface(&ftdic, ifnum);
2015-07-18 13:07:39 +02:00
if (devstr != NULL) {
if (ftdi_usb_open_string(&ftdic, devstr)) {
fprintf(stderr, "Can't find iCE FTDI USB device (device string %s).\n", devstr);
2015-07-18 13:07:39 +02:00
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");
2015-07-18 13:07:39 +02:00
error();
}
}
ftdic_open = true;
if (ftdi_usb_reset(&ftdic)) {
fprintf(stderr, "Failed to reset iCE FTDI USB device.\n");
2015-07-18 13:07:39 +02:00
error();
}
if (ftdi_usb_purge_buffers(&ftdic)) {
fprintf(stderr, "Failed to purge buffers on iCE FTDI USB device.\n");
2015-07-18 13:07:39 +02:00
error();
}
if (ftdi_set_bitmode(&ftdic, 0xff, BITMODE_MPSSE) < 0) {
fprintf(stderr, "Failed set BITMODE_MPSSE on iCE FTDI USB device.\n");
2015-07-18 13:07:39 +02:00
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");
2015-07-18 13:07:39 +02:00
set_gpio(1, 1);
usleep(100000);
2015-07-18 13:10:40 +02:00
if (test_mode)
{
fprintf(stderr, "reset..\n");
2015-07-18 13:10:40 +02:00
set_gpio(1, 0);
usleep(250000);
fprintf(stderr, "cdone: %s\n", get_cdone() ? "high" : "low");
2015-07-18 13:10:40 +02:00
flash_power_up();
flash_read_id();
flash_power_down();
set_gpio(1, 1);
usleep(250000);
fprintf(stderr, "cdone: %s\n", get_cdone() ? "high" : "low");
2015-07-18 13:10:40 +02:00
}
else if (prog_sram)
2015-07-18 13:07:39 +02:00
{
// ---------------------------------------------------------
// Reset
// ---------------------------------------------------------
fprintf(stderr, "reset..\n");
2015-07-18 13:07:39 +02:00
set_gpio(0, 0);
usleep(100);
set_gpio(0, 1);
usleep(2000);
fprintf(stderr, "cdone: %s\n", get_cdone() ? "high" : "low");
2015-07-18 13:07:39 +02:00
// ---------------------------------------------------------
// Program
// ---------------------------------------------------------
FILE *f = (strcmp(filename, "-") == 0) ? stdin :
fopen(filename, "r");
2015-07-18 13:07:39 +02:00
if (f == NULL) {
fprintf(stderr, "Error: Can't open '%s' for reading: %s\n", filename, strerror(errno));
error();
}
fprintf(stderr, "programming..\n");
2015-07-18 13:07:39 +02:00
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);
2015-07-18 13:07:39 +02:00
send_spi(buffer, rc);
}
if (f != stdin)
fclose(f);
2015-07-18 13:07:39 +02:00
// 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");
2015-07-18 13:07:39 +02:00
}
else
{
// ---------------------------------------------------------
// Reset
// ---------------------------------------------------------
fprintf(stderr, "reset..\n");
2015-07-18 13:07:39 +02:00
set_gpio(1, 0);
usleep(250000);
fprintf(stderr, "cdone: %s\n", get_cdone() ? "high" : "low");
2015-07-18 13:07:39 +02:00
2015-07-18 13:10:40 +02:00
flash_power_up();
2015-07-18 13:07:39 +02:00
flash_read_id();
// ---------------------------------------------------------
// Program
// ---------------------------------------------------------
if (!read_mode && !check_mode)
{
FILE *f = (strcmp(filename, "-") == 0) ? stdin :
fopen(filename, "r");
2015-07-18 13:07:39 +02:00
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
{
2015-07-18 13:10:40 +02:00
struct stat st_buf;
if (stat(filename, &st_buf)) {
fprintf(stderr, "Error: Can't stat '%s': %s\n", filename, strerror(errno));
error();
}
2015-07-18 13:07:39 +02:00
fprintf(stderr, "file size: %d\n", (int)st_buf.st_size);
2015-07-18 13:10:40 +02:00
for (int addr = 0; addr < st_buf.st_size; addr += 0x10000) {
2015-07-18 13:07:39 +02:00
flash_write_enable();
2015-07-18 13:10:40 +02:00
flash_64kB_sector_erase(addr);
2015-07-18 13:07:39 +02:00
flash_wait();
}
}
}
fprintf(stderr, "programming..\n");
2015-07-18 13:07:39 +02:00
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);
2015-07-18 13:07:39 +02:00
}
// ---------------------------------------------------------
// Read/Verify
// ---------------------------------------------------------
if (read_mode)
{
FILE *f = (strcmp(filename, "-") == 0) ? stdout :
fopen(filename, "w");
2015-07-18 13:07:39 +02:00
if (f == NULL) {
fprintf(stderr, "Error: Can't open '%s' for writing: %s\n", filename, strerror(errno));
error();
}
fprintf(stderr, "reading..\n");
2015-07-18 13:07:39 +02:00
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);
2015-07-18 13:07:39 +02:00
}
else
{
FILE *f = (strcmp(filename, "-") == 0) ? stdin :
fopen(filename, "r");
2015-07-18 13:07:39 +02:00
if (f == NULL) {
fprintf(stderr, "Error: Can't open '%s' for reading: %s\n", filename, strerror(errno));
error();
}
fprintf(stderr, "reading..\n");
2015-07-18 13:07:39 +02:00
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");
2015-07-18 13:07:39 +02:00
if (f != stdin)
fclose(f);
2015-07-18 13:07:39 +02:00
}
// ---------------------------------------------------------
// Reset
// ---------------------------------------------------------
2015-07-18 13:10:40 +02:00
flash_power_down();
2015-07-18 13:07:39 +02:00
set_gpio(1, 1);
usleep(250000);
fprintf(stderr, "cdone: %s\n", get_cdone() ? "high" : "low");
2015-07-18 13:07:39 +02:00
}
// ---------------------------------------------------------
// Exit
// ---------------------------------------------------------
fprintf(stderr, "Bye.\n");
2015-07-18 13:07:39 +02:00
ftdi_disable_bitbang(&ftdic);
ftdi_usb_close(&ftdic);
ftdi_deinit(&ftdic);
return 0;
}