add alchitry loader
This commit is contained in:
parent
0226d314ec
commit
30d9a2d7c8
21
alchitry-loader/LICENSE.txt
Normal file
21
alchitry-loader/LICENSE.txt
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2019 alchitry
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
BIN
alchitry-loader/au_loader.bin
Normal file
BIN
alchitry-loader/au_loader.bin
Normal file
Binary file not shown.
BIN
alchitry-loader/lib/linux/libftd2xx.a
Normal file
BIN
alchitry-loader/lib/linux/libftd2xx.a
Normal file
Binary file not shown.
BIN
alchitry-loader/lib/macos/libftd2xx.a
Normal file
BIN
alchitry-loader/lib/macos/libftd2xx.a
Normal file
Binary file not shown.
BIN
alchitry-loader/lib/windows/ftbusui.dll
Normal file
BIN
alchitry-loader/lib/windows/ftbusui.dll
Normal file
Binary file not shown.
BIN
alchitry-loader/lib/windows/ftcserco.dll
Normal file
BIN
alchitry-loader/lib/windows/ftcserco.dll
Normal file
Binary file not shown.
BIN
alchitry-loader/lib/windows/ftd2xx.dll
Normal file
BIN
alchitry-loader/lib/windows/ftd2xx.dll
Normal file
Binary file not shown.
BIN
alchitry-loader/lib/windows/ftd2xx.lib
Normal file
BIN
alchitry-loader/lib/windows/ftd2xx.lib
Normal file
Binary file not shown.
BIN
alchitry-loader/lib/windows/ftdibus.sys
Normal file
BIN
alchitry-loader/lib/windows/ftdibus.sys
Normal file
Binary file not shown.
BIN
alchitry-loader/lib/windows/ftlang.dll
Normal file
BIN
alchitry-loader/lib/windows/ftlang.dll
Normal file
Binary file not shown.
BIN
alchitry-loader/lib/windows/ftser2k.sys
Normal file
BIN
alchitry-loader/lib/windows/ftser2k.sys
Normal file
Binary file not shown.
BIN
alchitry-loader/lib/windows/ftserui2.dll
Normal file
BIN
alchitry-loader/lib/windows/ftserui2.dll
Normal file
Binary file not shown.
24
alchitry-loader/makefile
Normal file
24
alchitry-loader/makefile
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
sources = $(wildcard src/*.cpp)
|
||||||
|
objects = $(sources:.cpp=.o)
|
||||||
|
static_libs = lib/macos/libftd2xx.a
|
||||||
|
libs = -lpthread
|
||||||
|
|
||||||
|
CXXFLAGS = -std=c++11
|
||||||
|
|
||||||
|
UNAME := $(shell uname)
|
||||||
|
# Assume target is Mac OS if build host is Mac OS; any other host targets Linux
|
||||||
|
ifeq ($(UNAME), Darwin)
|
||||||
|
libs += -lobjc -framework IOKit -framework CoreFoundation
|
||||||
|
else
|
||||||
|
libs += -lrt
|
||||||
|
endif
|
||||||
|
|
||||||
|
all: loader
|
||||||
|
|
||||||
|
loader: $(objects)
|
||||||
|
$(CXX) $(LDFLAGS) -o $@ $^ $(static_libs) $(libs)
|
||||||
|
|
||||||
|
.PHONY: clean
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm -rf loader $(objects)
|
611
alchitry-loader/src/Alchitry_Loader.cpp
Normal file
611
alchitry-loader/src/Alchitry_Loader.cpp
Normal 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;
|
||||||
|
}
|
||||||
|
|
154
alchitry-loader/src/WinTypes.h
Normal file
154
alchitry-loader/src/WinTypes.h
Normal 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__ */
|
272
alchitry-loader/src/config_type.cpp
Normal file
272
alchitry-loader/src/config_type.cpp
Normal 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;
|
||||||
|
}
|
177
alchitry-loader/src/config_type.h
Normal file
177
alchitry-loader/src/config_type.h
Normal 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
1443
alchitry-loader/src/ftd2xx.h
Normal file
File diff suppressed because it is too large
Load Diff
781
alchitry-loader/src/jtag.cpp
Normal file
781
alchitry-loader/src/jtag.cpp
Normal 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;
|
||||||
|
}
|
40
alchitry-loader/src/jtag.h
Normal file
40
alchitry-loader/src/jtag.h
Normal 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_ */
|
119
alchitry-loader/src/jtag_fsm.cpp
Normal file
119
alchitry-loader/src/jtag_fsm.cpp
Normal 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;
|
||||||
|
}
|
52
alchitry-loader/src/jtag_fsm.h
Normal file
52
alchitry-loader/src/jtag_fsm.h
Normal 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_ */
|
388
alchitry-loader/src/loader.cpp
Normal file
388
alchitry-loader/src/loader.cpp
Normal 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;
|
||||||
|
}
|
||||||
|
|
77
alchitry-loader/src/loader.h
Normal file
77
alchitry-loader/src/loader.h
Normal 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_ */
|
414
alchitry-loader/src/mingw.thread.h
Normal file
414
alchitry-loader/src/mingw.thread.h
Normal 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
940
alchitry-loader/src/spi.cpp
Normal 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
66
alchitry-loader/src/spi.h
Normal 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_ */
|
Loading…
Reference in New Issue
Block a user