├── src ├── ftd2xx.h ├── jtag_fsm.h ├── jtag.h ├── loader.h ├── spi.h ├── jtag_fsm.cpp ├── WinTypes.h ├── config_type.h ├── config_type.cpp ├── loader.cpp ├── mingw.thread.h ├── Alchitry_Loader.cpp ├── jtag.cpp └── spi.cpp ├── bridge ├── au_loader.bin └── au_plus_loader.bin ├── lib ├── linux │ └── libftd2xx.a └── windows │ ├── ftbusui.dll │ ├── ftcserco.dll │ ├── ftd2xx.lib │ ├── ftd2xx64.dll │ ├── ftdibus.sys │ ├── ftlang.dll │ ├── ftser2k.sys │ └── ftserui2.dll ├── .gitignore ├── CMakeLists.txt ├── LICENSE.txt └── README.md /src/ftd2xx.h: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alchitry/alchitry-loader/HEAD/src/ftd2xx.h -------------------------------------------------------------------------------- /bridge/au_loader.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alchitry/alchitry-loader/HEAD/bridge/au_loader.bin -------------------------------------------------------------------------------- /lib/linux/libftd2xx.a: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alchitry/alchitry-loader/HEAD/lib/linux/libftd2xx.a -------------------------------------------------------------------------------- /lib/windows/ftbusui.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alchitry/alchitry-loader/HEAD/lib/windows/ftbusui.dll -------------------------------------------------------------------------------- /lib/windows/ftcserco.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alchitry/alchitry-loader/HEAD/lib/windows/ftcserco.dll -------------------------------------------------------------------------------- /lib/windows/ftd2xx.lib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alchitry/alchitry-loader/HEAD/lib/windows/ftd2xx.lib -------------------------------------------------------------------------------- /lib/windows/ftd2xx64.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alchitry/alchitry-loader/HEAD/lib/windows/ftd2xx64.dll -------------------------------------------------------------------------------- /lib/windows/ftdibus.sys: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alchitry/alchitry-loader/HEAD/lib/windows/ftdibus.sys -------------------------------------------------------------------------------- /lib/windows/ftlang.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alchitry/alchitry-loader/HEAD/lib/windows/ftlang.dll -------------------------------------------------------------------------------- /lib/windows/ftser2k.sys: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alchitry/alchitry-loader/HEAD/lib/windows/ftser2k.sys -------------------------------------------------------------------------------- /lib/windows/ftserui2.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alchitry/alchitry-loader/HEAD/lib/windows/ftserui2.dll -------------------------------------------------------------------------------- /bridge/au_plus_loader.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alchitry/alchitry-loader/HEAD/bridge/au_plus_loader.bin -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .project 2 | .cproject 3 | .settings/ 4 | V*/ 5 | Release/ 6 | /cmake-build-debug/ 7 | /.idea/ 8 | /cmake_install.cmake 9 | /CMakeCache.txt 10 | /Makefile 11 | /CMakeFiles/ 12 | /alchitry_loader.cbp 13 | /.cmake/ 14 | /Testing/Temporary/LastTest.log 15 | /alchitry_loader 16 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.0) 2 | project(alchitry_loader) 3 | 4 | set(CMAKE_CXX_STANDARD 14) 5 | 6 | include_directories(src) 7 | 8 | add_executable(alchitry_loader 9 | src/Alchitry_Loader.cpp 10 | src/config_type.cpp 11 | src/config_type.h 12 | src/ftd2xx.h 13 | src/jtag.cpp 14 | src/jtag.h 15 | src/jtag_fsm.cpp 16 | src/jtag_fsm.h 17 | src/loader.cpp 18 | src/loader.h 19 | src/mingw.thread.h 20 | src/spi.cpp 21 | src/spi.h 22 | src/WinTypes.h) 23 | 24 | 25 | target_link_libraries(alchitry_loader 26 | ${CMAKE_SOURCE_DIR}/lib/linux/libftd2xx.a 27 | ${CMAKE_SOURCE_DIR}/lib/windows/ftd2xx.lib 28 | pthread) -------------------------------------------------------------------------------- /src/jtag_fsm.h: -------------------------------------------------------------------------------- 1 | /* 2 | * jtag_fsm.h 3 | * 4 | * Created on: May 24, 2017 5 | * Author: justin 6 | */ 7 | 8 | #ifndef JTAG_FSM_H_ 9 | #define JTAG_FSM_H_ 10 | 11 | #include 12 | #include 13 | 14 | using namespace std; 15 | 16 | class Jtag_fsm { 17 | public: 18 | enum State { 19 | TEST_LOGIC_RESET, 20 | RUN_TEST_IDLE, 21 | SELECT_DR_SCAN, 22 | CAPTURE_DR, 23 | SHIFT_DR, 24 | EXIT1_DR, 25 | PAUSE_DR, 26 | EXIT2_DR, 27 | UPDATE_DR, 28 | SELECT_IR_SCAN, 29 | CAPTURE_IR, 30 | SHIFT_IR, 31 | EXIT1_IR, 32 | PAUSE_IR, 33 | EXIT2_IR, 34 | UPDATE_IR 35 | }; 36 | class Transistions { 37 | public: 38 | State currentState; 39 | uint8_t tms; 40 | uint8_t moves; 41 | }; 42 | 43 | static Transistions getTransitions(State, State); 44 | static State getStateFromName(string); 45 | 46 | 47 | private: 48 | static State getTransition(State, bool); 49 | 50 | }; 51 | 52 | #endif /* JTAG_FSM_H_ */ 53 | -------------------------------------------------------------------------------- /src/jtag.h: -------------------------------------------------------------------------------- 1 | /* 2 | * jtag.h 3 | * 4 | * Created on: May 24, 2017 5 | * Author: justin 6 | */ 7 | 8 | #ifndef JTAG_H_ 9 | #define JTAG_H_ 10 | 11 | #include "ftd2xx.h" 12 | #include "jtag_fsm.h" 13 | #include 14 | 15 | class Jtag { 16 | FT_HANDLE ftHandle; 17 | unsigned int uiDevIndex = 0xF; // The device in the list that is used 18 | bool active; 19 | 20 | public: 21 | Jtag(); 22 | FT_STATUS connect(unsigned int); 23 | FT_STATUS disconnect(); 24 | bool initialize(); 25 | bool setFreq(double); 26 | bool navigateToState(Jtag_fsm::State, Jtag_fsm::State); 27 | bool shiftData(unsigned int, string, string, string); 28 | string shiftData(unsigned int, string); 29 | bool sendClocks(unsigned long); 30 | 31 | private: 32 | bool sync_mpsse(); 33 | bool config_jtag(); 34 | static void hexToByte(string, BYTE*); 35 | bool flush(); 36 | bool compareHexString(string, string, string); 37 | 38 | }; 39 | 40 | #endif /* JTAG_H_ */ 41 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 alchitry 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /src/loader.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Loader.h 3 | * 4 | * Created on: Nov 6, 2017 5 | * Author: justin 6 | */ 7 | 8 | #ifndef LOADER_H_ 9 | #define LOADER_H_ 10 | 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include "jtag.h" 19 | #include "jtag_fsm.h" 20 | 21 | class Loader { 22 | Jtag* device; 23 | Jtag_fsm::State currentState; 24 | 25 | public: 26 | enum Instruction { 27 | EXTEST = 0x26, 28 | EXTEST_PULSE = 0x3C, 29 | EXTEST_TRAIN = 0x3D, 30 | SAMPLE = 0x01, 31 | USER1 = 0x02, 32 | USER2 = 0x03, 33 | USER3 = 0x22, 34 | USER4 = 0x23, 35 | CFG_OUT = 0x04, 36 | CFG_IN = 0x05, 37 | USERCODE = 0x08, 38 | IDCODE = 0x09, 39 | HIGHZ_IO = 0x0A, 40 | JPROGRAM = 0x0B, 41 | JSTART = 0x0C, 42 | JSHUTDOWN = 0x0D, 43 | XADC_DRP = 0x37, 44 | ISC_ENABLE = 0x10, 45 | ISC_PROGRAM = 0x11, 46 | XSC_PROGRAM_KEY = 0x12, 47 | XSC_DNA = 0x17, 48 | FUSE_DNA = 0x32, 49 | ISC_NOOP = 0x14, 50 | ISC_DISABLE = 0x16, 51 | BYPASS = 0x2F, 52 | }; 53 | 54 | public: 55 | Loader(Jtag*); 56 | bool resetState(); 57 | bool checkIDCODE(); 58 | bool eraseFlash(string); 59 | bool writeBin(string, bool, string); 60 | 61 | private: 62 | bool setWREN(); 63 | bool setIR(Instruction); 64 | bool shiftUDR(int, string, string, string); 65 | bool shiftDR(int, string, string, string); 66 | string shiftDR(int, string); 67 | bool shiftIR(int, string, string, string); 68 | int getStatus(); 69 | string reverseBytes(string); 70 | string fileToBinStr(string); 71 | bool loadBin(string); 72 | bool setState(Jtag_fsm::State); 73 | }; 74 | 75 | 76 | 77 | #endif /* LOADER_H_ */ 78 | -------------------------------------------------------------------------------- /src/spi.h: -------------------------------------------------------------------------------- 1 | /* 2 | * spi.h 3 | * 4 | * Created on: Feb 5, 2019 5 | * Author: justin 6 | */ 7 | 8 | #ifndef SPI_H_ 9 | #define SPI_H_ 10 | 11 | #include "ftd2xx.h" 12 | #include 13 | #include 14 | #include 15 | 16 | using namespace std; 17 | 18 | class Spi { 19 | FT_HANDLE ftHandle; 20 | unsigned int uiDevIndex = 0xF; // The device in the list that is used 21 | bool active; 22 | bool verbose; 23 | 24 | public: 25 | Spi(); 26 | FT_STATUS connect(unsigned int); 27 | FT_STATUS disconnect(); 28 | bool initialize(); 29 | bool eraseFlash(); 30 | bool writeBin(string); 31 | 32 | private: 33 | bool sync_mpsse(); 34 | bool config_spi(); 35 | static void hexToByte(string, BYTE*); 36 | bool flush(); 37 | bool compareHexString(string, string, string); 38 | void check_rx(); 39 | void error(int); 40 | BYTE recv_byte(); 41 | void send_byte(BYTE data); 42 | void send_spi(uint8_t *data, int n); 43 | void xfer_spi(uint8_t *data, int n); 44 | uint8_t xfer_spi_bits(uint8_t data, int n); 45 | void set_gpio(int slavesel_b, int creset_b); 46 | int get_cdone(); 47 | void flash_release_reset(); 48 | void flash_chip_select(); 49 | void flash_chip_deselect(); 50 | void sram_reset(); 51 | void sram_chip_select(); 52 | void flash_read_id(); 53 | void flash_reset(); 54 | void flash_power_up(); 55 | void flash_power_down(); 56 | uint8_t flash_read_status(); 57 | void flash_write_enable(); 58 | void flash_bulk_erase(); 59 | void flash_64kB_sector_erase(int addr); 60 | void flash_prog(int addr, uint8_t *data, int n); 61 | void flash_read(int addr, uint8_t *data, int n); 62 | void flash_wait(); 63 | void flash_disable_protection(); 64 | 65 | }; 66 | 67 | #endif /* SPI_H_ */ 68 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Alchitry Loader (based on D2XX) 2 | This project was originally used as a tool by Alchitry Labs to program the Alchitry boards using the 3 | official D2XX library from FTDI. You can find D2XX here https://ftdichip.com/drivers/d2xx-drivers/ 4 | 5 | It has been updated to include the Alchitry Au+ and can be used as a stand-alone command line tool for 6 | loading the Alchitry boards without using Alchitry Labs. 7 | 8 | Alchitry Labs no longer relies on this loader and instead has a fully Java loader built in. 9 | 10 | ## Building 11 | Clone the repository 12 | 13 | `git clone https://github.com/alchitry/alchitry-loader.git` 14 | 15 | Enter the project files 16 | 17 | `cd alchitry-loader` 18 | 19 | Create the Makefile 20 | 21 | `cmake CMakeLists.txt` 22 | 23 | Build the project 24 | 25 | `make` 26 | 27 | Test it out 28 | 29 | `./alchitry_loader` 30 | 31 | ## Usage 32 | 33 | ``` 34 | Usage: "loader arguments" 35 | 36 | Arguments: 37 | -e : erase FPGA flash 38 | -l : list detected boards 39 | -h : print this help message 40 | -f config.bin : write FPGA flash 41 | -r config.bin : write FPGA RAM 42 | -u config.data : write FTDI eeprom 43 | -b n : select board "n" (defaults to 0) 44 | -p loader.bin : Au bridge bin 45 | -t TYPE : TYPE can be au, au+, or cu (defaults to au) 46 | ``` 47 | 48 | ### Examples 49 | Load a .bin onto an Au's RAM (lost on power cycle) 50 | 51 | `./alchitry_loader -t au -r au_config.bin` 52 | 53 | Load a .bin onto an Au+'s flash (persistent config) 54 | 55 | `./alchitry_loader -t "au+" -f au_config.bin -p ./bridge/au_plus_loader.bin` 56 | 57 | Note that to load to the Au or Au+ flash memory you need to specify a bridge bin file. These can be found 58 | in the bridge folder of this repo. This file is loaded onto the Au and allows this loader to program the 59 | flash memory. It acts as a bridge from the JTAG port to the SPI of the flash memory. 60 | 61 | The source for the bridge files can be found here https://github.com/alchitry/au-bridge 62 | 63 | This isn't needed for the Cu which has direct access to the flash over the SPI protocol. -------------------------------------------------------------------------------- /src/jtag_fsm.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * jtag_fsm.cpp 3 | * 4 | * Created on: May 24, 2017 5 | * Author: justin 6 | */ 7 | 8 | #include "jtag_fsm.h" 9 | #include 10 | #include 11 | 12 | using namespace std; 13 | 14 | Jtag_fsm::State Jtag_fsm::getTransition(State state, bool tms) { 15 | switch (state) { 16 | case TEST_LOGIC_RESET: 17 | return tms ? TEST_LOGIC_RESET : RUN_TEST_IDLE; 18 | case RUN_TEST_IDLE: 19 | return tms ? SELECT_DR_SCAN : RUN_TEST_IDLE; 20 | case SELECT_DR_SCAN: 21 | return tms ? SELECT_IR_SCAN : CAPTURE_DR; 22 | case CAPTURE_DR: 23 | return tms ? EXIT1_DR : SHIFT_DR; 24 | case SHIFT_DR: 25 | return tms ? EXIT1_DR : SHIFT_DR; 26 | case EXIT1_DR: 27 | return tms ? UPDATE_DR : PAUSE_DR; 28 | case PAUSE_DR: 29 | return tms ? EXIT2_DR : PAUSE_DR; 30 | case EXIT2_DR: 31 | return tms ? UPDATE_DR : SHIFT_DR; 32 | case UPDATE_DR: 33 | return tms ? SELECT_DR_SCAN : RUN_TEST_IDLE; 34 | case SELECT_IR_SCAN: 35 | return tms ? TEST_LOGIC_RESET : CAPTURE_IR; 36 | case CAPTURE_IR: 37 | return tms ? EXIT1_IR : SHIFT_IR; 38 | case SHIFT_IR: 39 | return tms ? EXIT1_IR : SHIFT_IR; 40 | case EXIT1_IR: 41 | return tms ? UPDATE_IR : PAUSE_IR; 42 | case PAUSE_IR: 43 | return tms ? EXIT2_IR : PAUSE_IR; 44 | case EXIT2_IR: 45 | return tms ? UPDATE_IR : SHIFT_IR; 46 | case UPDATE_IR: 47 | return tms ? SELECT_DR_SCAN : RUN_TEST_IDLE; 48 | } 49 | 50 | return TEST_LOGIC_RESET; 51 | } 52 | 53 | Jtag_fsm::Transistions Jtag_fsm::getTransitions(State init, State final) { 54 | queue queue; 55 | Transistions t; 56 | t.currentState = init; 57 | t.moves = 0; 58 | t.tms = 0; 59 | queue.push(t); 60 | while (!queue.empty()) { 61 | t = queue.front(); 62 | queue.pop(); 63 | if (t.currentState == final) { 64 | break; 65 | } 66 | 67 | State s0 = getTransition(t.currentState, false); 68 | State s1 = getTransition(t.currentState, true); 69 | 70 | t.moves++; 71 | t.tms &= ~(1 << (t.moves-1)); // clear bit 72 | t.currentState = s0; 73 | queue.push(t); 74 | 75 | t.tms |= (1 << (t.moves-1)); 76 | t.currentState = s1; 77 | queue.push(t); 78 | } 79 | 80 | return t; 81 | } 82 | 83 | Jtag_fsm::State Jtag_fsm::getStateFromName(string s) { 84 | if (s == "RESET") 85 | return TEST_LOGIC_RESET; 86 | if (s == "IDLE") 87 | return RUN_TEST_IDLE; 88 | if (s == "DRSELECT") 89 | return SELECT_DR_SCAN; 90 | if (s == "DRCAPTURE") 91 | return CAPTURE_DR; 92 | if (s == "DRSHIFT") 93 | return SHIFT_DR; 94 | if (s == "DREXIT1") 95 | return EXIT1_DR; 96 | if (s == "DRPAUSE") 97 | return PAUSE_DR; 98 | if (s == "DREXIT2") 99 | return EXIT2_DR; 100 | if (s == "DRUPDATE") 101 | return UPDATE_DR; 102 | if (s == "IRSELECT") 103 | return SELECT_IR_SCAN; 104 | if (s == "IRCAPTURE") 105 | return CAPTURE_IR; 106 | if (s == "IRSHIFT") 107 | return SHIFT_IR; 108 | if (s == "IREXIT1") 109 | return EXIT1_IR; 110 | if (s == "IRPAUSE") 111 | return PAUSE_IR; 112 | if (s == "IREXIT2") 113 | return EXIT2_IR; 114 | if (s == "IRUPDATE") 115 | return UPDATE_IR; 116 | 117 | cerr << "ERROR! Invalid state name " << s << endl; 118 | return TEST_LOGIC_RESET; 119 | } 120 | -------------------------------------------------------------------------------- /src/WinTypes.h: -------------------------------------------------------------------------------- 1 | #ifndef __WINDOWS_TYPES__ 2 | #define __WINDOWS_TYPES__ 3 | 4 | #define WINAPI 5 | 6 | typedef unsigned int DWORD; 7 | typedef unsigned int ULONG; 8 | typedef unsigned short USHORT; 9 | typedef unsigned short SHORT; 10 | typedef unsigned char UCHAR; 11 | typedef unsigned short WORD; 12 | typedef unsigned short WCHAR; 13 | typedef unsigned char BYTE; 14 | typedef BYTE *LPBYTE; 15 | typedef unsigned int BOOL; 16 | typedef unsigned char BOOLEAN; 17 | typedef unsigned char CHAR; 18 | typedef BOOL *LPBOOL; 19 | typedef UCHAR *PUCHAR; 20 | typedef const char *LPCSTR; 21 | typedef char *PCHAR; 22 | typedef void *PVOID; 23 | typedef void *HANDLE; 24 | typedef unsigned int LONG; 25 | typedef int INT; 26 | typedef unsigned int UINT; 27 | typedef char *LPSTR; 28 | typedef char *LPTSTR; 29 | typedef const char *LPCTSTR; 30 | typedef DWORD *LPDWORD; 31 | typedef WORD *LPWORD; 32 | typedef ULONG *PULONG; 33 | typedef LONG *LPLONG; 34 | typedef PVOID LPVOID; 35 | typedef void VOID; 36 | typedef USHORT *PUSHORT; 37 | typedef unsigned long long int ULONGLONG; 38 | 39 | typedef struct _OVERLAPPED { 40 | DWORD Internal; 41 | DWORD InternalHigh; 42 | union { 43 | struct{ 44 | DWORD Offset; 45 | DWORD OffsetHigh; 46 | }; 47 | PVOID Pointer; 48 | }; 49 | HANDLE hEvent; 50 | } OVERLAPPED, *LPOVERLAPPED; 51 | 52 | typedef struct _SECURITY_ATTRIBUTES { 53 | DWORD nLength; 54 | LPVOID lpSecurityDescriptor; 55 | BOOL bInheritHandle; 56 | } SECURITY_ATTRIBUTES , *LPSECURITY_ATTRIBUTES; 57 | 58 | #include 59 | // Substitute for HANDLE returned by Windows CreateEvent API. 60 | // FT_SetEventNotification expects parameter 3 to be the address 61 | // of one of these structures. 62 | typedef struct _EVENT_HANDLE 63 | { 64 | pthread_cond_t eCondVar; 65 | pthread_mutex_t eMutex; 66 | int iVar; 67 | } EVENT_HANDLE; 68 | 69 | typedef struct timeval SYSTEMTIME; 70 | typedef struct timeval FILETIME; 71 | 72 | // WaitForSingleObject return values. 73 | #define WAIT_ABANDONED 0x00000080L 74 | #define WAIT_OBJECT_0 0x00000000L 75 | #define WAIT_TIMEOUT 0x00000102L 76 | #define WAIT_FAILED 0xFFFFFFFF 77 | // Special value for WaitForSingleObject dwMilliseconds parameter 78 | #define INFINITE 0xFFFFFFFF // Infinite timeout 79 | 80 | #ifndef TRUE 81 | #define TRUE 1 82 | #endif 83 | #ifndef FALSE 84 | #define FALSE 0 85 | #endif 86 | #ifndef CONST 87 | #define CONST const 88 | #endif 89 | // 90 | // Modem Status Flags 91 | // 92 | #define MS_CTS_ON ((DWORD)0x0010) 93 | #define MS_DSR_ON ((DWORD)0x0020) 94 | #define MS_RING_ON ((DWORD)0x0040) 95 | #define MS_RLSD_ON ((DWORD)0x0080) 96 | 97 | // 98 | // Error Flags 99 | // 100 | #define CE_RXOVER 0x0001 // Receive Queue overflow 101 | #define CE_OVERRUN 0x0002 // Receive Overrun Error 102 | #define CE_RXPARITY 0x0004 // Receive Parity Error 103 | #define CE_FRAME 0x0008 // Receive Framing error 104 | #define CE_BREAK 0x0010 // Break Detected 105 | #define CE_TXFULL 0x0100 // TX Queue is full 106 | #define CE_PTO 0x0200 // LPTx Timeout 107 | #define CE_IOE 0x0400 // LPTx I/O Error 108 | #define CE_DNS 0x0800 // LPTx Device not selected 109 | #define CE_OOP 0x1000 // LPTx Out-Of-Paper 110 | #define CE_MODE 0x8000 // Requested mode unsupported 111 | 112 | // 113 | // Events 114 | // 115 | #define EV_RXCHAR 0x0001 // Any Character received 116 | #define EV_RXFLAG 0x0002 // Received certain character 117 | #define EV_TXEMPTY 0x0004 // Transmit Queue Empty 118 | #define EV_CTS 0x0008 // CTS changed state 119 | #define EV_DSR 0x0010 // DSR changed state 120 | #define EV_RLSD 0x0020 // RLSD changed state 121 | #define EV_BREAK 0x0040 // BREAK received 122 | #define EV_ERR 0x0080 // Line status error occurred 123 | #define EV_RING 0x0100 // Ring signal detected 124 | #define EV_PERR 0x0200 // Printer error occured 125 | #define EV_RX80FULL 0x0400 // Receive buffer is 80 percent full 126 | #define EV_EVENT1 0x0800 // Provider specific event 1 127 | #define EV_EVENT2 0x1000 // Provider specific event 2 128 | 129 | // 130 | // Escape Functions 131 | // 132 | #define SETXOFF 1 // Simulate XOFF received 133 | #define SETXON 2 // Simulate XON received 134 | #define SETRTS 3 // Set RTS high 135 | #define CLRRTS 4 // Set RTS low 136 | #define SETDTR 5 // Set DTR high 137 | #define CLRDTR 6 // Set DTR low 138 | #define RESETDEV 7 // Reset device if possible 139 | #define SETBREAK 8 // Set the device break line. 140 | #define CLRBREAK 9 // Clear the device break line. 141 | 142 | // 143 | // PURGE function flags. 144 | // 145 | #define PURGE_TXABORT 0x0001 // Kill the pending/current writes to the comm port. 146 | #define PURGE_RXABORT 0x0002 // Kill the pending/current reads to the comm port. 147 | #define PURGE_TXCLEAR 0x0004 // Kill the transmit queue if there. 148 | #define PURGE_RXCLEAR 0x0008 // Kill the typeahead buffer if there. 149 | 150 | #ifndef INVALID_HANDLE_VALUE 151 | #define INVALID_HANDLE_VALUE 0xFFFFFFFF 152 | #endif 153 | 154 | #endif /* __WINDOWS_TYPES__ */ 155 | -------------------------------------------------------------------------------- /src/config_type.h: -------------------------------------------------------------------------------- 1 | /* 2 | * config_def.h 3 | * 4 | * Created on: Feb 11, 2019 5 | * Author: justin 6 | */ 7 | 8 | #ifndef CONFIG_TYPE_H_ 9 | #define CONFIG_TYPE_H_ 10 | 11 | #ifdef _WIN32 12 | #include 13 | #else 14 | #include "WinTypes.h" 15 | #endif 16 | #include "ftd2xx.h" 17 | 18 | typedef struct config_data { 19 | 20 | DWORD Signature1; // Header - must be 0x00000000 21 | DWORD Signature2; // Header - must be 0xffffffff 22 | DWORD Version; // Header - FT_PROGRAM_DATA version 23 | // 0 = original 24 | // 1 = FT2232 extensions 25 | // 2 = FT232R extensions 26 | // 3 = FT2232H extensions 27 | // 4 = FT4232H extensions 28 | // 5 = FT232H extensions 29 | 30 | WORD VendorId; // 0x0403 31 | WORD ProductId; // 0x6001 32 | WORD MaxPower; // 0 < MaxPower <= 500 33 | WORD PnP; // 0 = disabled, 1 = enabled 34 | WORD SelfPowered; // 0 = bus powered, 1 = self powered 35 | WORD RemoteWakeup; // 0 = not capable, 1 = capable 36 | // 37 | // Rev4 (FT232B) extensions 38 | // 39 | UCHAR Rev4; // non-zero if Rev4 chip, zero otherwise 40 | UCHAR IsoIn; // non-zero if in endpoint is isochronous 41 | UCHAR IsoOut; // non-zero if out endpoint is isochronous 42 | UCHAR PullDownEnable; // non-zero if pull down enabled 43 | UCHAR SerNumEnable; // non-zero if serial number to be used 44 | UCHAR USBVersionEnable; // non-zero if chip uses USBVersion 45 | WORD USBVersion; // BCD (0x0200 => USB2) 46 | // 47 | // Rev 5 (FT2232) extensions 48 | // 49 | UCHAR Rev5; // non-zero if Rev5 chip, zero otherwise 50 | UCHAR IsoInA; // non-zero if in endpoint is isochronous 51 | UCHAR IsoInB; // non-zero if in endpoint is isochronous 52 | UCHAR IsoOutA; // non-zero if out endpoint is isochronous 53 | UCHAR IsoOutB; // non-zero if out endpoint is isochronous 54 | UCHAR PullDownEnable5; // non-zero if pull down enabled 55 | UCHAR SerNumEnable5; // non-zero if serial number to be used 56 | UCHAR USBVersionEnable5; // non-zero if chip uses USBVersion 57 | WORD USBVersion5; // BCD (0x0200 => USB2) 58 | UCHAR AIsHighCurrent; // non-zero if interface is high current 59 | UCHAR BIsHighCurrent; // non-zero if interface is high current 60 | UCHAR IFAIsFifo; // non-zero if interface is 245 FIFO 61 | UCHAR IFAIsFifoTar; // non-zero if interface is 245 FIFO CPU target 62 | UCHAR IFAIsFastSer; // non-zero if interface is Fast serial 63 | UCHAR AIsVCP; // non-zero if interface is to use VCP drivers 64 | UCHAR IFBIsFifo; // non-zero if interface is 245 FIFO 65 | UCHAR IFBIsFifoTar; // non-zero if interface is 245 FIFO CPU target 66 | UCHAR IFBIsFastSer; // non-zero if interface is Fast serial 67 | UCHAR BIsVCP; // non-zero if interface is to use VCP drivers 68 | // 69 | // Rev 6 (FT232R) extensions 70 | // 71 | UCHAR UseExtOsc; // Use External Oscillator 72 | UCHAR HighDriveIOs; // High Drive I/Os 73 | UCHAR EndpointSize; // Endpoint size 74 | UCHAR PullDownEnableR; // non-zero if pull down enabled 75 | UCHAR SerNumEnableR; // non-zero if serial number to be used 76 | UCHAR InvertTXD; // non-zero if invert TXD 77 | UCHAR InvertRXD; // non-zero if invert RXD 78 | UCHAR InvertRTS; // non-zero if invert RTS 79 | UCHAR InvertCTS; // non-zero if invert CTS 80 | UCHAR InvertDTR; // non-zero if invert DTR 81 | UCHAR InvertDSR; // non-zero if invert DSR 82 | UCHAR InvertDCD; // non-zero if invert DCD 83 | UCHAR InvertRI; // non-zero if invert RI 84 | UCHAR Cbus0; // Cbus Mux control 85 | UCHAR Cbus1; // Cbus Mux control 86 | UCHAR Cbus2; // Cbus Mux control 87 | UCHAR Cbus3; // Cbus Mux control 88 | UCHAR Cbus4; // Cbus Mux control 89 | UCHAR RIsD2XX; // non-zero if using D2XX driver 90 | // 91 | // Rev 7 (FT2232H) Extensions 92 | // 93 | UCHAR PullDownEnable7; // non-zero if pull down enabled 94 | UCHAR SerNumEnable7; // non-zero if serial number to be used 95 | UCHAR ALSlowSlew; // non-zero if AL pins have slow slew 96 | UCHAR ALSchmittInput; // non-zero if AL pins are Schmitt input 97 | UCHAR ALDriveCurrent; // valid values are 4mA, 8mA, 12mA, 16mA 98 | UCHAR AHSlowSlew; // non-zero if AH pins have slow slew 99 | UCHAR AHSchmittInput; // non-zero if AH pins are Schmitt input 100 | UCHAR AHDriveCurrent; // valid values are 4mA, 8mA, 12mA, 16mA 101 | UCHAR BLSlowSlew; // non-zero if BL pins have slow slew 102 | UCHAR BLSchmittInput; // non-zero if BL pins are Schmitt input 103 | UCHAR BLDriveCurrent; // valid values are 4mA, 8mA, 12mA, 16mA 104 | UCHAR BHSlowSlew; // non-zero if BH pins have slow slew 105 | UCHAR BHSchmittInput; // non-zero if BH pins are Schmitt input 106 | UCHAR BHDriveCurrent; // valid values are 4mA, 8mA, 12mA, 16mA 107 | UCHAR IFAIsFifo7; // non-zero if interface is 245 FIFO 108 | UCHAR IFAIsFifoTar7; // non-zero if interface is 245 FIFO CPU target 109 | UCHAR IFAIsFastSer7; // non-zero if interface is Fast serial 110 | UCHAR AIsVCP7; // non-zero if interface is to use VCP drivers 111 | UCHAR IFBIsFifo7; // non-zero if interface is 245 FIFO 112 | UCHAR IFBIsFifoTar7; // non-zero if interface is 245 FIFO CPU target 113 | UCHAR IFBIsFastSer7; // non-zero if interface is Fast serial 114 | UCHAR BIsVCP7; // non-zero if interface is to use VCP drivers 115 | UCHAR PowerSaveEnable; // non-zero if using BCBUS7 to save power for self-powered designs 116 | // 117 | // Rev 8 (FT4232H) Extensions 118 | // 119 | UCHAR PullDownEnable8; // non-zero if pull down enabled 120 | UCHAR SerNumEnable8; // non-zero if serial number to be used 121 | UCHAR ASlowSlew; // non-zero if A pins have slow slew 122 | UCHAR ASchmittInput; // non-zero if A pins are Schmitt input 123 | UCHAR ADriveCurrent; // valid values are 4mA, 8mA, 12mA, 16mA 124 | UCHAR BSlowSlew; // non-zero if B pins have slow slew 125 | UCHAR BSchmittInput; // non-zero if B pins are Schmitt input 126 | UCHAR BDriveCurrent; // valid values are 4mA, 8mA, 12mA, 16mA 127 | UCHAR CSlowSlew; // non-zero if C pins have slow slew 128 | UCHAR CSchmittInput; // non-zero if C pins are Schmitt input 129 | UCHAR CDriveCurrent; // valid values are 4mA, 8mA, 12mA, 16mA 130 | UCHAR DSlowSlew; // non-zero if D pins have slow slew 131 | UCHAR DSchmittInput; // non-zero if D pins are Schmitt input 132 | UCHAR DDriveCurrent; // valid values are 4mA, 8mA, 12mA, 16mA 133 | UCHAR ARIIsTXDEN; // non-zero if port A uses RI as RS485 TXDEN 134 | UCHAR BRIIsTXDEN; // non-zero if port B uses RI as RS485 TXDEN 135 | UCHAR CRIIsTXDEN; // non-zero if port C uses RI as RS485 TXDEN 136 | UCHAR DRIIsTXDEN; // non-zero if port D uses RI as RS485 TXDEN 137 | UCHAR AIsVCP8; // non-zero if interface is to use VCP drivers 138 | UCHAR BIsVCP8; // non-zero if interface is to use VCP drivers 139 | UCHAR CIsVCP8; // non-zero if interface is to use VCP drivers 140 | UCHAR DIsVCP8; // non-zero if interface is to use VCP drivers 141 | // 142 | // Rev 9 (FT232H) Extensions 143 | // 144 | UCHAR PullDownEnableH; // non-zero if pull down enabled 145 | UCHAR SerNumEnableH; // non-zero if serial number to be used 146 | UCHAR ACSlowSlewH; // non-zero if AC pins have slow slew 147 | UCHAR ACSchmittInputH; // non-zero if AC pins are Schmitt input 148 | UCHAR ACDriveCurrentH; // valid values are 4mA, 8mA, 12mA, 16mA 149 | UCHAR ADSlowSlewH; // non-zero if AD pins have slow slew 150 | UCHAR ADSchmittInputH; // non-zero if AD pins are Schmitt input 151 | UCHAR ADDriveCurrentH; // valid values are 4mA, 8mA, 12mA, 16mA 152 | UCHAR Cbus0H; // Cbus Mux control 153 | UCHAR Cbus1H; // Cbus Mux control 154 | UCHAR Cbus2H; // Cbus Mux control 155 | UCHAR Cbus3H; // Cbus Mux control 156 | UCHAR Cbus4H; // Cbus Mux control 157 | UCHAR Cbus5H; // Cbus Mux control 158 | UCHAR Cbus6H; // Cbus Mux control 159 | UCHAR Cbus7H; // Cbus Mux control 160 | UCHAR Cbus8H; // Cbus Mux control 161 | UCHAR Cbus9H; // Cbus Mux control 162 | UCHAR IsFifoH; // non-zero if interface is 245 FIFO 163 | UCHAR IsFifoTarH; // non-zero if interface is 245 FIFO CPU target 164 | UCHAR IsFastSerH; // non-zero if interface is Fast serial 165 | UCHAR IsFT1248H; // non-zero if interface is FT1248 166 | UCHAR FT1248CpolH; // FT1248 clock polarity - clock idle high (1) or clock idle low (0) 167 | UCHAR FT1248LsbH; // FT1248 data is LSB (1) or MSB (0) 168 | UCHAR FT1248FlowControlH; // FT1248 flow control enable 169 | UCHAR IsVCPH; // non-zero if interface is to use VCP drivers 170 | UCHAR PowerSaveEnableH; // non-zero if using ACBUS7 to save power for self-powered designs 171 | 172 | } CONFIG_DATA; 173 | 174 | void ft_to_config(CONFIG_DATA*, PFT_PROGRAM_DATA); 175 | void config_to_ft(PFT_PROGRAM_DATA, CONFIG_DATA*); 176 | 177 | #endif /* CONFIG_TYPE_H_ */ 178 | -------------------------------------------------------------------------------- /src/config_type.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * config_type.cpp 3 | * 4 | * Created on: Feb 11, 2019 5 | * Author: justin 6 | */ 7 | 8 | #include "config_type.h" 9 | #ifdef _WIN32 10 | #include 11 | #else 12 | #include "WinTypes.h" 13 | #endif 14 | #include "ftd2xx.h" 15 | 16 | void ft_to_config(CONFIG_DATA* dest, PFT_PROGRAM_DATA src) { 17 | dest->Signature1 = src->Signature1; 18 | dest->Signature2 = src->Signature2; 19 | dest->Version = src->Version; 20 | dest->VendorId = src->VendorId; 21 | dest->ProductId = src->ProductId; 22 | dest->MaxPower = src->MaxPower; 23 | dest->PnP = src->PnP; 24 | dest->SelfPowered = src->SelfPowered; 25 | dest->RemoteWakeup = src->RemoteWakeup; 26 | dest->Rev4 = src->Rev4; 27 | dest->IsoIn = src->IsoIn; 28 | dest->IsoOut = src->IsoOut; 29 | dest->PullDownEnable = src->PullDownEnable; 30 | dest->SerNumEnable = src->SerNumEnable; 31 | dest->USBVersionEnable = src->USBVersionEnable; 32 | dest->USBVersion = src->USBVersion; 33 | dest->Rev5 = src->Rev5; 34 | dest->IsoInA = src->IsoInA; 35 | dest->IsoInB = src->IsoInB; 36 | dest->IsoOutA = src->IsoOutA; 37 | dest->IsoOutB = src->IsoOutB; 38 | dest->PullDownEnable5 = src->PullDownEnable5; 39 | dest->SerNumEnable5 = src->SerNumEnable5; 40 | dest->USBVersionEnable5 = src->USBVersionEnable5; 41 | dest->USBVersion5 = src->USBVersion5; 42 | dest->AIsHighCurrent = src->AIsHighCurrent; 43 | dest->BIsHighCurrent = src->BIsHighCurrent; 44 | dest->IFAIsFifo = src->IFAIsFifo; 45 | dest->IFAIsFifoTar = src->IFAIsFifoTar; 46 | dest->IFAIsFastSer = src->IFAIsFastSer; 47 | dest->AIsVCP = src->AIsVCP; 48 | dest->IFBIsFifo = src->IFBIsFifo; 49 | dest->IFBIsFifoTar = src->IFBIsFifoTar; 50 | dest->IFBIsFastSer = src->IFBIsFastSer; 51 | dest->BIsVCP = src->BIsVCP; 52 | dest->UseExtOsc = src->UseExtOsc; 53 | dest->HighDriveIOs = src->HighDriveIOs; 54 | dest->EndpointSize = src->EndpointSize; 55 | dest->PullDownEnableR = src->PullDownEnableR; 56 | dest->SerNumEnableR = src->SerNumEnableR; 57 | dest->InvertTXD = src->InvertTXD; 58 | dest->InvertRXD = src->InvertRXD; 59 | dest->InvertRTS = src->InvertRTS; 60 | dest->InvertCTS = src->InvertCTS; 61 | dest->InvertDTR = src->InvertDTR; 62 | dest->InvertDSR = src->InvertDSR; 63 | dest->InvertDCD = src->InvertDCD; 64 | dest->InvertRI = src->InvertRI; 65 | dest->Cbus0 = src->Cbus0; 66 | dest->Cbus1 = src->Cbus1; 67 | dest->Cbus2 = src->Cbus2; 68 | dest->Cbus3 = src->Cbus3; 69 | dest->Cbus4 = src->Cbus4; 70 | dest->RIsD2XX = src->RIsD2XX; 71 | dest->PullDownEnable7 = src->PullDownEnable7; 72 | dest->SerNumEnable7 = src->SerNumEnable7; 73 | dest->ALSlowSlew = src->ALSlowSlew; 74 | dest->ALSchmittInput = src->ALSchmittInput; 75 | dest->ALDriveCurrent = src->ALDriveCurrent; 76 | dest->AHSlowSlew = src->AHSlowSlew; 77 | dest->AHSchmittInput = src->AHSchmittInput; 78 | dest->AHDriveCurrent = src->AHDriveCurrent; 79 | dest->BLSlowSlew = src->BLSlowSlew; 80 | dest->BLSchmittInput = src->BLSchmittInput; 81 | dest->BLDriveCurrent = src->BLDriveCurrent; 82 | dest->BHSlowSlew = src->BHSlowSlew; 83 | dest->BHSchmittInput = src->BHSchmittInput; 84 | dest->BHDriveCurrent = src->BHDriveCurrent; 85 | dest->IFAIsFifo7 = src->IFAIsFifo7; 86 | dest->IFAIsFifoTar7 = src->IFAIsFifoTar7; 87 | dest->IFAIsFastSer7 = src->IFAIsFastSer7; 88 | dest->AIsVCP7 = src->AIsVCP7; 89 | dest->IFBIsFifo7 = src->IFBIsFifo7; 90 | dest->IFBIsFifoTar7 = src->IFBIsFifoTar7; 91 | dest->IFBIsFastSer7 = src->IFBIsFastSer7; 92 | dest->BIsVCP7 = src->BIsVCP7; 93 | dest->PowerSaveEnable = src->PowerSaveEnable; 94 | dest->PullDownEnable8 = src->PullDownEnable8; 95 | dest->SerNumEnable8 = src->SerNumEnable8; 96 | dest->ASlowSlew = src->ASlowSlew; 97 | dest->ASchmittInput = src->ASchmittInput; 98 | dest->ADriveCurrent = src->ADriveCurrent; 99 | dest->BSlowSlew = src->BSlowSlew; 100 | dest->BSchmittInput = src->BSchmittInput; 101 | dest->BDriveCurrent = src->BDriveCurrent; 102 | dest->CSlowSlew = src->CSlowSlew; 103 | dest->CSchmittInput = src->CSchmittInput; 104 | dest->CDriveCurrent = src->CDriveCurrent; 105 | dest->DSlowSlew = src->DSlowSlew; 106 | dest->DSchmittInput = src->DSchmittInput; 107 | dest->DDriveCurrent = src->DDriveCurrent; 108 | dest->ARIIsTXDEN = src->ARIIsTXDEN; 109 | dest->BRIIsTXDEN = src->BRIIsTXDEN; 110 | dest->CRIIsTXDEN = src->CRIIsTXDEN; 111 | dest->DRIIsTXDEN = src->DRIIsTXDEN; 112 | dest->AIsVCP8 = src->AIsVCP8; 113 | dest->BIsVCP8 = src->BIsVCP8; 114 | dest->CIsVCP8 = src->CIsVCP8; 115 | dest->DIsVCP8 = src->DIsVCP8; 116 | dest->PullDownEnableH = src->PullDownEnableH; 117 | dest->SerNumEnableH = src->SerNumEnableH; 118 | dest->ACSlowSlewH = src->ACSlowSlewH; 119 | dest->ACSchmittInputH = src->ACSchmittInputH; 120 | dest->ACDriveCurrentH = src->ACDriveCurrentH; 121 | dest->ADSlowSlewH = src->ADSlowSlewH; 122 | dest->ADSchmittInputH = src->ADSchmittInputH; 123 | dest->ADDriveCurrentH = src->ADDriveCurrentH; 124 | dest->Cbus0H = src->Cbus0H; 125 | dest->Cbus1H = src->Cbus1H; 126 | dest->Cbus2H = src->Cbus2H; 127 | dest->Cbus3H = src->Cbus3H; 128 | dest->Cbus4H = src->Cbus4H; 129 | dest->Cbus5H = src->Cbus5H; 130 | dest->Cbus6H = src->Cbus6H; 131 | dest->Cbus7H = src->Cbus7H; 132 | dest->Cbus8H = src->Cbus8H; 133 | dest->Cbus9H = src->Cbus9H; 134 | dest->IsFifoH = src->IsFifoH; 135 | dest->IsFifoTarH = src->IsFifoTarH; 136 | dest->IsFastSerH = src->IsFastSerH; 137 | dest->IsFT1248H = src->IsFT1248H; 138 | dest->FT1248CpolH = src->FT1248CpolH; 139 | dest->FT1248LsbH = src->FT1248LsbH; 140 | dest->FT1248FlowControlH = src->FT1248FlowControlH; 141 | dest->IsVCPH = src->IsVCPH; 142 | dest->PowerSaveEnableH = src->PowerSaveEnableH; 143 | } 144 | 145 | void config_to_ft(PFT_PROGRAM_DATA dest, CONFIG_DATA* src) { 146 | dest->Signature1 = src->Signature1; 147 | dest->Signature2 = src->Signature2; 148 | dest->Version = src->Version; 149 | dest->VendorId = src->VendorId; 150 | dest->ProductId = src->ProductId; 151 | dest->MaxPower = src->MaxPower; 152 | dest->PnP = src->PnP; 153 | dest->SelfPowered = src->SelfPowered; 154 | dest->RemoteWakeup = src->RemoteWakeup; 155 | dest->Rev4 = src->Rev4; 156 | dest->IsoIn = src->IsoIn; 157 | dest->IsoOut = src->IsoOut; 158 | dest->PullDownEnable = src->PullDownEnable; 159 | dest->SerNumEnable = src->SerNumEnable; 160 | dest->USBVersionEnable = src->USBVersionEnable; 161 | dest->USBVersion = src->USBVersion; 162 | dest->Rev5 = src->Rev5; 163 | dest->IsoInA = src->IsoInA; 164 | dest->IsoInB = src->IsoInB; 165 | dest->IsoOutA = src->IsoOutA; 166 | dest->IsoOutB = src->IsoOutB; 167 | dest->PullDownEnable5 = src->PullDownEnable5; 168 | dest->SerNumEnable5 = src->SerNumEnable5; 169 | dest->USBVersionEnable5 = src->USBVersionEnable5; 170 | dest->USBVersion5 = src->USBVersion5; 171 | dest->AIsHighCurrent = src->AIsHighCurrent; 172 | dest->BIsHighCurrent = src->BIsHighCurrent; 173 | dest->IFAIsFifo = src->IFAIsFifo; 174 | dest->IFAIsFifoTar = src->IFAIsFifoTar; 175 | dest->IFAIsFastSer = src->IFAIsFastSer; 176 | dest->AIsVCP = src->AIsVCP; 177 | dest->IFBIsFifo = src->IFBIsFifo; 178 | dest->IFBIsFifoTar = src->IFBIsFifoTar; 179 | dest->IFBIsFastSer = src->IFBIsFastSer; 180 | dest->BIsVCP = src->BIsVCP; 181 | dest->UseExtOsc = src->UseExtOsc; 182 | dest->HighDriveIOs = src->HighDriveIOs; 183 | dest->EndpointSize = src->EndpointSize; 184 | dest->PullDownEnableR = src->PullDownEnableR; 185 | dest->SerNumEnableR = src->SerNumEnableR; 186 | dest->InvertTXD = src->InvertTXD; 187 | dest->InvertRXD = src->InvertRXD; 188 | dest->InvertRTS = src->InvertRTS; 189 | dest->InvertCTS = src->InvertCTS; 190 | dest->InvertDTR = src->InvertDTR; 191 | dest->InvertDSR = src->InvertDSR; 192 | dest->InvertDCD = src->InvertDCD; 193 | dest->InvertRI = src->InvertRI; 194 | dest->Cbus0 = src->Cbus0; 195 | dest->Cbus1 = src->Cbus1; 196 | dest->Cbus2 = src->Cbus2; 197 | dest->Cbus3 = src->Cbus3; 198 | dest->Cbus4 = src->Cbus4; 199 | dest->RIsD2XX = src->RIsD2XX; 200 | dest->PullDownEnable7 = src->PullDownEnable7; 201 | dest->SerNumEnable7 = src->SerNumEnable7; 202 | dest->ALSlowSlew = src->ALSlowSlew; 203 | dest->ALSchmittInput = src->ALSchmittInput; 204 | dest->ALDriveCurrent = src->ALDriveCurrent; 205 | dest->AHSlowSlew = src->AHSlowSlew; 206 | dest->AHSchmittInput = src->AHSchmittInput; 207 | dest->AHDriveCurrent = src->AHDriveCurrent; 208 | dest->BLSlowSlew = src->BLSlowSlew; 209 | dest->BLSchmittInput = src->BLSchmittInput; 210 | dest->BLDriveCurrent = src->BLDriveCurrent; 211 | dest->BHSlowSlew = src->BHSlowSlew; 212 | dest->BHSchmittInput = src->BHSchmittInput; 213 | dest->BHDriveCurrent = src->BHDriveCurrent; 214 | dest->IFAIsFifo7 = src->IFAIsFifo7; 215 | dest->IFAIsFifoTar7 = src->IFAIsFifoTar7; 216 | dest->IFAIsFastSer7 = src->IFAIsFastSer7; 217 | dest->AIsVCP7 = src->AIsVCP7; 218 | dest->IFBIsFifo7 = src->IFBIsFifo7; 219 | dest->IFBIsFifoTar7 = src->IFBIsFifoTar7; 220 | dest->IFBIsFastSer7 = src->IFBIsFastSer7; 221 | dest->BIsVCP7 = src->BIsVCP7; 222 | dest->PowerSaveEnable = src->PowerSaveEnable; 223 | dest->PullDownEnable8 = src->PullDownEnable8; 224 | dest->SerNumEnable8 = src->SerNumEnable8; 225 | dest->ASlowSlew = src->ASlowSlew; 226 | dest->ASchmittInput = src->ASchmittInput; 227 | dest->ADriveCurrent = src->ADriveCurrent; 228 | dest->BSlowSlew = src->BSlowSlew; 229 | dest->BSchmittInput = src->BSchmittInput; 230 | dest->BDriveCurrent = src->BDriveCurrent; 231 | dest->CSlowSlew = src->CSlowSlew; 232 | dest->CSchmittInput = src->CSchmittInput; 233 | dest->CDriveCurrent = src->CDriveCurrent; 234 | dest->DSlowSlew = src->DSlowSlew; 235 | dest->DSchmittInput = src->DSchmittInput; 236 | dest->DDriveCurrent = src->DDriveCurrent; 237 | dest->ARIIsTXDEN = src->ARIIsTXDEN; 238 | dest->BRIIsTXDEN = src->BRIIsTXDEN; 239 | dest->CRIIsTXDEN = src->CRIIsTXDEN; 240 | dest->DRIIsTXDEN = src->DRIIsTXDEN; 241 | dest->AIsVCP8 = src->AIsVCP8; 242 | dest->BIsVCP8 = src->BIsVCP8; 243 | dest->CIsVCP8 = src->CIsVCP8; 244 | dest->DIsVCP8 = src->DIsVCP8; 245 | dest->PullDownEnableH = src->PullDownEnableH; 246 | dest->SerNumEnableH = src->SerNumEnableH; 247 | dest->ACSlowSlewH = src->ACSlowSlewH; 248 | dest->ACSchmittInputH = src->ACSchmittInputH; 249 | dest->ACDriveCurrentH = src->ACDriveCurrentH; 250 | dest->ADSlowSlewH = src->ADSlowSlewH; 251 | dest->ADSchmittInputH = src->ADSchmittInputH; 252 | dest->ADDriveCurrentH = src->ADDriveCurrentH; 253 | dest->Cbus0H = src->Cbus0H; 254 | dest->Cbus1H = src->Cbus1H; 255 | dest->Cbus2H = src->Cbus2H; 256 | dest->Cbus3H = src->Cbus3H; 257 | dest->Cbus4H = src->Cbus4H; 258 | dest->Cbus5H = src->Cbus5H; 259 | dest->Cbus6H = src->Cbus6H; 260 | dest->Cbus7H = src->Cbus7H; 261 | dest->Cbus8H = src->Cbus8H; 262 | dest->Cbus9H = src->Cbus9H; 263 | dest->IsFifoH = src->IsFifoH; 264 | dest->IsFifoTarH = src->IsFifoTarH; 265 | dest->IsFastSerH = src->IsFastSerH; 266 | dest->IsFT1248H = src->IsFT1248H; 267 | dest->FT1248CpolH = src->FT1248CpolH; 268 | dest->FT1248LsbH = src->FT1248LsbH; 269 | dest->FT1248FlowControlH = src->FT1248FlowControlH; 270 | dest->IsVCPH = src->IsVCPH; 271 | dest->PowerSaveEnableH = src->PowerSaveEnableH; 272 | } 273 | -------------------------------------------------------------------------------- /src/loader.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Loader.cpp 3 | * 4 | * Created on: Nov 6, 2017 5 | * Author: justin 6 | */ 7 | 8 | #include "loader.h" 9 | #include "jtag.h" 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include "config_type.h" 17 | #ifdef _WIN32 18 | #include "mingw.thread.h" 19 | #else 20 | #include 21 | #endif 22 | 23 | 24 | using namespace std; 25 | 26 | Loader::Loader(Jtag *dev) { 27 | device = dev; 28 | currentState = Jtag_fsm::TEST_LOGIC_RESET; 29 | } 30 | bool Loader::setState(Jtag_fsm::State state) { 31 | if (!device->navigateToState(currentState, state)) 32 | return false; 33 | currentState = state; 34 | return true; 35 | 36 | } 37 | 38 | bool Loader::resetState() { 39 | currentState = Jtag_fsm::TEST_LOGIC_RESET; 40 | return device->navigateToState(Jtag_fsm::CAPTURE_DR, 41 | Jtag_fsm::TEST_LOGIC_RESET); 42 | } 43 | 44 | bool Loader::setIR(Instruction inst) { 45 | stringstream inst_str; 46 | inst_str << std::setfill('0') << std::setw(2) << hex 47 | << static_cast(inst); 48 | 49 | if (!device->navigateToState(currentState, Jtag_fsm::SHIFT_IR)) { 50 | cerr << "Failed to change to SHIFT_IR state!" << endl; 51 | return false; 52 | } 53 | if (!device->shiftData(6, inst_str.str(), "", "")) { 54 | cerr << "Failed to shift instruction data!" << endl; 55 | return false; 56 | } 57 | if (!device->navigateToState(Jtag_fsm::EXIT1_IR, Jtag_fsm::RUN_TEST_IDLE)) { 58 | cerr << "Failed to change to RUN_TEST_IDLE state!" << endl; 59 | return false; 60 | } 61 | currentState = Jtag_fsm::RUN_TEST_IDLE; 62 | return true; 63 | } 64 | 65 | // basically the same as shiftDR but ignores the first four bits 66 | bool Loader::shiftUDR(int bits, string write, string read, string mask) { 67 | string uread = read; 68 | string umask = mask; 69 | string uwrite = write; 70 | 71 | if (!read.empty()) { 72 | uread = read + "0"; 73 | umask = mask + "0"; 74 | uwrite = "0" + write; 75 | bits += 4; 76 | } 77 | 78 | return shiftDR(bits, uwrite, uread, umask); 79 | } 80 | 81 | bool Loader::shiftDR(int bits, string write, string read, string mask) { 82 | if (!device->navigateToState(currentState, Jtag_fsm::SHIFT_DR)) { 83 | cerr << "Failed to change to SHIFT_DR state!" << endl; 84 | return false; 85 | } 86 | if (!device->shiftData(bits, write, read, mask)) { 87 | cerr << "Failed to shift data!" << endl; 88 | return false; 89 | } 90 | if (!device->navigateToState(Jtag_fsm::EXIT1_DR, Jtag_fsm::RUN_TEST_IDLE)) { 91 | cerr << "Failed to change to RUN_TEST_IDLE state!" << endl; 92 | return false; 93 | } 94 | currentState = Jtag_fsm::RUN_TEST_IDLE; 95 | return true; 96 | } 97 | 98 | bool Loader::shiftIR(int bits, string write, string read, string mask) { 99 | if (!device->navigateToState(currentState, Jtag_fsm::SHIFT_IR)) { 100 | cerr << "Failed to change to SHIFT_IR state!" << endl; 101 | return false; 102 | } 103 | if (!device->shiftData(bits, write, read, mask)) { 104 | cerr << "Failed to shift data!" << endl; 105 | return false; 106 | } 107 | if (!device->navigateToState(Jtag_fsm::EXIT1_IR, Jtag_fsm::RUN_TEST_IDLE)) { 108 | cerr << "Failed to change to RUN_TEST_IDLE state!" << endl; 109 | return false; 110 | } 111 | currentState = Jtag_fsm::RUN_TEST_IDLE; 112 | return true; 113 | } 114 | 115 | string Loader::shiftDR(int bits, string write) { 116 | if (!device->navigateToState(currentState, Jtag_fsm::SHIFT_DR)) { 117 | cerr << "Failed to change to SHIFT_DR state!" << endl; 118 | return NULL; 119 | } 120 | 121 | string data = device->shiftData(bits, write); 122 | if (data.empty()) { 123 | cerr << "Failed to shift data!" << endl; 124 | return NULL; 125 | } 126 | if (!device->navigateToState(Jtag_fsm::EXIT1_DR, Jtag_fsm::RUN_TEST_IDLE)) { 127 | cerr << "Failed to change to RUN_TEST_IDLE state!" << endl; 128 | return NULL; 129 | } 130 | currentState = Jtag_fsm::RUN_TEST_IDLE; 131 | return data; 132 | } 133 | 134 | bool Loader::loadBin(string file) { 135 | string binStr = fileToBinStr(file); 136 | 137 | if (binStr.empty()) { 138 | cerr << "Failed to read bin file: "+ file << endl; 139 | return false; 140 | } 141 | 142 | string reversedBinStr = reverseBytes(binStr); 143 | 144 | if (!device->setFreq(10000000)) { 145 | cerr << "Failed to set JTAG frequency!" << endl; 146 | return false; 147 | } 148 | if (!resetState()) 149 | return false; 150 | if (!setState(Jtag_fsm::RUN_TEST_IDLE)) 151 | return false; 152 | 153 | if (!setIR(JPROGRAM)) 154 | return false; 155 | if (!setIR(ISC_NOOP)) 156 | return false; 157 | std::this_thread::sleep_for(std::chrono::milliseconds(100)); 158 | 159 | // config/jprog/poll 160 | if (!device->sendClocks(10000)) 161 | return false; 162 | if (!shiftIR(6, "14", "11", "31")) 163 | return false; 164 | 165 | // config/slr 166 | if (!setIR(CFG_IN)) 167 | return false; 168 | if (!shiftDR(binStr.length() * 4, reversedBinStr, "", "")) 169 | return false; 170 | 171 | // config/start 172 | if (!setState(Jtag_fsm::RUN_TEST_IDLE)) 173 | return false; 174 | if (!device->sendClocks(100000)) 175 | return false; 176 | if (!setIR(JSTART)) 177 | return false; 178 | if (!setState(Jtag_fsm::RUN_TEST_IDLE)) 179 | return false; 180 | if (!device->sendClocks(100)) 181 | return false; 182 | if (!shiftIR(6, "09", "31", "11")) 183 | return false; 184 | 185 | // config/status 186 | if (!setState(Jtag_fsm::TEST_LOGIC_RESET)) 187 | return false; 188 | if (!device->sendClocks(5)) 189 | return false; 190 | if (!setIR(CFG_IN)) 191 | return false; 192 | if (!shiftDR(160, "0000000400000004800700140000000466aa9955", "", "")) 193 | return false; 194 | if (!setIR(CFG_OUT)) 195 | return false; 196 | if (!shiftDR(32, "00000000", "3f5e0d40", "08000000")) 197 | return false; 198 | if (!setState(Jtag_fsm::TEST_LOGIC_RESET)) 199 | return false; 200 | if (!device->sendClocks(5)) 201 | return false; 202 | 203 | return true; 204 | } 205 | 206 | string Loader::fileToBinStr(string file) { 207 | ifstream binFile(file); 208 | stringstream hexString; 209 | 210 | char *buffer; 211 | binFile.seekg(0, ios::end); 212 | unsigned int byteCount = binFile.tellg(); 213 | binFile.seekg(0, ios::beg); 214 | buffer = new char[byteCount]; 215 | binFile.read(buffer, byteCount); 216 | binFile.close(); 217 | 218 | for (int i = byteCount - 1; i >= 0; i--) { 219 | hexString << setfill('0') << setw(2) << std::hex 220 | << (int) ((unsigned char) buffer[i]); 221 | } 222 | 223 | delete buffer; 224 | 225 | return hexString.str(); 226 | } 227 | 228 | bool Loader::eraseFlash(string loaderFile) { 229 | cout << "Initializing FPGA..." << endl; 230 | if (!loadBin(loaderFile)) { 231 | cerr << "Failed to initialize FPGA!" << endl; 232 | return false; 233 | } 234 | 235 | if (!device->setFreq(1500000)) { 236 | cerr << "Failed to set JTAG frequency!" << endl; 237 | return false; 238 | } 239 | 240 | cout << "Erasing..." << endl; 241 | 242 | // Erase the flash 243 | if (!setIR(USER1)) 244 | return false; 245 | 246 | if (!shiftDR(1, "0", "", "")) 247 | return false; 248 | 249 | std::this_thread::sleep_for(std::chrono::seconds(1)); // wait for erase 250 | 251 | if (!setIR(JPROGRAM)) 252 | return false; 253 | 254 | // reset just for good measure 255 | if (!resetState()) 256 | return false; 257 | 258 | return true; 259 | } 260 | 261 | bool Loader::writeBin(string binFile, bool flash, string loaderFile) { 262 | if (flash) { 263 | string binStr = fileToBinStr(binFile); 264 | 265 | cout << "Initializing FPGA..." << endl; 266 | if (!loadBin(loaderFile)) { 267 | cerr << "Failed to initialize FPGA!" << endl; 268 | return false; 269 | } 270 | 271 | if (!device->setFreq(1500000)) { 272 | cerr << "Failed to set JTAG frequency!" << endl; 273 | return false; 274 | } 275 | 276 | cout << "Erasing..." << endl; 277 | 278 | // Erase the flash 279 | if (!setIR(USER1)) 280 | return false; 281 | 282 | if (!shiftDR(1, "0", "", "")) 283 | return false; 284 | 285 | std::this_thread::sleep_for(std::chrono::milliseconds(100)); 286 | 287 | cout << "Writing..." << endl; 288 | 289 | // Write the flash 290 | if (!setIR(USER2)) 291 | return false; 292 | 293 | if (!shiftDR(binStr.length() * 4, binStr, "", "")) 294 | return false; 295 | 296 | // If you enter the reset state after a write 297 | // the loader firmware resets the flash into 298 | // regular SPI mode and gets stuck in a dead FSM 299 | // state. You need to do this before issuing a 300 | // JPROGRAM command or the FPGA can't read the 301 | // flash. 302 | if (!resetState()) 303 | return false; 304 | 305 | std::this_thread::sleep_for(std::chrono::milliseconds(100)); // 100ms delay is required before issuing JPROGRAM 306 | 307 | cout << "Resetting FPGA..." << endl; 308 | // JPROGRAM resets the FPGA configuration and will 309 | // cause it to read the flash memory 310 | if (!setIR(JPROGRAM)) 311 | return false; 312 | } else { 313 | cout << "Programming FPGA..." << endl; 314 | if (!loadBin(binFile)) { 315 | cerr << "Failed to initialize FPGA!" << endl; 316 | return false; 317 | } 318 | } 319 | 320 | // reset just for good measure 321 | if (!resetState()) 322 | return false; 323 | 324 | cout << "Done." << endl; 325 | return true; 326 | } 327 | 328 | bool Loader::checkIDCODE() { 329 | if (!setIR(IDCODE)) 330 | return false; 331 | 332 | if (!shiftDR(32, "00000000", "0362D093", "0FFFFFFF")) // FPGA IDCODE 333 | return false; 334 | 335 | return true; 336 | } 337 | 338 | BYTE reverse(BYTE b) { 339 | b = (b & 0xF0) >> 4 | (b & 0x0F) << 4; 340 | b = (b & 0xCC) >> 2 | (b & 0x33) << 2; 341 | b = (b & 0xAA) >> 1 | (b & 0x55) << 1; 342 | return b; 343 | } 344 | 345 | bool Loader::setWREN() { 346 | if (!setIR(USER1)) 347 | return false; 348 | if (!shiftDR(8, reverseBytes("06"), "", "")) 349 | return false; 350 | return true; 351 | } 352 | 353 | int Loader::getStatus() { 354 | if (!setIR(USER1)) 355 | return -1; 356 | string data = shiftDR(17, reverseBytes("00005")); 357 | cout << data << endl; 358 | if (data.empty()) 359 | return -1; 360 | int status = stoi(data, 0, 16); 361 | status >>= 9; 362 | return reverse(status); 363 | } 364 | 365 | void hexToByte(string hex, BYTE* out) { 366 | int length = hex.length(); 367 | for (int i = 0; i < length / 2; i++) { 368 | out[i] = stoi(hex.substr(length - 2 - i * 2, 2), 0, 16); 369 | } 370 | if ((length & 1) != 0) 371 | out[length / 2] = stoi(hex.substr(0, 1), 0, 16); 372 | } 373 | 374 | string Loader::reverseBytes(string start) { 375 | unsigned long l = start.length(); 376 | if (l & 1) 377 | l++; 378 | l /= 2; 379 | BYTE* bytes = new BYTE[l]; 380 | hexToByte(start, bytes); 381 | for (unsigned long i = 0; i < l; i++) { 382 | bytes[i] = reverse(bytes[i]); 383 | } 384 | std::stringstream ss; 385 | for (long i = l - 1; i >= 0; i--) 386 | ss << setfill('0') << setw(2) << hex << (unsigned int) bytes[i]; 387 | string out = ss.str(); 388 | if (out.length() - 1 == start.length()) 389 | out = out.substr(1, out.length() - 1); 390 | delete bytes; 391 | return out; 392 | } 393 | 394 | -------------------------------------------------------------------------------- /src/mingw.thread.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file mingw.thread.h 3 | * @brief std::thread implementation for MinGW 4 | * (c) 2013-2016 by Mega Limited, Auckland, New Zealand 5 | * @author Alexander Vassilev 6 | * 7 | * @copyright Simplified (2-clause) BSD License. 8 | * You should have received a copy of the license along with this 9 | * program. 10 | * 11 | * This code is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 14 | * @note 15 | * This file may become part of the mingw-w64 runtime package. If/when this happens, 16 | * the appropriate license will be added, i.e. this code will become dual-licensed, 17 | * and the current BSD 2-clause license will stay. 18 | */ 19 | 20 | #ifndef WIN32STDTHREAD_H 21 | #define WIN32STDTHREAD_H 22 | 23 | #if !defined(__cplusplus) || (__cplusplus < 201103L) 24 | #error A C++11 compiler is required! 25 | #endif 26 | 27 | // Use the standard classes for std::, if available. 28 | #include 29 | 30 | #include // For std::size_t 31 | #include // Detect error type. 32 | #include // For std::terminate 33 | #include // For std::system_error 34 | #include // For std::hash 35 | #include // For std::tuple 36 | #include // For sleep timing. 37 | #include // For std::unique_ptr 38 | #include // Stream output for thread ids. 39 | #include // For std::swap, std::forward 40 | 41 | // For the invoke implementation only: 42 | #include // For std::result_of, etc. 43 | //#include // For std::forward 44 | //#include // For std::reference_wrapper 45 | 46 | #include 47 | #include // For _beginthreadex 48 | 49 | #ifndef NDEBUG 50 | #include 51 | #endif 52 | 53 | #if !defined(_WIN32_WINNT) || (_WIN32_WINNT < 0x0501) 54 | #error To use the MinGW-std-threads library, you will need to define the macro _WIN32_WINNT to be 0x0501 (Windows XP) or higher. 55 | #endif 56 | 57 | // Instead of INVALID_HANDLE_VALUE, _beginthreadex returns 0. 58 | namespace mingw_stdthread 59 | { 60 | namespace detail 61 | { 62 | // For compatibility, implement std::invoke for C++11 and C++14 63 | #if __cplusplus < 201703L 64 | template 65 | struct Invoker 66 | { 67 | template 68 | inline static typename std::result_of::type invoke (F&& f, Args&&... args) 69 | { 70 | return std::forward(f)(std::forward(args)...); 71 | } 72 | }; 73 | template 74 | struct InvokerHelper; 75 | 76 | template<> 77 | struct InvokerHelper 78 | { 79 | template 80 | inline static auto get (T1&& t1) -> decltype(*std::forward(t1)) 81 | { 82 | return *std::forward(t1); 83 | } 84 | 85 | template 86 | inline static auto get (const std::reference_wrapper& t1) -> decltype(t1.get()) 87 | { 88 | return t1.get(); 89 | } 90 | }; 91 | 92 | template<> 93 | struct InvokerHelper 94 | { 95 | template 96 | inline static auto get (T1&& t1) -> decltype(std::forward(t1)) 97 | { 98 | return std::forward(t1); 99 | } 100 | }; 101 | 102 | template<> 103 | struct Invoker 104 | { 105 | template 106 | inline static auto invoke (F T::* f, T1&& t1, Args&&... args) ->\ 107 | decltype((InvokerHelper::type>::value>::get(std::forward(t1)).*f)(std::forward(args)...)) 108 | { 109 | return (InvokerHelper::type>::value>::get(std::forward(t1)).*f)(std::forward(args)...); 110 | } 111 | }; 112 | 113 | template<> 114 | struct Invoker 115 | { 116 | template 117 | inline static auto invoke (F T::* f, T1&& t1, Args&&... args) ->\ 118 | decltype(InvokerHelper::type>::value>::get(t1).*f) 119 | { 120 | return InvokerHelper::type>::value>::get(t1).*f; 121 | } 122 | }; 123 | 124 | template 125 | struct InvokeResult 126 | { 127 | typedef Invoker::type>::value, 128 | std::is_member_object_pointer::type>::value && 129 | (sizeof...(Args) == 1)> invoker; 130 | inline static auto invoke (F&& f, Args&&... args) -> decltype(invoker::invoke(std::forward(f), std::forward(args)...)) 131 | { 132 | return invoker::invoke(std::forward(f), std::forward(args)...); 133 | }; 134 | }; 135 | 136 | template 137 | auto invoke (F&& f, Args&&... args) -> decltype(InvokeResult::invoke(std::forward(f), std::forward(args)...)) 138 | { 139 | return InvokeResult::invoke(std::forward(f), std::forward(args)...); 140 | } 141 | #else 142 | using std::invoke; 143 | #endif 144 | 145 | template 146 | struct IntSeq {}; 147 | 148 | template 149 | struct GenIntSeq : GenIntSeq { }; 150 | 151 | template 152 | struct GenIntSeq<0, S...> { typedef IntSeq type; }; 153 | 154 | // We can't define the Call struct in the function - the standard forbids template methods in that case 155 | template 156 | class ThreadFuncCall 157 | { 158 | typedef std::tuple Tuple; 159 | Func mFunc; 160 | Tuple mArgs; 161 | 162 | template 163 | void callFunc(detail::IntSeq) 164 | { 165 | detail::invoke(std::forward(mFunc), std::get(std::forward(mArgs)) ...); 166 | } 167 | public: 168 | ThreadFuncCall(Func&& aFunc, Args&&... aArgs) 169 | :mFunc(std::forward(aFunc)), mArgs(std::forward(aArgs)...){} 170 | 171 | void callFunc() 172 | { 173 | callFunc(typename detail::GenIntSeq::type()); 174 | } 175 | }; 176 | 177 | } // Namespace "detail" 178 | 179 | class thread 180 | { 181 | public: 182 | class id 183 | { 184 | DWORD mId; 185 | void clear() {mId = 0;} 186 | friend class thread; 187 | friend class std::hash; 188 | public: 189 | explicit id(DWORD aId=0) noexcept : mId(aId){} 190 | friend bool operator==(id x, id y) noexcept {return x.mId == y.mId; } 191 | friend bool operator!=(id x, id y) noexcept {return x.mId != y.mId; } 192 | friend bool operator< (id x, id y) noexcept {return x.mId < y.mId; } 193 | friend bool operator<=(id x, id y) noexcept {return x.mId <= y.mId; } 194 | friend bool operator> (id x, id y) noexcept {return x.mId > y.mId; } 195 | friend bool operator>=(id x, id y) noexcept {return x.mId >= y.mId; } 196 | 197 | template 198 | friend std::basic_ostream<_CharT, _Traits>& 199 | operator<<(std::basic_ostream<_CharT, _Traits>& __out, id __id) 200 | { 201 | if (__id.mId == 0) 202 | { 203 | return __out << "(invalid std::thread::id)"; 204 | } 205 | else 206 | { 207 | return __out << __id.mId; 208 | } 209 | } 210 | }; 211 | private: 212 | static constexpr HANDLE kInvalidHandle = nullptr; 213 | HANDLE mHandle; 214 | id mThreadId; 215 | 216 | template 217 | static unsigned __stdcall threadfunc(void* arg) 218 | { 219 | std::unique_ptr call(static_cast(arg)); 220 | call->callFunc(); 221 | return 0; 222 | } 223 | 224 | static unsigned int _hardware_concurrency_helper() noexcept 225 | { 226 | SYSTEM_INFO sysinfo; 227 | // This is one of the few functions used by the library which has a nearly- 228 | // equivalent function defined in earlier versions of Windows. Include the 229 | // workaround, just as a reminder that it does exist. 230 | #if defined(_WIN32_WINNT) && (_WIN32_WINNT >= 0x0501) 231 | ::GetNativeSystemInfo(&sysinfo); 232 | #else 233 | ::GetSystemInfo(&sysinfo); 234 | #endif 235 | return sysinfo.dwNumberOfProcessors; 236 | } 237 | public: 238 | typedef HANDLE native_handle_type; 239 | id get_id() const noexcept {return mThreadId;} 240 | native_handle_type native_handle() const {return mHandle;} 241 | thread(): mHandle(kInvalidHandle), mThreadId(){} 242 | 243 | thread(thread&& other) 244 | :mHandle(other.mHandle), mThreadId(other.mThreadId) 245 | { 246 | other.mHandle = kInvalidHandle; 247 | other.mThreadId.clear(); 248 | } 249 | 250 | thread(const thread &other)=delete; 251 | 252 | template 253 | explicit thread(Func&& func, Args&&... args) : mHandle(), mThreadId() 254 | { 255 | typedef detail::ThreadFuncCall Call; 256 | auto call = new Call( 257 | std::forward(func), std::forward(args)...); 258 | auto int_handle = _beginthreadex(NULL, 0, threadfunc, 259 | static_cast(call), 0, 260 | reinterpret_cast(&(mThreadId.mId))); 261 | if (int_handle == 0) 262 | { 263 | mHandle = kInvalidHandle; 264 | int errnum = errno; 265 | delete call; 266 | // Note: Should only throw EINVAL, EAGAIN, EACCES 267 | throw std::system_error(errnum, std::generic_category()); 268 | } else 269 | mHandle = reinterpret_cast(int_handle); 270 | } 271 | 272 | bool joinable() const {return mHandle != kInvalidHandle;} 273 | 274 | // Note: Due to lack of synchronization, this function has a race condition 275 | // if called concurrently, which leads to undefined behavior. The same applies 276 | // to all other member functions of this class, but this one is mentioned 277 | // explicitly. 278 | void join() 279 | { 280 | using namespace std; 281 | if (get_id() == id(GetCurrentThreadId())) 282 | throw system_error(make_error_code(errc::resource_deadlock_would_occur)); 283 | if (mHandle == kInvalidHandle) 284 | throw system_error(make_error_code(errc::no_such_process)); 285 | if (!joinable()) 286 | throw system_error(make_error_code(errc::invalid_argument)); 287 | WaitForSingleObject(mHandle, INFINITE); 288 | CloseHandle(mHandle); 289 | mHandle = kInvalidHandle; 290 | mThreadId.clear(); 291 | } 292 | 293 | ~thread() 294 | { 295 | if (joinable()) 296 | { 297 | #ifndef NDEBUG 298 | std::printf("Error: Must join() or detach() a thread before \ 299 | destroying it.\n"); 300 | #endif 301 | std::terminate(); 302 | } 303 | } 304 | thread& operator=(const thread&) = delete; 305 | thread& operator=(thread&& other) noexcept 306 | { 307 | if (joinable()) 308 | { 309 | #ifndef NDEBUG 310 | std::printf("Error: Must join() or detach() a thread before \ 311 | moving another thread to it.\n"); 312 | #endif 313 | std::terminate(); 314 | } 315 | swap(std::forward(other)); 316 | return *this; 317 | } 318 | void swap(thread&& other) noexcept 319 | { 320 | HANDLE tmph = mHandle; 321 | mHandle = other.mHandle; 322 | other.mHandle = tmph; 323 | DWORD tmpd = mThreadId.mId; 324 | mThreadId.mId = other.mThreadId.mId; 325 | other.mThreadId.mId = tmpd; 326 | } 327 | 328 | static unsigned int hardware_concurrency() noexcept 329 | { 330 | static unsigned int cached = _hardware_concurrency_helper(); 331 | return cached; 332 | } 333 | 334 | void detach() 335 | { 336 | if (!joinable()) 337 | { 338 | using namespace std; 339 | throw system_error(make_error_code(errc::invalid_argument)); 340 | } 341 | if (mHandle != kInvalidHandle) 342 | { 343 | CloseHandle(mHandle); 344 | mHandle = kInvalidHandle; 345 | } 346 | mThreadId.clear(); 347 | } 348 | }; 349 | 350 | namespace this_thread 351 | { 352 | inline thread::id get_id() noexcept {return thread::id(GetCurrentThreadId());} 353 | inline void yield() noexcept {Sleep(0);} 354 | template< class Rep, class Period > 355 | void sleep_for( const std::chrono::duration& sleep_duration) 356 | { 357 | using namespace std::chrono; 358 | using rep = milliseconds::rep; 359 | rep ms = duration_cast(sleep_duration).count(); 360 | while (ms > 0) 361 | { 362 | constexpr rep kMaxRep = static_cast(INFINITE - 1); 363 | auto sleepTime = (ms < kMaxRep) ? ms : kMaxRep; 364 | Sleep(static_cast(sleepTime)); 365 | ms -= sleepTime; 366 | } 367 | } 368 | template 369 | void sleep_until(const std::chrono::time_point& sleep_time) 370 | { 371 | sleep_for(sleep_time-Clock::now()); 372 | } 373 | } 374 | } // Namespace mingw_stdthread 375 | 376 | namespace std 377 | { 378 | // Because of quirks of the compiler, the common "using namespace std;" 379 | // directive would flatten the namespaces and introduce ambiguity where there 380 | // was none. Direct specification (std::), however, would be unaffected. 381 | // Take the safe option, and include only in the presence of MinGW's win32 382 | // implementation. 383 | #if defined(__MINGW32__ ) && !defined(_GLIBCXX_HAS_GTHREADS) 384 | using mingw_stdthread::thread; 385 | // Remove ambiguity immediately, to avoid problems arising from the above. 386 | //using std::thread; 387 | namespace this_thread 388 | { 389 | using namespace mingw_stdthread::this_thread; 390 | } 391 | #elif !defined(MINGW_STDTHREAD_REDUNDANCY_WARNING) // Skip repetition 392 | #define MINGW_STDTHREAD_REDUNDANCY_WARNING 393 | #pragma message "This version of MinGW seems to include a win32 port of\ 394 | pthreads, and probably already has C++11 std threading classes implemented,\ 395 | based on pthreads. These classes, found in namespace std, are not overridden\ 396 | by the mingw-std-thread library. If you would still like to use this\ 397 | implementation (as it is more lightweight), use the classes provided in\ 398 | namespace mingw_stdthread." 399 | #endif 400 | 401 | // Specialize hash for this implementation's thread::id, even if the 402 | // std::thread::id already has a hash. 403 | template<> 404 | struct hash 405 | { 406 | typedef mingw_stdthread::thread::id argument_type; 407 | typedef size_t result_type; 408 | size_t operator() (const argument_type & i) const noexcept 409 | { 410 | return i.mId; 411 | } 412 | }; 413 | } 414 | #endif // WIN32STDTHREAD_H 415 | -------------------------------------------------------------------------------- /src/Alchitry_Loader.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "ftd2xx.h" 4 | #include "jtag.h" 5 | #include "jtag_fsm.h" 6 | #include "loader.h" 7 | #include "spi.h" 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include "loader.h" 16 | #include 17 | 18 | #ifdef _WIN32 19 | #include "mingw.thread.h" 20 | #else 21 | 22 | #include 23 | 24 | #endif 25 | 26 | #include 27 | #include "config_type.h" 28 | 29 | #define BOARD_ERROR -2 30 | #define BOARD_UNKNOWN -1 31 | #define BOARD_AU 0 32 | #define BOARD_AU_PLUS 1 33 | #define BOARD_CU 2 34 | 35 | using namespace std; 36 | using get_time = chrono::steady_clock; 37 | 38 | char ManufacturerBuf[32]; 39 | char ManufacturerIdBuf[16]; 40 | char DescriptionBuf[64]; 41 | char SerialNumberBuf[16]; 42 | 43 | string getErrorName(unsigned int error) { 44 | switch (error) { 45 | case FT_OK: 46 | return "FT_OK"; 47 | case FT_INVALID_HANDLE: 48 | return "FT_INVALID_HANDLE"; 49 | case FT_DEVICE_NOT_FOUND: 50 | return "FT_DEVICE_NOT_FOUND"; 51 | case FT_DEVICE_NOT_OPENED: 52 | return "FT_DEVICE_NOT_OPENED"; 53 | case FT_IO_ERROR: 54 | return "FT_IO_ERROR"; 55 | case FT_INSUFFICIENT_RESOURCES: 56 | return "FT_INSUFFICIENT_RESOURCES"; 57 | case FT_INVALID_PARAMETER: 58 | return "FT_INVALID_PARAMETER"; 59 | case FT_INVALID_BAUD_RATE: 60 | return "FT_INVALID_BAUD_RATE"; 61 | 62 | case FT_DEVICE_NOT_OPENED_FOR_ERASE: 63 | return "FT_DEVICE_NOT_OPENED_FOR_ERASE"; 64 | case FT_DEVICE_NOT_OPENED_FOR_WRITE: 65 | return "FT_DEVICE_NOT_OPENED_FOR_WRITE"; 66 | case FT_FAILED_TO_WRITE_DEVICE: 67 | return "FT_FAILED_TO_WRITE_DEVICE"; 68 | case FT_EEPROM_READ_FAILED: 69 | return "FT_EEPROM_READ_FAILED"; 70 | case FT_EEPROM_WRITE_FAILED: 71 | return "FT_EEPROM_WRITE_FAILED"; 72 | case FT_EEPROM_ERASE_FAILED: 73 | return "FT_EEPROM_ERASE_FAILED"; 74 | case FT_EEPROM_NOT_PRESENT: 75 | return "FT_EEPROM_NOT_PRESENT"; 76 | case FT_EEPROM_NOT_PROGRAMMED: 77 | return "FT_EEPROM_NOT_PROGRAMMED"; 78 | case FT_INVALID_ARGS: 79 | return "FT_INVALID_ARGS"; 80 | case FT_NOT_SUPPORTED: 81 | return "FT_NOT_SUPPORTED"; 82 | case FT_OTHER_ERROR: 83 | return "FT_OTHER_ERROR"; 84 | case FT_DEVICE_LIST_NOT_READY: 85 | return "FT_DEVICE_LIST_NOT_READY"; 86 | default: 87 | return "Unknown"; 88 | } 89 | } 90 | 91 | void write_to_file(const string& file, PFT_PROGRAM_DATA ftData) { 92 | ofstream output_file(file, ios::binary); 93 | CONFIG_DATA config; 94 | ft_to_config(&config, ftData); 95 | output_file.write((char *) &config, sizeof(CONFIG_DATA)); 96 | output_file.write(ManufacturerBuf, sizeof(ManufacturerBuf)); 97 | output_file.write(ManufacturerIdBuf, sizeof(ManufacturerIdBuf)); 98 | output_file.write(DescriptionBuf, sizeof(DescriptionBuf)); 99 | output_file.write(SerialNumberBuf, sizeof(SerialNumberBuf)); 100 | output_file.close(); 101 | } 102 | 103 | bool read_from_file(const string& file, PFT_PROGRAM_DATA ftData) { 104 | cout << "Reading " << file << endl; 105 | try { 106 | ifstream input_file(file, ios::in | ios::binary); 107 | if (!input_file.is_open()) { 108 | cerr << "Failed to open file " << file << endl; 109 | return false; 110 | } 111 | CONFIG_DATA config; 112 | 113 | input_file.read((char *) &config, sizeof(CONFIG_DATA)); 114 | config_to_ft(ftData, &config); 115 | input_file.read(ManufacturerBuf, sizeof(ManufacturerBuf)); 116 | input_file.read(ManufacturerIdBuf, sizeof(ManufacturerIdBuf)); 117 | input_file.read(DescriptionBuf, sizeof(DescriptionBuf)); 118 | input_file.read(SerialNumberBuf, sizeof(SerialNumberBuf)); 119 | input_file.close(); 120 | ftData->Manufacturer = ManufacturerBuf; 121 | ftData->ManufacturerId = ManufacturerIdBuf; 122 | ftData->Description = DescriptionBuf; 123 | ftData->SerialNumber = SerialNumberBuf; 124 | } catch (...) { 125 | cerr << "Failed to read file " << file << endl; 126 | return false; 127 | } 128 | return true; 129 | } 130 | 131 | FT_STATUS read_from_device(FT_HANDLE ftHandle, PFT_PROGRAM_DATA ftData) { 132 | ftData->Signature1 = 0x00000000; 133 | ftData->Signature2 = 0xffffffff; 134 | ftData->Version = 0x00000005; 135 | ftData->Manufacturer = ManufacturerBuf; 136 | ftData->ManufacturerId = ManufacturerIdBuf; 137 | ftData->Description = DescriptionBuf; 138 | ftData->SerialNumber = SerialNumberBuf; 139 | 140 | return FT_EE_Read(ftHandle, ftData); 141 | } 142 | 143 | void print_info() { 144 | cout << "Manufacture: " << ManufacturerBuf << endl; 145 | cout << "ManufacturerId: " << ManufacturerIdBuf << endl; 146 | cout << "Description: " << DescriptionBuf << endl; 147 | cout << "SerialNumber: " << SerialNumberBuf << endl; 148 | } 149 | 150 | void erase(FT_HANDLE ftHandle) { 151 | cout << "Erasing... "; 152 | FT_STATUS ftStatus = FT_EraseEE(ftHandle); 153 | if (ftStatus != FT_OK) // Did the command execute OK? 154 | { 155 | cerr << "Error in erasing device!" << endl; 156 | FT_Close(ftHandle); 157 | return; 158 | } 159 | cout << "Done." << endl; 160 | } 161 | 162 | bool programDevice(int devNumber, const string& file) { 163 | FT_HANDLE ftHandle; 164 | 165 | cout << "Opening device... "; 166 | FT_STATUS ftStatus = FT_Open(devNumber, &ftHandle); 167 | if (ftStatus != FT_OK) // Did the command execute OK? 168 | { 169 | printf("Error in opening device!\n"); 170 | return false; // Exit with error 171 | } 172 | cout << "Done." << endl; 173 | FT_PROGRAM_DATA ftData; 174 | 175 | cout << "Checking EEPROM... "; 176 | ftStatus = read_from_device(ftHandle, &ftData); 177 | 178 | if (ftStatus != FT_EEPROM_NOT_PROGRAMMED) { // device isn't blank 179 | if (ftStatus == FT_OK) { 180 | cout << "Not blank." << endl; 181 | erase(ftHandle); 182 | } else if (ftStatus == FT_EEPROM_NOT_PRESENT) { 183 | cout << "Not present!" << endl; 184 | FT_Close(ftHandle); 185 | return false; 186 | } else { 187 | cout << getErrorName(ftStatus) << endl; 188 | FT_Close(ftHandle); 189 | return false; 190 | } 191 | } else { 192 | cout << "Blank." << endl; 193 | } 194 | 195 | if (!read_from_file(file, &ftData)) { 196 | FT_Close(ftHandle); 197 | return false; 198 | } 199 | 200 | print_info(); 201 | 202 | cout << "Programming... "; 203 | ftStatus = FT_EE_Program(ftHandle, &ftData); 204 | if (ftStatus != FT_OK) // Did the command execute OK? 205 | { 206 | cout << "ERROR: " << getErrorName(ftStatus) << endl; 207 | FT_Close(ftHandle); 208 | return false; 209 | } 210 | cout << "Done." << endl; 211 | 212 | FT_Close(ftHandle); 213 | return true; 214 | } 215 | 216 | string descriptionToName(const string &des) { 217 | if (des == "Alchitry Cu A") { 218 | return "Alchitry Cu"; 219 | } else if (des == "Alchitry Au A") { 220 | return "Alchitry Au"; 221 | } else if (des == "Alchitry Au+ A") { 222 | return "Alchitry Au+"; 223 | } else { 224 | return "Unknown"; 225 | } 226 | } 227 | 228 | int descriptionToType(const string &des) { 229 | if (des == "Alchitry Cu A") { 230 | return BOARD_CU; 231 | } else if (des == "Alchitry Au A") { 232 | return BOARD_AU; 233 | } else if (des == "Alchitry Au+ A") { 234 | return BOARD_AU_PLUS; 235 | } else { 236 | return BOARD_UNKNOWN; 237 | } 238 | } 239 | 240 | string boardToName(int board) { 241 | switch (board) { 242 | case BOARD_AU: 243 | return "Alchitry Au"; 244 | case BOARD_AU_PLUS: 245 | return "Alchitry Au+"; 246 | case BOARD_CU: 247 | return "Alchitry Cu"; 248 | default: 249 | return "Unknown"; 250 | } 251 | } 252 | 253 | void printDeviceList() { 254 | FT_STATUS ftStatus; 255 | FT_DEVICE_LIST_INFO_NODE *devInfo; 256 | DWORD numDevs = 0; 257 | // create the device information list 258 | ftStatus = FT_CreateDeviceInfoList(&numDevs); 259 | if (ftStatus != FT_OK) { 260 | cerr << "Could not read device list!" << endl; 261 | return; 262 | } 263 | 264 | if (numDevs > 0) { 265 | cout << "Devices: " << endl; 266 | // allocate storage for list based on numDevs 267 | devInfo = (FT_DEVICE_LIST_INFO_NODE *) malloc( 268 | sizeof(FT_DEVICE_LIST_INFO_NODE) * numDevs); 269 | // get the device information list 270 | ftStatus = FT_GetDeviceInfoList(devInfo, &numDevs); 271 | if (ftStatus == FT_OK) { 272 | for (unsigned int i = 0; i < numDevs; i++) { 273 | cout << " " << i << ": " 274 | << descriptionToName(devInfo[i].Description) << endl; 275 | } 276 | } else { 277 | cerr << "Error getting device list!" << endl; 278 | } 279 | free(devInfo); 280 | } else { 281 | cout << "No devices found!" << endl; 282 | } 283 | } 284 | 285 | int getDeviceType(unsigned int devNumber) { 286 | FT_STATUS ftStatus; 287 | FT_DEVICE_LIST_INFO_NODE *devInfo; 288 | DWORD numDevs = 0; 289 | int board = BOARD_ERROR; 290 | // create the device information list 291 | ftStatus = FT_CreateDeviceInfoList(&numDevs); 292 | if (ftStatus != FT_OK) { 293 | cerr << "Could not read device list!" << endl; 294 | return BOARD_ERROR; 295 | } 296 | 297 | if (numDevs <= devNumber) { 298 | cerr << "Invalid device number!" << endl; 299 | return BOARD_ERROR; 300 | } 301 | 302 | // allocate storage for list based on numDevs 303 | devInfo = (FT_DEVICE_LIST_INFO_NODE *) malloc( 304 | sizeof(FT_DEVICE_LIST_INFO_NODE) * numDevs); 305 | // get the device information list 306 | ftStatus = FT_GetDeviceInfoList(devInfo, &numDevs); 307 | if (ftStatus == FT_OK) { 308 | string boardDescription = devInfo[devNumber].Description; 309 | if (boardDescription == "Alchitry Cu A") { 310 | board = BOARD_CU; 311 | } else if (boardDescription == "Alchitry Au A") { 312 | board = BOARD_AU; 313 | } else if (boardDescription == "Alchitry Au+ A") { 314 | board = BOARD_AU_PLUS; 315 | } else { 316 | board = BOARD_UNKNOWN; 317 | } 318 | } else { 319 | cerr << "Error getting device list!" << endl; 320 | } 321 | free(devInfo); 322 | 323 | return board; 324 | } 325 | 326 | int getFirstDeviceOfType(int board) { 327 | FT_STATUS ftStatus; 328 | FT_DEVICE_LIST_INFO_NODE *devInfo; 329 | DWORD numDevs = 0; 330 | 331 | // create the device information list 332 | ftStatus = FT_CreateDeviceInfoList(&numDevs); 333 | if (ftStatus != FT_OK) { 334 | cerr << "Could not read device list!" << endl; 335 | return -1; 336 | } 337 | 338 | if (numDevs < 1) { 339 | cerr << "No devices found!" << endl; 340 | return -1; 341 | } 342 | 343 | // allocate storage for list based on numDevs 344 | devInfo = (FT_DEVICE_LIST_INFO_NODE *) malloc( 345 | sizeof(FT_DEVICE_LIST_INFO_NODE) * numDevs); 346 | // get the device information list 347 | ftStatus = FT_GetDeviceInfoList(devInfo, &numDevs); 348 | 349 | if (ftStatus == FT_OK) { 350 | for (int devNumber = 0; devNumber < numDevs; devNumber++) { 351 | string boardDescription = devInfo[devNumber].Description; 352 | int type = descriptionToType(boardDescription); 353 | if (type == board) { 354 | free(devInfo); 355 | return devNumber; 356 | } 357 | } 358 | } else { 359 | cerr << "Error getting device list!" << endl; 360 | } 361 | free(devInfo); 362 | return -1; 363 | } 364 | 365 | bool readAndSaveFTDI(const string& file) { 366 | FT_HANDLE ftHandle; 367 | 368 | cout << "Opening device... "; 369 | FT_STATUS ftStatus = FT_Open(0, &ftHandle); 370 | if (ftStatus != FT_OK) // Did the command execute OK? 371 | { 372 | printf("Error in opening device!\n"); 373 | return false; // Exit with error 374 | } 375 | cout << "Done." << endl; 376 | FT_PROGRAM_DATA ftData; 377 | 378 | cout << "Checking EEPROM... "; 379 | ftStatus = read_from_device(ftHandle, &ftData); 380 | 381 | if (ftStatus != FT_OK) { 382 | if (ftStatus == FT_EEPROM_NOT_PROGRAMMED) { 383 | cout << "Blank." << endl; 384 | return false; 385 | } else if (ftStatus == FT_EEPROM_NOT_PRESENT) { 386 | cout << "Not present!" << endl; 387 | FT_Close(ftHandle); 388 | return false; 389 | } else { 390 | cout << "Unknown error " << ftStatus << endl; 391 | FT_Close(ftHandle); 392 | return false; 393 | } 394 | } else { // eeprom not blank 395 | cout << "Done." << endl; 396 | } 397 | 398 | cout << "Writing to file... "; 399 | 400 | write_to_file(file, &ftData); 401 | 402 | cout << "Done." << endl; 403 | 404 | FT_Close(ftHandle); 405 | return true; 406 | } 407 | 408 | void printUsage() { 409 | cout << "Usage: \"loader arguments\"" << endl; 410 | cout << endl; 411 | cout << "Arguments:" << endl; 412 | cout << " -e : erase FPGA flash" << endl; 413 | cout << " -l : list detected boards" << endl; 414 | cout << " -h : print this help message" << endl; 415 | cout << " -f config.bin : write FPGA flash" << endl; 416 | cout << " -r config.bin : write FPGA RAM" << endl; 417 | cout << " -u config.data : write FTDI eeprom" << endl; 418 | cout << " -b n : select board \"n\" (defaults to 0)" << endl; 419 | cout << " -p loader.bin : Au bridge bin" << endl; 420 | cout << " -t TYPE : TYPE can be au, au+, or cu (defaults to au)" << endl; 421 | } 422 | 423 | int main(int argc, char *argv[]) { 424 | if (argc < 2) { 425 | printUsage(); 426 | return 1; 427 | } 428 | 429 | bool fpgaFlash = false; 430 | bool fpgaRam = false; 431 | bool eeprom = false; 432 | string eepromConfig; 433 | string fpgaBinFlash; 434 | string fpgaBinRam; 435 | bool erase = false; 436 | bool list = false; 437 | bool print = false; 438 | int deviceNumber = -1; 439 | bool bridgeProvided = false; 440 | string auBridgeBin; 441 | int board = BOARD_AU; 442 | 443 | for (int i = 1; i < argc;) { 444 | string arg = argv[i]; 445 | if (arg == "-e") { 446 | i++; 447 | erase = true; 448 | } else if (arg == "-l") { 449 | i++; 450 | list = true; 451 | } else if (arg == "-h") { 452 | i++; 453 | print = true; 454 | } else if (arg == "-f") { 455 | if (argc <= i + 1) { 456 | cerr << "Missing bin file!" << endl; 457 | printUsage(); 458 | return 1; 459 | } 460 | fpgaFlash = true; 461 | fpgaBinFlash = argv[i + 1]; 462 | i += 2; 463 | } else if (arg == "-r") { 464 | if (argc <= i + 1) { 465 | cerr << "Missing bin file!" << endl; 466 | printUsage(); 467 | return 1; 468 | } 469 | fpgaRam = true; 470 | fpgaBinRam = argv[i + 1]; 471 | i += 2; 472 | } else if (arg == "-u") { 473 | if (argc <= i + 1) { 474 | cerr << "Missing data file!" << endl; 475 | printUsage(); 476 | return 1; 477 | } 478 | eeprom = true; 479 | eepromConfig = argv[i + 1]; 480 | i += 2; 481 | } else if (arg == "-b") { 482 | if (argc <= i + 1) { 483 | cerr << "Missing board number!" << endl; 484 | printUsage(); 485 | return 1; 486 | } 487 | try { 488 | deviceNumber = stoi(argv[i + 1]); 489 | } catch (const std::invalid_argument &ia) { 490 | cerr << argv[i + 1] << " is not a number!" << endl; 491 | printUsage(); 492 | return 1; 493 | } 494 | if (deviceNumber < 0) { 495 | cerr << "Device numbers can't be negative!" << endl; 496 | printUsage(); 497 | return 1; 498 | } 499 | i += 2; 500 | } else if (arg == "-p") { 501 | if (argc <= i + 1) { 502 | cerr << "Missing bin file!" << endl; 503 | printUsage(); 504 | return 1; 505 | } 506 | bridgeProvided = true; 507 | auBridgeBin = argv[i + 1]; 508 | i += 2; 509 | } else if (arg == "-t") { 510 | if (argc <= i + 1) { 511 | cerr << "Missing board type!" << endl; 512 | printUsage(); 513 | return 1; 514 | } 515 | if (strcmp(argv[i + 1], "au") == 0) { 516 | board = BOARD_AU; 517 | } else if (strcmp(argv[i + 1], "au+") == 0) { 518 | board = BOARD_AU_PLUS; 519 | } else if (strcmp(argv[i + 1], "cu") == 0) { 520 | board = BOARD_CU; 521 | } else { 522 | cerr << "Invalid board type: " << argv[i + 1] << endl; 523 | printUsage(); 524 | return 1; 525 | } 526 | i += 2; 527 | 528 | } else { 529 | cerr << "Unknown argument " << arg << endl; 530 | printUsage(); 531 | return 1; 532 | } 533 | } 534 | 535 | if (print) 536 | printUsage(); 537 | 538 | if (list) 539 | printDeviceList(); 540 | 541 | if (deviceNumber < 0) 542 | deviceNumber = getFirstDeviceOfType(board); 543 | 544 | if (deviceNumber < 0) { 545 | cerr << "Couldn't find device!" << endl; 546 | return 2; 547 | } 548 | 549 | cout << "Found " << boardToName(board) << " as device " << deviceNumber 550 | << "." << endl; 551 | 552 | if (eeprom) 553 | programDevice(deviceNumber, eepromConfig); 554 | 555 | if (erase || fpgaFlash || fpgaRam) { 556 | int boardType = getDeviceType(deviceNumber); 557 | if (board != boardType) { 558 | cerr << "Invalid board type detected!" << endl; 559 | return 2; 560 | } 561 | 562 | if (boardType == BOARD_AU || boardType == BOARD_AU_PLUS) { 563 | if (!bridgeProvided && (erase || fpgaFlash)) { 564 | cerr << "No bridge bin provided!" << endl; 565 | return 2; 566 | } 567 | Jtag jtag; 568 | if (jtag.connect(deviceNumber) != FT_OK) { 569 | cerr << "Failed to connect to JTAG!" << endl; 570 | return 2; 571 | } 572 | if (!jtag.initialize()) { 573 | cerr << "Failed to initialize JTAG!" << endl; 574 | return 2; 575 | } 576 | Loader loader(&jtag); 577 | 578 | if (erase) { 579 | if (!loader.eraseFlash(auBridgeBin)) { 580 | cerr << "Failed to erase flash!" << endl; 581 | } else { 582 | cout << "Done." << endl; 583 | } 584 | } 585 | 586 | if (fpgaFlash) { 587 | if (!loader.writeBin(fpgaBinFlash, true, auBridgeBin)) { 588 | cerr << "Failed to write FPGA flash!" << endl; 589 | } 590 | } 591 | 592 | if (fpgaRam) { 593 | if (!loader.writeBin(fpgaBinRam, false, "")) { 594 | cerr << "Failed to write FPGA RAM!" << endl; 595 | } 596 | } 597 | 598 | jtag.disconnect(); 599 | } else if (boardType == BOARD_CU) { 600 | Spi spi; 601 | if (spi.connect(deviceNumber) != FT_OK) { 602 | cerr << "Failed to connect to SPI!" << endl; 603 | return 2; 604 | } 605 | if (!spi.initialize()) { 606 | cerr << "Failed to initialize SPI!" << endl; 607 | return 2; 608 | } 609 | 610 | if (erase) { 611 | if (!spi.eraseFlash()) { 612 | cerr << "Failed to erase flash!" << endl; 613 | } else { 614 | cout << "Done." << endl; 615 | } 616 | } 617 | 618 | if (fpgaFlash) { 619 | if (!spi.writeBin(fpgaBinFlash)) { 620 | cerr << "Failed to write FPGA flash!" << endl; 621 | } 622 | } 623 | 624 | if (fpgaRam) { 625 | cerr << "Alchitry Cu doesn't support RAM only programming!" 626 | << endl; 627 | return 1; 628 | } 629 | } else { 630 | cerr << "Unknown board type!" << endl; 631 | return 2; 632 | } 633 | } 634 | 635 | return 0; 636 | } 637 | 638 | -------------------------------------------------------------------------------- /src/jtag.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * jtag.cpp 3 | * 4 | * Created on: May 24, 2017 5 | * Author: justin 6 | */ 7 | 8 | #include "jtag.h" 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #ifdef _WIN32 15 | #include "mingw.thread.h" 16 | #else 17 | #include 18 | #endif 19 | 20 | #include 21 | 22 | using namespace std; 23 | 24 | Jtag::Jtag() { 25 | ftHandle = 0; 26 | active = false; 27 | } 28 | 29 | FT_STATUS Jtag::connect(unsigned int devNumber) { 30 | return FT_Open(devNumber, &ftHandle); 31 | } 32 | 33 | FT_STATUS Jtag::disconnect() { 34 | active = false; 35 | return FT_Close(ftHandle); 36 | } 37 | 38 | bool Jtag::initialize() { 39 | BYTE byInputBuffer[1024]; // Buffer to hold data read from the FT2232H 40 | DWORD dwNumBytesToRead = 0; // Number of bytes available to read in the driver's input buffer 41 | DWORD dwNumBytesRead = 0; // Count of actual bytes read - used with FT_Read 42 | FT_STATUS ftStatus; 43 | ftStatus = FT_ResetDevice(ftHandle); 44 | //Reset USB device 45 | //Purge USB receive buffer first by reading out all old data from FT2232H receive buffer 46 | ftStatus |= FT_GetQueueStatus(ftHandle, &dwNumBytesToRead); // Get the number of bytes in the FT2232H receive buffer 47 | if ((ftStatus == FT_OK) && (dwNumBytesToRead > 0)) 48 | FT_Read(ftHandle, &byInputBuffer, dwNumBytesToRead, &dwNumBytesRead);//Read out the data from FT2232H receive buffer 49 | ftStatus |= FT_SetUSBParameters(ftHandle, 65536, 65535);//Set USB request transfer sizes to 64K 50 | ftStatus |= FT_SetChars(ftHandle, false, 0, false, 0); //Disable event and error characters 51 | ftStatus |= FT_SetTimeouts(ftHandle, 0, 5000); //Sets the read and write timeouts in milliseconds 52 | ftStatus |= FT_SetLatencyTimer(ftHandle, 16); //Set the latency timer (default is 16mS) 53 | ftStatus |= FT_SetBitMode(ftHandle, 0x0, 0x00); //Reset controller 54 | ftStatus |= FT_SetBitMode(ftHandle, 0x0, 0x02); //Enable MPSSE mode 55 | 56 | if (ftStatus != FT_OK) { 57 | cerr << "Failed to set initial configuration!" << endl; 58 | return false; 59 | } 60 | 61 | std::this_thread::sleep_for(std::chrono::milliseconds(100)); // Wait for all the USB stuff to complete and work 62 | 63 | if (!sync_mpsse()) { 64 | cerr << "Failed to sync with MPSSE!" << endl; 65 | return false; 66 | } 67 | 68 | if (!config_jtag()) { 69 | cerr << "Failed to set JTAG configuration!" << endl; 70 | return false; 71 | } 72 | 73 | active = true; 74 | 75 | return true; 76 | } 77 | 78 | bool Jtag::sync_mpsse() { 79 | BYTE byOutputBuffer[8]; // Buffer to hold MPSSE commands and data to be sent to the FT2232H 80 | BYTE byInputBuffer[8]; // Buffer to hold data read from the FT2232H 81 | DWORD dwCount = 0; // General loop index 82 | DWORD dwNumBytesToSend = 0; // Index to the output buffer 83 | DWORD dwNumBytesSent = 0; // Count of actual bytes sent - used with FT_Write 84 | DWORD dwNumBytesToRead = 0; // Number of bytes available to read in the driver's input buffer 85 | DWORD dwNumBytesRead = 0; // Count of actual bytes read - used with FT_Read 86 | FT_STATUS ftStatus; 87 | 88 | byOutputBuffer[dwNumBytesToSend++] = 0xAA; //'\xAA'; 89 | //Add bogus command ‘xAA’ to the queue 90 | ftStatus = FT_Write(ftHandle, byOutputBuffer, dwNumBytesToSend, 91 | &dwNumBytesSent); 92 | if (ftStatus != FT_OK) 93 | cerr << "Failed to send bad command" << endl; 94 | // Send off the BAD commands 95 | dwNumBytesToSend = 0; // Reset output buffer pointer 96 | do { 97 | ftStatus = FT_GetQueueStatus(ftHandle, &dwNumBytesToRead); 98 | if (ftStatus != FT_OK) 99 | cerr << "Failed to get queue status " << ftStatus << endl; 100 | // Get the number of bytes in the device input buffer 101 | } while ((dwNumBytesToRead == 0) && (ftStatus == FT_OK)); 102 | if (dwNumBytesRead > 8) { 103 | cerr << "Input buffer too small in sync_mpsse()!" << endl; 104 | return false; 105 | } 106 | //or Timeout 107 | bool bCommandEchod = false; 108 | ftStatus = FT_Read(ftHandle, &byInputBuffer, dwNumBytesToRead, 109 | &dwNumBytesRead); 110 | 111 | if (ftStatus != FT_OK) 112 | cerr << "Failed to read data" << endl; 113 | 114 | //Read out the data from input buffer 115 | for (dwCount = 0; dwCount < dwNumBytesRead - 1; dwCount++) 116 | //Check if Bad command and echo command received 117 | { 118 | if ((byInputBuffer[dwCount] == 0xFA) 119 | && (byInputBuffer[dwCount + 1] == 0xAA)) { 120 | bCommandEchod = true; 121 | break; 122 | } 123 | } 124 | 125 | return bCommandEchod; 126 | } 127 | 128 | bool Jtag::config_jtag() { 129 | FT_STATUS ftStatus; 130 | BYTE byOutputBuffer[64]; // Buffer to hold MPSSE commands and data to be sent to the FT2232H 131 | DWORD dwNumBytesToSend = 0; // Index to the output buffer 132 | DWORD dwNumBytesSent = 0; // Count of actual bytes sent - used with FT_Write 133 | DWORD dwClockDivisor = 0x05DB; // Value of clock divisor, SCL Frequency = 60/((1+0x05DB)*2) (MHz) = 20khz 134 | // ----------------------------------------------------------- 135 | // Configure the MPSSE settings for JTAG 136 | // Multiple commands can be sent to the MPSSE with one FT_Write 137 | // ----------------------------------------------------------- 138 | dwNumBytesToSend = 0; // Start with a fresh index 139 | // Set up the Hi-Speed specific commands for the FTx232H 140 | byOutputBuffer[dwNumBytesToSend++] = 0x8A; 141 | // Use 60MHz master clock (disable divide by 5) 142 | byOutputBuffer[dwNumBytesToSend++] = 0x97; 143 | // Turn off adaptive clocking (may be needed for ARM) 144 | byOutputBuffer[dwNumBytesToSend++] = 0x8D; 145 | // Disable three-phase clocking 146 | ftStatus = FT_Write(ftHandle, byOutputBuffer, dwNumBytesToSend, 147 | &dwNumBytesSent); 148 | // Send off the HS-specific commands 149 | 150 | if (ftStatus != FT_OK) 151 | return false; 152 | 153 | dwNumBytesToSend = 0; // Reset output buffer pointer 154 | // Set initial states of the MPSSE interface - low byte, both pin directions and output values 155 | // Pin name Signal Direction Config Initial State Config 156 | // ADBUS0 TCK output 1 low 0 157 | // ADBUS1 TDI output 1 low 0 158 | // ADBUS2 TDO input 0 0 159 | // ADBUS3 TMS output 1 high 1 160 | // ADBUS4 GPIOL0 input 0 0 161 | // ADBUS5 GPIOL1 input 0 0 162 | // ADBUS6 GPIOL2 input 0 0 163 | // ADBUS7 GPIOL3 input 0 0 164 | byOutputBuffer[dwNumBytesToSend++] = 0x80; 165 | // Set data bits low-byte of MPSSE port 166 | byOutputBuffer[dwNumBytesToSend++] = 0x08; 167 | // Initial state config above 168 | byOutputBuffer[dwNumBytesToSend++] = 0x0B; 169 | // Direction config above 170 | ftStatus = FT_Write(ftHandle, byOutputBuffer, dwNumBytesToSend, 171 | &dwNumBytesSent); 172 | // Send off the low GPIO config commands 173 | 174 | if (ftStatus != FT_OK) 175 | return false; 176 | 177 | dwNumBytesToSend = 0; // Reset output buffer pointer 178 | // Set initial states of the MPSSE interface - high byte, both pin directions and output values 179 | // Pin name Signal Direction Config Initial State Config 180 | // ACBUS0 GPIOH0 input 0 0 181 | // ACBUS1 GPIOH1 input 0 0 182 | // ACBUS2 GPIOH2 input 0 0 183 | // ACBUS3 GPIOH3 input 0 0 184 | // ACBUS4 GPIOH4 input 0 0 185 | // ACBUS5 GPIOH5 input 0 0 186 | // ACBUS6 GPIOH6 input 0 0 187 | // ACBUS7 GPIOH7 input 0 0 188 | byOutputBuffer[dwNumBytesToSend++] = 0x82; 189 | // Set data bits low-byte of MPSSE port 190 | byOutputBuffer[dwNumBytesToSend++] = 0x00; 191 | // Initial state config above 192 | byOutputBuffer[dwNumBytesToSend++] = 0x00; 193 | // Direction config above 194 | ftStatus = FT_Write(ftHandle, byOutputBuffer, dwNumBytesToSend, 195 | &dwNumBytesSent); 196 | // Send off the high GPIO config commands 197 | 198 | if (ftStatus != FT_OK) 199 | return false; 200 | 201 | dwNumBytesToSend = 0; // Reset output buffer pointer 202 | // Set TCK frequency 203 | // TCK = 60MHz /((1 + [(1 +0xValueH*256) OR 0xValueL])*2) 204 | byOutputBuffer[dwNumBytesToSend++] = 0x86; 205 | //Command to set clock divisor 206 | byOutputBuffer[dwNumBytesToSend++] = dwClockDivisor & 0xFF; 207 | //Set 0xValueL of clock divisor 208 | byOutputBuffer[dwNumBytesToSend++] = (dwClockDivisor >> 8) & 0xFF; 209 | //Set 0xValueH of clock divisor 210 | ftStatus = FT_Write(ftHandle, byOutputBuffer, dwNumBytesToSend, 211 | &dwNumBytesSent); 212 | // Send off the clock divisor commands 213 | 214 | if (ftStatus != FT_OK) 215 | return false; 216 | 217 | dwNumBytesToSend = 0; // Reset output buffer pointer 218 | // Disable internal loop-back 219 | byOutputBuffer[dwNumBytesToSend++] = 0x85; 220 | // Disable loopback 221 | ftStatus = FT_Write(ftHandle, byOutputBuffer, dwNumBytesToSend, 222 | &dwNumBytesSent); 223 | // Send off the loopback command 224 | 225 | if (ftStatus != FT_OK) 226 | return false; 227 | 228 | return true; 229 | } 230 | 231 | bool Jtag::setFreq(double freq) { 232 | if (!active) { 233 | cerr 234 | << "Jtag must be connected and initialized before calling setFreq()!" 235 | << endl; 236 | return false; 237 | } 238 | FT_STATUS ftStatus; 239 | BYTE byOutputBuffer[8]; // Buffer to hold MPSSE commands and data to be sent to the FT2232H 240 | DWORD dwNumBytesToSend = 0; // Index to the output buffer 241 | DWORD dwNumBytesSent = 0; // Count of actual bytes sent - used with FT_Write 242 | DWORD dwClockDivisor; // Value of clock divisor, SCL Frequency = 60/((1+clkDiv)*2) (MHz) 243 | 244 | dwClockDivisor = 30.0 / (freq / 1000000.0) - 1.0; 245 | 246 | // Set TCK frequency 247 | // TCK = 60MHz /((1 + [(1 +0xValueH*256) OR 0xValueL])*2) 248 | byOutputBuffer[dwNumBytesToSend++] = 0x86; 249 | //Command to set clock divisor 250 | byOutputBuffer[dwNumBytesToSend++] = dwClockDivisor & 0xFF; 251 | //Set 0xValueL of clock divisor 252 | byOutputBuffer[dwNumBytesToSend++] = (dwClockDivisor >> 8) & 0xFF; 253 | //Set 0xValueH of clock divisor 254 | ftStatus = FT_Write(ftHandle, byOutputBuffer, dwNumBytesToSend, 255 | &dwNumBytesSent); 256 | // Send off the clock divisor commands 257 | 258 | if (ftStatus != FT_OK) 259 | return false; 260 | return true; 261 | } 262 | 263 | bool Jtag::navigateToState(Jtag_fsm::State init, Jtag_fsm::State dest) { 264 | FT_STATUS ftStatus; 265 | BYTE byOutputBuffer[3]; // Buffer to hold MPSSE commands and data to be sent to the FT2232H 266 | DWORD dwNumBytesSent = 0; // Count of actual bytes sent - used with FT_Write 267 | 268 | Jtag_fsm::Transistions transistions = Jtag_fsm::getTransitions(init, dest); 269 | 270 | if (transistions.moves > 0) { 271 | if (transistions.moves < 8) { 272 | byOutputBuffer[0] = 0x4B; 273 | byOutputBuffer[1] = transistions.moves - 1; 274 | byOutputBuffer[2] = 0x7f & transistions.tms; 275 | ftStatus = FT_Write(ftHandle, byOutputBuffer, 3, &dwNumBytesSent); 276 | if (ftStatus != FT_OK) 277 | return false; 278 | } else { 279 | cout << "Transition of 8 moves!" << endl; 280 | byOutputBuffer[0] = 0x4B; 281 | byOutputBuffer[1] = 6; 282 | byOutputBuffer[2] = 0x7f & transistions.tms; 283 | ftStatus = FT_Write(ftHandle, byOutputBuffer, 3, &dwNumBytesSent); 284 | if (ftStatus != FT_OK) 285 | return false; 286 | byOutputBuffer[0] = 0x4B; 287 | byOutputBuffer[1] = transistions.moves - 8; 288 | byOutputBuffer[2] = 0x7f & (transistions.tms >> 7); 289 | ftStatus = FT_Write(ftHandle, byOutputBuffer, 3, &dwNumBytesSent); 290 | if (ftStatus != FT_OK) 291 | return false; 292 | } 293 | } 294 | return true; 295 | } 296 | 297 | bool Jtag::shiftData(unsigned int bitCount, string tdi, string tdo, 298 | string mask) { 299 | FT_STATUS ftStatus; 300 | unsigned int reqBytes = bitCount / 8 + (bitCount % 8 > 0); 301 | BYTE *byOutputBuffer = new BYTE[reqBytes + 3]; 302 | BYTE *tdoBuffer = new BYTE[reqBytes + 3]; 303 | BYTE *byInputBuffer = new BYTE[reqBytes + 6]; 304 | DWORD dwNumBytesSent = 0; 305 | DWORD dwNumBytesToRead = 0; 306 | DWORD dwNumBytesRead = 0; 307 | DWORD tdoBytes = 0; 308 | 309 | unsigned int reqHex = bitCount / 4 + (bitCount % 4 > 0); 310 | 311 | if (tdi.length() < reqHex) 312 | return false; 313 | bool read = tdo != ""; 314 | if (read) { 315 | if (tdo.length() < reqHex) { 316 | delete byOutputBuffer; 317 | delete tdoBuffer; 318 | delete byInputBuffer; 319 | return false; 320 | } 321 | if (mask != "" && mask.length() < reqHex) { 322 | delete byOutputBuffer; 323 | delete tdoBuffer; 324 | delete byInputBuffer; 325 | return false; 326 | } 327 | } 328 | 329 | if (!flush()) 330 | return false; 331 | 332 | if (bitCount < 9) { 333 | int data = stoi(tdi, 0, 16); 334 | byOutputBuffer[0] = read ? 0x3B : 0x1B; 335 | byOutputBuffer[1] = bitCount - 2; 336 | byOutputBuffer[2] = data & 0xff; 337 | ftStatus = FT_Write(ftHandle, byOutputBuffer, 3, &dwNumBytesSent); 338 | if (ftStatus != FT_OK) { 339 | delete byOutputBuffer; 340 | delete tdoBuffer; 341 | delete byInputBuffer; 342 | return false; 343 | } 344 | 345 | BYTE lastBit = (data >> ((bitCount - 1) % 8)) & 0x01; 346 | 347 | byOutputBuffer[0] = read ? 0x6E : 0x4E; 348 | byOutputBuffer[1] = 0x00; 349 | byOutputBuffer[2] = 0x03 | (lastBit << 7); 350 | ftStatus = FT_Write(ftHandle, byOutputBuffer, 3, &dwNumBytesSent); 351 | if (ftStatus != FT_OK) { 352 | delete byOutputBuffer; 353 | delete tdoBuffer; 354 | delete byInputBuffer; 355 | return false; 356 | } 357 | 358 | if (read) { 359 | do { 360 | ftStatus = FT_GetQueueStatus(ftHandle, &dwNumBytesToRead); 361 | // Get the number of bytes in the device input buffer 362 | } while ((dwNumBytesToRead == 0) && (ftStatus == FT_OK)); 363 | //or Timeout 364 | ftStatus = FT_Read(ftHandle, byInputBuffer, dwNumBytesToRead, 365 | &dwNumBytesRead); 366 | if (ftStatus != FT_OK) { 367 | delete byOutputBuffer; 368 | delete tdoBuffer; 369 | delete byInputBuffer; 370 | return false; 371 | } 372 | 373 | tdoBuffer[0] = byInputBuffer[0] >> (8 - (bitCount - 1)); 374 | tdoBuffer[0] |= byInputBuffer[1] >> (7 - (bitCount - 1)); 375 | tdoBytes = 1; 376 | } 377 | } else { 378 | BYTE *tdiBytes = new BYTE[reqBytes + 1]; 379 | hexToByte(tdi, tdiBytes); 380 | 381 | unsigned int fullBytes = (bitCount - 1) / 8; 382 | unsigned int remBytes = fullBytes; 383 | unsigned int offset = 0; 384 | 385 | if (fullBytes > 65536 && read) { 386 | cout << "Large transfers with reads may not work!" << endl; 387 | } 388 | 389 | while (remBytes > 0) { 390 | unsigned int bct = remBytes > 65536 ? 65536 : remBytes; 391 | byOutputBuffer[0] = read ? 0x39 : 0x19; 392 | byOutputBuffer[1] = (bct - 1) & 0xff; 393 | byOutputBuffer[2] = ((bct - 1) >> 8) & 0xff; 394 | 395 | for (unsigned int i = 0; i < bct; i++) { 396 | byOutputBuffer[3 + i] = tdiBytes[i + offset]; 397 | } 398 | 399 | ftStatus = FT_Write(ftHandle, byOutputBuffer, 3 + bct, 400 | &dwNumBytesSent); 401 | if (ftStatus != FT_OK) { 402 | delete byOutputBuffer; 403 | delete tdoBuffer; 404 | delete byInputBuffer; 405 | delete tdiBytes; 406 | return false; 407 | } 408 | 409 | remBytes -= bct; 410 | offset += bct; 411 | } 412 | 413 | unsigned int partialBits = bitCount - 1 - (fullBytes * 8); 414 | 415 | if (fullBytes * 8 + 1 != bitCount) { 416 | byOutputBuffer[0] = read ? 0x3B : 0x1B; 417 | byOutputBuffer[1] = partialBits - 1; 418 | byOutputBuffer[2] = tdiBytes[reqBytes - 1] & 0xff; 419 | ftStatus = FT_Write(ftHandle, byOutputBuffer, 3, &dwNumBytesSent); 420 | if (ftStatus != FT_OK) { 421 | delete byOutputBuffer; 422 | delete tdoBuffer; 423 | delete byInputBuffer; 424 | delete tdiBytes; 425 | return false; 426 | } 427 | } 428 | 429 | BYTE lastBit = (tdiBytes[reqBytes - 1] >> ((bitCount - 1) % 8)) & 0x01; 430 | 431 | byOutputBuffer[0] = read ? 0x6E : 0x4E; 432 | byOutputBuffer[1] = 0x00; 433 | byOutputBuffer[2] = 0x03 | (lastBit << 7); 434 | ftStatus = FT_Write(ftHandle, byOutputBuffer, 3, &dwNumBytesSent); 435 | if (ftStatus != FT_OK) { 436 | delete byOutputBuffer; 437 | delete tdoBuffer; 438 | delete byInputBuffer; 439 | delete tdiBytes; 440 | return false; 441 | } 442 | 443 | if (read) { 444 | DWORD bytesToRead = fullBytes 445 | + ((fullBytes * 8 + 1 != bitCount) ? 2 : 1); 446 | do { 447 | ftStatus = FT_GetQueueStatus(ftHandle, &dwNumBytesToRead); 448 | // Get the number of bytes in the device input buffer 449 | } while ((dwNumBytesToRead != bytesToRead) && (ftStatus == FT_OK)); 450 | //or Timeout 451 | ftStatus = FT_Read(ftHandle, byInputBuffer, dwNumBytesToRead, 452 | &dwNumBytesRead); 453 | if (ftStatus != FT_OK) { 454 | delete byOutputBuffer; 455 | delete tdoBuffer; 456 | delete byInputBuffer; 457 | delete tdiBytes; 458 | return false; 459 | } 460 | 461 | for (unsigned int i = 0; i < fullBytes; i++) 462 | tdoBuffer[tdoBytes++] = byInputBuffer[i]; 463 | 464 | if (fullBytes * 8 + 1 != bitCount) { 465 | tdoBuffer[tdoBytes] = byInputBuffer[tdoBytes] 466 | >> (8 - partialBits); 467 | tdoBuffer[tdoBytes++] |= byInputBuffer[dwNumBytesRead - 1] 468 | >> (7 - partialBits); 469 | } else { 470 | tdoBuffer[tdoBytes++] = byInputBuffer[dwNumBytesRead - 1] >> 7; 471 | } 472 | } 473 | 474 | delete tdiBytes; 475 | } 476 | 477 | if (read) { 478 | //Read out the data from input buffer 479 | std::stringstream ss; 480 | for (int i = tdoBytes - 1; i >= 0; i--) 481 | ss << setfill('0') << setw(2) << hex << (int) tdoBuffer[i]; 482 | string hexTdo = ss.str(); 483 | if (hexTdo.length() - 1 == mask.length()) 484 | hexTdo = hexTdo.substr(1, hexTdo.length() - 1); 485 | if (!compareHexString(hexTdo, tdo, mask)) { 486 | cerr << "TDO didn't match expected string. Got " << hexTdo 487 | << " expected " << tdo << " with mask " << mask << endl; 488 | 489 | delete byOutputBuffer; 490 | delete tdoBuffer; 491 | delete byInputBuffer; 492 | return false; 493 | 494 | } 495 | } 496 | 497 | delete byOutputBuffer; 498 | delete tdoBuffer; 499 | delete byInputBuffer; 500 | return true; 501 | } 502 | 503 | string Jtag::shiftData(unsigned int bitCount, string tdi) { 504 | FT_STATUS ftStatus; 505 | unsigned int reqBytes = bitCount / 8 + (bitCount % 8 > 0); 506 | BYTE *byOutputBuffer = new BYTE[reqBytes + 3]; 507 | BYTE *tdoBuffer = new BYTE[reqBytes + 3]; 508 | BYTE *byInputBuffer = new BYTE[reqBytes + 6]; 509 | DWORD dwNumBytesSent = 0; 510 | DWORD dwNumBytesToRead = 0; 511 | DWORD dwNumBytesRead = 0; 512 | DWORD tdoBytes = 0; 513 | 514 | unsigned int reqHex = bitCount / 4 + (bitCount % 4 > 0); 515 | 516 | if (tdi.length() < reqHex) 517 | return ""; 518 | 519 | if (!flush()) 520 | return ""; 521 | 522 | if (bitCount < 9) { 523 | int data = stoi(tdi, 0, 16); 524 | byOutputBuffer[0] = 0x3B; 525 | byOutputBuffer[1] = bitCount - 2; 526 | byOutputBuffer[2] = data & 0xff; 527 | ftStatus = FT_Write(ftHandle, byOutputBuffer, 3, &dwNumBytesSent); 528 | if (ftStatus != FT_OK) { 529 | delete byOutputBuffer; 530 | delete tdoBuffer; 531 | delete byInputBuffer; 532 | return ""; 533 | } 534 | 535 | BYTE lastBit = (data >> ((bitCount - 1) % 8)) & 0x01; 536 | 537 | byOutputBuffer[0] = 0x6E; 538 | byOutputBuffer[1] = 0x00; 539 | byOutputBuffer[2] = 0x03 | (lastBit << 7); 540 | ftStatus = FT_Write(ftHandle, byOutputBuffer, 3, &dwNumBytesSent); 541 | if (ftStatus != FT_OK) { 542 | delete byOutputBuffer; 543 | delete tdoBuffer; 544 | delete byInputBuffer; 545 | return ""; 546 | } 547 | 548 | do { 549 | ftStatus = FT_GetQueueStatus(ftHandle, &dwNumBytesToRead); 550 | // Get the number of bytes in the device input buffer 551 | } while ((dwNumBytesToRead == 0) && (ftStatus == FT_OK)); 552 | //or Timeout 553 | ftStatus = FT_Read(ftHandle, byInputBuffer, dwNumBytesToRead, 554 | &dwNumBytesRead); 555 | if (ftStatus != FT_OK) { 556 | delete byOutputBuffer; 557 | delete tdoBuffer; 558 | delete byInputBuffer; 559 | return ""; 560 | } 561 | 562 | tdoBuffer[0] = byInputBuffer[0] >> (8 - (bitCount - 1)); 563 | tdoBuffer[0] |= byInputBuffer[1] >> (7 - (bitCount - 1)); 564 | tdoBytes = 1; 565 | 566 | } else { 567 | BYTE *tdiBytes = new BYTE[reqBytes + 1]; 568 | hexToByte(tdi, tdiBytes); 569 | 570 | unsigned int fullBytes = (bitCount - 1) / 8; 571 | unsigned int remBytes = fullBytes; 572 | unsigned int offset = 0; 573 | 574 | if (fullBytes > 65536) { 575 | cout << "Large transfers with reads may not work!" << endl; 576 | } 577 | 578 | while (remBytes > 0) { 579 | unsigned int bct = remBytes > 65536 ? 65536 : remBytes; 580 | byOutputBuffer[0] = 0x39; 581 | byOutputBuffer[1] = (bct - 1) & 0xff; 582 | byOutputBuffer[2] = ((bct - 1) >> 8) & 0xff; 583 | 584 | for (unsigned int i = 0; i < bct; i++) { 585 | byOutputBuffer[3 + i] = tdiBytes[i + offset]; 586 | } 587 | 588 | ftStatus = FT_Write(ftHandle, byOutputBuffer, 3 + bct, 589 | &dwNumBytesSent); 590 | if (ftStatus != FT_OK) { 591 | delete byOutputBuffer; 592 | delete tdoBuffer; 593 | delete byInputBuffer; 594 | delete tdiBytes; 595 | return ""; 596 | } 597 | 598 | remBytes -= bct; 599 | offset += bct; 600 | } 601 | 602 | unsigned int partialBits = bitCount - 1 - (fullBytes * 8); 603 | 604 | if (fullBytes * 8 + 1 != bitCount) { 605 | byOutputBuffer[0] = 0x3B; 606 | byOutputBuffer[1] = partialBits - 1; 607 | byOutputBuffer[2] = tdiBytes[reqBytes - 1] & 0xff; 608 | ftStatus = FT_Write(ftHandle, byOutputBuffer, 3, &dwNumBytesSent); 609 | if (ftStatus != FT_OK) { 610 | delete byOutputBuffer; 611 | delete tdoBuffer; 612 | delete byInputBuffer; 613 | delete tdiBytes; 614 | return ""; 615 | } 616 | } 617 | 618 | BYTE lastBit = (tdiBytes[reqBytes - 1] >> ((bitCount - 1) % 8)) & 0x01; 619 | 620 | byOutputBuffer[0] = 0x6E; 621 | byOutputBuffer[1] = 0x00; 622 | byOutputBuffer[2] = 0x03 | (lastBit << 7); 623 | ftStatus = FT_Write(ftHandle, byOutputBuffer, 3, &dwNumBytesSent); 624 | if (ftStatus != FT_OK) { 625 | delete byOutputBuffer; 626 | delete tdoBuffer; 627 | delete byInputBuffer; 628 | delete tdiBytes; 629 | return ""; 630 | } 631 | 632 | DWORD bytesToRead = fullBytes 633 | + ((fullBytes * 8 + 1 != bitCount) ? 2 : 1); 634 | do { 635 | ftStatus = FT_GetQueueStatus(ftHandle, &dwNumBytesToRead); 636 | // Get the number of bytes in the device input buffer 637 | } while ((dwNumBytesToRead != bytesToRead) && (ftStatus == FT_OK)); 638 | //or Timeout 639 | ftStatus = FT_Read(ftHandle, byInputBuffer, dwNumBytesToRead, 640 | &dwNumBytesRead); 641 | if (ftStatus != FT_OK) { 642 | delete byOutputBuffer; 643 | delete tdoBuffer; 644 | delete byInputBuffer; 645 | delete tdiBytes; 646 | return ""; 647 | } 648 | 649 | for (unsigned int i = 0; i < fullBytes; i++) 650 | tdoBuffer[tdoBytes++] = byInputBuffer[i]; 651 | 652 | if (fullBytes * 8 + 1 != bitCount) { 653 | tdoBuffer[tdoBytes] = byInputBuffer[tdoBytes] >> (8 - partialBits); 654 | tdoBuffer[tdoBytes++] |= byInputBuffer[dwNumBytesRead - 1] 655 | >> (7 - partialBits); 656 | } else { 657 | tdoBuffer[tdoBytes++] = byInputBuffer[dwNumBytesRead - 1] >> 7; 658 | } 659 | 660 | delete tdiBytes; 661 | } 662 | 663 | //Read out the data from input buffer 664 | std::stringstream ss; 665 | for (int i = tdoBytes - 1; i >= 0; i--) 666 | ss << setfill('0') << setw(2) << hex << (int) tdoBuffer[i]; 667 | string hexTdo = ss.str(); 668 | if (hexTdo.length() - 1 == tdi.length()) 669 | hexTdo = hexTdo.substr(1, hexTdo.length() - 1); 670 | 671 | delete byOutputBuffer; 672 | delete tdoBuffer; 673 | delete byInputBuffer; 674 | return hexTdo; 675 | } 676 | 677 | bool Jtag::sendClocks(unsigned long cycles) { 678 | BYTE byOutputBuffer[3]; 679 | DWORD dwNumBytesSent; 680 | FT_STATUS ftStatus; 681 | 682 | if (cycles / 8 > 65536) { 683 | if (!sendClocks(cycles - 65536 * 8)) 684 | return false; 685 | cycles = 65536 * 8; 686 | } 687 | 688 | cycles /= 8; 689 | 690 | byOutputBuffer[0] = 0x8F; 691 | byOutputBuffer[1] = (cycles - 1) & 0xff; 692 | byOutputBuffer[2] = ((cycles - 1) >> 8) & 0xff; 693 | ftStatus = FT_Write(ftHandle, byOutputBuffer, 3, &dwNumBytesSent); 694 | if (ftStatus != FT_OK) 695 | return false; 696 | 697 | return true; 698 | } 699 | 700 | bool Jtag::compareHexString(string a, string b, string mask) { 701 | unsigned int length = a.length(); 702 | 703 | if (length != b.length()) { 704 | cout << "length mismatch!" << endl; 705 | return false; 706 | } 707 | 708 | if (mask == "") 709 | return a == b; 710 | 711 | if (length != mask.length()) { 712 | cout << "length and mask mismatch! " << length << " and " 713 | << mask.length() << " of strings " << a << " and " << mask 714 | << endl; 715 | return false; 716 | } 717 | 718 | for (unsigned int i = 0; i < length / 2; i++) { 719 | BYTE m = stoi(mask.substr(length - 2 - i * 2, 2), 0, 16); 720 | BYTE pa = stoi(a.substr(length - 2 - i * 2, 2), 0, 16); 721 | BYTE pb = stoi(b.substr(length - 2 - i * 2, 2), 0, 16); 722 | if ((pa & m) != (pb & m)) { 723 | cout << "Mismatch at " << i << endl; 724 | return false; 725 | } 726 | 727 | } 728 | if ((length & 1) != 0) { 729 | BYTE m = stoi(mask.substr(0, 1), 0, 16); 730 | BYTE pa = stoi(a.substr(0, 1), 0, 16); 731 | BYTE pb = stoi(b.substr(0, 1), 0, 16); 732 | if ((pa & m) != (pb & m)) { 733 | cout << "Mismatch at last bit" << endl; 734 | return false; 735 | } 736 | } 737 | 738 | return true; 739 | } 740 | 741 | void Jtag::hexToByte(string hex, BYTE* out) { 742 | int length = hex.length(); 743 | for (int i = 0; i < length / 2; i++) { 744 | try { 745 | out[i] = stoi(hex.substr(length - 2 - i * 2, 2), 0, 16); 746 | } catch (exception& e) { 747 | cerr << "Failed at " << i << " with length " << length << endl; 748 | cerr.flush(); 749 | exit(2); 750 | } 751 | } 752 | if ((length & 1) != 0) 753 | try { 754 | out[length / 2] = stoi(hex.substr(0, 1), 0, 16); 755 | } catch (exception& e) { 756 | cerr << "Failed to convert string " << hex.substr(0, 1) 757 | << " to an int!" << endl; 758 | cerr.flush(); 759 | exit(2); 760 | } 761 | } 762 | 763 | bool Jtag::flush() { 764 | FT_STATUS ftStatus; 765 | BYTE byInputBuffer[1024]; 766 | DWORD dwNumBytesToRead = 0; 767 | DWORD dwNumBytesRead = 0; 768 | 769 | ftStatus = FT_GetQueueStatus(ftHandle, &dwNumBytesToRead); 770 | if (ftStatus != FT_OK) 771 | return false; 772 | if (dwNumBytesToRead == 0) 773 | return true; 774 | 775 | //or Timeout 776 | ftStatus = FT_Read(ftHandle, &byInputBuffer, dwNumBytesToRead, 777 | &dwNumBytesRead); 778 | if (ftStatus != FT_OK) 779 | return false; 780 | return true; 781 | } 782 | -------------------------------------------------------------------------------- /src/spi.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * iceprog -- simple programming tool for FTDI-based Lattice iCE programmers 3 | * 4 | * Copyright (C) 2015 Clifford Wolf 5 | * Copyright (C) 2018 Piotr Esden-Tempski 6 | * 7 | * Permission to use, copy, modify, and/or distribute this software for any 8 | * purpose with or without fee is hereby granted, provided that the above 9 | * copyright notice and this permission notice appear in all copies. 10 | * 11 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 12 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 13 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 14 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 15 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 16 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 17 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 18 | * 19 | * Relevant Documents: 20 | * ------------------- 21 | * http://www.latticesemi.com/~/media/Documents/UserManuals/EI/icestickusermanual.pdf 22 | * http://www.micron.com/~/media/documents/products/data-sheet/nor-flash/serial-nor/n25q/n25q_32mb_3v_65nm.pdf 23 | * http://www.ftdichip.com/Support/Documents/AppNotes/AN_108_Command_Processor_for_MPSSE_and_MCU_Host_Bus_Emulation_Modes.pdf 24 | */ 25 | 26 | #include "spi.h" 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #ifdef _WIN32 35 | #include "mingw.thread.h" 36 | #else 37 | #include 38 | #endif 39 | 40 | #include 41 | 42 | using namespace std; 43 | 44 | /* MPSSE engine command definitions */ 45 | enum mpsse_cmd { 46 | /* Mode commands */ 47 | MC_SETB_LOW = 0x80, /* Set Data bits LowByte */ 48 | MC_READB_LOW = 0x81, /* Read Data bits LowByte */ 49 | MC_SETB_HIGH = 0x82, /* Set Data bits HighByte */ 50 | MC_READB_HIGH = 0x83, /* Read data bits HighByte */ 51 | MC_LOOPBACK_EN = 0x84, /* Enable loopback */ 52 | MC_LOOPBACK_DIS = 0x85, /* Disable loopback */ 53 | MC_SET_CLK_DIV = 0x86, /* Set clock divisor */ 54 | MC_FLUSH = 0x87, /* Flush buffer fifos to the PC. */ 55 | MC_WAIT_H = 0x88, /* Wait on GPIOL1 to go high. */ 56 | MC_WAIT_L = 0x89, /* Wait on GPIOL1 to go low. */ 57 | MC_TCK_X5 = 0x8A, /* Disable /5 div, enables 60MHz master clock */ 58 | MC_TCK_D5 = 0x8B, /* Enable /5 div, backward compat to FT2232D */ 59 | MC_EN_3PH_CLK = 0x8C, /* Enable 3 phase clk, DDR I2C */ 60 | MC_DIS_3PH_CLK = 0x8D, /* Disable 3 phase clk */ 61 | MC_CLK_N = 0x8E, /* Clock every bit, used for JTAG */ 62 | MC_CLK_N8 = 0x8F, /* Clock every byte, used for JTAG */ 63 | MC_CLK_TO_H = 0x94, /* Clock until GPIOL1 goes high */ 64 | MC_CLK_TO_L = 0x95, /* Clock until GPIOL1 goes low */ 65 | MC_EN_ADPT_CLK = 0x96, /* Enable adaptive clocking */ 66 | MC_DIS_ADPT_CLK = 0x97, /* Disable adaptive clocking */ 67 | MC_CLK8_TO_H = 0x9C, /* Clock until GPIOL1 goes high, count bytes */ 68 | MC_CLK8_TO_L = 0x9D, /* Clock until GPIOL1 goes low, count bytes */ 69 | MC_TRI = 0x9E, /* Set IO to only drive on 0 and tristate on 1 */ 70 | /* CPU mode commands */ 71 | MC_CPU_RS = 0x90, /* CPUMode read short address */ 72 | MC_CPU_RE = 0x91, /* CPUMode read extended address */ 73 | MC_CPU_WS = 0x92, /* CPUMode write short address */ 74 | MC_CPU_WE = 0x93, /* CPUMode write extended address */ 75 | }; 76 | 77 | // --------------------------------------------------------- 78 | // FLASH definitions 79 | // --------------------------------------------------------- 80 | 81 | /* Transfer Command bits */ 82 | 83 | /* All byte based commands consist of: 84 | * - Command byte 85 | * - Length lsb 86 | * - Length msb 87 | * 88 | * If data out is enabled the data follows after the above command bytes, 89 | * otherwise no additional data is needed. 90 | * - Data * n 91 | * 92 | * All bit based commands consist of: 93 | * - Command byte 94 | * - Length 95 | * 96 | * If data out is enabled a byte containing bitst to transfer follows. 97 | * Otherwise no additional data is needed. Only up to 8 bits can be transferred 98 | * per transaction when in bit mode. 99 | */ 100 | 101 | /* b 0000 0000 102 | * |||| |||`- Data out negative enable. Update DO on negative clock edge. 103 | * |||| ||`-- Bit count enable. When reset count represents bytes. 104 | * |||| |`--- Data in negative enable. Latch DI on negative clock edge. 105 | * |||| `---- LSB enable. When set clock data out LSB first. 106 | * |||| 107 | * |||`------ Data out enable 108 | * ||`------- Data in enable 109 | * |`-------- TMS mode enable 110 | * `--------- Special command mode enable. See mpsse_cmd enum. 111 | */ 112 | 113 | #define MC_DATA_TMS (0x40) /* When set use TMS mode */ 114 | #define MC_DATA_IN (0x20) /* When set read data (Data IN) */ 115 | #define MC_DATA_OUT (0x10) /* When set write data (Data OUT) */ 116 | #define MC_DATA_LSB (0x08) /* When set input/output data LSB first. */ 117 | #define MC_DATA_ICN (0x04) /* When set receive data on negative clock edge */ 118 | #define MC_DATA_BITS (0x02) /* When set count bits not bytes */ 119 | #define MC_DATA_OCN (0x01) /* When set update data on negative clock edge */ 120 | 121 | /* Flash command definitions */ 122 | /* This command list is based on the Winbond W25Q128JV Datasheet */ 123 | enum flash_cmd { 124 | FC_WE = 0x06, /* Write Enable */ 125 | FC_SRWE = 0x50, /* Volatile SR Write Enable */ 126 | FC_WD = 0x04, /* Write Disable */ 127 | FC_RPD = 0xAB, /* Release Power-Down, returns Device ID */ 128 | FC_MFGID = 0x90, /* Read Manufacturer/Device ID */ 129 | FC_JEDECID = 0x9F, /* Read JEDEC ID */ 130 | FC_UID = 0x4B, /* Read Unique ID */ 131 | FC_RD = 0x03, /* Read Data */ 132 | FC_FR = 0x0B, /* Fast Read */ 133 | FC_PP = 0x02, /* Page Program */ 134 | FC_SE = 0x20, /* Sector Erase 4kb */ 135 | FC_BE32 = 0x52, /* Block Erase 32kb */ 136 | FC_BE64 = 0xD8, /* Block Erase 64kb */ 137 | FC_CE = 0xC7, /* Chip Erase */ 138 | FC_RSR1 = 0x05, /* Read Status Register 1 */ 139 | FC_WSR1 = 0x01, /* Write Status Register 1 */ 140 | FC_RSR2 = 0x35, /* Read Status Register 2 */ 141 | FC_WSR2 = 0x31, /* Write Status Register 2 */ 142 | FC_RSR3 = 0x15, /* Read Status Register 3 */ 143 | FC_WSR3 = 0x11, /* Write Status Register 3 */ 144 | FC_RSFDP = 0x5A, /* Read SFDP Register */ 145 | FC_ESR = 0x44, /* Erase Security Register */ 146 | FC_PSR = 0x42, /* Program Security Register */ 147 | FC_RSR = 0x48, /* Read Security Register */ 148 | FC_GBL = 0x7E, /* Global Block Lock */ 149 | FC_GBU = 0x98, /* Global Block Unlock */ 150 | FC_RBL = 0x3D, /* Read Block Lock */ 151 | FC_RPR = 0x3C, /* Read Sector Protection Registers (adesto) */ 152 | FC_IBL = 0x36, /* Individual Block Lock */ 153 | FC_IBU = 0x39, /* Individual Block Unlock */ 154 | FC_EPS = 0x75, /* Erase / Program Suspend */ 155 | FC_EPR = 0x7A, /* Erase / Program Resume */ 156 | FC_PD = 0xB9, /* Power-down */ 157 | FC_QPI = 0x38, /* Enter QPI mode */ 158 | FC_ERESET = 0x66, /* Enable Reset */ 159 | FC_RESET = 0x99, /* Reset Device */ 160 | }; 161 | 162 | Spi::Spi() { 163 | ftHandle = 0; 164 | active = false; 165 | verbose = false; 166 | } 167 | 168 | FT_STATUS Spi::connect(unsigned int devNumber) { 169 | return FT_Open(devNumber, &ftHandle); 170 | } 171 | 172 | FT_STATUS Spi::disconnect() { 173 | active = false; 174 | return FT_Close(ftHandle); 175 | } 176 | 177 | bool Spi::initialize() { 178 | BYTE byInputBuffer[1024]; // Buffer to hold data read from the FT2232H 179 | DWORD dwNumBytesToRead = 0; // Number of bytes available to read in the driver's input buffer 180 | DWORD dwNumBytesRead = 0; // Count of actual bytes read - used with FT_Read 181 | FT_STATUS ftStatus; 182 | ftStatus = FT_ResetDevice(ftHandle); 183 | //Reset USB device 184 | //Purge USB receive buffer first by reading out all old data from FT2232H receive buffer 185 | ftStatus |= FT_GetQueueStatus(ftHandle, &dwNumBytesToRead); // Get the number of bytes in the FT2232H receive buffer 186 | if ((ftStatus == FT_OK) && (dwNumBytesToRead > 0)) 187 | FT_Read(ftHandle, &byInputBuffer, dwNumBytesToRead, &dwNumBytesRead);//Read out the data from FT2232H receive buffer 188 | ftStatus |= FT_SetUSBParameters(ftHandle, 65536, 65535);//Set USB request transfer sizes to 64K 189 | ftStatus |= FT_SetChars(ftHandle, false, 0, false, 0); //Disable event and error characters 190 | ftStatus |= FT_SetTimeouts(ftHandle, 0, 5000); //Sets the read and write timeouts in milliseconds 191 | ftStatus |= FT_SetLatencyTimer(ftHandle, 1); //Set the latency timer 1ms 192 | ftStatus |= FT_SetBitMode(ftHandle, 0x0, 0x00); //Reset controller 193 | ftStatus |= FT_SetBitMode(ftHandle, 0x0, 0x02); //Enable MPSSE mode 194 | 195 | if (ftStatus != FT_OK) { 196 | cerr << "Failed to set initial configuration!" << endl; 197 | return false; 198 | } 199 | 200 | std::this_thread::sleep_for(std::chrono::milliseconds(100)); // Wait for all the USB stuff to complete and work 201 | 202 | if (!sync_mpsse()){ 203 | cerr << "Failed to sync with MPSSE!" << endl; 204 | return false; 205 | } 206 | 207 | if (!config_spi()){ 208 | cerr << "Failed to set SPI configuration!" << endl; 209 | return false; 210 | } 211 | 212 | active = true; 213 | 214 | return true; 215 | } 216 | 217 | bool Spi::sync_mpsse() { 218 | BYTE byOutputBuffer[8]; // Buffer to hold MPSSE commands and data to be sent to the FT2232H 219 | BYTE byInputBuffer[8]; // Buffer to hold data read from the FT2232H 220 | DWORD dwCount = 0; // General loop index 221 | DWORD dwNumBytesToSend = 0; // Index to the output buffer 222 | DWORD dwNumBytesSent = 0; // Count of actual bytes sent - used with FT_Write 223 | DWORD dwNumBytesToRead = 0; // Number of bytes available to read in the driver's input buffer 224 | DWORD dwNumBytesRead = 0; // Count of actual bytes read - used with FT_Read 225 | FT_STATUS ftStatus; 226 | 227 | byOutputBuffer[dwNumBytesToSend++] = 0xAA; //'\xAA'; 228 | //Add bogus command ‘xAA’ to the queue 229 | ftStatus = FT_Write(ftHandle, byOutputBuffer, dwNumBytesToSend, 230 | &dwNumBytesSent); 231 | // Send off the BAD commands 232 | dwNumBytesToSend = 0; // Reset output buffer pointer 233 | do { 234 | ftStatus = FT_GetQueueStatus(ftHandle, &dwNumBytesToRead); 235 | // Get the number of bytes in the device input buffer 236 | } while ((dwNumBytesToRead == 0) && (ftStatus == FT_OK)); 237 | if (dwNumBytesRead > 8) { 238 | cerr << "Input buffer too small in sync_mpsse()!" << endl; 239 | return false; 240 | } 241 | //or Timeout 242 | bool bCommandEchod = false; 243 | ftStatus = FT_Read(ftHandle, &byInputBuffer, dwNumBytesToRead, 244 | &dwNumBytesRead); 245 | //Read out the data from input buffer 246 | for (dwCount = 0; dwCount < dwNumBytesRead - 1; dwCount++) 247 | //Check if Bad command and echo command received 248 | { 249 | if ((byInputBuffer[dwCount] == 0xFA) 250 | && (byInputBuffer[dwCount + 1] == 0xAA)) { 251 | bCommandEchod = true; 252 | break; 253 | } 254 | } 255 | 256 | return bCommandEchod; 257 | } 258 | 259 | bool Spi::config_spi() { 260 | FT_STATUS ftStatus; 261 | BYTE byOutputBuffer[64]; // Buffer to hold MPSSE commands and data to be sent to the FT2232H 262 | DWORD dwNumBytesToSend = 0; // Index to the output buffer 263 | DWORD dwNumBytesSent = 0; // Count of actual bytes sent - used with FT_Write 264 | DWORD dwClockDivisor = 0x0; // Value of clock divisor, SCL Frequency = 60/((1+0x0)*2) (MHz) = 30MHz 265 | // ----------------------------------------------------------- 266 | // Configure the MPSSE settings for SPI 267 | // Multiple commands can be sent to the MPSSE with one FT_Write 268 | // ----------------------------------------------------------- 269 | dwNumBytesToSend = 0; // Start with a fresh index 270 | // Set up the Hi-Speed specific commands for the FTx232H 271 | byOutputBuffer[dwNumBytesToSend++] = 0x8A; 272 | // Use 60MHz master clock (disable divide by 5) 273 | byOutputBuffer[dwNumBytesToSend++] = 0x97; 274 | // Turn off adaptive clocking (may be needed for ARM) 275 | byOutputBuffer[dwNumBytesToSend++] = 0x8D; 276 | // Disable three-phase clocking 277 | ftStatus = FT_Write(ftHandle, byOutputBuffer, dwNumBytesToSend, 278 | &dwNumBytesSent); 279 | // Send off the HS-specific commands 280 | 281 | if (ftStatus != FT_OK) 282 | return false; 283 | 284 | dwNumBytesToSend = 0; // Reset output buffer pointer 285 | // Set initial states of the MPSSE interface - low byte, both pin directions and output values 286 | // Pin name Signal Direction Config Initial State Config 287 | // ADBUS0 SCK output 1 low 0 288 | // ADBUS1 MOSI output 1 low 0 289 | // ADBUS2 MISO input 0 low 0 290 | // ADBUS3 NC output 1 low 0 291 | // ADBUS4 SS output 1 low 0 292 | // ADBUS5 NC output 1 low 0 293 | // ADBUS6 CDONE input 0 low 0 294 | // ADBUS7 CRESET output 1 low 0 295 | byOutputBuffer[dwNumBytesToSend++] = 0x80; 296 | // Set data bits low-byte of MPSSE port 297 | byOutputBuffer[dwNumBytesToSend++] = 0x00; 298 | // Initial state config above 299 | byOutputBuffer[dwNumBytesToSend++] = 0xBB; 300 | // Direction config above 301 | ftStatus = FT_Write(ftHandle, byOutputBuffer, dwNumBytesToSend, 302 | &dwNumBytesSent); 303 | // Send off the low GPIO config commands 304 | 305 | if (ftStatus != FT_OK) 306 | return false; 307 | 308 | dwNumBytesToSend = 0; // Reset output buffer pointer 309 | // Set initial states of the MPSSE interface - high byte, both pin directions and output values 310 | // Pin name Signal Direction Config Initial State Config 311 | // ACBUS0 GPIOH0 input 0 0 312 | // ACBUS1 GPIOH1 input 0 0 313 | // ACBUS2 GPIOH2 input 0 0 314 | // ACBUS3 GPIOH3 input 0 0 315 | // ACBUS4 GPIOH4 input 0 0 316 | // ACBUS5 GPIOH5 input 0 0 317 | // ACBUS6 GPIOH6 input 0 0 318 | // ACBUS7 GPIOH7 input 0 0 319 | byOutputBuffer[dwNumBytesToSend++] = 0x82; 320 | // Set data bits low-byte of MPSSE port 321 | byOutputBuffer[dwNumBytesToSend++] = 0x00; 322 | // Initial state config above 323 | byOutputBuffer[dwNumBytesToSend++] = 0x00; 324 | // Direction config above 325 | ftStatus = FT_Write(ftHandle, byOutputBuffer, dwNumBytesToSend, 326 | &dwNumBytesSent); 327 | // Send off the high GPIO config commands 328 | 329 | if (ftStatus != FT_OK) 330 | return false; 331 | 332 | dwNumBytesToSend = 0; // Reset output buffer pointer 333 | // Set TCK frequency 334 | // TCK = 60MHz /((1 + [(1 +0xValueH*256) OR 0xValueL])*2) 335 | byOutputBuffer[dwNumBytesToSend++] = 0x86; 336 | //Command to set clock divisor 337 | byOutputBuffer[dwNumBytesToSend++] = dwClockDivisor & 0xFF; 338 | //Set 0xValueL of clock divisor 339 | byOutputBuffer[dwNumBytesToSend++] = (dwClockDivisor >> 8) & 0xFF; 340 | //Set 0xValueH of clock divisor 341 | ftStatus = FT_Write(ftHandle, byOutputBuffer, dwNumBytesToSend, 342 | &dwNumBytesSent); 343 | // Send off the clock divisor commands 344 | 345 | if (ftStatus != FT_OK) 346 | return false; 347 | 348 | dwNumBytesToSend = 0; // Reset output buffer pointer 349 | // Disable internal loop-back 350 | byOutputBuffer[dwNumBytesToSend++] = 0x85; 351 | // Disable loopback 352 | ftStatus = FT_Write(ftHandle, byOutputBuffer, dwNumBytesToSend, 353 | &dwNumBytesSent); 354 | // Send off the loopback command 355 | 356 | if (ftStatus != FT_OK) 357 | return false; 358 | 359 | return true; 360 | } 361 | 362 | void Spi::check_rx() { 363 | FT_STATUS ftStatus; 364 | BYTE byInputBuffer[1]; 365 | DWORD dwNumBytesRead = 0; 366 | 367 | while (1) { 368 | ftStatus = FT_Read(ftHandle, &byInputBuffer, 1, &dwNumBytesRead); 369 | if (ftStatus != FT_OK) 370 | break; 371 | cerr << "Unexpected rx byte: " << byInputBuffer[0] << endl; 372 | } 373 | } 374 | 375 | void Spi::error(int status) { 376 | check_rx(); 377 | cerr << "ABORT." << endl; 378 | if (active) 379 | disconnect(); 380 | exit(status); 381 | } 382 | 383 | BYTE Spi::recv_byte() { 384 | FT_STATUS ftStatus; 385 | BYTE byInputBuffer[1]; 386 | DWORD dwNumBytesRead = 0; 387 | while (1) { 388 | ftStatus = FT_Read(ftHandle, &byInputBuffer, 1, &dwNumBytesRead); 389 | if (ftStatus != FT_OK) { 390 | cerr << "Read error." << endl; 391 | error(2); 392 | } 393 | if (dwNumBytesRead == 1) 394 | break; 395 | std::this_thread::sleep_for(std::chrono::microseconds(100)); 396 | } 397 | return byInputBuffer[0]; 398 | } 399 | 400 | void Spi::send_byte(uint8_t data) { 401 | FT_STATUS ftStatus; 402 | BYTE byOutputBuffer[1]; 403 | DWORD dwNumBytesSent = 0; 404 | 405 | byOutputBuffer[0] = data; 406 | 407 | ftStatus = FT_Write(ftHandle, byOutputBuffer, 1, &dwNumBytesSent); 408 | if (ftStatus != FT_OK) { 409 | cerr << "Write error!" << endl; 410 | error(2); 411 | } 412 | if (dwNumBytesSent != 1) { 413 | cerr << "Write error (single byte, received " << dwNumBytesSent 414 | << ", expected 1)." << endl; 415 | error(2); 416 | } 417 | } 418 | 419 | void Spi::send_spi(uint8_t *data, int n) { 420 | if (n < 1) 421 | return; 422 | 423 | /* Output only, update data on negative clock edge. */ 424 | send_byte(MC_DATA_OUT | MC_DATA_OCN); 425 | send_byte(n - 1); 426 | send_byte((n - 1) >> 8); 427 | 428 | FT_STATUS ftStatus; 429 | DWORD dwNumBytesSent = 0; 430 | 431 | ftStatus = FT_Write(ftHandle, data, n, &dwNumBytesSent); 432 | if (ftStatus != FT_OK) { 433 | cerr << "Write error!" << endl; 434 | error(2); 435 | } 436 | 437 | if (dwNumBytesSent != (unsigned int) n) { 438 | fprintf(stderr, "Write error (chunk, rc=%u, expected %d).\n", 439 | dwNumBytesSent, n); 440 | error(2); 441 | } 442 | } 443 | 444 | void Spi::xfer_spi(uint8_t *data, int n) { 445 | if (n < 1) 446 | return; 447 | 448 | /* Input and output, update data on negative edge read on positive. */ 449 | send_byte(MC_DATA_IN | MC_DATA_OUT | MC_DATA_OCN); 450 | send_byte(n - 1); 451 | send_byte((n - 1) >> 8); 452 | 453 | FT_STATUS ftStatus; 454 | DWORD dwNumBytesSent = 0; 455 | 456 | ftStatus = FT_Write(ftHandle, data, n, &dwNumBytesSent); 457 | if (ftStatus != FT_OK) { 458 | cerr << "Write error!" << endl; 459 | error(2); 460 | } 461 | 462 | if (dwNumBytesSent != (unsigned int) n) { 463 | fprintf(stderr, "Write error (chunk, rc=%u, expected %d).\n", 464 | dwNumBytesSent, n); 465 | error(2); 466 | } 467 | 468 | for (int i = 0; i < n; i++) 469 | data[i] = recv_byte(); 470 | } 471 | 472 | uint8_t Spi::xfer_spi_bits(uint8_t data, int n) { 473 | if (n < 1) 474 | return 0; 475 | 476 | /* Input and output, update data on negative edge read on positive, bits. */ 477 | send_byte(MC_DATA_IN | MC_DATA_OUT | MC_DATA_OCN | MC_DATA_BITS); 478 | send_byte(n - 1); 479 | send_byte(data); 480 | 481 | return recv_byte(); 482 | } 483 | 484 | void Spi::set_gpio(int slavesel_b, int creset_b) { 485 | uint8_t gpio = 0; 486 | 487 | if (slavesel_b) { 488 | // ADBUS4 (GPIOL0) 489 | gpio |= 0x10; 490 | } 491 | 492 | if (creset_b) { 493 | // ADBUS7 (GPIOL3) 494 | gpio |= 0x80; 495 | } 496 | 497 | send_byte(MC_SETB_LOW); 498 | send_byte(gpio); /* Value */ 499 | send_byte(0x93); /* Direction */ 500 | } 501 | 502 | int Spi::get_cdone() { 503 | uint8_t data; 504 | send_byte(MC_READB_LOW); 505 | data = recv_byte(); 506 | // ADBUS6 (GPIOL2) 507 | return (data & 0x40) != 0; 508 | } 509 | 510 | // --------------------------------------------------------- 511 | // FLASH function implementations 512 | // --------------------------------------------------------- 513 | 514 | // the FPGA reset is released so also FLASH chip select should be deasserted 515 | void Spi::flash_release_reset() { 516 | set_gpio(1, 1); 517 | } 518 | 519 | // FLASH chip select assert 520 | // should only happen while FPGA reset is asserted 521 | void Spi::flash_chip_select() { 522 | set_gpio(0, 0); 523 | } 524 | 525 | // FLASH chip select deassert 526 | void Spi::flash_chip_deselect() { 527 | set_gpio(1, 0); 528 | } 529 | 530 | // SRAM reset is the same as flash_chip_select() 531 | // For ease of code reading we use this function instead 532 | void Spi::sram_reset() { 533 | // Asserting chip select and reset lines 534 | set_gpio(0, 0); 535 | } 536 | 537 | // SRAM chip select assert 538 | // When accessing FPGA SRAM the reset should be released 539 | void Spi::sram_chip_select() { 540 | set_gpio(0, 1); 541 | } 542 | 543 | void Spi::flash_read_id() { 544 | /* JEDEC ID structure: 545 | * Byte No. | Data Type 546 | * ---------+---------- 547 | * 0 | FC_JEDECID Request Command 548 | * 1 | MFG ID 549 | * 2 | Dev ID 1 550 | * 3 | Dev ID 2 551 | * 4 | Ext Dev Str Len 552 | */ 553 | 554 | uint8_t data[260] = { FC_JEDECID }; 555 | int len = 5; // command + 4 response bytes 556 | 557 | if (verbose) 558 | fprintf(stdout, "read flash ID..\n"); 559 | 560 | flash_chip_select(); 561 | 562 | // Write command and read first 4 bytes 563 | xfer_spi(data, len); 564 | 565 | if (data[4] == 0xFF) 566 | fprintf(stderr, "Extended Device String Length is 0xFF, " 567 | "this is likely a read error. Ignorig...\n"); 568 | else { 569 | // Read extended JEDEC ID bytes 570 | if (data[4] != 0) { 571 | len += data[4]; 572 | xfer_spi(data + 5, len - 5); 573 | } 574 | } 575 | 576 | flash_chip_deselect(); 577 | 578 | fprintf(stdout, "flash ID:"); 579 | for (int i = 1; i < len; i++) 580 | fprintf(stdout, " 0x%02X", data[i]); 581 | fprintf(stdout, "\n"); 582 | } 583 | 584 | void Spi::flash_reset() { 585 | flash_chip_select(); 586 | xfer_spi_bits(0xFF, 8); 587 | flash_chip_deselect(); 588 | 589 | flash_chip_select(); 590 | xfer_spi_bits(0xFF, 2); 591 | flash_chip_deselect(); 592 | } 593 | 594 | void Spi::flash_power_up() { 595 | uint8_t data_rpd[1] = { FC_RPD }; 596 | flash_chip_select(); 597 | xfer_spi(data_rpd, 1); 598 | flash_chip_deselect(); 599 | } 600 | 601 | void Spi::flash_power_down() { 602 | uint8_t data[1] = { FC_PD }; 603 | flash_chip_select(); 604 | xfer_spi(data, 1); 605 | flash_chip_deselect(); 606 | } 607 | 608 | uint8_t Spi::flash_read_status() { 609 | uint8_t data[2] = { FC_RSR1 }; 610 | 611 | flash_chip_select(); 612 | xfer_spi(data, 2); 613 | flash_chip_deselect(); 614 | 615 | if (verbose) { 616 | fprintf(stdout, "SR1: 0x%02X\n", data[1]); 617 | fprintf(stdout, " - SPRL: %s\n", 618 | ((data[1] & (1 << 7)) == 0) ? "unlocked" : "locked"); 619 | fprintf(stdout, " - SPM: %s\n", 620 | ((data[1] & (1 << 6)) == 0) ? 621 | "Byte/Page Prog Mode" : "Sequential Prog Mode"); 622 | fprintf(stdout, " - EPE: %s\n", 623 | ((data[1] & (1 << 5)) == 0) ? 624 | "Erase/Prog success" : "Erase/Prog error"); 625 | fprintf(stdout, "- SPM: %s\n", 626 | ((data[1] & (1 << 4)) == 0) ? 627 | "~WP asserted" : "~WP deasserted"); 628 | fprintf(stdout, " - SWP: "); 629 | switch ((data[1] >> 2) & 0x3) { 630 | case 0: 631 | fprintf(stdout, "All sectors unprotected\n"); 632 | break; 633 | case 1: 634 | fprintf(stdout, "Some sectors protected\n"); 635 | break; 636 | case 2: 637 | fprintf(stdout, "Reserved (xxxx 10xx)\n"); 638 | break; 639 | case 3: 640 | fprintf(stdout, "All sectors protected\n"); 641 | break; 642 | } 643 | fprintf(stdout, " - WEL: %s\n", 644 | ((data[1] & (1 << 1)) == 0) ? 645 | "Not write enabled" : "Write enabled"); 646 | fprintf(stdout, " - ~RDY: %s\n", 647 | ((data[1] & (1 << 0)) == 0) ? "Ready" : "Busy"); 648 | } 649 | 650 | std::this_thread::sleep_for(std::chrono::milliseconds(1)); 651 | return data[1]; 652 | } 653 | 654 | void Spi::flash_write_enable() { 655 | if (verbose) { 656 | fprintf(stdout, "status before enable:\n"); 657 | flash_read_status(); 658 | } 659 | 660 | if (verbose) 661 | fprintf(stdout, "write enable..\n"); 662 | 663 | uint8_t data[1] = { FC_WE }; 664 | flash_chip_select(); 665 | xfer_spi(data, 1); 666 | flash_chip_deselect(); 667 | 668 | if (verbose) { 669 | fprintf(stdout, "status after enable:\n"); 670 | flash_read_status(); 671 | } 672 | } 673 | 674 | void Spi::flash_bulk_erase() { 675 | if (verbose) 676 | fprintf(stdout, "bulk erase..\n"); 677 | 678 | uint8_t data[1] = { FC_CE }; 679 | flash_chip_select(); 680 | xfer_spi(data, 1); 681 | flash_chip_deselect(); 682 | } 683 | 684 | void Spi::flash_64kB_sector_erase(int addr) { 685 | if (verbose) 686 | fprintf(stdout, "erase 64kB sector at 0x%06X..\n", addr); 687 | 688 | uint8_t command[4] = { FC_BE64, (uint8_t) (addr >> 16), 689 | (uint8_t) (addr >> 8), (uint8_t) addr }; 690 | 691 | flash_chip_select(); 692 | send_spi(command, 4); 693 | flash_chip_deselect(); 694 | } 695 | 696 | void Spi::flash_prog(int addr, uint8_t *data, int n) { 697 | if (verbose) 698 | fprintf(stdout, "prog 0x%06X +0x%03X..\n", addr, n); 699 | 700 | uint8_t command[4] = { FC_PP, (uint8_t) (addr >> 16), (uint8_t) (addr >> 8), 701 | (uint8_t) addr }; 702 | 703 | flash_chip_select(); 704 | send_spi(command, 4); 705 | send_spi(data, n); 706 | flash_chip_deselect(); 707 | 708 | if (verbose) 709 | for (int i = 0; i < n; i++) 710 | fprintf(stderr, "%02x%c", data[i], 711 | i == n - 1 || i % 32 == 31 ? '\n' : ' '); 712 | } 713 | 714 | void Spi::flash_read(int addr, uint8_t *data, int n) { 715 | if (verbose) 716 | fprintf(stdout, "read 0x%06X +0x%03X..\n", addr, n); 717 | 718 | uint8_t command[4] = { FC_RD, (uint8_t) (addr >> 16), (uint8_t) (addr >> 8), 719 | (uint8_t) addr }; 720 | 721 | flash_chip_select(); 722 | send_spi(command, 4); 723 | memset(data, 0, n); 724 | xfer_spi(data, n); 725 | flash_chip_deselect(); 726 | 727 | if (verbose) 728 | for (int i = 0; i < n; i++) 729 | fprintf(stderr, "%02x%c", data[i], 730 | i == n - 1 || i % 32 == 31 ? '\n' : ' '); 731 | } 732 | 733 | void Spi::flash_wait() { 734 | if (verbose) 735 | fprintf(stderr, "waiting.."); 736 | 737 | int count = 0; 738 | while (1) { 739 | uint8_t data[2] = { FC_RSR1 }; 740 | 741 | flash_chip_select(); 742 | xfer_spi(data, 2); 743 | flash_chip_deselect(); 744 | 745 | if ((data[1] & 0x01) == 0) { 746 | if (count < 2) { 747 | count++; 748 | if (verbose) { 749 | fprintf(stderr, "r"); 750 | fflush(stderr); 751 | } 752 | } else { 753 | if (verbose) { 754 | fprintf(stderr, "R"); 755 | fflush(stderr); 756 | } 757 | break; 758 | } 759 | } else { 760 | if (verbose) { 761 | fprintf(stderr, "."); 762 | fflush(stderr); 763 | } 764 | count = 0; 765 | } 766 | 767 | std::this_thread::sleep_for(std::chrono::milliseconds(1)); 768 | } 769 | 770 | if (verbose) 771 | fprintf(stderr, "\n"); 772 | 773 | } 774 | 775 | void Spi::flash_disable_protection() { 776 | fprintf(stderr, "disable flash protection...\n"); 777 | 778 | // Write Status Register 1 <- 0x00 779 | uint8_t data[2] = { FC_WSR1, 0x00 }; 780 | flash_chip_select(); 781 | xfer_spi(data, 2); 782 | flash_chip_deselect(); 783 | 784 | flash_wait(); 785 | 786 | // Read Status Register 1 787 | data[0] = FC_RSR1; 788 | 789 | flash_chip_select(); 790 | xfer_spi(data, 2); 791 | flash_chip_deselect(); 792 | 793 | if (data[1] != 0x00) 794 | fprintf(stderr, 795 | "failed to disable protection, SR now equal to 0x%02x (expected 0x00)\n", 796 | data[1]); 797 | 798 | } 799 | 800 | bool Spi::eraseFlash() { 801 | fprintf(stdout, "reset..\n"); 802 | 803 | flash_chip_deselect(); 804 | std::this_thread::sleep_for(std::chrono::milliseconds(250)); 805 | 806 | fprintf(stdout, "cdone: %s\n", get_cdone() ? "high" : "low"); 807 | 808 | flash_reset(); 809 | flash_power_up(); 810 | 811 | flash_read_id(); 812 | 813 | flash_write_enable(); 814 | flash_bulk_erase(); 815 | flash_wait(); 816 | 817 | // --------------------------------------------------------- 818 | // Reset 819 | // --------------------------------------------------------- 820 | 821 | flash_power_down(); 822 | 823 | set_gpio(1, 1); 824 | std::this_thread::sleep_for(std::chrono::milliseconds(250)); 825 | 826 | fprintf(stdout, "cdone: %s\n", get_cdone() ? "high" : "low"); 827 | 828 | return true; 829 | } 830 | bool Spi::writeBin(string filename) { 831 | int rw_offset = 0; 832 | 833 | FILE *f = NULL; 834 | long file_size = -1; 835 | 836 | f = fopen(filename.c_str(), "rb"); 837 | 838 | if (f == NULL) { 839 | fprintf(stderr, "Can't open '%s' for reading: ", filename.c_str()); 840 | return false; 841 | } 842 | 843 | if (fseek(f, 0L, SEEK_END) != -1) { 844 | file_size = ftell(f); 845 | if (file_size == -1) { 846 | fprintf(stderr, "%s: ftell: ", filename.c_str()); 847 | return false; 848 | } 849 | if (fseek(f, 0L, SEEK_SET) == -1) { 850 | fprintf(stderr, "%s: fseek: ", filename.c_str()); 851 | return false; 852 | } 853 | } else { 854 | FILE *pipe = f; 855 | 856 | f = tmpfile(); 857 | if (f == NULL) { 858 | fprintf(stderr, "can't open temporary file\n"); 859 | return false; 860 | } 861 | file_size = 0; 862 | 863 | while (true) { 864 | static unsigned char buffer[4096]; 865 | size_t rc = fread(buffer, 1, 4096, pipe); 866 | if (rc <= 0) 867 | break; 868 | size_t wc = fwrite(buffer, 1, rc, f); 869 | if (wc != rc) { 870 | fprintf(stderr, "can't write to temporary file\n"); 871 | return false; 872 | } 873 | file_size += rc; 874 | } 875 | fclose(pipe); 876 | 877 | /* now seek to the beginning so we can 878 | start reading again */ 879 | fseek(f, 0, SEEK_SET); 880 | } 881 | 882 | cout << "Resetting..." << endl; 883 | 884 | flash_chip_deselect(); 885 | std::this_thread::sleep_for(std::chrono::milliseconds(250)); 886 | 887 | cout << "cdone: " << (get_cdone() ? "high" : "low") << endl; 888 | 889 | flash_reset(); 890 | flash_power_up(); 891 | 892 | flash_read_id(); 893 | 894 | int begin_addr = rw_offset & ~0xffff; 895 | int end_addr = (rw_offset + file_size + 0xffff) & ~0xffff; 896 | 897 | for (int addr = begin_addr; addr < end_addr; addr += 0x10000) { 898 | flash_write_enable(); 899 | flash_64kB_sector_erase(addr); 900 | if (verbose) { 901 | fprintf(stderr, "Status after block erase:\n"); 902 | flash_read_status(); 903 | } 904 | flash_wait(); 905 | } 906 | 907 | cout << "Programming... "; 908 | 909 | for (int rc, addr = 0; true; addr += rc) { 910 | uint8_t buffer[256]; 911 | int page_size = 256 - (rw_offset + addr) % 256; 912 | rc = fread(buffer, 1, page_size, f); 913 | if (rc <= 0) 914 | break; 915 | flash_write_enable(); 916 | flash_prog(rw_offset + addr, buffer, rc); 917 | flash_wait(); 918 | } 919 | 920 | cout << "Done." << endl; 921 | 922 | /* seek to the beginning for second pass */ 923 | fseek(f, 0, SEEK_SET); 924 | 925 | // --------------------------------------------------------- 926 | // Reset 927 | // --------------------------------------------------------- 928 | 929 | flash_power_down(); 930 | 931 | set_gpio(1, 1); 932 | std::this_thread::sleep_for(std::chrono::milliseconds(250)); 933 | 934 | cout << "cdone: " << (get_cdone() ? "high" : "low") << endl; 935 | cout << "Done." << endl; 936 | 937 | if (f != NULL && f != stdin && f != stdout) 938 | fclose(f); 939 | return true; 940 | } 941 | --------------------------------------------------------------------------------