mirror of
https://github.com/trabucayre/openFPGALoader.git
synced 2026-09-04 00:31:29 +02:00
Update cmake according to trabucayre request https://github.com/trabucayre/openFPGALoader/pull/17
This commit is contained in:
@@ -0,0 +1,64 @@
|
||||
#include "altera.hpp"
|
||||
#include "ftdijtag.hpp"
|
||||
#include "device.hpp"
|
||||
#include "epcq.hpp"
|
||||
|
||||
#define IDCODE 6
|
||||
#define IRLENGTH 10
|
||||
#define BIT_FOR_FLASH "/usr/local/share/openFPGALoader/test_sfl.svf"
|
||||
|
||||
Altera::Altera(FtdiJtag *jtag, std::string filename, bool verbose):
|
||||
Device(jtag, filename, verbose), _svf(_jtag, _verbose)
|
||||
{
|
||||
if (_filename != "") {
|
||||
if (_file_extension == "svf")
|
||||
_mode = Device::MEM_MODE;
|
||||
else
|
||||
_mode = Device::SPI_MODE;
|
||||
}
|
||||
}
|
||||
Altera::~Altera()
|
||||
{}
|
||||
void Altera::reset()
|
||||
{
|
||||
/* PULSE_NCONFIG */
|
||||
unsigned char tx_buff[2] = {0x01, 0x00};
|
||||
_jtag->set_state(FtdiJtag::TEST_LOGIC_RESET);
|
||||
_jtag->shiftIR(tx_buff, NULL, IRLENGTH);
|
||||
_jtag->toggleClk(1);
|
||||
_jtag->set_state(FtdiJtag::TEST_LOGIC_RESET);
|
||||
}
|
||||
|
||||
void Altera::program(unsigned int offset)
|
||||
{
|
||||
if (_mode == Device::NONE_MODE)
|
||||
return;
|
||||
/* in all case we consider svf is mandatory
|
||||
* MEM_MODE : svf file provided for constructor
|
||||
* is the bitstream to use
|
||||
* SPI_MODE : svf file provided is bridge to have
|
||||
* access to the SPI flash
|
||||
*/
|
||||
/* mem mode -> svf */
|
||||
if (_mode == Device::MEM_MODE) {
|
||||
_svf.parse(_filename);
|
||||
} else if (_mode == Device::SPI_MODE) {
|
||||
/* GGM: TODO: fix this issue */
|
||||
EPCQ epcq(_jtag->vid(), _jtag->pid(), 2, 6000000);
|
||||
_svf.parse(BIT_FOR_FLASH);
|
||||
epcq.program(offset, _filename, (_file_extension == "rpd")? true:false);
|
||||
reset();
|
||||
}
|
||||
}
|
||||
int Altera::idCode()
|
||||
{
|
||||
unsigned char tx_data = IDCODE;
|
||||
unsigned char rx_data[4];
|
||||
_jtag->go_test_logic_reset();
|
||||
_jtag->shiftIR(&tx_data, NULL, IRLENGTH);
|
||||
_jtag->shiftDR(NULL, rx_data, 32);
|
||||
return ((rx_data[0] & 0x000000ff) |
|
||||
((rx_data[1] << 8) & 0x0000ff00) |
|
||||
((rx_data[2] << 16) & 0x00ff0000) |
|
||||
((rx_data[3] << 24) & 0xff000000));
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
#ifndef ALTERA_HPP
|
||||
#define ALTERA_HPP
|
||||
|
||||
#include "bitparser.hpp"
|
||||
#include "device.hpp"
|
||||
#include "ftdijtag.hpp"
|
||||
#include "svf_jtag.hpp"
|
||||
|
||||
class Altera: public Device {
|
||||
public:
|
||||
Altera(FtdiJtag *jtag, std::string filename, bool verbose);
|
||||
~Altera();
|
||||
|
||||
void program(unsigned int offset = 0);
|
||||
int idCode();
|
||||
void reset() override;
|
||||
private:
|
||||
SVF_jtag _svf;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,129 @@
|
||||
#include "bitparser.hpp"
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <iostream>
|
||||
#include <arpa/inet.h>
|
||||
|
||||
using namespace std;
|
||||
|
||||
#define display(...) \
|
||||
do { if (_verbose) fprintf(stdout, __VA_ARGS__);} while(0)
|
||||
|
||||
BitParser::BitParser(string filename, bool verbose):
|
||||
ConfigBitstreamParser(filename, ConfigBitstreamParser::BIN_MODE,
|
||||
verbose), fieldA(), part_name(), date(), hour(),
|
||||
design_name(), userID(), toolVersion()
|
||||
{
|
||||
}
|
||||
BitParser::~BitParser()
|
||||
{
|
||||
}
|
||||
|
||||
int BitParser::parseField()
|
||||
{
|
||||
int ret = 1;
|
||||
short length;
|
||||
char tmp[64];
|
||||
int pos, prev_pos;
|
||||
|
||||
/* type */
|
||||
uint8_t type;
|
||||
_fd.read((char *)&type, sizeof(uint8_t));
|
||||
|
||||
if (type != 'e') {
|
||||
_fd.read((char*)&length, sizeof(uint16_t));
|
||||
length = ntohs(length);
|
||||
} else {
|
||||
length = 4;
|
||||
}
|
||||
_fd.read(tmp, sizeof(uint8_t)*length);
|
||||
if (_verbose) {
|
||||
for (int i = 0; i < length; i++)
|
||||
printf("%c", tmp[i]);
|
||||
printf("\n");
|
||||
}
|
||||
switch (type) {
|
||||
case 'a': /* design name:userid:synthesize tool version */
|
||||
fieldA=(tmp);
|
||||
prev_pos = 0;
|
||||
pos = fieldA.find(";");
|
||||
design_name = fieldA.substr(prev_pos, pos);
|
||||
display("%d %d %s\n", prev_pos, pos, design_name.c_str());
|
||||
prev_pos = pos+1;
|
||||
|
||||
pos = fieldA.find(";", prev_pos);
|
||||
userID = fieldA.substr(prev_pos, pos-prev_pos);
|
||||
display("%d %d %s\n", prev_pos, pos, userID.c_str());
|
||||
prev_pos = pos+1;
|
||||
|
||||
//pos = fieldA.find(";", prev_pos);
|
||||
toolVersion = fieldA.substr(prev_pos);
|
||||
display("%d %d %s\n", prev_pos, pos, toolVersion.c_str());
|
||||
break;
|
||||
case 'b': /* FPGA model */
|
||||
part_name = (tmp);
|
||||
break;
|
||||
case 'c': /* buildDate */
|
||||
date = (tmp);
|
||||
break;
|
||||
case 'd': /* buildHour */
|
||||
hour = (tmp);
|
||||
break;
|
||||
case 'e': /* file size */
|
||||
_bit_length = 0;
|
||||
for (int i = 0; i < 4; i++) {
|
||||
display("%x %x\n", 0xff & tmp[i], _bit_length);
|
||||
_bit_length <<= 8;
|
||||
_bit_length |= 0xff & tmp[i];
|
||||
}
|
||||
display(" %x\n", _bit_length);
|
||||
ret = 0;
|
||||
|
||||
break;
|
||||
}
|
||||
return ret;
|
||||
|
||||
}
|
||||
int BitParser::parse()
|
||||
{
|
||||
uint16_t length;
|
||||
display("parser\n\n");
|
||||
|
||||
/* Field 1 : misc header */
|
||||
_fd.read((char*)&length, sizeof(uint16_t));
|
||||
length = ntohs(length);
|
||||
_fd.seekg(length, _fd.cur);
|
||||
|
||||
_fd.read((char*)&length, sizeof(uint16_t));
|
||||
length = ntohs(length);
|
||||
|
||||
/* process all field */
|
||||
do {} while (parseField());
|
||||
|
||||
if (_verbose) {
|
||||
display("results\n\n");
|
||||
|
||||
cout << "fieldA : " << fieldA << endl;
|
||||
cout << " : " << design_name << ";" << userID << ";" << toolVersion << endl;
|
||||
cout << "part name : " << part_name << endl;
|
||||
cout << "date : " << date << endl;
|
||||
cout << "hour : " << hour << endl;
|
||||
cout << "file length : " << _bit_length << endl;
|
||||
}
|
||||
|
||||
/* rest of the file is data to send */
|
||||
int pos = _fd.tellg();
|
||||
display("%d %d\n", pos, _bit_length);
|
||||
_fd.read((char *)&_bit_data[0], sizeof(uint8_t) * _bit_length);
|
||||
if (_fd.gcount() != _bit_length) {
|
||||
cerr << "Error: data read different to asked length ";
|
||||
cerr << to_string(_fd.gcount()) << " " << to_string(_bit_length) << endl;
|
||||
return -1;
|
||||
}
|
||||
|
||||
for (int i = 0; i < _bit_length; i++) {
|
||||
_bit_data[i] = reverseByte(_bit_data[i]);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
#ifndef BITPARSER_H
|
||||
#define BITPARSER_H
|
||||
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
|
||||
#include "configBitstreamParser.hpp"
|
||||
|
||||
class BitParser: public ConfigBitstreamParser {
|
||||
public:
|
||||
BitParser(std::string filename, bool verbose = false);
|
||||
~BitParser();
|
||||
int parse();
|
||||
|
||||
private:
|
||||
int parseField();
|
||||
std::string fieldA;
|
||||
std::string part_name;
|
||||
std::string date;
|
||||
std::string hour;
|
||||
std::string design_name;
|
||||
std::string userID;
|
||||
std::string toolVersion;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,15 @@
|
||||
#ifndef BOARD_HPP
|
||||
#define BOARD_HPP
|
||||
|
||||
#include <map>
|
||||
|
||||
static std::map <std::string, std::string > board_list = {
|
||||
{"arty", "digilent"},
|
||||
{"cyc1000", "ft2232"},
|
||||
{"de0nano", "usbblaster"},
|
||||
{"machXO3SK", "ft2232"},
|
||||
{"littleBee", "ft2232"},
|
||||
{"tangnano", "ft2232"}
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,14 @@
|
||||
#ifndef CABLE_HPP
|
||||
#define CABLE_HPP
|
||||
|
||||
#include <map>
|
||||
|
||||
#include "ftdipp_mpsse.hpp"
|
||||
|
||||
static std::map <std::string, FTDIpp_MPSSE::mpsse_bit_config > cable_list = {
|
||||
{"digilent", {0x0403, 0x6010, 0xe8, 0xeb, 0x00, 0x60}},
|
||||
{"digilent_hs3", {0x0403, 0x6014, 0x88, 0x8B, 0x20, 0x30}},
|
||||
{"ft2232", {0x0403, 0x6010, 0x08, 0x0B, 0x08, 0x0B}}
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,39 @@
|
||||
#include <iostream>
|
||||
#include <stdint.h>
|
||||
#include <strings.h>
|
||||
|
||||
#include "configBitstreamParser.hpp"
|
||||
|
||||
using namespace std;
|
||||
|
||||
ConfigBitstreamParser::ConfigBitstreamParser(string filename, int mode,
|
||||
bool verbose):
|
||||
_filename(filename), _bit_length(0),
|
||||
_file_size(0), _verbose(verbose), _fd(filename,
|
||||
ifstream::in | (ios_base::openmode)mode), _bit_data()
|
||||
{
|
||||
if (!_fd.is_open()) {
|
||||
cerr << "Error: fail to open " << _filename << endl;
|
||||
throw std::exception();
|
||||
}
|
||||
_fd.seekg(0, _fd.end);
|
||||
_file_size = _fd.tellg();
|
||||
_fd.seekg(0, _fd.beg);
|
||||
|
||||
_bit_data.reserve(_file_size);
|
||||
}
|
||||
|
||||
ConfigBitstreamParser::~ConfigBitstreamParser()
|
||||
{
|
||||
_fd.close();
|
||||
}
|
||||
|
||||
uint8_t ConfigBitstreamParser::reverseByte(uint8_t src)
|
||||
{
|
||||
uint8_t dst = 0;
|
||||
for (int i=0; i < 8; i++) {
|
||||
dst = (dst << 1) | (src & 0x01);
|
||||
src >>= 1;
|
||||
}
|
||||
return dst;
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
#ifndef CONFIGBITSTREAMPARSER_H
|
||||
#define CONFIGBITSTREAMPARSER_H
|
||||
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
#include <stdint.h>
|
||||
|
||||
class ConfigBitstreamParser {
|
||||
public:
|
||||
ConfigBitstreamParser(std::string filename, int mode = ASCII_MODE,
|
||||
bool verbose = false);
|
||||
virtual ~ConfigBitstreamParser();
|
||||
virtual int parse() = 0;
|
||||
uint8_t *getData() {return (uint8_t*)_bit_data.c_str();}
|
||||
int getLength() {return _bit_length;}
|
||||
|
||||
enum {
|
||||
ASCII_MODE = 0,
|
||||
BIN_MODE = std::ifstream::binary
|
||||
};
|
||||
|
||||
static uint8_t reverseByte(uint8_t src);
|
||||
|
||||
protected:
|
||||
std::string _filename;
|
||||
int _bit_length;
|
||||
int _file_size;
|
||||
bool _verbose;
|
||||
std::ifstream _fd;
|
||||
std::string _bit_data;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,24 @@
|
||||
#include <iostream>
|
||||
#include <stdexcept>
|
||||
|
||||
#include "device.hpp"
|
||||
|
||||
using namespace std;
|
||||
|
||||
Device::Device(FtdiJtag *jtag, string filename, bool verbose):
|
||||
_filename(filename),
|
||||
_file_extension(filename.substr(filename.find_last_of(".") +1)),
|
||||
_mode(NONE_MODE), _verbose(verbose)
|
||||
{
|
||||
_jtag = jtag;
|
||||
if (_verbose)
|
||||
cout << "File type : " << _file_extension << endl;
|
||||
}
|
||||
|
||||
Device::~Device() {}
|
||||
|
||||
void Device::reset()
|
||||
{
|
||||
throw std::runtime_error("Not implemented");
|
||||
}
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
#ifndef DEVICE_HPP
|
||||
#define DEVICE_HPP
|
||||
|
||||
#include <iostream>
|
||||
|
||||
#include "ftdijtag.hpp"
|
||||
|
||||
/* GGM: TODO: program must have an optional
|
||||
* offset
|
||||
* and question: bitstream to load bitstream in SPI mode must
|
||||
* be hardcoded or provided by user?
|
||||
*/
|
||||
class Device {
|
||||
public:
|
||||
enum prog_mode {
|
||||
NONE_MODE = 0,
|
||||
SPI_MODE = 1,
|
||||
FLASH_MODE = 1,
|
||||
MEM_MODE = 2
|
||||
};
|
||||
Device(FtdiJtag *jtag, std::string filename, bool verbose = false);
|
||||
virtual ~Device();
|
||||
virtual void program(unsigned int offset = 0) = 0;
|
||||
virtual int idCode() = 0;
|
||||
virtual void reset();
|
||||
protected:
|
||||
FtdiJtag *_jtag;
|
||||
std::string _filename;
|
||||
std::string _file_extension;
|
||||
enum prog_mode _mode;
|
||||
bool _verbose;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
* Copyright (C) 2019 Gwenhael Goavec-Merou <[email protected]>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
|
||||
#include "display.hpp"
|
||||
|
||||
#define KNRM "\x1B[0m"
|
||||
#define KRED "\x1B[31m"
|
||||
#define KGRN "\x1B[32m"
|
||||
#define KYEL "\x1B[33m"
|
||||
#define KBLU "\x1B[34m"
|
||||
#define KMAG "\x1B[35m"
|
||||
#define KCYN "\x1B[36m"
|
||||
#define KWHT "\x1B[37m"
|
||||
|
||||
void printError(std::string err, bool eol)
|
||||
{
|
||||
std::cerr << KRED << err << "\e[0m" << std::flush;
|
||||
if (eol)
|
||||
std::cerr << std::endl;
|
||||
}
|
||||
|
||||
void printInfo(std::string info, bool eol)
|
||||
{
|
||||
std::cout << KBLU << info << "\e[0m" << std::flush;
|
||||
if (eol)
|
||||
std::cout << std::endl;
|
||||
}
|
||||
|
||||
void printSuccess(std::string success, bool eol)
|
||||
{
|
||||
std::cout << KGRN << success << "\e[0m" << std::flush;
|
||||
if (eol)
|
||||
std::cout << std::endl;
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
/*
|
||||
* Copyright (C) 2019 Gwenhael Goavec-Merou <[email protected]>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef DISPLAY_HPP_
|
||||
#define DISPLAY_HPP_
|
||||
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
|
||||
void printError(std::string err, bool eol = true);
|
||||
void printInfo(std::string info, bool eol = true);
|
||||
void printSuccess(std::string success, bool eol = true);
|
||||
|
||||
#endif // DISPLAY_HPP_
|
||||
+270
@@ -0,0 +1,270 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <strings.h>
|
||||
|
||||
#include "epcq.hpp"
|
||||
|
||||
#define RD_STATUS_REG 0x05
|
||||
# define STATUS_REG_WEL (0x01 << 1)
|
||||
# define STATUS_REG_WIP (0x01 << 0)
|
||||
#define RD_BYTE_REG 0x03
|
||||
#define RD_DEV_ID_REG 0x9F
|
||||
#define RD_SILICON_ID_REG 0xAB
|
||||
#define RD_FAST_READ_REG 0x0B
|
||||
/* TBD */
|
||||
#define WR_ENABLE_REG 0x06
|
||||
#define WR_DISABLE_REG 0x04
|
||||
#define WR_STATUS_REG 0x01
|
||||
#define WR_BYTES_REG 0x02
|
||||
/* TBD */
|
||||
#define ERASE_BULK_REG 0xC7
|
||||
#define ERASE_SECTOR_REG 0xD8
|
||||
#define ERASE_SUBSECTOR_REG 0x20
|
||||
#define RD_SFDP_REG_REG 0x5A
|
||||
|
||||
#define SECTOR_SIZE 65536
|
||||
|
||||
/* EPCQ wait for LSB first data
|
||||
* so we simply reconstruct a new char with reverse
|
||||
*/
|
||||
unsigned char EPCQ::convertLSB(unsigned char src)
|
||||
{
|
||||
unsigned char res = 0;
|
||||
|
||||
for (int i=0; i < 8; i++)
|
||||
res = (res << 1) | ((src >> i) & 0x01);
|
||||
|
||||
return res;
|
||||
|
||||
}
|
||||
|
||||
/* wait for WEL goes high by reading
|
||||
* status register in a loop
|
||||
*/
|
||||
void EPCQ::wait_wel()
|
||||
{
|
||||
uint8_t cmd = RD_STATUS_REG, recv;
|
||||
|
||||
_spi.setCSmode(SPI_CS_MANUAL);
|
||||
_spi.clearCs();
|
||||
_spi.ft2232_spi_wr_and_rd(1, &cmd, NULL);
|
||||
do {
|
||||
_spi.ft2232_spi_wr_and_rd(1, NULL, &recv);
|
||||
} while(!(recv & STATUS_REG_WEL));
|
||||
_spi.setCs();
|
||||
_spi.setCSmode(SPI_CS_AUTO);
|
||||
}
|
||||
|
||||
/* wait for WIP goes low by reading
|
||||
* status register in a loop
|
||||
*/
|
||||
void EPCQ::wait_wip()
|
||||
{
|
||||
uint8_t cmd = RD_STATUS_REG, recv;
|
||||
|
||||
_spi.setCSmode( SPI_CS_MANUAL);
|
||||
_spi.clearCs();
|
||||
_spi.ft2232_spi_wr_and_rd(1, &cmd, NULL);
|
||||
do {
|
||||
_spi.ft2232_spi_wr_and_rd(1, NULL, &recv);
|
||||
} while(0x00 != (recv & STATUS_REG_WIP));
|
||||
_spi.setCs();
|
||||
_spi.setCSmode( SPI_CS_AUTO);
|
||||
}
|
||||
|
||||
/* enable write enable */
|
||||
int EPCQ::do_write_enable()
|
||||
{
|
||||
uint8_t cmd;
|
||||
cmd = WR_ENABLE_REG;
|
||||
_spi.ft2232_spi_wr_and_rd(1, &cmd, NULL);
|
||||
wait_wel();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* currently we erase sector but it's possible to
|
||||
* do sector + subsector to reduce erase
|
||||
*/
|
||||
|
||||
int EPCQ::erase_sector(char start_sector, char nb_sectors)
|
||||
{
|
||||
uint8_t buffer[4] = {ERASE_SECTOR_REG, 0, 0, 0};
|
||||
uint32_t base_addr = start_sector * SECTOR_SIZE;
|
||||
|
||||
/* 1. enable write
|
||||
* 2. send opcode + address in targeted sector
|
||||
* 3. wait for end.
|
||||
*/
|
||||
|
||||
printf("erase %d sectors\n", nb_sectors);
|
||||
for (base_addr = start_sector * SECTOR_SIZE; nb_sectors >= 0; nb_sectors--, base_addr += SECTOR_SIZE) {
|
||||
/* allow write */
|
||||
do_write_enable();
|
||||
/* send addr in the current sector */
|
||||
buffer[1] = (base_addr >> 16) & 0xff;
|
||||
buffer[2] = (base_addr >> 8) & 0x0ff;
|
||||
buffer[3] = (base_addr) & 0x0ff;
|
||||
printf("%d %d %x %x %x %x ", nb_sectors, base_addr, buffer[0], buffer[1], buffer[2], buffer[3]);
|
||||
|
||||
if (_spi.ft2232_spi_wr_and_rd(4, buffer, NULL) < 0) {
|
||||
cout << "Write error in erase_sector\n" << endl;
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* read status reg, wait for WIP goes low */
|
||||
wait_wip();
|
||||
printf("sector %d ok\n", nb_sectors);
|
||||
}
|
||||
printf("erase : end\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* write must be do by 256bytes. Before writting next 256bytes we must
|
||||
* wait for WIP goes low
|
||||
*/
|
||||
|
||||
void EPCQ::program(unsigned int start_offset, string filename, bool reverse)
|
||||
{
|
||||
FILE *fd;
|
||||
int file_size, nb_sect, i, ii;
|
||||
unsigned char buffer[256 + 4], rd_buffer[256], start_sector;
|
||||
int nb_iter, len, nb_read, offset = start_offset;
|
||||
/* 1. we need to know the size of the bistream
|
||||
* 2. according to the same we compute number of sector needed
|
||||
* 3. we erase sectors
|
||||
* 4. we write new content
|
||||
*/
|
||||
fd = fopen(filename.c_str(), "r");
|
||||
if (!fd) {
|
||||
cout << "Error opening " << filename << endl;
|
||||
return;
|
||||
}
|
||||
fseek(fd, 0, SEEK_END);
|
||||
file_size = ftell(fd);
|
||||
fseek(fd, 0, SEEK_SET);
|
||||
|
||||
/* compute number of sector used */
|
||||
nb_sect = file_size / SECTOR_SIZE;
|
||||
nb_sect += ((file_size % SECTOR_SIZE) ? 1 : 0);
|
||||
/* compute number of iterations */
|
||||
nb_iter = file_size / 256;
|
||||
nb_iter += ((file_size % 256) ? 1 : 0);
|
||||
len = file_size;
|
||||
/* compute start sector */
|
||||
start_sector = start_offset / SECTOR_SIZE;
|
||||
|
||||
printf("erase %d sectors starting at 0x%x (sector %d)\n", nb_sect, offset, start_sector);
|
||||
erase_sector(start_sector, (char)nb_sect);
|
||||
|
||||
/* now start programming */
|
||||
if (_verbose) {
|
||||
printf("program in ");
|
||||
if (reverse)
|
||||
printf("reverse mode\n");
|
||||
else
|
||||
printf("direct mode\n");
|
||||
}
|
||||
buffer[0] = WR_BYTES_REG;
|
||||
for (i= 0; i < nb_iter; i++) {
|
||||
do_write_enable();
|
||||
|
||||
nb_read = fread(rd_buffer, 1, 256, fd);
|
||||
if (nb_read == 0) {
|
||||
printf("problem dans le read du fichier source\n");
|
||||
break;
|
||||
}
|
||||
buffer[1] = (offset >> 16) & 0xff;
|
||||
buffer[2] = (offset >> 8) & 0xff;
|
||||
buffer[3] = offset & 0xff;
|
||||
for (ii= 0; ii < nb_read; ii++)
|
||||
buffer[ii+4] = (reverse) ? convertLSB(rd_buffer[ii]):rd_buffer[ii];
|
||||
_spi.ft2232_spi_wr_and_rd(nb_read+4, buffer, NULL);
|
||||
wait_wip();
|
||||
len -= nb_read;
|
||||
offset += nb_read;
|
||||
if ((i % 10) == 0)
|
||||
printf("%s sector done len %d %d %d\n", __func__, len, i, nb_iter);
|
||||
}
|
||||
|
||||
fclose(fd);
|
||||
}
|
||||
|
||||
|
||||
void EPCQ::dumpJICFile(char *jic_file, char *out_file, size_t max_len)
|
||||
{
|
||||
int offset = 0xA1;
|
||||
unsigned char c;
|
||||
size_t i=0;
|
||||
|
||||
FILE *jic = fopen(jic_file, "r");
|
||||
fseek(jic, offset, SEEK_SET);
|
||||
FILE *out = fopen(out_file, "w");
|
||||
for (i=0; i < max_len && (1 == fread(&c, 1, 1, jic)); i++) {
|
||||
fprintf(out, "%lx %x\n", i, c);
|
||||
}
|
||||
fclose(jic);
|
||||
fclose(out);
|
||||
}
|
||||
|
||||
void EPCQ::dumpflash(char *dest_file, int size)
|
||||
{
|
||||
(void)size;
|
||||
(void)dest_file;
|
||||
int i;
|
||||
unsigned char tx_buf[5] = {RD_FAST_READ_REG, 0, 0, 0, 0};
|
||||
|
||||
/* 1 byte cmd + 3 byte addr + 8 dummy clk cycle -> 1 byte */
|
||||
int realByteToRead = 2097380;
|
||||
realByteToRead = 0x1FFFFF;
|
||||
realByteToRead = 718569;
|
||||
unsigned char big_buf[realByteToRead];
|
||||
|
||||
_spi.ft2232_spi_wr_then_rd(tx_buf, 5, big_buf, realByteToRead);
|
||||
|
||||
FILE *fd = fopen("flash_dump.dd", "w");
|
||||
FILE *fd_txt = fopen("flash_dump.txt", "w");
|
||||
unsigned char c;
|
||||
for (i=0; i<realByteToRead; i++) {
|
||||
c = convertLSB(big_buf[i]);
|
||||
fwrite(&c, 1, 1, fd);
|
||||
fprintf(fd_txt, "%x %x\n", i, c);
|
||||
}
|
||||
fclose(fd);
|
||||
fclose(fd_txt);
|
||||
}
|
||||
short EPCQ::detect()
|
||||
{
|
||||
unsigned char tx_buf[5];
|
||||
/* read EPCQ device id */
|
||||
tx_buf[0] = 0x9f;
|
||||
/* 1 cmd byte + 2 dummy_byte */
|
||||
_spi.ft2232_spi_wr_then_rd(tx_buf, 3, &_device_id, 1);
|
||||
if (_verbose)
|
||||
printf("device id 0x%x attendu 0x15\n", _device_id);
|
||||
/* read EPCQ silicon id */
|
||||
tx_buf[0] = 0xAB;
|
||||
/* 1 cmd byte + 3 dummy_byte */
|
||||
_spi.ft2232_spi_wr_then_rd(tx_buf, 4, &_silicon_id, 1);
|
||||
if (_verbose)
|
||||
printf("silicon id 0x%x attendu 0x14\n", _silicon_id);
|
||||
return (_device_id << 8) | _silicon_id;
|
||||
}
|
||||
|
||||
EPCQ::EPCQ(int vid, int pid, unsigned char interface, uint32_t clkHZ,
|
||||
bool verbose):
|
||||
_spi(vid, pid, interface, clkHZ, verbose)
|
||||
{
|
||||
unsigned char mode = 0;
|
||||
|
||||
_spi.setMode(mode);
|
||||
|
||||
}
|
||||
|
||||
EPCQ::~EPCQ()
|
||||
{
|
||||
//ftdi_spi_close(_spi);
|
||||
//free(_spi);
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
#include <iostream>
|
||||
#include <vector>
|
||||
#include "ftdispi.hpp"
|
||||
using namespace std;
|
||||
|
||||
class EPCQ {
|
||||
public:
|
||||
EPCQ(int vid, int pid, unsigned char interface, uint32_t clkHZ,
|
||||
bool verbose = false);
|
||||
~EPCQ();
|
||||
|
||||
short detect();
|
||||
|
||||
void program(unsigned int start_offet, string filename, bool reverse=true);
|
||||
int erase_sector(char start_sector, char nb_sectors);
|
||||
void dumpflash(char *dest_file, int size);
|
||||
|
||||
private:
|
||||
unsigned char convertLSB(unsigned char src);
|
||||
void wait_wel();
|
||||
void wait_wip();
|
||||
int do_write_enable();
|
||||
|
||||
/* trash */
|
||||
void dumpJICFile(char *jic_file, char *out_file, size_t max_len);
|
||||
|
||||
//struct ftdi_spi *_spi;
|
||||
FtdiSpi _spi;
|
||||
|
||||
unsigned char _device_id;
|
||||
unsigned char _silicon_id;
|
||||
bool _verbose;
|
||||
|
||||
#if 0
|
||||
uint32_t _freq_hz;
|
||||
int _enddr;
|
||||
int _endir;
|
||||
int _run_state;
|
||||
int _end_state;
|
||||
svf_XYR hdr;
|
||||
svf_XYR hir;
|
||||
svf_XYR sdr;
|
||||
svf_XYR sir;
|
||||
svf_XYR tdr;
|
||||
svf_XYR tir;
|
||||
#endif
|
||||
};
|
||||
@@ -0,0 +1,143 @@
|
||||
/*
|
||||
* Copyright (C) 2019 Gwenhael Goavec-Merou <[email protected]>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <arpa/inet.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <iostream>
|
||||
#include <vector>
|
||||
|
||||
#include "fsparser.hpp"
|
||||
#include "display.hpp"
|
||||
|
||||
using namespace std;
|
||||
|
||||
FsParser::FsParser(string filename, bool reverseByte, bool verbose):
|
||||
ConfigBitstreamParser(filename, ConfigBitstreamParser::ASCII_MODE,
|
||||
verbose), _reverseByte(reverseByte), _toolVersion(), _partNumber(),
|
||||
_devicePackage(), _backgroundProgramming(false), _checksum(0),
|
||||
_crcCheck(false), _compress(false), _encryption(false),
|
||||
_securityBit(false), _jtagAsRegularIO(false), _date()
|
||||
{
|
||||
}
|
||||
FsParser::~FsParser()
|
||||
{
|
||||
}
|
||||
|
||||
int FsParser::parseHeader()
|
||||
{
|
||||
int ret = 1;
|
||||
string buffer;
|
||||
|
||||
while (1){
|
||||
std::getline(_fd, buffer, '\n');
|
||||
if (buffer[0] != '/')
|
||||
break;
|
||||
buffer = buffer.substr(2);
|
||||
size_t pos = buffer.find(':');
|
||||
if (pos == string::npos)
|
||||
continue;
|
||||
string v1, v2;
|
||||
v1 = buffer.substr(0, pos);
|
||||
if (pos+2 == buffer.size())
|
||||
v2 = "None";
|
||||
else
|
||||
v2 = buffer.substr(pos+2) + '\0'; // ':' + ' '
|
||||
|
||||
if (v1 == "GOWIN Version")
|
||||
_toolVersion = v2;
|
||||
if (v1 == "Part Number")
|
||||
_partNumber = v2;
|
||||
if (v1 == "Device-package")
|
||||
_devicePackage = v2;
|
||||
if (v1 == "BackgroundProgramming")
|
||||
_backgroundProgramming = ((v2 == "OFF")?false:true);
|
||||
if (v1 == "CheckSum")
|
||||
sscanf(v2.c_str(), "0x%04hx", &_checksum);
|
||||
if (v1 == "CRCCheck")
|
||||
_crcCheck = ((v2 == "OFF")?false:true);
|
||||
if (v1 == "Compress")
|
||||
_compress = ((v2 == "OFF")?false:true);
|
||||
if (v1 == "Encryption")
|
||||
_encryption = ((v2 == "OFF")?false:true);
|
||||
if (v1 == "SecurityBit")
|
||||
_securityBit = ((v2 == "OFF")?false:true);
|
||||
if (v1 == "JTAGAsRegularIO")
|
||||
_jtagAsRegularIO = ((v2 == "OFF")?false:true);
|
||||
if (v1 == "Created Time")
|
||||
_date = v2;
|
||||
}
|
||||
if (_verbose) {
|
||||
printInfo("tool version: " + _toolVersion);
|
||||
printInfo("Part number: " + _partNumber);
|
||||
printInfo("Device package: " + _devicePackage);
|
||||
printInfo("Background programming: " +
|
||||
string((_backgroundProgramming)?"ON":"OFF"));
|
||||
printInfo("Checksum: " + _checksum);
|
||||
printInfo("CRC check: " + string((_crcCheck)?"ON":"OFF"));
|
||||
printInfo("Compression: " + string((_compress)?"ON":"OFF"));
|
||||
printInfo("Encryption: " + string((_encryption)?"ON":"OFF"));
|
||||
printInfo("Security bit: " + string((_securityBit)?"ON":"OFF"));
|
||||
printInfo("Jtag as regular IO: " + string((_jtagAsRegularIO)?"ON":"OFF"));
|
||||
printInfo("Creation date: " + _date);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int FsParser::parse()
|
||||
{
|
||||
uint8_t data;
|
||||
string buffer, tmp;
|
||||
|
||||
printInfo("Parse " + _filename + ": ", true);
|
||||
|
||||
parseHeader();
|
||||
_fd.seekg(0, _fd.beg);
|
||||
|
||||
while (1) {
|
||||
std::getline(_fd, buffer, '\n');
|
||||
if (buffer.size() == 0)
|
||||
break;
|
||||
if (buffer[0] == '/')
|
||||
continue;
|
||||
tmp += buffer;
|
||||
}
|
||||
|
||||
_bit_length = tmp.size();
|
||||
|
||||
/* Fs file format is MSB first
|
||||
* so if reverseByte = false bit 0 -> 7, 1 -> 6,
|
||||
* if true 0 -> 0, 1 -> 1
|
||||
*/
|
||||
|
||||
for (int i = 0; i < _bit_length; i+=8) {
|
||||
data = 0;
|
||||
for (int ii = 0; ii < 8; ii++) {
|
||||
uint8_t val = (tmp[i+ii] == '1'?1:0);
|
||||
if (_reverseByte)
|
||||
data |= val << ii;
|
||||
else
|
||||
data |= val << (7-ii);
|
||||
}
|
||||
_bit_data += data;
|
||||
}
|
||||
|
||||
printSuccess("Done");
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
* Copyright (C) 2019 Gwenhael Goavec-Merou <[email protected]>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef FSPARSER_HPP_
|
||||
#define FSPARSER_HPP_
|
||||
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
|
||||
#include "configBitstreamParser.hpp"
|
||||
|
||||
class FsParser: public ConfigBitstreamParser {
|
||||
public:
|
||||
FsParser(const std::string filename, bool reverseByte, bool verbose);
|
||||
~FsParser();
|
||||
int parse();
|
||||
|
||||
uint16_t checksum() {return _checksum;}
|
||||
|
||||
private:
|
||||
int parseHeader();
|
||||
bool _reverseByte;
|
||||
std::string _toolVersion;
|
||||
std::string _partNumber;
|
||||
std::string _devicePackage;
|
||||
bool _backgroundProgramming;
|
||||
uint16_t _checksum;
|
||||
bool _crcCheck;
|
||||
bool _compress;
|
||||
bool _encryption;
|
||||
bool _securityBit;
|
||||
bool _jtagAsRegularIO;
|
||||
std::string _date;
|
||||
};
|
||||
|
||||
#endif // FSPARSER_HPP_
|
||||
@@ -0,0 +1,595 @@
|
||||
#include <libusb.h>
|
||||
|
||||
#include <iostream>
|
||||
#include <map>
|
||||
#include <vector>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <string>
|
||||
|
||||
#include "ftdijtag.hpp"
|
||||
#include "ftdipp_mpsse.hpp"
|
||||
|
||||
using namespace std;
|
||||
|
||||
#define DEBUG 0
|
||||
|
||||
#ifdef DEBUG
|
||||
#define display(...) \
|
||||
do { if (_verbose) fprintf(stdout, __VA_ARGS__);}while(0)
|
||||
#else
|
||||
#define display(...) do {}while(0)
|
||||
#endif
|
||||
|
||||
/*
|
||||
* AD0 -> TCK
|
||||
* AD1 -> TDI
|
||||
* AD2 -> TD0
|
||||
* AD3 -> TMS
|
||||
*/
|
||||
|
||||
/* Rmq:
|
||||
* pour TMS: l'envoi de n necessite de mettre n-1 comme longueur
|
||||
* mais le bit n+1 est utilise pour l'etat suivant le dernier
|
||||
* front. Donc il faut envoyer 6bits ([5:0]) pertinents pour
|
||||
* utiliser le bit 6 comme etat apres la commande,
|
||||
* le bit 7 corresponds a l'etat de TDI (donc si on fait 7 cycles
|
||||
* l'etat de TDI va donner l'etat de TMS...)
|
||||
* transfert/lecture: le dernier bit de IR ou DR doit etre envoye en
|
||||
* meme temps que le TMS qui fait sortir de l'etat donc il faut
|
||||
* pour n bits a transferer :
|
||||
* - envoyer 8bits * (n/8)-1
|
||||
* - envoyer les 7 bits du dernier octet;
|
||||
* - envoyer le dernier avec 0x4B ou 0x6B
|
||||
*/
|
||||
|
||||
FtdiJtag::FtdiJtag(FTDIpp_MPSSE::mpsse_bit_config &cable, string dev,
|
||||
unsigned char interface, uint32_t clkHZ, bool verbose):
|
||||
FTDIpp_MPSSE(dev, interface, clkHZ, verbose),
|
||||
_state(RUN_TEST_IDLE),
|
||||
_tms_buffer_size(128), _num_tms(0),
|
||||
_board_name("nope"), _ch552WA(false)
|
||||
{
|
||||
init_internal(cable);
|
||||
}
|
||||
|
||||
FtdiJtag::FtdiJtag(FTDIpp_MPSSE::mpsse_bit_config &cable,
|
||||
unsigned char interface, uint32_t clkHZ, bool verbose):
|
||||
FTDIpp_MPSSE(cable.vid, cable.pid, interface, clkHZ, verbose),
|
||||
_state(RUN_TEST_IDLE),
|
||||
_tms_buffer_size(128), _num_tms(0),
|
||||
_board_name("nope"), _ch552WA(false)
|
||||
{
|
||||
init_internal(cable);
|
||||
}
|
||||
|
||||
FtdiJtag::~FtdiJtag()
|
||||
{
|
||||
int read;
|
||||
/* Before shutdown, we must wait until everything is shifted out
|
||||
* Do this by temporary enabling loopback mode, write something
|
||||
* and wait until we can read it back
|
||||
* */
|
||||
static unsigned char tbuf[16] = { SET_BITS_LOW, 0xff, 0x00,
|
||||
SET_BITS_HIGH, 0xff, 0x00,
|
||||
LOOPBACK_START,
|
||||
MPSSE_DO_READ |
|
||||
MPSSE_DO_WRITE | MPSSE_WRITE_NEG | MPSSE_LSB,
|
||||
0x04, 0x00,
|
||||
0xaa, 0x55, 0x00, 0xff, 0xaa,
|
||||
LOOPBACK_END
|
||||
};
|
||||
mpsse_store(tbuf, 16);
|
||||
read = mpsse_read(tbuf, 5);
|
||||
if (read != 5)
|
||||
fprintf(stderr,
|
||||
"Loopback failed, expect problems on later runs %d\n", read);
|
||||
|
||||
free(_tms_buffer);
|
||||
}
|
||||
|
||||
void FtdiJtag::init_internal(FTDIpp_MPSSE::mpsse_bit_config &cable)
|
||||
{
|
||||
/* search for iProduct -> need to have
|
||||
* ftdi->usb_dev (libusb_device_handler) -> libusb_device ->
|
||||
* libusb_device_descriptor
|
||||
*/
|
||||
struct libusb_device * usb_dev = libusb_get_device(_ftdi->usb_dev);
|
||||
struct libusb_device_descriptor usb_desc;
|
||||
unsigned char iProduct[200];
|
||||
libusb_get_device_descriptor(usb_dev, &usb_desc);
|
||||
libusb_get_string_descriptor_ascii(_ftdi->usb_dev, usb_desc.iProduct,
|
||||
iProduct, 200);
|
||||
|
||||
display("iProduct : %s\n", iProduct);
|
||||
if (!strncmp((const char *)iProduct, "Sipeed-Debug", 12)) {
|
||||
_ch552WA = true;
|
||||
}
|
||||
|
||||
display("board_name %s\n", _board_name.c_str());
|
||||
display("%x\n", cable.bit_low_val);
|
||||
display("%x\n", cable.bit_low_dir);
|
||||
display("%x\n", cable.bit_high_val);
|
||||
display("%x\n", cable.bit_high_dir);
|
||||
|
||||
_tms_buffer = (unsigned char *)malloc(sizeof(unsigned char) * _tms_buffer_size);
|
||||
bzero(_tms_buffer, _tms_buffer_size);
|
||||
init(5, 0xfb, cable);
|
||||
}
|
||||
|
||||
int FtdiJtag::detectChain(vector<int> &devices, int max_dev)
|
||||
{
|
||||
unsigned char rx_buff[4];
|
||||
/* WA for CH552/tangNano: write is always mandatory */
|
||||
unsigned char tx_buff[4] = {0xff, 0xff, 0xff, 0xff};
|
||||
unsigned int tmp;
|
||||
|
||||
devices.clear();
|
||||
go_test_logic_reset();
|
||||
set_state(SHIFT_DR);
|
||||
|
||||
for (int i = 0; i < max_dev; i++) {
|
||||
read_write(tx_buff, rx_buff, 32, (i == max_dev-1)?1:0);
|
||||
tmp = 0;
|
||||
for (int ii=0; ii < 4; ii++)
|
||||
tmp |= (rx_buff[ii] << (8*ii));
|
||||
if (tmp != 0 && tmp != 0xffffffff)
|
||||
devices.push_back(tmp);
|
||||
}
|
||||
go_test_logic_reset();
|
||||
return devices.size();
|
||||
}
|
||||
|
||||
void FtdiJtag::setTMS(unsigned char tms)
|
||||
{
|
||||
display("%s %d %d\n", __func__, _num_tms, (_num_tms >> 3));
|
||||
if (_num_tms+1 == _tms_buffer_size * 8)
|
||||
flushTMS();
|
||||
if (tms != 0)
|
||||
_tms_buffer[_num_tms>>3] |= (0x1) << (_num_tms & 0x7);
|
||||
_num_tms++;
|
||||
}
|
||||
|
||||
/* reconstruct byte sent to TMS pins
|
||||
* - use up to 6 bits
|
||||
* -since next bit after length is use to
|
||||
* fix TMS state after sent we copy last bit
|
||||
* to bit after next
|
||||
* -bit 7 is TDI state for each clk cycles
|
||||
*/
|
||||
|
||||
int FtdiJtag::flushTMS(bool flush_buffer)
|
||||
{
|
||||
int xfer, pos = 0;
|
||||
unsigned char buf[3]= {MPSSE_WRITE_TMS | MPSSE_LSB | MPSSE_BITMODE |
|
||||
MPSSE_WRITE_NEG, 0, 0};
|
||||
|
||||
if (_num_tms == 0)
|
||||
return 0;
|
||||
|
||||
display("%s: %d %x\n", __func__, _num_tms, _tms_buffer[0]);
|
||||
|
||||
while (_num_tms != 0) {
|
||||
xfer = (_num_tms > 6) ? 6 : _num_tms;
|
||||
buf[1] = xfer - 1;
|
||||
buf[2] = 0x80;
|
||||
for (int i = 0; i < xfer; i++, pos++) {
|
||||
buf[2] |=
|
||||
(((_tms_buffer[pos >> 3] & (1 << (pos & 0x07))) ? 1 : 0) << i);
|
||||
}
|
||||
_num_tms -= xfer;
|
||||
mpsse_store(buf, 3);
|
||||
}
|
||||
|
||||
/* reset buffer and number of bits */
|
||||
bzero(_tms_buffer, _tms_buffer_size);
|
||||
_num_tms = 0;
|
||||
if (flush_buffer)
|
||||
return mpsse_write();
|
||||
return 0;
|
||||
}
|
||||
|
||||
void FtdiJtag::go_test_logic_reset()
|
||||
{
|
||||
/* idenpendly to current state 5 clk with TMS high is enough */
|
||||
for (int i = 0; i < 6; i++)
|
||||
setTMS(0x01);
|
||||
flushTMS(true);
|
||||
_state = TEST_LOGIC_RESET;
|
||||
}
|
||||
|
||||
/* GGM: faut tenir plus compte de la taille de la fifo interne
|
||||
* du FT2232 pour maximiser l'envoi au lieu de faire de petits envoies
|
||||
*/
|
||||
int FtdiJtag::read_write(unsigned char *tdi, unsigned char *tdo, int len, char last)
|
||||
{
|
||||
/* 3 possible case :
|
||||
* - n * 8bits to send -> use byte command
|
||||
* - less than 8bits -> use bit command
|
||||
* - last bit to send -> sent in conjunction with TMS
|
||||
*/
|
||||
int tx_buff_size = mpsse_get_buffer_size();
|
||||
int real_len = (last) ? len - 1 : len; // if its a buffer in a big send send len
|
||||
// else supress last bit -> with TMS
|
||||
int nb_byte = real_len >> 3; // number of byte to send
|
||||
int nb_bit = (real_len & 0x07); // residual bits
|
||||
int xfer = tx_buff_size - 3;
|
||||
unsigned char c[len];
|
||||
unsigned char *rx_ptr = (unsigned char *)tdo;
|
||||
unsigned char *tx_ptr = (unsigned char *)tdi;
|
||||
unsigned char tx_buf[3] = {(unsigned char)(MPSSE_LSB | MPSSE_WRITE_NEG |
|
||||
((tdi) ? MPSSE_DO_WRITE : 0) |
|
||||
((tdo) ? MPSSE_DO_READ : 0)),
|
||||
static_cast<unsigned char>((xfer - 1) & 0xff), // low
|
||||
static_cast<unsigned char>((((xfer - 1) >> 8) & 0xff))}; // high
|
||||
|
||||
flushTMS(true);
|
||||
|
||||
display("%s len : %d %d %d %d\n", __func__, len, real_len, nb_byte,
|
||||
nb_bit);
|
||||
while (nb_byte > xfer) {
|
||||
mpsse_store(tx_buf, 3);
|
||||
if (tdi) {
|
||||
mpsse_store(tx_ptr, xfer);
|
||||
tx_ptr += xfer;
|
||||
}
|
||||
if (tdo) {
|
||||
mpsse_read(rx_ptr, xfer);
|
||||
rx_ptr += xfer;
|
||||
} else if (_ch552WA) {
|
||||
ftdi_read_data(_ftdi, c, xfer);
|
||||
}
|
||||
nb_byte -= xfer;
|
||||
}
|
||||
|
||||
|
||||
/* 1/ send serie of byte */
|
||||
if (nb_byte > 0) {
|
||||
display("%s read/write %d byte\n", __func__, nb_byte);
|
||||
tx_buf[1] = ((nb_byte - 1) & 0xff); // low
|
||||
tx_buf[2] = (((nb_byte - 1) >> 8) & 0xff); // high
|
||||
mpsse_store(tx_buf, 3);
|
||||
if (tdi) {
|
||||
mpsse_store(tx_ptr, nb_byte);
|
||||
tx_ptr += nb_byte;
|
||||
}
|
||||
if (tdo) {
|
||||
mpsse_read(rx_ptr, nb_byte);
|
||||
rx_ptr += nb_byte;
|
||||
} else if (_ch552WA) {
|
||||
ftdi_read_data(_ftdi, c, nb_byte);
|
||||
}
|
||||
}
|
||||
|
||||
unsigned char last_bit = (tdi) ? *tx_ptr : 0;
|
||||
|
||||
if (nb_bit != 0) {
|
||||
display("%s read/write %d bit\n", __func__, nb_bit);
|
||||
tx_buf[0] |= MPSSE_BITMODE;
|
||||
tx_buf[1] = nb_bit - 1;
|
||||
mpsse_store(tx_buf, 2);
|
||||
if (tdi) {
|
||||
display("%s last_bit %x size %d\n", __func__, last_bit, nb_bit-1);
|
||||
mpsse_store(last_bit);
|
||||
}
|
||||
mpsse_write();
|
||||
if (tdo) {
|
||||
mpsse_read(rx_ptr, 1);
|
||||
/* realign we have read nb_bit
|
||||
* since LSB add bit by the left and shift
|
||||
* we need to complete shift
|
||||
*/
|
||||
*rx_ptr >>= (8 - nb_bit);
|
||||
display("%s %x\n", __func__, *rx_ptr);
|
||||
} else if (_ch552WA) {
|
||||
ftdi_read_data(_ftdi, c, nb_bit);
|
||||
}
|
||||
}
|
||||
|
||||
/* display : must be dropped */
|
||||
if (_verbose && tdo) {
|
||||
display("\n");
|
||||
for (int i = (len / 8) - 1; i >= 0; i--)
|
||||
display("%x ", (unsigned char)tdo[i]);
|
||||
display("\n");
|
||||
}
|
||||
|
||||
if (last == 1) {
|
||||
last_bit = (tdi)? (*tx_ptr & (1 << nb_bit)) : 0;
|
||||
|
||||
display("%s move to EXIT1_xx and send last bit %x\n", __func__, (last_bit?0x81:0x01));
|
||||
/* write the last bit in conjunction with TMS */
|
||||
tx_buf[0] = MPSSE_WRITE_TMS | MPSSE_LSB | MPSSE_BITMODE | MPSSE_WRITE_NEG |
|
||||
((tdo) ? MPSSE_DO_READ : 0);
|
||||
tx_buf[1] = 0x0 ; // send 1bit
|
||||
tx_buf[2] = ((last_bit)?0x81:0x01); // we know in TMS tdi is bit 7
|
||||
// and to move to EXIT_XR TMS = 1
|
||||
mpsse_store(tx_buf, 3);
|
||||
mpsse_write();
|
||||
if (tdo) {
|
||||
unsigned char c;
|
||||
mpsse_read(&c, 1);
|
||||
/* in this case for 1 one it's always bit 7 */
|
||||
*rx_ptr |= ((c & 0x80) << (7 - nb_bit));
|
||||
display("%s %x\n", __func__, c);
|
||||
} else if (_ch552WA) {
|
||||
ftdi_read_data(_ftdi, c, 1);
|
||||
}
|
||||
_state = (_state == SHIFT_DR) ? EXIT1_DR : EXIT1_IR;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void FtdiJtag::toggleClk(int nb)
|
||||
{
|
||||
unsigned char c = (TEST_LOGIC_RESET == _state) ? 1 : 0;
|
||||
for (int i = 0; i < nb; i++)
|
||||
setTMS(c);
|
||||
flushTMS(true);
|
||||
}
|
||||
|
||||
int FtdiJtag::shiftDR(unsigned char *tdi, unsigned char *tdo, int drlen, int end_state)
|
||||
{
|
||||
set_state(SHIFT_DR);
|
||||
// force transmit tms state
|
||||
flushTMS(true);
|
||||
// currently don't care about multiple device in the chain
|
||||
read_write(tdi, tdo, drlen, 1);// 1 since only one device
|
||||
|
||||
set_state(end_state);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int FtdiJtag::shiftIR(unsigned char tdi, int irlen, int end_state)
|
||||
{
|
||||
if (irlen > 8) {
|
||||
cerr << "Error: this method this direct char don't support more than 1 byte" << endl;
|
||||
return -1;
|
||||
}
|
||||
return shiftIR(&tdi, NULL, irlen, end_state);
|
||||
}
|
||||
|
||||
int FtdiJtag::shiftIR(unsigned char *tdi, unsigned char *tdo, int irlen, int end_state)
|
||||
{
|
||||
display("%s: avant shiftIR\n", __func__);
|
||||
set_state(SHIFT_IR);
|
||||
flushTMS(true);
|
||||
// currently don't care about multiple device in the chain
|
||||
|
||||
display("%s: envoi ircode\n", __func__);
|
||||
read_write(tdi, tdo, irlen, 1);// 1 since only one device
|
||||
|
||||
set_state(end_state);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void FtdiJtag::set_state(int newState)
|
||||
{
|
||||
unsigned char tms;
|
||||
while (newState != _state) {
|
||||
display("_state : %16s(%02d) -> %s(%02d) ",
|
||||
getStateName((tapState_t)_state),
|
||||
_state,
|
||||
getStateName((tapState_t)newState), newState);
|
||||
switch (_state) {
|
||||
case TEST_LOGIC_RESET:
|
||||
if (newState == TEST_LOGIC_RESET) {
|
||||
tms = 1;
|
||||
} else {
|
||||
tms = 0;
|
||||
_state = RUN_TEST_IDLE;
|
||||
}
|
||||
break;
|
||||
case RUN_TEST_IDLE:
|
||||
if (newState == RUN_TEST_IDLE) {
|
||||
tms = 0;
|
||||
} else {
|
||||
tms = 1;
|
||||
_state = SELECT_DR_SCAN;
|
||||
}
|
||||
break;
|
||||
case SELECT_DR_SCAN:
|
||||
switch (newState) {
|
||||
case CAPTURE_DR:
|
||||
case SHIFT_DR:
|
||||
case EXIT1_DR:
|
||||
case PAUSE_DR:
|
||||
case EXIT2_DR:
|
||||
case UPDATE_DR:
|
||||
tms = 0;
|
||||
_state = CAPTURE_DR;
|
||||
break;
|
||||
default:
|
||||
tms = 1;
|
||||
_state = SELECT_IR_SCAN;
|
||||
}
|
||||
break;
|
||||
case SELECT_IR_SCAN:
|
||||
switch (newState) {
|
||||
case CAPTURE_IR:
|
||||
case SHIFT_IR:
|
||||
case EXIT1_IR:
|
||||
case PAUSE_IR:
|
||||
case EXIT2_IR:
|
||||
case UPDATE_IR:
|
||||
tms = 0;
|
||||
_state = CAPTURE_IR;
|
||||
break;
|
||||
default:
|
||||
tms = 1;
|
||||
_state = TEST_LOGIC_RESET;
|
||||
}
|
||||
break;
|
||||
/* DR column */
|
||||
case CAPTURE_DR:
|
||||
if (newState == SHIFT_DR) {
|
||||
tms = 0;
|
||||
_state = SHIFT_DR;
|
||||
} else {
|
||||
tms = 1;
|
||||
_state = EXIT1_DR;
|
||||
}
|
||||
break;
|
||||
case SHIFT_DR:
|
||||
if (newState == SHIFT_DR) {
|
||||
tms = 0;
|
||||
} else {
|
||||
tms = 1;
|
||||
_state = EXIT1_DR;
|
||||
}
|
||||
break;
|
||||
case EXIT1_DR:
|
||||
switch (newState) {
|
||||
case PAUSE_DR:
|
||||
case EXIT2_DR:
|
||||
case SHIFT_DR:
|
||||
case EXIT1_DR:
|
||||
tms = 0;
|
||||
_state = PAUSE_DR;
|
||||
break;
|
||||
default:
|
||||
tms = 1;
|
||||
_state = UPDATE_DR;
|
||||
}
|
||||
break;
|
||||
case PAUSE_DR:
|
||||
if (newState == PAUSE_DR) {
|
||||
tms = 0;
|
||||
} else {
|
||||
tms = 1;
|
||||
_state = EXIT2_DR;
|
||||
}
|
||||
break;
|
||||
case EXIT2_DR:
|
||||
switch (newState) {
|
||||
case SHIFT_DR:
|
||||
case EXIT1_DR:
|
||||
case PAUSE_DR:
|
||||
tms = 0;
|
||||
_state = SHIFT_DR;
|
||||
break;
|
||||
default:
|
||||
tms = 1;
|
||||
_state = UPDATE_DR;
|
||||
}
|
||||
break;
|
||||
case UPDATE_DR:
|
||||
if (newState == RUN_TEST_IDLE) {
|
||||
tms = 0;
|
||||
_state = RUN_TEST_IDLE;
|
||||
} else {
|
||||
tms = 1;
|
||||
_state = SELECT_DR_SCAN;
|
||||
}
|
||||
break;
|
||||
/* IR column */
|
||||
case CAPTURE_IR:
|
||||
if (newState == SHIFT_IR) {
|
||||
tms = 0;
|
||||
_state = SHIFT_IR;
|
||||
} else {
|
||||
tms = 1;
|
||||
_state = EXIT1_IR;
|
||||
}
|
||||
break;
|
||||
case SHIFT_IR:
|
||||
if (newState == SHIFT_IR) {
|
||||
tms = 0;
|
||||
} else {
|
||||
tms = 1;
|
||||
_state = EXIT1_IR;
|
||||
}
|
||||
break;
|
||||
case EXIT1_IR:
|
||||
switch (newState) {
|
||||
case PAUSE_IR:
|
||||
case EXIT2_IR:
|
||||
case SHIFT_IR:
|
||||
case EXIT1_IR:
|
||||
tms = 0;
|
||||
_state = PAUSE_IR;
|
||||
break;
|
||||
default:
|
||||
tms = 1;
|
||||
_state = UPDATE_IR;
|
||||
}
|
||||
break;
|
||||
case PAUSE_IR:
|
||||
if (newState == PAUSE_IR) {
|
||||
tms = 0;
|
||||
} else {
|
||||
tms = 1;
|
||||
_state = EXIT2_IR;
|
||||
}
|
||||
break;
|
||||
case EXIT2_IR:
|
||||
switch (newState) {
|
||||
case SHIFT_IR:
|
||||
case EXIT1_IR:
|
||||
case PAUSE_IR:
|
||||
tms = 0;
|
||||
_state = SHIFT_IR;
|
||||
break;
|
||||
default:
|
||||
tms = 1;
|
||||
_state = UPDATE_IR;
|
||||
}
|
||||
break;
|
||||
case UPDATE_IR:
|
||||
if (newState == RUN_TEST_IDLE) {
|
||||
tms = 0;
|
||||
_state = RUN_TEST_IDLE;
|
||||
} else {
|
||||
tms = 1;
|
||||
_state = SELECT_DR_SCAN;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
setTMS(tms);
|
||||
display("%d %d %d %x\n", tms, _num_tms-1, _state, _tms_buffer[(_num_tms-1) / 8]);
|
||||
}
|
||||
/* force write buffer */
|
||||
flushTMS();
|
||||
}
|
||||
|
||||
const char *FtdiJtag::getStateName(tapState_t s)
|
||||
{
|
||||
switch (s) {
|
||||
case TEST_LOGIC_RESET:
|
||||
return "TEST_LOGIC_RESET";
|
||||
case RUN_TEST_IDLE:
|
||||
return "RUN_TEST_IDLE";
|
||||
case SELECT_DR_SCAN:
|
||||
return "SELECT_DR_SCAN";
|
||||
case CAPTURE_DR:
|
||||
return "CAPTURE_DR";
|
||||
case SHIFT_DR:
|
||||
return "SHIFT_DR";
|
||||
case EXIT1_DR:
|
||||
return "EXIT1_DR";
|
||||
case PAUSE_DR:
|
||||
return "PAUSE_DR";
|
||||
case EXIT2_DR:
|
||||
return "EXIT2_DR";
|
||||
case UPDATE_DR:
|
||||
return "UPDATE_DR";
|
||||
case SELECT_IR_SCAN:
|
||||
return "SELECT_IR_SCAN";
|
||||
case CAPTURE_IR:
|
||||
return "CAPTURE_IR";
|
||||
case SHIFT_IR:
|
||||
return "SHIFT_IR";
|
||||
case EXIT1_IR:
|
||||
return "EXIT1_IR";
|
||||
case PAUSE_IR:
|
||||
return "PAUSE_IR";
|
||||
case EXIT2_IR:
|
||||
return "EXIT2_IR";
|
||||
case UPDATE_IR:
|
||||
return "UPDATE_IR";
|
||||
default:
|
||||
return "Unknown";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
#ifndef FTDIJTAG_H
|
||||
#define FTDIJTAG_H
|
||||
#include <ftdi.h>
|
||||
#include <iostream>
|
||||
#include <vector>
|
||||
|
||||
#include "ftdipp_mpsse.hpp"
|
||||
|
||||
class FtdiJtag : public FTDIpp_MPSSE {
|
||||
public:
|
||||
//FtdiJtag(std::string board_name, int vid, int pid, unsigned char interface, uint32_t clkHZ);
|
||||
FtdiJtag(FTDIpp_MPSSE::mpsse_bit_config &cable, std::string dev,
|
||||
unsigned char interface, uint32_t clkHZ, bool verbose = false);
|
||||
FtdiJtag(FTDIpp_MPSSE::mpsse_bit_config &cable, unsigned char interface, uint32_t clkHZ,
|
||||
bool verbose);
|
||||
~FtdiJtag();
|
||||
|
||||
int detectChain(std::vector<int> &devices, int max_dev);
|
||||
|
||||
int shiftIR(unsigned char *tdi, unsigned char *tdo, int irlen, int end_state = RUN_TEST_IDLE);
|
||||
int shiftIR(unsigned char tdi, int irlen, int end_state = RUN_TEST_IDLE);
|
||||
int shiftDR(unsigned char *tdi, unsigned char *tdo, int drlen, int end_state = RUN_TEST_IDLE);
|
||||
int read_write(unsigned char *tdi, unsigned char *tdo, int len, char last);
|
||||
|
||||
void toggleClk(int nb);
|
||||
void go_test_logic_reset();
|
||||
void set_state(int newState);
|
||||
int flushTMS(bool flush_buffer = false);
|
||||
void flush() {mpsse_write();}
|
||||
void setTMS(unsigned char tms);
|
||||
|
||||
enum tapState_t {
|
||||
TEST_LOGIC_RESET = 0,
|
||||
RUN_TEST_IDLE = 1,
|
||||
SELECT_DR_SCAN = 2,
|
||||
CAPTURE_DR = 3,
|
||||
SHIFT_DR = 4,
|
||||
EXIT1_DR = 5,
|
||||
PAUSE_DR = 6,
|
||||
EXIT2_DR = 7,
|
||||
UPDATE_DR = 8,
|
||||
SELECT_IR_SCAN = 9,
|
||||
CAPTURE_IR = 10,
|
||||
SHIFT_IR = 11,
|
||||
EXIT1_IR = 12,
|
||||
PAUSE_IR = 13,
|
||||
EXIT2_IR = 14,
|
||||
UPDATE_IR = 15,
|
||||
UNKNOWN = 999
|
||||
};
|
||||
const char *getStateName(tapState_t s);
|
||||
|
||||
/* utilities */
|
||||
void setVerbose(bool verbose){_verbose=verbose;}
|
||||
|
||||
private:
|
||||
void init_internal(FTDIpp_MPSSE::mpsse_bit_config &cable);
|
||||
int _state;
|
||||
int _tms_buffer_size;
|
||||
int _num_tms;
|
||||
unsigned char *_tms_buffer;
|
||||
std::string _board_name;
|
||||
bool _ch552WA;
|
||||
};
|
||||
#endif
|
||||
@@ -0,0 +1,424 @@
|
||||
#include <fcntl.h>
|
||||
#include <string.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <termios.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <iostream>
|
||||
|
||||
#include <libudev.h>
|
||||
#include <libusb.h>
|
||||
|
||||
#include "ftdipp_mpsse.hpp"
|
||||
|
||||
using namespace std;
|
||||
|
||||
//#define DEBUG 1
|
||||
#define display(...) \
|
||||
do { if (_verbose) fprintf(stdout, __VA_ARGS__);}while(0)
|
||||
|
||||
FTDIpp_MPSSE::FTDIpp_MPSSE(const string &dev, unsigned char interface,
|
||||
uint32_t clkHZ, bool verbose):_verbose(verbose), _vid(0),
|
||||
_pid(0), _bus(-1), _addr(-1), _product(""), _interface(interface),
|
||||
_clkHZ(clkHZ), _buffer_size(2*32768), _num(0)
|
||||
{
|
||||
if (!search_with_dev(dev)) {
|
||||
cerr << "No cable found" << endl;
|
||||
throw std::exception();
|
||||
}
|
||||
|
||||
open_device(115200);
|
||||
_buffer_size = _ftdi->max_packet_size;
|
||||
|
||||
_buffer = (unsigned char *)malloc(sizeof(unsigned char) * _buffer_size);
|
||||
if (!_buffer) {
|
||||
cout << "_buffer malloc failed" << endl;
|
||||
throw std::exception();
|
||||
}
|
||||
}
|
||||
|
||||
FTDIpp_MPSSE::FTDIpp_MPSSE(int vid, int pid, unsigned char interface,
|
||||
uint32_t clkHZ, bool verbose):_verbose(verbose), _vid(vid),
|
||||
_pid(pid), _bus(-1),
|
||||
_addr(-1), _product(""), _interface(interface),
|
||||
_clkHZ(clkHZ), _buffer_size(2*32768), _num(0)
|
||||
{
|
||||
open_device(115200);
|
||||
_buffer_size = _ftdi->max_packet_size;
|
||||
|
||||
_buffer = (unsigned char *)malloc(sizeof(unsigned char) * _buffer_size);
|
||||
if (!_buffer) {
|
||||
cout << "_buffer malloc failed" << endl;
|
||||
throw std::exception();
|
||||
}
|
||||
}
|
||||
|
||||
FTDIpp_MPSSE::~FTDIpp_MPSSE()
|
||||
{
|
||||
ftdi_set_bitmode(_ftdi, 0, BITMODE_RESET);
|
||||
|
||||
ftdi_usb_reset(_ftdi);
|
||||
close_device();
|
||||
free(_buffer);
|
||||
}
|
||||
|
||||
void FTDIpp_MPSSE::open_device(unsigned int baudrate)
|
||||
{
|
||||
int ret;
|
||||
|
||||
display("try to open %x %x %d %d\n", _vid, _pid, _bus, _addr);
|
||||
|
||||
_ftdi = ftdi_new();
|
||||
if (_ftdi == NULL) {
|
||||
cout << "open_device: failed to initialize ftdi" << endl;
|
||||
throw std::exception();
|
||||
}
|
||||
|
||||
ftdi_set_interface(_ftdi, (ftdi_interface)_interface);
|
||||
if (_bus == -1 || _addr == -1)
|
||||
ret = ftdi_usb_open_desc(_ftdi, _vid, _pid, NULL, NULL);
|
||||
else
|
||||
#if (OLD_FTDI_VERSION == 1)
|
||||
ret = ftdi_usb_open_desc(_ftdi, _vid, _pid, _product, NULL);
|
||||
#else
|
||||
ret = ftdi_usb_open_bus_addr(_ftdi, _bus, _addr);
|
||||
#endif
|
||||
if (ret < 0) {
|
||||
fprintf(stderr, "unable to open ftdi device: %d (%s)\n",
|
||||
ret, ftdi_get_error_string(_ftdi));
|
||||
ftdi_free(_ftdi);
|
||||
throw std::exception();
|
||||
}
|
||||
if (ftdi_set_baudrate(_ftdi, baudrate) < 0) {
|
||||
fprintf(stderr, "baudrate error\n");
|
||||
close_device();
|
||||
throw std::exception();
|
||||
}
|
||||
}
|
||||
|
||||
/* cf. ftdi.c same function */
|
||||
void FTDIpp_MPSSE::ftdi_usb_close_internal()
|
||||
{
|
||||
libusb_close(_ftdi->usb_dev);
|
||||
_ftdi->usb_dev = NULL;
|
||||
}
|
||||
|
||||
int FTDIpp_MPSSE::close_device()
|
||||
{
|
||||
int rtn;
|
||||
if (_ftdi == NULL)
|
||||
return EXIT_FAILURE;
|
||||
|
||||
/* purge FTDI */
|
||||
ftdi_usb_purge_rx_buffer(_ftdi);
|
||||
ftdi_usb_purge_tx_buffer(_ftdi);
|
||||
|
||||
/*
|
||||
* repompe de la fonction et des suivantes
|
||||
*/
|
||||
if (_ftdi->usb_dev != NULL) {
|
||||
rtn = libusb_release_interface(_ftdi->usb_dev, _ftdi->interface);
|
||||
if (rtn < 0) {
|
||||
fprintf(stderr, "release interface failed %d\n", rtn);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
if (_ftdi->module_detach_mode == AUTO_DETACH_SIO_MODULE) {
|
||||
rtn = libusb_attach_kernel_driver(_ftdi->usb_dev, _ftdi->interface);
|
||||
if( rtn != 0)
|
||||
fprintf(stderr, "detach error %d\n", rtn);
|
||||
}
|
||||
}
|
||||
ftdi_usb_close_internal();
|
||||
|
||||
ftdi_free(_ftdi);
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
|
||||
int FTDIpp_MPSSE::init(unsigned char latency, unsigned char bitmask_mode,
|
||||
mpsse_bit_config & bit_conf)
|
||||
{
|
||||
unsigned char buf_cmd[6] = { SET_BITS_LOW, 0, 0,
|
||||
SET_BITS_HIGH, 0, 0
|
||||
};
|
||||
|
||||
if (ftdi_usb_reset(_ftdi) != 0) {
|
||||
cout << "reset error" << endl;
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (ftdi_set_bitmode(_ftdi, 0x00, BITMODE_RESET) < 0) {
|
||||
cout << "bitmode_reset error" << endl;
|
||||
return -1;
|
||||
}
|
||||
if (ftdi_usb_purge_buffers(_ftdi) != 0) {
|
||||
cout << "reset error" << endl;
|
||||
return -1;
|
||||
}
|
||||
if (ftdi_set_latency_timer(_ftdi, latency) != 0) {
|
||||
cout << "reset error" << endl;
|
||||
return -1;
|
||||
}
|
||||
/* enable MPSSE mode */
|
||||
if (ftdi_set_bitmode(_ftdi, bitmask_mode, BITMODE_MPSSE) < 0) {
|
||||
cout << "bitmode_mpsse error" << endl;
|
||||
return -1;
|
||||
}
|
||||
|
||||
unsigned char buf1[5];
|
||||
ftdi_read_data(_ftdi, buf1, 5);
|
||||
|
||||
if (setClkFreq(_clkHZ, 0) < 0)
|
||||
return -1;
|
||||
|
||||
buf_cmd[1] = bit_conf.bit_low_val; // 0xe8;
|
||||
buf_cmd[2] = bit_conf.bit_low_dir; // 0xeb;
|
||||
|
||||
buf_cmd[4] = bit_conf.bit_high_val; // 0x00;
|
||||
buf_cmd[5] = bit_conf.bit_high_dir; // 0x60;
|
||||
mpsse_store(buf_cmd, 6);
|
||||
mpsse_write();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int FTDIpp_MPSSE::setClkFreq(uint32_t clkHZ)
|
||||
{
|
||||
return setClkFreq(clkHZ, 0);
|
||||
}
|
||||
|
||||
int FTDIpp_MPSSE::setClkFreq(uint32_t clkHZ, char use_divide_by_5)
|
||||
{
|
||||
_clkHZ = clkHZ;
|
||||
|
||||
int ret;
|
||||
uint8_t buffer[4] = { TCK_DIVISOR, 0x00, 0x00};
|
||||
uint32_t base_freq;
|
||||
uint32_t real_freq = 0;
|
||||
uint16_t presc;
|
||||
|
||||
/* FT2232C has no divide by 5 instruction
|
||||
* and default freq is 12MHz
|
||||
*/
|
||||
if (_ftdi->type != TYPE_2232C) {
|
||||
base_freq = 60000000;
|
||||
if (use_divide_by_5) {
|
||||
base_freq /= 5;
|
||||
mpsse_store(EN_DIV_5);
|
||||
} else {
|
||||
mpsse_store(DIS_DIV_5);
|
||||
}
|
||||
} else {
|
||||
base_freq = 12000000;
|
||||
use_divide_by_5 = false;
|
||||
}
|
||||
|
||||
if ((use_divide_by_5 && _clkHZ > 6000000) || _clkHZ > 30000000) {
|
||||
fprintf(stderr, "Error: too fast frequency\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
presc = (base_freq /(_clkHZ * 2)) -1;
|
||||
real_freq = base_freq / ((1+presc)*2);
|
||||
if (real_freq > clkHZ)
|
||||
presc ++;
|
||||
real_freq = base_freq / ((1+presc)*2);
|
||||
display("presc : %d input freq : %d requested freq : %d real freq : %d\n",
|
||||
presc, base_freq, _clkHZ, real_freq);
|
||||
buffer[1] = presc & 0xff;
|
||||
buffer[2] = (presc >> 8) & 0xff;
|
||||
|
||||
mpsse_store(buffer, 3);
|
||||
ret = mpsse_write();
|
||||
if (ret < 0) {
|
||||
fprintf(stderr, "Error: write for frequency return %d\n", ret);
|
||||
return -1;
|
||||
}
|
||||
ret = ftdi_read_data(_ftdi, buffer, 4);
|
||||
|
||||
return real_freq;
|
||||
}
|
||||
|
||||
int FTDIpp_MPSSE::mpsse_store(unsigned char c)
|
||||
{
|
||||
return mpsse_store(&c, 1);
|
||||
}
|
||||
|
||||
int FTDIpp_MPSSE::mpsse_store(unsigned char *buff, int len)
|
||||
{
|
||||
unsigned char *ptr = buff;
|
||||
int store_size;
|
||||
/* check if _buffer as space to store all */
|
||||
if (_num + len > _buffer_size) {
|
||||
/* flush buffer if already full */
|
||||
if (_num == _buffer_size)
|
||||
mpsse_write();
|
||||
/* loop until loop < _buffer_size */
|
||||
while (_num + len > _buffer_size) {
|
||||
/* we now have len enough to fill
|
||||
* buffer -> just complete buffer
|
||||
*/
|
||||
store_size = _buffer_size - _num;
|
||||
memcpy(_buffer + _num, ptr, store_size);
|
||||
_num += store_size;
|
||||
if (mpsse_write() < 0) {
|
||||
cout << "write_data error in " << __func__ << endl;
|
||||
return -1;
|
||||
}
|
||||
ptr += store_size;
|
||||
len -= store_size;
|
||||
}
|
||||
|
||||
}
|
||||
#ifdef DEBUG
|
||||
display("%s %d %d\n", __func__, _num, len);
|
||||
#endif
|
||||
if (len > 0) {
|
||||
memcpy(_buffer + _num, ptr, len);
|
||||
_num += len;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int FTDIpp_MPSSE::mpsse_write()
|
||||
{
|
||||
int ret;
|
||||
if (_num == 0)
|
||||
return 0;
|
||||
|
||||
#ifdef DEBUG
|
||||
display("%s %d\n", __func__, _num);
|
||||
#endif
|
||||
|
||||
if ((ret = ftdi_write_data(_ftdi, _buffer, _num)) != _num) {
|
||||
cout << "write error: " << ret << " instead of " << _num << endl;
|
||||
return ret;
|
||||
}
|
||||
|
||||
_num = 0;
|
||||
return ret;
|
||||
}
|
||||
|
||||
int FTDIpp_MPSSE::mpsse_read(unsigned char *rx_buff, int len)
|
||||
{
|
||||
int n;
|
||||
int num_read = 0;
|
||||
unsigned char *p = rx_buff;
|
||||
|
||||
/* force buffer transmission before read */
|
||||
mpsse_store(SEND_IMMEDIATE);
|
||||
mpsse_write();
|
||||
|
||||
do {
|
||||
n = ftdi_read_data(_ftdi, p, len);
|
||||
if (n < 0) {
|
||||
fprintf(stderr, "Error: ftdi_read_data in %s", __func__);
|
||||
return -1;
|
||||
}
|
||||
#ifdef DEBUG
|
||||
if (_verbose) {
|
||||
display("%s %d\n", __func__, n);
|
||||
for (int i = 0; i < n; i++)
|
||||
display("\t%s %x\n", __func__, p[i]);
|
||||
}
|
||||
#endif
|
||||
|
||||
len -= n;
|
||||
p += n;
|
||||
num_read += n;
|
||||
} while (len > 0);
|
||||
return num_read;
|
||||
}
|
||||
|
||||
unsigned int FTDIpp_MPSSE::udevstufftoint(const char *udevstring, int base)
|
||||
{
|
||||
char *endp;
|
||||
int ret;
|
||||
errno = 0;
|
||||
|
||||
if (udevstring == NULL)
|
||||
return (-1);
|
||||
|
||||
ret = (unsigned int)strtol(udevstring, &endp, base);
|
||||
if (errno) {
|
||||
fprintf(stderr,
|
||||
"udevstufftoint: Unable to parse number Error : %s (%d)\n",
|
||||
strerror(errno), errno);
|
||||
return (-2);
|
||||
}
|
||||
if (endp == optarg) {
|
||||
fprintf(stderr, "udevstufftoint: No digits were found\n");
|
||||
return (-3);
|
||||
}
|
||||
return (ret);
|
||||
}
|
||||
|
||||
bool FTDIpp_MPSSE::search_with_dev(const string &device)
|
||||
{
|
||||
struct udev *udev;
|
||||
struct udev_device *dev, *usbdeviceparent;
|
||||
char devtype;
|
||||
|
||||
struct stat statinfo;
|
||||
if (stat(device.c_str(), &statinfo) < 0) {
|
||||
printf("unable to stat file\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
/* get device type */
|
||||
switch (statinfo.st_mode & S_IFMT) {
|
||||
case S_IFBLK:
|
||||
devtype = 'b';
|
||||
break;
|
||||
case S_IFCHR:
|
||||
devtype = 'c';
|
||||
break;
|
||||
default:
|
||||
printf("not char or block device\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Create the udev object */
|
||||
udev = udev_new();
|
||||
if (!udev) {
|
||||
printf("Can't create udev\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
dev = udev_device_new_from_devnum(udev, devtype, statinfo.st_rdev);
|
||||
|
||||
if (dev == NULL) {
|
||||
printf("no dev\n");
|
||||
udev_device_unref(dev);
|
||||
udev_unref(udev);
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Get closest usb device parent (we need VIP/PID) */
|
||||
usbdeviceparent =
|
||||
udev_device_get_parent_with_subsystem_devtype(dev, "usb",
|
||||
"usb_device");
|
||||
if (!usbdeviceparent) {
|
||||
printf
|
||||
("Unable to find parent usb device! Is this actually an USB device ?\n");
|
||||
udev_device_unref(dev);
|
||||
udev_unref(udev);
|
||||
return false;
|
||||
}
|
||||
|
||||
_bus = udevstufftoint(udev_device_get_sysattr_value(
|
||||
usbdeviceparent, "busnum"), 10);
|
||||
_addr = udevstufftoint(udev_device_get_sysattr_value(
|
||||
usbdeviceparent, "devnum"), 10);
|
||||
sprintf(_product, "%s", udev_device_get_sysattr_value(usbdeviceparent, "product"));
|
||||
_vid = udevstufftoint(
|
||||
udev_device_get_sysattr_value(usbdeviceparent, "idVendor"), 16);
|
||||
_pid = udevstufftoint(udev_device_get_sysattr_value(
|
||||
usbdeviceparent, "idProduct"), 16);
|
||||
|
||||
display("vid %x pid %x bus %d addr %d product name : %s\n", _vid, _pid, _bus, _addr, _product);
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
#ifndef _FTDIPP_MPSSE_H
|
||||
#define _FTDIPP_MPSSE_H
|
||||
#include <ftdi.h>
|
||||
#include <string>
|
||||
|
||||
class FTDIpp_MPSSE {
|
||||
public:
|
||||
FTDIpp_MPSSE(const std::string &dev, unsigned char interface,
|
||||
uint32_t clkHZ, bool verbose = false);
|
||||
FTDIpp_MPSSE(int vid, int pid, unsigned char interface,
|
||||
uint32_t clkHZ, bool verbose = false);
|
||||
~FTDIpp_MPSSE();
|
||||
|
||||
typedef struct {
|
||||
int vid;
|
||||
int pid;
|
||||
int bit_low_val;
|
||||
int bit_low_dir;
|
||||
int bit_high_val;
|
||||
int bit_high_dir;
|
||||
} mpsse_bit_config;
|
||||
|
||||
int init(unsigned char latency, unsigned char bitmask_mode, mpsse_bit_config &bit_conf);
|
||||
int setClkFreq(uint32_t clkHZ);
|
||||
int setClkFreq(uint32_t clkHZ, char use_divide_by_5);
|
||||
|
||||
int vid() {return _vid;}
|
||||
int pid() {return _pid;}
|
||||
|
||||
protected:
|
||||
void open_device(unsigned int baudrate);
|
||||
void ftdi_usb_close_internal();
|
||||
int close_device();
|
||||
int mpsse_write();
|
||||
int mpsse_read(unsigned char *rx_buff, int len);
|
||||
int mpsse_store(unsigned char c);
|
||||
int mpsse_store(unsigned char *c, int len);
|
||||
int mpsse_get_buffer_size() {return _buffer_size;}
|
||||
unsigned int udevstufftoint(const char *udevstring, int base);
|
||||
bool search_with_dev(const std::string &device);
|
||||
bool _verbose;
|
||||
struct ftdi_context *_ftdi;
|
||||
|
||||
private:
|
||||
int _vid;
|
||||
int _pid;
|
||||
int _bus;
|
||||
int _addr;
|
||||
char _product[64];
|
||||
unsigned char _interface;
|
||||
int _clkHZ;
|
||||
int _buffer_size;
|
||||
int _num;
|
||||
unsigned char *_buffer;
|
||||
};
|
||||
|
||||
#endif
|
||||
+389
@@ -0,0 +1,389 @@
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <ftdi.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
#include "ftdipp_mpsse.hpp"
|
||||
#include "ftdispi.hpp"
|
||||
//#include "ftdi_handle.h"
|
||||
|
||||
/*
|
||||
* SCLK -> ADBUS0
|
||||
* MOSI -> ADBUS1
|
||||
* MISO -> ADBUS2
|
||||
* CS -> ADBUS3
|
||||
*/
|
||||
#define SPI_CLK (1 << 0)
|
||||
#define cs_bits 0x08
|
||||
#define pindir 0x0b
|
||||
|
||||
|
||||
//uint8_t buffer[1024];
|
||||
//int num = 0;
|
||||
|
||||
/* GGM: Faut aussi definir l'etat des broches par defaut */
|
||||
/* necessaire en mode0 et 1, ainsi qu'entre 2 et 3
|
||||
*/
|
||||
/* Rappel :
|
||||
* Mode0 : clk idle low, ecriture avant le premier front
|
||||
* ie lecture sur le premier front (montant)
|
||||
* Mode1 : clk idle low, ecriture sur le premier front (montant)
|
||||
* lecture sur le second front (descendant)
|
||||
* Mode2 : clk idle high, ecriture avant le premier front
|
||||
* lecture sur le premier front (descendant)
|
||||
* Mode3 : clk idle high, ecriture sur le premier front (descendant)
|
||||
* lecture sur le second front (montant)
|
||||
*/
|
||||
void FtdiSpi::setMode(uint8_t mode)
|
||||
{
|
||||
switch (mode) {
|
||||
case 0:
|
||||
_clk = 0;
|
||||
_wr_mode = MPSSE_WRITE_NEG;
|
||||
_rd_mode = 0;
|
||||
break;
|
||||
case 1:
|
||||
_clk = 0;
|
||||
_wr_mode = 0;
|
||||
_rd_mode = MPSSE_READ_NEG;
|
||||
break;
|
||||
case 2:
|
||||
_clk = SPI_CLK;
|
||||
_wr_mode = 0; //POS
|
||||
_rd_mode = MPSSE_READ_NEG;
|
||||
break;
|
||||
case 3:
|
||||
_clk = SPI_CLK;
|
||||
_wr_mode = MPSSE_WRITE_NEG;
|
||||
_rd_mode = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static FTDIpp_MPSSE::mpsse_bit_config bit_conf =
|
||||
{0x08, 0x0B, 0x08, 0x0B};
|
||||
|
||||
FtdiSpi::FtdiSpi(int vid, int pid, unsigned char interface, uint32_t clkHZ,
|
||||
bool verbose):
|
||||
FTDIpp_MPSSE(vid, pid, interface, clkHZ, verbose)
|
||||
{
|
||||
setCSmode(SPI_CS_AUTO);
|
||||
setEndianness(SPI_MSB_FIRST);
|
||||
|
||||
init(1, 0x00, bit_conf);
|
||||
}
|
||||
FtdiSpi::~FtdiSpi()
|
||||
{
|
||||
}
|
||||
|
||||
#if 0
|
||||
#define CLOCK 0x08
|
||||
#define LATENCY 16
|
||||
#define TIMEOUT 0
|
||||
#define SIZE 65536
|
||||
#define TX_BUFS (60000/8-3)
|
||||
|
||||
int ftdi_spi_init_internal(struct ftdi_spi *spi, uint32_t clk_freq_hz);
|
||||
|
||||
int ftdi_spi_init_by_name(struct ftdi_spi *spi, char *devname,
|
||||
uint8_t interface, uint32_t clk_freq_hz)
|
||||
{
|
||||
spi->ftdic = open_device_by_name(devname, interface, 115200);
|
||||
if (spi->ftdic == NULL) {
|
||||
printf("opening error\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
return ftdi_spi_init_internal(spi, clk_freq_hz);
|
||||
}
|
||||
|
||||
int ftdi_spi_init(struct ftdi_spi *spi, uint32_t vid, uint32_t pid,
|
||||
uint8_t interface, uint32_t clk_freq_hz)
|
||||
{
|
||||
spi->ftdic = open_device(vid, pid, interface, 115200);
|
||||
if (spi->ftdic == NULL) {
|
||||
printf("opening error\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
return ftdi_spi_init_internal(spi, clk_freq_hz);
|
||||
}
|
||||
#endif
|
||||
#if 0
|
||||
int ftdi_spi_init_internal(struct ftdi_spi *spi, uint32_t clock_freq_hz)
|
||||
{
|
||||
setCSmode(spi, SPI_CS_AUTO);
|
||||
setEndianness(spi, SPI_MSB_FIRST);
|
||||
spi->tx_buff = (uint8_t *)malloc(sizeof(uint8_t) * TX_BUFS);
|
||||
|
||||
if (ftdi_usb_reset(spi->ftdic) != 0) {
|
||||
printf("reset error\n");
|
||||
return -1;
|
||||
}
|
||||
if (ftdi_usb_purge_rx_buffer(spi->ftdic) != 0) {
|
||||
printf("reset error\n");
|
||||
return -1;
|
||||
}
|
||||
if (ftdi_usb_purge_tx_buffer(spi->ftdic) != 0) {
|
||||
printf("reset error\n");
|
||||
return -1;
|
||||
}
|
||||
if (ftdi_read_data_set_chunksize(spi->ftdic, SIZE) != 0) {
|
||||
printf("reset error\n");
|
||||
return -1;
|
||||
}
|
||||
if (ftdi_write_data_set_chunksize(spi->ftdic, SIZE) != 0) {
|
||||
printf("reset error\n");
|
||||
return -1;
|
||||
}
|
||||
if (ftdi_set_latency_timer(spi->ftdic, LATENCY) != 0) {
|
||||
printf("reset error\n");
|
||||
return -1;
|
||||
}
|
||||
if (ftdi_set_event_char(spi->ftdic, 0x00, 0) != 0) {
|
||||
printf("reset error\n");
|
||||
return -1;
|
||||
}
|
||||
if (ftdi_set_error_char(spi->ftdic, 0x00, 0) != 0) {
|
||||
printf("reset error\n");
|
||||
return -1;
|
||||
}
|
||||
// set the read timeouts in ms for the ft2232H
|
||||
spi->ftdic->usb_read_timeout = TIMEOUT;
|
||||
// set the write timeouts in ms for the ft2232H
|
||||
spi->ftdic->usb_write_timeout = 5000;
|
||||
if (ftdi_set_bitmode(spi->ftdic, 0x00, 0x00) != 0) { // reset controller
|
||||
printf("reset error\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (ftdi_set_bitmode(spi->ftdic, 0x00, 0x02) != 0) { // enable mpsse mode
|
||||
printf("reset error\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (ftdi_setClock(spi->ftdic, /*0x08,*/ clock_freq_hz) < 0)
|
||||
return -1;
|
||||
spi->tx_size = 0;
|
||||
|
||||
spi->tx_buff[spi->tx_size++] = 0x97; // disable adaptive clocking
|
||||
// devrait etre 8C pour enable et non 8D
|
||||
spi->tx_buff[spi->tx_size++] = 0x8d; //disable tri phase data clocking
|
||||
if (ftdi_write_data(spi->ftdic, spi->tx_buff, spi->tx_size) != spi->tx_size) {
|
||||
printf("write error for dis clock, adaptive, tri phase\n");
|
||||
return -1;
|
||||
}
|
||||
spi->tx_size = 0;
|
||||
spi->tx_buff[spi->tx_size++] = 0x85; // disable loopback
|
||||
if (ftdi_write_data(spi->ftdic, spi->tx_buff, spi->tx_size) != spi->tx_size) {
|
||||
printf("disable loopback error\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
spi->tx_size = 0;
|
||||
spi->tx_buff[spi->tx_size++] = 0x80;
|
||||
spi->tx_buff[spi->tx_size++] = 0x08;
|
||||
spi->tx_buff[spi->tx_size++] = 0x0B;
|
||||
if (ftdi_write_data(spi->ftdic, spi->tx_buff, spi->tx_size) != spi->tx_size) {
|
||||
printf("write set bit error\n");
|
||||
return -1;
|
||||
}
|
||||
spi->tx_size = 0;
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
//FtdiSpi::~FtdiSpi()
|
||||
//int ftdi_spi_close(struct ftdi_spi *spi)
|
||||
//{
|
||||
//struct ftdi_context *ftdic = spi->ftdic;
|
||||
//free(spi->tx_buff);
|
||||
//return close_device(ftdic);
|
||||
//}
|
||||
|
||||
// mpsse_write
|
||||
/*static int send_buf(struct ftdi_context *ftdic, const unsigned char *buf,
|
||||
int size)
|
||||
{
|
||||
int r;
|
||||
r = ftdi_write_data(ftdic, (unsigned char *)buf, size);
|
||||
if (r < 0) {
|
||||
printf("ftdi_write_data: %d, %s\n", r,
|
||||
ftdi_get_error_string(ftdic));
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ft_flush_buffer(struct ftdi_spi *spi)
|
||||
{
|
||||
int ret = 0;
|
||||
if (spi->tx_size != 0) {
|
||||
ret = send_buf(spi->ftdic, spi->tx_buff, spi->tx_size);
|
||||
spi->tx_size = 0;
|
||||
}
|
||||
return ret;
|
||||
}*/
|
||||
|
||||
// mpsse_store
|
||||
/*static int ft_store_char(struct ftdi_spi *spi, uint8_t c)
|
||||
{
|
||||
int ret = 0;
|
||||
if (spi->tx_size == TX_BUFS)
|
||||
ret = ft_flush_buffer(spi);
|
||||
spi->tx_buff[spi->tx_size] = c;
|
||||
spi->tx_size++;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ft_store_star_char(struct ftdi_spi *spi, uint8_t *buff, int len)
|
||||
{
|
||||
int ret = 0;
|
||||
if (spi->tx_size + len + 1 == TX_BUFS)
|
||||
ret = ft_flush_buffer(spi);
|
||||
memcpy(spi->tx_buff + spi->tx_size, buff, len);
|
||||
spi->tx_size += len;
|
||||
return ret;
|
||||
}*/
|
||||
|
||||
// mpsse read
|
||||
/*static int get_buf(struct ftdi_spi *spi, const unsigned char *buf,
|
||||
int size)
|
||||
{
|
||||
int r;
|
||||
ft_store_char(spi, SEND_IMMEDIATE);
|
||||
ft_flush_buffer(spi);
|
||||
|
||||
while (size > 0) {
|
||||
r = ftdi_read_data(spi->ftdic, (unsigned char *)buf, size);
|
||||
if (r < 0) {
|
||||
printf("ftdi_read_data: %d, %s\n", r,
|
||||
ftdi_get_error_string(spi->ftdic));
|
||||
return 1;
|
||||
}
|
||||
buf += r;
|
||||
size -= r;
|
||||
}
|
||||
return 0;
|
||||
}*/
|
||||
|
||||
/* send two consecutive cs configuration */
|
||||
void FtdiSpi::confCs(char stat)
|
||||
{
|
||||
uint8_t tx_buf[6] = {SET_BITS_LOW, _clk, pindir,
|
||||
SET_BITS_LOW, _clk, pindir};
|
||||
|
||||
tx_buf[1] |= (stat) ? cs_bits : 0;
|
||||
tx_buf[4] |= (stat) ? cs_bits : 0;
|
||||
|
||||
if (mpsse_store(tx_buf, 6) != 0)
|
||||
printf("error\n");
|
||||
}
|
||||
|
||||
void FtdiSpi::setCs()
|
||||
{
|
||||
_cs = cs_bits;
|
||||
confCs(_cs);
|
||||
}
|
||||
|
||||
void FtdiSpi::clearCs()
|
||||
{
|
||||
_cs = 0x00;
|
||||
confCs(_cs);
|
||||
}
|
||||
|
||||
int FtdiSpi::ft2232_spi_wr_then_rd(
|
||||
const uint8_t *tx_data, uint32_t tx_len,
|
||||
uint8_t *rx_data, uint32_t rx_len)
|
||||
{
|
||||
setCSmode(SPI_CS_MANUAL);
|
||||
clearCs();
|
||||
uint32_t ret = ft2232_spi_wr_and_rd(tx_len, tx_data, NULL);
|
||||
if (ret != 0) {
|
||||
printf("%s : write error %d %d\n", __func__, ret, tx_len);
|
||||
} else {
|
||||
ret = ft2232_spi_wr_and_rd(rx_len, NULL, rx_data);
|
||||
if (ret != 0) {
|
||||
printf("%s : read error\n", __func__);
|
||||
}
|
||||
}
|
||||
setCs();
|
||||
setCSmode(SPI_CS_AUTO);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Returns 0 upon success, a negative number upon errors. */
|
||||
int FtdiSpi::ft2232_spi_wr_and_rd(//struct ftdi_spi *spi,
|
||||
uint32_t writecnt,
|
||||
const uint8_t * writearr, uint8_t * readarr)
|
||||
{
|
||||
#define TX_BUF (60000/8-3)
|
||||
//struct ftdi_context *ftdic = spi->ftdic;
|
||||
uint8_t buf[TX_BUF+3];//65536+9];
|
||||
/* failed is special. We use bitwise ops, but it is essentially bool. */
|
||||
int i = 0, failed = 0;
|
||||
int ret = 0;
|
||||
|
||||
uint8_t *rx_ptr = readarr;
|
||||
uint8_t *tx_ptr = (uint8_t *)writearr;
|
||||
int len = writecnt;
|
||||
int xfer;
|
||||
|
||||
if (_cs_mode == SPI_CS_AUTO) {
|
||||
buf[i++] = SET_BITS_LOW;
|
||||
buf[i++] = (0 & ~cs_bits) | _clk; /* assertive */
|
||||
buf[i++] = pindir;
|
||||
mpsse_store(buf, i);
|
||||
i=0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Minimize USB transfers by packing as many commands as possible
|
||||
* together. If we're not expecting to read, we can assert CS#, write,
|
||||
* and deassert CS# all in one shot. If reading, we do three separate
|
||||
* operations.
|
||||
*/
|
||||
while (len > 0) {
|
||||
xfer = (len > TX_BUF) ? TX_BUF: len;
|
||||
|
||||
buf[i++] = /*(spi->endian == SPI_MSB_FIRST) ? 0 : MPSSE_LSB |*/
|
||||
((readarr) ? (MPSSE_DO_READ | _rd_mode) : 0) |
|
||||
((writearr) ? (MPSSE_DO_WRITE | _wr_mode) : 0);// |
|
||||
/*MPSSE_DO_WRITE |*/// spi->wr_mode | spi->rd_mode;
|
||||
buf[i++] = (xfer - 1) & 0xff;
|
||||
buf[i++] = ((xfer - 1) >> 8) & 0xff;
|
||||
if (writearr) {
|
||||
memcpy(buf + i, tx_ptr, xfer);
|
||||
tx_ptr += xfer;
|
||||
i += xfer;
|
||||
}
|
||||
|
||||
ret = mpsse_store(buf, i);
|
||||
failed = ret;
|
||||
if (ret)
|
||||
printf("send_buf failed before read: %i %s\n", ret, "plop");// ftdi_get_error_string(ftdic));
|
||||
i = 0;
|
||||
if (readarr) {
|
||||
//if (ret == 0) {
|
||||
ret = mpsse_read(rx_ptr, xfer);
|
||||
failed = ret;
|
||||
if (ret != xfer)
|
||||
printf("get_buf failed: %i\n", ret);
|
||||
//}
|
||||
rx_ptr += xfer;
|
||||
}
|
||||
len -= xfer;
|
||||
|
||||
}
|
||||
|
||||
if (_cs_mode == SPI_CS_AUTO) {
|
||||
buf[i++] = SET_BITS_LOW;
|
||||
buf[i++] = cs_bits | _clk;
|
||||
buf[i++] = pindir;
|
||||
ret = mpsse_store(buf, i);
|
||||
failed |= ret;
|
||||
if (ret)
|
||||
printf("send_buf failed at end: %i\n", ret);
|
||||
}
|
||||
|
||||
return 0;//failed ? -1 : 0;
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
#include <ftdi.h>
|
||||
#include <iostream>
|
||||
#include <vector>
|
||||
|
||||
#include "ftdipp_mpsse.hpp"
|
||||
|
||||
class FtdiSpi : public FTDIpp_MPSSE {
|
||||
public:
|
||||
#define SPI_MSB_FIRST 0
|
||||
#define SPI_LSB_FIRST 1
|
||||
|
||||
#define SPI_CS_AUTO 0
|
||||
#define SPI_CS_MANUAL 1
|
||||
|
||||
|
||||
FtdiSpi(int vid, int pid, unsigned char interface, uint32_t clkHZ,
|
||||
bool verbose);
|
||||
~FtdiSpi();
|
||||
|
||||
void setMode(uint8_t mode);
|
||||
void setEndianness(unsigned char endian) {
|
||||
_endian =(endian == SPI_MSB_FIRST) ? 0 : MPSSE_LSB;
|
||||
}
|
||||
|
||||
void setCSmode(uint8_t cs_mode) {_cs_mode = cs_mode;}
|
||||
void confCs(char stat);
|
||||
void setCs();
|
||||
void clearCs();
|
||||
|
||||
int ft2232_spi_wr_then_rd(const uint8_t *tx_data, uint32_t tx_len,
|
||||
uint8_t *rx_data, uint32_t rx_len);
|
||||
int ft2232_spi_wr_and_rd(uint32_t writecnt,
|
||||
const uint8_t *writearr, uint8_t *readarr);
|
||||
|
||||
private:
|
||||
uint8_t _cs;
|
||||
uint8_t _clk;
|
||||
uint8_t _wr_mode;
|
||||
uint8_t _rd_mode;
|
||||
unsigned char _endian;
|
||||
uint8_t _cs_mode;
|
||||
};
|
||||
+462
@@ -0,0 +1,462 @@
|
||||
/*
|
||||
* Copyright (C) 2019 Gwenhael Goavec-Merou <[email protected]>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <strings.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <iostream>
|
||||
|
||||
#include "ftdijtag.hpp"
|
||||
#include "gowin.hpp"
|
||||
#include "progressBar.hpp"
|
||||
#include "display.hpp"
|
||||
#include "fsparser.hpp"
|
||||
|
||||
using namespace std;
|
||||
|
||||
#define NOOP 0x02
|
||||
#define ERASE_SRAM 0x05
|
||||
#define READ_SRAM 0x03
|
||||
#define XFER_DONE 0x09
|
||||
#define READ_IDCODE 0x11
|
||||
#define INIT_ADDR 0x12
|
||||
#define READ_USERCODE 0x13
|
||||
#define CONFIG_ENABLE 0x15
|
||||
#define XFER_WRITE 0x17
|
||||
#define CONFIG_DISABLE 0x3A
|
||||
#define RELOAD 0x3C
|
||||
#define STATUS_REGISTER 0x41
|
||||
# define STATUS_CRC_ERROR (1 << 0)
|
||||
# define STATUS_BAD_COMMAND (1 << 1)
|
||||
# define STATUS_ID_VERIFY_FAILED (1 << 2)
|
||||
# define STATUS_TIMEOUT (1 << 3)
|
||||
# define STATUS_MEMORY_ERASE (1 << 5)
|
||||
# define STATUS_PREAMBLE (1 << 6)
|
||||
# define STATUS_SYSTEM_EDIT_MODE (1 << 7)
|
||||
# define STATUS_PRG_SPIFLASH_DIRECT (1 << 8)
|
||||
# define STATUS_NON_JTAG_CNF_ACTIVE (1 << 10)
|
||||
# define STATUS_BYPASS (1 << 11)
|
||||
# define STATUS_GOWIN_VLD (1 << 12)
|
||||
# define STATUS_DONE_FINAL (1 << 13)
|
||||
# define STATUS_SECURITY_FINAL (1 << 14)
|
||||
# define STATUS_READY (1 << 15)
|
||||
# define STATUS_POR (1 << 16)
|
||||
# define STATUS_FLASH_LOCK (1 << 17)
|
||||
#define EF_PROGRAM 0x71
|
||||
#define EFLASH_ERASE 0x75
|
||||
|
||||
Gowin::Gowin(FtdiJtag *jtag, const string filename, bool flash_wr, bool sram_wr,
|
||||
bool verbose): Device(jtag, filename, verbose)
|
||||
{
|
||||
_fs = NULL;
|
||||
if (_filename != "") {
|
||||
if (_file_extension == "fs") {
|
||||
if (flash_wr && sram_wr)
|
||||
throw std::runtime_error("both write-flash and write-sram can't be set");
|
||||
if (flash_wr)
|
||||
_mode = Device::FLASH_MODE;
|
||||
else
|
||||
_mode = Device::MEM_MODE;
|
||||
_fs = new FsParser(_filename, _mode == Device::MEM_MODE, _verbose);
|
||||
_fs->parse();
|
||||
} else {
|
||||
throw std::runtime_error("incompatible file format");
|
||||
}
|
||||
}
|
||||
_jtag->setClkFreq(2500000, 0);
|
||||
}
|
||||
|
||||
Gowin::~Gowin()
|
||||
{
|
||||
if (_fs)
|
||||
delete _fs;
|
||||
}
|
||||
|
||||
void Gowin::reset()
|
||||
{
|
||||
wr_rd(RELOAD, NULL, 0, NULL, 0);
|
||||
wr_rd(NOOP, NULL, 0, NULL, 0);
|
||||
}
|
||||
|
||||
void Gowin::programFlash()
|
||||
{
|
||||
uint8_t *data;
|
||||
int length;
|
||||
|
||||
data = _fs->getData();
|
||||
length = _fs->getLength();
|
||||
|
||||
/* erase SRAM */
|
||||
if (!EnableCfg())
|
||||
return;
|
||||
eraseSRAM();
|
||||
wr_rd(XFER_DONE, NULL, 0, NULL, 0);
|
||||
wr_rd(NOOP, NULL, 0, NULL, 0);
|
||||
if (!DisableCfg())
|
||||
return;
|
||||
|
||||
if (!EnableCfg())
|
||||
return;
|
||||
if (!eraseFLASH())
|
||||
return;
|
||||
if (!DisableCfg())
|
||||
return;
|
||||
wr_rd(RELOAD, NULL, 0, NULL, 0);
|
||||
wr_rd(NOOP, NULL, 0, NULL, 0);
|
||||
/* test status a faire */
|
||||
if (!flashFLASH(data, length))
|
||||
return;
|
||||
if (!DisableCfg())
|
||||
return;
|
||||
wr_rd(RELOAD, NULL, 0, NULL, 0);
|
||||
wr_rd(NOOP, NULL, 0, NULL, 0);
|
||||
if (_verbose)
|
||||
printInfo("%08x\n", readUserCode());
|
||||
}
|
||||
|
||||
void Gowin::program(unsigned int offset)
|
||||
{
|
||||
(void) offset;
|
||||
|
||||
uint8_t *data;
|
||||
uint32_t status;
|
||||
int length;
|
||||
|
||||
if (_filename == "" || !_fs)
|
||||
return;
|
||||
|
||||
if (_mode == FLASH_MODE) {
|
||||
programFlash();
|
||||
return;
|
||||
}
|
||||
|
||||
if (_verbose) {
|
||||
displayReadReg(readStatusReg());
|
||||
}
|
||||
|
||||
data = _fs->getData();
|
||||
length = _fs->getLength();
|
||||
|
||||
wr_rd(READ_IDCODE, NULL, 0, NULL, 0);
|
||||
|
||||
/* erase SRAM */
|
||||
if (!EnableCfg())
|
||||
return;
|
||||
eraseSRAM();
|
||||
if (!DisableCfg())
|
||||
return;
|
||||
|
||||
/* load bitstream in SRAM */
|
||||
if (!EnableCfg())
|
||||
return;
|
||||
if (!flashSRAM(data, length))
|
||||
return;
|
||||
if (!DisableCfg())
|
||||
return;
|
||||
|
||||
/* check if file checksum == checksum in FPGA */
|
||||
status = readUserCode();
|
||||
if (_fs->checksum() != status)
|
||||
printError("SRAM Flash: FAIL");
|
||||
else
|
||||
printSuccess("SRAM Flash: Success");
|
||||
if (_verbose)
|
||||
displayReadReg(readStatusReg());
|
||||
}
|
||||
|
||||
bool Gowin::EnableCfg()
|
||||
{
|
||||
wr_rd(CONFIG_ENABLE, NULL, 0, NULL, 0);
|
||||
return pollFlag(STATUS_SYSTEM_EDIT_MODE, STATUS_SYSTEM_EDIT_MODE);
|
||||
}
|
||||
|
||||
bool Gowin::DisableCfg()
|
||||
{
|
||||
wr_rd(CONFIG_DISABLE, NULL, 0, NULL, 0);
|
||||
wr_rd(NOOP, NULL, 0, NULL, 0);
|
||||
return pollFlag(STATUS_SYSTEM_EDIT_MODE, 0);
|
||||
}
|
||||
|
||||
int Gowin::idCode()
|
||||
{
|
||||
uint8_t device_id[4];
|
||||
wr_rd(READ_IDCODE, NULL, 0, device_id, 4);
|
||||
return device_id[3] << 24 |
|
||||
device_id[2] << 16 |
|
||||
device_id[1] << 8 |
|
||||
device_id[0];
|
||||
}
|
||||
|
||||
uint32_t Gowin::readStatusReg()
|
||||
{
|
||||
uint32_t reg;
|
||||
uint8_t rx[4];
|
||||
wr_rd(STATUS_REGISTER, NULL, 0, rx, 4);
|
||||
reg = rx[3] << 24 | rx[2] << 16 | rx[1] << 8 | rx[0];
|
||||
return reg;
|
||||
}
|
||||
|
||||
uint32_t Gowin::readUserCode()
|
||||
{
|
||||
uint8_t rx[4];
|
||||
wr_rd(READ_USERCODE, NULL, 0, rx, 4);
|
||||
return rx[3] << 24 | rx[2] << 16 | rx[1] << 8 | rx[0];
|
||||
}
|
||||
|
||||
bool Gowin::wr_rd(uint8_t cmd,
|
||||
uint8_t *tx, int tx_len,
|
||||
uint8_t *rx, int rx_len,
|
||||
bool verbose)
|
||||
{
|
||||
int xfer_len = rx_len;
|
||||
if (tx_len > rx_len)
|
||||
xfer_len = tx_len;
|
||||
|
||||
uint8_t xfer_tx[xfer_len], xfer_rx[xfer_len];
|
||||
bzero(xfer_tx, xfer_len);
|
||||
int i;
|
||||
if (tx != NULL) {
|
||||
for (i = 0; i < tx_len; i++)
|
||||
xfer_tx[i] = tx[i];
|
||||
}
|
||||
|
||||
_jtag->shiftIR(&cmd, NULL, 8);
|
||||
_jtag->toggleClk(6);
|
||||
if (rx || tx) {
|
||||
_jtag->shiftDR(xfer_tx, (rx) ? xfer_rx : NULL, 8 * xfer_len);
|
||||
_jtag->toggleClk(6);
|
||||
}
|
||||
if (rx) {
|
||||
if (verbose) {
|
||||
for (i=xfer_len-1; i >= 0; i--)
|
||||
printf("%02x ", xfer_rx[i]);
|
||||
printf("\n");
|
||||
}
|
||||
for (i = 0; i < rx_len; i++)
|
||||
rx[i] = (xfer_rx[i]);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void Gowin::displayReadReg(uint32_t dev)
|
||||
{
|
||||
printf("displayReadReg %08x\n", dev);
|
||||
if (dev & STATUS_CRC_ERROR)
|
||||
printf("\tCRC Error\n");
|
||||
if (dev & STATUS_BAD_COMMAND)
|
||||
printf("\tBad Command\n");
|
||||
if (dev & STATUS_ID_VERIFY_FAILED)
|
||||
printf("\tID Verify Failed\n");
|
||||
if (dev & STATUS_TIMEOUT)
|
||||
printf("\tTimeout\n");
|
||||
if (dev & STATUS_MEMORY_ERASE)
|
||||
printf("\tMemory Erase\n");
|
||||
if (dev & STATUS_PREAMBLE)
|
||||
printf("\tPreamble\n");
|
||||
if (dev & STATUS_SYSTEM_EDIT_MODE)
|
||||
printf("\tSystem Edit Mode\n");
|
||||
if (dev & STATUS_PRG_SPIFLASH_DIRECT)
|
||||
printf("\tProgram spi flash directly\n");
|
||||
if (dev & STATUS_NON_JTAG_CNF_ACTIVE)
|
||||
printf("\tNon-jtag is active\n");
|
||||
if (dev & STATUS_BYPASS)
|
||||
printf("\tBypass\n");
|
||||
if (dev & STATUS_GOWIN_VLD)
|
||||
printf("\tGowin VLD\n");
|
||||
if (dev & STATUS_DONE_FINAL)
|
||||
printf("\tDone Final\n");
|
||||
if (dev & STATUS_SECURITY_FINAL)
|
||||
printf("\tSecurity Final\n");
|
||||
if (dev & STATUS_READY)
|
||||
printf("\tReady\n");
|
||||
if (dev & STATUS_POR)
|
||||
printf("\tPOR\n");
|
||||
if (dev & STATUS_FLASH_LOCK)
|
||||
printf("\tFlash Lock\n");
|
||||
}
|
||||
|
||||
bool Gowin::pollFlag(uint32_t mask, uint32_t value)
|
||||
{
|
||||
uint32_t status;
|
||||
int timeout = 0;
|
||||
do {
|
||||
status = readStatusReg();
|
||||
if (_verbose)
|
||||
printf("pollFlag: %x\n", status);
|
||||
if (timeout == 100000000){
|
||||
printError("timeout");
|
||||
return false;
|
||||
}
|
||||
timeout++;
|
||||
} while ((status & mask) != value);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/* TN653 p. 17-21 */
|
||||
bool Gowin::flashFLASH(uint8_t *data, int length)
|
||||
{
|
||||
uint8_t tx[4] = {0x4E, 0x31, 0x57, 0x47};
|
||||
uint8_t tmp[4];
|
||||
uint32_t addr;
|
||||
int nb_iter;
|
||||
int byte_length = length / 8;
|
||||
uint8_t tt[39];
|
||||
bzero(tt, 39);
|
||||
|
||||
ProgressBar progress("Flash SRAM", byte_length, 50);
|
||||
_jtag->go_test_logic_reset();
|
||||
|
||||
/* we have to send
|
||||
* bootcode a X=0, Y=0 (4Bytes)
|
||||
* 5 x 32 dummy bits
|
||||
* full bitstream
|
||||
*/
|
||||
int buffer_length = byte_length+(6*4);
|
||||
unsigned char buffer[byte_length+(6*4)] = {
|
||||
0x47, 0x57, 0x31, 0x4E,
|
||||
0xff, 0xff , 0xff, 0xff,
|
||||
0xff, 0xff , 0xff, 0xff,
|
||||
0xff, 0xff , 0xff, 0xff,
|
||||
0xff, 0xff , 0xff, 0xff,
|
||||
0xff, 0xff , 0xff, 0xff};
|
||||
memcpy(buffer+6*4, data, byte_length);
|
||||
|
||||
int nb_xpage = buffer_length/256;
|
||||
if (nb_xpage * 256 != buffer_length)
|
||||
nb_xpage++;
|
||||
|
||||
for (int i=0, xpage=0; xpage < nb_xpage; i+=(nb_iter*4), xpage++) {
|
||||
wr_rd(CONFIG_ENABLE, NULL, 0, NULL, 0);
|
||||
wr_rd(EF_PROGRAM, NULL, 0, NULL, 0);
|
||||
_jtag->read_write(tt, NULL, 312, 0);
|
||||
addr = xpage << 6;
|
||||
tmp[3] = 0xff&(addr >> 24);
|
||||
tmp[2] = 0xff&(addr >> 16);
|
||||
tmp[1] = 0xff&(addr >> 8);
|
||||
tmp[0] = addr&0xff;
|
||||
_jtag->shiftDR(tmp, NULL, 32);
|
||||
_jtag->read_write(tt, NULL, 312, 0);
|
||||
|
||||
int xoffset = xpage * 256; // each page containt 256Bytes
|
||||
if (xoffset + 256 > buffer_length)
|
||||
nb_iter = (buffer_length-xoffset) / 4;
|
||||
else
|
||||
nb_iter = 64;
|
||||
|
||||
for (int ypage = 0; ypage < nb_iter; ypage++) {
|
||||
unsigned char *t = buffer+xoffset + 4*ypage;
|
||||
for (int x=0; x < 4; x++)
|
||||
tx[3-x] = t[x];
|
||||
_jtag->shiftDR(tx, NULL, 32);
|
||||
_jtag->read_write(tt, NULL, 40, 0);
|
||||
}
|
||||
progress.display(i);
|
||||
}
|
||||
/* 2.2.6.6 */
|
||||
_jtag->set_state(FtdiJtag::RUN_TEST_IDLE);
|
||||
|
||||
progress.done();
|
||||
return true;
|
||||
}
|
||||
|
||||
/* TN653 p. 9 */
|
||||
bool Gowin::flashSRAM(uint8_t *data, int length)
|
||||
{
|
||||
int tx_len, tx_end;
|
||||
int byte_length = length / 8;
|
||||
|
||||
ProgressBar progress("Flash SRAM", byte_length, 50);
|
||||
|
||||
/* 2.2.6.4 */
|
||||
wr_rd(XFER_WRITE, NULL, 0, NULL, 0);
|
||||
|
||||
/* 2.2.6.5 */
|
||||
_jtag->set_state(FtdiJtag::SHIFT_DR);
|
||||
|
||||
for (int i=0; i < byte_length; i+=256) {
|
||||
if (i + 256 > byte_length) { // last packet with some size
|
||||
tx_len = (byte_length - i) * 8;
|
||||
tx_end = 1; // to move in EXIT1_DR
|
||||
} else {
|
||||
tx_len = 256 * 8;
|
||||
tx_end = 0;
|
||||
}
|
||||
_jtag->read_write(data+i, NULL, tx_len, tx_end);
|
||||
_jtag->flush();
|
||||
progress.display(i);
|
||||
}
|
||||
/* 2.2.6.6 */
|
||||
_jtag->set_state(FtdiJtag::RUN_TEST_IDLE);
|
||||
|
||||
/* p.15 fig 2.11 */
|
||||
wr_rd(XFER_DONE, NULL, 0, NULL, 0);
|
||||
if (pollFlag(STATUS_DONE_FINAL, STATUS_DONE_FINAL)) {
|
||||
progress.done();
|
||||
return true;
|
||||
} else {
|
||||
progress.fail();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/* Erase SRAM:
|
||||
* TN653 p.14-17
|
||||
*/
|
||||
bool Gowin::eraseFLASH()
|
||||
{
|
||||
unsigned char tx[4] = {0, 0, 0, 0};
|
||||
printInfo("erase Flash ", false);
|
||||
wr_rd(EFLASH_ERASE, NULL, 0, NULL, 0);
|
||||
_jtag->set_state(FtdiJtag::RUN_TEST_IDLE);
|
||||
_jtag->shiftDR(tx, NULL, 32);
|
||||
/* TN653 specifies to wait for 120ms with
|
||||
* there are no bit in status register to specify
|
||||
* when this operation is done so we need to wait
|
||||
*/
|
||||
usleep(120000);
|
||||
printSuccess("Done");
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Erase SRAM:
|
||||
* TN653 p.9-10, 14 and 31
|
||||
*/
|
||||
bool Gowin::eraseSRAM()
|
||||
{
|
||||
printInfo("erase SRAM ", false);
|
||||
wr_rd(ERASE_SRAM, NULL, 0, NULL, 0);
|
||||
wr_rd(NOOP, NULL, 0, NULL, 0);
|
||||
|
||||
/* TN653 specifies to wait for 4ms with
|
||||
* clock generated but
|
||||
* status register bit MEMORY_ERASE goes low when ERASE_SRAM
|
||||
* is send and goes high after erase
|
||||
* this check seems enough
|
||||
*/
|
||||
if (pollFlag(STATUS_MEMORY_ERASE, STATUS_MEMORY_ERASE)) {
|
||||
printSuccess("Done");
|
||||
return true;
|
||||
} else {
|
||||
printError("FAIL");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
/*
|
||||
* Copyright (C) 2019 Gwenhael Goavec-Merou <[email protected]>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef GOWIN_HPP_
|
||||
#define GOWIN_HPP_
|
||||
|
||||
#include <stdint.h>
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "device.hpp"
|
||||
#include "fsparser.hpp"
|
||||
#include "ftdijtag.hpp"
|
||||
#include "jedParser.hpp"
|
||||
|
||||
class Gowin: public Device {
|
||||
public:
|
||||
Gowin(FtdiJtag *jtag, std::string filename, bool flash_wr, bool sram_wr,
|
||||
bool verbose);
|
||||
~Gowin();
|
||||
int idCode() override;
|
||||
void reset() override;
|
||||
void program(unsigned int offset) override;
|
||||
void programFlash();
|
||||
|
||||
private:
|
||||
bool wr_rd(uint8_t cmd, uint8_t *tx, int tx_len,
|
||||
uint8_t *rx, int rx_len, bool verbose = false);
|
||||
bool EnableCfg();
|
||||
bool DisableCfg();
|
||||
bool pollFlag(uint32_t mask, uint32_t value);
|
||||
bool eraseSRAM();
|
||||
bool eraseFLASH();
|
||||
bool flashSRAM(uint8_t *data, int length);
|
||||
bool flashFLASH(uint8_t *data, int length);
|
||||
void displayReadReg(uint32_t dev);
|
||||
uint32_t readStatusReg();
|
||||
uint32_t readUserCode();
|
||||
FsParser *_fs;
|
||||
};
|
||||
#endif // GOWIN_HPP_
|
||||
@@ -0,0 +1,300 @@
|
||||
/*
|
||||
* Copyright (C) 2019 Gwenhael Goavec-Merou <[email protected]>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <strings.h>
|
||||
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <iterator>
|
||||
#include <sstream>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "jedParser.hpp"
|
||||
|
||||
/* GGM: TODO
|
||||
* - use NOTE for Lxxx
|
||||
* - be less lattice compliant
|
||||
*/
|
||||
|
||||
using namespace std;
|
||||
|
||||
JedParser::JedParser(string filename, bool verbose):
|
||||
ConfigBitstreamParser(filename, ConfigBitstreamParser::BIN_MODE),
|
||||
_fuse_count(0), _pin_count(0), _featuresRow(0), _feabits(0), _checksum(0),
|
||||
_userCode(0), _security_settings(0), _default_fuse_state(0)
|
||||
{
|
||||
}
|
||||
|
||||
/* fill a vector with consecutive lines until '*'
|
||||
*/
|
||||
vector<string> JedParser::readJEDLine()
|
||||
{
|
||||
string buffer;
|
||||
vector<string> lines;
|
||||
bool inLine = true;
|
||||
|
||||
do {
|
||||
std::getline(_fd, buffer, '\n');
|
||||
if (buffer.size() == 0)
|
||||
break;
|
||||
|
||||
if (buffer[buffer.size()-1] == '*') {
|
||||
inLine = false;
|
||||
buffer.pop_back();
|
||||
}
|
||||
lines.push_back(buffer);
|
||||
} while (inLine);
|
||||
return lines;
|
||||
}
|
||||
|
||||
/* convert one serie ASCII 1/0 to a vector of
|
||||
* unsigned char
|
||||
*/
|
||||
void JedParser::buildDataArray(const string &content, struct jed_data &jed)
|
||||
{
|
||||
size_t data_len = content.size();
|
||||
string tmp_buff;
|
||||
uint8_t data = 0;
|
||||
for (size_t i = 0; i < content.size(); i+=8) {
|
||||
data = 0;
|
||||
for (int ii = 0; ii < 8; ii++) {
|
||||
uint8_t val = (content[i+ii] == '1'?1:0);
|
||||
data |= val << ii;
|
||||
}
|
||||
tmp_buff += data;
|
||||
}
|
||||
jed.data.push_back(std::move(tmp_buff));
|
||||
jed.len += data_len;
|
||||
}
|
||||
|
||||
void JedParser::display()
|
||||
{
|
||||
printf("feabits :\n");
|
||||
printf("%04x <-> %d\n", _feabits, _feabits);
|
||||
/* 15-14: always 0 */
|
||||
printf("\tBoot Mode : ");
|
||||
switch ((_feabits>>11)&0x07) {
|
||||
case 0:
|
||||
printf("Single Boot from Configuration Flash\n");
|
||||
break;
|
||||
case 1:
|
||||
printf("Dual Boot from Configuration Flash then External if there is a failure\n");
|
||||
break;
|
||||
case 3:
|
||||
printf("Single Boot from External Flash\n");
|
||||
break;
|
||||
default:
|
||||
printf("Error\n");
|
||||
}
|
||||
|
||||
printf("\tMaster Mode SPI : %s\n",
|
||||
(((_feabits>>11)&0x01)?"enable":"disable"));
|
||||
printf("\tI2c port : %s\n",
|
||||
(((_feabits>>10)&0x01)?"disable":"enable"));
|
||||
printf("\tSlave SPI port : %s\n",
|
||||
(((_feabits>>9)&0x01)?"disable":"enable"));
|
||||
printf("\tJTAG port : %s\n",
|
||||
(((_feabits>>8)&0x01)?"disable":"enable"));
|
||||
printf("\tDONE : %s\n",
|
||||
(((_feabits>>7)&0x01)?"enable":"disable"));
|
||||
printf("\tINITN : %s\n",
|
||||
(((_feabits>>6)&0x01)?"enable":"disable"));
|
||||
printf("\tPROGRAMN : %s\n",
|
||||
(((_feabits>>5)&0x01)?"disable":"enable"));
|
||||
printf("\tMy_ASSP : %s\n",
|
||||
(((_feabits>>4)&0x01)?"enable":"disable"));
|
||||
/* 3-0: always 0 */
|
||||
|
||||
printf("Pin Count : %d\n", _pin_count);
|
||||
printf("Fuse Count : %d\n", _fuse_count);
|
||||
}
|
||||
|
||||
/* E field, for latice contains two sub-field
|
||||
* 1: Exxxx\n : feature Row
|
||||
* 2: yyyy*\n : feabits
|
||||
*/
|
||||
void JedParser::parseEField(vector<string> content)
|
||||
{
|
||||
_featuresRow = 0;
|
||||
string featuresRow = content[0].substr(1);
|
||||
for (size_t i = 0; i < featuresRow.size(); i++)
|
||||
_featuresRow |= ((featuresRow[i] - '0') << i);
|
||||
string feabits = content[1];
|
||||
_feabits = 0;
|
||||
for (size_t i = 0; i < feabits.size(); i++) {
|
||||
_feabits |= ((feabits[i] - '0') << i);
|
||||
}
|
||||
}
|
||||
|
||||
void JedParser::parseLField(vector<string> content)
|
||||
{
|
||||
int start_offset;
|
||||
sscanf(content[0].substr(1).c_str(), "%d", &start_offset);
|
||||
/* two possibilities
|
||||
* current line finish with '*' : Lxxxx YYYYY*<EOF>
|
||||
* or current line is only offset and next(s) line(s) are data :
|
||||
* Lxxxx<EOF>
|
||||
*/
|
||||
struct jed_data d;
|
||||
string buffer;
|
||||
d.offset = start_offset;
|
||||
d.len = 0;
|
||||
if (content.size() > 1) {
|
||||
for (size_t i = 1; i < content.size(); i++) {
|
||||
if (content[i].size() != 0)
|
||||
buildDataArray((content[i]), d);
|
||||
}
|
||||
} else {
|
||||
// search space
|
||||
std::istringstream iss(content[0]);
|
||||
vector<string> myList((std::istream_iterator<string>(iss)),
|
||||
std::istream_iterator<string>());
|
||||
myList[1].pop_back();
|
||||
buildDataArray(myList[1], d);
|
||||
}
|
||||
_data_list.push_back(std::move(d));
|
||||
}
|
||||
|
||||
int JedParser::parse()
|
||||
{
|
||||
string previousNote;
|
||||
|
||||
if (!_fd.is_open()) {
|
||||
_fd.open(_filename);
|
||||
if (!_fd.is_open()) {
|
||||
cerr << "error to opening jed file " << _filename << endl;
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
}
|
||||
|
||||
string content;
|
||||
|
||||
_fd.seekg(0, _fd.beg);
|
||||
|
||||
/* First line must STX (0x02) */
|
||||
std::getline(_fd, content, '\n');
|
||||
if (content[0] != 0x02) {
|
||||
printf("wrong file\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* read full content
|
||||
* JED file end fix ETX (0x03) + file checksum + \n
|
||||
*/
|
||||
std::vector<string>lines;
|
||||
do {
|
||||
lines = readJEDLine();
|
||||
if (lines.size() == 0)
|
||||
break;
|
||||
|
||||
switch (lines[0][0]) {
|
||||
case 'N': // note
|
||||
previousNote = lines[0].substr(5);
|
||||
break;
|
||||
case 'Q':
|
||||
int count;
|
||||
sscanf(lines[0].c_str()+2, "%d", &count);
|
||||
switch (lines[0][1]) {
|
||||
case 'F': // fuse count
|
||||
_fuse_count = count;
|
||||
break;
|
||||
case 'P': // pin count
|
||||
_pin_count = count;
|
||||
break;
|
||||
default:
|
||||
cerr << "Error for 'Q' unknown qualifier " << lines[1] << endl;
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
break;
|
||||
case 'G':
|
||||
_security_settings = static_cast<uint8_t>(lines[0][1]) - '0';
|
||||
break;
|
||||
case 'F':
|
||||
_default_fuse_state = lines[0][1] - '0';
|
||||
break;
|
||||
case 'C':
|
||||
sscanf(lines[0].c_str() + 1, "%hx", &_checksum);
|
||||
break;
|
||||
case 0x03:
|
||||
if (_verbose)
|
||||
cout << "end" << endl;
|
||||
break;
|
||||
case 'E':
|
||||
parseEField(lines);
|
||||
break;
|
||||
case 'L': // fuse offset
|
||||
parseLField(lines);
|
||||
_data_list[_data_list.size()-1].associatedPrevNote = previousNote;
|
||||
break;
|
||||
case 'U': // userCode
|
||||
switch (lines[0][1]) {
|
||||
case 'H': /* hex */
|
||||
sscanf(lines[0].c_str() + 2, "%x", &_userCode);
|
||||
break;
|
||||
case 'A': /* ASCII */
|
||||
sscanf(lines[0].c_str() + 2, "%d", &_userCode);
|
||||
break;
|
||||
default: /* binary */
|
||||
for (size_t ii = 1; ii < lines[0].size(); ii++)
|
||||
_userCode = ((_userCode << 1) | (lines[0][ii] - '0'));
|
||||
}
|
||||
break;
|
||||
default:
|
||||
printf("inconnu\n");
|
||||
cout << lines[0]<< endl;
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
} while (lines[0][0] != 0x03);
|
||||
|
||||
int size = 0;
|
||||
for (size_t i = 0; i < _data_list.size(); i++) {
|
||||
if (_verbose) {
|
||||
printf("area[%ld] %d %d ", i, _data_list[i].offset, _data_list[i].len);
|
||||
printf("%s\n", _data_list[i].associatedPrevNote.c_str());
|
||||
}
|
||||
size += _data_list[i].len;
|
||||
}
|
||||
|
||||
uint16_t checksum = 0;
|
||||
for (size_t line = 0; line < _data_list[0].data.size(); line++) {
|
||||
for (size_t col = 0; col < _data_list[0].data[line].size(); col++)
|
||||
checksum += (uint8_t)_data_list[0].data[line][col];
|
||||
}
|
||||
|
||||
if (_verbose)
|
||||
printf("theorical checksum %x -> %x\n", _checksum, checksum);
|
||||
if (_checksum != checksum) {
|
||||
cerr << "Error: wrong checksum" << endl;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (_verbose)
|
||||
printf("array size %ld\n", _data_list[0].data.size());
|
||||
|
||||
if (_fuse_count != size) {
|
||||
cerr << "Not all fuses are programmed" << endl;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
/*
|
||||
* Copyright (C) 2019 Gwenhael Goavec-Merou <[email protected]>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#ifndef JEDPARSER_HPP_
|
||||
#define JEDPARSER_HPP_
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "configBitstreamParser.hpp"
|
||||
|
||||
class JedParser: public ConfigBitstreamParser {
|
||||
private:
|
||||
struct jed_data {
|
||||
int offset;
|
||||
std::vector<std::string> data;
|
||||
int len;
|
||||
std::string associatedPrevNote;
|
||||
};
|
||||
|
||||
public:
|
||||
JedParser(std::string filename, bool verbose = false);
|
||||
int parse() override;
|
||||
void display();
|
||||
|
||||
size_t nb_section() { return _data_list.size();}
|
||||
size_t offset_for_section(int id) {return _data_list[id].offset;}
|
||||
std::vector<std::string> data_for_section(int id) {
|
||||
return _data_list[id].data;
|
||||
}
|
||||
std::string noteForSection(int id) {return _data_list[id].associatedPrevNote;}
|
||||
uint32_t feabits() {return _feabits;}
|
||||
uint64_t featuresRow() {return _featuresRow;}
|
||||
|
||||
private:
|
||||
std::vector<std::string>readJEDLine();
|
||||
void buildDataArray(const std::string &content, struct jed_data &jed);
|
||||
void parseEField(const std::vector<std::string> content);
|
||||
void parseLField(const std::vector<std::string> content);
|
||||
|
||||
std::vector<struct jed_data> _data_list;
|
||||
int _fuse_count;
|
||||
int _pin_count;
|
||||
uint64_t _featuresRow;
|
||||
uint16_t _feabits;
|
||||
uint16_t _checksum;
|
||||
uint32_t _userCode;
|
||||
uint8_t _security_settings;
|
||||
uint8_t _default_fuse_state;
|
||||
};
|
||||
|
||||
#endif // JEDPARSER_HPP_
|
||||
+821
@@ -0,0 +1,821 @@
|
||||
/*
|
||||
* Copyright (C) 2019 Gwenhael Goavec-Merou <[email protected]>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <strings.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <iostream>
|
||||
|
||||
#include "ftdijtag.hpp"
|
||||
#include "lattice.hpp"
|
||||
#include "progressBar.hpp"
|
||||
#include "display.hpp"
|
||||
|
||||
using namespace std;
|
||||
|
||||
#define ISC_ENABLE 0xc6
|
||||
# define ISC_ENABLE_FLASH_MODE (1 << 3)
|
||||
# define ISC_ENABLE_SRAM_MODE (0 << 3)
|
||||
#define ISC_DISABLE 0x26
|
||||
#define READ_DEVICE_ID_CODE 0xE0
|
||||
#define FLASH_ERASE 0x0E
|
||||
# define FLASH_ERASE_UFM (1<<3)
|
||||
# define FLASH_ERASE_CFG (1<<2)
|
||||
# define FLASH_ERASE_FEATURE (1<<1)
|
||||
# define FLASH_ERASE_SRAM (1<<0)
|
||||
# define FLASH_ERASE_ALL 0x0F
|
||||
#define CHECK_BUSY_FLAG 0xF0
|
||||
# define CHECK_BUSY_FLAG_BUSY (1 << 7)
|
||||
#define RESET_CFG_ADDR 0x46
|
||||
#define PROG_CFG_FLASH 0x70
|
||||
#define PROG_FEATURE_ROW 0xE4
|
||||
#define PROG_FEABITS 0xF8
|
||||
#define PROG_DONE 0x5E
|
||||
#define REFRESH 0x79
|
||||
|
||||
#define READ_FEATURE_ROW 0xE7
|
||||
#define READ_FEABITS 0xFB
|
||||
#define READ_STATUS_REGISTER 0x3C
|
||||
# define REG_STATUS_DONE (1 << 8)
|
||||
# define REG_STATUS_ISC_EN (1 << 9)
|
||||
# define REG_STATUS_BUSY (1 << 12)
|
||||
# define REG_STATUS_FAIL (1 << 13)
|
||||
# define REG_STATUS_CNF_CHK_MASK (0x7 << 23)
|
||||
# define REG_STATUS_EXEC_ERR (1 << 26)
|
||||
|
||||
Lattice::Lattice(FtdiJtag *jtag, const string filename, bool verbose):
|
||||
Device(jtag, filename, verbose)
|
||||
{
|
||||
if (_filename != "") {
|
||||
if (_file_extension == "jed") {
|
||||
_mode = Device::FLASH_MODE;
|
||||
} else if (_file_extension == "bit") {
|
||||
_mode = Device::MEM_MODE;
|
||||
} else {
|
||||
throw std::exception();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void displayFeabits(uint16_t _featbits)
|
||||
{
|
||||
uint8_t boot_sequence = (_featbits >> 12) & 0x03;
|
||||
uint8_t m = (_featbits >> 11) & 0x01;
|
||||
printf("\tboot mode :");
|
||||
switch (boot_sequence) {
|
||||
case 0:
|
||||
if (m != 0x01)
|
||||
printf(" Single Boot from NVCM/Flash\n");
|
||||
else
|
||||
printf(" Dual Boot from NVCM/Flash then External if there is a failure\n");
|
||||
break;
|
||||
case 1:
|
||||
if (m == 0x01)
|
||||
printf(" Single Boot from External Flash\n");
|
||||
else
|
||||
printf(" Error!\n");
|
||||
break;
|
||||
default:
|
||||
printf(" Error!\n");
|
||||
}
|
||||
printf("\tMaster Mode SPI : %s\n",
|
||||
(((_featbits>>11)&0x01)?"enable":"disable"));
|
||||
printf("\tI2c port : %s\n",
|
||||
(((_featbits>>10)&0x01)?"disable":"enable"));
|
||||
printf("\tSlave SPI port : %s\n",
|
||||
(((_featbits>>9)&0x01)?"disable":"enable"));
|
||||
printf("\tJTAG port : %s\n",
|
||||
(((_featbits>>8)&0x01)?"disable":"enable"));
|
||||
printf("\tDONE : %s\n",
|
||||
(((_featbits>>7)&0x01)?"enable":"disable"));
|
||||
printf("\tINITN : %s\n",
|
||||
(((_featbits>>6)&0x01)?"enable":"disable"));
|
||||
printf("\tPROGRAMN : %s\n",
|
||||
(((_featbits>>5)&0x01)?"disable":"enable"));
|
||||
printf("\tMy_ASSP : %s\n",
|
||||
(((_featbits>>4)&0x01)?"enable":"disable"));
|
||||
printf("\tPassword (Flash Protect Key) Protect All : %s\n",
|
||||
(((_featbits>>3)&0x01)?"Enaabled" : "Disabled"));
|
||||
printf("\tPassword (Flash Protect Key) Protect : %s\n",
|
||||
(((_featbits>>2)&0x01)?"Enabled" : "Disabled"));
|
||||
}
|
||||
|
||||
bool Lattice::checkStatus(uint32_t val, uint32_t mask)
|
||||
{
|
||||
uint32_t reg = readStatusReg();
|
||||
|
||||
return ((reg & mask) == val) ? true : false;
|
||||
}
|
||||
|
||||
bool Lattice::program_mem()
|
||||
{
|
||||
bool err;
|
||||
LatticeBitParser _bit(_filename, _verbose);
|
||||
|
||||
printInfo("Open file " + _filename + " ", false);
|
||||
printSuccess("DONE");
|
||||
|
||||
err = _bit.parse();
|
||||
|
||||
printInfo("Parse file ", false);
|
||||
if (err == EXIT_FAILURE) {
|
||||
printError("FAIL");
|
||||
return false;
|
||||
} else {
|
||||
printSuccess("DONE");
|
||||
}
|
||||
|
||||
if (_verbose)
|
||||
_bit.displayHeader();
|
||||
|
||||
/* read ID Code 0xE0 */
|
||||
if (_verbose) {
|
||||
printf("IDCode : %x\n", idCode());
|
||||
displayReadReg(readStatusReg());
|
||||
}
|
||||
|
||||
/* preload 0x1C */
|
||||
uint8_t tx_buf[26];
|
||||
memset(tx_buf, 0xff, 26);
|
||||
wr_rd(0x1C, tx_buf, 26, NULL, 0);
|
||||
|
||||
wr_rd(0xFf, NULL, 0, NULL, 0);
|
||||
|
||||
/* ISC Enable 0xC6 */
|
||||
printInfo("Enable configuration: ", false);
|
||||
if (!EnableISC(0x00)) {
|
||||
printError("FAIL");
|
||||
displayReadReg(readStatusReg());
|
||||
return false;
|
||||
} else {
|
||||
printSuccess("DONE");
|
||||
}
|
||||
|
||||
/* ISC ERASE */
|
||||
printInfo("SRAM erase: ", false);
|
||||
if (flashErase(FLASH_ERASE_SRAM) == false) {
|
||||
printError("FAIL");
|
||||
displayReadReg(readStatusReg());
|
||||
return false;
|
||||
} else {
|
||||
printSuccess("DONE");
|
||||
}
|
||||
|
||||
/* LSC_INIT_ADDRESS */
|
||||
wr_rd(0x46, NULL, 0, NULL, 0);
|
||||
_jtag->set_state(FtdiJtag::RUN_TEST_IDLE);
|
||||
_jtag->toggleClk(1000);
|
||||
|
||||
uint8_t *data = _bit.getData();
|
||||
int length = _bit.getLength()/8;
|
||||
wr_rd(0x7A, NULL, 0, NULL, 0);
|
||||
_jtag->set_state(FtdiJtag::RUN_TEST_IDLE);
|
||||
_jtag->toggleClk(2);
|
||||
|
||||
uint8_t tmp[1024];
|
||||
int size = 1024;
|
||||
|
||||
ProgressBar progress("Loading", length, 50);
|
||||
|
||||
for (int i = 0; i < length; i += size) {
|
||||
progress.display(i);
|
||||
|
||||
if (length < i + size)
|
||||
size = length-i;
|
||||
|
||||
for (int ii = 0; ii < size; ii++)
|
||||
tmp[ii] = ConfigBitstreamParser::reverseByte(data[i+ii]);
|
||||
|
||||
_jtag->shiftDR(tmp, NULL, size*8, FtdiJtag::SHIFT_DR);
|
||||
}
|
||||
|
||||
_jtag->set_state(FtdiJtag::RUN_TEST_IDLE);
|
||||
|
||||
if (checkStatus(0, REG_STATUS_CNF_CHK_MASK))
|
||||
progress.done();
|
||||
else {
|
||||
progress.fail();
|
||||
displayReadReg(readStatusReg());
|
||||
return false;
|
||||
}
|
||||
|
||||
wr_rd(0xff, NULL, 0, NULL, 0);
|
||||
|
||||
if (_verbose)
|
||||
printf("userCode: %08x\n", userCode());
|
||||
|
||||
/* bypass */
|
||||
wr_rd(0xff, NULL, 0, NULL, 0);
|
||||
/* disable configuration mode */
|
||||
printInfo("Disable configuration: ", false);
|
||||
if (!DisableISC()) {
|
||||
printError("FAIL");
|
||||
displayReadReg(readStatusReg());
|
||||
return false;
|
||||
} else {
|
||||
printSuccess("DONE");
|
||||
}
|
||||
|
||||
if (_verbose)
|
||||
displayReadReg(readStatusReg());
|
||||
|
||||
/* bypass */
|
||||
wr_rd(0xff, NULL, 0, NULL, 0);
|
||||
_jtag->go_test_logic_reset();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Lattice::program_flash(unsigned int offset)
|
||||
{
|
||||
(void) offset;
|
||||
bool err;
|
||||
uint64_t featuresRow;
|
||||
uint16_t feabits;
|
||||
uint8_t eraseMode;
|
||||
vector<string> ufm_data, cfg_data;
|
||||
|
||||
JedParser _jed(_filename, _verbose);
|
||||
|
||||
printInfo("Open file " + _filename + " ", false);
|
||||
printSuccess("DONE");
|
||||
|
||||
err = _jed.parse();
|
||||
|
||||
printInfo("Parse file ", false);
|
||||
if (err == EXIT_FAILURE) {
|
||||
printError("FAIL");
|
||||
return false;
|
||||
} else {
|
||||
printSuccess("DONE");
|
||||
}
|
||||
|
||||
/* read ID Code 0xE0 */
|
||||
if (_verbose) {
|
||||
printf("IDCode : %x\n", idCode());
|
||||
displayReadReg(readStatusReg());
|
||||
}
|
||||
|
||||
/* preload 0x1C */
|
||||
uint8_t tx_buf[26];
|
||||
memset(tx_buf, 0xff, 26);
|
||||
wr_rd(0x1C, tx_buf, 26, NULL, 0);
|
||||
|
||||
wr_rd(0xFf, NULL, 0, NULL, 0);
|
||||
|
||||
/* ISC Enable 0xC6 */
|
||||
printInfo("Enable configuration: ", false);
|
||||
if (!EnableISC(0x00)) {
|
||||
printError("FAIL");
|
||||
displayReadReg(readStatusReg());
|
||||
return false;
|
||||
} else {
|
||||
printSuccess("DONE");
|
||||
}
|
||||
/* ISC ERASE */
|
||||
printInfo("SRAM erase: ", false);
|
||||
if (flashErase(FLASH_ERASE_SRAM) == false) {
|
||||
printError("FAIL");
|
||||
displayReadReg(readStatusReg());
|
||||
return false;
|
||||
} else {
|
||||
printSuccess("DONE");
|
||||
}
|
||||
|
||||
/* bypass */
|
||||
wr_rd(0xff, NULL, 0, NULL, 0);
|
||||
/* ISC Enable 0xC6 followed by 0x08 */
|
||||
printInfo("Enable configuration: ", false);
|
||||
if (!EnableISC(0x08)) {
|
||||
printError("FAIL");
|
||||
displayReadReg(readStatusReg());
|
||||
return false;
|
||||
} else {
|
||||
printSuccess("DONE");
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < _jed.nb_section(); i++) {
|
||||
string note = _jed.noteForSection(i);
|
||||
if (note == "TAG DATA") {
|
||||
eraseMode |= FLASH_ERASE_UFM;
|
||||
ufm_data = _jed.data_for_section(i);
|
||||
} else if (note == "END CONFIG DATA") {
|
||||
continue;
|
||||
} else {
|
||||
cfg_data = _jed.data_for_section(i);
|
||||
}
|
||||
}
|
||||
|
||||
/* check if feature area must be updated */
|
||||
featuresRow = _jed.featuresRow();
|
||||
feabits = _jed.feabits();
|
||||
eraseMode = FLASH_ERASE_CFG;
|
||||
if (featuresRow != readFeaturesRow() || feabits != readFeabits())
|
||||
eraseMode |= FLASH_ERASE_FEATURE;
|
||||
|
||||
/* ISC ERASE */
|
||||
printInfo("Flash erase: ", false);
|
||||
if (flashErase(eraseMode) == false) {
|
||||
printError("FAIL");
|
||||
return false;
|
||||
} else {
|
||||
printSuccess("DONE");
|
||||
}
|
||||
|
||||
/* LSC_INIT_ADDRESS */
|
||||
wr_rd(0x46, NULL, 0, NULL, 0);
|
||||
_jtag->set_state(FtdiJtag::RUN_TEST_IDLE);
|
||||
_jtag->toggleClk(1000);
|
||||
|
||||
/* flash UFM */
|
||||
if (false == flashProg(0, cfg_data))
|
||||
return false;
|
||||
if (Verify(_jed) == false)
|
||||
return false;
|
||||
|
||||
/* missing usercode update */
|
||||
|
||||
/* LSC_INIT_ADDRESS */
|
||||
wr_rd(0x46, NULL, 0, NULL, 0);
|
||||
_jtag->set_state(FtdiJtag::RUN_TEST_IDLE);
|
||||
_jtag->toggleClk(1000);
|
||||
|
||||
if ((eraseMode & FLASH_ERASE_FEATURE) != 0) {
|
||||
/* write feature row */
|
||||
printInfo("Program features Row: ", false);
|
||||
if (writeFeaturesRow(_jed.featuresRow(), true) == false) {
|
||||
printError("FAIL");
|
||||
return false;
|
||||
} else {
|
||||
printSuccess("DONE");
|
||||
}
|
||||
/* write feabits */
|
||||
printInfo("Program feabits: ", false);
|
||||
if (writeFeabits(_jed.feabits(), true) == false) {
|
||||
printError("FAIL");
|
||||
return false;
|
||||
} else {
|
||||
printSuccess("DONE");
|
||||
}
|
||||
}
|
||||
|
||||
/* ISC program done 0x5E */
|
||||
printInfo("Write program Done: ", false);
|
||||
if (writeProgramDone() == false) {
|
||||
printError("FAIL");
|
||||
return false;
|
||||
} else {
|
||||
printSuccess("DONE");
|
||||
}
|
||||
|
||||
/* bypass */
|
||||
wr_rd(0xff, NULL, 0, NULL, 0);
|
||||
/* disable configuration mode */
|
||||
printInfo("Disable configuration: ", false);
|
||||
if (!DisableISC()) {
|
||||
printError("FAIL");
|
||||
return false;
|
||||
} else {
|
||||
printSuccess("DONE");
|
||||
}
|
||||
|
||||
/* ISC REFRESH 0x79 */
|
||||
printInfo("Refresh: ", false);
|
||||
if (loadConfiguration() == false) {
|
||||
printError("FAIL");
|
||||
return false;
|
||||
} else {
|
||||
printSuccess("DONE");
|
||||
}
|
||||
|
||||
/* bypass */
|
||||
wr_rd(0xff, NULL, 0, NULL, 0);
|
||||
_jtag->go_test_logic_reset();
|
||||
return true;
|
||||
}
|
||||
|
||||
void Lattice::program(unsigned int offset)
|
||||
{
|
||||
if (_mode == FLASH_MODE)
|
||||
program_flash(offset);
|
||||
else if (_mode == MEM_MODE)
|
||||
program_mem();
|
||||
|
||||
}
|
||||
|
||||
bool Lattice::EnableISC(uint8_t flash_mode)
|
||||
{
|
||||
wr_rd(ISC_ENABLE, &flash_mode, 1, NULL, 0);
|
||||
|
||||
_jtag->set_state(FtdiJtag::RUN_TEST_IDLE);
|
||||
_jtag->toggleClk(1000);
|
||||
if (!pollBusyFlag())
|
||||
return false;
|
||||
if (!checkStatus(REG_STATUS_ISC_EN, REG_STATUS_ISC_EN))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Lattice::DisableISC()
|
||||
{
|
||||
wr_rd(ISC_DISABLE, NULL, 0, NULL, 0);
|
||||
_jtag->set_state(FtdiJtag::RUN_TEST_IDLE);
|
||||
_jtag->toggleClk(1000);
|
||||
if (!pollBusyFlag())
|
||||
return false;
|
||||
if (!checkStatus(0, REG_STATUS_ISC_EN))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Lattice::EnableCfgIf()
|
||||
{
|
||||
uint8_t tx_buf = 0x08;
|
||||
wr_rd(0x74, &tx_buf, 1, NULL, 0);
|
||||
_jtag->set_state(FtdiJtag::RUN_TEST_IDLE);
|
||||
_jtag->toggleClk(1000);
|
||||
return pollBusyFlag();
|
||||
}
|
||||
|
||||
bool Lattice::DisableCfg()
|
||||
{
|
||||
uint8_t tx_buf, rx_buf;
|
||||
wr_rd(0x26, &tx_buf, 1, &rx_buf, 1);
|
||||
_jtag->set_state(FtdiJtag::RUN_TEST_IDLE);
|
||||
_jtag->toggleClk(1000);
|
||||
return true;
|
||||
}
|
||||
|
||||
int Lattice::idCode()
|
||||
{
|
||||
uint8_t device_id[4];
|
||||
wr_rd(READ_DEVICE_ID_CODE, NULL, 0, device_id, 4);
|
||||
return device_id[3] << 24 |
|
||||
device_id[2] << 16 |
|
||||
device_id[1] << 8 |
|
||||
device_id[0];
|
||||
}
|
||||
|
||||
int Lattice::userCode()
|
||||
{
|
||||
uint8_t usercode[4];
|
||||
wr_rd(0xC0, NULL, 0, usercode, 4);
|
||||
return usercode[3] << 24 |
|
||||
usercode[2] << 16 |
|
||||
usercode[1] << 8 |
|
||||
usercode[0];
|
||||
}
|
||||
|
||||
bool Lattice::checkID()
|
||||
{
|
||||
printf("\n");
|
||||
printf("check ID\n");
|
||||
uint8_t tx[4];
|
||||
wr_rd(0xE2, tx, 4, NULL, 0);
|
||||
_jtag->set_state(FtdiJtag::RUN_TEST_IDLE);
|
||||
_jtag->toggleClk(1000);
|
||||
|
||||
uint32_t reg = readStatusReg();
|
||||
displayReadReg(reg);
|
||||
|
||||
tx[3] = 0x61;
|
||||
tx[2] = 0x2b;
|
||||
tx[1] = 0xd0;
|
||||
tx[0] = 0x43;
|
||||
wr_rd(0xE2, tx, 4, NULL, 0);
|
||||
_jtag->set_state(FtdiJtag::RUN_TEST_IDLE);
|
||||
_jtag->toggleClk(1000);
|
||||
reg = readStatusReg();
|
||||
displayReadReg(reg);
|
||||
printf("%08x\n", reg);
|
||||
printf("\n");
|
||||
return true;
|
||||
}
|
||||
|
||||
/* feabits is MSB first
|
||||
* maybe this register too
|
||||
* or not
|
||||
*/
|
||||
uint32_t Lattice::readStatusReg()
|
||||
{
|
||||
uint32_t reg;
|
||||
uint8_t rx[4], tx[4];
|
||||
wr_rd(0x3C, tx, 4, rx, 4);
|
||||
_jtag->set_state(FtdiJtag::RUN_TEST_IDLE);
|
||||
_jtag->toggleClk(1000);
|
||||
reg = rx[3] << 24 | rx[2] << 16 | rx[1] << 8 | rx[0];
|
||||
return reg;
|
||||
}
|
||||
|
||||
bool Lattice::wr_rd(uint8_t cmd,
|
||||
uint8_t *tx, int tx_len,
|
||||
uint8_t *rx, int rx_len,
|
||||
bool verbose)
|
||||
{
|
||||
int xfer_len = rx_len;
|
||||
if (tx_len > rx_len)
|
||||
xfer_len = tx_len;
|
||||
|
||||
uint8_t xfer_tx[xfer_len];
|
||||
uint8_t xfer_rx[xfer_len];
|
||||
bzero(xfer_tx, xfer_len);
|
||||
int i;
|
||||
if (tx != NULL) {
|
||||
for (i = 0; i < tx_len; i++)
|
||||
xfer_tx[i] = tx[i];
|
||||
}
|
||||
|
||||
_jtag->shiftIR(&cmd, NULL, 8, FtdiJtag::PAUSE_IR);
|
||||
if (rx || tx) {
|
||||
_jtag->shiftDR(xfer_tx, (rx) ? xfer_rx : NULL, 8 * xfer_len,
|
||||
FtdiJtag::PAUSE_DR);
|
||||
}
|
||||
if (rx) {
|
||||
if (verbose) {
|
||||
for (i=xfer_len-1; i >= 0; i--)
|
||||
printf("%02x ", xfer_rx[i]);
|
||||
printf("\n");
|
||||
}
|
||||
for (i = 0; i < rx_len; i++)
|
||||
rx[i] = (xfer_rx[i]);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void Lattice::displayReadReg(uint32_t dev)
|
||||
{
|
||||
printf("displayReadReg\n");
|
||||
if (dev & 1<<0)
|
||||
printf("\tTRAN Mode\n");
|
||||
printf("\tConfig Target Selection : %x\n", (dev >> 1) & 0x07);
|
||||
if (dev & 1<<4)
|
||||
printf("\tJTAG Active\n");
|
||||
if (dev & 1<<5)
|
||||
printf("\tPWD Protect\n");
|
||||
if (dev & 1<<6)
|
||||
printf("\tOTP\n");
|
||||
if (dev & 1<<7)
|
||||
printf("\tDecrypt Enable\n");
|
||||
if (dev & REG_STATUS_DONE)
|
||||
printf("\tDone Flag\n");
|
||||
if (dev & REG_STATUS_ISC_EN)
|
||||
printf("\tISC Enable\n");
|
||||
if (dev & 1 << 10)
|
||||
printf("\tWrite Enable\n");
|
||||
if (dev & 1 << 11)
|
||||
printf("\tRead Enable\n");
|
||||
if (dev & REG_STATUS_BUSY)
|
||||
printf("\tBusy Flag\n");
|
||||
if (dev & REG_STATUS_FAIL)
|
||||
printf("\tFail Flag\n");
|
||||
if (dev & 1 << 14)
|
||||
printf("\tFFEA OTP\n");
|
||||
if (dev & 1 << 15)
|
||||
printf("\tDecrypt Only\n");
|
||||
if (dev & 1 << 16)
|
||||
printf("\tPWD Enable\n");
|
||||
if (dev & 1 << 17)
|
||||
printf("\tUFM OTP\n");
|
||||
if (dev & 1 << 18)
|
||||
printf("\tASSP\n");
|
||||
if (dev & 1 << 19)
|
||||
printf("\tSDM Enable\n");
|
||||
if (dev & 1 << 20)
|
||||
printf("\tEncryption PreAmble\n");
|
||||
if (dev & 1 << 21)
|
||||
printf("\tStd PreAmble\n");
|
||||
if (dev & 1 << 22)
|
||||
printf("\tSPIm Fail1\n");
|
||||
|
||||
uint8_t err = (dev >> 23)&0x07;
|
||||
printf("\t");
|
||||
switch (err) {
|
||||
case 0:
|
||||
printf("No err\n");
|
||||
break;
|
||||
case 1:
|
||||
printf("ID ERR\n");
|
||||
break;
|
||||
case 2:
|
||||
printf("CMD ERR\n");
|
||||
break;
|
||||
case 3:
|
||||
printf("CRC ERR\n");
|
||||
break;
|
||||
case 4:
|
||||
printf("Preamble ERR\n");
|
||||
break;
|
||||
case 5:
|
||||
printf("Abort ERR\n");
|
||||
break;
|
||||
case 6:
|
||||
printf("Overflow ERR\n");
|
||||
break;
|
||||
case 7:
|
||||
printf("SDM EOF\n");
|
||||
break;
|
||||
default:
|
||||
printf("unknown %x\n", err);
|
||||
}
|
||||
if (dev & REG_STATUS_EXEC_ERR)
|
||||
printf("\tEXEC Error\n");
|
||||
if (dev & 1 << 27)
|
||||
printf("\tDevice failed to verify\n");
|
||||
if (dev & 1 << 28)
|
||||
printf("\tInvalid Command\n");
|
||||
if (dev & 1 << 29) printf("\tSED Error\n");
|
||||
if (dev & 1 << 30) printf("\tBypass Mode\n");
|
||||
if (dev & ((uint32_t)1 << 31)) printf("\tFT Mode\n");
|
||||
}
|
||||
|
||||
bool Lattice::pollBusyFlag(bool verbose)
|
||||
{
|
||||
uint8_t rx;
|
||||
int timeout = 0;
|
||||
do {
|
||||
wr_rd(CHECK_BUSY_FLAG, NULL, 0, &rx, 1);
|
||||
_jtag->set_state(FtdiJtag::RUN_TEST_IDLE);
|
||||
_jtag->toggleClk(1000);
|
||||
if (verbose)
|
||||
printf("pollBusyFlag :%02x\n", rx);
|
||||
if (timeout == 100000000){
|
||||
cerr << "timeout" << endl;
|
||||
return false;
|
||||
} else {
|
||||
timeout++;
|
||||
}
|
||||
} while (rx != 0);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Lattice::flashEraseAll()
|
||||
{
|
||||
return flashErase(0xf);
|
||||
}
|
||||
|
||||
bool Lattice::flashErase(uint8_t mask)
|
||||
{
|
||||
uint8_t tx[1] = {mask};
|
||||
wr_rd(FLASH_ERASE, tx, 1, NULL, 0);
|
||||
_jtag->set_state(FtdiJtag::RUN_TEST_IDLE);
|
||||
_jtag->toggleClk(1000);
|
||||
if (!pollBusyFlag())
|
||||
return false;
|
||||
if (!checkStatus(0, REG_STATUS_FAIL))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Lattice::flashProg(uint32_t start_addr, std::vector<std::string> data)
|
||||
{
|
||||
(void)start_addr;
|
||||
ProgressBar progress("Writing", data.size(), 50);
|
||||
for (uint32_t line = 0; line < data.size(); line++) {
|
||||
wr_rd(PROG_CFG_FLASH, (uint8_t *)data[line].c_str(),
|
||||
16, NULL, 0);
|
||||
_jtag->set_state(FtdiJtag::RUN_TEST_IDLE);
|
||||
_jtag->toggleClk(1000);
|
||||
progress.display(line);
|
||||
if (pollBusyFlag() == false)
|
||||
return false;
|
||||
}
|
||||
progress.done();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Lattice::Verify(JedParser &_jed, bool unlock)
|
||||
{
|
||||
uint8_t tx_buf[16], rx_buf[16];
|
||||
if (unlock)
|
||||
EnableISC(0x08);
|
||||
|
||||
wr_rd(0x46, NULL, 0, NULL, 0);
|
||||
_jtag->set_state(FtdiJtag::RUN_TEST_IDLE);
|
||||
_jtag->toggleClk(1000);
|
||||
|
||||
tx_buf[0] = 0x73;
|
||||
_jtag->shiftIR(tx_buf, NULL, 8, FtdiJtag::PAUSE_IR);
|
||||
|
||||
bzero(tx_buf, 16);
|
||||
bool failure = false;
|
||||
vector<string> data = _jed.data_for_section(0);
|
||||
ProgressBar progress("Verifying", data.size(), 50);
|
||||
for (size_t line = 0; line< data.size(); line++) {
|
||||
_jtag->set_state(FtdiJtag::RUN_TEST_IDLE);
|
||||
_jtag->toggleClk(2);
|
||||
_jtag->shiftDR(tx_buf, rx_buf, 16*8, FtdiJtag::PAUSE_DR);
|
||||
for (size_t i = 0; i < data[i].size(); i++) {
|
||||
if (rx_buf[i] != (unsigned char)data[line][i]) {
|
||||
printf("%3ld %3ld %02x -> %02x\n", line, i,
|
||||
rx_buf[i], (unsigned char)data[line][i]);
|
||||
failure = true;
|
||||
}
|
||||
}
|
||||
if (failure) {
|
||||
printf("Verify Failure\n");
|
||||
break;
|
||||
}
|
||||
progress.display(line);
|
||||
}
|
||||
if (unlock)
|
||||
DisableISC();
|
||||
|
||||
progress.done();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
uint64_t Lattice::readFeaturesRow()
|
||||
{
|
||||
uint8_t tx_buf[8];
|
||||
uint8_t rx_buf[8];
|
||||
uint64_t reg = 0;
|
||||
bzero(tx_buf, 8);
|
||||
wr_rd(READ_FEATURE_ROW, tx_buf, 8, rx_buf, 8);
|
||||
for (int i = 0; i < 8; i++)
|
||||
reg |= ((uint64_t)rx_buf[i] << (i*8));
|
||||
return reg;
|
||||
}
|
||||
|
||||
uint16_t Lattice::readFeabits()
|
||||
{
|
||||
uint8_t rx_buf[2];
|
||||
wr_rd(READ_FEABITS, NULL, 0, rx_buf, 2);
|
||||
_jtag->set_state(FtdiJtag::RUN_TEST_IDLE);
|
||||
_jtag->toggleClk(1000);
|
||||
|
||||
return rx_buf[0] | (((uint16_t)rx_buf[1]) << 8);
|
||||
}
|
||||
|
||||
bool Lattice::writeFeaturesRow(uint64_t features, bool verify)
|
||||
{
|
||||
uint8_t tx_buf[8];
|
||||
for (int i=0; i < 8; i++)
|
||||
tx_buf[i] = ((features >> (i*8)) & 0x00ff);
|
||||
wr_rd(PROG_FEATURE_ROW, tx_buf, 8, NULL, 0);
|
||||
_jtag->set_state(FtdiJtag::RUN_TEST_IDLE);
|
||||
_jtag->toggleClk(1000);
|
||||
if (!pollBusyFlag())
|
||||
return false;
|
||||
if (verify)
|
||||
return (features == readFeaturesRow()) ? true : false;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Lattice::writeFeabits(uint16_t feabits, bool verify)
|
||||
{
|
||||
uint8_t tx_buf[2] = {(uint8_t)(feabits&0x00ff),
|
||||
(uint8_t)(0x00ff & (feabits>>8))};
|
||||
|
||||
wr_rd(PROG_FEABITS, tx_buf, 2, NULL, 0);
|
||||
_jtag->set_state(FtdiJtag::RUN_TEST_IDLE);
|
||||
_jtag->toggleClk(1000);
|
||||
if (!pollBusyFlag())
|
||||
return false;
|
||||
if (verify)
|
||||
return (feabits == readFeabits()) ? true : false;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Lattice::writeProgramDone()
|
||||
{
|
||||
wr_rd(PROG_DONE, NULL, 0, NULL, 0);
|
||||
_jtag->set_state(FtdiJtag::RUN_TEST_IDLE);
|
||||
_jtag->toggleClk(1000);
|
||||
if (!pollBusyFlag())
|
||||
return false;
|
||||
if (!checkStatus(REG_STATUS_DONE, REG_STATUS_DONE))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Lattice::loadConfiguration()
|
||||
{
|
||||
wr_rd(REFRESH, NULL, 0, NULL, 0);
|
||||
_jtag->set_state(FtdiJtag::RUN_TEST_IDLE);
|
||||
_jtag->toggleClk(1000);
|
||||
if (!pollBusyFlag())
|
||||
return false;
|
||||
if (!checkStatus(REG_STATUS_DONE, REG_STATUS_DONE))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
/*
|
||||
* Copyright (C) 2019 Gwenhael Goavec-Merou <[email protected]>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef LATTICE_HPP_
|
||||
#define LATTICE_HPP_
|
||||
|
||||
#include <stdint.h>
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "ftdijtag.hpp"
|
||||
#include "device.hpp"
|
||||
#include "jedParser.hpp"
|
||||
#include "latticeBitParser.hpp"
|
||||
|
||||
class Lattice: public Device {
|
||||
public:
|
||||
Lattice(FtdiJtag *jtag, std::string filename, bool verbose);
|
||||
int idCode() override;
|
||||
int userCode();
|
||||
void reset() override {}
|
||||
void program(unsigned int offset) override;
|
||||
bool program_mem();
|
||||
bool program_flash(unsigned int offset);
|
||||
bool Verify(JedParser &_jed, bool unlock = false);
|
||||
|
||||
private:
|
||||
bool wr_rd(uint8_t cmd, uint8_t *tx, int tx_len,
|
||||
uint8_t *rx, int rx_len, bool verbose = false);
|
||||
void unlock();
|
||||
bool EnableISC(uint8_t flash_mode);
|
||||
bool DisableISC();
|
||||
bool EnableCfgIf();
|
||||
bool DisableCfg();
|
||||
bool pollBusyFlag(bool verbose = false);
|
||||
bool flashEraseAll();
|
||||
bool flashErase(uint8_t mask);
|
||||
bool flashProg(uint32_t start_addr, std::vector<std::string> data);
|
||||
bool checkStatus(uint32_t val, uint32_t mask);
|
||||
void displayReadReg(uint32_t dev);
|
||||
uint32_t readStatusReg();
|
||||
uint64_t readFeaturesRow();
|
||||
bool writeFeaturesRow(uint64_t features, bool verify);
|
||||
uint16_t readFeabits();
|
||||
bool writeFeabits(uint16_t feabits, bool verify);
|
||||
bool writeProgramDone();
|
||||
bool loadConfiguration();
|
||||
|
||||
/* test */
|
||||
bool checkID();
|
||||
};
|
||||
#endif // LATTICE_HPP_
|
||||
@@ -0,0 +1,112 @@
|
||||
/*
|
||||
* Copyright (C) 2019 Gwenhael Goavec-Merou <[email protected]>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <arpa/inet.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <functional>
|
||||
#include <cctype>
|
||||
#include <iostream>
|
||||
#include <locale>
|
||||
|
||||
#include "latticeBitParser.hpp"
|
||||
|
||||
using namespace std;
|
||||
|
||||
LatticeBitParser::LatticeBitParser(const string &filename, bool verbose):
|
||||
ConfigBitstreamParser(filename, ConfigBitstreamParser::BIN_MODE, verbose),
|
||||
_attribs(), _endHeader(0)
|
||||
{}
|
||||
|
||||
LatticeBitParser::~LatticeBitParser()
|
||||
{
|
||||
}
|
||||
|
||||
void LatticeBitParser::displayHeader()
|
||||
{
|
||||
cout << "Lattice bitstream header infos" << endl;
|
||||
for (auto it = _attribs.begin(); it != _attribs.end(); it++) {
|
||||
cout << (*it).first << ": " << (*it).second << endl;
|
||||
}
|
||||
}
|
||||
|
||||
int LatticeBitParser::parseHeader()
|
||||
{
|
||||
int currPos = _fd.tellg();
|
||||
char tmp[_file_size-currPos];
|
||||
char field[256];
|
||||
bool foundEndHeader = false;
|
||||
uint32_t *d;
|
||||
|
||||
_fd.read(tmp, (_file_size-currPos)*sizeof(char));
|
||||
|
||||
for (int i = 0; i < _file_size-currPos;) {
|
||||
if (tmp[i] == 0xff) {
|
||||
d = (uint32_t*)(tmp+i);
|
||||
if (d[0] != 0xBDffffff && (0xffffff00 & d[1]) != 0x3BFFFF00){
|
||||
foundEndHeader = true;
|
||||
_endHeader = i + currPos -1;
|
||||
break;
|
||||
}
|
||||
i++;
|
||||
} else {
|
||||
strcpy(field, tmp+i);
|
||||
string buff(field);
|
||||
int pos = buff.find_first_of(':', 0);
|
||||
if (pos != -1) {
|
||||
string key(buff.substr(0, pos));
|
||||
string val(buff.substr(pos+1, buff.size()));
|
||||
int startPos = val.find_first_not_of(" ");
|
||||
int endPos = val.find_last_not_of(" ")+1;
|
||||
_attribs[key] = val.substr(startPos, endPos).c_str();
|
||||
}
|
||||
i+=strlen(field)+1;
|
||||
}
|
||||
}
|
||||
|
||||
return (foundEndHeader) ? EXIT_SUCCESS : EXIT_FAILURE;
|
||||
}
|
||||
|
||||
int LatticeBitParser::parse()
|
||||
{
|
||||
uint8_t dummy[2];
|
||||
|
||||
/* bit file start with 0xff00 */
|
||||
_fd.read(reinterpret_cast<char*>(&dummy), 2*sizeof(uint8_t));
|
||||
if (dummy[0] != 0xff || dummy[1] != 0x00) {
|
||||
printf("Wrong File %02x%02x\n", dummy[0], dummy[1]);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
/* until 0xFFFFBDB3 0xFFFF */
|
||||
if (parseHeader() == EXIT_FAILURE)
|
||||
return EXIT_FAILURE;
|
||||
|
||||
/* read All data */
|
||||
_fd.seekg(_endHeader, _fd.beg);
|
||||
char buffer[_file_size];
|
||||
int end = _file_size-_endHeader;
|
||||
_fd.read(buffer, end);
|
||||
|
||||
for (int i = 0; i < end; i++)
|
||||
_bit_data+=(buffer[i]);
|
||||
|
||||
_bit_length = _bit_data.size() * 8;
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
* Copyright (C) 2019 Gwenhael Goavec-Merou <[email protected]>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#ifndef LATTICEBITPARSER_HPP_
|
||||
#define LATTICEBITPARSER_HPP_
|
||||
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
#include <map>
|
||||
#include <string>
|
||||
|
||||
#include "configBitstreamParser.hpp"
|
||||
|
||||
class LatticeBitParser: public ConfigBitstreamParser {
|
||||
public:
|
||||
LatticeBitParser(const std::string &filename, bool verbose = false);
|
||||
~LatticeBitParser();
|
||||
int parse() override;
|
||||
void displayHeader();
|
||||
|
||||
private:
|
||||
int parseHeader();
|
||||
std::map<std::string, std::string> _attribs;
|
||||
int _endHeader;
|
||||
};
|
||||
|
||||
#endif // LATTICEBITPARSER_HPP_
|
||||
+276
@@ -0,0 +1,276 @@
|
||||
/*
|
||||
* Copyright (C) 2019 Gwenhael Goavec-Merou <[email protected]>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <argp.h>
|
||||
#include <fstream>
|
||||
#include <iomanip>
|
||||
#include <iostream>
|
||||
#include <map>
|
||||
#include <sstream>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <vector>
|
||||
|
||||
#include "altera.hpp"
|
||||
#include "board.hpp"
|
||||
#include "cable.hpp"
|
||||
#include "device.hpp"
|
||||
#include "display.hpp"
|
||||
#include "gowin.hpp"
|
||||
#include "lattice.hpp"
|
||||
#include "ftdijtag.hpp"
|
||||
#include "part.hpp"
|
||||
#include "xilinx.hpp"
|
||||
|
||||
using namespace std;
|
||||
|
||||
struct arguments {
|
||||
bool verbose, reset;
|
||||
unsigned int offset;
|
||||
string bit_file;
|
||||
string device;
|
||||
string cable;
|
||||
string board;
|
||||
bool list_cables;
|
||||
bool list_boards;
|
||||
bool list_fpga;
|
||||
bool write_flash;
|
||||
bool write_sram;
|
||||
};
|
||||
|
||||
#define LIST_CABLE 1
|
||||
#define LIST_BOARD 2
|
||||
#define LIST_FPGA 3
|
||||
|
||||
const char *argp_program_version = "openFPGALoader 1.0";
|
||||
const char *argp_program_bug_address = "<[email protected]>";
|
||||
static char doc[] = "openFPGALoader -- a program to flash FPGA";
|
||||
static char args_doc[] = "BIT_FILE";
|
||||
static error_t parse_opt(int key, char *arg, struct argp_state *state);
|
||||
static struct argp_option options[] = {
|
||||
{"cable", 'c', "CABLE", 0, "jtag interface"},
|
||||
{"list-cables", LIST_CABLE, 0, 0, "list all supported cables"},
|
||||
{"board", 'b', "BOARD", 0, "board name, may be used instead of cable"},
|
||||
{"list-boards", LIST_BOARD, 0, 0, "list all supported boards"},
|
||||
{"device", 'd', "DEVICE", 0, "device to use (/dev/ttyUSBx)"},
|
||||
{"list-fpga", LIST_FPGA, 0, 0, "list all supported FPGA"},
|
||||
{"write-flash", 'f', 0, 0,
|
||||
"write bitstream in flash (default: false, only for Gowin devices)"},
|
||||
{"write-sram", 'm', 0, 0,
|
||||
"write bitstream in SRAM (default: true, only for Gowin devices)"},
|
||||
{"offset", 'o', "OFFSET", 0, "start offset in EEPROM"},
|
||||
{"verbose", 'v', 0, 0, "Produce verbose output"},
|
||||
{"reset", 'r', 0, 0, "reset FPGA after operations"},
|
||||
{0}
|
||||
};
|
||||
|
||||
static struct argp argp = { options, parse_opt, args_doc, doc };
|
||||
void displaySupported(const struct arguments &args);
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
FTDIpp_MPSSE::mpsse_bit_config cable;
|
||||
|
||||
/* command line args. */
|
||||
struct arguments args = {false, false, 0, "", "-", "-", "-",
|
||||
false, false, false, false, true};
|
||||
/* parse arguments */
|
||||
argp_parse(&argp, argc, argv, 0, 0, &args);
|
||||
|
||||
if (args.list_boards == true || args.list_cables == true || args.list_fpga) {
|
||||
displaySupported(args);
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
/* if a board name is specified try to use this to determine cable */
|
||||
if (args.board[0] != '-' && board_list.find(args.board) != board_list.end()) {
|
||||
auto t = cable_list.find(board_list[args.board]);
|
||||
if (t == cable_list.end()) {
|
||||
cerr << "Error: interface "<< board_list[args.board];
|
||||
cerr << " for board " << args.board << " is not supported" << endl;
|
||||
return 1;
|
||||
}
|
||||
args.cable = (*t).first;
|
||||
} else if (args.cable[0] == '-') { /* if no board and no cable */
|
||||
if (args.verbose)
|
||||
cout << "No cable or board specified: using direct ft2232 interface" << endl;
|
||||
args.cable = "ft2232";
|
||||
}
|
||||
|
||||
auto select_cable = cable_list.find(args.cable);
|
||||
if (select_cable == cable_list.end()) {
|
||||
cerr << "error : " << args.cable << " not found" << endl;
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
cable = select_cable->second;
|
||||
|
||||
/* jtag base */
|
||||
FtdiJtag *jtag;
|
||||
if (args.device == "-")
|
||||
jtag = new FtdiJtag(cable, 1, 6000000, false);
|
||||
else
|
||||
jtag = new FtdiJtag(cable, args.device, 1, 6000000, false);
|
||||
|
||||
/* chain detection */
|
||||
vector<int> listDev;
|
||||
int found = jtag->detectChain(listDev, 5);
|
||||
|
||||
if (args.verbose)
|
||||
cout << "found " << std::to_string(found) << " devices" << endl;
|
||||
if (found > 1) {
|
||||
cerr << "Error: currently only one device is supported" << endl;
|
||||
return EXIT_FAILURE;
|
||||
} else if (found < 1) {
|
||||
cerr << "Error: no device found" << endl;
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
int idcode = listDev[0];
|
||||
|
||||
if (fpga_list.find(idcode) == fpga_list.end()) {
|
||||
cerr << "Error: device " << hex << idcode << " not supported" << endl;
|
||||
return 1;
|
||||
} else if (args.verbose) {
|
||||
printf("idcode 0x%x\nmanufacturer %s\nmodel %s\nfamily %s\n",
|
||||
idcode,
|
||||
fpga_list[idcode].manufacturer.c_str(),
|
||||
fpga_list[idcode].model.c_str(),
|
||||
fpga_list[idcode].family.c_str());
|
||||
}
|
||||
string fab = fpga_list[idcode].manufacturer;
|
||||
|
||||
Device *fpga;
|
||||
if (fab == "xilinx") {
|
||||
fpga = new Xilinx(jtag, args.bit_file, args.verbose);
|
||||
} else if (fab == "altera") {
|
||||
fpga = new Altera(jtag, args.bit_file, args.verbose);
|
||||
} else if (fab == "Gowin") {
|
||||
fpga = new Gowin(jtag, args.bit_file, args.write_flash, args.write_sram,
|
||||
args.verbose);
|
||||
} else if (fab == "lattice") {
|
||||
fpga = new Lattice(jtag, args.bit_file, args.verbose);
|
||||
} else {
|
||||
cerr << "Error: manufacturer " << fab << " not supported" << endl;
|
||||
delete(jtag);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
fpga->program(args.offset);
|
||||
|
||||
if (args.reset)
|
||||
fpga->reset();
|
||||
|
||||
delete(fpga);
|
||||
delete(jtag);
|
||||
}
|
||||
|
||||
/* arguments parser */
|
||||
static error_t parse_opt(int key, char *arg, struct argp_state *state)
|
||||
{
|
||||
struct arguments *arguments = (struct arguments *)state->input;
|
||||
|
||||
switch (key) {
|
||||
case 'f':
|
||||
arguments->write_flash = true;
|
||||
arguments->write_sram = false;
|
||||
break;
|
||||
case 'm':
|
||||
arguments->write_sram = true;
|
||||
break;
|
||||
case 'r':
|
||||
arguments->reset = true;
|
||||
break;
|
||||
case 'd':
|
||||
arguments->device = arg;
|
||||
break;
|
||||
case 'v':
|
||||
arguments->verbose = true;
|
||||
break;
|
||||
case 'o':
|
||||
arguments->offset = strtoul(arg, NULL, 16);
|
||||
break;
|
||||
case 'c':
|
||||
arguments->cable = arg;
|
||||
break;
|
||||
case 'b':
|
||||
arguments->board = arg;
|
||||
break;
|
||||
case ARGP_KEY_ARG:
|
||||
arguments->bit_file = arg;
|
||||
break;
|
||||
case ARGP_KEY_END:
|
||||
break;
|
||||
case LIST_CABLE:
|
||||
arguments->list_cables = true;
|
||||
break;
|
||||
case LIST_BOARD:
|
||||
arguments->list_boards = true;
|
||||
break;
|
||||
case LIST_FPGA:
|
||||
arguments->list_fpga = true;
|
||||
break;
|
||||
default:
|
||||
return ARGP_ERR_UNKNOWN;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* display list of cables, boards and devices supported */
|
||||
void displaySupported(const struct arguments &args)
|
||||
{
|
||||
if (args.list_cables == true) {
|
||||
stringstream t;
|
||||
t << setw(15) << left << "cable name:" << "vid:pid";
|
||||
printSuccess(t.str());
|
||||
for (auto b = cable_list.begin(); b != cable_list.end(); b++) {
|
||||
FTDIpp_MPSSE::mpsse_bit_config c = (*b).second;
|
||||
stringstream ss;
|
||||
ss << setw(15) << left << (*b).first;
|
||||
ss << "0x" << hex << c.vid << ":" << c.pid;
|
||||
printInfo(ss.str());
|
||||
}
|
||||
cout << endl;
|
||||
}
|
||||
|
||||
if (args.list_boards) {
|
||||
stringstream t;
|
||||
t << setw(15) << left << "board name:" << "cable_name";
|
||||
printSuccess(t.str());
|
||||
for (auto b = board_list.begin(); b != board_list.end(); b++) {
|
||||
stringstream ss;
|
||||
ss << setw(15) << left << (*b).first << " " << (*b).second;
|
||||
printInfo(ss.str());
|
||||
}
|
||||
cout << endl;
|
||||
}
|
||||
|
||||
if (args.list_fpga) {
|
||||
stringstream t;
|
||||
t << setw(12) << left << "IDCode" << setw(14) << "manufacturer";
|
||||
t << setw(15) << "family" << setw(20) << "model";
|
||||
printSuccess(t.str());
|
||||
for (auto b = fpga_list.begin(); b != fpga_list.end(); b++) {
|
||||
fpga_model fpga = (*b).second;
|
||||
stringstream ss, idCode;
|
||||
idCode << "0x" << hex << (*b).first;
|
||||
ss << setw(12) << left << idCode.str();
|
||||
ss << setw(14) << fpga.manufacturer << setw(15) << fpga.family;
|
||||
ss << setw(20) << fpga.model;
|
||||
printInfo(ss.str());
|
||||
}
|
||||
cout << endl;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,111 @@
|
||||
/*
|
||||
* Copyright (C) 2019 Gwenhael Goavec-Merou <[email protected]>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "configBitstreamParser.hpp"
|
||||
#include "mcsParser.hpp"
|
||||
|
||||
using namespace std;
|
||||
|
||||
/* line format
|
||||
* :LLAAAATTHH...HHCC
|
||||
* LL : nb octets de data dans la ligne (hexa)
|
||||
* AAAA : addresse du debut de la ligne ou mettre les data
|
||||
* TT : type de la ligne (cf. plus bas)
|
||||
* HH : le champ de data
|
||||
* CC : Checksum (cf. plus bas)
|
||||
*/
|
||||
/* type : 00 -> data + addr 16b
|
||||
* 01 -> end of file
|
||||
* 02 -> extended addr
|
||||
* 03 -> start segment addr record
|
||||
* 04 -> extented linear addr record
|
||||
* 05 -> start linear addr record
|
||||
*/
|
||||
|
||||
#define LEN_BASE 1
|
||||
#define ADDR_BASE 3
|
||||
#define TYPE_BASE 7
|
||||
#define DATA_BASE 9
|
||||
|
||||
McsParser::McsParser(string filename, bool verbose):
|
||||
ConfigBitstreamParser(filename, ConfigBitstreamParser::ASCII_MODE,
|
||||
verbose),
|
||||
_base_addr(0)
|
||||
{}
|
||||
|
||||
int McsParser::parse()
|
||||
{
|
||||
string str;
|
||||
int ret;
|
||||
|
||||
do {
|
||||
getline(_fd, str);
|
||||
ret = parseLine(str);
|
||||
} while (ret == 0);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int McsParser::parseLine(string buffer)
|
||||
{
|
||||
char *ptr;
|
||||
const char *buff = buffer.c_str();
|
||||
uint16_t tmp, byteLen, type, checksum;
|
||||
uint32_t addr, loc_addr;
|
||||
uint8_t sum = 0;
|
||||
|
||||
if (buff[0] != ':') {
|
||||
cout << "Error: a line must start with ':'" << endl;
|
||||
return -1;
|
||||
}
|
||||
/* len */
|
||||
sscanf(buff + LEN_BASE, "%2hx", &byteLen);
|
||||
/* address */
|
||||
sscanf(buff + ADDR_BASE, "%4x", &addr);
|
||||
/* type */
|
||||
sscanf(buff + TYPE_BASE, "%2hx", &type);
|
||||
/* checksum */
|
||||
sscanf(buff + DATA_BASE + byteLen * 2, "%2hx", &checksum);
|
||||
|
||||
sum = byteLen + type + (addr & 0xff) + ((addr >> 8) & 0xff);
|
||||
|
||||
if (type == 0) {
|
||||
loc_addr = _base_addr + addr;
|
||||
ptr = (char *)(buff + DATA_BASE);
|
||||
for (int i = 0; i < byteLen; i++, ptr += 2) {
|
||||
sscanf(ptr, "%2hx", &tmp);
|
||||
_bit_data[loc_addr + i] = tmp;
|
||||
sum += tmp;
|
||||
}
|
||||
_bit_length +=byteLen;
|
||||
} else if (type == 1) {
|
||||
return 1;
|
||||
} else if (type == 4) {
|
||||
sscanf(buff + DATA_BASE, "%4x", &loc_addr);
|
||||
_base_addr = (loc_addr << 16);
|
||||
sum += (loc_addr & 0xff) + ((loc_addr >> 8) & 0xff);
|
||||
} else {
|
||||
cerr << "Error: unknown type" << endl;
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (checksum != (0xff&((~sum)+1))) {
|
||||
cerr << "Error: wrong checksum" << endl;
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* Copyright (C) 2019 Gwenhael Goavec-Merou <[email protected]>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef MCSPARSER_HPP
|
||||
#define MCSPARSER_HPP
|
||||
|
||||
#include "configBitstreamParser.hpp"
|
||||
|
||||
class McsParser: public ConfigBitstreamParser {
|
||||
public:
|
||||
McsParser(std::string filename, bool verbose);
|
||||
int parse();
|
||||
|
||||
private:
|
||||
int parseLine(std::string buffer);
|
||||
|
||||
int _base_addr;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
// the configured options and settings for openFPGALoader
|
||||
#define openFPGALoader_VERSION_MAJOR @openFPGALoader_VERSION_MAJOR@
|
||||
#define openFPGALoader_VERSION_MINOR @openFPGALoader_VERSION_MINOR@
|
||||
@@ -0,0 +1,22 @@
|
||||
#ifndef PART_HPP
|
||||
#define PART_HPP
|
||||
|
||||
#include <map>
|
||||
#include <string>
|
||||
|
||||
typedef struct {
|
||||
std::string manufacturer;
|
||||
std::string family;
|
||||
std::string model;
|
||||
} fpga_model;
|
||||
|
||||
static std::map <int, fpga_model> fpga_list = {
|
||||
{0x0362D093, {"xilinx", "artix a7 35t", "xc7a35"}},
|
||||
{0x020f30dd, {"altera", "cyclone 10 LP", "10CL025"}},
|
||||
{0x612bd043, {"lattice", "MachXO3LF", "LCMX03LF-6900C"}},
|
||||
{0x1100581b, {"Gowin", "GW1N", "GW1NR-9"}},
|
||||
{0x0900281B, {"Gowin", "GW1N", "GW1N-1"}},
|
||||
{0x0100381B, {"Gowin", "GW1N", "GW1N-4"}},
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
* Copyright (C) 2019 Gwenhael Goavec-Merou <[email protected]>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include "progressBar.hpp"
|
||||
#include "display.hpp"
|
||||
|
||||
ProgressBar::ProgressBar(std::string mess, int maxValue, int progressLen):
|
||||
_mess(mess), _maxValue(maxValue), _progressLen(progressLen)
|
||||
{
|
||||
}
|
||||
|
||||
void ProgressBar::display(int value)
|
||||
{
|
||||
float percent = ((float)value * 100.0f)/(float)_maxValue;
|
||||
float nbEq = (percent * (float) _progressLen)/100.0f;
|
||||
|
||||
//fprintf(stderr, "\r%s: [", _mess.c_str());
|
||||
printInfo("\r" + _mess + ": [", false);
|
||||
for (int z=0; z < nbEq; z++) {
|
||||
fputc('=', stderr);
|
||||
}
|
||||
fprintf(stderr, "%*s", (int)(_progressLen-nbEq), "");
|
||||
//fprintf(stderr, "] %3.2f%%", percent);
|
||||
printInfo("] " + std::to_string(percent) + "%", false);
|
||||
}
|
||||
void ProgressBar::done()
|
||||
{
|
||||
display(_maxValue);
|
||||
//fprintf(stderr, "\nDone\n");
|
||||
printSuccess("\nDone");
|
||||
}
|
||||
void ProgressBar::fail()
|
||||
{
|
||||
display(_maxValue);
|
||||
//fprintf(stderr, "\nDone\n");
|
||||
printError("\nFail");
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* Copyright (C) 2019 Gwenhael Goavec-Merou <[email protected]>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef PROGRESSBARE_HPP
|
||||
#define PROGRESSBARE_HPP
|
||||
|
||||
#include <iostream>
|
||||
|
||||
class ProgressBar {
|
||||
public:
|
||||
ProgressBar(std::string mess, int maxValue, int progressLen);
|
||||
void display(int value);
|
||||
void done();
|
||||
void fail();
|
||||
private:
|
||||
std::string _mess;
|
||||
int _maxValue;
|
||||
int _progressLen;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,325 @@
|
||||
/*
|
||||
* Copyright (C) 2019 Gwenhael Goavec-Merou <[email protected]>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <iostream>
|
||||
|
||||
#include "ftdijtag.hpp"
|
||||
#include "ftdipp_mpsse.hpp"
|
||||
#include "progressBar.hpp"
|
||||
#include "spiFlash.hpp"
|
||||
|
||||
#define USER1 0x02
|
||||
|
||||
static uint8_t reverseByte(uint8_t src)
|
||||
{
|
||||
uint8_t dst = 0;
|
||||
for (int i=0; i < 8; i++) {
|
||||
dst = (dst << 1) | (src & 0x01);
|
||||
src >>= 1;
|
||||
}
|
||||
return dst;
|
||||
}
|
||||
|
||||
|
||||
/* read/write status register : 0B addr + 0 dummy */
|
||||
#define FLASH_WRSR 0x01
|
||||
#define FLASH_RDSR 0x05
|
||||
# define FLASH_RDSR_WIP (0x01)
|
||||
# define FLASH_RDSR_WEL (0x02)
|
||||
/* flash program */
|
||||
#define FLASH_PP 0x02
|
||||
/* write [en|dis]able : 0B addr + 0 dummy */
|
||||
#define FLASH_WRDIS 0x04
|
||||
#define FLASH_WREN 0x06
|
||||
/* Read OTP : 3 B addr + 8 clk cycle*/
|
||||
#define FLASH_ROTP 0x4B
|
||||
#define FLASH_POWER_UP 0xAB
|
||||
#define FLASH_POWER_DOWN 0xB9
|
||||
/* read/write non volatile register: 0B addr + 0 dummy */
|
||||
#define FLASH_RDNVCR 0xB5
|
||||
#define FLASH_WRNVCR 0x81
|
||||
/* bulk erase */
|
||||
#define FLASH_BE 0xC7
|
||||
/* sector (64kb) erase */
|
||||
#define FLASH_SE 0xD8
|
||||
/* read/write lock register : 3B addr + 0 dummy */
|
||||
#define FLASH_WRLR 0xE5
|
||||
#define FLASH_RDLR 0xE8
|
||||
/* read/clear flag status register : 0B addr + 0 dummy */
|
||||
#define FLASH_CLFSR 0x50
|
||||
#define FLASH_RFSR 0x70
|
||||
/* */
|
||||
#define FLASH_WRVCR 0x81
|
||||
#define FLASH_RDVCR 0x85
|
||||
/* */
|
||||
#define FLASH_WRVECR 0x61
|
||||
#define FLASH_RDVECR 0x65
|
||||
|
||||
SPIFlash::SPIFlash(FtdiJtag *jtag, bool verbose):_jtag(jtag), _verbose(verbose)
|
||||
{
|
||||
}
|
||||
|
||||
/*
|
||||
* jtag : jtag interface
|
||||
* cmd : opcode for SPI flash
|
||||
* tx : buffer to send
|
||||
* rx : buffer to fill
|
||||
* len : number of byte to send/receive (cmd not comprise)
|
||||
* so to send only a cmd set len to 0 (or omit this param)
|
||||
*/
|
||||
|
||||
void SPIFlash::jtag_write_read(uint8_t cmd,
|
||||
uint8_t *tx, uint8_t *rx, uint16_t len)
|
||||
{
|
||||
int xfer_len = len + 1 + ((rx == NULL) ? 0 : 1);
|
||||
uint8_t jtx[xfer_len] = {reverseByte(cmd)};
|
||||
uint8_t jrx[xfer_len];
|
||||
if (tx != NULL) {
|
||||
for (int i=0; i < len; i++)
|
||||
jtx[i+1] = reverseByte(tx[i]);
|
||||
}
|
||||
/* addr BSCAN user1 */
|
||||
_jtag->shiftIR(USER1, 6);
|
||||
/* send first already stored cmd,
|
||||
* in the same time store each byte
|
||||
* to send next
|
||||
*/
|
||||
_jtag->shiftDR(jtx, (rx == NULL)? NULL: jrx, 8*xfer_len);
|
||||
|
||||
if (rx != NULL) {
|
||||
for (int i=0; i < len; i++)
|
||||
rx[i] = reverseByte(jrx[i+1] >> 1) | (jrx[i+2] & 0x01);
|
||||
}
|
||||
}
|
||||
|
||||
int SPIFlash::wait(uint8_t mask, uint8_t cond, uint32_t timeout, bool verbose)
|
||||
{
|
||||
uint8_t rx[2];
|
||||
uint8_t tmp;
|
||||
uint8_t tx = reverseByte(FLASH_RDSR);
|
||||
uint32_t count = 0;
|
||||
|
||||
_jtag->shiftIR(USER1, 6, FtdiJtag::UPDATE_IR);
|
||||
_jtag->set_state(FtdiJtag::SHIFT_DR);
|
||||
_jtag->read_write(&tx, NULL, 8, 0);
|
||||
|
||||
do {
|
||||
_jtag->read_write(NULL, rx, 8*2, 0);
|
||||
tmp = (reverseByte(rx[0]>>1)) | (0x01 & rx[1]);
|
||||
count ++;
|
||||
if (count == timeout){
|
||||
printf("timeout: %x %x %x\n", tmp, rx[0], rx[1]);
|
||||
break;
|
||||
}
|
||||
if (tmp & ~0x3) {
|
||||
printf("Error: rx %x %x %x\n", tmp, reverseByte(rx[0]), rx[1]);
|
||||
count = timeout;
|
||||
break;
|
||||
}
|
||||
if (verbose) {
|
||||
printf("%x %x %x %d\n", tmp, mask, cond, count);
|
||||
}
|
||||
} while ((tmp & mask) != cond);
|
||||
_jtag->go_test_logic_reset();
|
||||
|
||||
if (count == timeout) {
|
||||
printf("%x\n", tmp);
|
||||
std::cout << "wait: Error" << std::endl;
|
||||
return -1;
|
||||
} else
|
||||
return 0;
|
||||
}
|
||||
|
||||
int SPIFlash::bulk_erase()
|
||||
{
|
||||
if (write_enable() == -1)
|
||||
return -1;
|
||||
jtag_write_read(FLASH_BE, NULL, NULL, 0);
|
||||
return wait(FLASH_RDSR_WIP, 0x00, 100000, true);
|
||||
}
|
||||
|
||||
int SPIFlash::sector_erase(int addr)
|
||||
{
|
||||
uint8_t tx[3] = {(uint8_t)(0xff & (addr >> 16)),
|
||||
(uint8_t)(0xff & (addr >> 8)),
|
||||
(uint8_t)(addr & 0xff)};
|
||||
jtag_write_read(FLASH_SE, tx, NULL, 3);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int SPIFlash::sectors_erase(int base_addr, int size)
|
||||
{
|
||||
int start_addr = base_addr;
|
||||
int end_addr = (size + 0xffff) & ~0xffff;
|
||||
ProgressBar progress("Erasing", end_addr, 50);
|
||||
for (int addr = start_addr; addr < end_addr; addr += 0x10000) {
|
||||
if (write_enable() == -1)
|
||||
return -1;
|
||||
if (sector_erase(addr) == -1)
|
||||
return -1;
|
||||
if (wait(FLASH_RDSR_WIP, 0x00, 100000, false) == -1)
|
||||
return -1;
|
||||
progress.display(addr);
|
||||
}
|
||||
progress.done();
|
||||
return 0;
|
||||
}
|
||||
|
||||
int SPIFlash::write_page(int addr, uint8_t *data, int len)
|
||||
{
|
||||
uint8_t tx[len+3] = {(uint8_t)(0xff & (addr >> 16)),
|
||||
(uint8_t)(0xff & (addr >> 8)),
|
||||
(uint8_t)(addr & 0xff)};
|
||||
for (int i=0; i < len; i++) {
|
||||
tx[i+3] = data[i];
|
||||
}
|
||||
if (write_enable() == -1)
|
||||
return -1;
|
||||
|
||||
jtag_write_read(FLASH_PP, tx, NULL, len+3);
|
||||
return wait(FLASH_RDSR_WIP, 0x00, 1000);
|
||||
}
|
||||
|
||||
int SPIFlash::erase_and_prog(int base_addr, uint8_t *data, int len)
|
||||
{
|
||||
ProgressBar progress("Writing", len, 50);
|
||||
if (sectors_erase(0, len) == -1)
|
||||
return -1;
|
||||
|
||||
uint8_t *ptr = data;
|
||||
int size = 0;
|
||||
for (int addr = base_addr; addr < len; addr += size, ptr+=size) {
|
||||
size = (addr + 256 > len)?(len-addr) : 256;
|
||||
if (write_page(addr, ptr, size) == -1)
|
||||
return -1;
|
||||
progress.display(addr);
|
||||
}
|
||||
progress.done();
|
||||
return 0;
|
||||
}
|
||||
|
||||
void SPIFlash::reset()
|
||||
{
|
||||
uint8_t data[8] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
|
||||
jtag_write_read(0xff, data, NULL, 8);
|
||||
}
|
||||
|
||||
void SPIFlash::read_id()
|
||||
{
|
||||
int len = 4;
|
||||
uint8_t rx[512];
|
||||
|
||||
jtag_write_read(0x9F, NULL, rx, 4);
|
||||
int d = 0;
|
||||
for (int i=0; i < 4; i++) {
|
||||
d = d << 8;
|
||||
d |= (0x00ff & (int)rx[i]);
|
||||
if (_verbose)
|
||||
printf("%x ", rx[i]);
|
||||
}
|
||||
|
||||
if (_verbose)
|
||||
printf("read %x\n", d);
|
||||
/* read extented */
|
||||
len += (d & 0x0ff);
|
||||
|
||||
jtag_write_read(0x9F, NULL, rx, len);
|
||||
|
||||
/* must be 0x20BA1810 ... */
|
||||
|
||||
printf("Detail: \n");
|
||||
printf("Jedec ID : %02x\n", rx[0]);
|
||||
printf("memory type : %02x\n", rx[1]);
|
||||
printf("memory capacity : %02x\n", rx[2]);
|
||||
printf("EDID + CFD length : %02x\n", rx[3]);
|
||||
printf("EDID : %02x%02x\n", rx[5], rx[4]);
|
||||
printf("CFD : ");
|
||||
if (_verbose) {
|
||||
for (int i = 6; i < len; i++)
|
||||
printf("%02x ", rx[i]);
|
||||
printf("\n");
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t SPIFlash::read_status_reg()
|
||||
{
|
||||
uint8_t rx;
|
||||
jtag_write_read(FLASH_RDSR, NULL, &rx, 1);
|
||||
if (_verbose) {
|
||||
printf("RDSR : %02x\n", rx);
|
||||
printf("WIP : %d\n", rx&0x01);
|
||||
printf("WEL : %d\n", (rx>>1)&0x01);
|
||||
printf("BP : %x\n", (((rx>>6)&0x01)<<3) | ((rx >> 2) & 0x07));
|
||||
printf("TB : %d\n", (((rx>>5)&0x01)));
|
||||
printf("SRWD : %d\n", (((rx>>7)&0x01)));
|
||||
}
|
||||
return rx;
|
||||
}
|
||||
|
||||
void SPIFlash::power_up()
|
||||
{
|
||||
jtag_write_read(FLASH_POWER_UP, NULL, NULL, 0);
|
||||
}
|
||||
|
||||
void SPIFlash::power_down()
|
||||
{
|
||||
jtag_write_read(FLASH_POWER_DOWN, NULL, NULL, 0);
|
||||
}
|
||||
|
||||
int SPIFlash::write_enable()
|
||||
{
|
||||
jtag_write_read(FLASH_WREN, NULL, NULL, 0);
|
||||
/* wait WEL */
|
||||
if (wait(FLASH_RDSR_WEL, FLASH_RDSR_WEL, 1000)) {
|
||||
printf("write en: Error\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (_verbose)
|
||||
std::cout << "write en: Success" << std::endl;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int SPIFlash::write_disable()
|
||||
{
|
||||
jtag_write_read(FLASH_WRDIS, NULL, NULL, 0);
|
||||
/* wait ! WEL */
|
||||
int ret = wait(FLASH_RDSR_WEL, 0x00, 1000);
|
||||
if (ret == -1)
|
||||
printf("write disable: Error\n");
|
||||
else if (_verbose)
|
||||
printf("write disable: Success\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
int SPIFlash::disable_protection()
|
||||
{
|
||||
uint8_t data = 0x00;
|
||||
jtag_write_read(FLASH_WRSR, &data, NULL, 1);
|
||||
if (wait(0xff, 0, 1000) < 0)
|
||||
return -1;
|
||||
|
||||
/* read status */
|
||||
if (read_status_reg() != 0) {
|
||||
std::cout << "disable protection failed" << std::endl;
|
||||
return -1;
|
||||
} else
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
* Copyright (C) 2019 Gwenhael Goavec-Merou <[email protected]>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef SPIFLASH_HPP
|
||||
#define SPIFLASH_HPP
|
||||
|
||||
#include "ftdijtag.hpp"
|
||||
|
||||
class SPIFlash {
|
||||
public:
|
||||
SPIFlash(FtdiJtag *jtag, bool verbose);
|
||||
/* power */
|
||||
void power_up();
|
||||
void power_down();
|
||||
void reset();
|
||||
/* protection */
|
||||
int write_enable();
|
||||
int write_disable();
|
||||
int disable_protection();
|
||||
/* erase */
|
||||
int bulk_erase();
|
||||
int sector_erase(int addr);
|
||||
int sectors_erase(int base_addr, int len);
|
||||
/* write */
|
||||
int write_page(int addr, uint8_t *data, int len);
|
||||
/* combo flash + erase */
|
||||
int erase_and_prog(int base_addr, uint8_t *data, int len);
|
||||
/* display/info */
|
||||
uint8_t read_status_reg();
|
||||
void read_id();
|
||||
private:
|
||||
void jtag_write_read(uint8_t cmd, uint8_t *tx, uint8_t *rx, uint16_t len = 0);
|
||||
int wait(uint8_t mask, uint8_t cond, uint32_t timeout, bool verbose=false);
|
||||
|
||||
FtdiJtag *_jtag;
|
||||
bool _verbose;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,288 @@
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
#include <fstream>
|
||||
#include <map>
|
||||
#include <vector>
|
||||
|
||||
#include "ftdijtag.hpp"
|
||||
|
||||
#include "svf_jtag.hpp"
|
||||
|
||||
using namespace std;
|
||||
|
||||
void SVF_jtag::split_str(string const &str, vector<string> &vparse)
|
||||
{
|
||||
string token;
|
||||
std::istringstream tokenStream(str);
|
||||
while (std::getline(tokenStream, token, ' '))
|
||||
vparse.push_back(token);
|
||||
}
|
||||
|
||||
void SVF_jtag::clear_XYR(svf_XYR &t)
|
||||
{
|
||||
t.len = 0;
|
||||
t.tdo.clear();
|
||||
t.tdi.clear();
|
||||
t.mask.clear();
|
||||
t.smask.clear();
|
||||
}
|
||||
|
||||
|
||||
/* pas clair:
|
||||
* si length = 0 : tout est remis a zero
|
||||
* tdi, mask et smask sont memorises. Si pas present c'est la memoire
|
||||
* qui est utilise
|
||||
* tdo si absent on s'en fout
|
||||
* TODO: faut prendre en compte smask, mask and tdo
|
||||
* ameliorer l'analyse des chaines de caracteres
|
||||
*/
|
||||
void SVF_jtag::parse_XYR(vector<string> const &vstr, svf_XYR &t)
|
||||
{
|
||||
if (_verbose) cout << endl;
|
||||
int mode = 0;
|
||||
string s;
|
||||
//string tdi;
|
||||
string full_line;
|
||||
full_line.reserve(1276);
|
||||
int write_data = -1;
|
||||
|
||||
if (vstr[0][0] == 'S')
|
||||
write_data = ((vstr[0][1] == 'I') ? 0 : 1);
|
||||
|
||||
t.len = stoul(vstr[1]);
|
||||
if (t.len == 0) {
|
||||
clear_XYR(t);
|
||||
return;
|
||||
}
|
||||
|
||||
for (long unsigned int pos=2; pos < vstr.size(); pos++) {
|
||||
s = vstr[pos];
|
||||
|
||||
if (!s.compare("TDO")) {
|
||||
mode = 1;
|
||||
continue;
|
||||
} else if (!s.compare("TDI")) {
|
||||
mode = 2;
|
||||
continue;
|
||||
} else if (!s.compare("MASK")) {
|
||||
mode = 3;
|
||||
continue;
|
||||
} else if (!s.compare("SMASK")) {
|
||||
mode = 4;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (s.front() == '(')
|
||||
s = s.substr(1);
|
||||
if (s.front() == '\t')
|
||||
s = s.substr(1);
|
||||
if (s.back() == ')')
|
||||
s = s.substr(0, s.size()-1);
|
||||
|
||||
/* faut analyser et convertir le string ici
|
||||
* quand s.back() == ')'
|
||||
*/
|
||||
|
||||
full_line += s;
|
||||
s.clear();
|
||||
|
||||
if (vstr[pos].back() == ')') {
|
||||
switch (mode) {
|
||||
case 1:
|
||||
t.tdo.clear();
|
||||
t.tdo = full_line;
|
||||
break;
|
||||
case 2:
|
||||
t.tdi = full_line;
|
||||
break;
|
||||
case 3:
|
||||
t.mask.clear();
|
||||
t.mask= full_line;
|
||||
break;
|
||||
case 4:
|
||||
t.smask.clear();
|
||||
t.smask= full_line;
|
||||
break;
|
||||
}
|
||||
full_line.clear();
|
||||
}
|
||||
}
|
||||
if (write_data != -1) {
|
||||
string txbuf;
|
||||
int len = t.tdi.size() / 2 + ((t.tdi.size() % 2)? 1 : 0);
|
||||
txbuf.resize(len);
|
||||
char c;
|
||||
for (int i = t.tdi.size()-1, pos = 0; i >= 0; i--, pos++) {
|
||||
if (t.tdi[i] <= '9')
|
||||
c = 0x0f & (t.tdi[i] - '0');
|
||||
else
|
||||
c = 0x0f & (t.tdi[i] - 'A' + 10);
|
||||
|
||||
txbuf[pos/2] |= ((0x0F & c) << ((4*(pos & 1))));
|
||||
}
|
||||
|
||||
if (write_data == 0)
|
||||
_jtag->shiftIR((unsigned char *)txbuf.c_str(), NULL, t.len, _endir);
|
||||
else
|
||||
_jtag->shiftDR((unsigned char *)txbuf.c_str(), NULL, t.len, _enddr);
|
||||
}
|
||||
}
|
||||
|
||||
/* Implementation partielle de la spec */
|
||||
void SVF_jtag::parse_runtest(vector<string> const &vstr)
|
||||
{
|
||||
int pos = 1;
|
||||
int nb_iter = 0;
|
||||
int run_state = -1;
|
||||
int end_state = -1;
|
||||
// 0 => RUNTEST
|
||||
// 1 => Ca depend
|
||||
if (vstr[pos][0] > '9') {
|
||||
run_state = fsm_state[vstr[1]];
|
||||
pos++;
|
||||
}
|
||||
nb_iter = atoi(vstr[pos].c_str()); // duree mais attention ca peut etre un xxeyy
|
||||
pos++;
|
||||
pos++; // clk currently don't care
|
||||
if (!vstr[pos].compare("ENDSTATE")) {
|
||||
pos++;
|
||||
end_state = fsm_state[vstr[pos]];
|
||||
}
|
||||
|
||||
if (run_state != -1) {
|
||||
_run_state = run_state;
|
||||
}
|
||||
if (end_state != -1) {
|
||||
_end_state = end_state;
|
||||
}
|
||||
else if (run_state != -1)
|
||||
_end_state = run_state;
|
||||
_jtag->set_state(_run_state);
|
||||
_jtag->toggleClk(nb_iter);
|
||||
_jtag->set_state(_end_state);
|
||||
}
|
||||
|
||||
void SVF_jtag::handle_instruction(vector<string> const &vstr)
|
||||
{
|
||||
if (!vstr[0].compare("FREQUENCY")) {
|
||||
_freq_hz = atof(vstr[1].c_str());
|
||||
if (_verbose) {
|
||||
cout << "frequence valeur " << vstr[1] << " unite " << vstr[2];
|
||||
cout << _freq_hz << endl;
|
||||
}
|
||||
_jtag->setClkFreq(_freq_hz);
|
||||
} else if (!vstr[0].compare("TRST")) {
|
||||
if (_verbose) cout << "trst value : " << vstr[1] << endl;
|
||||
} else if (!vstr[0].compare("ENDDR")) {
|
||||
if (_verbose) cout << "enddr value : " << vstr[1] << endl;
|
||||
_enddr = fsm_state[vstr[1]];
|
||||
} else if (!vstr[0].compare("ENDIR")) {
|
||||
if (_verbose) cout << "endir value : " << vstr[1] << endl;
|
||||
_endir = fsm_state[vstr[1]];
|
||||
} else if (!vstr[0].compare("STATE")) {
|
||||
if (_verbose) cout << "state value : " << vstr[1] << endl;
|
||||
_jtag->set_state(fsm_state[vstr[1]]);
|
||||
} else if (!vstr[0].compare("RUNTEST")) {
|
||||
parse_runtest(vstr);
|
||||
} else if (!vstr[0].compare("HIR")) {
|
||||
parse_XYR(vstr, hir);
|
||||
if (_verbose) {
|
||||
cout << "HIR" << endl;
|
||||
cout << "\tlen : " << hir.len << endl;
|
||||
cout << "\ttdo : " << hir.tdo.size()*4 << endl;
|
||||
cout << "\ttdi : " << hir.tdi.size()*4 << endl;
|
||||
cout << "\tmask : " << hir.mask.size()*4 << endl;
|
||||
cout << "\tsmask : " << hir.smask.size()*4 << endl;
|
||||
}
|
||||
} else if (!vstr[0].compare("HDR")) {
|
||||
parse_XYR(vstr, hdr);
|
||||
if (_verbose) {
|
||||
cout << "HDR" << endl;
|
||||
cout << "\tlen : " << hdr.len << endl;
|
||||
cout << "\ttdo : " << hdr.tdo.size()*4 << endl;
|
||||
cout << "\ttdi : " << hdr.tdi.size()*4 << endl;
|
||||
cout << "\tmask : " << hdr.mask.size()*4 << endl;
|
||||
cout << "\tsmask : " << hdr.smask.size()*4 << endl;
|
||||
}
|
||||
} else if (!vstr[0].compare("SIR")) {
|
||||
parse_XYR(vstr, sir);
|
||||
if (_verbose) {
|
||||
for (auto &&t: vstr)
|
||||
cout << t << " ";
|
||||
cout << endl;
|
||||
cout << "\tlen : " << sir.len << endl;
|
||||
cout << "\ttdo : " << sir.tdo.size()*4 << endl;
|
||||
cout << "\ttdi : " << sir.tdi.size()*4 << endl;
|
||||
cout << "\tmask : " << sir.mask.size()*4 << endl;
|
||||
cout << "\tsmask : " << sir.smask.size()*4 << endl;
|
||||
}
|
||||
} else if (!vstr[0].compare("SDR")) {
|
||||
parse_XYR(vstr, sdr);
|
||||
if (_verbose) {
|
||||
cout << "SDR" << endl;
|
||||
cout << "\tlen : " << sdr.len << endl;
|
||||
cout << "\ttdo : " << sdr.tdo.size()*4 << endl;
|
||||
cout << "\ttdi : " << sdr.tdi.size()*4 << endl;
|
||||
cout << "\tmask : " << sdr.mask.size()*4 << endl;
|
||||
cout << "\tsmask : " << sdr.smask.size()*4 << endl;
|
||||
}
|
||||
} else {
|
||||
cout << "error: unhandled instruction : " << vstr[0] << endl;
|
||||
}
|
||||
}
|
||||
|
||||
SVF_jtag::SVF_jtag(FtdiJtag *jtag, bool verbose):_verbose(verbose), _freq_hz(0),
|
||||
_enddr(fsm_state["IDLE"]), _endir(fsm_state["IDLE"]),
|
||||
_run_state(fsm_state["IDLE"]), _end_state(fsm_state["IDLE"])
|
||||
|
||||
{
|
||||
_jtag = jtag;
|
||||
_jtag->go_test_logic_reset();
|
||||
}
|
||||
|
||||
SVF_jtag::~SVF_jtag() {}
|
||||
|
||||
/* Read SVF file line by line
|
||||
* concat continuous lines
|
||||
* and pass instruction to handle_instruction
|
||||
*/
|
||||
void SVF_jtag::parse(string filename)
|
||||
{
|
||||
string str;
|
||||
vector<string> vstr;
|
||||
bool is_complete;
|
||||
ifstream fs;
|
||||
|
||||
fs.open(filename);
|
||||
if (!fs.is_open()) {
|
||||
cerr << "error to opening svf file " << filename << endl;
|
||||
return;
|
||||
}
|
||||
|
||||
while (getline(fs, str)) {
|
||||
is_complete = false;
|
||||
if (str[0] == '!') // comment
|
||||
continue;
|
||||
if (str.back() == ';') {
|
||||
str.pop_back();
|
||||
is_complete = true;
|
||||
}
|
||||
|
||||
split_str(str, vstr);
|
||||
if (is_complete) {
|
||||
if (_verbose) {
|
||||
if (vstr[0].compare("HDR") && vstr[0].compare("HIR")
|
||||
&& vstr[0].compare("SDR") && vstr[0].compare("SIR")) {
|
||||
for (auto &&word: vstr)
|
||||
cout << word << " ";
|
||||
cout << endl;
|
||||
}
|
||||
}
|
||||
handle_instruction(vstr);
|
||||
vstr.clear();
|
||||
}
|
||||
}
|
||||
|
||||
cout << "end of flash" << endl;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
#ifndef SVF_JTAG_HPP
|
||||
#define SVF_JTAG_HPP
|
||||
#include <iostream>
|
||||
#include <vector>
|
||||
#include <map>
|
||||
using namespace std;
|
||||
|
||||
class SVF_jtag {
|
||||
public:
|
||||
SVF_jtag(FtdiJtag *jtag, bool verbose);
|
||||
~SVF_jtag();
|
||||
void parse(string filename);
|
||||
void setVerbose(bool verbose) {_verbose = verbose;}
|
||||
|
||||
private:
|
||||
typedef struct {
|
||||
uint32_t len;
|
||||
string tdo;
|
||||
string tdi;
|
||||
string mask;
|
||||
string smask;
|
||||
} svf_XYR;
|
||||
|
||||
void split_str(string const &str, vector<string> &vparse);
|
||||
void clear_XYR(svf_XYR &t);
|
||||
void parse_XYR(vector<string> const &vstr/*, svf_stat &svfs*/, svf_XYR &t);
|
||||
void parse_runtest(vector<string> const &vstr);
|
||||
void handle_instruction(vector<string> const &vstr);
|
||||
|
||||
map <string, uint8_t> fsm_state = {
|
||||
{"RESET", 0},
|
||||
{"IDLE", 1},
|
||||
{"DRSELECT", 2},
|
||||
{"DRCAPTURE", 3},
|
||||
{"DRSHIFT", 4},
|
||||
{"DREXIT1", 5},
|
||||
{"DRPAUSE", 6},
|
||||
{"DREXIT2", 7},
|
||||
{"DRUPDATE", 8},
|
||||
{"IRSELECT", 9},
|
||||
{"IRCAPTURE", 10},
|
||||
{"IRSHIFT", 11},
|
||||
{"IREXIT1", 12},
|
||||
{"IRPAUSE", 13},
|
||||
{"IREXIT2", 14},
|
||||
{"IRUPDATE", 15}
|
||||
};
|
||||
|
||||
FtdiJtag *_jtag;
|
||||
bool _verbose;
|
||||
|
||||
uint32_t _freq_hz;
|
||||
int _enddr;
|
||||
int _endir;
|
||||
int _run_state;
|
||||
int _end_state;
|
||||
svf_XYR hdr;
|
||||
svf_XYR hir;
|
||||
svf_XYR sdr;
|
||||
svf_XYR sir;
|
||||
svf_XYR tdr;
|
||||
svf_XYR tir;
|
||||
};
|
||||
#endif
|
||||
+186
@@ -0,0 +1,186 @@
|
||||
#include <iostream>
|
||||
#include <stdexcept>
|
||||
|
||||
#include "ftdijtag.hpp"
|
||||
#include "bitparser.hpp"
|
||||
#include "mcsParser.hpp"
|
||||
#include "spiFlash.hpp"
|
||||
|
||||
#include "xilinx.hpp"
|
||||
#include "part.hpp"
|
||||
|
||||
Xilinx::Xilinx(FtdiJtag *jtag, std::string filename, bool verbose):
|
||||
Device(jtag, filename, verbose)
|
||||
{
|
||||
if (_filename != ""){
|
||||
if (_file_extension == "bit")
|
||||
_mode = Device::MEM_MODE;
|
||||
else
|
||||
_mode = Device::SPI_MODE;
|
||||
}
|
||||
}
|
||||
Xilinx::~Xilinx() {}
|
||||
|
||||
#define CFG_IN 0x05
|
||||
#define USERCODE 0x08
|
||||
#define IDCODE 0x09
|
||||
#define ISC_ENABLE 0x10
|
||||
#define JPROGRAM 0x0B
|
||||
#define JSTART 0x0C
|
||||
#define JSHUTDOWN 0x0D
|
||||
#define ISC_DISABLE 0x16
|
||||
#define BYPASS 0x3f
|
||||
|
||||
void Xilinx::reset()
|
||||
{
|
||||
_jtag->shiftIR(JSHUTDOWN, 6);
|
||||
_jtag->shiftIR(JPROGRAM, 6);
|
||||
_jtag->set_state(FtdiJtag::RUN_TEST_IDLE);
|
||||
_jtag->toggleClk(10000*12);
|
||||
|
||||
_jtag->set_state(FtdiJtag::RUN_TEST_IDLE);
|
||||
_jtag->toggleClk(2000);
|
||||
|
||||
_jtag->shiftIR(BYPASS, 6);
|
||||
_jtag->set_state(FtdiJtag::RUN_TEST_IDLE);
|
||||
_jtag->toggleClk(2000);
|
||||
}
|
||||
|
||||
int Xilinx::idCode()
|
||||
{
|
||||
unsigned char rx_data[4];
|
||||
_jtag->go_test_logic_reset();
|
||||
_jtag->shiftIR(IDCODE, 6);
|
||||
_jtag->shiftDR(NULL, rx_data, 32);
|
||||
return ((rx_data[0] & 0x000000ff) |
|
||||
((rx_data[1] << 8) & 0x0000ff00) |
|
||||
((rx_data[2] << 16) & 0x00ff0000) |
|
||||
((rx_data[3] << 24) & 0xff000000));
|
||||
}
|
||||
|
||||
void Xilinx::program(unsigned int offset)
|
||||
{
|
||||
switch (_mode) {
|
||||
case Device::NONE_MODE:
|
||||
return;
|
||||
break;
|
||||
case Device::SPI_MODE:
|
||||
program_spi(offset);
|
||||
reset();
|
||||
break;
|
||||
case Device::MEM_MODE:
|
||||
BitParser bitfile(_filename, _verbose);
|
||||
bitfile.parse();
|
||||
program_mem(bitfile, offset);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void Xilinx::program_spi(unsigned int offset)
|
||||
{
|
||||
std::string bitname = "/usr/local/share/openFPGALoader/spiOverJtag_";
|
||||
bitname += fpga_list[idCode()].family + ".bit";
|
||||
|
||||
/* first: load spi over jtag */
|
||||
BitParser bitfile(bitname, _verbose);
|
||||
bitfile.parse();
|
||||
program_mem(bitfile, offset);
|
||||
|
||||
/* last: read file and erase/flash spi flash */
|
||||
McsParser mcs(_filename, _verbose);
|
||||
mcs.parse();
|
||||
SPIFlash spiFlash(_jtag, _verbose);
|
||||
spiFlash.erase_and_prog(offset, mcs.getData(), mcs.getLength());
|
||||
}
|
||||
|
||||
void Xilinx::program_mem(BitParser &bitfile, unsigned int offset)
|
||||
{
|
||||
if (_filename == "") return;
|
||||
std::cout << "load program" << std::endl;
|
||||
unsigned char tx_buf, rx_buf;
|
||||
/* comment TDI TMS TCK
|
||||
* 1: On power-up, place a logic 1 on the TMS,
|
||||
* and clock the TCK five times. This ensures X 1 5
|
||||
* starting in the TLR (Test-Logic-Reset) state.
|
||||
*/
|
||||
_jtag->go_test_logic_reset();
|
||||
/*
|
||||
* 2: Move into the RTI state. X 0 1
|
||||
* 3: Move into the SELECT-IR state. X 1 2
|
||||
* 4: Enter the SHIFT-IR state. X 0 2
|
||||
* 5: Start loading the JPROGRAM instruction, 01011(4) 0 5
|
||||
* LSB first:
|
||||
* 6: Load the MSB of the JPROGRAM instruction
|
||||
* when exiting SHIFT-IR, as defined in the 0 1 1
|
||||
* IEEE standard.
|
||||
* 7: Place a logic 1 on the TMS and clock the
|
||||
* TCK five times. This ensures starting in X 1 5
|
||||
* the TLR (Test-Logic-Reset) state.
|
||||
*/
|
||||
_jtag->shiftIR(JPROGRAM, 6);
|
||||
/* test */
|
||||
tx_buf = BYPASS;
|
||||
do {
|
||||
_jtag->shiftIR(&tx_buf, &rx_buf, 6);
|
||||
} while (!(rx_buf &0x01));
|
||||
/*
|
||||
* 8: Move into the RTI state. X 0 10,000(1)
|
||||
*/
|
||||
_jtag->set_state(FtdiJtag::RUN_TEST_IDLE);
|
||||
_jtag->toggleClk(10000*12);
|
||||
/*
|
||||
* 9: Start loading the CFG_IN instruction,
|
||||
* LSB first: 00101 0 5
|
||||
* 10: Load the MSB of CFG_IN instruction when
|
||||
* exiting SHIFT-IR, as defined in the 0 1 1
|
||||
* IEEE standard.
|
||||
*/
|
||||
_jtag->shiftIR(CFG_IN, 6);
|
||||
/*
|
||||
* 11: Enter the SELECT-DR state. X 1 2
|
||||
*/
|
||||
_jtag->set_state(FtdiJtag::SELECT_DR_SCAN);
|
||||
/*
|
||||
* 12: Enter the SHIFT-DR state. X 0 2
|
||||
*/
|
||||
_jtag->set_state(FtdiJtag::SHIFT_DR);
|
||||
/*
|
||||
* 13: Shift in the FPGA bitstream. Bitn (MSB)
|
||||
* is the first bit in the bitstream(2). bit1...bitn 0 (bits in bitstream)-1
|
||||
* 14: Shift in the last bit of the bitstream.
|
||||
* Bit0 (LSB) shifts on the transition to bit0 1 1
|
||||
* EXIT1-DR.
|
||||
*/
|
||||
/* GGM: TODO */
|
||||
_jtag->shiftDR(bitfile.getData(), NULL, 8*bitfile.getLength());
|
||||
/*
|
||||
* 15: Enter UPDATE-DR state. X 1 1
|
||||
*/
|
||||
_jtag->set_state(FtdiJtag::UPDATE_DR);
|
||||
/*
|
||||
* 16: Move into RTI state. X 0 1
|
||||
*/
|
||||
_jtag->set_state(FtdiJtag::RUN_TEST_IDLE);
|
||||
/*
|
||||
* 17: Enter the SELECT-IR state. X 1 2
|
||||
* 18: Move to the SHIFT-IR state. X 0 2
|
||||
* 19: Start loading the JSTART instruction
|
||||
* (optional). The JSTART instruction 01100 0 5
|
||||
* initializes the startup sequence.
|
||||
* 20: Load the last bit of the JSTART instruction. 0 1 1
|
||||
* 21: Move to the UPDATE-IR state. X 1 1
|
||||
*/
|
||||
_jtag->shiftIR(JSTART, 6, FtdiJtag::UPDATE_IR);
|
||||
/*
|
||||
* 22: Move to the RTI state and clock the
|
||||
* startup sequence by applying a minimum X 0 2000
|
||||
* of 2000 clock cycles to the TCK.
|
||||
*/
|
||||
_jtag->set_state(FtdiJtag::RUN_TEST_IDLE);
|
||||
_jtag->toggleClk(2000);
|
||||
/*
|
||||
* 23: Move to the TLR state. The device is
|
||||
* now functional. X 1 3
|
||||
*/
|
||||
_jtag->go_test_logic_reset();
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
#ifndef XILINX_HPP
|
||||
#define XILINX_HPP
|
||||
|
||||
#include "bitparser.hpp"
|
||||
#include "device.hpp"
|
||||
#include "ftdijtag.hpp"
|
||||
|
||||
class Xilinx: public Device {
|
||||
public:
|
||||
Xilinx(FtdiJtag *jtag, std::string filename, bool verbose);
|
||||
~Xilinx();
|
||||
|
||||
void program(unsigned int offset = 0) override;
|
||||
void program_spi(unsigned int offset = 0);
|
||||
void program_mem(BitParser &bitfile, unsigned int offset = 0);
|
||||
int idCode();
|
||||
void reset();
|
||||
};
|
||||
|
||||
#endif
|
||||
Reference in New Issue
Block a user