mirror of https://github.com/YosysHQ/icestorm.git
970 lines
27 KiB
C
970 lines
27 KiB
C
/*
|
|
* iceprog -- simple programming tool for FTDI-based Lattice iCE programmers
|
|
*
|
|
* Copyright (C) 2015 Clifford Wolf <clifford@clifford.at>
|
|
* Copyright (C) 2018 Piotr Esden-Tempski <piotr@esden.net>
|
|
*
|
|
* 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
|
|
*/
|
|
|
|
#define _GNU_SOURCE
|
|
|
|
#include <stdio.h>
|
|
#include <stdint.h>
|
|
#include <stdbool.h>
|
|
#include <stdlib.h>
|
|
#include <unistd.h>
|
|
#include <string.h>
|
|
#include <getopt.h>
|
|
#include <errno.h>
|
|
#include <sys/types.h>
|
|
#include <sys/stat.h>
|
|
|
|
#include "mpsse.h"
|
|
|
|
static bool verbose = false;
|
|
|
|
// ---------------------------------------------------------
|
|
// FLASH definitions
|
|
// ---------------------------------------------------------
|
|
|
|
/* Flash command definitions */
|
|
/* This command list is based on the Winbond W25Q128JV Datasheet */
|
|
enum flash_cmd {
|
|
FC_WE = 0x06, /* Write Enable */
|
|
FC_SRWE = 0x50, /* Volatile SR Write Enable */
|
|
FC_WD = 0x04, /* Write Disable */
|
|
FC_RPD = 0xAB, /* Release Power-Down, returns Device ID */
|
|
FC_MFGID = 0x90, /* Read Manufacturer/Device ID */
|
|
FC_JEDECID = 0x9F, /* Read JEDEC ID */
|
|
FC_UID = 0x4B, /* Read Unique ID */
|
|
FC_RD = 0x03, /* Read Data */
|
|
FC_FR = 0x0B, /* Fast Read */
|
|
FC_PP = 0x02, /* Page Program */
|
|
FC_SE = 0x20, /* Sector Erase 4kb */
|
|
FC_BE32 = 0x52, /* Block Erase 32kb */
|
|
FC_BE64 = 0xD8, /* Block Erase 64kb */
|
|
FC_CE = 0xC7, /* Chip Erase */
|
|
FC_RSR1 = 0x05, /* Read Status Register 1 */
|
|
FC_WSR1 = 0x01, /* Write Status Register 1 */
|
|
FC_RSR2 = 0x35, /* Read Status Register 2 */
|
|
FC_WSR2 = 0x31, /* Write Status Register 2 */
|
|
FC_RSR3 = 0x15, /* Read Status Register 3 */
|
|
FC_WSR3 = 0x11, /* Write Status Register 3 */
|
|
FC_RSFDP = 0x5A, /* Read SFDP Register */
|
|
FC_ESR = 0x44, /* Erase Security Register */
|
|
FC_PSR = 0x42, /* Program Security Register */
|
|
FC_RSR = 0x48, /* Read Security Register */
|
|
FC_GBL = 0x7E, /* Global Block Lock */
|
|
FC_GBU = 0x98, /* Global Block Unlock */
|
|
FC_RBL = 0x3D, /* Read Block Lock */
|
|
FC_RPR = 0x3C, /* Read Sector Protection Registers (adesto) */
|
|
FC_IBL = 0x36, /* Individual Block Lock */
|
|
FC_IBU = 0x39, /* Individual Block Unlock */
|
|
FC_EPS = 0x75, /* Erase / Program Suspend */
|
|
FC_EPR = 0x7A, /* Erase / Program Resume */
|
|
FC_PD = 0xB9, /* Power-down */
|
|
FC_QPI = 0x38, /* Enter QPI mode */
|
|
FC_ERESET = 0x66, /* Enable Reset */
|
|
FC_RESET = 0x99, /* Reset Device */
|
|
};
|
|
|
|
// ---------------------------------------------------------
|
|
// Hardware specific CS, CReset, CDone functions
|
|
// ---------------------------------------------------------
|
|
|
|
static void set_cs_creset(int cs_b, int creset_b)
|
|
{
|
|
uint8_t gpio = 0;
|
|
uint8_t direction = 0x93;
|
|
|
|
if (cs_b) {
|
|
// ADBUS4 (GPIOL0)
|
|
gpio |= 0x10;
|
|
}
|
|
|
|
if (creset_b) {
|
|
// ADBUS7 (GPIOL3)
|
|
gpio |= 0x80;
|
|
}
|
|
|
|
mpsse_set_gpio(gpio, direction);
|
|
}
|
|
|
|
static bool get_cdone(void)
|
|
{
|
|
// ADBUS6 (GPIOL2)
|
|
return (mpsse_readb_low() & 0x40) != 0;
|
|
}
|
|
|
|
// ---------------------------------------------------------
|
|
// FLASH function implementations
|
|
// ---------------------------------------------------------
|
|
|
|
// the FPGA reset is released so also FLASH chip select should be deasserted
|
|
static void flash_release_reset()
|
|
{
|
|
set_cs_creset(1, 1);
|
|
}
|
|
|
|
// FLASH chip select assert
|
|
// should only happen while FPGA reset is asserted
|
|
static void flash_chip_select()
|
|
{
|
|
set_cs_creset(0, 0);
|
|
}
|
|
|
|
// FLASH chip select deassert
|
|
static void flash_chip_deselect()
|
|
{
|
|
set_cs_creset(1, 0);
|
|
}
|
|
|
|
// SRAM reset is the same as flash_chip_select()
|
|
// For ease of code reading we use this function instead
|
|
static void sram_reset()
|
|
{
|
|
// Asserting chip select and reset lines
|
|
set_cs_creset(0, 0);
|
|
}
|
|
|
|
// SRAM chip select assert
|
|
// When accessing FPGA SRAM the reset should be released
|
|
static void sram_chip_select()
|
|
{
|
|
set_cs_creset(0, 1);
|
|
}
|
|
|
|
static void flash_read_id()
|
|
{
|
|
/* JEDEC ID structure:
|
|
* Byte No. | Data Type
|
|
* ---------+----------
|
|
* 0 | FC_JEDECID Request Command
|
|
* 1 | MFG ID
|
|
* 2 | Dev ID 1
|
|
* 3 | Dev ID 2
|
|
* 4 | Ext Dev Str Len
|
|
*/
|
|
|
|
uint8_t data[260] = { FC_JEDECID };
|
|
int len = 5; // command + 4 response bytes
|
|
|
|
if (verbose)
|
|
fprintf(stderr, "read flash ID..\n");
|
|
|
|
flash_chip_select();
|
|
|
|
// Write command and read first 4 bytes
|
|
mpsse_xfer_spi(data, len);
|
|
|
|
if (data[4] == 0xFF)
|
|
fprintf(stderr, "Extended Device String Length is 0xFF, "
|
|
"this is likely a read error. Ignorig...\n");
|
|
else {
|
|
// Read extended JEDEC ID bytes
|
|
if (data[4] != 0) {
|
|
len += data[4];
|
|
mpsse_xfer_spi(data + 5, len - 5);
|
|
}
|
|
}
|
|
|
|
flash_chip_deselect();
|
|
|
|
// TODO: Add full decode of the JEDEC ID.
|
|
fprintf(stderr, "flash ID:");
|
|
for (int i = 1; i < len; i++)
|
|
fprintf(stderr, " 0x%02X", data[i]);
|
|
fprintf(stderr, "\n");
|
|
}
|
|
|
|
static void flash_reset()
|
|
{
|
|
flash_chip_select();
|
|
mpsse_xfer_spi_bits(0xFF, 8);
|
|
flash_chip_deselect();
|
|
|
|
flash_chip_select();
|
|
mpsse_xfer_spi_bits(0xFF, 2);
|
|
flash_chip_deselect();
|
|
}
|
|
|
|
static void flash_power_up()
|
|
{
|
|
uint8_t data_rpd[1] = { FC_RPD };
|
|
flash_chip_select();
|
|
mpsse_xfer_spi(data_rpd, 1);
|
|
flash_chip_deselect();
|
|
}
|
|
|
|
static void flash_power_down()
|
|
{
|
|
uint8_t data[1] = { FC_PD };
|
|
flash_chip_select();
|
|
mpsse_xfer_spi(data, 1);
|
|
flash_chip_deselect();
|
|
}
|
|
|
|
static uint8_t flash_read_status()
|
|
{
|
|
uint8_t data[2] = { FC_RSR1 };
|
|
|
|
flash_chip_select();
|
|
mpsse_xfer_spi(data, 2);
|
|
flash_chip_deselect();
|
|
|
|
if (verbose) {
|
|
fprintf(stderr, "SR1: 0x%02X\n", data[1]);
|
|
fprintf(stderr, " - SPRL: %s\n",
|
|
((data[1] & (1 << 7)) == 0) ?
|
|
"unlocked" :
|
|
"locked");
|
|
fprintf(stderr, " - SPM: %s\n",
|
|
((data[1] & (1 << 6)) == 0) ?
|
|
"Byte/Page Prog Mode" :
|
|
"Sequential Prog Mode");
|
|
fprintf(stderr, " - EPE: %s\n",
|
|
((data[1] & (1 << 5)) == 0) ?
|
|
"Erase/Prog success" :
|
|
"Erase/Prog error");
|
|
fprintf(stderr, "- SPM: %s\n",
|
|
((data[1] & (1 << 4)) == 0) ?
|
|
"~WP asserted" :
|
|
"~WP deasserted");
|
|
fprintf(stderr, " - SWP: ");
|
|
switch((data[1] >> 2) & 0x3) {
|
|
case 0:
|
|
fprintf(stderr, "All sectors unprotected\n");
|
|
break;
|
|
case 1:
|
|
fprintf(stderr, "Some sectors protected\n");
|
|
break;
|
|
case 2:
|
|
fprintf(stderr, "Reserved (xxxx 10xx)\n");
|
|
break;
|
|
case 3:
|
|
fprintf(stderr, "All sectors protected\n");
|
|
break;
|
|
}
|
|
fprintf(stderr, " - WEL: %s\n",
|
|
((data[1] & (1 << 1)) == 0) ?
|
|
"Not write enabled" :
|
|
"Write enabled");
|
|
fprintf(stderr, " - ~RDY: %s\n",
|
|
((data[1] & (1 << 0)) == 0) ?
|
|
"Ready" :
|
|
"Busy");
|
|
}
|
|
|
|
usleep(1000);
|
|
|
|
return data[1];
|
|
}
|
|
|
|
static void flash_write_enable()
|
|
{
|
|
if (verbose) {
|
|
fprintf(stderr, "status before enable:\n");
|
|
flash_read_status();
|
|
}
|
|
|
|
if (verbose)
|
|
fprintf(stderr, "write enable..\n");
|
|
|
|
uint8_t data[1] = { FC_WE };
|
|
flash_chip_select();
|
|
mpsse_xfer_spi(data, 1);
|
|
flash_chip_deselect();
|
|
|
|
if (verbose) {
|
|
fprintf(stderr, "status after enable:\n");
|
|
flash_read_status();
|
|
}
|
|
}
|
|
|
|
static void flash_bulk_erase()
|
|
{
|
|
fprintf(stderr, "bulk erase..\n");
|
|
|
|
uint8_t data[1] = { FC_CE };
|
|
flash_chip_select();
|
|
mpsse_xfer_spi(data, 1);
|
|
flash_chip_deselect();
|
|
}
|
|
|
|
static void flash_64kB_sector_erase(int addr)
|
|
{
|
|
fprintf(stderr, "erase 64kB sector at 0x%06X..\n", addr);
|
|
|
|
uint8_t command[4] = { FC_BE64, (uint8_t)(addr >> 16), (uint8_t)(addr >> 8), (uint8_t)addr };
|
|
|
|
flash_chip_select();
|
|
mpsse_send_spi(command, 4);
|
|
flash_chip_deselect();
|
|
}
|
|
|
|
static 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] = { FC_PP, (uint8_t)(addr >> 16), (uint8_t)(addr >> 8), (uint8_t)addr };
|
|
|
|
flash_chip_select();
|
|
mpsse_send_spi(command, 4);
|
|
mpsse_send_spi(data, n);
|
|
flash_chip_deselect();
|
|
|
|
if (verbose)
|
|
for (int i = 0; i < n; i++)
|
|
fprintf(stderr, "%02x%c", data[i], i == n - 1 || i % 32 == 31 ? '\n' : ' ');
|
|
}
|
|
|
|
static 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] = { FC_RD, (uint8_t)(addr >> 16), (uint8_t)(addr >> 8), (uint8_t)addr };
|
|
|
|
flash_chip_select();
|
|
mpsse_send_spi(command, 4);
|
|
memset(data, 0, n);
|
|
mpsse_xfer_spi(data, n);
|
|
flash_chip_deselect();
|
|
|
|
if (verbose)
|
|
for (int i = 0; i < n; i++)
|
|
fprintf(stderr, "%02x%c", data[i], i == n - 1 || i % 32 == 31 ? '\n' : ' ');
|
|
}
|
|
|
|
static void flash_wait()
|
|
{
|
|
if (verbose)
|
|
fprintf(stderr, "waiting..");
|
|
|
|
int count = 0;
|
|
while (1)
|
|
{
|
|
uint8_t data[2] = { FC_RSR1 };
|
|
|
|
flash_chip_select();
|
|
mpsse_xfer_spi(data, 2);
|
|
flash_chip_deselect();
|
|
|
|
if ((data[1] & 0x01) == 0) {
|
|
if (count < 2) {
|
|
count++;
|
|
if (verbose) {
|
|
fprintf(stderr, "r");
|
|
fflush(stderr);
|
|
}
|
|
} else {
|
|
if (verbose) {
|
|
fprintf(stderr, "R");
|
|
fflush(stderr);
|
|
}
|
|
break;
|
|
}
|
|
} else {
|
|
if (verbose) {
|
|
fprintf(stderr, ".");
|
|
fflush(stderr);
|
|
}
|
|
count = 0;
|
|
}
|
|
|
|
usleep(1000);
|
|
}
|
|
|
|
if (verbose)
|
|
fprintf(stderr, "\n");
|
|
|
|
}
|
|
|
|
static void flash_disable_protection()
|
|
{
|
|
fprintf(stderr, "disable flash protection...\n");
|
|
|
|
// Write Status Register 1 <- 0x00
|
|
uint8_t data[2] = { FC_WSR1, 0x00 };
|
|
flash_chip_select();
|
|
mpsse_xfer_spi(data, 2);
|
|
flash_chip_deselect();
|
|
|
|
flash_wait();
|
|
|
|
// Read Status Register 1
|
|
data[0] = FC_RSR1;
|
|
|
|
flash_chip_select();
|
|
mpsse_xfer_spi(data, 2);
|
|
flash_chip_deselect();
|
|
|
|
if (data[1] != 0x00)
|
|
fprintf(stderr, "failed to disable protection, SR now equal to 0x%02x (expected 0x00)\n", data[1]);
|
|
|
|
}
|
|
|
|
// ---------------------------------------------------------
|
|
// iceprog implementation
|
|
// ---------------------------------------------------------
|
|
|
|
static void help(const char *progname)
|
|
{
|
|
fprintf(stderr, "Simple programming tool for FTDI-based Lattice iCE programmers.\n");
|
|
fprintf(stderr, "Usage: %s [-b|-n|-c] <input file>\n", progname);
|
|
fprintf(stderr, " %s -r|-R<bytes> <output file>\n", progname);
|
|
fprintf(stderr, " %s -S <input file>\n", progname);
|
|
fprintf(stderr, " %s -t\n", progname);
|
|
fprintf(stderr, "\n");
|
|
fprintf(stderr, "General options:\n");
|
|
fprintf(stderr, " -d <device string> use the specified USB device [default: i:0x0403:0x6010 or i:0x0403:0x6014]\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, " -I [ABCD] connect to the specified interface on the FTDI chip\n");
|
|
fprintf(stderr, " [default: A]\n");
|
|
fprintf(stderr, " -o <offset in bytes> start address for read/write [default: 0]\n");
|
|
fprintf(stderr, " (append 'k' to the argument for size in kilobytes,\n");
|
|
fprintf(stderr, " or 'M' for size in megabytes)\n");
|
|
fprintf(stderr, " -s slow SPI (50 kHz instead of 6 MHz)\n");
|
|
fprintf(stderr, " -v verbose output\n");
|
|
fprintf(stderr, "\n");
|
|
fprintf(stderr, "Mode of operation:\n");
|
|
fprintf(stderr, " [default] write file contents to flash, then verify\n");
|
|
fprintf(stderr, " -X write file contents to flash only\n");
|
|
fprintf(stderr, " -r read first 256 kB from flash and write to file\n");
|
|
fprintf(stderr, " -R <size in bytes> read the specified number of bytes from flash\n");
|
|
fprintf(stderr, " (append 'k' to the argument for size in kilobytes,\n");
|
|
fprintf(stderr, " or 'M' for size in megabytes)\n");
|
|
fprintf(stderr, " -c do not write flash, only verify (`check')\n");
|
|
fprintf(stderr, " -S perform SRAM programming\n");
|
|
fprintf(stderr, " -t just read the flash ID sequence\n");
|
|
fprintf(stderr, "\n");
|
|
fprintf(stderr, "Erase mode (only meaningful in default mode):\n");
|
|
fprintf(stderr, " [default] erase aligned chunks of 64kB in write mode\n");
|
|
fprintf(stderr, " This means that some data after the written data (or\n");
|
|
fprintf(stderr, " even before when -o is used) may be erased as well.\n");
|
|
fprintf(stderr, " -b bulk erase entire flash before writing\n");
|
|
fprintf(stderr, " -e <size in bytes> erase flash as if we were writing that number of bytes\n");
|
|
fprintf(stderr, " -n do not erase flash before writing\n");
|
|
fprintf(stderr, " -p disable write protection before erasing or writing\n");
|
|
fprintf(stderr, " This can be useful if flash memory appears to be\n");
|
|
fprintf(stderr, " bricked and won't respond to erasing or programming.\n");
|
|
fprintf(stderr, "\n");
|
|
fprintf(stderr, "Miscellaneous options:\n");
|
|
fprintf(stderr, " --help display this help and exit\n");
|
|
fprintf(stderr, " -- treat all remaining arguments as filenames\n");
|
|
fprintf(stderr, "\n");
|
|
fprintf(stderr, "Exit status:\n");
|
|
fprintf(stderr, " 0 on success,\n");
|
|
fprintf(stderr, " 1 if a non-hardware error occurred (e.g., failure to read from or\n");
|
|
fprintf(stderr, " write to a file, or invoked with invalid options),\n");
|
|
fprintf(stderr, " 2 if communication with the hardware failed (e.g., cannot find the\n");
|
|
fprintf(stderr, " iCE FTDI USB device),\n");
|
|
fprintf(stderr, " 3 if verification of the data failed.\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:\n");
|
|
fprintf(stderr, " http://www.clifford.at/gallery/2014-elektronik/IMG_20141115_183838\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, "If you have a bug report, please file an issue on github:\n");
|
|
fprintf(stderr, " https://github.com/cliffordwolf/icestorm/issues\n");
|
|
}
|
|
|
|
int main(int argc, char **argv)
|
|
{
|
|
/* used for error reporting */
|
|
const char *my_name = argv[0];
|
|
for (size_t i = 0; argv[0][i]; i++)
|
|
if (argv[0][i] == '/')
|
|
my_name = argv[0] + i + 1;
|
|
|
|
int read_size = 256 * 1024;
|
|
int erase_size = 0;
|
|
int rw_offset = 0;
|
|
|
|
bool read_mode = false;
|
|
bool check_mode = false;
|
|
bool erase_mode = false;
|
|
bool bulk_erase = false;
|
|
bool dont_erase = false;
|
|
bool prog_sram = false;
|
|
bool test_mode = false;
|
|
bool slow_clock = false;
|
|
bool disable_protect = false;
|
|
bool disable_verify = false;
|
|
const char *filename = NULL;
|
|
const char *devstr = NULL;
|
|
int ifnum = 0;
|
|
|
|
static struct option long_options[] = {
|
|
{"help", no_argument, NULL, -2},
|
|
{NULL, 0, NULL, 0}
|
|
};
|
|
|
|
/* Decode command line parameters */
|
|
int opt;
|
|
char *endptr;
|
|
while ((opt = getopt_long(argc, argv, "d:I:rR:e:o:cbnStvspX", long_options, NULL)) != -1) {
|
|
switch (opt) {
|
|
case 'd': /* device string */
|
|
devstr = optarg;
|
|
break;
|
|
case 'I': /* FTDI Chip interface select */
|
|
if (!strcmp(optarg, "A"))
|
|
ifnum = 0;
|
|
else if (!strcmp(optarg, "B"))
|
|
ifnum = 1;
|
|
else if (!strcmp(optarg, "C"))
|
|
ifnum = 2;
|
|
else if (!strcmp(optarg, "D"))
|
|
ifnum = 3;
|
|
else {
|
|
fprintf(stderr, "%s: `%s' is not a valid interface (must be `A', `B', `C', or `D')\n", my_name, optarg);
|
|
return EXIT_FAILURE;
|
|
}
|
|
break;
|
|
case 'r': /* Read 256 bytes to file */
|
|
read_mode = true;
|
|
break;
|
|
case 'R': /* Read n bytes to file */
|
|
read_mode = true;
|
|
read_size = strtol(optarg, &endptr, 0);
|
|
if (*endptr == '\0')
|
|
/* ok */;
|
|
else if (!strcmp(endptr, "k"))
|
|
read_size *= 1024;
|
|
else if (!strcmp(endptr, "M"))
|
|
read_size *= 1024 * 1024;
|
|
else {
|
|
fprintf(stderr, "%s: `%s' is not a valid size\n", my_name, optarg);
|
|
return EXIT_FAILURE;
|
|
}
|
|
break;
|
|
case 'e': /* Erase blocks as if we were writing n bytes */
|
|
erase_mode = true;
|
|
erase_size = strtol(optarg, &endptr, 0);
|
|
if (*endptr == '\0')
|
|
/* ok */;
|
|
else if (!strcmp(endptr, "k"))
|
|
erase_size *= 1024;
|
|
else if (!strcmp(endptr, "M"))
|
|
erase_size *= 1024 * 1024;
|
|
else {
|
|
fprintf(stderr, "%s: `%s' is not a valid size\n", my_name, optarg);
|
|
return EXIT_FAILURE;
|
|
}
|
|
break;
|
|
case 'o': /* set address offset */
|
|
rw_offset = strtol(optarg, &endptr, 0);
|
|
if (*endptr == '\0')
|
|
/* ok */;
|
|
else if (!strcmp(endptr, "k"))
|
|
rw_offset *= 1024;
|
|
else if (!strcmp(endptr, "M"))
|
|
rw_offset *= 1024 * 1024;
|
|
else {
|
|
fprintf(stderr, "%s: `%s' is not a valid offset\n", my_name, optarg);
|
|
return EXIT_FAILURE;
|
|
}
|
|
break;
|
|
case 'c': /* do not write just check */
|
|
check_mode = true;
|
|
break;
|
|
case 'b': /* bulk erase before writing */
|
|
bulk_erase = true;
|
|
break;
|
|
case 'n': /* do not erase before writing */
|
|
dont_erase = true;
|
|
break;
|
|
case 'S': /* write to sram directly */
|
|
prog_sram = true;
|
|
break;
|
|
case 't': /* just read flash id */
|
|
test_mode = true;
|
|
break;
|
|
case 'v': /* provide verbose output */
|
|
verbose = true;
|
|
break;
|
|
case 's': /* use slow SPI clock */
|
|
slow_clock = true;
|
|
break;
|
|
case 'p': /* disable flash protect before erase/write */
|
|
disable_protect = true;
|
|
break;
|
|
case 'X': /* disable verification */
|
|
disable_verify = true;
|
|
break;
|
|
case -2:
|
|
help(argv[0]);
|
|
return EXIT_SUCCESS;
|
|
default:
|
|
/* error message has already been printed */
|
|
fprintf(stderr, "Try `%s --help' for more information.\n", argv[0]);
|
|
return EXIT_FAILURE;
|
|
}
|
|
}
|
|
|
|
/* Make sure that the combination of provided parameters makes sense */
|
|
|
|
if (read_mode + erase_mode + check_mode + prog_sram + test_mode > 1) {
|
|
fprintf(stderr, "%s: options `-r'/`-R', `-e`, `-c', `-S', and `-t' are mutually exclusive\n", my_name);
|
|
return EXIT_FAILURE;
|
|
}
|
|
|
|
if (bulk_erase && dont_erase) {
|
|
fprintf(stderr, "%s: options `-b' and `-n' are mutually exclusive\n", my_name);
|
|
return EXIT_FAILURE;
|
|
}
|
|
|
|
if (disable_protect && (read_mode || check_mode || prog_sram || test_mode)) {
|
|
fprintf(stderr, "%s: option `-p' only valid in programming mode\n", my_name);
|
|
return EXIT_FAILURE;
|
|
}
|
|
|
|
if (bulk_erase && (read_mode || check_mode || prog_sram || test_mode)) {
|
|
fprintf(stderr, "%s: option `-b' only valid in programming mode\n", my_name);
|
|
return EXIT_FAILURE;
|
|
}
|
|
|
|
if (dont_erase && (read_mode || check_mode || prog_sram || test_mode)) {
|
|
fprintf(stderr, "%s: option `-n' only valid in programming mode\n", my_name);
|
|
return EXIT_FAILURE;
|
|
}
|
|
|
|
if (rw_offset != 0 && prog_sram) {
|
|
fprintf(stderr, "%s: option `-o' not supported in SRAM mode\n", my_name);
|
|
return EXIT_FAILURE;
|
|
}
|
|
|
|
if (rw_offset != 0 && test_mode) {
|
|
fprintf(stderr, "%s: option `-o' not supported in test mode\n", my_name);
|
|
return EXIT_FAILURE;
|
|
}
|
|
|
|
if (optind + 1 == argc) {
|
|
if (test_mode) {
|
|
fprintf(stderr, "%s: test mode doesn't take a file name\n", my_name);
|
|
fprintf(stderr, "Try `%s --help' for more information.\n", argv[0]);
|
|
return EXIT_FAILURE;
|
|
}
|
|
filename = argv[optind];
|
|
} else if (optind != argc) {
|
|
fprintf(stderr, "%s: too many arguments\n", my_name);
|
|
fprintf(stderr, "Try `%s --help' for more information.\n", argv[0]);
|
|
return EXIT_FAILURE;
|
|
} else if (bulk_erase || disable_protect) {
|
|
filename = "/dev/null";
|
|
} else if (!test_mode && !erase_mode && !disable_protect) {
|
|
fprintf(stderr, "%s: missing argument\n", my_name);
|
|
fprintf(stderr, "Try `%s --help' for more information.\n", argv[0]);
|
|
return EXIT_FAILURE;
|
|
}
|
|
|
|
/* open input/output file in advance
|
|
so we can fail before initializing the hardware */
|
|
|
|
FILE *f = NULL;
|
|
long file_size = -1;
|
|
|
|
if (test_mode) {
|
|
/* nop */;
|
|
} else if (erase_mode) {
|
|
file_size = erase_size;
|
|
} else if (read_mode) {
|
|
f = (strcmp(filename, "-") == 0) ? stdout : fopen(filename, "wb");
|
|
if (f == NULL) {
|
|
fprintf(stderr, "%s: can't open '%s' for writing: ", my_name, filename);
|
|
perror(0);
|
|
return EXIT_FAILURE;
|
|
}
|
|
} else {
|
|
f = (strcmp(filename, "-") == 0) ? stdin : fopen(filename, "rb");
|
|
if (f == NULL) {
|
|
fprintf(stderr, "%s: can't open '%s' for reading: ", my_name, filename);
|
|
perror(0);
|
|
return EXIT_FAILURE;
|
|
}
|
|
|
|
/* For regular programming, we need to read the file
|
|
twice--once for programming and once for verifying--and
|
|
need to know the file size in advance in order to erase
|
|
the correct amount of memory.
|
|
|
|
See if we can seek on the input file. Checking for "-"
|
|
as an argument isn't enough as we might be reading from a
|
|
named pipe, or contrarily, the standard input may be an
|
|
ordinary file. */
|
|
|
|
if (!prog_sram && !check_mode) {
|
|
if (fseek(f, 0L, SEEK_END) != -1) {
|
|
file_size = ftell(f);
|
|
if (file_size == -1) {
|
|
fprintf(stderr, "%s: %s: ftell: ", my_name, filename);
|
|
perror(0);
|
|
return EXIT_FAILURE;
|
|
}
|
|
if (fseek(f, 0L, SEEK_SET) == -1) {
|
|
fprintf(stderr, "%s: %s: fseek: ", my_name, filename);
|
|
perror(0);
|
|
return EXIT_FAILURE;
|
|
}
|
|
} else {
|
|
FILE *pipe = f;
|
|
|
|
f = tmpfile();
|
|
if (f == NULL) {
|
|
fprintf(stderr, "%s: can't open temporary file\n", my_name);
|
|
return EXIT_FAILURE;
|
|
}
|
|
file_size = 0;
|
|
|
|
while (true) {
|
|
static unsigned char buffer[4096];
|
|
size_t rc = fread(buffer, 1, 4096, pipe);
|
|
if (rc <= 0)
|
|
break;
|
|
size_t wc = fwrite(buffer, 1, rc, f);
|
|
if (wc != rc) {
|
|
fprintf(stderr, "%s: can't write to temporary file\n", my_name);
|
|
return EXIT_FAILURE;
|
|
}
|
|
file_size += rc;
|
|
}
|
|
fclose(pipe);
|
|
|
|
/* now seek to the beginning so we can
|
|
start reading again */
|
|
fseek(f, 0, SEEK_SET);
|
|
}
|
|
}
|
|
}
|
|
|
|
// ---------------------------------------------------------
|
|
// Initialize USB connection to FT2232H
|
|
// ---------------------------------------------------------
|
|
|
|
fprintf(stderr, "init..\n");
|
|
|
|
mpsse_init(ifnum, devstr, slow_clock);
|
|
|
|
fprintf(stderr, "cdone: %s\n", get_cdone() ? "high" : "low");
|
|
|
|
flash_release_reset();
|
|
usleep(100000);
|
|
|
|
if (test_mode)
|
|
{
|
|
fprintf(stderr, "reset..\n");
|
|
|
|
flash_chip_deselect();
|
|
usleep(250000);
|
|
|
|
fprintf(stderr, "cdone: %s\n", get_cdone() ? "high" : "low");
|
|
|
|
flash_reset();
|
|
flash_power_up();
|
|
|
|
flash_read_id();
|
|
|
|
flash_power_down();
|
|
|
|
flash_release_reset();
|
|
usleep(250000);
|
|
|
|
fprintf(stderr, "cdone: %s\n", get_cdone() ? "high" : "low");
|
|
}
|
|
else if (prog_sram)
|
|
{
|
|
// ---------------------------------------------------------
|
|
// Reset
|
|
// ---------------------------------------------------------
|
|
|
|
fprintf(stderr, "reset..\n");
|
|
|
|
sram_reset();
|
|
usleep(100);
|
|
|
|
sram_chip_select();
|
|
usleep(2000);
|
|
|
|
fprintf(stderr, "cdone: %s\n", get_cdone() ? "high" : "low");
|
|
|
|
|
|
// ---------------------------------------------------------
|
|
// Program
|
|
// ---------------------------------------------------------
|
|
|
|
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);
|
|
mpsse_send_spi(buffer, rc);
|
|
}
|
|
|
|
mpsse_send_dummy_bytes(6);
|
|
mpsse_send_dummy_bit();
|
|
|
|
fprintf(stderr, "cdone: %s\n", get_cdone() ? "high" : "low");
|
|
}
|
|
else /* program flash */
|
|
{
|
|
// ---------------------------------------------------------
|
|
// Reset
|
|
// ---------------------------------------------------------
|
|
|
|
fprintf(stderr, "reset..\n");
|
|
|
|
flash_chip_deselect();
|
|
usleep(250000);
|
|
|
|
fprintf(stderr, "cdone: %s\n", get_cdone() ? "high" : "low");
|
|
|
|
flash_reset();
|
|
flash_power_up();
|
|
|
|
flash_read_id();
|
|
|
|
|
|
// ---------------------------------------------------------
|
|
// Program
|
|
// ---------------------------------------------------------
|
|
|
|
if (!read_mode && !check_mode)
|
|
{
|
|
if (disable_protect)
|
|
{
|
|
flash_write_enable();
|
|
flash_disable_protection();
|
|
}
|
|
|
|
if (!dont_erase)
|
|
{
|
|
if (bulk_erase)
|
|
{
|
|
flash_write_enable();
|
|
flash_bulk_erase();
|
|
flash_wait();
|
|
}
|
|
else
|
|
{
|
|
fprintf(stderr, "file size: %ld\n", file_size);
|
|
|
|
int begin_addr = rw_offset & ~0xffff;
|
|
int end_addr = (rw_offset + file_size + 0xffff) & ~0xffff;
|
|
|
|
for (int addr = begin_addr; addr < end_addr; addr += 0x10000) {
|
|
flash_write_enable();
|
|
flash_64kB_sector_erase(addr);
|
|
if (verbose) {
|
|
fprintf(stderr, "Status after block erase:\n");
|
|
flash_read_status();
|
|
}
|
|
flash_wait();
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!erase_mode)
|
|
{
|
|
fprintf(stderr, "programming..\n");
|
|
|
|
for (int rc, addr = 0; true; addr += rc) {
|
|
uint8_t buffer[256];
|
|
int page_size = 256 - (rw_offset + addr) % 256;
|
|
rc = fread(buffer, 1, page_size, f);
|
|
if (rc <= 0)
|
|
break;
|
|
flash_write_enable();
|
|
flash_prog(rw_offset + addr, buffer, rc);
|
|
flash_wait();
|
|
}
|
|
|
|
/* seek to the beginning for second pass */
|
|
fseek(f, 0, SEEK_SET);
|
|
}
|
|
}
|
|
|
|
// ---------------------------------------------------------
|
|
// Read/Verify
|
|
// ---------------------------------------------------------
|
|
|
|
if (read_mode) {
|
|
fprintf(stderr, "reading..\n");
|
|
for (int addr = 0; addr < read_size; addr += 256) {
|
|
uint8_t buffer[256];
|
|
flash_read(rw_offset + addr, buffer, 256);
|
|
fwrite(buffer, read_size - addr > 256 ? 256 : read_size - addr, 1, f);
|
|
}
|
|
} else if (!erase_mode && !disable_verify) {
|
|
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(rw_offset + addr, buffer_flash, rc);
|
|
if (memcmp(buffer_file, buffer_flash, rc)) {
|
|
fprintf(stderr, "Found difference between flash and file!\n");
|
|
mpsse_error(3);
|
|
}
|
|
}
|
|
|
|
fprintf(stderr, "VERIFY OK\n");
|
|
}
|
|
|
|
|
|
// ---------------------------------------------------------
|
|
// Reset
|
|
// ---------------------------------------------------------
|
|
|
|
flash_power_down();
|
|
|
|
set_cs_creset(1, 1);
|
|
usleep(250000);
|
|
|
|
fprintf(stderr, "cdone: %s\n", get_cdone() ? "high" : "low");
|
|
}
|
|
|
|
if (f != NULL && f != stdin && f != stdout)
|
|
fclose(f);
|
|
|
|
// ---------------------------------------------------------
|
|
// Exit
|
|
// ---------------------------------------------------------
|
|
|
|
fprintf(stderr, "Bye.\n");
|
|
mpsse_close();
|
|
return 0;
|
|
}
|