From: Greg Davill Date: Sun, 19 Apr 2020 04:00:19 +0000 (+0930) Subject: move project into folder X-Git-Url: https://git.libre-soc.org/?a=commitdiff_plain;h=d628b4c1977125e65565a6b59ba10fd8605c2c96;p=ecpprog.git move project into folder --- diff --git a/.gitignore b/.gitignore index 5141302..60aca84 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,4 @@ -ecpprog +ecpprog/ecpprog *.exe *.o *.d diff --git a/Makefile b/Makefile deleted file mode 100644 index 611d39d..0000000 --- a/Makefile +++ /dev/null @@ -1,71 +0,0 @@ -PREFIX ?= /usr/local -DEBUG ?= 0 -ICEPROG ?= 1 -PROGRAM_PREFIX ?= - -CXX ?= clang++ -CC ?= clang -PKG_CONFIG ?= pkg-config - -C_STD ?= c99 -CXX_STD ?= c++11 -ifeq ($(DEBUG),1) -OPT_LEVEL ?= 0 -DBG_LEVEL ?= -ggdb -else -OPT_LEVEL ?= 2 -DBG_LEVEL ?= -endif -WARN_LEVEL ?= all - -LDLIBS = -lm -lstdc++ -CFLAGS += -MD -O$(OPT_LEVEL) $(DBG_LEVEL) -W$(WARN_LEVEL) -std=$(C_STD) -I$(PREFIX)/include -CXXFLAGS += -MD -O$(OPT_LEVEL) $(DBG_LEVEL) -W$(WARN_LEVEL) -std=$(CXX_STD) -I$(PREFIX)/include - -DESTDIR ?= -CHIPDB_SUBDIR ?= $(PROGRAM_PREFIX)icebox - -ifeq ($(MXE),1) -EXE = .exe -CXX = /usr/local/src/mxe/usr/bin/i686-w64-mingw32.static-gcc -CC = $(CXX) -PKG_CONFIG = /usr/local/src/mxe/usr/bin/i686-w64-mingw32.static-pkg-config -endif - -ifneq ($(shell uname -s),Darwin) - LDLIBS = -L/usr/local/lib -lm -else - LIBFTDI_NAME = $(shell $(PKG_CONFIG) --exists libftdi1 && echo ftdi1 || echo ftdi) - LDLIBS = -L/usr/local/lib -l$(LIBFTDI_NAME) -lm -endif - -ifeq ($(STATIC),1) -LDFLAGS += -static -LDLIBS += $(shell for pkg in libftdi1 libftdi; do $(PKG_CONFIG) --silence-errors --static --libs $$pkg && exit; done; echo -lftdi; ) -CFLAGS += $(shell for pkg in libftdi1 libftdi; do $(PKG_CONFIG) --silence-errors --static --cflags $$pkg && exit; done; ) -else -LDLIBS += $(shell for pkg in libftdi1 libftdi; do $(PKG_CONFIG) --silence-errors --libs $$pkg && exit; done; echo -lftdi; ) -CFLAGS += $(shell for pkg in libftdi1 libftdi; do $(PKG_CONFIG) --silence-errors --cflags $$pkg && exit; done; ) -endif - -all: $(PROGRAM_PREFIX)ecpprog$(EXE) - -$(PROGRAM_PREFIX)ecpprog$(EXE): ecpprog.o mpsse.o jtag_tap.o - $(CC) -o $@ $(LDFLAGS) $^ $(LDLIBS) - -install: all - mkdir -p $(DESTDIR)$(PREFIX)/bin - cp $(PROGRAM_PREFIX)ecpprog$(EXE) $(DESTDIR)$(PREFIX)/bin/$(PROGRAM_PREFIX)ecpprog$(EXE) - -uninstall: - rm -f $(DESTDIR)$(PREFIX)/bin/$(PROGRAM_PREFIX)ecpprog$(EXE) - -clean: - rm -f $(PROGRAM_PREFIX)ecpprog - rm -f $(PROGRAM_PREFIX)ecpprog.exe - rm -f *.o *.d - --include *.d - -.PHONY: all install uninstall clean - diff --git a/ecpprog.c b/ecpprog.c deleted file mode 100644 index e058a38..0000000 --- a/ecpprog.c +++ /dev/null @@ -1,1075 +0,0 @@ -/* - * ecpprog -- simple programming tool for FTDI-based JTAG programmers - * Based on iceprog - * - * Copyright (C) 2015 Clifford Wolf - * Copyright (C) 2018 Piotr Esden-Tempski - * Copyright (C) 2020 Gregory Davill - * - * 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 -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#ifdef _WIN32 -#include /* _setmode() */ -#include /* _O_BINARY */ -#endif - -#include "jtag.h" -#include "lattice_cmds.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 */ -}; - - -// --------------------------------------------------------- -// JTAG -> SPI functions -// --------------------------------------------------------- - -/* - * JTAG performrs all shifts LSB first, our FLSAH is expeting bytes MSB first, - * There are a few ways to fix this, for now we just bit-reverse all the input data to the JTAG core - */ -uint8_t bit_reverse(uint8_t in){ - - uint8_t out = (in & 0x01) ? 0x80 : 0x00; - out |= (in & 0x02) ? 0x40 : 0x00; - out |= (in & 0x04) ? 0x20 : 0x00; - out |= (in & 0x08) ? 0x10 : 0x00; - out |= (in & 0x10) ? 0x08 : 0x00; - out |= (in & 0x20) ? 0x04 : 0x00; - out |= (in & 0x40) ? 0x02 : 0x00; - out |= (in & 0x80) ? 0x01 : 0x00; - - return out; -} - -void xfer_spi(uint8_t* data, uint32_t len){ - /* Reverse bit order of all bytes */ - for(int i = 0; i < len; i++){ - data[i] = bit_reverse(data[i]); - } - - /* Don't switch states if we're already in SHIFT-DR */ - if(jtag_current_state() != STATE_SHIFT_DR) - jtag_go_to_state(STATE_SHIFT_DR); - jtag_tap_shift(data, data, len * 8, true); - - /* Reverse bit order of all return bytes */ - for(int i = 0; i < len; i++){ - data[i] = bit_reverse(data[i]); - } -} - -void send_spi(uint8_t* data, uint32_t len){ - uint8_t unused[len]; - - /* Flip bit order of all bytes */ - for(int i = 0; i < len; i++){ - data[i] = bit_reverse(data[i]); - } - - jtag_go_to_state(STATE_SHIFT_DR); - /* Stay in SHIFT-DR state, this keep CS low */ - jtag_tap_shift(data, unused, len * 8, false); -} - -// --------------------------------------------------------- -// FLASH function implementations -// --------------------------------------------------------- - -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"); - - // Write command and read first 4 bytes - 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]; - data[0] = FC_JEDECID; - xfer_spi(data, len); - } - } - - fprintf(stderr, "flash ID:"); - for (int i = 1; i < len; i++) - fprintf(stderr, " 0x%02X", data[i]); - fprintf(stderr, "\n"); -} - -static void flash_reset() -{ - uint8_t data[8] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; - xfer_spi(data, 8); -} - -static uint8_t flash_read_status() -{ - uint8_t data[2] = { FC_RSR1 }; - - xfer_spi(data, 2); - - 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"); - } - - 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 }; - xfer_spi(data, 1); - - 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 }; - xfer_spi(data, 1); -} - -static void flash_4kB_sector_erase(int addr) -{ - fprintf(stderr, "erase 4kB sector at 0x%06X..\n", addr); - - uint8_t command[4] = { FC_SE, (uint8_t)(addr >> 16), (uint8_t)(addr >> 8), (uint8_t)addr }; - - xfer_spi(command, 4); -} - -static void flash_32kB_sector_erase(int addr) -{ - fprintf(stderr, "erase 64kB sector at 0x%06X..\n", addr); - - uint8_t command[4] = { FC_BE32, (uint8_t)(addr >> 16), (uint8_t)(addr >> 8), (uint8_t)addr }; - - xfer_spi(command, 4); -} - -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 }; - - xfer_spi(command, 4); -} - -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 }; - - send_spi(command, 4); - xfer_spi(data, n); - - 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 }; - - send_spi(command, 4); - memset(data, 0, n); - xfer_spi(data, n); - - 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 }; - - xfer_spi(data, 2); - - 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 }; - xfer_spi(data, 2); - - flash_wait(); - - // Read Status Register 1 - data[0] = FC_RSR1; - - xfer_spi(data, 2); - - if (data[1] != 0x00) - fprintf(stderr, "failed to disable protection, SR now equal to 0x%02x (expected 0x00)\n", data[1]); - -} - -// --------------------------------------------------------- -// ECP5 specific JTAG functions -// --------------------------------------------------------- - - -static void print_idcode(uint32_t idcode){ - for(int i = 0; i < sizeof(ecp_devices)/sizeof(struct ecp_device_id); i++){ - if(idcode == ecp_devices[i].device_id) - { - printf("IDCODE: 0x%08x (%s)\n", idcode ,ecp_devices[i].device_name); - return; - } - } - printf("IDCODE: 0x%08x does not match :(\n", idcode); -} - -static void read_idcode(){ - - uint8_t data[4] = {READ_ID}; - - jtag_go_to_state(STATE_SHIFT_IR); - jtag_tap_shift(data, data, 8, true); - - data[0] = 0; - jtag_go_to_state(STATE_SHIFT_DR); - jtag_tap_shift(data, data, 32, true); - - uint32_t idcode = 0; - - /* Format the IDCODE into a 32bit value */ - for(int i = 0; i< 4; i++) - idcode = data[i] << 24 | idcode >> 8; - - print_idcode(idcode); -} - - -static void print_status_register(uint32_t status){ - printf("ECP5 Status Register: 0x%08x\n", status); - - if(verbose){ - printf(" Transparent Mode: %s\n", status & (1 << 0) ? "Yes" : "No" ); - printf(" Config Target: %s\n", status & (7 << 1) ? "eFuse" : "SRAM" ); - printf(" JTAG Active: %s\n", status & (1 << 4) ? "Yes" : "No" ); - printf(" PWD Protection: %s\n", status & (1 << 5) ? "Yes" : "No" ); - printf(" Decrypt Enable: %s\n", status & (1 << 7) ? "Yes" : "No" ); - printf(" DONE: %s\n", status & (1 << 8) ? "Yes" : "No" ); - printf(" ISC Enable: %s\n", status & (1 << 9) ? "Yes" : "No" ); - printf(" Write Enable: %s\n", status & (1 << 10) ? "Writable" : "Not Writable"); - printf(" Read Enable: %s\n", status & (1 << 11) ? "Readable" : "Not Readable"); - printf(" Busy Flag: %s\n", status & (1 << 12) ? "Yes" : "No" ); - printf(" Fail Flag: %s\n", status & (1 << 13) ? "Yes" : "No" ); - printf(" Feature OTP: %s\n", status & (1 << 14) ? "Yes" : "No" ); - printf(" Decrypt Only: %s\n", status & (1 << 15) ? "Yes" : "No" ); - printf(" PWD Enable: %s\n", status & (1 << 16) ? "Yes" : "No" ); - printf(" Encrypt Preamble: %s\n", status & (1 << 20) ? "Yes" : "No" ); - printf(" Std Preamble: %s\n", status & (1 << 21) ? "Yes" : "No" ); - printf(" SPIm Fail 1: %s\n", status & (1 << 22) ? "Yes" : "No" ); - - uint8_t bse_error = (status & (7 << 23)) >> 23; - switch (bse_error){ - case 0b000: printf(" BSE Error Code: No Error (0b000)\n"); break; - case 0b001: printf(" BSE Error Code: ID Error (0b001)\n"); break; - case 0b010: printf(" BSE Error Code: CMD Error - illegal command (0b010)\n"); break; - case 0b011: printf(" BSE Error Code: CRC Error (0b011)\n"); break; - case 0b100: printf(" BSE Error Code: PRMB Error - preamble error (0b100)\n"); break; - case 0b101: printf(" BSE Error Code: ABRT Error - configuration aborted by the user (0b101)\n"); break; - case 0b110: printf(" BSE Error Code: OVFL Error - data overflow error (0b110)\n"); break; - case 0b111: printf(" BSE Error Code: SDM Error - bitstream pass the size of SRAM array (0b111)\n"); break; - } - - printf(" Execution Error: %s\n", status & (1 << 26) ? "Yes" : "No" ); - printf(" ID Error: %s\n", status & (1 << 27) ? "Yes" : "No" ); - printf(" Invalid Command: %s\n", status & (1 << 28) ? "Yes" : "No" ); - printf(" SED Error: %s\n", status & (1 << 29) ? "Yes" : "No" ); - printf(" Bypass Mode: %s\n", status & (1 << 30) ? "Yes" : "No" ); - printf(" Flow Through Mode: %s\n", status & (1 << 31) ? "Yes" : "No" ); - } -} - - -static void read_status_register(){ - - uint8_t data[4] = {LSC_READ_STATUS}; - - jtag_go_to_state(STATE_SHIFT_IR); - jtag_tap_shift(data, data, 8, true); - - data[0] = 0; - jtag_go_to_state(STATE_SHIFT_DR); - jtag_tap_shift(data, data, 32, true); - - uint32_t status = 0; - - /* Format the IDCODE into a 32bit value */ - for(int i = 0; i< 4; i++) - status = data[i] << 24 | status >> 8; - - print_status_register(status); -} - - - -static void enter_spi_background_mode(){ - - uint8_t data_in[4] = {0,0,0,0}; - uint8_t data_out[4] = {0,0,0,0}; - - data_in[0] = 0x3A; - jtag_go_to_state(STATE_SHIFT_IR); - jtag_tap_shift(data_in, data_out, 8, true); - - /* These bytes seem to be required to un-lock the SPI interface */ - data_in[0] = 0xFE; - data_in[1] = 0x68; - jtag_go_to_state(STATE_SHIFT_DR); - jtag_tap_shift(data_in, data_out, 16, true); - - /* Entering IDLE is essential */ - jtag_go_to_state(STATE_RUN_TEST_IDLE); -} - - -void ecp_jtag_cmd(uint8_t cmd){ - uint8_t data[1] = {cmd}; - - jtag_go_to_state(STATE_SHIFT_IR); - jtag_tap_shift(data, data, 8, true); - - jtag_go_to_state(STATE_RUN_TEST_IDLE); - jtag_wait_time(10); -} - -// --------------------------------------------------------- -// iceprog implementation -// --------------------------------------------------------- - -static void help(const char *progname) -{ - fprintf(stderr, "Simple programming tool for FTDI-based Lattice ECP JTAG programmers.\n"); - fprintf(stderr, "Usage: %s [-b|-n|-c] \n", progname); - fprintf(stderr, " %s -r|-R \n", progname); - fprintf(stderr, " %s -S \n", progname); - fprintf(stderr, " %s -t\n", progname); - fprintf(stderr, "\n"); - fprintf(stderr, "General options:\n"); - fprintf(stderr, " -d use the specified USB device [default: i:0x0403:0x6010 or i:0x0403:0x6014]\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, " -I [ABCD] connect to the specified interface on the FTDI chip\n"); - fprintf(stderr, " [default: A]\n"); - fprintf(stderr, " -o 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, " -i [4,32,64] select erase block size [default: 64k]\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 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 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, "If you have a bug report, please file an issue on github:\n"); - fprintf(stderr, " https://github.com/gregdavill/ecpprog/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_block_size = 64; - 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; - -#ifdef _WIN32 - _setmode(_fileno(stdin), _O_BINARY); - _setmode(_fileno(stdout), _O_BINARY); -#endif - - 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:I:rR:e:o:cbnStvspX", long_options, NULL)) != -1) { - switch (opt) { - case 'd': /* device string */ - devstr = optarg; - break; - case 'i': /* block erase size */ - if (!strcmp(optarg, "4")) - erase_block_size = 4; - else if (!strcmp(optarg, "32")) - erase_block_size = 32; - else if (!strcmp(optarg, "64")) - erase_block_size = 64; - else { - fprintf(stderr, "%s: `%s' is not a valid erase block size (must be `4', `32' or `64')\n", my_name, optarg); - return EXIT_FAILURE; - } - 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.."); - jtag_init(ifnum, devstr, slow_clock); - - fprintf(stderr, "idcode..\n"); - read_idcode(); - - fprintf(stderr, "status..\n"); - read_status_register(); - - - /* Reset ECP5 to release SPI interface */ - ecp_jtag_cmd(ISC_ENABLE); - ecp_jtag_cmd(ISC_ERASE); - ecp_jtag_cmd(ISC_DISABLE); - - /* Put device into SPI bypass mode */ - enter_spi_background_mode(); - //usleep(20000); - - if (test_mode) - { - - - flash_reset(); - flash_read_id(); - } - else if (prog_sram) - { - // --------------------------------------------------------- - // Reset - // --------------------------------------------------------- - - fprintf(stderr, "Not Supported yet\n"); - fprintf(stderr, "reset..\n"); - - //sram_reset(); - usleep(100); - - //sram_chip_select(); - usleep(2000); - - - // --------------------------------------------------------- - // 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(); - - - } - else /* program flash */ - { - // --------------------------------------------------------- - // Reset - // --------------------------------------------------------- - - fprintf(stderr, "reset..\n"); - - //flash_chip_deselect(); - usleep(250000); - - - - flash_reset(); - - 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 block_size = erase_block_size << 10; - int block_mask = block_size - 1; - int begin_addr = rw_offset & ~block_mask; - int end_addr = (rw_offset + file_size + block_mask) & ~block_mask; - - for (int addr = begin_addr; addr < end_addr; addr += block_size) { - flash_write_enable(); - switch(erase_block_size) { - case 4: - flash_4kB_sector_erase(addr); - break; - case 32: - flash_32kB_sector_erase(addr); - break; - case 64: - flash_64kB_sector_erase(addr); - break; - } - 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"); - jtag_error(3); - } - } - - fprintf(stderr, "VERIFY OK\n"); - } - } - - if (f != NULL && f != stdin && f != stdout) - fclose(f); - - // --------------------------------------------------------- - // Exit - // --------------------------------------------------------- - - fprintf(stderr, "Bye.\n"); - jtag_deinit(); - return 0; -} diff --git a/ecpprog/Makefile b/ecpprog/Makefile new file mode 100644 index 0000000..611d39d --- /dev/null +++ b/ecpprog/Makefile @@ -0,0 +1,71 @@ +PREFIX ?= /usr/local +DEBUG ?= 0 +ICEPROG ?= 1 +PROGRAM_PREFIX ?= + +CXX ?= clang++ +CC ?= clang +PKG_CONFIG ?= pkg-config + +C_STD ?= c99 +CXX_STD ?= c++11 +ifeq ($(DEBUG),1) +OPT_LEVEL ?= 0 +DBG_LEVEL ?= -ggdb +else +OPT_LEVEL ?= 2 +DBG_LEVEL ?= +endif +WARN_LEVEL ?= all + +LDLIBS = -lm -lstdc++ +CFLAGS += -MD -O$(OPT_LEVEL) $(DBG_LEVEL) -W$(WARN_LEVEL) -std=$(C_STD) -I$(PREFIX)/include +CXXFLAGS += -MD -O$(OPT_LEVEL) $(DBG_LEVEL) -W$(WARN_LEVEL) -std=$(CXX_STD) -I$(PREFIX)/include + +DESTDIR ?= +CHIPDB_SUBDIR ?= $(PROGRAM_PREFIX)icebox + +ifeq ($(MXE),1) +EXE = .exe +CXX = /usr/local/src/mxe/usr/bin/i686-w64-mingw32.static-gcc +CC = $(CXX) +PKG_CONFIG = /usr/local/src/mxe/usr/bin/i686-w64-mingw32.static-pkg-config +endif + +ifneq ($(shell uname -s),Darwin) + LDLIBS = -L/usr/local/lib -lm +else + LIBFTDI_NAME = $(shell $(PKG_CONFIG) --exists libftdi1 && echo ftdi1 || echo ftdi) + LDLIBS = -L/usr/local/lib -l$(LIBFTDI_NAME) -lm +endif + +ifeq ($(STATIC),1) +LDFLAGS += -static +LDLIBS += $(shell for pkg in libftdi1 libftdi; do $(PKG_CONFIG) --silence-errors --static --libs $$pkg && exit; done; echo -lftdi; ) +CFLAGS += $(shell for pkg in libftdi1 libftdi; do $(PKG_CONFIG) --silence-errors --static --cflags $$pkg && exit; done; ) +else +LDLIBS += $(shell for pkg in libftdi1 libftdi; do $(PKG_CONFIG) --silence-errors --libs $$pkg && exit; done; echo -lftdi; ) +CFLAGS += $(shell for pkg in libftdi1 libftdi; do $(PKG_CONFIG) --silence-errors --cflags $$pkg && exit; done; ) +endif + +all: $(PROGRAM_PREFIX)ecpprog$(EXE) + +$(PROGRAM_PREFIX)ecpprog$(EXE): ecpprog.o mpsse.o jtag_tap.o + $(CC) -o $@ $(LDFLAGS) $^ $(LDLIBS) + +install: all + mkdir -p $(DESTDIR)$(PREFIX)/bin + cp $(PROGRAM_PREFIX)ecpprog$(EXE) $(DESTDIR)$(PREFIX)/bin/$(PROGRAM_PREFIX)ecpprog$(EXE) + +uninstall: + rm -f $(DESTDIR)$(PREFIX)/bin/$(PROGRAM_PREFIX)ecpprog$(EXE) + +clean: + rm -f $(PROGRAM_PREFIX)ecpprog + rm -f $(PROGRAM_PREFIX)ecpprog.exe + rm -f *.o *.d + +-include *.d + +.PHONY: all install uninstall clean + diff --git a/ecpprog/ecpprog.c b/ecpprog/ecpprog.c new file mode 100644 index 0000000..e058a38 --- /dev/null +++ b/ecpprog/ecpprog.c @@ -0,0 +1,1075 @@ +/* + * ecpprog -- simple programming tool for FTDI-based JTAG programmers + * Based on iceprog + * + * Copyright (C) 2015 Clifford Wolf + * Copyright (C) 2018 Piotr Esden-Tempski + * Copyright (C) 2020 Gregory Davill + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef _WIN32 +#include /* _setmode() */ +#include /* _O_BINARY */ +#endif + +#include "jtag.h" +#include "lattice_cmds.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 */ +}; + + +// --------------------------------------------------------- +// JTAG -> SPI functions +// --------------------------------------------------------- + +/* + * JTAG performrs all shifts LSB first, our FLSAH is expeting bytes MSB first, + * There are a few ways to fix this, for now we just bit-reverse all the input data to the JTAG core + */ +uint8_t bit_reverse(uint8_t in){ + + uint8_t out = (in & 0x01) ? 0x80 : 0x00; + out |= (in & 0x02) ? 0x40 : 0x00; + out |= (in & 0x04) ? 0x20 : 0x00; + out |= (in & 0x08) ? 0x10 : 0x00; + out |= (in & 0x10) ? 0x08 : 0x00; + out |= (in & 0x20) ? 0x04 : 0x00; + out |= (in & 0x40) ? 0x02 : 0x00; + out |= (in & 0x80) ? 0x01 : 0x00; + + return out; +} + +void xfer_spi(uint8_t* data, uint32_t len){ + /* Reverse bit order of all bytes */ + for(int i = 0; i < len; i++){ + data[i] = bit_reverse(data[i]); + } + + /* Don't switch states if we're already in SHIFT-DR */ + if(jtag_current_state() != STATE_SHIFT_DR) + jtag_go_to_state(STATE_SHIFT_DR); + jtag_tap_shift(data, data, len * 8, true); + + /* Reverse bit order of all return bytes */ + for(int i = 0; i < len; i++){ + data[i] = bit_reverse(data[i]); + } +} + +void send_spi(uint8_t* data, uint32_t len){ + uint8_t unused[len]; + + /* Flip bit order of all bytes */ + for(int i = 0; i < len; i++){ + data[i] = bit_reverse(data[i]); + } + + jtag_go_to_state(STATE_SHIFT_DR); + /* Stay in SHIFT-DR state, this keep CS low */ + jtag_tap_shift(data, unused, len * 8, false); +} + +// --------------------------------------------------------- +// FLASH function implementations +// --------------------------------------------------------- + +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"); + + // Write command and read first 4 bytes + 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]; + data[0] = FC_JEDECID; + xfer_spi(data, len); + } + } + + fprintf(stderr, "flash ID:"); + for (int i = 1; i < len; i++) + fprintf(stderr, " 0x%02X", data[i]); + fprintf(stderr, "\n"); +} + +static void flash_reset() +{ + uint8_t data[8] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; + xfer_spi(data, 8); +} + +static uint8_t flash_read_status() +{ + uint8_t data[2] = { FC_RSR1 }; + + xfer_spi(data, 2); + + 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"); + } + + 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 }; + xfer_spi(data, 1); + + 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 }; + xfer_spi(data, 1); +} + +static void flash_4kB_sector_erase(int addr) +{ + fprintf(stderr, "erase 4kB sector at 0x%06X..\n", addr); + + uint8_t command[4] = { FC_SE, (uint8_t)(addr >> 16), (uint8_t)(addr >> 8), (uint8_t)addr }; + + xfer_spi(command, 4); +} + +static void flash_32kB_sector_erase(int addr) +{ + fprintf(stderr, "erase 64kB sector at 0x%06X..\n", addr); + + uint8_t command[4] = { FC_BE32, (uint8_t)(addr >> 16), (uint8_t)(addr >> 8), (uint8_t)addr }; + + xfer_spi(command, 4); +} + +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 }; + + xfer_spi(command, 4); +} + +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 }; + + send_spi(command, 4); + xfer_spi(data, n); + + 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 }; + + send_spi(command, 4); + memset(data, 0, n); + xfer_spi(data, n); + + 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 }; + + xfer_spi(data, 2); + + 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 }; + xfer_spi(data, 2); + + flash_wait(); + + // Read Status Register 1 + data[0] = FC_RSR1; + + xfer_spi(data, 2); + + if (data[1] != 0x00) + fprintf(stderr, "failed to disable protection, SR now equal to 0x%02x (expected 0x00)\n", data[1]); + +} + +// --------------------------------------------------------- +// ECP5 specific JTAG functions +// --------------------------------------------------------- + + +static void print_idcode(uint32_t idcode){ + for(int i = 0; i < sizeof(ecp_devices)/sizeof(struct ecp_device_id); i++){ + if(idcode == ecp_devices[i].device_id) + { + printf("IDCODE: 0x%08x (%s)\n", idcode ,ecp_devices[i].device_name); + return; + } + } + printf("IDCODE: 0x%08x does not match :(\n", idcode); +} + +static void read_idcode(){ + + uint8_t data[4] = {READ_ID}; + + jtag_go_to_state(STATE_SHIFT_IR); + jtag_tap_shift(data, data, 8, true); + + data[0] = 0; + jtag_go_to_state(STATE_SHIFT_DR); + jtag_tap_shift(data, data, 32, true); + + uint32_t idcode = 0; + + /* Format the IDCODE into a 32bit value */ + for(int i = 0; i< 4; i++) + idcode = data[i] << 24 | idcode >> 8; + + print_idcode(idcode); +} + + +static void print_status_register(uint32_t status){ + printf("ECP5 Status Register: 0x%08x\n", status); + + if(verbose){ + printf(" Transparent Mode: %s\n", status & (1 << 0) ? "Yes" : "No" ); + printf(" Config Target: %s\n", status & (7 << 1) ? "eFuse" : "SRAM" ); + printf(" JTAG Active: %s\n", status & (1 << 4) ? "Yes" : "No" ); + printf(" PWD Protection: %s\n", status & (1 << 5) ? "Yes" : "No" ); + printf(" Decrypt Enable: %s\n", status & (1 << 7) ? "Yes" : "No" ); + printf(" DONE: %s\n", status & (1 << 8) ? "Yes" : "No" ); + printf(" ISC Enable: %s\n", status & (1 << 9) ? "Yes" : "No" ); + printf(" Write Enable: %s\n", status & (1 << 10) ? "Writable" : "Not Writable"); + printf(" Read Enable: %s\n", status & (1 << 11) ? "Readable" : "Not Readable"); + printf(" Busy Flag: %s\n", status & (1 << 12) ? "Yes" : "No" ); + printf(" Fail Flag: %s\n", status & (1 << 13) ? "Yes" : "No" ); + printf(" Feature OTP: %s\n", status & (1 << 14) ? "Yes" : "No" ); + printf(" Decrypt Only: %s\n", status & (1 << 15) ? "Yes" : "No" ); + printf(" PWD Enable: %s\n", status & (1 << 16) ? "Yes" : "No" ); + printf(" Encrypt Preamble: %s\n", status & (1 << 20) ? "Yes" : "No" ); + printf(" Std Preamble: %s\n", status & (1 << 21) ? "Yes" : "No" ); + printf(" SPIm Fail 1: %s\n", status & (1 << 22) ? "Yes" : "No" ); + + uint8_t bse_error = (status & (7 << 23)) >> 23; + switch (bse_error){ + case 0b000: printf(" BSE Error Code: No Error (0b000)\n"); break; + case 0b001: printf(" BSE Error Code: ID Error (0b001)\n"); break; + case 0b010: printf(" BSE Error Code: CMD Error - illegal command (0b010)\n"); break; + case 0b011: printf(" BSE Error Code: CRC Error (0b011)\n"); break; + case 0b100: printf(" BSE Error Code: PRMB Error - preamble error (0b100)\n"); break; + case 0b101: printf(" BSE Error Code: ABRT Error - configuration aborted by the user (0b101)\n"); break; + case 0b110: printf(" BSE Error Code: OVFL Error - data overflow error (0b110)\n"); break; + case 0b111: printf(" BSE Error Code: SDM Error - bitstream pass the size of SRAM array (0b111)\n"); break; + } + + printf(" Execution Error: %s\n", status & (1 << 26) ? "Yes" : "No" ); + printf(" ID Error: %s\n", status & (1 << 27) ? "Yes" : "No" ); + printf(" Invalid Command: %s\n", status & (1 << 28) ? "Yes" : "No" ); + printf(" SED Error: %s\n", status & (1 << 29) ? "Yes" : "No" ); + printf(" Bypass Mode: %s\n", status & (1 << 30) ? "Yes" : "No" ); + printf(" Flow Through Mode: %s\n", status & (1 << 31) ? "Yes" : "No" ); + } +} + + +static void read_status_register(){ + + uint8_t data[4] = {LSC_READ_STATUS}; + + jtag_go_to_state(STATE_SHIFT_IR); + jtag_tap_shift(data, data, 8, true); + + data[0] = 0; + jtag_go_to_state(STATE_SHIFT_DR); + jtag_tap_shift(data, data, 32, true); + + uint32_t status = 0; + + /* Format the IDCODE into a 32bit value */ + for(int i = 0; i< 4; i++) + status = data[i] << 24 | status >> 8; + + print_status_register(status); +} + + + +static void enter_spi_background_mode(){ + + uint8_t data_in[4] = {0,0,0,0}; + uint8_t data_out[4] = {0,0,0,0}; + + data_in[0] = 0x3A; + jtag_go_to_state(STATE_SHIFT_IR); + jtag_tap_shift(data_in, data_out, 8, true); + + /* These bytes seem to be required to un-lock the SPI interface */ + data_in[0] = 0xFE; + data_in[1] = 0x68; + jtag_go_to_state(STATE_SHIFT_DR); + jtag_tap_shift(data_in, data_out, 16, true); + + /* Entering IDLE is essential */ + jtag_go_to_state(STATE_RUN_TEST_IDLE); +} + + +void ecp_jtag_cmd(uint8_t cmd){ + uint8_t data[1] = {cmd}; + + jtag_go_to_state(STATE_SHIFT_IR); + jtag_tap_shift(data, data, 8, true); + + jtag_go_to_state(STATE_RUN_TEST_IDLE); + jtag_wait_time(10); +} + +// --------------------------------------------------------- +// iceprog implementation +// --------------------------------------------------------- + +static void help(const char *progname) +{ + fprintf(stderr, "Simple programming tool for FTDI-based Lattice ECP JTAG programmers.\n"); + fprintf(stderr, "Usage: %s [-b|-n|-c] \n", progname); + fprintf(stderr, " %s -r|-R \n", progname); + fprintf(stderr, " %s -S \n", progname); + fprintf(stderr, " %s -t\n", progname); + fprintf(stderr, "\n"); + fprintf(stderr, "General options:\n"); + fprintf(stderr, " -d use the specified USB device [default: i:0x0403:0x6010 or i:0x0403:0x6014]\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, " -I [ABCD] connect to the specified interface on the FTDI chip\n"); + fprintf(stderr, " [default: A]\n"); + fprintf(stderr, " -o 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, " -i [4,32,64] select erase block size [default: 64k]\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 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 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, "If you have a bug report, please file an issue on github:\n"); + fprintf(stderr, " https://github.com/gregdavill/ecpprog/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_block_size = 64; + 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; + +#ifdef _WIN32 + _setmode(_fileno(stdin), _O_BINARY); + _setmode(_fileno(stdout), _O_BINARY); +#endif + + 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:I:rR:e:o:cbnStvspX", long_options, NULL)) != -1) { + switch (opt) { + case 'd': /* device string */ + devstr = optarg; + break; + case 'i': /* block erase size */ + if (!strcmp(optarg, "4")) + erase_block_size = 4; + else if (!strcmp(optarg, "32")) + erase_block_size = 32; + else if (!strcmp(optarg, "64")) + erase_block_size = 64; + else { + fprintf(stderr, "%s: `%s' is not a valid erase block size (must be `4', `32' or `64')\n", my_name, optarg); + return EXIT_FAILURE; + } + 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.."); + jtag_init(ifnum, devstr, slow_clock); + + fprintf(stderr, "idcode..\n"); + read_idcode(); + + fprintf(stderr, "status..\n"); + read_status_register(); + + + /* Reset ECP5 to release SPI interface */ + ecp_jtag_cmd(ISC_ENABLE); + ecp_jtag_cmd(ISC_ERASE); + ecp_jtag_cmd(ISC_DISABLE); + + /* Put device into SPI bypass mode */ + enter_spi_background_mode(); + //usleep(20000); + + if (test_mode) + { + + + flash_reset(); + flash_read_id(); + } + else if (prog_sram) + { + // --------------------------------------------------------- + // Reset + // --------------------------------------------------------- + + fprintf(stderr, "Not Supported yet\n"); + fprintf(stderr, "reset..\n"); + + //sram_reset(); + usleep(100); + + //sram_chip_select(); + usleep(2000); + + + // --------------------------------------------------------- + // 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(); + + + } + else /* program flash */ + { + // --------------------------------------------------------- + // Reset + // --------------------------------------------------------- + + fprintf(stderr, "reset..\n"); + + //flash_chip_deselect(); + usleep(250000); + + + + flash_reset(); + + 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 block_size = erase_block_size << 10; + int block_mask = block_size - 1; + int begin_addr = rw_offset & ~block_mask; + int end_addr = (rw_offset + file_size + block_mask) & ~block_mask; + + for (int addr = begin_addr; addr < end_addr; addr += block_size) { + flash_write_enable(); + switch(erase_block_size) { + case 4: + flash_4kB_sector_erase(addr); + break; + case 32: + flash_32kB_sector_erase(addr); + break; + case 64: + flash_64kB_sector_erase(addr); + break; + } + 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"); + jtag_error(3); + } + } + + fprintf(stderr, "VERIFY OK\n"); + } + } + + if (f != NULL && f != stdin && f != stdout) + fclose(f); + + // --------------------------------------------------------- + // Exit + // --------------------------------------------------------- + + fprintf(stderr, "Bye.\n"); + jtag_deinit(); + return 0; +} diff --git a/ecpprog/jtag.h b/ecpprog/jtag.h new file mode 100644 index 0000000..51caa9d --- /dev/null +++ b/ecpprog/jtag.h @@ -0,0 +1,69 @@ +/* + * Code for interacting with the FPGA via JTAG. + * This file is part of LUNA. + * + * This JTAG driver is intended to be as simple as possible in order to facilitate + * configuration and debugging of the attached FPGA. It is not intended to be a general- + * purpose JTAG link. + */ + +#ifndef __JTAG_H__ +#define __JTAG_H__ + +typedef enum e_TAPState +{ + STATE_TEST_LOGIC_RESET = 0, + STATE_RUN_TEST_IDLE = 1, + STATE_SELECT_DR_SCAN = 2, + STATE_CAPTURE_DR = 3, + STATE_SHIFT_DR = 4, + STATE_EXIT1_DR = 5, + STATE_PAUSE_DR = 6, + STATE_EXIT2_DR = 7, + STATE_UPDATE_DR = 8, + STATE_SELECT_IR_SCAN = 9, + STATE_CAPTURE_IR = 10, + STATE_SHIFT_IR = 11, + STATE_EXIT1_IR = 12, + STATE_PAUSE_IR = 13, + STATE_EXIT2_IR = 14, + STATE_UPDATE_IR = 15 +} jtag_tap_state_t; + + +/** + * Performs the start-of-day tasks necessary to talk JTAG to our FPGA. + */ +void jtag_init(int ifnum, const char *devstr, bool slow_clock); + + +/** + * De-inits the JTAG connection, so the JTAG chain. is no longer driven. + */ +void jtag_deinit(void); + + +/** + * Moves to a given JTAG state. + */ +void jtag_goto_state(int state); + + +/** + * Performs a raw TAP scan. + */ +void jtag_tap_shift( + uint8_t *input_data, + uint8_t *output_data, + uint32_t data_bits, + bool must_end); + +void jtag_error(int status); + +void jtag_wait_time(uint32_t microseconds); + +void jtag_go_to_state(unsigned state); + +uint8_t jtag_current_state(void); + +#endif diff --git a/ecpprog/jtag_tap.c b/ecpprog/jtag_tap.c new file mode 100644 index 0000000..44781f5 --- /dev/null +++ b/ecpprog/jtag_tap.c @@ -0,0 +1,268 @@ +/** + * Code adapted from Arduino-JTAG; + * portions copyright (c) 2015 Marcelo Roberto Jimenez . + * portions copyright (c) 2019 Katherine J. Temkin + * portions copyright (c) 2019 Great Scott Gadgets + */ + +#include +#include +#include +#include +#include +#include + +#include "mpsse.h" +#include "jtag.h" + +void jtag_state_ack(bool tms); + +/* + * Low nibble : TMS == 0 + * High nibble: TMS == 1 + */ + +#define TMS_T(TMS_HIGH_STATE, TMS_LOW_STATE) (((TMS_HIGH_STATE) << 4) | (TMS_LOW_STATE)) + +static const uint8_t tms_transitions[] = { + /* STATE_TEST_LOGIC_RESET */ TMS_T(STATE_TEST_LOGIC_RESET, STATE_RUN_TEST_IDLE), + /* STATE_RUN_TEST_IDLE */ TMS_T(STATE_SELECT_DR_SCAN, STATE_RUN_TEST_IDLE), + /* STATE_SELECT_DR_SCAN */ TMS_T(STATE_SELECT_IR_SCAN, STATE_CAPTURE_DR), + /* STATE_CAPTURE_DR */ TMS_T(STATE_EXIT1_DR, STATE_SHIFT_DR), + /* STATE_SHIFT_DR */ TMS_T(STATE_EXIT1_DR, STATE_SHIFT_DR), + /* STATE_EXIT1_DR */ TMS_T(STATE_UPDATE_DR, STATE_PAUSE_DR), + /* STATE_PAUSE_DR */ TMS_T(STATE_EXIT2_DR, STATE_PAUSE_DR), + /* STATE_EXIT2_DR */ TMS_T(STATE_UPDATE_DR, STATE_SHIFT_DR), + /* STATE_UPDATE_DR */ TMS_T(STATE_SELECT_DR_SCAN, STATE_RUN_TEST_IDLE), + /* STATE_SELECT_IR_SCAN */ TMS_T(STATE_TEST_LOGIC_RESET, STATE_CAPTURE_IR), + /* STATE_CAPTURE_IR */ TMS_T(STATE_EXIT1_IR, STATE_SHIFT_IR), + /* STATE_SHIFT_IR */ TMS_T(STATE_EXIT1_IR, STATE_SHIFT_IR), + /* STATE_EXIT1_IR */ TMS_T(STATE_UPDATE_IR, STATE_PAUSE_IR), + /* STATE_PAUSE_IR */ TMS_T(STATE_EXIT2_IR, STATE_PAUSE_IR), + /* STATE_EXIT2_IR */ TMS_T(STATE_UPDATE_IR, STATE_SHIFT_IR), + /* STATE_UPDATE_IR */ TMS_T(STATE_SELECT_DR_SCAN, STATE_RUN_TEST_IDLE), +}; + +#define BITSTR(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P) ( \ + ((uint16_t)(A) << 15) | \ + ((uint16_t)(B) << 14) | \ + ((uint16_t)(C) << 13) | \ + ((uint16_t)(D) << 12) | \ + ((uint16_t)(E) << 11) | \ + ((uint16_t)(F) << 10) | \ + ((uint16_t)(G) << 9) | \ + ((uint16_t)(H) << 8) | \ + ((uint16_t)(I) << 7) | \ + ((uint16_t)(J) << 6) | \ + ((uint16_t)(K) << 5) | \ + ((uint16_t)(L) << 4) | \ + ((uint16_t)(M) << 3) | \ + ((uint16_t)(N) << 2) | \ + ((uint16_t)(O) << 1) | \ + ((uint16_t)(P) << 0) ) + +/* + * The index of this vector is the current state. The i-th bit tells you the + * value TMS must assume in order to go to state "i". + +------------------------------------------------------------------------------------------------------------ +| | || F | E | D | C || B | A | 9 | 8 || 7 | 6 | 5 | 4 || 3 | 2 | 1 | 0 || HEX | +------------------------------------------------------------------------------------------------------------ +| STATE_TEST_LOGIC_RESET | 0 || 0 | 0 | 0 | 0 || 0 | 0 | 0 | 0 || 0 | 0 | 0 | 0 || 0 | 0 | 0 | 1 || 0x0001 | +| STATE_RUN_TEST_IDLE | 1 || 1 | 1 | 1 | 1 || 1 | 1 | 1 | 1 || 1 | 1 | 1 | 1 || 1 | 1 | 0 | 1 || 0xFFFD | +| STATE_SELECT_DR_SCAN | 2 || 1 | 1 | 1 | 1 || 1 | 1 | 1 | 0 || 0 | 0 | 0 | 0 || 0 | x | 1 | 1 || 0xFE03 | +| STATE_CAPTURE_DR | 3 || 1 | 1 | 1 | 1 || 1 | 1 | 1 | 1 || 1 | 1 | 1 | 0 || x | 1 | 1 | 1 || 0xFFE7 | +| STATE_SHIFT_DR | 4 || 1 | 1 | 1 | 1 || 1 | 1 | 1 | 1 || 1 | 1 | 1 | 0 || 1 | 1 | 1 | 1 || 0xFFEF | +| STATE_EXIT1_DR | 5 || 1 | 1 | 1 | 1 || 1 | 1 | 1 | 1 || 0 | 0 | x | 0 || 1 | 1 | 1 | 1 || 0xFF0F | +| STATE_PAUSE_DR | 6 || 1 | 1 | 1 | 1 || 1 | 1 | 1 | 1 || 1 | 0 | 1 | 1 || 1 | 1 | 1 | 1 || 0xFFBF | +| STATE_EXIT2_DR | 7 || 1 | 1 | 1 | 1 || 1 | 1 | 1 | 1 || x | 0 | 0 | 0 || 1 | 1 | 1 | 1 || 0xFF0F | +| STATE_UPDATE_DR | 8 || 1 | 1 | 1 | 1 || 1 | 1 | 1 | x || 1 | 1 | 1 | 1 || 1 | 1 | 0 | 1 || 0xFEFD | +| STATE_SELECT_IR_SCAN | 9 || 0 | 0 | 0 | 0 || 0 | 0 | x | 1 || 1 | 1 | 1 | 1 || 1 | 1 | 1 | 1 || 0x01FF | +| STATE_CAPTURE_IR | A || 1 | 1 | 1 | 1 || 0 | x | 1 | 1 || 1 | 1 | 1 | 1 || 1 | 1 | 1 | 1 || 0xF3FF | +| STATE_SHIFT_IR | B || 1 | 1 | 1 | 1 || 0 | 1 | 1 | 1 || 1 | 1 | 1 | 1 || 1 | 1 | 1 | 1 || 0xF7FF | +| STATE_EXIT1_IR | C || 1 | 0 | 0 | x || 0 | 1 | 1 | 1 || 1 | 1 | 1 | 1 || 1 | 1 | 1 | 1 || 0x87FF | +| STATE_PAUSE_IR | D || 1 | 1 | 0 | 1 || 1 | 1 | 1 | 1 || 1 | 1 | 1 | 1 || 1 | 1 | 1 | 1 || 0xDFFF | +| STATE_EXIT2_IR | E || 1 | x | 0 | 0 || 0 | 1 | 1 | 1 || 1 | 1 | 1 | 1 || 1 | 1 | 1 | 1 || 0x87FF | +| STATE_UPDATE_IR | F || x | 1 | 1 | 1 || 1 | 1 | 1 | 1 || 1 | 1 | 1 | 1 || 1 | 1 | 0 | 1 || 0x7FFD | +------------------------------------------------------------------------------------------------------------ + +*/ +static const uint16_t tms_map[] = { +/* STATE_TEST_LOGIC_RESET */ BITSTR( 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 ), +/* STATE_RUN_TEST_IDLE */ BITSTR( 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1 ), +/* STATE_SELECT_DR_SCAN */ BITSTR( 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1 ), +/* STATE_CAPTURE_DR */ BITSTR( 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1 ), +/* STATE_SHIFT_DR */ BITSTR( 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1 ), +/* STATE_EXIT1_DR */ BITSTR( 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1 ), +/* STATE_PAUSE_DR */ BITSTR( 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1 ), +/* STATE_EXIT2_DR */ BITSTR( 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1 ), +/* STATE_UPDATE_DR */ BITSTR( 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1 ), +/* STATE_SELECT_IR_SCAN */ BITSTR( 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1 ), +/* STATE_CAPTURE_IR */ BITSTR( 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 ), +/* STATE_SHIFT_IR */ BITSTR( 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 ), +/* STATE_EXIT1_IR */ BITSTR( 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 ), +/* STATE_PAUSE_IR */ BITSTR( 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 ), +/* STATE_EXIT2_IR */ BITSTR( 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 ), +/* STATE_UPDATE_IR */ BITSTR( 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1 ), +}; + +static uint8_t current_state; + +uint8_t jtag_current_state(void) +{ + return current_state; +} + +void jtag_set_current_state(uint8_t state) +{ + current_state = state; +} + +void jtag_error(int status){ + mpsse_error(status); +} + +void jtag_deinit(){ + mpsse_close(); +} + +/** + * Performs any start-of-day tasks necessary to talk JTAG to our FPGA. + */ +void jtag_init(int ifnum, const char *devstr, bool slow_clock) +{ + mpsse_init(ifnum, devstr, slow_clock); + + jtag_set_current_state(STATE_TEST_LOGIC_RESET); + jtag_go_to_state(STATE_TEST_LOGIC_RESET); +} + +uint8_t data[32*1024]; +uint8_t* ptr; +uint16_t rx_cnt; + +extern struct ftdi_context mpsse_ftdic; + +static inline void jtag_pulse_clock_and_read_tdo(bool tms, bool tdi) +{ + *ptr++ = MC_DATA_TMS | MC_DATA_IN | MC_DATA_LSB | MC_DATA_BITS; + *ptr++ = 0; + *ptr++ = (tdi ? 0x80 : 0) | (tms ? 0x01 : 0); + rx_cnt++; +} + +static void _jtag_tap_shift( + uint8_t *input_data, + uint8_t *output_data, + uint32_t data_bits, + bool must_end) +{ + + //printf("_jtag_tap_shift(0x%08x,0x%08x,%u,%s);\n",input_data, output_data, data_bits, must_end ? "true" : "false"); + uint32_t bit_count = data_bits; + uint32_t byte_count = (data_bits + 7) / 8; + rx_cnt = 0; + ptr = data; + + for (uint32_t i = 0; i < byte_count; ++i) { + uint8_t byte_out = input_data[i]; + for (int j = 0; j < 8 && bit_count-- > 0; ++j) { + bool tms = false; + if (bit_count == 0 && must_end) { + tms = true; + jtag_state_ack(1); + } + jtag_pulse_clock_and_read_tdo(tms, byte_out & 1); + byte_out >>= 1; + } + } + + mpsse_xfer(data, ptr-data, rx_cnt); + + /* Data out from the FTDI is actually from an internal shift register + * Instead of reconstructing the bitpattern, we can just take every 8th byte.*/ + for(int i = 0; i < rx_cnt/8; i++) + output_data[i] = data[7+i*8]; +} + +#define MIN(a,b) (a < b) ? a : b + +void jtag_tap_shift( + uint8_t *input_data, + uint8_t *output_data, + uint32_t data_bits, + bool must_end) +{ + uint32_t data_bits_sent = 0; + while(data_bits_sent != data_bits){ + + uint32_t _data_bits = MIN(256, data_bits); + bool last = (data_bits_sent + _data_bits) == data_bits; + + _jtag_tap_shift( + input_data + data_bits_sent/8, + output_data + data_bits_sent/8, + _data_bits, + last & must_end + ); + data_bits_sent += _data_bits; + } +} + +void jtag_state_ack(bool tms) +{ + if (tms) { + jtag_set_current_state((tms_transitions[jtag_current_state()] >> 4) & 0xf); + } else { + jtag_set_current_state(tms_transitions[jtag_current_state()] & 0xf); + } +} + +void jtag_go_to_state(unsigned state) +{ + + if (state == STATE_TEST_LOGIC_RESET) { + for (int i = 0; i < 5; ++i) { + jtag_state_ack(true); + } + + uint8_t data[3] = { + MC_DATA_TMS | MC_DATA_LSB | MC_DATA_BITS, + 5 - 1, + 0b11111 + }; + mpsse_xfer(data, 3, 0); + + } else { + while (jtag_current_state() != state) { + uint8_t data[3] = { + MC_DATA_TMS | MC_DATA_LSB | MC_DATA_ICN | MC_DATA_BITS, + 0, + (tms_map[jtag_current_state()] >> state) & 1 + }; + + jtag_state_ack((tms_map[jtag_current_state()] >> state) & 1); + mpsse_xfer(data, 3, 0); + } + } +} + +void jtag_wait_time(uint32_t microseconds) +{ + uint16_t bytes = microseconds / 8; + uint8_t remain = microseconds % 8; + + uint8_t data[3] = { + MC_CLK_N8, + bytes & 0xFF, + (bytes >> 8) & 0xFF + }; + mpsse_xfer(data, 3, 0); + + if(remain){ + data[0] = MC_CLK_N; + data[1] = remain; + mpsse_xfer(data, 2, 0); + } +} + diff --git a/ecpprog/lattice_cmds.h b/ecpprog/lattice_cmds.h new file mode 100644 index 0000000..5e42d6c --- /dev/null +++ b/ecpprog/lattice_cmds.h @@ -0,0 +1,65 @@ + +#include + +/* Not sure if all of these are applicable to the JTAG interface */ +enum lattice_cmd +{ + ISC_NOOP = 0xFF, /* 0 bits - Non-operation */ + READ_ID = 0xE0, /* 24 bits - Read out the 32-bit IDCODE of the device */ + USERCODE = 0xC0, /* 24 bits - Read 32-bit usercode */ + LSC_READ_STATUS = 0x3C, /* 24 bits - Read out internal status */ + LSC_CHECK_BUSY = 0xF0, /* 24 bits - Read 1 bit busy flag to check the command execution status */ + LSC_REFRESH = 0x79, /* 24 bits - Equivalent to toggle PROGRAMN pin */ + ISC_ENABLE = 0xC6, /* 24 bits - Enable the Offline configuration mode */ + ISC_ENABLE_X = 0x74, /* 24 bits - Enable the Transparent configuration mode */ + ISC_DISABLE = 0x26, /* 24 bits - Disable the configuration operation */ + ISC_PROGRAM_USERCODE = 0xC2, /* 24 bits - Write the 32-bit new USERCODE data to USERCODE register */ + ISC_ERASE = 0x0E, /* 24 bits - Bulk erase the memory array base on the access mode and array selection */ + ISC_PROGRAM_DONE = 0x5E, /* 24 bits - Program the DONE bit if the device is in Configuration state. */ + ISC_PROGRAM_SECURITY = 0xCE, /* 24 bits - Program the Security bit if the device is in Configuration state */ + LSC_INIT_ADDRESS = 0x46, /* 24 bits - Initialize the Address Shift Register */ + LSC_WRITE_ADDRESS = 0xB4, /* 24 bits - Write the 16 bit Address Register to move the address quickly */ + LSC_BITSTREAM_BURST = 0x7A, /* 24 bits - Program the device the whole bitstream sent in as the command operand */ + LSC_PROG_INCR_RTI = 0x82, /* 24 bits - Write configuration data to the configuration memory frame at current address and post increment the address, Byte 2~0 of the opcode indicate number of the frames included in the operand field */ + LSC_PROG_INCR_ENC = 0xB6, /* 24 bits - Encrypt the configuration data then write */ + LSC_PROG_INCR_CMP = 0xB8, /* 24 bits - Decompress the configuration data, then write */ + LSC_PROG_INCR_CNE = 0xBA, /* 24 bits - Decompress and Encrypt the configuration data, then write */ + LSC_VERIFY_INCR_RTI = 0x6A, /* 24 bits - Read back the configuration memory frame selected by the address register and post increment the address */ + LSC_PROG_CTRL0 = 0x22, /* 24 bits - Modify the Control Register 0 */ + LSC_READ_CTRL0 = 0x20, /* 24 bits - Read the Control Register 0 */ + LSC_RESET_CRC = 0x3B, /* 24 bits - Reset 16-bit frame CRC register to 0x0000 */ + LSC_READ_CRC = 0x60, /* 24 bits - Read 16-bit frame CRC register content */ + LSC_PROG_SED_CRC = 0xA2, /* 24 bits - Program the calculated 32-bit CRC based on configuration bit values only into overall CRC register */ + LSC_READ_SED_CRC = 0xA4, /* 24 bits - Read the 32-bit SED CRC */ + LSC_PROG_PASSWORD = 0xF1, /* 24 bits - Program 64-bit password into the non-volatile memory (Efuse) */ + LSC_READ_PASSWORD = 0xF2, /* 24 bits - Read out the 64-bit password before activated for verification */ + LSC_SHIFT_PASSWORD = 0xBC, /* 24 bits - Shift in the password to unlock for re-configuration (necessary when password protection feature is active). */ + LSC_PROG_CIPHER_KEY = 0xF3, /* 24 bits - Program the 128-bit cipher key into Efuse */ + LSC_READ_CIPHER_KEY = 0xF4, /* 24 bits - Read out the 128-bit cipher key before activated for verification */ + LSC_PROG_FEATURE = 0xE4, /* 24 bits - Program User Feature, such as Customer ID, I2C Slave Address, Unique ID Header */ + LSC_READ_FEATURE = 0xE7, /* 24 bits - Read User Feature, such as Customer ID, I2C Slave Address, Unique ID Header */ + LSC_PROG_FEABITS = 0xF8, /* 24 bits - Program User Feature Bits, such as CFG port and pin persistence, PWD_EN, PWD_ALL, DEC_ONLY, Feature Row Lock etc. */ + LSC_READ_FEABITS = 0xFB, /* 24 bits - Read User Feature Bits, such as CFH port and pin persistence, PWD_EN, PWD_ALL, DEC_ONLY, Feature Row Lock etc. */ + LSC_PROG_OTP = 0xF9, /* 24 bits - Program OTP bits, to set Memory Sectors One Time Programmable */ + LSC_READ_OTP = 0xFA, /* 24 bits - Read OTP bits setting */ +}; + + +struct ecp_device_id { + const char* device_name; + uint32_t device_id; +}; + +const struct ecp_device_id ecp_devices[] = +{ + {"LFE5U-12" , 0x21111043 }, + {"LFE5U-25" , 0x41111043 }, + {"LFE5U-45" , 0x41112043 }, + {"LFE5U-85" , 0x41113043 }, + {"LFE5UM-25" , 0x01111043 }, + {"LFE5UM-45" , 0x01112043 }, + {"LFE5UM-85" , 0x01113043 }, + {"LFE5UM5G-25", 0x81111043 }, + {"LFE5UM5G-45", 0x81112043 }, + {"LFE5UM5G-85", 0x81113043 } +}; diff --git a/ecpprog/mpsse.c b/ecpprog/mpsse.c new file mode 100644 index 0000000..5833c65 --- /dev/null +++ b/ecpprog/mpsse.c @@ -0,0 +1,247 @@ +/* + * iceprog -- simple programming tool for FTDI-based Lattice iCE programmers + * + * Copyright (C) 2015 Clifford Wolf + * Copyright (C) 2018 Piotr Esden-Tempski + * + * 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.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 "mpsse.h" + +// --------------------------------------------------------- +// MPSSE / FTDI definitions +// --------------------------------------------------------- + +/* FTDI bank pinout typically used for iCE dev boards + * BUS IO | Signal | Control + * -------+--------+-------------- + * xDBUS0 | SCK | MPSSE + * xDBUS1 | MOSI | MPSSE + * xDBUS2 | MISO | MPSSE + * xDBUS3 | nc | + * xDBUS4 | CS | GPIO + * xDBUS5 | nc | + * xDBUS6 | CDONE | GPIO + * xDBUS7 | CRESET | GPIO + */ + +struct ftdi_context mpsse_ftdic; +bool mpsse_ftdic_open = false; +bool mpsse_ftdic_latency_set = false; +unsigned char mpsse_ftdi_latency; + + +// --------------------------------------------------------- +// MPSSE / FTDI function implementations +// --------------------------------------------------------- + +void mpsse_check_rx() +{ + uint8_t cnt = 0; + while (1) { + uint8_t data; + int rc = ftdi_read_data(&mpsse_ftdic, &data, 1); + if (rc <= 0) + break; + fprintf(stderr, "unexpected rx byte: %02X\n", data); + cnt++; + + if(cnt > 32) + break; + } +} + +void mpsse_error(int status) +{ + //mpsse_check_rx(); + fprintf(stderr, "ABORT.\n"); + if (mpsse_ftdic_open) { + if (mpsse_ftdic_latency_set) + ftdi_set_latency_timer(&mpsse_ftdic, mpsse_ftdi_latency); + ftdi_usb_close(&mpsse_ftdic); + } + ftdi_deinit(&mpsse_ftdic); + exit(status); +} + +uint8_t mpsse_recv_byte() +{ + uint8_t data; + while (1) { + int rc = ftdi_read_data(&mpsse_ftdic, &data, 1); + if (rc < 0) { + fprintf(stderr, "Read error.\n"); + mpsse_error(2); + } + if (rc == 1) + break; + usleep(100); + } + return data; +} + +void mpsse_send_byte(uint8_t data) +{ + int rc = ftdi_write_data(&mpsse_ftdic, &data, 1); + if (rc != 1) { + fprintf(stderr, "Write error (single byte, rc=%d, expected %d)(%s).\n", rc, 1, ftdi_get_error_string(&mpsse_ftdic)); + mpsse_error(2); + } +} + +void mpsse_purge(void){ + int rc = ftdi_usb_purge_buffers(&mpsse_ftdic); + if (rc != 0) { + fprintf(stderr, "Purge error.\n"); + mpsse_error(2); + } +} + +void mpsse_xfer(uint8_t* data_buffer, uint16_t send_length, uint16_t receive_length) +{ + if(send_length){ + int rc = ftdi_write_data(&mpsse_ftdic, data_buffer, send_length); + if (rc != send_length) { + fprintf(stderr, "Write error (rc=%d, expected %d)[%s]\n", rc, 1, ftdi_get_error_string(&mpsse_ftdic)); + mpsse_error(2); + } + } + + if(receive_length){ + /* Calls to ftdi_read_data may return with less data than requested if it wasn't ready. + * We stay in this while loop to collect all the data that we expect. */ + uint16_t rx_len = 0; + while(rx_len != receive_length){ + int rc = ftdi_read_data(&mpsse_ftdic, data_buffer + rx_len, receive_length - rx_len); + if (rc < 0) { + fprintf(stderr, "Read error (rc=%d)[%s]\n", rc, ftdi_get_error_string(&mpsse_ftdic)); + mpsse_error(2); + }else{ + rx_len += rc; + } + } + } +} + +void mpsse_init(int ifnum, const char *devstr, bool slow_clock) +{ + enum ftdi_interface ftdi_ifnum = INTERFACE_A; + + switch (ifnum) { + case 0: + ftdi_ifnum = INTERFACE_A; + break; + case 1: + ftdi_ifnum = INTERFACE_B; + break; + case 2: + ftdi_ifnum = INTERFACE_C; + break; + case 3: + ftdi_ifnum = INTERFACE_D; + break; + default: + ftdi_ifnum = INTERFACE_A; + break; + } + + ftdi_init(&mpsse_ftdic); + ftdi_set_interface(&mpsse_ftdic, ftdi_ifnum); + + if (devstr != NULL) { + if (ftdi_usb_open_string(&mpsse_ftdic, devstr)) { + fprintf(stderr, "Can't find iCE FTDI USB device (device string %s).\n", devstr); + mpsse_error(2); + } + } else { + if (ftdi_usb_open(&mpsse_ftdic, 0x0403, 0x6010) && ftdi_usb_open(&mpsse_ftdic, 0x0403, 0x6014)) { + fprintf(stderr, "Can't find iCE FTDI USB device (vendor_id 0x0403, device_id 0x6010 or 0x6014).\n"); + mpsse_error(2); + } + } + + mpsse_ftdic_open = true; + + if (ftdi_usb_reset(&mpsse_ftdic)) { + fprintf(stderr, "Failed to reset iCE FTDI USB device.\n"); + mpsse_error(2); + } + + if (ftdi_usb_purge_buffers(&mpsse_ftdic)) { + fprintf(stderr, "Failed to purge buffers on iCE FTDI USB device.\n"); + mpsse_error(2); + } + + if (ftdi_get_latency_timer(&mpsse_ftdic, &mpsse_ftdi_latency) < 0) { + fprintf(stderr, "Failed to get latency timer (%s).\n", ftdi_get_error_string(&mpsse_ftdic)); + mpsse_error(2); + } + + /* 1 is the fastest polling, it means 1 kHz polling */ + if (ftdi_set_latency_timer(&mpsse_ftdic, 1) < 0) { + fprintf(stderr, "Failed to set latency timer (%s).\n", ftdi_get_error_string(&mpsse_ftdic)); + mpsse_error(2); + } + + mpsse_ftdic_latency_set = true; + + /* Enter MPSSE (Multi-Protocol Synchronous Serial Engine) mode. Set all pins to output. */ + if (ftdi_set_bitmode(&mpsse_ftdic, 0xff, BITMODE_MPSSE) < 0) { + fprintf(stderr, "Failed to set BITMODE_MPSSE on FTDI USB device.\n"); + mpsse_error(2); + } + + int rc = ftdi_usb_purge_buffers(&mpsse_ftdic); + if (rc != 0) { + fprintf(stderr, "Purge error.\n"); + mpsse_error(2); + } + + if (slow_clock) { + // set 50 kHz clock + mpsse_send_byte(MC_SET_CLK_DIV); + mpsse_send_byte(119); + mpsse_send_byte(0x00); + } else { + // set 6 MHz clock + mpsse_send_byte(MC_SET_CLK_DIV); + mpsse_send_byte(4); + mpsse_send_byte(0x00); + } + + mpsse_send_byte(MC_SETB_LOW); + mpsse_send_byte(0x08); /* Value */ + mpsse_send_byte(0x0B); /* Direction */ +} + +void mpsse_close(void) +{ + ftdi_set_latency_timer(&mpsse_ftdic, mpsse_ftdi_latency); + ftdi_disable_bitbang(&mpsse_ftdic); + ftdi_usb_close(&mpsse_ftdic); + ftdi_deinit(&mpsse_ftdic); +} \ No newline at end of file diff --git a/ecpprog/mpsse.h b/ecpprog/mpsse.h new file mode 100644 index 0000000..e10601a --- /dev/null +++ b/ecpprog/mpsse.h @@ -0,0 +1,119 @@ +/* + * iceprog -- simple programming tool for FTDI-based Lattice iCE programmers + * + * Copyright (C) 2015 Clifford Wolf + * Copyright (C) 2018 Piotr Esden-Tempski + * + * 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. + */ + +#ifndef MPSSE_H +#define MPSSE_H + +#include + + + +/* MPSSE engine command definitions */ +enum mpsse_cmd +{ + /* Mode commands */ + MC_SETB_LOW = 0x80, /* Set Data bits LowByte */ + MC_READB_LOW = 0x81, /* Read Data bits LowByte */ + MC_SETB_HIGH = 0x82, /* Set Data bits HighByte */ + MC_READB_HIGH = 0x83, /* Read data bits HighByte */ + MC_LOOPBACK_EN = 0x84, /* Enable loopback */ + MC_LOOPBACK_DIS = 0x85, /* Disable loopback */ + MC_SET_CLK_DIV = 0x86, /* Set clock divisor */ + MC_FLUSH = 0x87, /* Flush buffer fifos to the PC. */ + MC_WAIT_H = 0x88, /* Wait on GPIOL1 to go high. */ + MC_WAIT_L = 0x89, /* Wait on GPIOL1 to go low. */ + MC_TCK_X5 = 0x8A, /* Disable /5 div, enables 60MHz master clock */ + MC_TCK_D5 = 0x8B, /* Enable /5 div, backward compat to FT2232D */ + MC_EN_3PH_CLK = 0x8C, /* Enable 3 phase clk, DDR I2C */ + MC_DIS_3PH_CLK = 0x8D, /* Disable 3 phase clk */ + MC_CLK_N = 0x8E, /* Clock every bit, used for JTAG */ + MC_CLK_N8 = 0x8F, /* Clock every byte, used for JTAG */ + MC_CLK_TO_H = 0x94, /* Clock until GPIOL1 goes high */ + MC_CLK_TO_L = 0x95, /* Clock until GPIOL1 goes low */ + MC_EN_ADPT_CLK = 0x96, /* Enable adaptive clocking */ + MC_DIS_ADPT_CLK = 0x97, /* Disable adaptive clocking */ + MC_CLK8_TO_H = 0x9C, /* Clock until GPIOL1 goes high, count bytes */ + MC_CLK8_TO_L = 0x9D, /* Clock until GPIOL1 goes low, count bytes */ + MC_TRI = 0x9E, /* Set IO to only drive on 0 and tristate on 1 */ + /* CPU mode commands */ + MC_CPU_RS = 0x90, /* CPUMode read short address */ + MC_CPU_RE = 0x91, /* CPUMode read extended address */ + MC_CPU_WS = 0x92, /* CPUMode write short address */ + MC_CPU_WE = 0x93, /* CPUMode write extended address */ +}; + + +/* Transfer Command bits */ + +/* All byte based commands consist of: + * - Command byte + * - Length lsb + * - Length msb + * + * If data out is enabled the data follows after the above command bytes, + * otherwise no additional data is needed. + * - Data * n + * + * All bit based commands consist of: + * - Command byte + * - Length + * + * If data out is enabled a byte containing bitst to transfer follows. + * Otherwise no additional data is needed. Only up to 8 bits can be transferred + * per transaction when in bit mode. + */ + +/* b 0000 0000 + * |||| |||`- Data out negative enable. Update DO on negative clock edge. + * |||| ||`-- Bit count enable. When reset count represents bytes. + * |||| |`--- Data in negative enable. Latch DI on negative clock edge. + * |||| `---- LSB enable. When set clock data out LSB first. + * |||| + * |||`------ Data out enable + * ||`------- Data in enable + * |`-------- TMS mode enable + * `--------- Special command mode enable. See mpsse_cmd enum. + */ + +#define MC_DATA_TMS (0x40) /* When set use TMS mode */ +#define MC_DATA_IN (0x20) /* When set read data (Data IN) */ +#define MC_DATA_OUT (0x10) /* When set write data (Data OUT) */ +#define MC_DATA_LSB (0x08) /* When set input/output data LSB first. */ +#define MC_DATA_ICN (0x04) /* When set receive data on negative clock edge */ +#define MC_DATA_BITS (0x02) /* When set count bits not bytes */ +#define MC_DATA_OCN (0x01) /* When set update data on negative clock edge */ + + +void mpsse_check_rx(void); +void mpsse_error(int status); +uint8_t mpsse_recv_byte(void); +void mpsse_xfer(uint8_t* data_buffer, uint16_t send_length, uint16_t receive_length); +void mpsse_send_byte(uint8_t data); +void mpsse_send_spi(uint8_t *data, int n); +void mpsse_xfer_spi(uint8_t *data, int n); +uint8_t mpsse_xfer_spi_bits(uint8_t data, int n); +void mpsse_set_gpio(uint8_t gpio, uint8_t direction); +int mpsse_readb_low(void); +int mpsse_readb_high(void); +void mpsse_send_dummy_bytes(uint8_t n); +void mpsse_send_dummy_bit(void); +void mpsse_init(int ifnum, const char *devstr, bool slow_clock); +void mpsse_close(void); + +#endif /* MPSSE_H */ diff --git a/jtag.h b/jtag.h deleted file mode 100644 index 51caa9d..0000000 --- a/jtag.h +++ /dev/null @@ -1,69 +0,0 @@ -/* - * Code for interacting with the FPGA via JTAG. - * This file is part of LUNA. - * - * This JTAG driver is intended to be as simple as possible in order to facilitate - * configuration and debugging of the attached FPGA. It is not intended to be a general- - * purpose JTAG link. - */ - -#ifndef __JTAG_H__ -#define __JTAG_H__ - -typedef enum e_TAPState -{ - STATE_TEST_LOGIC_RESET = 0, - STATE_RUN_TEST_IDLE = 1, - STATE_SELECT_DR_SCAN = 2, - STATE_CAPTURE_DR = 3, - STATE_SHIFT_DR = 4, - STATE_EXIT1_DR = 5, - STATE_PAUSE_DR = 6, - STATE_EXIT2_DR = 7, - STATE_UPDATE_DR = 8, - STATE_SELECT_IR_SCAN = 9, - STATE_CAPTURE_IR = 10, - STATE_SHIFT_IR = 11, - STATE_EXIT1_IR = 12, - STATE_PAUSE_IR = 13, - STATE_EXIT2_IR = 14, - STATE_UPDATE_IR = 15 -} jtag_tap_state_t; - - -/** - * Performs the start-of-day tasks necessary to talk JTAG to our FPGA. - */ -void jtag_init(int ifnum, const char *devstr, bool slow_clock); - - -/** - * De-inits the JTAG connection, so the JTAG chain. is no longer driven. - */ -void jtag_deinit(void); - - -/** - * Moves to a given JTAG state. - */ -void jtag_goto_state(int state); - - -/** - * Performs a raw TAP scan. - */ -void jtag_tap_shift( - uint8_t *input_data, - uint8_t *output_data, - uint32_t data_bits, - bool must_end); - -void jtag_error(int status); - -void jtag_wait_time(uint32_t microseconds); - -void jtag_go_to_state(unsigned state); - -uint8_t jtag_current_state(void); - -#endif diff --git a/jtag_tap.c b/jtag_tap.c deleted file mode 100644 index 44781f5..0000000 --- a/jtag_tap.c +++ /dev/null @@ -1,268 +0,0 @@ -/** - * Code adapted from Arduino-JTAG; - * portions copyright (c) 2015 Marcelo Roberto Jimenez . - * portions copyright (c) 2019 Katherine J. Temkin - * portions copyright (c) 2019 Great Scott Gadgets - */ - -#include -#include -#include -#include -#include -#include - -#include "mpsse.h" -#include "jtag.h" - -void jtag_state_ack(bool tms); - -/* - * Low nibble : TMS == 0 - * High nibble: TMS == 1 - */ - -#define TMS_T(TMS_HIGH_STATE, TMS_LOW_STATE) (((TMS_HIGH_STATE) << 4) | (TMS_LOW_STATE)) - -static const uint8_t tms_transitions[] = { - /* STATE_TEST_LOGIC_RESET */ TMS_T(STATE_TEST_LOGIC_RESET, STATE_RUN_TEST_IDLE), - /* STATE_RUN_TEST_IDLE */ TMS_T(STATE_SELECT_DR_SCAN, STATE_RUN_TEST_IDLE), - /* STATE_SELECT_DR_SCAN */ TMS_T(STATE_SELECT_IR_SCAN, STATE_CAPTURE_DR), - /* STATE_CAPTURE_DR */ TMS_T(STATE_EXIT1_DR, STATE_SHIFT_DR), - /* STATE_SHIFT_DR */ TMS_T(STATE_EXIT1_DR, STATE_SHIFT_DR), - /* STATE_EXIT1_DR */ TMS_T(STATE_UPDATE_DR, STATE_PAUSE_DR), - /* STATE_PAUSE_DR */ TMS_T(STATE_EXIT2_DR, STATE_PAUSE_DR), - /* STATE_EXIT2_DR */ TMS_T(STATE_UPDATE_DR, STATE_SHIFT_DR), - /* STATE_UPDATE_DR */ TMS_T(STATE_SELECT_DR_SCAN, STATE_RUN_TEST_IDLE), - /* STATE_SELECT_IR_SCAN */ TMS_T(STATE_TEST_LOGIC_RESET, STATE_CAPTURE_IR), - /* STATE_CAPTURE_IR */ TMS_T(STATE_EXIT1_IR, STATE_SHIFT_IR), - /* STATE_SHIFT_IR */ TMS_T(STATE_EXIT1_IR, STATE_SHIFT_IR), - /* STATE_EXIT1_IR */ TMS_T(STATE_UPDATE_IR, STATE_PAUSE_IR), - /* STATE_PAUSE_IR */ TMS_T(STATE_EXIT2_IR, STATE_PAUSE_IR), - /* STATE_EXIT2_IR */ TMS_T(STATE_UPDATE_IR, STATE_SHIFT_IR), - /* STATE_UPDATE_IR */ TMS_T(STATE_SELECT_DR_SCAN, STATE_RUN_TEST_IDLE), -}; - -#define BITSTR(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P) ( \ - ((uint16_t)(A) << 15) | \ - ((uint16_t)(B) << 14) | \ - ((uint16_t)(C) << 13) | \ - ((uint16_t)(D) << 12) | \ - ((uint16_t)(E) << 11) | \ - ((uint16_t)(F) << 10) | \ - ((uint16_t)(G) << 9) | \ - ((uint16_t)(H) << 8) | \ - ((uint16_t)(I) << 7) | \ - ((uint16_t)(J) << 6) | \ - ((uint16_t)(K) << 5) | \ - ((uint16_t)(L) << 4) | \ - ((uint16_t)(M) << 3) | \ - ((uint16_t)(N) << 2) | \ - ((uint16_t)(O) << 1) | \ - ((uint16_t)(P) << 0) ) - -/* - * The index of this vector is the current state. The i-th bit tells you the - * value TMS must assume in order to go to state "i". - ------------------------------------------------------------------------------------------------------------- -| | || F | E | D | C || B | A | 9 | 8 || 7 | 6 | 5 | 4 || 3 | 2 | 1 | 0 || HEX | ------------------------------------------------------------------------------------------------------------- -| STATE_TEST_LOGIC_RESET | 0 || 0 | 0 | 0 | 0 || 0 | 0 | 0 | 0 || 0 | 0 | 0 | 0 || 0 | 0 | 0 | 1 || 0x0001 | -| STATE_RUN_TEST_IDLE | 1 || 1 | 1 | 1 | 1 || 1 | 1 | 1 | 1 || 1 | 1 | 1 | 1 || 1 | 1 | 0 | 1 || 0xFFFD | -| STATE_SELECT_DR_SCAN | 2 || 1 | 1 | 1 | 1 || 1 | 1 | 1 | 0 || 0 | 0 | 0 | 0 || 0 | x | 1 | 1 || 0xFE03 | -| STATE_CAPTURE_DR | 3 || 1 | 1 | 1 | 1 || 1 | 1 | 1 | 1 || 1 | 1 | 1 | 0 || x | 1 | 1 | 1 || 0xFFE7 | -| STATE_SHIFT_DR | 4 || 1 | 1 | 1 | 1 || 1 | 1 | 1 | 1 || 1 | 1 | 1 | 0 || 1 | 1 | 1 | 1 || 0xFFEF | -| STATE_EXIT1_DR | 5 || 1 | 1 | 1 | 1 || 1 | 1 | 1 | 1 || 0 | 0 | x | 0 || 1 | 1 | 1 | 1 || 0xFF0F | -| STATE_PAUSE_DR | 6 || 1 | 1 | 1 | 1 || 1 | 1 | 1 | 1 || 1 | 0 | 1 | 1 || 1 | 1 | 1 | 1 || 0xFFBF | -| STATE_EXIT2_DR | 7 || 1 | 1 | 1 | 1 || 1 | 1 | 1 | 1 || x | 0 | 0 | 0 || 1 | 1 | 1 | 1 || 0xFF0F | -| STATE_UPDATE_DR | 8 || 1 | 1 | 1 | 1 || 1 | 1 | 1 | x || 1 | 1 | 1 | 1 || 1 | 1 | 0 | 1 || 0xFEFD | -| STATE_SELECT_IR_SCAN | 9 || 0 | 0 | 0 | 0 || 0 | 0 | x | 1 || 1 | 1 | 1 | 1 || 1 | 1 | 1 | 1 || 0x01FF | -| STATE_CAPTURE_IR | A || 1 | 1 | 1 | 1 || 0 | x | 1 | 1 || 1 | 1 | 1 | 1 || 1 | 1 | 1 | 1 || 0xF3FF | -| STATE_SHIFT_IR | B || 1 | 1 | 1 | 1 || 0 | 1 | 1 | 1 || 1 | 1 | 1 | 1 || 1 | 1 | 1 | 1 || 0xF7FF | -| STATE_EXIT1_IR | C || 1 | 0 | 0 | x || 0 | 1 | 1 | 1 || 1 | 1 | 1 | 1 || 1 | 1 | 1 | 1 || 0x87FF | -| STATE_PAUSE_IR | D || 1 | 1 | 0 | 1 || 1 | 1 | 1 | 1 || 1 | 1 | 1 | 1 || 1 | 1 | 1 | 1 || 0xDFFF | -| STATE_EXIT2_IR | E || 1 | x | 0 | 0 || 0 | 1 | 1 | 1 || 1 | 1 | 1 | 1 || 1 | 1 | 1 | 1 || 0x87FF | -| STATE_UPDATE_IR | F || x | 1 | 1 | 1 || 1 | 1 | 1 | 1 || 1 | 1 | 1 | 1 || 1 | 1 | 0 | 1 || 0x7FFD | ------------------------------------------------------------------------------------------------------------- - -*/ -static const uint16_t tms_map[] = { -/* STATE_TEST_LOGIC_RESET */ BITSTR( 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 ), -/* STATE_RUN_TEST_IDLE */ BITSTR( 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1 ), -/* STATE_SELECT_DR_SCAN */ BITSTR( 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1 ), -/* STATE_CAPTURE_DR */ BITSTR( 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1 ), -/* STATE_SHIFT_DR */ BITSTR( 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1 ), -/* STATE_EXIT1_DR */ BITSTR( 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1 ), -/* STATE_PAUSE_DR */ BITSTR( 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1 ), -/* STATE_EXIT2_DR */ BITSTR( 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1 ), -/* STATE_UPDATE_DR */ BITSTR( 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1 ), -/* STATE_SELECT_IR_SCAN */ BITSTR( 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1 ), -/* STATE_CAPTURE_IR */ BITSTR( 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 ), -/* STATE_SHIFT_IR */ BITSTR( 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 ), -/* STATE_EXIT1_IR */ BITSTR( 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 ), -/* STATE_PAUSE_IR */ BITSTR( 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 ), -/* STATE_EXIT2_IR */ BITSTR( 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 ), -/* STATE_UPDATE_IR */ BITSTR( 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1 ), -}; - -static uint8_t current_state; - -uint8_t jtag_current_state(void) -{ - return current_state; -} - -void jtag_set_current_state(uint8_t state) -{ - current_state = state; -} - -void jtag_error(int status){ - mpsse_error(status); -} - -void jtag_deinit(){ - mpsse_close(); -} - -/** - * Performs any start-of-day tasks necessary to talk JTAG to our FPGA. - */ -void jtag_init(int ifnum, const char *devstr, bool slow_clock) -{ - mpsse_init(ifnum, devstr, slow_clock); - - jtag_set_current_state(STATE_TEST_LOGIC_RESET); - jtag_go_to_state(STATE_TEST_LOGIC_RESET); -} - -uint8_t data[32*1024]; -uint8_t* ptr; -uint16_t rx_cnt; - -extern struct ftdi_context mpsse_ftdic; - -static inline void jtag_pulse_clock_and_read_tdo(bool tms, bool tdi) -{ - *ptr++ = MC_DATA_TMS | MC_DATA_IN | MC_DATA_LSB | MC_DATA_BITS; - *ptr++ = 0; - *ptr++ = (tdi ? 0x80 : 0) | (tms ? 0x01 : 0); - rx_cnt++; -} - -static void _jtag_tap_shift( - uint8_t *input_data, - uint8_t *output_data, - uint32_t data_bits, - bool must_end) -{ - - //printf("_jtag_tap_shift(0x%08x,0x%08x,%u,%s);\n",input_data, output_data, data_bits, must_end ? "true" : "false"); - uint32_t bit_count = data_bits; - uint32_t byte_count = (data_bits + 7) / 8; - rx_cnt = 0; - ptr = data; - - for (uint32_t i = 0; i < byte_count; ++i) { - uint8_t byte_out = input_data[i]; - for (int j = 0; j < 8 && bit_count-- > 0; ++j) { - bool tms = false; - if (bit_count == 0 && must_end) { - tms = true; - jtag_state_ack(1); - } - jtag_pulse_clock_and_read_tdo(tms, byte_out & 1); - byte_out >>= 1; - } - } - - mpsse_xfer(data, ptr-data, rx_cnt); - - /* Data out from the FTDI is actually from an internal shift register - * Instead of reconstructing the bitpattern, we can just take every 8th byte.*/ - for(int i = 0; i < rx_cnt/8; i++) - output_data[i] = data[7+i*8]; -} - -#define MIN(a,b) (a < b) ? a : b - -void jtag_tap_shift( - uint8_t *input_data, - uint8_t *output_data, - uint32_t data_bits, - bool must_end) -{ - uint32_t data_bits_sent = 0; - while(data_bits_sent != data_bits){ - - uint32_t _data_bits = MIN(256, data_bits); - bool last = (data_bits_sent + _data_bits) == data_bits; - - _jtag_tap_shift( - input_data + data_bits_sent/8, - output_data + data_bits_sent/8, - _data_bits, - last & must_end - ); - data_bits_sent += _data_bits; - } -} - -void jtag_state_ack(bool tms) -{ - if (tms) { - jtag_set_current_state((tms_transitions[jtag_current_state()] >> 4) & 0xf); - } else { - jtag_set_current_state(tms_transitions[jtag_current_state()] & 0xf); - } -} - -void jtag_go_to_state(unsigned state) -{ - - if (state == STATE_TEST_LOGIC_RESET) { - for (int i = 0; i < 5; ++i) { - jtag_state_ack(true); - } - - uint8_t data[3] = { - MC_DATA_TMS | MC_DATA_LSB | MC_DATA_BITS, - 5 - 1, - 0b11111 - }; - mpsse_xfer(data, 3, 0); - - } else { - while (jtag_current_state() != state) { - uint8_t data[3] = { - MC_DATA_TMS | MC_DATA_LSB | MC_DATA_ICN | MC_DATA_BITS, - 0, - (tms_map[jtag_current_state()] >> state) & 1 - }; - - jtag_state_ack((tms_map[jtag_current_state()] >> state) & 1); - mpsse_xfer(data, 3, 0); - } - } -} - -void jtag_wait_time(uint32_t microseconds) -{ - uint16_t bytes = microseconds / 8; - uint8_t remain = microseconds % 8; - - uint8_t data[3] = { - MC_CLK_N8, - bytes & 0xFF, - (bytes >> 8) & 0xFF - }; - mpsse_xfer(data, 3, 0); - - if(remain){ - data[0] = MC_CLK_N; - data[1] = remain; - mpsse_xfer(data, 2, 0); - } -} - diff --git a/lattice_cmds.h b/lattice_cmds.h deleted file mode 100644 index 5e42d6c..0000000 --- a/lattice_cmds.h +++ /dev/null @@ -1,65 +0,0 @@ - -#include - -/* Not sure if all of these are applicable to the JTAG interface */ -enum lattice_cmd -{ - ISC_NOOP = 0xFF, /* 0 bits - Non-operation */ - READ_ID = 0xE0, /* 24 bits - Read out the 32-bit IDCODE of the device */ - USERCODE = 0xC0, /* 24 bits - Read 32-bit usercode */ - LSC_READ_STATUS = 0x3C, /* 24 bits - Read out internal status */ - LSC_CHECK_BUSY = 0xF0, /* 24 bits - Read 1 bit busy flag to check the command execution status */ - LSC_REFRESH = 0x79, /* 24 bits - Equivalent to toggle PROGRAMN pin */ - ISC_ENABLE = 0xC6, /* 24 bits - Enable the Offline configuration mode */ - ISC_ENABLE_X = 0x74, /* 24 bits - Enable the Transparent configuration mode */ - ISC_DISABLE = 0x26, /* 24 bits - Disable the configuration operation */ - ISC_PROGRAM_USERCODE = 0xC2, /* 24 bits - Write the 32-bit new USERCODE data to USERCODE register */ - ISC_ERASE = 0x0E, /* 24 bits - Bulk erase the memory array base on the access mode and array selection */ - ISC_PROGRAM_DONE = 0x5E, /* 24 bits - Program the DONE bit if the device is in Configuration state. */ - ISC_PROGRAM_SECURITY = 0xCE, /* 24 bits - Program the Security bit if the device is in Configuration state */ - LSC_INIT_ADDRESS = 0x46, /* 24 bits - Initialize the Address Shift Register */ - LSC_WRITE_ADDRESS = 0xB4, /* 24 bits - Write the 16 bit Address Register to move the address quickly */ - LSC_BITSTREAM_BURST = 0x7A, /* 24 bits - Program the device the whole bitstream sent in as the command operand */ - LSC_PROG_INCR_RTI = 0x82, /* 24 bits - Write configuration data to the configuration memory frame at current address and post increment the address, Byte 2~0 of the opcode indicate number of the frames included in the operand field */ - LSC_PROG_INCR_ENC = 0xB6, /* 24 bits - Encrypt the configuration data then write */ - LSC_PROG_INCR_CMP = 0xB8, /* 24 bits - Decompress the configuration data, then write */ - LSC_PROG_INCR_CNE = 0xBA, /* 24 bits - Decompress and Encrypt the configuration data, then write */ - LSC_VERIFY_INCR_RTI = 0x6A, /* 24 bits - Read back the configuration memory frame selected by the address register and post increment the address */ - LSC_PROG_CTRL0 = 0x22, /* 24 bits - Modify the Control Register 0 */ - LSC_READ_CTRL0 = 0x20, /* 24 bits - Read the Control Register 0 */ - LSC_RESET_CRC = 0x3B, /* 24 bits - Reset 16-bit frame CRC register to 0x0000 */ - LSC_READ_CRC = 0x60, /* 24 bits - Read 16-bit frame CRC register content */ - LSC_PROG_SED_CRC = 0xA2, /* 24 bits - Program the calculated 32-bit CRC based on configuration bit values only into overall CRC register */ - LSC_READ_SED_CRC = 0xA4, /* 24 bits - Read the 32-bit SED CRC */ - LSC_PROG_PASSWORD = 0xF1, /* 24 bits - Program 64-bit password into the non-volatile memory (Efuse) */ - LSC_READ_PASSWORD = 0xF2, /* 24 bits - Read out the 64-bit password before activated for verification */ - LSC_SHIFT_PASSWORD = 0xBC, /* 24 bits - Shift in the password to unlock for re-configuration (necessary when password protection feature is active). */ - LSC_PROG_CIPHER_KEY = 0xF3, /* 24 bits - Program the 128-bit cipher key into Efuse */ - LSC_READ_CIPHER_KEY = 0xF4, /* 24 bits - Read out the 128-bit cipher key before activated for verification */ - LSC_PROG_FEATURE = 0xE4, /* 24 bits - Program User Feature, such as Customer ID, I2C Slave Address, Unique ID Header */ - LSC_READ_FEATURE = 0xE7, /* 24 bits - Read User Feature, such as Customer ID, I2C Slave Address, Unique ID Header */ - LSC_PROG_FEABITS = 0xF8, /* 24 bits - Program User Feature Bits, such as CFG port and pin persistence, PWD_EN, PWD_ALL, DEC_ONLY, Feature Row Lock etc. */ - LSC_READ_FEABITS = 0xFB, /* 24 bits - Read User Feature Bits, such as CFH port and pin persistence, PWD_EN, PWD_ALL, DEC_ONLY, Feature Row Lock etc. */ - LSC_PROG_OTP = 0xF9, /* 24 bits - Program OTP bits, to set Memory Sectors One Time Programmable */ - LSC_READ_OTP = 0xFA, /* 24 bits - Read OTP bits setting */ -}; - - -struct ecp_device_id { - const char* device_name; - uint32_t device_id; -}; - -const struct ecp_device_id ecp_devices[] = -{ - {"LFE5U-12" , 0x21111043 }, - {"LFE5U-25" , 0x41111043 }, - {"LFE5U-45" , 0x41112043 }, - {"LFE5U-85" , 0x41113043 }, - {"LFE5UM-25" , 0x01111043 }, - {"LFE5UM-45" , 0x01112043 }, - {"LFE5UM-85" , 0x01113043 }, - {"LFE5UM5G-25", 0x81111043 }, - {"LFE5UM5G-45", 0x81112043 }, - {"LFE5UM5G-85", 0x81113043 } -}; diff --git a/mpsse.c b/mpsse.c deleted file mode 100644 index 5833c65..0000000 --- a/mpsse.c +++ /dev/null @@ -1,247 +0,0 @@ -/* - * iceprog -- simple programming tool for FTDI-based Lattice iCE programmers - * - * Copyright (C) 2015 Clifford Wolf - * Copyright (C) 2018 Piotr Esden-Tempski - * - * 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.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 "mpsse.h" - -// --------------------------------------------------------- -// MPSSE / FTDI definitions -// --------------------------------------------------------- - -/* FTDI bank pinout typically used for iCE dev boards - * BUS IO | Signal | Control - * -------+--------+-------------- - * xDBUS0 | SCK | MPSSE - * xDBUS1 | MOSI | MPSSE - * xDBUS2 | MISO | MPSSE - * xDBUS3 | nc | - * xDBUS4 | CS | GPIO - * xDBUS5 | nc | - * xDBUS6 | CDONE | GPIO - * xDBUS7 | CRESET | GPIO - */ - -struct ftdi_context mpsse_ftdic; -bool mpsse_ftdic_open = false; -bool mpsse_ftdic_latency_set = false; -unsigned char mpsse_ftdi_latency; - - -// --------------------------------------------------------- -// MPSSE / FTDI function implementations -// --------------------------------------------------------- - -void mpsse_check_rx() -{ - uint8_t cnt = 0; - while (1) { - uint8_t data; - int rc = ftdi_read_data(&mpsse_ftdic, &data, 1); - if (rc <= 0) - break; - fprintf(stderr, "unexpected rx byte: %02X\n", data); - cnt++; - - if(cnt > 32) - break; - } -} - -void mpsse_error(int status) -{ - //mpsse_check_rx(); - fprintf(stderr, "ABORT.\n"); - if (mpsse_ftdic_open) { - if (mpsse_ftdic_latency_set) - ftdi_set_latency_timer(&mpsse_ftdic, mpsse_ftdi_latency); - ftdi_usb_close(&mpsse_ftdic); - } - ftdi_deinit(&mpsse_ftdic); - exit(status); -} - -uint8_t mpsse_recv_byte() -{ - uint8_t data; - while (1) { - int rc = ftdi_read_data(&mpsse_ftdic, &data, 1); - if (rc < 0) { - fprintf(stderr, "Read error.\n"); - mpsse_error(2); - } - if (rc == 1) - break; - usleep(100); - } - return data; -} - -void mpsse_send_byte(uint8_t data) -{ - int rc = ftdi_write_data(&mpsse_ftdic, &data, 1); - if (rc != 1) { - fprintf(stderr, "Write error (single byte, rc=%d, expected %d)(%s).\n", rc, 1, ftdi_get_error_string(&mpsse_ftdic)); - mpsse_error(2); - } -} - -void mpsse_purge(void){ - int rc = ftdi_usb_purge_buffers(&mpsse_ftdic); - if (rc != 0) { - fprintf(stderr, "Purge error.\n"); - mpsse_error(2); - } -} - -void mpsse_xfer(uint8_t* data_buffer, uint16_t send_length, uint16_t receive_length) -{ - if(send_length){ - int rc = ftdi_write_data(&mpsse_ftdic, data_buffer, send_length); - if (rc != send_length) { - fprintf(stderr, "Write error (rc=%d, expected %d)[%s]\n", rc, 1, ftdi_get_error_string(&mpsse_ftdic)); - mpsse_error(2); - } - } - - if(receive_length){ - /* Calls to ftdi_read_data may return with less data than requested if it wasn't ready. - * We stay in this while loop to collect all the data that we expect. */ - uint16_t rx_len = 0; - while(rx_len != receive_length){ - int rc = ftdi_read_data(&mpsse_ftdic, data_buffer + rx_len, receive_length - rx_len); - if (rc < 0) { - fprintf(stderr, "Read error (rc=%d)[%s]\n", rc, ftdi_get_error_string(&mpsse_ftdic)); - mpsse_error(2); - }else{ - rx_len += rc; - } - } - } -} - -void mpsse_init(int ifnum, const char *devstr, bool slow_clock) -{ - enum ftdi_interface ftdi_ifnum = INTERFACE_A; - - switch (ifnum) { - case 0: - ftdi_ifnum = INTERFACE_A; - break; - case 1: - ftdi_ifnum = INTERFACE_B; - break; - case 2: - ftdi_ifnum = INTERFACE_C; - break; - case 3: - ftdi_ifnum = INTERFACE_D; - break; - default: - ftdi_ifnum = INTERFACE_A; - break; - } - - ftdi_init(&mpsse_ftdic); - ftdi_set_interface(&mpsse_ftdic, ftdi_ifnum); - - if (devstr != NULL) { - if (ftdi_usb_open_string(&mpsse_ftdic, devstr)) { - fprintf(stderr, "Can't find iCE FTDI USB device (device string %s).\n", devstr); - mpsse_error(2); - } - } else { - if (ftdi_usb_open(&mpsse_ftdic, 0x0403, 0x6010) && ftdi_usb_open(&mpsse_ftdic, 0x0403, 0x6014)) { - fprintf(stderr, "Can't find iCE FTDI USB device (vendor_id 0x0403, device_id 0x6010 or 0x6014).\n"); - mpsse_error(2); - } - } - - mpsse_ftdic_open = true; - - if (ftdi_usb_reset(&mpsse_ftdic)) { - fprintf(stderr, "Failed to reset iCE FTDI USB device.\n"); - mpsse_error(2); - } - - if (ftdi_usb_purge_buffers(&mpsse_ftdic)) { - fprintf(stderr, "Failed to purge buffers on iCE FTDI USB device.\n"); - mpsse_error(2); - } - - if (ftdi_get_latency_timer(&mpsse_ftdic, &mpsse_ftdi_latency) < 0) { - fprintf(stderr, "Failed to get latency timer (%s).\n", ftdi_get_error_string(&mpsse_ftdic)); - mpsse_error(2); - } - - /* 1 is the fastest polling, it means 1 kHz polling */ - if (ftdi_set_latency_timer(&mpsse_ftdic, 1) < 0) { - fprintf(stderr, "Failed to set latency timer (%s).\n", ftdi_get_error_string(&mpsse_ftdic)); - mpsse_error(2); - } - - mpsse_ftdic_latency_set = true; - - /* Enter MPSSE (Multi-Protocol Synchronous Serial Engine) mode. Set all pins to output. */ - if (ftdi_set_bitmode(&mpsse_ftdic, 0xff, BITMODE_MPSSE) < 0) { - fprintf(stderr, "Failed to set BITMODE_MPSSE on FTDI USB device.\n"); - mpsse_error(2); - } - - int rc = ftdi_usb_purge_buffers(&mpsse_ftdic); - if (rc != 0) { - fprintf(stderr, "Purge error.\n"); - mpsse_error(2); - } - - if (slow_clock) { - // set 50 kHz clock - mpsse_send_byte(MC_SET_CLK_DIV); - mpsse_send_byte(119); - mpsse_send_byte(0x00); - } else { - // set 6 MHz clock - mpsse_send_byte(MC_SET_CLK_DIV); - mpsse_send_byte(4); - mpsse_send_byte(0x00); - } - - mpsse_send_byte(MC_SETB_LOW); - mpsse_send_byte(0x08); /* Value */ - mpsse_send_byte(0x0B); /* Direction */ -} - -void mpsse_close(void) -{ - ftdi_set_latency_timer(&mpsse_ftdic, mpsse_ftdi_latency); - ftdi_disable_bitbang(&mpsse_ftdic); - ftdi_usb_close(&mpsse_ftdic); - ftdi_deinit(&mpsse_ftdic); -} \ No newline at end of file diff --git a/mpsse.h b/mpsse.h deleted file mode 100644 index e10601a..0000000 --- a/mpsse.h +++ /dev/null @@ -1,119 +0,0 @@ -/* - * iceprog -- simple programming tool for FTDI-based Lattice iCE programmers - * - * Copyright (C) 2015 Clifford Wolf - * Copyright (C) 2018 Piotr Esden-Tempski - * - * 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. - */ - -#ifndef MPSSE_H -#define MPSSE_H - -#include - - - -/* MPSSE engine command definitions */ -enum mpsse_cmd -{ - /* Mode commands */ - MC_SETB_LOW = 0x80, /* Set Data bits LowByte */ - MC_READB_LOW = 0x81, /* Read Data bits LowByte */ - MC_SETB_HIGH = 0x82, /* Set Data bits HighByte */ - MC_READB_HIGH = 0x83, /* Read data bits HighByte */ - MC_LOOPBACK_EN = 0x84, /* Enable loopback */ - MC_LOOPBACK_DIS = 0x85, /* Disable loopback */ - MC_SET_CLK_DIV = 0x86, /* Set clock divisor */ - MC_FLUSH = 0x87, /* Flush buffer fifos to the PC. */ - MC_WAIT_H = 0x88, /* Wait on GPIOL1 to go high. */ - MC_WAIT_L = 0x89, /* Wait on GPIOL1 to go low. */ - MC_TCK_X5 = 0x8A, /* Disable /5 div, enables 60MHz master clock */ - MC_TCK_D5 = 0x8B, /* Enable /5 div, backward compat to FT2232D */ - MC_EN_3PH_CLK = 0x8C, /* Enable 3 phase clk, DDR I2C */ - MC_DIS_3PH_CLK = 0x8D, /* Disable 3 phase clk */ - MC_CLK_N = 0x8E, /* Clock every bit, used for JTAG */ - MC_CLK_N8 = 0x8F, /* Clock every byte, used for JTAG */ - MC_CLK_TO_H = 0x94, /* Clock until GPIOL1 goes high */ - MC_CLK_TO_L = 0x95, /* Clock until GPIOL1 goes low */ - MC_EN_ADPT_CLK = 0x96, /* Enable adaptive clocking */ - MC_DIS_ADPT_CLK = 0x97, /* Disable adaptive clocking */ - MC_CLK8_TO_H = 0x9C, /* Clock until GPIOL1 goes high, count bytes */ - MC_CLK8_TO_L = 0x9D, /* Clock until GPIOL1 goes low, count bytes */ - MC_TRI = 0x9E, /* Set IO to only drive on 0 and tristate on 1 */ - /* CPU mode commands */ - MC_CPU_RS = 0x90, /* CPUMode read short address */ - MC_CPU_RE = 0x91, /* CPUMode read extended address */ - MC_CPU_WS = 0x92, /* CPUMode write short address */ - MC_CPU_WE = 0x93, /* CPUMode write extended address */ -}; - - -/* Transfer Command bits */ - -/* All byte based commands consist of: - * - Command byte - * - Length lsb - * - Length msb - * - * If data out is enabled the data follows after the above command bytes, - * otherwise no additional data is needed. - * - Data * n - * - * All bit based commands consist of: - * - Command byte - * - Length - * - * If data out is enabled a byte containing bitst to transfer follows. - * Otherwise no additional data is needed. Only up to 8 bits can be transferred - * per transaction when in bit mode. - */ - -/* b 0000 0000 - * |||| |||`- Data out negative enable. Update DO on negative clock edge. - * |||| ||`-- Bit count enable. When reset count represents bytes. - * |||| |`--- Data in negative enable. Latch DI on negative clock edge. - * |||| `---- LSB enable. When set clock data out LSB first. - * |||| - * |||`------ Data out enable - * ||`------- Data in enable - * |`-------- TMS mode enable - * `--------- Special command mode enable. See mpsse_cmd enum. - */ - -#define MC_DATA_TMS (0x40) /* When set use TMS mode */ -#define MC_DATA_IN (0x20) /* When set read data (Data IN) */ -#define MC_DATA_OUT (0x10) /* When set write data (Data OUT) */ -#define MC_DATA_LSB (0x08) /* When set input/output data LSB first. */ -#define MC_DATA_ICN (0x04) /* When set receive data on negative clock edge */ -#define MC_DATA_BITS (0x02) /* When set count bits not bytes */ -#define MC_DATA_OCN (0x01) /* When set update data on negative clock edge */ - - -void mpsse_check_rx(void); -void mpsse_error(int status); -uint8_t mpsse_recv_byte(void); -void mpsse_xfer(uint8_t* data_buffer, uint16_t send_length, uint16_t receive_length); -void mpsse_send_byte(uint8_t data); -void mpsse_send_spi(uint8_t *data, int n); -void mpsse_xfer_spi(uint8_t *data, int n); -uint8_t mpsse_xfer_spi_bits(uint8_t data, int n); -void mpsse_set_gpio(uint8_t gpio, uint8_t direction); -int mpsse_readb_low(void); -int mpsse_readb_high(void); -void mpsse_send_dummy_bytes(uint8_t n); -void mpsse_send_dummy_bit(void); -void mpsse_init(int ifnum, const char *devstr, bool slow_clock); -void mpsse_close(void); - -#endif /* MPSSE_H */