add alchitry loader

This commit is contained in:
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,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_ */