add alchitry loader

This commit is contained in:
Paul Mathieu 2022-05-16 20:36:27 -07:00
parent 0226d314ec
commit 30d9a2d7c8
27 changed files with 5579 additions and 0 deletions

View File

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2019 alchitry
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

24
alchitry-loader/makefile Normal file
View File

@ -0,0 +1,24 @@
sources = $(wildcard src/*.cpp)
objects = $(sources:.cpp=.o)
static_libs = lib/macos/libftd2xx.a
libs = -lpthread
CXXFLAGS = -std=c++11
UNAME := $(shell uname)
# Assume target is Mac OS if build host is Mac OS; any other host targets Linux
ifeq ($(UNAME), Darwin)
libs += -lobjc -framework IOKit -framework CoreFoundation
else
libs += -lrt
endif
all: loader
loader: $(objects)
$(CXX) $(LDFLAGS) -o $@ $^ $(static_libs) $(libs)
.PHONY: clean
clean:
rm -rf loader $(objects)

View File

@ -0,0 +1,611 @@
#include <iostream>
#include <stdio.h>
#include "ftd2xx.h"
#include "jtag.h"
#include "jtag_fsm.h"
#include "loader.h"
#include "spi.h"
#include <unistd.h>
#include <stack>
#include <fstream>
#include <streambuf>
#include <sstream>
#include <iomanip>
#include <iterator>
#include "loader.h"
#include <chrono>
#ifdef _WIN32
#include "mingw.thread.h"
#else
#include <thread>
#endif
#include <cstring>
#include "config_type.h"
#define BOARD_ERROR -2
#define BOARD_UNKNOWN -1
#define BOARD_AU 0
#define BOARD_CU 1
using namespace std;
using get_time = chrono::steady_clock;
char ManufacturerBuf[32];
char ManufacturerIdBuf[16];
char DescriptionBuf[64];
char SerialNumberBuf[16];
string getErrorName(int error) {
switch (error) {
case FT_OK:
return "FT_OK";
case FT_INVALID_HANDLE:
return "FT_INVALID_HANDLE";
case FT_DEVICE_NOT_FOUND:
return "FT_DEVICE_NOT_FOUND";
case FT_DEVICE_NOT_OPENED:
return "FT_DEVICE_NOT_OPENED";
case FT_IO_ERROR:
return "FT_IO_ERROR";
case FT_INSUFFICIENT_RESOURCES:
return "FT_INSUFFICIENT_RESOURCES";
case FT_INVALID_PARAMETER:
return "FT_INVALID_PARAMETER";
case FT_INVALID_BAUD_RATE:
return "FT_INVALID_BAUD_RATE";
case FT_DEVICE_NOT_OPENED_FOR_ERASE:
return "FT_DEVICE_NOT_OPENED_FOR_ERASE";
case FT_DEVICE_NOT_OPENED_FOR_WRITE:
return "FT_DEVICE_NOT_OPENED_FOR_WRITE";
case FT_FAILED_TO_WRITE_DEVICE:
return "FT_FAILED_TO_WRITE_DEVICE";
case FT_EEPROM_READ_FAILED:
return "FT_EEPROM_READ_FAILED";
case FT_EEPROM_WRITE_FAILED:
return "FT_EEPROM_WRITE_FAILED";
case FT_EEPROM_ERASE_FAILED:
return "FT_EEPROM_ERASE_FAILED";
case FT_EEPROM_NOT_PRESENT:
return "FT_EEPROM_NOT_PRESENT";
case FT_EEPROM_NOT_PROGRAMMED:
return "FT_EEPROM_NOT_PROGRAMMED";
case FT_INVALID_ARGS:
return "FT_INVALID_ARGS";
case FT_NOT_SUPPORTED:
return "FT_NOT_SUPPORTED";
case FT_OTHER_ERROR:
return "FT_OTHER_ERROR";
case FT_DEVICE_LIST_NOT_READY:
return "FT_DEVICE_LIST_NOT_READY";
}
return "Unknown";
}
void write_to_file(string file, PFT_PROGRAM_DATA ftData) {
ofstream output_file(file, ios::binary);
CONFIG_DATA config;
ft_to_config(&config, ftData);
output_file.write((char*) &config, sizeof(CONFIG_DATA));
output_file.write(ManufacturerBuf, sizeof(ManufacturerBuf));
output_file.write(ManufacturerIdBuf, sizeof(ManufacturerIdBuf));
output_file.write(DescriptionBuf, sizeof(DescriptionBuf));
output_file.write(SerialNumberBuf, sizeof(SerialNumberBuf));
output_file.close();
}
bool read_from_file(string file, PFT_PROGRAM_DATA ftData) {
cout << "Reading " << file << endl;
try {
ifstream input_file(file, ios::in | ios::binary);
if (!input_file.is_open()) {
cerr << "Failed to open file " << file << endl;
return false;
}
CONFIG_DATA config;
input_file.read((char*) &config, sizeof(CONFIG_DATA));
config_to_ft(ftData, &config);
input_file.read(ManufacturerBuf, sizeof(ManufacturerBuf));
input_file.read(ManufacturerIdBuf, sizeof(ManufacturerIdBuf));
input_file.read(DescriptionBuf, sizeof(DescriptionBuf));
input_file.read(SerialNumberBuf, sizeof(SerialNumberBuf));
input_file.close();
ftData->Manufacturer = ManufacturerBuf;
ftData->ManufacturerId = ManufacturerIdBuf;
ftData->Description = DescriptionBuf;
ftData->SerialNumber = SerialNumberBuf;
} catch (...) {
cerr << "Failed to read file " << file << endl;
return false;
}
return true;
}
FT_STATUS read_from_device(FT_HANDLE ftHandle, PFT_PROGRAM_DATA ftData) {
ftData->Signature1 = 0x00000000;
ftData->Signature2 = 0xffffffff;
ftData->Version = 0x00000005;
ftData->Manufacturer = ManufacturerBuf;
ftData->ManufacturerId = ManufacturerIdBuf;
ftData->Description = DescriptionBuf;
ftData->SerialNumber = SerialNumberBuf;
return FT_EE_Read(ftHandle, ftData);
}
void print_info() {
cout << "Manufacture: " << ManufacturerBuf << endl;
cout << "ManufacturerId: " << ManufacturerIdBuf << endl;
cout << "Description: " << DescriptionBuf << endl;
cout << "SerialNumber: " << SerialNumberBuf << endl;
}
void erase(FT_HANDLE ftHandle) {
cout << "Erasing... ";
FT_STATUS ftStatus = FT_EraseEE(ftHandle);
if (ftStatus != FT_OK) // Did the command execute OK?
{
cerr << "Error in erasing device!" << endl;
FT_Close(ftHandle);
return;
}
cout << "Done." << endl;
}
bool programDevice(unsigned int devNumber, string file) {
FT_HANDLE ftHandle;
cout << "Opening device... ";
FT_STATUS ftStatus = FT_Open(devNumber, &ftHandle);
if (ftStatus != FT_OK) // Did the command execute OK?
{
printf("Error in opening device!\n");
return false; // Exit with error
}
cout << "Done." << endl;
FT_PROGRAM_DATA ftData;
cout << "Checking EEPROM... ";
ftStatus = read_from_device(ftHandle, &ftData);
if (ftStatus != FT_EEPROM_NOT_PROGRAMMED) { // device isn't blank
if (ftStatus == FT_OK) {
cout << "Not blank." << endl;
erase(ftHandle);
} else if (ftStatus == FT_EEPROM_NOT_PRESENT) {
cout << "Not present!" << endl;
FT_Close(ftHandle);
return false;
} else {
cout << getErrorName(ftStatus) << endl;
FT_Close(ftHandle);
return false;
}
} else {
cout << "Blank." << endl;
}
if (!read_from_file(file, &ftData)) {
FT_Close(ftHandle);
return false;
}
print_info();
cout << "Programming... ";
ftStatus = FT_EE_Program(ftHandle, &ftData);
if (ftStatus != FT_OK) // Did the command execute OK?
{
cout << "ERROR: " << getErrorName(ftStatus) << endl;
FT_Close(ftHandle);
return false;
}
cout << "Done." << endl;
FT_Close(ftHandle);
return true;
}
string descriptionToName(string des) {
if (des == "Alchitry Cu A") {
return "Alchitry Cu";
} else if (des == "Alchitry Au A") {
return "Alchitry Au";
} else {
return "Unknown";
}
}
int desciptionToType(string des) {
if (des == "Alchitry Cu A") {
return BOARD_CU;
} else if (des == "Alchitry Au A") {
return BOARD_AU;
} else {
return BOARD_UNKNOWN;
}
}
void printDeviceList() {
FT_STATUS ftStatus;
FT_DEVICE_LIST_INFO_NODE *devInfo;
DWORD numDevs = 0;
// create the device information list
ftStatus = FT_CreateDeviceInfoList(&numDevs);
if (ftStatus != FT_OK) {
cerr << "Could not read device list!" << endl;
return;
}
if (numDevs > 0) {
cout << "Devices: " << endl;
// allocate storage for list based on numDevs
devInfo = (FT_DEVICE_LIST_INFO_NODE*) malloc(
sizeof(FT_DEVICE_LIST_INFO_NODE) * numDevs);
// get the device information list
ftStatus = FT_GetDeviceInfoList(devInfo, &numDevs);
if (ftStatus == FT_OK) {
for (unsigned int i = 0; i < numDevs; i++) {
cout << " " << i << ": "
<< descriptionToName(devInfo[i].Description) << endl;
}
} else {
cerr << "Error getting device list!" << endl;
}
free(devInfo);
} else {
cout << "No devices found!" << endl;
}
}
int getDeviceType(unsigned int devNumber) {
FT_STATUS ftStatus;
FT_DEVICE_LIST_INFO_NODE *devInfo;
DWORD numDevs = 0;
int board = BOARD_ERROR;
// create the device information list
ftStatus = FT_CreateDeviceInfoList(&numDevs);
if (ftStatus != FT_OK) {
cerr << "Could not read device list!" << endl;
return BOARD_ERROR;
}
if (numDevs <= devNumber) {
cerr << "Invalid device number!" << endl;
return BOARD_ERROR;
}
// allocate storage for list based on numDevs
devInfo = (FT_DEVICE_LIST_INFO_NODE*) malloc(
sizeof(FT_DEVICE_LIST_INFO_NODE) * numDevs);
// get the device information list
ftStatus = FT_GetDeviceInfoList(devInfo, &numDevs);
if (ftStatus == FT_OK) {
string boardDescription = devInfo[devNumber].Description;
if (boardDescription == "Alchitry Cu A") {
board = BOARD_CU;
} else if (boardDescription == "Alchitry Au A") {
board = BOARD_AU;
} else {
board = BOARD_UNKNOWN;
}
} else {
cerr << "Error getting device list!" << endl;
}
free(devInfo);
return board;
}
int getFirstDeviceOfType(int board) {
FT_STATUS ftStatus;
FT_DEVICE_LIST_INFO_NODE *devInfo;
DWORD numDevs = 0;
// create the device information list
ftStatus = FT_CreateDeviceInfoList(&numDevs);
if (ftStatus != FT_OK) {
cerr << "Could not read device list!" << endl;
return -1;
}
if (numDevs < 1) {
cerr << "No devices found!" << endl;
return -1;
}
// allocate storage for list based on numDevs
devInfo = (FT_DEVICE_LIST_INFO_NODE*) malloc(
sizeof(FT_DEVICE_LIST_INFO_NODE) * numDevs);
// get the device information list
ftStatus = FT_GetDeviceInfoList(devInfo, &numDevs);
if (ftStatus == FT_OK) {
for (int devNumber = 0; devNumber < numDevs; devNumber++) {
string boardDescription = devInfo[devNumber].Description;
int type = desciptionToType(boardDescription);
if (type == board) {
free(devInfo);
return devNumber;
}
}
} else {
cerr << "Error getting device list!" << endl;
}
free(devInfo);
return -1;
}
bool readAndSaveFTDI(string file) {
FT_HANDLE ftHandle;
cout << "Opening device... ";
FT_STATUS ftStatus = FT_Open(0, &ftHandle);
if (ftStatus != FT_OK) // Did the command execute OK?
{
printf("Error in opening device!\n");
return false; // Exit with error
}
cout << "Done." << endl;
FT_PROGRAM_DATA ftData;
cout << "Checking EEPROM... ";
ftStatus = read_from_device(ftHandle, &ftData);
if (ftStatus != FT_OK) {
if (ftStatus == FT_EEPROM_NOT_PROGRAMMED) {
cout << "Blank." << endl;
return false;
} else if (ftStatus == FT_EEPROM_NOT_PRESENT) {
cout << "Not present!" << endl;
FT_Close(ftHandle);
return false;
} else {
cout << "Unknown error " << ftStatus << endl;
FT_Close(ftHandle);
return false;
}
} else { // eeprom not blank
cout << "Done." << endl;
}
cout << "Writing to file... ";
write_to_file(file, &ftData);
cout << "Done." << endl;
FT_Close(ftHandle);
return true;
}
void printUsage() {
cout << "Usage: \"loader arguments\"" << endl;
cout << endl;
cout << "Arguments:" << endl;
cout << " -e : erase FPGA flash" << endl;
cout << " -l : list detected boards" << endl;
cout << " -h : print this help message" << endl;
cout << " -f config.bin : write FPGA flash" << endl;
cout << " -r config.bin : write FPGA RAM" << endl;
cout << " -u config.data : write FTDI eeprom" << endl;
cout << " -b n : select board \"n\" (defaults to 0)" << endl;
cout << " -p loader.bin : Au bridge bin" << endl;
cout << " -t TYPE : TYPE can be au or cu (defaults to au)" << endl;
}
int main(int argc, char *argv[]) {
if (argc < 2) {
printUsage();
return 1;
}
bool fpgaFlash = false;
bool fpgaRam = false;
bool eeprom = false;
string eepromConfig;
string fpgaBinFlash;
string fpgaBinRam;
bool erase = false;
bool list = false;
bool print = false;
int deviceNumber = -1;
bool bridgeProvided = false;
string auBridgeBin;
bool isAu = true;
for (int i = 1; i < argc;) {
string arg = argv[i];
if (arg == "-e") {
i++;
erase = true;
} else if (arg == "-l") {
i++;
list = true;
} else if (arg == "-h") {
i++;
print = true;
} else if (arg == "-f") {
if (argc <= i + 1) {
cerr << "Missing bin file!" << endl;
printUsage();
return 1;
}
fpgaFlash = true;
fpgaBinFlash = argv[i + 1];
i += 2;
} else if (arg == "-r") {
if (argc <= i + 1) {
cerr << "Missing bin file!" << endl;
printUsage();
return 1;
}
fpgaRam = true;
fpgaBinRam = argv[i + 1];
i += 2;
} else if (arg == "-u") {
if (argc <= i + 1) {
cerr << "Missing data file!" << endl;
printUsage();
return 1;
}
eeprom = true;
eepromConfig = argv[i + 1];
i += 2;
} else if (arg == "-b") {
if (argc <= i + 1) {
cerr << "Missing board number!" << endl;
printUsage();
return 1;
}
try {
deviceNumber = stoi(argv[i + 1]);
} catch (const std::invalid_argument& ia) {
cerr << argv[i + 1] << " is not a number!" << endl;
printUsage();
return 1;
}
if (deviceNumber < 0) {
cerr << "Device numbers can't be negative!" << endl;
printUsage();
return 1;
}
i += 2;
} else if (arg == "-p") {
if (argc <= i + 1) {
cerr << "Missing bin file!" << endl;
printUsage();
return 1;
}
bridgeProvided = true;
auBridgeBin = argv[i + 1];
i += 2;
} else if (arg == "-t") {
if (argc <= i + 1) {
cerr << "Missing board type!" << endl;
printUsage();
return 1;
}
if (strcmp(argv[i + 1], "au") == 0) {
isAu = true;
} else if (strcmp(argv[i + 1], "cu") == 0) {
isAu = false;
} else {
cerr << "Invalid board type: " << argv[i + 1] << endl;
printUsage();
return 1;
}
i += 2;
} else {
cerr << "Unknown argument " << arg << endl;
printUsage();
return 1;
}
}
if (print)
printUsage();
if (list)
printDeviceList();
if (deviceNumber < 0)
deviceNumber = getFirstDeviceOfType(isAu ? BOARD_AU : BOARD_CU);
if (deviceNumber < 0) {
cerr << "Couldn't find device!" << endl;
return 2;
}
cout << "Found " << (isAu ? "Au" : "Cu") << " as device " << deviceNumber
<< "." << endl;
if (eeprom)
programDevice(deviceNumber, eepromConfig);
if (erase || fpgaFlash || fpgaRam) {
int boardType = getDeviceType(deviceNumber);
if ((isAu && boardType != BOARD_AU)
|| (!isAu && boardType != BOARD_CU)) {
cerr << "Invalid board type detected!" << endl;
return 2;
}
if (boardType == BOARD_AU) {
if (bridgeProvided == false && (erase || fpgaFlash)) {
cerr << "No Au bridge bin provided!" << endl;
return 2;
}
Jtag jtag;
if (jtag.connect(deviceNumber) != FT_OK) {
cerr << "Failed to connect to JTAG!" << endl;
return 2;
}
if (jtag.initialize() == false) {
cerr << "Failed to initialize JTAG!" << endl;
return 2;
}
Loader loader(&jtag);
if (erase) {
if (!loader.eraseFlash(auBridgeBin)) {
cerr << "Failed to erase flash!" << endl;
} else {
cout << "Done." << endl;
}
}
if (fpgaFlash) {
if (!loader.writeBin(fpgaBinFlash, true, auBridgeBin)) {
cerr << "Failed to write FPGA flash!" << endl;
}
}
if (fpgaRam) {
if (!loader.writeBin(fpgaBinRam, false, "")) {
cerr << "Failed to write FPGA RAM!" << endl;
}
}
jtag.disconnect();
} else if (boardType == BOARD_CU) {
Spi spi;
if (spi.connect(deviceNumber) != FT_OK) {
cerr << "Failed to connect to SPI!" << endl;
return 2;
}
if (spi.initialize() == false) {
cerr << "Failed to initialize SPI!" << endl;
return 2;
}
if (erase) {
if (!spi.eraseFlash()) {
cerr << "Failed to erase flash!" << endl;
} else {
cout << "Done." << endl;
}
}
if (fpgaFlash) {
if (!spi.writeBin(fpgaBinFlash)) {
cerr << "Failed to write FPGA flash!" << endl;
}
}
if (fpgaRam) {
cerr << "Alchitry Cu doesn't support RAM only programming!"
<< endl;
return 1;
}
} else {
cerr << "Unknown board type!" << endl;
return 2;
}
}
return 0;
}

View File

@ -0,0 +1,154 @@
#ifndef __WINDOWS_TYPES__
#define __WINDOWS_TYPES__
#define WINAPI
typedef unsigned int DWORD;
typedef unsigned int ULONG;
typedef unsigned short USHORT;
typedef unsigned short SHORT;
typedef unsigned char UCHAR;
typedef unsigned short WORD;
typedef unsigned short WCHAR;
typedef unsigned char BYTE;
typedef BYTE *LPBYTE;
typedef unsigned int BOOL;
typedef unsigned char BOOLEAN;
typedef unsigned char CHAR;
typedef BOOL *LPBOOL;
typedef UCHAR *PUCHAR;
typedef const char *LPCSTR;
typedef char *PCHAR;
typedef void *PVOID;
typedef void *HANDLE;
typedef unsigned int LONG;
typedef int INT;
typedef unsigned int UINT;
typedef char *LPSTR;
typedef char *LPTSTR;
typedef const char *LPCTSTR;
typedef DWORD *LPDWORD;
typedef WORD *LPWORD;
typedef ULONG *PULONG;
typedef LONG *LPLONG;
typedef PVOID LPVOID;
typedef void VOID;
typedef USHORT *PUSHORT;
typedef unsigned long long int ULONGLONG;
typedef struct _OVERLAPPED {
DWORD Internal;
DWORD InternalHigh;
union {
struct{
DWORD Offset;
DWORD OffsetHigh;
};
PVOID Pointer;
};
HANDLE hEvent;
} OVERLAPPED, *LPOVERLAPPED;
typedef struct _SECURITY_ATTRIBUTES {
DWORD nLength;
LPVOID lpSecurityDescriptor;
BOOL bInheritHandle;
} SECURITY_ATTRIBUTES , *LPSECURITY_ATTRIBUTES;
#include <pthread.h>
// Substitute for HANDLE returned by Windows CreateEvent API.
// FT_SetEventNotification expects parameter 3 to be the address
// of one of these structures.
typedef struct _EVENT_HANDLE
{
pthread_cond_t eCondVar;
pthread_mutex_t eMutex;
int iVar;
} EVENT_HANDLE;
typedef struct timeval SYSTEMTIME;
typedef struct timeval FILETIME;
// WaitForSingleObject return values.
#define WAIT_ABANDONED 0x00000080L
#define WAIT_OBJECT_0 0x00000000L
#define WAIT_TIMEOUT 0x00000102L
#define WAIT_FAILED 0xFFFFFFFF
// Special value for WaitForSingleObject dwMilliseconds parameter
#define INFINITE 0xFFFFFFFF // Infinite timeout
#ifndef TRUE
#define TRUE 1
#endif
#ifndef FALSE
#define FALSE 0
#endif
#ifndef CONST
#define CONST const
#endif
//
// Modem Status Flags
//
#define MS_CTS_ON ((DWORD)0x0010)
#define MS_DSR_ON ((DWORD)0x0020)
#define MS_RING_ON ((DWORD)0x0040)
#define MS_RLSD_ON ((DWORD)0x0080)
//
// Error Flags
//
#define CE_RXOVER 0x0001 // Receive Queue overflow
#define CE_OVERRUN 0x0002 // Receive Overrun Error
#define CE_RXPARITY 0x0004 // Receive Parity Error
#define CE_FRAME 0x0008 // Receive Framing error
#define CE_BREAK 0x0010 // Break Detected
#define CE_TXFULL 0x0100 // TX Queue is full
#define CE_PTO 0x0200 // LPTx Timeout
#define CE_IOE 0x0400 // LPTx I/O Error
#define CE_DNS 0x0800 // LPTx Device not selected
#define CE_OOP 0x1000 // LPTx Out-Of-Paper
#define CE_MODE 0x8000 // Requested mode unsupported
//
// Events
//
#define EV_RXCHAR 0x0001 // Any Character received
#define EV_RXFLAG 0x0002 // Received certain character
#define EV_TXEMPTY 0x0004 // Transmit Queue Empty
#define EV_CTS 0x0008 // CTS changed state
#define EV_DSR 0x0010 // DSR changed state
#define EV_RLSD 0x0020 // RLSD changed state
#define EV_BREAK 0x0040 // BREAK received
#define EV_ERR 0x0080 // Line status error occurred
#define EV_RING 0x0100 // Ring signal detected
#define EV_PERR 0x0200 // Printer error occured
#define EV_RX80FULL 0x0400 // Receive buffer is 80 percent full
#define EV_EVENT1 0x0800 // Provider specific event 1
#define EV_EVENT2 0x1000 // Provider specific event 2
//
// Escape Functions
//
#define SETXOFF 1 // Simulate XOFF received
#define SETXON 2 // Simulate XON received
#define SETRTS 3 // Set RTS high
#define CLRRTS 4 // Set RTS low
#define SETDTR 5 // Set DTR high
#define CLRDTR 6 // Set DTR low
#define RESETDEV 7 // Reset device if possible
#define SETBREAK 8 // Set the device break line.
#define CLRBREAK 9 // Clear the device break line.
//
// PURGE function flags.
//
#define PURGE_TXABORT 0x0001 // Kill the pending/current writes to the comm port.
#define PURGE_RXABORT 0x0002 // Kill the pending/current reads to the comm port.
#define PURGE_TXCLEAR 0x0004 // Kill the transmit queue if there.
#define PURGE_RXCLEAR 0x0008 // Kill the typeahead buffer if there.
#ifndef INVALID_HANDLE_VALUE
#define INVALID_HANDLE_VALUE 0xFFFFFFFF
#endif
#endif /* __WINDOWS_TYPES__ */

View File

@ -0,0 +1,272 @@
/*
* config_type.cpp
*
* Created on: Feb 11, 2019
* Author: justin
*/
#include "config_type.h"
#ifdef _WIN32
#include <windows.h>
#else
#include "WinTypes.h"
#endif
#include "ftd2xx.h"
void ft_to_config(CONFIG_DATA* dest, PFT_PROGRAM_DATA src) {
dest->Signature1 = src->Signature1;
dest->Signature2 = src->Signature2;
dest->Version = src->Version;
dest->VendorId = src->VendorId;
dest->ProductId = src->ProductId;
dest->MaxPower = src->MaxPower;
dest->PnP = src->PnP;
dest->SelfPowered = src->SelfPowered;
dest->RemoteWakeup = src->RemoteWakeup;
dest->Rev4 = src->Rev4;
dest->IsoIn = src->IsoIn;
dest->IsoOut = src->IsoOut;
dest->PullDownEnable = src->PullDownEnable;
dest->SerNumEnable = src->SerNumEnable;
dest->USBVersionEnable = src->USBVersionEnable;
dest->USBVersion = src->USBVersion;
dest->Rev5 = src->Rev5;
dest->IsoInA = src->IsoInA;
dest->IsoInB = src->IsoInB;
dest->IsoOutA = src->IsoOutA;
dest->IsoOutB = src->IsoOutB;
dest->PullDownEnable5 = src->PullDownEnable5;
dest->SerNumEnable5 = src->SerNumEnable5;
dest->USBVersionEnable5 = src->USBVersionEnable5;
dest->USBVersion5 = src->USBVersion5;
dest->AIsHighCurrent = src->AIsHighCurrent;
dest->BIsHighCurrent = src->BIsHighCurrent;
dest->IFAIsFifo = src->IFAIsFifo;
dest->IFAIsFifoTar = src->IFAIsFifoTar;
dest->IFAIsFastSer = src->IFAIsFastSer;
dest->AIsVCP = src->AIsVCP;
dest->IFBIsFifo = src->IFBIsFifo;
dest->IFBIsFifoTar = src->IFBIsFifoTar;
dest->IFBIsFastSer = src->IFBIsFastSer;
dest->BIsVCP = src->BIsVCP;
dest->UseExtOsc = src->UseExtOsc;
dest->HighDriveIOs = src->HighDriveIOs;
dest->EndpointSize = src->EndpointSize;
dest->PullDownEnableR = src->PullDownEnableR;
dest->SerNumEnableR = src->SerNumEnableR;
dest->InvertTXD = src->InvertTXD;
dest->InvertRXD = src->InvertRXD;
dest->InvertRTS = src->InvertRTS;
dest->InvertCTS = src->InvertCTS;
dest->InvertDTR = src->InvertDTR;
dest->InvertDSR = src->InvertDSR;
dest->InvertDCD = src->InvertDCD;
dest->InvertRI = src->InvertRI;
dest->Cbus0 = src->Cbus0;
dest->Cbus1 = src->Cbus1;
dest->Cbus2 = src->Cbus2;
dest->Cbus3 = src->Cbus3;
dest->Cbus4 = src->Cbus4;
dest->RIsD2XX = src->RIsD2XX;
dest->PullDownEnable7 = src->PullDownEnable7;
dest->SerNumEnable7 = src->SerNumEnable7;
dest->ALSlowSlew = src->ALSlowSlew;
dest->ALSchmittInput = src->ALSchmittInput;
dest->ALDriveCurrent = src->ALDriveCurrent;
dest->AHSlowSlew = src->AHSlowSlew;
dest->AHSchmittInput = src->AHSchmittInput;
dest->AHDriveCurrent = src->AHDriveCurrent;
dest->BLSlowSlew = src->BLSlowSlew;
dest->BLSchmittInput = src->BLSchmittInput;
dest->BLDriveCurrent = src->BLDriveCurrent;
dest->BHSlowSlew = src->BHSlowSlew;
dest->BHSchmittInput = src->BHSchmittInput;
dest->BHDriveCurrent = src->BHDriveCurrent;
dest->IFAIsFifo7 = src->IFAIsFifo7;
dest->IFAIsFifoTar7 = src->IFAIsFifoTar7;
dest->IFAIsFastSer7 = src->IFAIsFastSer7;
dest->AIsVCP7 = src->AIsVCP7;
dest->IFBIsFifo7 = src->IFBIsFifo7;
dest->IFBIsFifoTar7 = src->IFBIsFifoTar7;
dest->IFBIsFastSer7 = src->IFBIsFastSer7;
dest->BIsVCP7 = src->BIsVCP7;
dest->PowerSaveEnable = src->PowerSaveEnable;
dest->PullDownEnable8 = src->PullDownEnable8;
dest->SerNumEnable8 = src->SerNumEnable8;
dest->ASlowSlew = src->ASlowSlew;
dest->ASchmittInput = src->ASchmittInput;
dest->ADriveCurrent = src->ADriveCurrent;
dest->BSlowSlew = src->BSlowSlew;
dest->BSchmittInput = src->BSchmittInput;
dest->BDriveCurrent = src->BDriveCurrent;
dest->CSlowSlew = src->CSlowSlew;
dest->CSchmittInput = src->CSchmittInput;
dest->CDriveCurrent = src->CDriveCurrent;
dest->DSlowSlew = src->DSlowSlew;
dest->DSchmittInput = src->DSchmittInput;
dest->DDriveCurrent = src->DDriveCurrent;
dest->ARIIsTXDEN = src->ARIIsTXDEN;
dest->BRIIsTXDEN = src->BRIIsTXDEN;
dest->CRIIsTXDEN = src->CRIIsTXDEN;
dest->DRIIsTXDEN = src->DRIIsTXDEN;
dest->AIsVCP8 = src->AIsVCP8;
dest->BIsVCP8 = src->BIsVCP8;
dest->CIsVCP8 = src->CIsVCP8;
dest->DIsVCP8 = src->DIsVCP8;
dest->PullDownEnableH = src->PullDownEnableH;
dest->SerNumEnableH = src->SerNumEnableH;
dest->ACSlowSlewH = src->ACSlowSlewH;
dest->ACSchmittInputH = src->ACSchmittInputH;
dest->ACDriveCurrentH = src->ACDriveCurrentH;
dest->ADSlowSlewH = src->ADSlowSlewH;
dest->ADSchmittInputH = src->ADSchmittInputH;
dest->ADDriveCurrentH = src->ADDriveCurrentH;
dest->Cbus0H = src->Cbus0H;
dest->Cbus1H = src->Cbus1H;
dest->Cbus2H = src->Cbus2H;
dest->Cbus3H = src->Cbus3H;
dest->Cbus4H = src->Cbus4H;
dest->Cbus5H = src->Cbus5H;
dest->Cbus6H = src->Cbus6H;
dest->Cbus7H = src->Cbus7H;
dest->Cbus8H = src->Cbus8H;
dest->Cbus9H = src->Cbus9H;
dest->IsFifoH = src->IsFifoH;
dest->IsFifoTarH = src->IsFifoTarH;
dest->IsFastSerH = src->IsFastSerH;
dest->IsFT1248H = src->IsFT1248H;
dest->FT1248CpolH = src->FT1248CpolH;
dest->FT1248LsbH = src->FT1248LsbH;
dest->FT1248FlowControlH = src->FT1248FlowControlH;
dest->IsVCPH = src->IsVCPH;
dest->PowerSaveEnableH = src->PowerSaveEnableH;
}
void config_to_ft(PFT_PROGRAM_DATA dest, CONFIG_DATA* src) {
dest->Signature1 = src->Signature1;
dest->Signature2 = src->Signature2;
dest->Version = src->Version;
dest->VendorId = src->VendorId;
dest->ProductId = src->ProductId;
dest->MaxPower = src->MaxPower;
dest->PnP = src->PnP;
dest->SelfPowered = src->SelfPowered;
dest->RemoteWakeup = src->RemoteWakeup;
dest->Rev4 = src->Rev4;
dest->IsoIn = src->IsoIn;
dest->IsoOut = src->IsoOut;
dest->PullDownEnable = src->PullDownEnable;
dest->SerNumEnable = src->SerNumEnable;
dest->USBVersionEnable = src->USBVersionEnable;
dest->USBVersion = src->USBVersion;
dest->Rev5 = src->Rev5;
dest->IsoInA = src->IsoInA;
dest->IsoInB = src->IsoInB;
dest->IsoOutA = src->IsoOutA;
dest->IsoOutB = src->IsoOutB;
dest->PullDownEnable5 = src->PullDownEnable5;
dest->SerNumEnable5 = src->SerNumEnable5;
dest->USBVersionEnable5 = src->USBVersionEnable5;
dest->USBVersion5 = src->USBVersion5;
dest->AIsHighCurrent = src->AIsHighCurrent;
dest->BIsHighCurrent = src->BIsHighCurrent;
dest->IFAIsFifo = src->IFAIsFifo;
dest->IFAIsFifoTar = src->IFAIsFifoTar;
dest->IFAIsFastSer = src->IFAIsFastSer;
dest->AIsVCP = src->AIsVCP;
dest->IFBIsFifo = src->IFBIsFifo;
dest->IFBIsFifoTar = src->IFBIsFifoTar;
dest->IFBIsFastSer = src->IFBIsFastSer;
dest->BIsVCP = src->BIsVCP;
dest->UseExtOsc = src->UseExtOsc;
dest->HighDriveIOs = src->HighDriveIOs;
dest->EndpointSize = src->EndpointSize;
dest->PullDownEnableR = src->PullDownEnableR;
dest->SerNumEnableR = src->SerNumEnableR;
dest->InvertTXD = src->InvertTXD;
dest->InvertRXD = src->InvertRXD;
dest->InvertRTS = src->InvertRTS;
dest->InvertCTS = src->InvertCTS;
dest->InvertDTR = src->InvertDTR;
dest->InvertDSR = src->InvertDSR;
dest->InvertDCD = src->InvertDCD;
dest->InvertRI = src->InvertRI;
dest->Cbus0 = src->Cbus0;
dest->Cbus1 = src->Cbus1;
dest->Cbus2 = src->Cbus2;
dest->Cbus3 = src->Cbus3;
dest->Cbus4 = src->Cbus4;
dest->RIsD2XX = src->RIsD2XX;
dest->PullDownEnable7 = src->PullDownEnable7;
dest->SerNumEnable7 = src->SerNumEnable7;
dest->ALSlowSlew = src->ALSlowSlew;
dest->ALSchmittInput = src->ALSchmittInput;
dest->ALDriveCurrent = src->ALDriveCurrent;
dest->AHSlowSlew = src->AHSlowSlew;
dest->AHSchmittInput = src->AHSchmittInput;
dest->AHDriveCurrent = src->AHDriveCurrent;
dest->BLSlowSlew = src->BLSlowSlew;
dest->BLSchmittInput = src->BLSchmittInput;
dest->BLDriveCurrent = src->BLDriveCurrent;
dest->BHSlowSlew = src->BHSlowSlew;
dest->BHSchmittInput = src->BHSchmittInput;
dest->BHDriveCurrent = src->BHDriveCurrent;
dest->IFAIsFifo7 = src->IFAIsFifo7;
dest->IFAIsFifoTar7 = src->IFAIsFifoTar7;
dest->IFAIsFastSer7 = src->IFAIsFastSer7;
dest->AIsVCP7 = src->AIsVCP7;
dest->IFBIsFifo7 = src->IFBIsFifo7;
dest->IFBIsFifoTar7 = src->IFBIsFifoTar7;
dest->IFBIsFastSer7 = src->IFBIsFastSer7;
dest->BIsVCP7 = src->BIsVCP7;
dest->PowerSaveEnable = src->PowerSaveEnable;
dest->PullDownEnable8 = src->PullDownEnable8;
dest->SerNumEnable8 = src->SerNumEnable8;
dest->ASlowSlew = src->ASlowSlew;
dest->ASchmittInput = src->ASchmittInput;
dest->ADriveCurrent = src->ADriveCurrent;
dest->BSlowSlew = src->BSlowSlew;
dest->BSchmittInput = src->BSchmittInput;
dest->BDriveCurrent = src->BDriveCurrent;
dest->CSlowSlew = src->CSlowSlew;
dest->CSchmittInput = src->CSchmittInput;
dest->CDriveCurrent = src->CDriveCurrent;
dest->DSlowSlew = src->DSlowSlew;
dest->DSchmittInput = src->DSchmittInput;
dest->DDriveCurrent = src->DDriveCurrent;
dest->ARIIsTXDEN = src->ARIIsTXDEN;
dest->BRIIsTXDEN = src->BRIIsTXDEN;
dest->CRIIsTXDEN = src->CRIIsTXDEN;
dest->DRIIsTXDEN = src->DRIIsTXDEN;
dest->AIsVCP8 = src->AIsVCP8;
dest->BIsVCP8 = src->BIsVCP8;
dest->CIsVCP8 = src->CIsVCP8;
dest->DIsVCP8 = src->DIsVCP8;
dest->PullDownEnableH = src->PullDownEnableH;
dest->SerNumEnableH = src->SerNumEnableH;
dest->ACSlowSlewH = src->ACSlowSlewH;
dest->ACSchmittInputH = src->ACSchmittInputH;
dest->ACDriveCurrentH = src->ACDriveCurrentH;
dest->ADSlowSlewH = src->ADSlowSlewH;
dest->ADSchmittInputH = src->ADSchmittInputH;
dest->ADDriveCurrentH = src->ADDriveCurrentH;
dest->Cbus0H = src->Cbus0H;
dest->Cbus1H = src->Cbus1H;
dest->Cbus2H = src->Cbus2H;
dest->Cbus3H = src->Cbus3H;
dest->Cbus4H = src->Cbus4H;
dest->Cbus5H = src->Cbus5H;
dest->Cbus6H = src->Cbus6H;
dest->Cbus7H = src->Cbus7H;
dest->Cbus8H = src->Cbus8H;
dest->Cbus9H = src->Cbus9H;
dest->IsFifoH = src->IsFifoH;
dest->IsFifoTarH = src->IsFifoTarH;
dest->IsFastSerH = src->IsFastSerH;
dest->IsFT1248H = src->IsFT1248H;
dest->FT1248CpolH = src->FT1248CpolH;
dest->FT1248LsbH = src->FT1248LsbH;
dest->FT1248FlowControlH = src->FT1248FlowControlH;
dest->IsVCPH = src->IsVCPH;
dest->PowerSaveEnableH = src->PowerSaveEnableH;
}

View File

@ -0,0 +1,177 @@
/*
* config_def.h
*
* Created on: Feb 11, 2019
* Author: justin
*/
#ifndef CONFIG_TYPE_H_
#define CONFIG_TYPE_H_
#ifdef _WIN32
#include <windows.h>
#else
#include "WinTypes.h"
#endif
#include "ftd2xx.h"
typedef struct config_data {
DWORD Signature1; // Header - must be 0x00000000
DWORD Signature2; // Header - must be 0xffffffff
DWORD Version; // Header - FT_PROGRAM_DATA version
// 0 = original
// 1 = FT2232 extensions
// 2 = FT232R extensions
// 3 = FT2232H extensions
// 4 = FT4232H extensions
// 5 = FT232H extensions
WORD VendorId; // 0x0403
WORD ProductId; // 0x6001
WORD MaxPower; // 0 < MaxPower <= 500
WORD PnP; // 0 = disabled, 1 = enabled
WORD SelfPowered; // 0 = bus powered, 1 = self powered
WORD RemoteWakeup; // 0 = not capable, 1 = capable
//
// Rev4 (FT232B) extensions
//
UCHAR Rev4; // non-zero if Rev4 chip, zero otherwise
UCHAR IsoIn; // non-zero if in endpoint is isochronous
UCHAR IsoOut; // non-zero if out endpoint is isochronous
UCHAR PullDownEnable; // non-zero if pull down enabled
UCHAR SerNumEnable; // non-zero if serial number to be used
UCHAR USBVersionEnable; // non-zero if chip uses USBVersion
WORD USBVersion; // BCD (0x0200 => USB2)
//
// Rev 5 (FT2232) extensions
//
UCHAR Rev5; // non-zero if Rev5 chip, zero otherwise
UCHAR IsoInA; // non-zero if in endpoint is isochronous
UCHAR IsoInB; // non-zero if in endpoint is isochronous
UCHAR IsoOutA; // non-zero if out endpoint is isochronous
UCHAR IsoOutB; // non-zero if out endpoint is isochronous
UCHAR PullDownEnable5; // non-zero if pull down enabled
UCHAR SerNumEnable5; // non-zero if serial number to be used
UCHAR USBVersionEnable5; // non-zero if chip uses USBVersion
WORD USBVersion5; // BCD (0x0200 => USB2)
UCHAR AIsHighCurrent; // non-zero if interface is high current
UCHAR BIsHighCurrent; // non-zero if interface is high current
UCHAR IFAIsFifo; // non-zero if interface is 245 FIFO
UCHAR IFAIsFifoTar; // non-zero if interface is 245 FIFO CPU target
UCHAR IFAIsFastSer; // non-zero if interface is Fast serial
UCHAR AIsVCP; // non-zero if interface is to use VCP drivers
UCHAR IFBIsFifo; // non-zero if interface is 245 FIFO
UCHAR IFBIsFifoTar; // non-zero if interface is 245 FIFO CPU target
UCHAR IFBIsFastSer; // non-zero if interface is Fast serial
UCHAR BIsVCP; // non-zero if interface is to use VCP drivers
//
// Rev 6 (FT232R) extensions
//
UCHAR UseExtOsc; // Use External Oscillator
UCHAR HighDriveIOs; // High Drive I/Os
UCHAR EndpointSize; // Endpoint size
UCHAR PullDownEnableR; // non-zero if pull down enabled
UCHAR SerNumEnableR; // non-zero if serial number to be used
UCHAR InvertTXD; // non-zero if invert TXD
UCHAR InvertRXD; // non-zero if invert RXD
UCHAR InvertRTS; // non-zero if invert RTS
UCHAR InvertCTS; // non-zero if invert CTS
UCHAR InvertDTR; // non-zero if invert DTR
UCHAR InvertDSR; // non-zero if invert DSR
UCHAR InvertDCD; // non-zero if invert DCD
UCHAR InvertRI; // non-zero if invert RI
UCHAR Cbus0; // Cbus Mux control
UCHAR Cbus1; // Cbus Mux control
UCHAR Cbus2; // Cbus Mux control
UCHAR Cbus3; // Cbus Mux control
UCHAR Cbus4; // Cbus Mux control
UCHAR RIsD2XX; // non-zero if using D2XX driver
//
// Rev 7 (FT2232H) Extensions
//
UCHAR PullDownEnable7; // non-zero if pull down enabled
UCHAR SerNumEnable7; // non-zero if serial number to be used
UCHAR ALSlowSlew; // non-zero if AL pins have slow slew
UCHAR ALSchmittInput; // non-zero if AL pins are Schmitt input
UCHAR ALDriveCurrent; // valid values are 4mA, 8mA, 12mA, 16mA
UCHAR AHSlowSlew; // non-zero if AH pins have slow slew
UCHAR AHSchmittInput; // non-zero if AH pins are Schmitt input
UCHAR AHDriveCurrent; // valid values are 4mA, 8mA, 12mA, 16mA
UCHAR BLSlowSlew; // non-zero if BL pins have slow slew
UCHAR BLSchmittInput; // non-zero if BL pins are Schmitt input
UCHAR BLDriveCurrent; // valid values are 4mA, 8mA, 12mA, 16mA
UCHAR BHSlowSlew; // non-zero if BH pins have slow slew
UCHAR BHSchmittInput; // non-zero if BH pins are Schmitt input
UCHAR BHDriveCurrent; // valid values are 4mA, 8mA, 12mA, 16mA
UCHAR IFAIsFifo7; // non-zero if interface is 245 FIFO
UCHAR IFAIsFifoTar7; // non-zero if interface is 245 FIFO CPU target
UCHAR IFAIsFastSer7; // non-zero if interface is Fast serial
UCHAR AIsVCP7; // non-zero if interface is to use VCP drivers
UCHAR IFBIsFifo7; // non-zero if interface is 245 FIFO
UCHAR IFBIsFifoTar7; // non-zero if interface is 245 FIFO CPU target
UCHAR IFBIsFastSer7; // non-zero if interface is Fast serial
UCHAR BIsVCP7; // non-zero if interface is to use VCP drivers
UCHAR PowerSaveEnable; // non-zero if using BCBUS7 to save power for self-powered designs
//
// Rev 8 (FT4232H) Extensions
//
UCHAR PullDownEnable8; // non-zero if pull down enabled
UCHAR SerNumEnable8; // non-zero if serial number to be used
UCHAR ASlowSlew; // non-zero if A pins have slow slew
UCHAR ASchmittInput; // non-zero if A pins are Schmitt input
UCHAR ADriveCurrent; // valid values are 4mA, 8mA, 12mA, 16mA
UCHAR BSlowSlew; // non-zero if B pins have slow slew
UCHAR BSchmittInput; // non-zero if B pins are Schmitt input
UCHAR BDriveCurrent; // valid values are 4mA, 8mA, 12mA, 16mA
UCHAR CSlowSlew; // non-zero if C pins have slow slew
UCHAR CSchmittInput; // non-zero if C pins are Schmitt input
UCHAR CDriveCurrent; // valid values are 4mA, 8mA, 12mA, 16mA
UCHAR DSlowSlew; // non-zero if D pins have slow slew
UCHAR DSchmittInput; // non-zero if D pins are Schmitt input
UCHAR DDriveCurrent; // valid values are 4mA, 8mA, 12mA, 16mA
UCHAR ARIIsTXDEN; // non-zero if port A uses RI as RS485 TXDEN
UCHAR BRIIsTXDEN; // non-zero if port B uses RI as RS485 TXDEN
UCHAR CRIIsTXDEN; // non-zero if port C uses RI as RS485 TXDEN
UCHAR DRIIsTXDEN; // non-zero if port D uses RI as RS485 TXDEN
UCHAR AIsVCP8; // non-zero if interface is to use VCP drivers
UCHAR BIsVCP8; // non-zero if interface is to use VCP drivers
UCHAR CIsVCP8; // non-zero if interface is to use VCP drivers
UCHAR DIsVCP8; // non-zero if interface is to use VCP drivers
//
// Rev 9 (FT232H) Extensions
//
UCHAR PullDownEnableH; // non-zero if pull down enabled
UCHAR SerNumEnableH; // non-zero if serial number to be used
UCHAR ACSlowSlewH; // non-zero if AC pins have slow slew
UCHAR ACSchmittInputH; // non-zero if AC pins are Schmitt input
UCHAR ACDriveCurrentH; // valid values are 4mA, 8mA, 12mA, 16mA
UCHAR ADSlowSlewH; // non-zero if AD pins have slow slew
UCHAR ADSchmittInputH; // non-zero if AD pins are Schmitt input
UCHAR ADDriveCurrentH; // valid values are 4mA, 8mA, 12mA, 16mA
UCHAR Cbus0H; // Cbus Mux control
UCHAR Cbus1H; // Cbus Mux control
UCHAR Cbus2H; // Cbus Mux control
UCHAR Cbus3H; // Cbus Mux control
UCHAR Cbus4H; // Cbus Mux control
UCHAR Cbus5H; // Cbus Mux control
UCHAR Cbus6H; // Cbus Mux control
UCHAR Cbus7H; // Cbus Mux control
UCHAR Cbus8H; // Cbus Mux control
UCHAR Cbus9H; // Cbus Mux control
UCHAR IsFifoH; // non-zero if interface is 245 FIFO
UCHAR IsFifoTarH; // non-zero if interface is 245 FIFO CPU target
UCHAR IsFastSerH; // non-zero if interface is Fast serial
UCHAR IsFT1248H; // non-zero if interface is FT1248
UCHAR FT1248CpolH; // FT1248 clock polarity - clock idle high (1) or clock idle low (0)
UCHAR FT1248LsbH; // FT1248 data is LSB (1) or MSB (0)
UCHAR FT1248FlowControlH; // FT1248 flow control enable
UCHAR IsVCPH; // non-zero if interface is to use VCP drivers
UCHAR PowerSaveEnableH; // non-zero if using ACBUS7 to save power for self-powered designs
} CONFIG_DATA;
void ft_to_config(CONFIG_DATA*, PFT_PROGRAM_DATA);
void config_to_ft(PFT_PROGRAM_DATA, CONFIG_DATA*);
#endif /* CONFIG_TYPE_H_ */

1443
alchitry-loader/src/ftd2xx.h Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,781 @@
/*
* jtag.cpp
*
* Created on: May 24, 2017
* Author: justin
*/
#include "jtag.h"
#include <unistd.h>
#include <iostream>
#include <iomanip>
#include <string>
#include <sstream>
#ifdef _WIN32
#include "mingw.thread.h"
#else
#include <thread>
#endif
#include <chrono>
using namespace std;
Jtag::Jtag() {
ftHandle = 0;
active = false;
}
FT_STATUS Jtag::connect(unsigned int devNumber) {
return FT_Open(devNumber, &ftHandle);
}
FT_STATUS Jtag::disconnect() {
active = false;
return FT_Close(ftHandle);
}
bool Jtag::initialize() {
BYTE byInputBuffer[1024]; // Buffer to hold data read from the FT2232H
DWORD dwNumBytesToRead = 0; // Number of bytes available to read in the driver's input buffer
DWORD dwNumBytesRead = 0; // Count of actual bytes read - used with FT_Read
FT_STATUS ftStatus;
ftStatus = FT_ResetDevice(ftHandle);
//Reset USB device
//Purge USB receive buffer first by reading out all old data from FT2232H receive buffer
ftStatus |= FT_GetQueueStatus(ftHandle, &dwNumBytesToRead); // Get the number of bytes in the FT2232H receive buffer
if ((ftStatus == FT_OK) && (dwNumBytesToRead > 0))
FT_Read(ftHandle, &byInputBuffer, dwNumBytesToRead, &dwNumBytesRead);//Read out the data from FT2232H receive buffer
ftStatus |= FT_SetUSBParameters(ftHandle, 65536, 65535);//Set USB request transfer sizes to 64K
ftStatus |= FT_SetChars(ftHandle, false, 0, false, 0); //Disable event and error characters
ftStatus |= FT_SetTimeouts(ftHandle, 0, 5000); //Sets the read and write timeouts in milliseconds
ftStatus |= FT_SetLatencyTimer(ftHandle, 16); //Set the latency timer (default is 16mS)
ftStatus |= FT_SetBitMode(ftHandle, 0x0, 0x00); //Reset controller
ftStatus |= FT_SetBitMode(ftHandle, 0x0, 0x02); //Enable MPSSE mode
if (ftStatus != FT_OK) {
cerr << "Failed to set initial configuration!" << endl;
return false;
}
std::this_thread::sleep_for(std::chrono::milliseconds(100)); // Wait for all the USB stuff to complete and work
if (!sync_mpsse()) {
cerr << "Failed to sync with MPSSE!" << endl;
return false;
}
if (!config_jtag()) {
cerr << "Failed to set JTAG configuration!" << endl;
return false;
}
active = true;
return true;
}
bool Jtag::sync_mpsse() {
BYTE byOutputBuffer[8]; // Buffer to hold MPSSE commands and data to be sent to the FT2232H
BYTE byInputBuffer[8]; // Buffer to hold data read from the FT2232H
DWORD dwCount = 0; // General loop index
DWORD dwNumBytesToSend = 0; // Index to the output buffer
DWORD dwNumBytesSent = 0; // Count of actual bytes sent - used with FT_Write
DWORD dwNumBytesToRead = 0; // Number of bytes available to read in the driver's input buffer
DWORD dwNumBytesRead = 0; // Count of actual bytes read - used with FT_Read
FT_STATUS ftStatus;
byOutputBuffer[dwNumBytesToSend++] = 0xAA; //'\xAA';
//Add bogus command xAA to the queue
ftStatus = FT_Write(ftHandle, byOutputBuffer, dwNumBytesToSend,
&dwNumBytesSent);
if (ftStatus != FT_OK)
cerr << "Failed to send bad command" << endl;
// Send off the BAD commands
dwNumBytesToSend = 0; // Reset output buffer pointer
do {
ftStatus = FT_GetQueueStatus(ftHandle, &dwNumBytesToRead);
if (ftStatus != FT_OK)
cerr << "Failed to get queue status " << ftStatus << endl;
// Get the number of bytes in the device input buffer
} while ((dwNumBytesToRead == 0) && (ftStatus == FT_OK));
if (dwNumBytesRead > 8) {
cerr << "Input buffer too small in sync_mpsse()!" << endl;
return false;
}
//or Timeout
bool bCommandEchod = false;
ftStatus = FT_Read(ftHandle, &byInputBuffer, dwNumBytesToRead,
&dwNumBytesRead);
if (ftStatus != FT_OK)
cerr << "Failed to read data" << endl;
//Read out the data from input buffer
for (dwCount = 0; dwCount < dwNumBytesRead - 1; dwCount++)
//Check if Bad command and echo command received
{
if ((byInputBuffer[dwCount] == 0xFA)
&& (byInputBuffer[dwCount + 1] == 0xAA)) {
bCommandEchod = true;
break;
}
}
return bCommandEchod;
}
bool Jtag::config_jtag() {
FT_STATUS ftStatus;
BYTE byOutputBuffer[64]; // Buffer to hold MPSSE commands and data to be sent to the FT2232H
DWORD dwNumBytesToSend = 0; // Index to the output buffer
DWORD dwNumBytesSent = 0; // Count of actual bytes sent - used with FT_Write
DWORD dwClockDivisor = 0x05DB; // Value of clock divisor, SCL Frequency = 60/((1+0x05DB)*2) (MHz) = 20khz
// -----------------------------------------------------------
// Configure the MPSSE settings for JTAG
// Multiple commands can be sent to the MPSSE with one FT_Write
// -----------------------------------------------------------
dwNumBytesToSend = 0; // Start with a fresh index
// Set up the Hi-Speed specific commands for the FTx232H
byOutputBuffer[dwNumBytesToSend++] = 0x8A;
// Use 60MHz master clock (disable divide by 5)
byOutputBuffer[dwNumBytesToSend++] = 0x97;
// Turn off adaptive clocking (may be needed for ARM)
byOutputBuffer[dwNumBytesToSend++] = 0x8D;
// Disable three-phase clocking
ftStatus = FT_Write(ftHandle, byOutputBuffer, dwNumBytesToSend,
&dwNumBytesSent);
// Send off the HS-specific commands
if (ftStatus != FT_OK)
return false;
dwNumBytesToSend = 0; // Reset output buffer pointer
// Set initial states of the MPSSE interface - low byte, both pin directions and output values
// Pin name Signal Direction Config Initial State Config
// ADBUS0 TCK output 1 low 0
// ADBUS1 TDI output 1 low 0
// ADBUS2 TDO input 0 0
// ADBUS3 TMS output 1 high 1
// ADBUS4 GPIOL0 input 0 0
// ADBUS5 GPIOL1 input 0 0
// ADBUS6 GPIOL2 input 0 0
// ADBUS7 GPIOL3 input 0 0
byOutputBuffer[dwNumBytesToSend++] = 0x80;
// Set data bits low-byte of MPSSE port
byOutputBuffer[dwNumBytesToSend++] = 0x08;
// Initial state config above
byOutputBuffer[dwNumBytesToSend++] = 0x0B;
// Direction config above
ftStatus = FT_Write(ftHandle, byOutputBuffer, dwNumBytesToSend,
&dwNumBytesSent);
// Send off the low GPIO config commands
if (ftStatus != FT_OK)
return false;
dwNumBytesToSend = 0; // Reset output buffer pointer
// Set initial states of the MPSSE interface - high byte, both pin directions and output values
// Pin name Signal Direction Config Initial State Config
// ACBUS0 GPIOH0 input 0 0
// ACBUS1 GPIOH1 input 0 0
// ACBUS2 GPIOH2 input 0 0
// ACBUS3 GPIOH3 input 0 0
// ACBUS4 GPIOH4 input 0 0
// ACBUS5 GPIOH5 input 0 0
// ACBUS6 GPIOH6 input 0 0
// ACBUS7 GPIOH7 input 0 0
byOutputBuffer[dwNumBytesToSend++] = 0x82;
// Set data bits low-byte of MPSSE port
byOutputBuffer[dwNumBytesToSend++] = 0x00;
// Initial state config above
byOutputBuffer[dwNumBytesToSend++] = 0x00;
// Direction config above
ftStatus = FT_Write(ftHandle, byOutputBuffer, dwNumBytesToSend,
&dwNumBytesSent);
// Send off the high GPIO config commands
if (ftStatus != FT_OK)
return false;
dwNumBytesToSend = 0; // Reset output buffer pointer
// Set TCK frequency
// TCK = 60MHz /((1 + [(1 +0xValueH*256) OR 0xValueL])*2)
byOutputBuffer[dwNumBytesToSend++] = 0x86;
//Command to set clock divisor
byOutputBuffer[dwNumBytesToSend++] = dwClockDivisor & 0xFF;
//Set 0xValueL of clock divisor
byOutputBuffer[dwNumBytesToSend++] = (dwClockDivisor >> 8) & 0xFF;
//Set 0xValueH of clock divisor
ftStatus = FT_Write(ftHandle, byOutputBuffer, dwNumBytesToSend,
&dwNumBytesSent);
// Send off the clock divisor commands
if (ftStatus != FT_OK)
return false;
dwNumBytesToSend = 0; // Reset output buffer pointer
// Disable internal loop-back
byOutputBuffer[dwNumBytesToSend++] = 0x85;
// Disable loopback
ftStatus = FT_Write(ftHandle, byOutputBuffer, dwNumBytesToSend,
&dwNumBytesSent);
// Send off the loopback command
if (ftStatus != FT_OK)
return false;
return true;
}
bool Jtag::setFreq(double freq) {
if (!active) {
cerr
<< "Jtag must be connected and initialized before calling setFreq()!"
<< endl;
return false;
}
FT_STATUS ftStatus;
BYTE byOutputBuffer[8]; // Buffer to hold MPSSE commands and data to be sent to the FT2232H
DWORD dwNumBytesToSend = 0; // Index to the output buffer
DWORD dwNumBytesSent = 0; // Count of actual bytes sent - used with FT_Write
DWORD dwClockDivisor; // Value of clock divisor, SCL Frequency = 60/((1+clkDiv)*2) (MHz)
dwClockDivisor = 30.0 / (freq / 1000000.0) - 1.0;
// Set TCK frequency
// TCK = 60MHz /((1 + [(1 +0xValueH*256) OR 0xValueL])*2)
byOutputBuffer[dwNumBytesToSend++] = 0x86;
//Command to set clock divisor
byOutputBuffer[dwNumBytesToSend++] = dwClockDivisor & 0xFF;
//Set 0xValueL of clock divisor
byOutputBuffer[dwNumBytesToSend++] = (dwClockDivisor >> 8) & 0xFF;
//Set 0xValueH of clock divisor
ftStatus = FT_Write(ftHandle, byOutputBuffer, dwNumBytesToSend,
&dwNumBytesSent);
// Send off the clock divisor commands
if (ftStatus != FT_OK)
return false;
return true;
}
bool Jtag::navigateToState(Jtag_fsm::State init, Jtag_fsm::State dest) {
FT_STATUS ftStatus;
BYTE byOutputBuffer[3]; // Buffer to hold MPSSE commands and data to be sent to the FT2232H
DWORD dwNumBytesSent = 0; // Count of actual bytes sent - used with FT_Write
Jtag_fsm::Transistions transistions = Jtag_fsm::getTransitions(init, dest);
if (transistions.moves > 0) {
if (transistions.moves < 8) {
byOutputBuffer[0] = 0x4B;
byOutputBuffer[1] = transistions.moves - 1;
byOutputBuffer[2] = 0x7f & transistions.tms;
ftStatus = FT_Write(ftHandle, byOutputBuffer, 3, &dwNumBytesSent);
if (ftStatus != FT_OK)
return false;
} else {
cout << "Transition of 8 moves!" << endl;
byOutputBuffer[0] = 0x4B;
byOutputBuffer[1] = 6;
byOutputBuffer[2] = 0x7f & transistions.tms;
ftStatus = FT_Write(ftHandle, byOutputBuffer, 3, &dwNumBytesSent);
if (ftStatus != FT_OK)
return false;
byOutputBuffer[0] = 0x4B;
byOutputBuffer[1] = transistions.moves - 8;
byOutputBuffer[2] = 0x7f & (transistions.tms >> 7);
ftStatus = FT_Write(ftHandle, byOutputBuffer, 3, &dwNumBytesSent);
if (ftStatus != FT_OK)
return false;
}
}
return true;
}
bool Jtag::shiftData(unsigned int bitCount, string tdi, string tdo,
string mask) {
FT_STATUS ftStatus;
unsigned int reqBytes = bitCount / 8 + (bitCount % 8 > 0);
BYTE *byOutputBuffer = new BYTE[reqBytes + 3];
BYTE *tdoBuffer = new BYTE[reqBytes + 3];
BYTE *byInputBuffer = new BYTE[reqBytes + 6];
DWORD dwNumBytesSent = 0;
DWORD dwNumBytesToRead = 0;
DWORD dwNumBytesRead = 0;
DWORD tdoBytes = 0;
unsigned int reqHex = bitCount / 4 + (bitCount % 4 > 0);
if (tdi.length() < reqHex)
return false;
bool read = tdo != "";
if (read) {
if (tdo.length() < reqHex) {
delete byOutputBuffer;
delete tdoBuffer;
delete byInputBuffer;
return false;
}
if (mask != "" && mask.length() < reqHex) {
delete byOutputBuffer;
delete tdoBuffer;
delete byInputBuffer;
return false;
}
}
if (!flush())
return false;
if (bitCount < 9) {
int data = stoi(tdi, 0, 16);
byOutputBuffer[0] = read ? 0x3B : 0x1B;
byOutputBuffer[1] = bitCount - 2;
byOutputBuffer[2] = data & 0xff;
ftStatus = FT_Write(ftHandle, byOutputBuffer, 3, &dwNumBytesSent);
if (ftStatus != FT_OK) {
delete byOutputBuffer;
delete tdoBuffer;
delete byInputBuffer;
return false;
}
BYTE lastBit = (data >> ((bitCount - 1) % 8)) & 0x01;
byOutputBuffer[0] = read ? 0x6E : 0x4E;
byOutputBuffer[1] = 0x00;
byOutputBuffer[2] = 0x03 | (lastBit << 7);
ftStatus = FT_Write(ftHandle, byOutputBuffer, 3, &dwNumBytesSent);
if (ftStatus != FT_OK) {
delete byOutputBuffer;
delete tdoBuffer;
delete byInputBuffer;
return false;
}
if (read) {
do {
ftStatus = FT_GetQueueStatus(ftHandle, &dwNumBytesToRead);
// Get the number of bytes in the device input buffer
} while ((dwNumBytesToRead == 0) && (ftStatus == FT_OK));
//or Timeout
ftStatus = FT_Read(ftHandle, byInputBuffer, dwNumBytesToRead,
&dwNumBytesRead);
if (ftStatus != FT_OK) {
delete byOutputBuffer;
delete tdoBuffer;
delete byInputBuffer;
return false;
}
tdoBuffer[0] = byInputBuffer[0] >> (8 - (bitCount - 1));
tdoBuffer[0] |= byInputBuffer[1] >> (7 - (bitCount - 1));
tdoBytes = 1;
}
} else {
BYTE *tdiBytes = new BYTE[reqBytes + 1];
hexToByte(tdi, tdiBytes);
unsigned int fullBytes = (bitCount - 1) / 8;
unsigned int remBytes = fullBytes;
unsigned int offset = 0;
if (fullBytes > 65536 && read) {
cout << "Large transfers with reads may not work!" << endl;
}
while (remBytes > 0) {
unsigned int bct = remBytes > 65536 ? 65536 : remBytes;
byOutputBuffer[0] = read ? 0x39 : 0x19;
byOutputBuffer[1] = (bct - 1) & 0xff;
byOutputBuffer[2] = ((bct - 1) >> 8) & 0xff;
for (unsigned int i = 0; i < bct; i++) {
byOutputBuffer[3 + i] = tdiBytes[i + offset];
}
ftStatus = FT_Write(ftHandle, byOutputBuffer, 3 + bct,
&dwNumBytesSent);
if (ftStatus != FT_OK) {
delete byOutputBuffer;
delete tdoBuffer;
delete byInputBuffer;
delete tdiBytes;
return false;
}
remBytes -= bct;
offset += bct;
}
unsigned int partialBits = bitCount - 1 - (fullBytes * 8);
if (fullBytes * 8 + 1 != bitCount) {
byOutputBuffer[0] = read ? 0x3B : 0x1B;
byOutputBuffer[1] = partialBits - 1;
byOutputBuffer[2] = tdiBytes[reqBytes - 1] & 0xff;
ftStatus = FT_Write(ftHandle, byOutputBuffer, 3, &dwNumBytesSent);
if (ftStatus != FT_OK) {
delete byOutputBuffer;
delete tdoBuffer;
delete byInputBuffer;
delete tdiBytes;
return false;
}
}
BYTE lastBit = (tdiBytes[reqBytes - 1] >> ((bitCount - 1) % 8)) & 0x01;
byOutputBuffer[0] = read ? 0x6E : 0x4E;
byOutputBuffer[1] = 0x00;
byOutputBuffer[2] = 0x03 | (lastBit << 7);
ftStatus = FT_Write(ftHandle, byOutputBuffer, 3, &dwNumBytesSent);
if (ftStatus != FT_OK) {
delete byOutputBuffer;
delete tdoBuffer;
delete byInputBuffer;
delete tdiBytes;
return false;
}
if (read) {
DWORD bytesToRead = fullBytes
+ ((fullBytes * 8 + 1 != bitCount) ? 2 : 1);
do {
ftStatus = FT_GetQueueStatus(ftHandle, &dwNumBytesToRead);
// Get the number of bytes in the device input buffer
} while ((dwNumBytesToRead != bytesToRead) && (ftStatus == FT_OK));
//or Timeout
ftStatus = FT_Read(ftHandle, byInputBuffer, dwNumBytesToRead,
&dwNumBytesRead);
if (ftStatus != FT_OK) {
delete byOutputBuffer;
delete tdoBuffer;
delete byInputBuffer;
delete tdiBytes;
return false;
}
for (unsigned int i = 0; i < fullBytes; i++)
tdoBuffer[tdoBytes++] = byInputBuffer[i];
if (fullBytes * 8 + 1 != bitCount) {
tdoBuffer[tdoBytes] = byInputBuffer[tdoBytes]
>> (8 - partialBits);
tdoBuffer[tdoBytes++] |= byInputBuffer[dwNumBytesRead - 1]
>> (7 - partialBits);
} else {
tdoBuffer[tdoBytes++] = byInputBuffer[dwNumBytesRead - 1] >> 7;
}
}
delete tdiBytes;
}
if (read) {
//Read out the data from input buffer
std::stringstream ss;
for (int i = tdoBytes - 1; i >= 0; i--)
ss << setfill('0') << setw(2) << hex << (int) tdoBuffer[i];
string hexTdo = ss.str();
if (hexTdo.length() - 1 == mask.length())
hexTdo = hexTdo.substr(1, hexTdo.length() - 1);
if (!compareHexString(hexTdo, tdo, mask)) {
cerr << "TDO didn't match expected string. Got " << hexTdo
<< " expected " << tdo << " with mask " << mask << endl;
delete byOutputBuffer;
delete tdoBuffer;
delete byInputBuffer;
return false;
}
}
delete byOutputBuffer;
delete tdoBuffer;
delete byInputBuffer;
return true;
}
string Jtag::shiftData(unsigned int bitCount, string tdi) {
FT_STATUS ftStatus;
unsigned int reqBytes = bitCount / 8 + (bitCount % 8 > 0);
BYTE *byOutputBuffer = new BYTE[reqBytes + 3];
BYTE *tdoBuffer = new BYTE[reqBytes + 3];
BYTE *byInputBuffer = new BYTE[reqBytes + 6];
DWORD dwNumBytesSent = 0;
DWORD dwNumBytesToRead = 0;
DWORD dwNumBytesRead = 0;
DWORD tdoBytes = 0;
unsigned int reqHex = bitCount / 4 + (bitCount % 4 > 0);
if (tdi.length() < reqHex)
return "";
if (!flush())
return "";
if (bitCount < 9) {
int data = stoi(tdi, 0, 16);
byOutputBuffer[0] = 0x3B;
byOutputBuffer[1] = bitCount - 2;
byOutputBuffer[2] = data & 0xff;
ftStatus = FT_Write(ftHandle, byOutputBuffer, 3, &dwNumBytesSent);
if (ftStatus != FT_OK) {
delete byOutputBuffer;
delete tdoBuffer;
delete byInputBuffer;
return "";
}
BYTE lastBit = (data >> ((bitCount - 1) % 8)) & 0x01;
byOutputBuffer[0] = 0x6E;
byOutputBuffer[1] = 0x00;
byOutputBuffer[2] = 0x03 | (lastBit << 7);
ftStatus = FT_Write(ftHandle, byOutputBuffer, 3, &dwNumBytesSent);
if (ftStatus != FT_OK) {
delete byOutputBuffer;
delete tdoBuffer;
delete byInputBuffer;
return "";
}
do {
ftStatus = FT_GetQueueStatus(ftHandle, &dwNumBytesToRead);
// Get the number of bytes in the device input buffer
} while ((dwNumBytesToRead == 0) && (ftStatus == FT_OK));
//or Timeout
ftStatus = FT_Read(ftHandle, byInputBuffer, dwNumBytesToRead,
&dwNumBytesRead);
if (ftStatus != FT_OK) {
delete byOutputBuffer;
delete tdoBuffer;
delete byInputBuffer;
return "";
}
tdoBuffer[0] = byInputBuffer[0] >> (8 - (bitCount - 1));
tdoBuffer[0] |= byInputBuffer[1] >> (7 - (bitCount - 1));
tdoBytes = 1;
} else {
BYTE *tdiBytes = new BYTE[reqBytes + 1];
hexToByte(tdi, tdiBytes);
unsigned int fullBytes = (bitCount - 1) / 8;
unsigned int remBytes = fullBytes;
unsigned int offset = 0;
if (fullBytes > 65536) {
cout << "Large transfers with reads may not work!" << endl;
}
while (remBytes > 0) {
unsigned int bct = remBytes > 65536 ? 65536 : remBytes;
byOutputBuffer[0] = 0x39;
byOutputBuffer[1] = (bct - 1) & 0xff;
byOutputBuffer[2] = ((bct - 1) >> 8) & 0xff;
for (unsigned int i = 0; i < bct; i++) {
byOutputBuffer[3 + i] = tdiBytes[i + offset];
}
ftStatus = FT_Write(ftHandle, byOutputBuffer, 3 + bct,
&dwNumBytesSent);
if (ftStatus != FT_OK) {
delete byOutputBuffer;
delete tdoBuffer;
delete byInputBuffer;
delete tdiBytes;
return "";
}
remBytes -= bct;
offset += bct;
}
unsigned int partialBits = bitCount - 1 - (fullBytes * 8);
if (fullBytes * 8 + 1 != bitCount) {
byOutputBuffer[0] = 0x3B;
byOutputBuffer[1] = partialBits - 1;
byOutputBuffer[2] = tdiBytes[reqBytes - 1] & 0xff;
ftStatus = FT_Write(ftHandle, byOutputBuffer, 3, &dwNumBytesSent);
if (ftStatus != FT_OK) {
delete byOutputBuffer;
delete tdoBuffer;
delete byInputBuffer;
delete tdiBytes;
return "";
}
}
BYTE lastBit = (tdiBytes[reqBytes - 1] >> ((bitCount - 1) % 8)) & 0x01;
byOutputBuffer[0] = 0x6E;
byOutputBuffer[1] = 0x00;
byOutputBuffer[2] = 0x03 | (lastBit << 7);
ftStatus = FT_Write(ftHandle, byOutputBuffer, 3, &dwNumBytesSent);
if (ftStatus != FT_OK) {
delete byOutputBuffer;
delete tdoBuffer;
delete byInputBuffer;
delete tdiBytes;
return "";
}
DWORD bytesToRead = fullBytes
+ ((fullBytes * 8 + 1 != bitCount) ? 2 : 1);
do {
ftStatus = FT_GetQueueStatus(ftHandle, &dwNumBytesToRead);
// Get the number of bytes in the device input buffer
} while ((dwNumBytesToRead != bytesToRead) && (ftStatus == FT_OK));
//or Timeout
ftStatus = FT_Read(ftHandle, byInputBuffer, dwNumBytesToRead,
&dwNumBytesRead);
if (ftStatus != FT_OK) {
delete byOutputBuffer;
delete tdoBuffer;
delete byInputBuffer;
delete tdiBytes;
return "";
}
for (unsigned int i = 0; i < fullBytes; i++)
tdoBuffer[tdoBytes++] = byInputBuffer[i];
if (fullBytes * 8 + 1 != bitCount) {
tdoBuffer[tdoBytes] = byInputBuffer[tdoBytes] >> (8 - partialBits);
tdoBuffer[tdoBytes++] |= byInputBuffer[dwNumBytesRead - 1]
>> (7 - partialBits);
} else {
tdoBuffer[tdoBytes++] = byInputBuffer[dwNumBytesRead - 1] >> 7;
}
delete tdiBytes;
}
//Read out the data from input buffer
std::stringstream ss;
for (int i = tdoBytes - 1; i >= 0; i--)
ss << setfill('0') << setw(2) << hex << (int) tdoBuffer[i];
string hexTdo = ss.str();
if (hexTdo.length() - 1 == tdi.length())
hexTdo = hexTdo.substr(1, hexTdo.length() - 1);
delete byOutputBuffer;
delete tdoBuffer;
delete byInputBuffer;
return hexTdo;
}
bool Jtag::sendClocks(unsigned long cycles) {
BYTE byOutputBuffer[3];
DWORD dwNumBytesSent;
FT_STATUS ftStatus;
if (cycles / 8 > 65536) {
if (!sendClocks(cycles - 65536 * 8))
return false;
cycles = 65536 * 8;
}
cycles /= 8;
byOutputBuffer[0] = 0x8F;
byOutputBuffer[1] = (cycles - 1) & 0xff;
byOutputBuffer[2] = ((cycles - 1) >> 8) & 0xff;
ftStatus = FT_Write(ftHandle, byOutputBuffer, 3, &dwNumBytesSent);
if (ftStatus != FT_OK)
return false;
return true;
}
bool Jtag::compareHexString(string a, string b, string mask) {
unsigned int length = a.length();
if (length != b.length()) {
cout << "length mismatch!" << endl;
return false;
}
if (mask == "")
return a == b;
if (length != mask.length()) {
cout << "length and mask mismatch! " << length << " and "
<< mask.length() << " of strings " << a << " and " << mask
<< endl;
return false;
}
for (unsigned int i = 0; i < length / 2; i++) {
BYTE m = stoi(mask.substr(length - 2 - i * 2, 2), 0, 16);
BYTE pa = stoi(a.substr(length - 2 - i * 2, 2), 0, 16);
BYTE pb = stoi(b.substr(length - 2 - i * 2, 2), 0, 16);
if ((pa & m) != (pb & m)) {
cout << "Mismatch at " << i << endl;
return false;
}
}
if ((length & 1) != 0) {
BYTE m = stoi(mask.substr(0, 1), 0, 16);
BYTE pa = stoi(a.substr(0, 1), 0, 16);
BYTE pb = stoi(b.substr(0, 1), 0, 16);
if ((pa & m) != (pb & m)) {
cout << "Mismatch at last bit" << endl;
return false;
}
}
return true;
}
void Jtag::hexToByte(string hex, BYTE* out) {
int length = hex.length();
for (int i = 0; i < length / 2; i++) {
try {
out[i] = stoi(hex.substr(length - 2 - i * 2, 2), 0, 16);
} catch (exception& e) {
cerr << "Failed at " << i << " with length " << length << endl;
cerr.flush();
exit(2);
}
}
if ((length & 1) != 0)
try {
out[length / 2] = stoi(hex.substr(0, 1), 0, 16);
} catch (exception& e) {
cerr << "Failed to convert string " << hex.substr(0, 1)
<< " to an int!" << endl;
cerr.flush();
exit(2);
}
}
bool Jtag::flush() {
FT_STATUS ftStatus;
BYTE byInputBuffer[1024];
DWORD dwNumBytesToRead = 0;
DWORD dwNumBytesRead = 0;
ftStatus = FT_GetQueueStatus(ftHandle, &dwNumBytesToRead);
if (ftStatus != FT_OK)
return false;
if (dwNumBytesToRead == 0)
return true;
//or Timeout
ftStatus = FT_Read(ftHandle, &byInputBuffer, dwNumBytesToRead,
&dwNumBytesRead);
if (ftStatus != FT_OK)
return false;
return true;
}

View File

@ -0,0 +1,40 @@
/*
* jtag.h
*
* Created on: May 24, 2017
* Author: justin
*/
#ifndef JTAG_H_
#define JTAG_H_
#include "ftd2xx.h"
#include "jtag_fsm.h"
#include <unistd.h>
class Jtag {
FT_HANDLE ftHandle;
unsigned int uiDevIndex = 0xF; // The device in the list that is used
bool active;
public:
Jtag();
FT_STATUS connect(unsigned int);
FT_STATUS disconnect();
bool initialize();
bool setFreq(double);
bool navigateToState(Jtag_fsm::State, Jtag_fsm::State);
bool shiftData(unsigned int, string, string, string);
string shiftData(unsigned int, string);
bool sendClocks(unsigned long);
private:
bool sync_mpsse();
bool config_jtag();
static void hexToByte(string, BYTE*);
bool flush();
bool compareHexString(string, string, string);
};
#endif /* JTAG_H_ */

View File

@ -0,0 +1,119 @@
/*
* jtag_fsm.cpp
*
* Created on: May 24, 2017
* Author: justin
*/
#include "jtag_fsm.h"
#include <queue>
#include <iostream>
using namespace std;
Jtag_fsm::State Jtag_fsm::getTransition(State state, bool tms) {
switch (state) {
case TEST_LOGIC_RESET:
return tms ? TEST_LOGIC_RESET : RUN_TEST_IDLE;
case RUN_TEST_IDLE:
return tms ? SELECT_DR_SCAN : RUN_TEST_IDLE;
case SELECT_DR_SCAN:
return tms ? SELECT_IR_SCAN : CAPTURE_DR;
case CAPTURE_DR:
return tms ? EXIT1_DR : SHIFT_DR;
case SHIFT_DR:
return tms ? EXIT1_DR : SHIFT_DR;
case EXIT1_DR:
return tms ? UPDATE_DR : PAUSE_DR;
case PAUSE_DR:
return tms ? EXIT2_DR : PAUSE_DR;
case EXIT2_DR:
return tms ? UPDATE_DR : SHIFT_DR;
case UPDATE_DR:
return tms ? SELECT_DR_SCAN : RUN_TEST_IDLE;
case SELECT_IR_SCAN:
return tms ? TEST_LOGIC_RESET : CAPTURE_IR;
case CAPTURE_IR:
return tms ? EXIT1_IR : SHIFT_IR;
case SHIFT_IR:
return tms ? EXIT1_IR : SHIFT_IR;
case EXIT1_IR:
return tms ? UPDATE_IR : PAUSE_IR;
case PAUSE_IR:
return tms ? EXIT2_IR : PAUSE_IR;
case EXIT2_IR:
return tms ? UPDATE_IR : SHIFT_IR;
case UPDATE_IR:
return tms ? SELECT_DR_SCAN : RUN_TEST_IDLE;
}
return TEST_LOGIC_RESET;
}
Jtag_fsm::Transistions Jtag_fsm::getTransitions(State init, State final) {
queue<Transistions> queue;
Transistions t;
t.currentState = init;
t.moves = 0;
t.tms = 0;
queue.push(t);
while (!queue.empty()) {
t = queue.front();
queue.pop();
if (t.currentState == final) {
break;
}
State s0 = getTransition(t.currentState, false);
State s1 = getTransition(t.currentState, true);
t.moves++;
t.tms &= ~(1 << (t.moves-1)); // clear bit
t.currentState = s0;
queue.push(t);
t.tms |= (1 << (t.moves-1));
t.currentState = s1;
queue.push(t);
}
return t;
}
Jtag_fsm::State Jtag_fsm::getStateFromName(string s) {
if (s == "RESET")
return TEST_LOGIC_RESET;
if (s == "IDLE")
return RUN_TEST_IDLE;
if (s == "DRSELECT")
return SELECT_DR_SCAN;
if (s == "DRCAPTURE")
return CAPTURE_DR;
if (s == "DRSHIFT")
return SHIFT_DR;
if (s == "DREXIT1")
return EXIT1_DR;
if (s == "DRPAUSE")
return PAUSE_DR;
if (s == "DREXIT2")
return EXIT2_DR;
if (s == "DRUPDATE")
return UPDATE_DR;
if (s == "IRSELECT")
return SELECT_IR_SCAN;
if (s == "IRCAPTURE")
return CAPTURE_IR;
if (s == "IRSHIFT")
return SHIFT_IR;
if (s == "IREXIT1")
return EXIT1_IR;
if (s == "IRPAUSE")
return PAUSE_IR;
if (s == "IREXIT2")
return EXIT2_IR;
if (s == "IRUPDATE")
return UPDATE_IR;
cerr << "ERROR! Invalid state name " << s << endl;
return TEST_LOGIC_RESET;
}

View File

@ -0,0 +1,52 @@
/*
* jtag_fsm.h
*
* Created on: May 24, 2017
* Author: justin
*/
#ifndef JTAG_FSM_H_
#define JTAG_FSM_H_
#include <cstdint>
#include <string>
using namespace std;
class Jtag_fsm {
public:
enum State {
TEST_LOGIC_RESET,
RUN_TEST_IDLE,
SELECT_DR_SCAN,
CAPTURE_DR,
SHIFT_DR,
EXIT1_DR,
PAUSE_DR,
EXIT2_DR,
UPDATE_DR,
SELECT_IR_SCAN,
CAPTURE_IR,
SHIFT_IR,
EXIT1_IR,
PAUSE_IR,
EXIT2_IR,
UPDATE_IR
};
class Transistions {
public:
State currentState;
uint8_t tms;
uint8_t moves;
};
static Transistions getTransitions(State, State);
static State getStateFromName(string);
private:
static State getTransition(State, bool);
};
#endif /* JTAG_FSM_H_ */

View File

@ -0,0 +1,388 @@
/*
* Loader.cpp
*
* Created on: Nov 6, 2017
* Author: justin
*/
#include "loader.h"
#include "jtag.h"
#include <iomanip>
#include <sstream>
#include <iostream>
#include <stdio.h>
#include <unistd.h>
#include <chrono>
#include "config_type.h"
#ifdef _WIN32
#include "mingw.thread.h"
#else
#include <thread>
#endif
using namespace std;
Loader::Loader(Jtag *dev) {
device = dev;
currentState = Jtag_fsm::TEST_LOGIC_RESET;
}
bool Loader::setState(Jtag_fsm::State state) {
if (!device->navigateToState(currentState, state))
return false;
currentState = state;
return true;
}
bool Loader::resetState() {
currentState = Jtag_fsm::TEST_LOGIC_RESET;
return device->navigateToState(Jtag_fsm::CAPTURE_DR,
Jtag_fsm::TEST_LOGIC_RESET);
}
bool Loader::setIR(Instruction inst) {
stringstream inst_str;
inst_str << std::setfill('0') << std::setw(2) << hex
<< static_cast<int>(inst);
if (!device->navigateToState(currentState, Jtag_fsm::SHIFT_IR)) {
cerr << "Failed to change to SHIFT_IR state!" << endl;
return false;
}
if (!device->shiftData(6, inst_str.str(), "", "")) {
cerr << "Failed to shift instruction data!" << endl;
return false;
}
if (!device->navigateToState(Jtag_fsm::EXIT1_IR, Jtag_fsm::RUN_TEST_IDLE)) {
cerr << "Failed to change to RUN_TEST_IDLE state!" << endl;
return false;
}
currentState = Jtag_fsm::RUN_TEST_IDLE;
return true;
}
// basically the same as shiftDR but ignores the first four bits
bool Loader::shiftUDR(int bits, string write, string read, string mask) {
string uread = read;
string umask = mask;
string uwrite = write;
if (!read.empty()) {
uread = read + "0";
umask = mask + "0";
uwrite = "0" + write;
bits += 4;
}
return shiftDR(bits, uwrite, uread, umask);
}
bool Loader::shiftDR(int bits, string write, string read, string mask) {
if (!device->navigateToState(currentState, Jtag_fsm::SHIFT_DR)) {
cerr << "Failed to change to SHIFT_DR state!" << endl;
return false;
}
if (!device->shiftData(bits, write, read, mask)) {
cerr << "Failed to shift data!" << endl;
return false;
}
if (!device->navigateToState(Jtag_fsm::EXIT1_DR, Jtag_fsm::RUN_TEST_IDLE)) {
cerr << "Failed to change to RUN_TEST_IDLE state!" << endl;
return false;
}
currentState = Jtag_fsm::RUN_TEST_IDLE;
return true;
}
bool Loader::shiftIR(int bits, string write, string read, string mask) {
if (!device->navigateToState(currentState, Jtag_fsm::SHIFT_IR)) {
cerr << "Failed to change to SHIFT_IR state!" << endl;
return false;
}
if (!device->shiftData(bits, write, read, mask)) {
cerr << "Failed to shift data!" << endl;
return false;
}
if (!device->navigateToState(Jtag_fsm::EXIT1_IR, Jtag_fsm::RUN_TEST_IDLE)) {
cerr << "Failed to change to RUN_TEST_IDLE state!" << endl;
return false;
}
currentState = Jtag_fsm::RUN_TEST_IDLE;
return true;
}
string Loader::shiftDR(int bits, string write) {
if (!device->navigateToState(currentState, Jtag_fsm::SHIFT_DR)) {
cerr << "Failed to change to SHIFT_DR state!" << endl;
return NULL;
}
string data = device->shiftData(bits, write);
if (data.empty()) {
cerr << "Failed to shift data!" << endl;
return NULL;
}
if (!device->navigateToState(Jtag_fsm::EXIT1_DR, Jtag_fsm::RUN_TEST_IDLE)) {
cerr << "Failed to change to RUN_TEST_IDLE state!" << endl;
return NULL;
}
currentState = Jtag_fsm::RUN_TEST_IDLE;
return data;
}
bool Loader::loadBin(string file) {
string binStr = fileToBinStr(file);
string reversedBinStr = reverseBytes(binStr);
if (!device->setFreq(10000000)) {
cerr << "Failed to set JTAG frequency!" << endl;
return false;
}
if (!resetState())
return false;
if (!setState(Jtag_fsm::RUN_TEST_IDLE))
return false;
if (!setIR(JPROGRAM))
return false;
if (!setIR(ISC_NOOP))
return false;
std::this_thread::sleep_for(std::chrono::milliseconds(100));
// config/jprog/poll
if (!device->sendClocks(10000))
return false;
if (!shiftIR(6, "14", "11", "31"))
return false;
// config/slr
if (!setIR(CFG_IN))
return false;
if (!shiftDR(binStr.length() * 4, reversedBinStr, "", ""))
return false;
// config/start
if (!setState(Jtag_fsm::RUN_TEST_IDLE))
return false;
if (!device->sendClocks(100000))
return false;
if (!setIR(JSTART))
return false;
if (!setState(Jtag_fsm::RUN_TEST_IDLE))
return false;
if (!device->sendClocks(100))
return false;
if (!shiftIR(6, "09", "31", "11"))
return false;
// config/status
if (!setState(Jtag_fsm::TEST_LOGIC_RESET))
return false;
if (!device->sendClocks(5))
return false;
if (!setIR(CFG_IN))
return false;
if (!shiftDR(160, "0000000400000004800700140000000466aa9955", "", ""))
return false;
if (!setIR(CFG_OUT))
return false;
if (!shiftDR(32, "00000000", "3f5e0d40", "08000000"))
return false;
if (!setState(Jtag_fsm::TEST_LOGIC_RESET))
return false;
if (!device->sendClocks(5))
return false;
return true;
}
string Loader::fileToBinStr(string file) {
ifstream binFile(file);
stringstream hexString;
char *buffer;
binFile.seekg(0, ios::end);
unsigned int byteCount = binFile.tellg();
binFile.seekg(0, ios::beg);
buffer = new char[byteCount];
binFile.read(buffer, byteCount);
binFile.close();
for (int i = byteCount - 1; i >= 0; i--) {
hexString << setfill('0') << setw(2) << std::hex
<< (int) ((unsigned char) buffer[i]);
}
delete buffer;
return hexString.str();
}
bool Loader::eraseFlash(string loaderFile) {
cout << "Initializing FPGA..." << endl;
if (!loadBin(loaderFile)) {
cerr << "Failed to initialize FPGA!" << endl;
return false;
}
if (!device->setFreq(1500000)) {
cerr << "Failed to set JTAG frequency!" << endl;
return false;
}
cout << "Erasing..." << endl;
// Erase the flash
if (!setIR(USER1))
return false;
if (!shiftDR(1, "0", "", ""))
return false;
std::this_thread::sleep_for(std::chrono::seconds(1)); // wait for erase
if (!setIR(JPROGRAM))
return false;
// reset just for good measure
if (!resetState())
return false;
return true;
}
bool Loader::writeBin(string binFile, bool flash, string loaderFile) {
if (flash) {
string binStr = fileToBinStr(binFile);
cout << "Initializing FPGA..." << endl;
if (!loadBin(loaderFile)) {
cerr << "Failed to initialize FPGA!" << endl;
return false;
}
if (!device->setFreq(1500000)) {
cerr << "Failed to set JTAG frequency!" << endl;
return false;
}
cout << "Erasing..." << endl;
// Erase the flash
if (!setIR(USER1))
return false;
if (!shiftDR(1, "0", "", ""))
return false;
std::this_thread::sleep_for(std::chrono::milliseconds(100));
cout << "Writing..." << endl;
// Write the flash
if (!setIR(USER2))
return false;
if (!shiftDR(binStr.length() * 4, binStr, "", ""))
return false;
// If you enter the reset state after a write
// the loader firmware resets the flash into
// regular SPI mode and gets stuck in a dead FSM
// state. You need to do this before issuing a
// JPROGRAM command or the FPGA can't read the
// flash.
if (!resetState())
return false;
std::this_thread::sleep_for(std::chrono::milliseconds(100)); // 100ms delay is required before issuing JPROGRAM
cout << "Resetting FPGA..." << endl;
// JPROGRAM resets the FPGA configuration and will
// cause it to read the flash memory
if (!setIR(JPROGRAM))
return false;
} else {
cout << "Programming FPGA..." << endl;
if (!loadBin(binFile)) {
cerr << "Failed to initialize FPGA!" << endl;
return false;
}
}
// reset just for good measure
if (!resetState())
return false;
cout << "Done." << endl;
return true;
}
bool Loader::checkIDCODE() {
if (!setIR(IDCODE))
return false;
if (!shiftDR(32, "00000000", "0362D093", "0FFFFFFF")) // FPGA IDCODE
return false;
return true;
}
BYTE reverse(BYTE b) {
b = (b & 0xF0) >> 4 | (b & 0x0F) << 4;
b = (b & 0xCC) >> 2 | (b & 0x33) << 2;
b = (b & 0xAA) >> 1 | (b & 0x55) << 1;
return b;
}
bool Loader::setWREN() {
if (!setIR(USER1))
return false;
if (!shiftDR(8, reverseBytes("06"), "", ""))
return false;
return true;
}
int Loader::getStatus() {
if (!setIR(USER1))
return -1;
string data = shiftDR(17, reverseBytes("00005"));
cout << data << endl;
if (data.empty())
return -1;
int status = stoi(data, 0, 16);
status >>= 9;
return reverse(status);
}
void hexToByte(string hex, BYTE* out) {
int length = hex.length();
for (int i = 0; i < length / 2; i++) {
out[i] = stoi(hex.substr(length - 2 - i * 2, 2), 0, 16);
}
if ((length & 1) != 0)
out[length / 2] = stoi(hex.substr(0, 1), 0, 16);
}
string Loader::reverseBytes(string start) {
unsigned long l = start.length();
if (l & 1)
l++;
l /= 2;
BYTE* bytes = new BYTE[l];
hexToByte(start, bytes);
for (unsigned long i = 0; i < l; i++) {
bytes[i] = reverse(bytes[i]);
}
std::stringstream ss;
for (long i = l - 1; i >= 0; i--)
ss << setfill('0') << setw(2) << hex << (unsigned int) bytes[i];
string out = ss.str();
if (out.length() - 1 == start.length())
out = out.substr(1, out.length() - 1);
delete bytes;
return out;
}

View File

@ -0,0 +1,77 @@
/*
* Loader.h
*
* Created on: Nov 6, 2017
* Author: justin
*/
#ifndef LOADER_H_
#define LOADER_H_
#include <string>
#include <fstream>
#include <streambuf>
#include <sstream>
#include <iostream>
#include <iomanip>
#include<algorithm>
#include "jtag.h"
#include "jtag_fsm.h"
class Loader {
Jtag* device;
Jtag_fsm::State currentState;
public:
enum Instruction {
EXTEST = 0x26,
EXTEST_PULSE = 0x3C,
EXTEST_TRAIN = 0x3D,
SAMPLE = 0x01,
USER1 = 0x02,
USER2 = 0x03,
USER3 = 0x22,
USER4 = 0x23,
CFG_OUT = 0x04,
CFG_IN = 0x05,
USERCODE = 0x08,
IDCODE = 0x09,
HIGHZ_IO = 0x0A,
JPROGRAM = 0x0B,
JSTART = 0x0C,
JSHUTDOWN = 0x0D,
XADC_DRP = 0x37,
ISC_ENABLE = 0x10,
ISC_PROGRAM = 0x11,
XSC_PROGRAM_KEY = 0x12,
XSC_DNA = 0x17,
FUSE_DNA = 0x32,
ISC_NOOP = 0x14,
ISC_DISABLE = 0x16,
BYPASS = 0x2F,
};
public:
Loader(Jtag*);
bool resetState();
bool checkIDCODE();
bool eraseFlash(string);
bool writeBin(string, bool, string);
private:
bool setWREN();
bool setIR(Instruction);
bool shiftUDR(int, string, string, string);
bool shiftDR(int, string, string, string);
string shiftDR(int, string);
bool shiftIR(int, string, string, string);
int getStatus();
string reverseBytes(string);
string fileToBinStr(string);
bool loadBin(string);
bool setState(Jtag_fsm::State);
};
#endif /* LOADER_H_ */

View File

@ -0,0 +1,414 @@
/**
* @file mingw.thread.h
* @brief std::thread implementation for MinGW
* (c) 2013-2016 by Mega Limited, Auckland, New Zealand
* @author Alexander Vassilev
*
* @copyright Simplified (2-clause) BSD License.
* You should have received a copy of the license along with this
* program.
*
* This code is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* @note
* This file may become part of the mingw-w64 runtime package. If/when this happens,
* the appropriate license will be added, i.e. this code will become dual-licensed,
* and the current BSD 2-clause license will stay.
*/
#ifndef WIN32STDTHREAD_H
#define WIN32STDTHREAD_H
#if !defined(__cplusplus) || (__cplusplus < 201103L)
#error A C++11 compiler is required!
#endif
// Use the standard classes for std::, if available.
#include <thread>
#include <cstddef> // For std::size_t
#include <cerrno> // Detect error type.
#include <exception> // For std::terminate
#include <system_error> // For std::system_error
#include <functional> // For std::hash
#include <tuple> // For std::tuple
#include <chrono> // For sleep timing.
#include <memory> // For std::unique_ptr
#include <ostream> // Stream output for thread ids.
#include <utility> // For std::swap, std::forward
// For the invoke implementation only:
#include <type_traits> // For std::result_of, etc.
//#include <utility> // For std::forward
//#include <functional> // For std::reference_wrapper
#include <windows.h>
#include <process.h> // For _beginthreadex
#ifndef NDEBUG
#include <cstdio>
#endif
#if !defined(_WIN32_WINNT) || (_WIN32_WINNT < 0x0501)
#error To use the MinGW-std-threads library, you will need to define the macro _WIN32_WINNT to be 0x0501 (Windows XP) or higher.
#endif
// Instead of INVALID_HANDLE_VALUE, _beginthreadex returns 0.
namespace mingw_stdthread
{
namespace detail
{
// For compatibility, implement std::invoke for C++11 and C++14
#if __cplusplus < 201703L
template<bool PMemFunc, bool PMemData>
struct Invoker
{
template<class F, class... Args>
inline static typename std::result_of<F(Args...)>::type invoke (F&& f, Args&&... args)
{
return std::forward<F>(f)(std::forward<Args>(args)...);
}
};
template<bool>
struct InvokerHelper;
template<>
struct InvokerHelper<false>
{
template<class T1>
inline static auto get (T1&& t1) -> decltype(*std::forward<T1>(t1))
{
return *std::forward<T1>(t1);
}
template<class T1>
inline static auto get (const std::reference_wrapper<T1>& t1) -> decltype(t1.get())
{
return t1.get();
}
};
template<>
struct InvokerHelper<true>
{
template<class T1>
inline static auto get (T1&& t1) -> decltype(std::forward<T1>(t1))
{
return std::forward<T1>(t1);
}
};
template<>
struct Invoker<true, false>
{
template<class T, class F, class T1, class... Args>
inline static auto invoke (F T::* f, T1&& t1, Args&&... args) ->\
decltype((InvokerHelper<std::is_base_of<T,typename std::decay<T1>::type>::value>::get(std::forward<T1>(t1)).*f)(std::forward<Args>(args)...))
{
return (InvokerHelper<std::is_base_of<T,typename std::decay<T1>::type>::value>::get(std::forward<T1>(t1)).*f)(std::forward<Args>(args)...);
}
};
template<>
struct Invoker<false, true>
{
template<class T, class F, class T1, class... Args>
inline static auto invoke (F T::* f, T1&& t1, Args&&... args) ->\
decltype(InvokerHelper<std::is_base_of<T,typename std::decay<T1>::type>::value>::get(t1).*f)
{
return InvokerHelper<std::is_base_of<T,typename std::decay<T1>::type>::value>::get(t1).*f;
}
};
template<class F, class... Args>
struct InvokeResult
{
typedef Invoker<std::is_member_function_pointer<typename std::remove_reference<F>::type>::value,
std::is_member_object_pointer<typename std::remove_reference<F>::type>::value &&
(sizeof...(Args) == 1)> invoker;
inline static auto invoke (F&& f, Args&&... args) -> decltype(invoker::invoke(std::forward<F>(f), std::forward<Args>(args)...))
{
return invoker::invoke(std::forward<F>(f), std::forward<Args>(args)...);
};
};
template<class F, class...Args>
auto invoke (F&& f, Args&&... args) -> decltype(InvokeResult<F, Args...>::invoke(std::forward<F>(f), std::forward<Args>(args)...))
{
return InvokeResult<F, Args...>::invoke(std::forward<F>(f), std::forward<Args>(args)...);
}
#else
using std::invoke;
#endif
template<std::size_t...>
struct IntSeq {};
template<std::size_t N, std::size_t... S>
struct GenIntSeq : GenIntSeq<N-1, N-1, S...> { };
template<std::size_t... S>
struct GenIntSeq<0, S...> { typedef IntSeq<S...> type; };
// We can't define the Call struct in the function - the standard forbids template methods in that case
template<class Func, typename... Args>
class ThreadFuncCall
{
typedef std::tuple<Args...> Tuple;
Func mFunc;
Tuple mArgs;
template <std::size_t... S>
void callFunc(detail::IntSeq<S...>)
{
detail::invoke(std::forward<Func>(mFunc), std::get<S>(std::forward<Tuple>(mArgs)) ...);
}
public:
ThreadFuncCall(Func&& aFunc, Args&&... aArgs)
:mFunc(std::forward<Func>(aFunc)), mArgs(std::forward<Args>(aArgs)...){}
void callFunc()
{
callFunc(typename detail::GenIntSeq<sizeof...(Args)>::type());
}
};
} // Namespace "detail"
class thread
{
public:
class id
{
DWORD mId;
void clear() {mId = 0;}
friend class thread;
friend class std::hash<id>;
public:
explicit id(DWORD aId=0) noexcept : mId(aId){}
friend bool operator==(id x, id y) noexcept {return x.mId == y.mId; }
friend bool operator!=(id x, id y) noexcept {return x.mId != y.mId; }
friend bool operator< (id x, id y) noexcept {return x.mId < y.mId; }
friend bool operator<=(id x, id y) noexcept {return x.mId <= y.mId; }
friend bool operator> (id x, id y) noexcept {return x.mId > y.mId; }
friend bool operator>=(id x, id y) noexcept {return x.mId >= y.mId; }
template<class _CharT, class _Traits>
friend std::basic_ostream<_CharT, _Traits>&
operator<<(std::basic_ostream<_CharT, _Traits>& __out, id __id)
{
if (__id.mId == 0)
{
return __out << "(invalid std::thread::id)";
}
else
{
return __out << __id.mId;
}
}
};
private:
static constexpr HANDLE kInvalidHandle = nullptr;
HANDLE mHandle;
id mThreadId;
template <class Call>
static unsigned __stdcall threadfunc(void* arg)
{
std::unique_ptr<Call> call(static_cast<Call*>(arg));
call->callFunc();
return 0;
}
static unsigned int _hardware_concurrency_helper() noexcept
{
SYSTEM_INFO sysinfo;
// This is one of the few functions used by the library which has a nearly-
// equivalent function defined in earlier versions of Windows. Include the
// workaround, just as a reminder that it does exist.
#if defined(_WIN32_WINNT) && (_WIN32_WINNT >= 0x0501)
::GetNativeSystemInfo(&sysinfo);
#else
::GetSystemInfo(&sysinfo);
#endif
return sysinfo.dwNumberOfProcessors;
}
public:
typedef HANDLE native_handle_type;
id get_id() const noexcept {return mThreadId;}
native_handle_type native_handle() const {return mHandle;}
thread(): mHandle(kInvalidHandle), mThreadId(){}
thread(thread&& other)
:mHandle(other.mHandle), mThreadId(other.mThreadId)
{
other.mHandle = kInvalidHandle;
other.mThreadId.clear();
}
thread(const thread &other)=delete;
template<class Func, typename... Args>
explicit thread(Func&& func, Args&&... args) : mHandle(), mThreadId()
{
typedef detail::ThreadFuncCall<Func, Args...> Call;
auto call = new Call(
std::forward<Func>(func), std::forward<Args>(args)...);
auto int_handle = _beginthreadex(NULL, 0, threadfunc<Call>,
static_cast<LPVOID>(call), 0,
reinterpret_cast<unsigned*>(&(mThreadId.mId)));
if (int_handle == 0)
{
mHandle = kInvalidHandle;
int errnum = errno;
delete call;
// Note: Should only throw EINVAL, EAGAIN, EACCES
throw std::system_error(errnum, std::generic_category());
} else
mHandle = reinterpret_cast<HANDLE>(int_handle);
}
bool joinable() const {return mHandle != kInvalidHandle;}
// Note: Due to lack of synchronization, this function has a race condition
// if called concurrently, which leads to undefined behavior. The same applies
// to all other member functions of this class, but this one is mentioned
// explicitly.
void join()
{
using namespace std;
if (get_id() == id(GetCurrentThreadId()))
throw system_error(make_error_code(errc::resource_deadlock_would_occur));
if (mHandle == kInvalidHandle)
throw system_error(make_error_code(errc::no_such_process));
if (!joinable())
throw system_error(make_error_code(errc::invalid_argument));
WaitForSingleObject(mHandle, INFINITE);
CloseHandle(mHandle);
mHandle = kInvalidHandle;
mThreadId.clear();
}
~thread()
{
if (joinable())
{
#ifndef NDEBUG
std::printf("Error: Must join() or detach() a thread before \
destroying it.\n");
#endif
std::terminate();
}
}
thread& operator=(const thread&) = delete;
thread& operator=(thread&& other) noexcept
{
if (joinable())
{
#ifndef NDEBUG
std::printf("Error: Must join() or detach() a thread before \
moving another thread to it.\n");
#endif
std::terminate();
}
swap(std::forward<thread>(other));
return *this;
}
void swap(thread&& other) noexcept
{
HANDLE tmph = mHandle;
mHandle = other.mHandle;
other.mHandle = tmph;
DWORD tmpd = mThreadId.mId;
mThreadId.mId = other.mThreadId.mId;
other.mThreadId.mId = tmpd;
}
static unsigned int hardware_concurrency() noexcept
{
static unsigned int cached = _hardware_concurrency_helper();
return cached;
}
void detach()
{
if (!joinable())
{
using namespace std;
throw system_error(make_error_code(errc::invalid_argument));
}
if (mHandle != kInvalidHandle)
{
CloseHandle(mHandle);
mHandle = kInvalidHandle;
}
mThreadId.clear();
}
};
namespace this_thread
{
inline thread::id get_id() noexcept {return thread::id(GetCurrentThreadId());}
inline void yield() noexcept {Sleep(0);}
template< class Rep, class Period >
void sleep_for( const std::chrono::duration<Rep,Period>& sleep_duration)
{
using namespace std::chrono;
using rep = milliseconds::rep;
rep ms = duration_cast<milliseconds>(sleep_duration).count();
while (ms > 0)
{
constexpr rep kMaxRep = static_cast<rep>(INFINITE - 1);
auto sleepTime = (ms < kMaxRep) ? ms : kMaxRep;
Sleep(static_cast<DWORD>(sleepTime));
ms -= sleepTime;
}
}
template <class Clock, class Duration>
void sleep_until(const std::chrono::time_point<Clock,Duration>& sleep_time)
{
sleep_for(sleep_time-Clock::now());
}
}
} // Namespace mingw_stdthread
namespace std
{
// Because of quirks of the compiler, the common "using namespace std;"
// directive would flatten the namespaces and introduce ambiguity where there
// was none. Direct specification (std::), however, would be unaffected.
// Take the safe option, and include only in the presence of MinGW's win32
// implementation.
#if defined(__MINGW32__ ) && !defined(_GLIBCXX_HAS_GTHREADS)
using mingw_stdthread::thread;
// Remove ambiguity immediately, to avoid problems arising from the above.
//using std::thread;
namespace this_thread
{
using namespace mingw_stdthread::this_thread;
}
#elif !defined(MINGW_STDTHREAD_REDUNDANCY_WARNING) // Skip repetition
#define MINGW_STDTHREAD_REDUNDANCY_WARNING
#pragma message "This version of MinGW seems to include a win32 port of\
pthreads, and probably already has C++11 std threading classes implemented,\
based on pthreads. These classes, found in namespace std, are not overridden\
by the mingw-std-thread library. If you would still like to use this\
implementation (as it is more lightweight), use the classes provided in\
namespace mingw_stdthread."
#endif
// Specialize hash for this implementation's thread::id, even if the
// std::thread::id already has a hash.
template<>
struct hash<mingw_stdthread::thread::id>
{
typedef mingw_stdthread::thread::id argument_type;
typedef size_t result_type;
size_t operator() (const argument_type & i) const noexcept
{
return i.mId;
}
};
}
#endif // WIN32STDTHREAD_H

940
alchitry-loader/src/spi.cpp Normal file
View File

@ -0,0 +1,940 @@
/*
* iceprog -- simple programming tool for FTDI-based Lattice iCE programmers
*
* Copyright (C) 2015 Clifford Wolf <clifford@clifford.at>
* Copyright (C) 2018 Piotr Esden-Tempski <piotr@esden.net>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
* Relevant Documents:
* -------------------
* http://www.latticesemi.com/~/media/Documents/UserManuals/EI/icestickusermanual.pdf
* http://www.micron.com/~/media/documents/products/data-sheet/nor-flash/serial-nor/n25q/n25q_32mb_3v_65nm.pdf
* http://www.ftdichip.com/Support/Documents/AppNotes/AN_108_Command_Processor_for_MPSSE_and_MCU_Host_Bus_Emulation_Modes.pdf
*/
#include "spi.h"
#include <unistd.h>
#include <iostream>
#include <iomanip>
#include <string>
#include <sstream>
#include <stdio.h>
#include <string.h>
#ifdef _WIN32
#include "mingw.thread.h"
#else
#include <thread>
#endif
#include <chrono>
using namespace std;
/* MPSSE engine command definitions */
enum mpsse_cmd {
/* Mode commands */
MC_SETB_LOW = 0x80, /* Set Data bits LowByte */
MC_READB_LOW = 0x81, /* Read Data bits LowByte */
MC_SETB_HIGH = 0x82, /* Set Data bits HighByte */
MC_READB_HIGH = 0x83, /* Read data bits HighByte */
MC_LOOPBACK_EN = 0x84, /* Enable loopback */
MC_LOOPBACK_DIS = 0x85, /* Disable loopback */
MC_SET_CLK_DIV = 0x86, /* Set clock divisor */
MC_FLUSH = 0x87, /* Flush buffer fifos to the PC. */
MC_WAIT_H = 0x88, /* Wait on GPIOL1 to go high. */
MC_WAIT_L = 0x89, /* Wait on GPIOL1 to go low. */
MC_TCK_X5 = 0x8A, /* Disable /5 div, enables 60MHz master clock */
MC_TCK_D5 = 0x8B, /* Enable /5 div, backward compat to FT2232D */
MC_EN_3PH_CLK = 0x8C, /* Enable 3 phase clk, DDR I2C */
MC_DIS_3PH_CLK = 0x8D, /* Disable 3 phase clk */
MC_CLK_N = 0x8E, /* Clock every bit, used for JTAG */
MC_CLK_N8 = 0x8F, /* Clock every byte, used for JTAG */
MC_CLK_TO_H = 0x94, /* Clock until GPIOL1 goes high */
MC_CLK_TO_L = 0x95, /* Clock until GPIOL1 goes low */
MC_EN_ADPT_CLK = 0x96, /* Enable adaptive clocking */
MC_DIS_ADPT_CLK = 0x97, /* Disable adaptive clocking */
MC_CLK8_TO_H = 0x9C, /* Clock until GPIOL1 goes high, count bytes */
MC_CLK8_TO_L = 0x9D, /* Clock until GPIOL1 goes low, count bytes */
MC_TRI = 0x9E, /* Set IO to only drive on 0 and tristate on 1 */
/* CPU mode commands */
MC_CPU_RS = 0x90, /* CPUMode read short address */
MC_CPU_RE = 0x91, /* CPUMode read extended address */
MC_CPU_WS = 0x92, /* CPUMode write short address */
MC_CPU_WE = 0x93, /* CPUMode write extended address */
};
// ---------------------------------------------------------
// FLASH definitions
// ---------------------------------------------------------
/* Transfer Command bits */
/* All byte based commands consist of:
* - Command byte
* - Length lsb
* - Length msb
*
* If data out is enabled the data follows after the above command bytes,
* otherwise no additional data is needed.
* - Data * n
*
* All bit based commands consist of:
* - Command byte
* - Length
*
* If data out is enabled a byte containing bitst to transfer follows.
* Otherwise no additional data is needed. Only up to 8 bits can be transferred
* per transaction when in bit mode.
*/
/* b 0000 0000
* |||| |||`- Data out negative enable. Update DO on negative clock edge.
* |||| ||`-- Bit count enable. When reset count represents bytes.
* |||| |`--- Data in negative enable. Latch DI on negative clock edge.
* |||| `---- LSB enable. When set clock data out LSB first.
* ||||
* |||`------ Data out enable
* ||`------- Data in enable
* |`-------- TMS mode enable
* `--------- Special command mode enable. See mpsse_cmd enum.
*/
#define MC_DATA_TMS (0x40) /* When set use TMS mode */
#define MC_DATA_IN (0x20) /* When set read data (Data IN) */
#define MC_DATA_OUT (0x10) /* When set write data (Data OUT) */
#define MC_DATA_LSB (0x08) /* When set input/output data LSB first. */
#define MC_DATA_ICN (0x04) /* When set receive data on negative clock edge */
#define MC_DATA_BITS (0x02) /* When set count bits not bytes */
#define MC_DATA_OCN (0x01) /* When set update data on negative clock edge */
/* Flash command definitions */
/* This command list is based on the Winbond W25Q128JV Datasheet */
enum flash_cmd {
FC_WE = 0x06, /* Write Enable */
FC_SRWE = 0x50, /* Volatile SR Write Enable */
FC_WD = 0x04, /* Write Disable */
FC_RPD = 0xAB, /* Release Power-Down, returns Device ID */
FC_MFGID = 0x90, /* Read Manufacturer/Device ID */
FC_JEDECID = 0x9F, /* Read JEDEC ID */
FC_UID = 0x4B, /* Read Unique ID */
FC_RD = 0x03, /* Read Data */
FC_FR = 0x0B, /* Fast Read */
FC_PP = 0x02, /* Page Program */
FC_SE = 0x20, /* Sector Erase 4kb */
FC_BE32 = 0x52, /* Block Erase 32kb */
FC_BE64 = 0xD8, /* Block Erase 64kb */
FC_CE = 0xC7, /* Chip Erase */
FC_RSR1 = 0x05, /* Read Status Register 1 */
FC_WSR1 = 0x01, /* Write Status Register 1 */
FC_RSR2 = 0x35, /* Read Status Register 2 */
FC_WSR2 = 0x31, /* Write Status Register 2 */
FC_RSR3 = 0x15, /* Read Status Register 3 */
FC_WSR3 = 0x11, /* Write Status Register 3 */
FC_RSFDP = 0x5A, /* Read SFDP Register */
FC_ESR = 0x44, /* Erase Security Register */
FC_PSR = 0x42, /* Program Security Register */
FC_RSR = 0x48, /* Read Security Register */
FC_GBL = 0x7E, /* Global Block Lock */
FC_GBU = 0x98, /* Global Block Unlock */
FC_RBL = 0x3D, /* Read Block Lock */
FC_RPR = 0x3C, /* Read Sector Protection Registers (adesto) */
FC_IBL = 0x36, /* Individual Block Lock */
FC_IBU = 0x39, /* Individual Block Unlock */
FC_EPS = 0x75, /* Erase / Program Suspend */
FC_EPR = 0x7A, /* Erase / Program Resume */
FC_PD = 0xB9, /* Power-down */
FC_QPI = 0x38, /* Enter QPI mode */
FC_ERESET = 0x66, /* Enable Reset */
FC_RESET = 0x99, /* Reset Device */
};
Spi::Spi() {
ftHandle = 0;
active = false;
verbose = false;
}
FT_STATUS Spi::connect(unsigned int devNumber) {
return FT_Open(devNumber, &ftHandle);
}
FT_STATUS Spi::disconnect() {
active = false;
return FT_Close(ftHandle);
}
bool Spi::initialize() {
BYTE byInputBuffer[1024]; // Buffer to hold data read from the FT2232H
DWORD dwNumBytesToRead = 0; // Number of bytes available to read in the driver's input buffer
DWORD dwNumBytesRead = 0; // Count of actual bytes read - used with FT_Read
FT_STATUS ftStatus;
ftStatus = FT_ResetDevice(ftHandle);
//Reset USB device
//Purge USB receive buffer first by reading out all old data from FT2232H receive buffer
ftStatus |= FT_GetQueueStatus(ftHandle, &dwNumBytesToRead); // Get the number of bytes in the FT2232H receive buffer
if ((ftStatus == FT_OK) && (dwNumBytesToRead > 0))
FT_Read(ftHandle, &byInputBuffer, dwNumBytesToRead, &dwNumBytesRead);//Read out the data from FT2232H receive buffer
ftStatus |= FT_SetUSBParameters(ftHandle, 65536, 65535);//Set USB request transfer sizes to 64K
ftStatus |= FT_SetChars(ftHandle, false, 0, false, 0); //Disable event and error characters
ftStatus |= FT_SetTimeouts(ftHandle, 0, 5000); //Sets the read and write timeouts in milliseconds
ftStatus |= FT_SetLatencyTimer(ftHandle, 1); //Set the latency timer 1ms
ftStatus |= FT_SetBitMode(ftHandle, 0x0, 0x00); //Reset controller
ftStatus |= FT_SetBitMode(ftHandle, 0x0, 0x02); //Enable MPSSE mode
if (ftStatus != FT_OK) {
cerr << "Failed to set initial configuration!" << endl;
return false;
}
std::this_thread::sleep_for(std::chrono::milliseconds(100)); // Wait for all the USB stuff to complete and work
if (!sync_mpsse()){
cerr << "Failed to sync with MPSSE!" << endl;
return false;
}
if (!config_spi()){
cerr << "Failed to set SPI configuration!" << endl;
return false;
}
active = true;
return true;
}
bool Spi::sync_mpsse() {
BYTE byOutputBuffer[8]; // Buffer to hold MPSSE commands and data to be sent to the FT2232H
BYTE byInputBuffer[8]; // Buffer to hold data read from the FT2232H
DWORD dwCount = 0; // General loop index
DWORD dwNumBytesToSend = 0; // Index to the output buffer
DWORD dwNumBytesSent = 0; // Count of actual bytes sent - used with FT_Write
DWORD dwNumBytesToRead = 0; // Number of bytes available to read in the driver's input buffer
DWORD dwNumBytesRead = 0; // Count of actual bytes read - used with FT_Read
FT_STATUS ftStatus;
byOutputBuffer[dwNumBytesToSend++] = 0xAA; //'\xAA';
//Add bogus command xAA to the queue
ftStatus = FT_Write(ftHandle, byOutputBuffer, dwNumBytesToSend,
&dwNumBytesSent);
// Send off the BAD commands
dwNumBytesToSend = 0; // Reset output buffer pointer
do {
ftStatus = FT_GetQueueStatus(ftHandle, &dwNumBytesToRead);
// Get the number of bytes in the device input buffer
} while ((dwNumBytesToRead == 0) && (ftStatus == FT_OK));
if (dwNumBytesRead > 8) {
cerr << "Input buffer too small in sync_mpsse()!" << endl;
return false;
}
//or Timeout
bool bCommandEchod = false;
ftStatus = FT_Read(ftHandle, &byInputBuffer, dwNumBytesToRead,
&dwNumBytesRead);
//Read out the data from input buffer
for (dwCount = 0; dwCount < dwNumBytesRead - 1; dwCount++)
//Check if Bad command and echo command received
{
if ((byInputBuffer[dwCount] == 0xFA)
&& (byInputBuffer[dwCount + 1] == 0xAA)) {
bCommandEchod = true;
break;
}
}
return bCommandEchod;
}
bool Spi::config_spi() {
FT_STATUS ftStatus;
BYTE byOutputBuffer[64]; // Buffer to hold MPSSE commands and data to be sent to the FT2232H
DWORD dwNumBytesToSend = 0; // Index to the output buffer
DWORD dwNumBytesSent = 0; // Count of actual bytes sent - used with FT_Write
DWORD dwClockDivisor = 0x0; // Value of clock divisor, SCL Frequency = 60/((1+0x0)*2) (MHz) = 30MHz
// -----------------------------------------------------------
// Configure the MPSSE settings for SPI
// Multiple commands can be sent to the MPSSE with one FT_Write
// -----------------------------------------------------------
dwNumBytesToSend = 0; // Start with a fresh index
// Set up the Hi-Speed specific commands for the FTx232H
byOutputBuffer[dwNumBytesToSend++] = 0x8A;
// Use 60MHz master clock (disable divide by 5)
byOutputBuffer[dwNumBytesToSend++] = 0x97;
// Turn off adaptive clocking (may be needed for ARM)
byOutputBuffer[dwNumBytesToSend++] = 0x8D;
// Disable three-phase clocking
ftStatus = FT_Write(ftHandle, byOutputBuffer, dwNumBytesToSend,
&dwNumBytesSent);
// Send off the HS-specific commands
if (ftStatus != FT_OK)
return false;
dwNumBytesToSend = 0; // Reset output buffer pointer
// Set initial states of the MPSSE interface - low byte, both pin directions and output values
// Pin name Signal Direction Config Initial State Config
// ADBUS0 SCK output 1 low 0
// ADBUS1 MOSI output 1 low 0
// ADBUS2 MISO input 0 low 0
// ADBUS3 NC output 1 low 0
// ADBUS4 SS output 1 low 0
// ADBUS5 NC output 1 low 0
// ADBUS6 CDONE input 0 low 0
// ADBUS7 CRESET output 1 low 0
byOutputBuffer[dwNumBytesToSend++] = 0x80;
// Set data bits low-byte of MPSSE port
byOutputBuffer[dwNumBytesToSend++] = 0x00;
// Initial state config above
byOutputBuffer[dwNumBytesToSend++] = 0xBB;
// Direction config above
ftStatus = FT_Write(ftHandle, byOutputBuffer, dwNumBytesToSend,
&dwNumBytesSent);
// Send off the low GPIO config commands
if (ftStatus != FT_OK)
return false;
dwNumBytesToSend = 0; // Reset output buffer pointer
// Set initial states of the MPSSE interface - high byte, both pin directions and output values
// Pin name Signal Direction Config Initial State Config
// ACBUS0 GPIOH0 input 0 0
// ACBUS1 GPIOH1 input 0 0
// ACBUS2 GPIOH2 input 0 0
// ACBUS3 GPIOH3 input 0 0
// ACBUS4 GPIOH4 input 0 0
// ACBUS5 GPIOH5 input 0 0
// ACBUS6 GPIOH6 input 0 0
// ACBUS7 GPIOH7 input 0 0
byOutputBuffer[dwNumBytesToSend++] = 0x82;
// Set data bits low-byte of MPSSE port
byOutputBuffer[dwNumBytesToSend++] = 0x00;
// Initial state config above
byOutputBuffer[dwNumBytesToSend++] = 0x00;
// Direction config above
ftStatus = FT_Write(ftHandle, byOutputBuffer, dwNumBytesToSend,
&dwNumBytesSent);
// Send off the high GPIO config commands
if (ftStatus != FT_OK)
return false;
dwNumBytesToSend = 0; // Reset output buffer pointer
// Set TCK frequency
// TCK = 60MHz /((1 + [(1 +0xValueH*256) OR 0xValueL])*2)
byOutputBuffer[dwNumBytesToSend++] = 0x86;
//Command to set clock divisor
byOutputBuffer[dwNumBytesToSend++] = dwClockDivisor & 0xFF;
//Set 0xValueL of clock divisor
byOutputBuffer[dwNumBytesToSend++] = (dwClockDivisor >> 8) & 0xFF;
//Set 0xValueH of clock divisor
ftStatus = FT_Write(ftHandle, byOutputBuffer, dwNumBytesToSend,
&dwNumBytesSent);
// Send off the clock divisor commands
if (ftStatus != FT_OK)
return false;
dwNumBytesToSend = 0; // Reset output buffer pointer
// Disable internal loop-back
byOutputBuffer[dwNumBytesToSend++] = 0x85;
// Disable loopback
ftStatus = FT_Write(ftHandle, byOutputBuffer, dwNumBytesToSend,
&dwNumBytesSent);
// Send off the loopback command
if (ftStatus != FT_OK)
return false;
return true;
}
void Spi::check_rx() {
FT_STATUS ftStatus;
BYTE byInputBuffer[1];
DWORD dwNumBytesRead = 0;
while (1) {
ftStatus = FT_Read(ftHandle, &byInputBuffer, 1, &dwNumBytesRead);
if (ftStatus != FT_OK)
break;
cerr << "Unexpected rx byte: " << byInputBuffer[0] << endl;
}
}
void Spi::error(int status) {
check_rx();
cerr << "ABORT." << endl;
if (active)
disconnect();
exit(status);
}
BYTE Spi::recv_byte() {
FT_STATUS ftStatus;
BYTE byInputBuffer[1];
DWORD dwNumBytesRead = 0;
while (1) {
ftStatus = FT_Read(ftHandle, &byInputBuffer, 1, &dwNumBytesRead);
if (ftStatus != FT_OK) {
cerr << "Read error." << endl;
error(2);
}
if (dwNumBytesRead == 1)
break;
std::this_thread::sleep_for(std::chrono::microseconds(100));
}
return byInputBuffer[0];
}
void Spi::send_byte(uint8_t data) {
FT_STATUS ftStatus;
BYTE byOutputBuffer[1];
DWORD dwNumBytesSent = 0;
byOutputBuffer[0] = data;
ftStatus = FT_Write(ftHandle, byOutputBuffer, 1, &dwNumBytesSent);
if (ftStatus != FT_OK) {
cerr << "Write error!" << endl;
error(2);
}
if (dwNumBytesSent != 1) {
cerr << "Write error (single byte, received " << dwNumBytesSent
<< ", expected 1)." << endl;
error(2);
}
}
void Spi::send_spi(uint8_t *data, int n) {
if (n < 1)
return;
/* Output only, update data on negative clock edge. */
send_byte(MC_DATA_OUT | MC_DATA_OCN);
send_byte(n - 1);
send_byte((n - 1) >> 8);
FT_STATUS ftStatus;
DWORD dwNumBytesSent = 0;
ftStatus = FT_Write(ftHandle, data, n, &dwNumBytesSent);
if (ftStatus != FT_OK) {
cerr << "Write error!" << endl;
error(2);
}
if (dwNumBytesSent != (unsigned int) n) {
fprintf(stderr, "Write error (chunk, rc=%u, expected %d).\n",
dwNumBytesSent, n);
error(2);
}
}
void Spi::xfer_spi(uint8_t *data, int n) {
if (n < 1)
return;
/* Input and output, update data on negative edge read on positive. */
send_byte(MC_DATA_IN | MC_DATA_OUT | MC_DATA_OCN);
send_byte(n - 1);
send_byte((n - 1) >> 8);
FT_STATUS ftStatus;
DWORD dwNumBytesSent = 0;
ftStatus = FT_Write(ftHandle, data, n, &dwNumBytesSent);
if (ftStatus != FT_OK) {
cerr << "Write error!" << endl;
error(2);
}
if (dwNumBytesSent != (unsigned int) n) {
fprintf(stderr, "Write error (chunk, rc=%u, expected %d).\n",
dwNumBytesSent, n);
error(2);
}
for (int i = 0; i < n; i++)
data[i] = recv_byte();
}
uint8_t Spi::xfer_spi_bits(uint8_t data, int n) {
if (n < 1)
return 0;
/* Input and output, update data on negative edge read on positive, bits. */
send_byte(MC_DATA_IN | MC_DATA_OUT | MC_DATA_OCN | MC_DATA_BITS);
send_byte(n - 1);
send_byte(data);
return recv_byte();
}
void Spi::set_gpio(int slavesel_b, int creset_b) {
uint8_t gpio = 0;
if (slavesel_b) {
// ADBUS4 (GPIOL0)
gpio |= 0x10;
}
if (creset_b) {
// ADBUS7 (GPIOL3)
gpio |= 0x80;
}
send_byte(MC_SETB_LOW);
send_byte(gpio); /* Value */
send_byte(0x93); /* Direction */
}
int Spi::get_cdone() {
uint8_t data;
send_byte(MC_READB_LOW);
data = recv_byte();
// ADBUS6 (GPIOL2)
return (data & 0x40) != 0;
}
// ---------------------------------------------------------
// FLASH function implementations
// ---------------------------------------------------------
// the FPGA reset is released so also FLASH chip select should be deasserted
void Spi::flash_release_reset() {
set_gpio(1, 1);
}
// FLASH chip select assert
// should only happen while FPGA reset is asserted
void Spi::flash_chip_select() {
set_gpio(0, 0);
}
// FLASH chip select deassert
void Spi::flash_chip_deselect() {
set_gpio(1, 0);
}
// SRAM reset is the same as flash_chip_select()
// For ease of code reading we use this function instead
void Spi::sram_reset() {
// Asserting chip select and reset lines
set_gpio(0, 0);
}
// SRAM chip select assert
// When accessing FPGA SRAM the reset should be released
void Spi::sram_chip_select() {
set_gpio(0, 1);
}
void Spi::flash_read_id() {
/* JEDEC ID structure:
* Byte No. | Data Type
* ---------+----------
* 0 | FC_JEDECID Request Command
* 1 | MFG ID
* 2 | Dev ID 1
* 3 | Dev ID 2
* 4 | Ext Dev Str Len
*/
uint8_t data[260] = { FC_JEDECID };
int len = 5; // command + 4 response bytes
if (verbose)
fprintf(stdout, "read flash ID..\n");
flash_chip_select();
// Write command and read first 4 bytes
xfer_spi(data, len);
if (data[4] == 0xFF)
fprintf(stderr, "Extended Device String Length is 0xFF, "
"this is likely a read error. Ignorig...\n");
else {
// Read extended JEDEC ID bytes
if (data[4] != 0) {
len += data[4];
xfer_spi(data + 5, len - 5);
}
}
flash_chip_deselect();
fprintf(stdout, "flash ID:");
for (int i = 1; i < len; i++)
fprintf(stdout, " 0x%02X", data[i]);
fprintf(stdout, "\n");
}
void Spi::flash_reset() {
flash_chip_select();
xfer_spi_bits(0xFF, 8);
flash_chip_deselect();
flash_chip_select();
xfer_spi_bits(0xFF, 2);
flash_chip_deselect();
}
void Spi::flash_power_up() {
uint8_t data_rpd[1] = { FC_RPD };
flash_chip_select();
xfer_spi(data_rpd, 1);
flash_chip_deselect();
}
void Spi::flash_power_down() {
uint8_t data[1] = { FC_PD };
flash_chip_select();
xfer_spi(data, 1);
flash_chip_deselect();
}
uint8_t Spi::flash_read_status() {
uint8_t data[2] = { FC_RSR1 };
flash_chip_select();
xfer_spi(data, 2);
flash_chip_deselect();
if (verbose) {
fprintf(stdout, "SR1: 0x%02X\n", data[1]);
fprintf(stdout, " - SPRL: %s\n",
((data[1] & (1 << 7)) == 0) ? "unlocked" : "locked");
fprintf(stdout, " - SPM: %s\n",
((data[1] & (1 << 6)) == 0) ?
"Byte/Page Prog Mode" : "Sequential Prog Mode");
fprintf(stdout, " - EPE: %s\n",
((data[1] & (1 << 5)) == 0) ?
"Erase/Prog success" : "Erase/Prog error");
fprintf(stdout, "- SPM: %s\n",
((data[1] & (1 << 4)) == 0) ?
"~WP asserted" : "~WP deasserted");
fprintf(stdout, " - SWP: ");
switch ((data[1] >> 2) & 0x3) {
case 0:
fprintf(stdout, "All sectors unprotected\n");
break;
case 1:
fprintf(stdout, "Some sectors protected\n");
break;
case 2:
fprintf(stdout, "Reserved (xxxx 10xx)\n");
break;
case 3:
fprintf(stdout, "All sectors protected\n");
break;
}
fprintf(stdout, " - WEL: %s\n",
((data[1] & (1 << 1)) == 0) ?
"Not write enabled" : "Write enabled");
fprintf(stdout, " - ~RDY: %s\n",
((data[1] & (1 << 0)) == 0) ? "Ready" : "Busy");
}
std::this_thread::sleep_for(std::chrono::milliseconds(1));
return data[1];
}
void Spi::flash_write_enable() {
if (verbose) {
fprintf(stdout, "status before enable:\n");
flash_read_status();
}
if (verbose)
fprintf(stdout, "write enable..\n");
uint8_t data[1] = { FC_WE };
flash_chip_select();
xfer_spi(data, 1);
flash_chip_deselect();
if (verbose) {
fprintf(stdout, "status after enable:\n");
flash_read_status();
}
}
void Spi::flash_bulk_erase() {
if (verbose)
fprintf(stdout, "bulk erase..\n");
uint8_t data[1] = { FC_CE };
flash_chip_select();
xfer_spi(data, 1);
flash_chip_deselect();
}
void Spi::flash_64kB_sector_erase(int addr) {
if (verbose)
fprintf(stdout, "erase 64kB sector at 0x%06X..\n", addr);
uint8_t command[4] = { FC_BE64, (uint8_t) (addr >> 16),
(uint8_t) (addr >> 8), (uint8_t) addr };
flash_chip_select();
send_spi(command, 4);
flash_chip_deselect();
}
void Spi::flash_prog(int addr, uint8_t *data, int n) {
if (verbose)
fprintf(stdout, "prog 0x%06X +0x%03X..\n", addr, n);
uint8_t command[4] = { FC_PP, (uint8_t) (addr >> 16), (uint8_t) (addr >> 8),
(uint8_t) addr };
flash_chip_select();
send_spi(command, 4);
send_spi(data, n);
flash_chip_deselect();
if (verbose)
for (int i = 0; i < n; i++)
fprintf(stderr, "%02x%c", data[i],
i == n - 1 || i % 32 == 31 ? '\n' : ' ');
}
void Spi::flash_read(int addr, uint8_t *data, int n) {
if (verbose)
fprintf(stdout, "read 0x%06X +0x%03X..\n", addr, n);
uint8_t command[4] = { FC_RD, (uint8_t) (addr >> 16), (uint8_t) (addr >> 8),
(uint8_t) addr };
flash_chip_select();
send_spi(command, 4);
memset(data, 0, n);
xfer_spi(data, n);
flash_chip_deselect();
if (verbose)
for (int i = 0; i < n; i++)
fprintf(stderr, "%02x%c", data[i],
i == n - 1 || i % 32 == 31 ? '\n' : ' ');
}
void Spi::flash_wait() {
if (verbose)
fprintf(stderr, "waiting..");
int count = 0;
while (1) {
uint8_t data[2] = { FC_RSR1 };
flash_chip_select();
xfer_spi(data, 2);
flash_chip_deselect();
if ((data[1] & 0x01) == 0) {
if (count < 2) {
count++;
if (verbose) {
fprintf(stderr, "r");
fflush(stderr);
}
} else {
if (verbose) {
fprintf(stderr, "R");
fflush(stderr);
}
break;
}
} else {
if (verbose) {
fprintf(stderr, ".");
fflush(stderr);
}
count = 0;
}
std::this_thread::sleep_for(std::chrono::milliseconds(1));
}
if (verbose)
fprintf(stderr, "\n");
}
void Spi::flash_disable_protection() {
fprintf(stderr, "disable flash protection...\n");
// Write Status Register 1 <- 0x00
uint8_t data[2] = { FC_WSR1, 0x00 };
flash_chip_select();
xfer_spi(data, 2);
flash_chip_deselect();
flash_wait();
// Read Status Register 1
data[0] = FC_RSR1;
flash_chip_select();
xfer_spi(data, 2);
flash_chip_deselect();
if (data[1] != 0x00)
fprintf(stderr,
"failed to disable protection, SR now equal to 0x%02x (expected 0x00)\n",
data[1]);
}
bool Spi::eraseFlash() {
fprintf(stdout, "reset..\n");
flash_chip_deselect();
std::this_thread::sleep_for(std::chrono::milliseconds(250));
fprintf(stdout, "cdone: %s\n", get_cdone() ? "high" : "low");
flash_reset();
flash_power_up();
flash_read_id();
flash_write_enable();
flash_bulk_erase();
flash_wait();
// ---------------------------------------------------------
// Reset
// ---------------------------------------------------------
flash_power_down();
set_gpio(1, 1);
std::this_thread::sleep_for(std::chrono::milliseconds(250));
fprintf(stdout, "cdone: %s\n", get_cdone() ? "high" : "low");
return true;
}
bool Spi::writeBin(string filename) {
int rw_offset = 0;
FILE *f = NULL;
long file_size = -1;
f = fopen(filename.c_str(), "rb");
if (f == NULL) {
fprintf(stderr, "Can't open '%s' for reading: ", filename.c_str());
return false;
}
if (fseek(f, 0L, SEEK_END) != -1) {
file_size = ftell(f);
if (file_size == -1) {
fprintf(stderr, "%s: ftell: ", filename.c_str());
return false;
}
if (fseek(f, 0L, SEEK_SET) == -1) {
fprintf(stderr, "%s: fseek: ", filename.c_str());
return false;
}
} else {
FILE *pipe = f;
f = tmpfile();
if (f == NULL) {
fprintf(stderr, "can't open temporary file\n");
return false;
}
file_size = 0;
while (true) {
static unsigned char buffer[4096];
size_t rc = fread(buffer, 1, 4096, pipe);
if (rc <= 0)
break;
size_t wc = fwrite(buffer, 1, rc, f);
if (wc != rc) {
fprintf(stderr, "can't write to temporary file\n");
return false;
}
file_size += rc;
}
fclose(pipe);
/* now seek to the beginning so we can
start reading again */
fseek(f, 0, SEEK_SET);
}
cout << "Resetting..." << endl;
flash_chip_deselect();
std::this_thread::sleep_for(std::chrono::milliseconds(250));
cout << "cdone: " << (get_cdone() ? "high" : "low") << endl;
flash_reset();
flash_power_up();
flash_read_id();
int begin_addr = rw_offset & ~0xffff;
int end_addr = (rw_offset + file_size + 0xffff) & ~0xffff;
for (int addr = begin_addr; addr < end_addr; addr += 0x10000) {
flash_write_enable();
flash_64kB_sector_erase(addr);
if (verbose) {
fprintf(stderr, "Status after block erase:\n");
flash_read_status();
}
flash_wait();
}
cout << "Programming... ";
for (int rc, addr = 0; true; addr += rc) {
uint8_t buffer[256];
int page_size = 256 - (rw_offset + addr) % 256;
rc = fread(buffer, 1, page_size, f);
if (rc <= 0)
break;
flash_write_enable();
flash_prog(rw_offset + addr, buffer, rc);
flash_wait();
}
cout << "Done." << endl;
/* seek to the beginning for second pass */
fseek(f, 0, SEEK_SET);
// ---------------------------------------------------------
// Reset
// ---------------------------------------------------------
flash_power_down();
set_gpio(1, 1);
std::this_thread::sleep_for(std::chrono::milliseconds(250));
cout << "cdone: " << (get_cdone() ? "high" : "low") << endl;
cout << "Done." << endl;
if (f != NULL && f != stdin && f != stdout)
fclose(f);
return true;
}

66
alchitry-loader/src/spi.h Normal file
View File

@ -0,0 +1,66 @@
/*
* spi.h
*
* Created on: Feb 5, 2019
* Author: justin
*/
#ifndef SPI_H_
#define SPI_H_
#include "ftd2xx.h"
#include <unistd.h>
#include <string>
using namespace std;
class Spi {
FT_HANDLE ftHandle;
unsigned int uiDevIndex = 0xF; // The device in the list that is used
bool active;
bool verbose;
public:
Spi();
FT_STATUS connect(unsigned int);
FT_STATUS disconnect();
bool initialize();
bool eraseFlash();
bool writeBin(string);
private:
bool sync_mpsse();
bool config_spi();
static void hexToByte(string, BYTE*);
bool flush();
bool compareHexString(string, string, string);
void check_rx();
void error(int);
BYTE recv_byte();
void send_byte(BYTE data);
void send_spi(uint8_t *data, int n);
void xfer_spi(uint8_t *data, int n);
uint8_t xfer_spi_bits(uint8_t data, int n);
void set_gpio(int slavesel_b, int creset_b);
int get_cdone();
void flash_release_reset();
void flash_chip_select();
void flash_chip_deselect();
void sram_reset();
void sram_chip_select();
void flash_read_id();
void flash_reset();
void flash_power_up();
void flash_power_down();
uint8_t flash_read_status();
void flash_write_enable();
void flash_bulk_erase();
void flash_64kB_sector_erase(int addr);
void flash_prog(int addr, uint8_t *data, int n);
void flash_read(int addr, uint8_t *data, int n);
void flash_wait();
void flash_disable_protection();
};
#endif /* SPI_H_ */