initial commit

This commit is contained in:
Gwenhael Goavec-Merou 2019-09-26 18:29:20 +02:00
parent 62af4cd266
commit 4530942e17
22 changed files with 2836 additions and 0 deletions

13
altera.cpp Normal file
View File

@ -0,0 +1,13 @@
#include "altera.hpp"
#include "ftdijtag.hpp"
#include "device.hpp"
Altera::Altera(FtdiJtag *jtag, enum prog_mode mode, std::string filename):Device(jtag, mode, filename),
_bitfile(filename)
{}
Altera::~Altera()
{}
void Altera::program()
{}
int Altera::idCode()
{}

19
altera.hpp Normal file
View File

@ -0,0 +1,19 @@
#ifndef ALTERA_HPP
#define ALTERA_HPP
#include "bitparser.hpp"
#include "device.hpp"
#include "ftdijtag.hpp"
class Altera: public Device {
public:
Altera(FtdiJtag *jtag, enum prog_mode mode, std::string filename);
~Altera();
void program();
int idCode();
private:
BitParser _bitfile;
};
#endif

182
bitparser.cpp Normal file
View File

@ -0,0 +1,182 @@
#include "bitparser.hpp"
#include <stdio.h>
#include <stdlib.h>
#include <iostream>
#include <arpa/inet.h>
using namespace std;
#ifdef DEBUG
#define display(...) \
do { if (1) fprintf(stdout, __VA_ARGS__);} while(0)
#else
#define display(...) do {} while(0)
#endif
BitParser::BitParser(string filename):
fieldA(), part_name(), date(), hour(),
design_name(), userID(), toolVersion(),
file_length(0), _filename(filename)
{
bit_data = NULL;
}
BitParser::~BitParser()
{
if (bit_data != NULL)
free(bit_data);
}
int BitParser::parseField(unsigned char type, FILE *fd)
{
short length;
char tmp[64];
int pos, prev_pos;
if (type != 'e') {
fread(&length, sizeof(short), 1, fd);
length = ntohs(length);
} else {
length = 4;
}
fread(tmp, sizeof(unsigned char), length, fd);
#ifdef DEBUG
for (int i = 0; i < length; i++)
printf("%c", tmp[i]);
printf("\n");
#endif
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);
printf("%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);
printf("%d %d %s\n", prev_pos, pos, userID.c_str());
prev_pos = pos+1;
//pos = fieldA.find(";", prev_pos);
toolVersion = fieldA.substr(prev_pos);
printf("%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 */
file_length = 0;
for (int i = 0; i < 4; i++) {
#ifdef DEBUG
printf("%x %x\n", 0xff & tmp[i], file_length);
#endif
file_length <<= 8;
file_length |= 0xff & tmp[i];
}
#ifdef DEBUG
printf(" %x\n", file_length);
#endif
break;
}
return length;
}
int BitParser::parse()
{
unsigned char *tmp_data;
FILE *fd = fopen(_filename.c_str(), "rb");
if (fd == NULL) {
cerr << "Error: failed to open " + _filename << endl;
return -1;
}
short length;
unsigned char type;
display("parser\n\n");
/* Field 1 : misc header */
fread(&length, sizeof(short), 1, fd);
length = ntohs(length);
display("%d\n", length);
fseek(fd, length, SEEK_CUR);
fread(&length, sizeof(short), 1, fd);
length = ntohs(length);
display("%d\n", length);
/* process all field */
fread(&type, sizeof(unsigned char), 1, fd);
display("Field 2 %c\n", type);
parseField(type, fd);
fread(&type, sizeof(unsigned char), 1, fd);
display("Field 3 %c\n", type);
parseField(type, fd);
fread(&type, sizeof(unsigned char), 1, fd);
display("Field 4 %c\n", type);
parseField(type, fd);
fread(&type, sizeof(unsigned char), 1, fd);
display("Field 5 %c\n", type);
parseField(type, fd);
fread(&type, sizeof(unsigned char), 1, fd);
display("Field 6 %c\n", type);
parseField(type, fd);
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 : " << file_length << endl;
/* rest of the file is data to send */
bit_data = (unsigned char *)malloc(sizeof(unsigned char) * file_length);
if (bit_data == NULL) {
cerr << "Error: data buffer malloc failed" << endl;
return -1;
}
tmp_data = (unsigned char *)malloc(sizeof(unsigned char) * file_length);
if (tmp_data == NULL) {
cerr << "Error: data buffer malloc failed" << endl;
return -1;
}
int pos = ftell(fd);
cout << pos + file_length << endl;
size_t ret = fread(tmp_data, sizeof(unsigned char), file_length, fd);
if (ret != (size_t)file_length) {
cerr << "Error: data read different to asked length" << endl;
return -1;
}
for (int i = 0; i < file_length; i++) {
bit_data[i] = reverseByte(tmp_data[i]);
}
fclose(fd);
free(tmp_data);
return 0;
}
unsigned char *BitParser::getData()
{
return bit_data;
}
unsigned char BitParser::reverseByte(unsigned char src)
{
unsigned char dst = 0;
for (int i=0; i < 8; i++) {
dst = (dst << 1) | (src & 0x01);
src >>= 1;
}
return dst;
}

29
bitparser.hpp Normal file
View File

@ -0,0 +1,29 @@
#ifndef BITPARSER_H
#define BITPARSER_H
#include <iostream>
class BitParser {
public:
BitParser(std::string filename);
~BitParser();
int parse();
unsigned char *getData();
int getLength() {return file_length;}
private:
int parseField(unsigned char type, FILE *fd);
unsigned char reverseByte(unsigned char c);
std::string fieldA;
std::string part_name;
std::string date;
std::string hour;
std::string design_name;
std::string userID;
std::string toolVersion;
int file_length;
unsigned char *bit_data;
std::string _filename;
};
#endif

12
board.hpp Normal file
View File

@ -0,0 +1,12 @@
#ifndef BOARD_HPP
#define BOARD_HPP
#include <map>
static std::map <std::string, std::string > board_list = {
{"arty", "digilent"},
{"cyc1000", "ft2232"},
{"de0nano", "usbblaster"}
};
#endif

14
cable.hpp Normal file
View File

@ -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}},
{"ft2232", {0x0403, 0x6010, 0x08, 0x0B, 0x08, 0x0B}},
{"altera", {0xcafe, 0xbebe, 0x08, 0x0B, 0x08, 0x0B}}
};
#endif

27
device.cpp Normal file
View File

@ -0,0 +1,27 @@
#include <iostream>
#include <stdexcept>
#include "device.hpp"
using namespace std;
Device::Device(FtdiJtag *jtag, enum prog_mode mode, string filename):
_filename(filename), _mode(mode)
{
_jtag = jtag;
}
int Device::idCode()
{
return 0;
}
void Device::program()
{
throw std::runtime_error("Not implemented");
}
void Device::reset()
{
throw std::runtime_error("Not implemented");
}

30
device.hpp Normal file
View File

@ -0,0 +1,30 @@
#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,
MEM_MODE
};
Device(FtdiJtag *jtag, enum prog_mode, std::string filename);
virtual void program();
int idCode();
void reset();
protected:
FtdiJtag *_jtag;
std::string _filename;
enum prog_mode _mode;
};
#endif

267
epcq.cpp Normal file
View File

@ -0,0 +1,267 @@
#include <stdio.h>
#include <stdlib.h>
#include <strings.h>
//#include <ftdi_spi.h>
//#include <ftdi_handle.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) {
printf("erreur de write dans erase_sector\n");
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 << "erreur d'ouverture de " << 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 */
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);
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);
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):
_spi(vid, pid, interface, clkHZ)
{
unsigned char mode = 0;
_spi.setMode(mode);
}
EPCQ::~EPCQ()
{
//ftdi_spi_close(_spi);
//free(_spi);
}

45
epcq.hpp Normal file
View File

@ -0,0 +1,45 @@
#include <iostream>
#include <vector>
#include <ftdispi.hpp>
using namespace std;
class EPCQ {
public:
EPCQ(int vid, int pid, unsigned char interface, uint32_t clkHZ);
~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;
#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
};

547
ftdijtag.cpp Normal file
View File

@ -0,0 +1,547 @@
#include <iostream>
#include <map>
#include <vector>
#include <stdio.h>
#include <string.h>
#include <string>
#include <ft2232_mpsse/ftdi_handle.h>
#include "ftdijtag.hpp"
#include "ftdipp_mpsse.hpp"
using namespace std;
#define DEBUG 1
#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,
unsigned char interface, uint32_t clkHZ):
FTDIpp_MPSSE(cable.vid, cable.pid, interface, clkHZ),
_state(RUN_TEST_IDLE),
_tms_buffer_size(128), _num_tms(0),
_board_name("nope"), _verbose(false)
{
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);
_verbose = false;
_tms_buffer = (unsigned char *)malloc(sizeof(unsigned char) * _tms_buffer_size);
bzero(_tms_buffer, _tms_buffer_size);
init(1, 0xfb, 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_READ_NEG |
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);
}
int FtdiJtag::detectChain(vector<int> &devices, int max_dev)
{
unsigned char rx_buff[4];
unsigned int tmp;
devices.clear();
go_test_logic_reset();
set_state(SHIFT_DR);
for (int i = 0; i < max_dev; i++) {
read_write(NULL, 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 *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 | MPSSE_READ_NEG) : 0)),
((xfer - 1) & 0xff), // low
(((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;
}
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;
}
}
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);
}
}
/* 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 | MPSSE_READ_NEG : 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);
}
_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
printf("drlen %d\n", drlen);
read_write(tdi, tdo, drlen, 1);// 1 since only one device
set_state(end_state);
return 0;
}
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";
}
}

59
ftdijtag.hpp Normal file
View File

@ -0,0 +1,59 @@
#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, unsigned char interface, uint32_t clkHZ);
~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 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 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:
int _state;
int _tms_buffer_size;
int _num_tms;
unsigned char *_tms_buffer;
std::string _board_name;
bool _verbose;
};
#endif

261
ftdipp_mpsse.cpp Normal file
View File

@ -0,0 +1,261 @@
#include <iostream>
#include <string.h>
#include <libusb.h>
#include "ftdipp_mpsse.hpp"
using namespace std;
#define DEBUG 1
#ifdef DEBUG
#define display(...) \
do { if (_verbose) fprintf(stdout, __VA_ARGS__);}while(0)
#else
#define display(...) do {}while(0)
#endif
FTDIpp_MPSSE::FTDIpp_MPSSE(int vid, int pid, unsigned char interface,
uint32_t clkHZ):_pid(pid), _interface(interface),
_clkHZ(clkHZ), _buffer_size(2*32768), _num(0), _verbose(false)
{
open_device(vid, pid, (unsigned char)interface, 115200);
_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 vid, unsigned int pid,
unsigned char interface, unsigned int baudrate)
{
int ret;
_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 ((ret = ftdi_usb_open_desc(_ftdi, vid, pid, NULL, NULL)) < 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) {
printf("erreur baudrate\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;
ftdi_usb_purge_rx_buffer(_ftdi);
ftdi_usb_purge_tx_buffer(_ftdi);
/*ftdi_usb_close(h->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) {
printf("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)
printf("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 << "erreur de reset" << endl;
return -1;
}
if (ftdi_set_bitmode(_ftdi, 0x00, BITMODE_RESET) < 0) {
cout << "erreur de bitmode_reset" << endl;
return -1;
}
if (ftdi_usb_purge_buffers(_ftdi) != 0) {
cout << "erreur de reset" << endl;
return -1;
}
if (ftdi_set_latency_timer(_ftdi, latency) != 0) {
cout << "erreur de reset" << endl;
return -1;
}
/* enable MPSSE mode */
if (ftdi_set_bitmode(_ftdi, bitmask_mode, BITMODE_MPSSE) < 0) {
cout << "erreur de bitmode_mpsse" << 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;
uint8_t buffer[4] = { 0x08A, 0x86, 0x00, 0x00};
uint32_t base_freq = 60000000;
uint32_t real_freq = 0;
uint16_t presc;
if (use_divide_by_5) {
base_freq /= 5;
buffer[0] = 0x8B;
}
if ((use_divide_by_5 && _clkHZ > 6000000) || _clkHZ > 30000000) {
printf("erreur: freq trop haute\n");
return -1;
}
presc = (base_freq /(_clkHZ * 2)) -1;
real_freq = base_freq / ((1+presc)*2);
printf("presc : %d input freq : %d requested freq : %d real freq : %d\n", presc,
base_freq, _clkHZ, real_freq);
buffer[2] = presc & 0xff;
buffer[3] = (presc >> 8) & 0xff;
if (ftdi_write_data(_ftdi, buffer, 4) != 4) {
printf("erreur de write pour set bit, frequency\n");
return -1;
}
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;
/* this case theorically never happen */
if (len > _buffer_size) {
mpsse_write();
for (; len > _buffer_size; len -= _buffer_size) {
memcpy(_buffer, ptr, _buffer_size);
mpsse_write();
ptr += _buffer_size;
}
}
if (_num + len + 1 >= _buffer_size) {
if (mpsse_write() < 0) {
cout << "erreur de write_data dans " << __func__ << endl;
return -1;
}
}
if (_verbose) cout << __func__ << " " << _num << " " << len << endl;
memcpy(_buffer + _num, ptr, len);
_num += len;
return 0;
}
int FTDIpp_MPSSE::mpsse_write()
{
if (_num == 0)
return 0;
display("%s %d\n", __func__, _num);
if (ftdi_write_data(_ftdi, _buffer, _num) != _num) {
cout << "erreur de write" << endl;
return -1;
}
_num = 0;
return 0;
}
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) {
printf("erreur dans %s", __func__);
return -1;
}
if (_verbose) {
display("%s %d\n", __func__, n);
for (int i = 0; i < n; i++)
display("\t%s %x\n", __func__, p[i]);
}
len -= n;
p += n;
num_read += n;
} while (len > 0);
return num_read;
}

47
ftdipp_mpsse.hpp Normal file
View File

@ -0,0 +1,47 @@
#ifndef _FTDIPP_MPSSE_H
#define _FTDIPP_MPSSE_H
#include <ftdi.h>
class FTDIpp_MPSSE {
public:
FTDIpp_MPSSE(int vid, int pid, unsigned char interface, uint32_t clkHZ);
~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);
protected:
void open_device(unsigned int vid, unsigned int pid,
unsigned char interface, 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;}
private:
int _vid;
int _pid;
unsigned char _interface;
int _clkHZ;
int _buffer_size;
int _num;
bool _verbose;
unsigned char *_buffer;
struct ftdi_context *_ftdi;
};
#endif

388
ftdispi.cpp Normal file
View File

@ -0,0 +1,388 @@
#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):
FTDIpp_MPSSE(vid, pid, interface, clkHZ)
{
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("erreur d'ouverture\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("erreur d'ouverture\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("erreur de reset\n");
return -1;
}
if (ftdi_usb_purge_rx_buffer(spi->ftdic) != 0) {
printf("erreur de reset\n");
return -1;
}
if (ftdi_usb_purge_tx_buffer(spi->ftdic) != 0) {
printf("erreur de reset\n");
return -1;
}
if (ftdi_read_data_set_chunksize(spi->ftdic, SIZE) != 0) {
printf("erreur de reset\n");
return -1;
}
if (ftdi_write_data_set_chunksize(spi->ftdic, SIZE) != 0) {
printf("erreur de reset\n");
return -1;
}
if (ftdi_set_latency_timer(spi->ftdic, LATENCY) != 0) {
printf("erreur de reset\n");
return -1;
}
if (ftdi_set_event_char(spi->ftdic, 0x00, 0) != 0) {
printf("erreur de reset\n");
return -1;
}
if (ftdi_set_error_char(spi->ftdic, 0x00, 0) != 0) {
printf("erreur de reset\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("erreur de reset\n");
return -1;
}
if (ftdi_set_bitmode(spi->ftdic, 0x00, 0x02) != 0) { // enable mpsse mode
printf("erreur de reset\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("erreur de write pour 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("erreur disable loopback\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("erreur de write pour set bit\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("erreur\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;
}

41
ftdispi.hpp Normal file
View File

@ -0,0 +1,41 @@
#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);
~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;
};

262
main.cpp Normal file
View File

@ -0,0 +1,262 @@
/*
* Copyright (C) 2019 Gwenhael Goavec-Merou <gwenhael.goavec-merou@trabucayre.com>
*
* 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 <iostream>
#include <map>
#include <sstream>
#include <string.h>
#include <unistd.h>
#include <vector>
#include "board.hpp"
#include "cable.hpp"
#include "device.hpp"
#include "part.hpp"
#include "epcq.hpp"
#include "ftdipp_mpsse.hpp"
#include "ftdijtag.hpp"
#include "svf_jtag.hpp"
#include "bitparser.hpp"
#include "xilinx.hpp"
#define BIT_FOR_FLASH "/usr/local/share/cycloader_prog/test_sfl.svf"
enum {
BIT_FORMAT = 1,
SVF_FORMAT,
RPD_FORMAT
} _file_format;
using namespace std;
struct arguments {
bool verbose, display, reset;
unsigned int offset;
string bit_file;
string cable;
string board;
};
const char *argp_program_version = "cycloader 1.0";
const char *argp_program_bug_address = "<gwenhael.goavec-merou@trabucayre.com>";
static char doc[] = "cycloader -- a program to flash cyclone10 LP 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"},
{"board", 'b', "BOARD", 0, "board name, may be used instead of cable"},
{"display", 'd', 0, 0, "display FPGA and EEPROM model"},
{"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 reset_fpga(FtdiJtag *jtag)
{
/* PULSE_NCONFIG */
unsigned char tx_buff[4] = {0x01, 0x00};
tx_buff[0] = 0x01;
tx_buff[1] = 0x00;
jtag->set_state(FtdiJtag::TEST_LOGIC_RESET);
jtag->shiftIR(tx_buff, NULL, 10);
jtag->toggleClk(1);
jtag->set_state(FtdiJtag::TEST_LOGIC_RESET);
}
int main(int argc, char **argv)
{
FTDIpp_MPSSE::mpsse_bit_config cable;
bool prog_mem = false, prog_eeprom = false;
/* EEPROM must have reverse order
* other flash (softcore binary) must have direct order
*/
bool reverse_order=true;
short file_format = 0;
/* command line args. */
struct arguments args = {false, false, false, 0, "-", "-", "-"};
/* parse arguments */
argp_parse(&argp, argc, argv, 0, 0, &args);
/* if a bit_file is provided check type using file extension */
if (args.bit_file[0] != '-') {
string file_extension = args.bit_file.substr(args.bit_file.find_last_of(".") + 1);
if(file_extension == "svf") {
/* svf file */
prog_mem = true;
file_format = SVF_FORMAT;
} else if(file_extension == "rpd") {
/* rdp file */
prog_eeprom = true;
args.reset = true; // FPGA must reload eeprom content
file_format = RPD_FORMAT;
} else if (file_extension == "bit") {
prog_mem = true;
file_format = BIT_FORMAT;
} else {
if (args.offset == 0) {
cout << args.bit_file << " not FPGA bit make no sense to flash at beginning" << endl;
return EXIT_FAILURE;
}
reverse_order = false;
}
}
/* To have access to eeprom, an svf file must be send to memory
* so we must reset FPGA if no other bitfile is sent.
*/
if (args.display && !prog_mem)
args.reset = true;
printf("%s svf:%s eeprom:%s\n", args.bit_file.c_str(), (prog_mem)?"true":"false",
(prog_eeprom)?"true":"false");
if (args.reset && prog_mem) {
cerr << "Error: using both flash to RAM and reset make no sense" << endl;
return EXIT_FAILURE;
}
/* 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 */
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;
printf("%x %x\n", cable.pid, cable.vid);
/* jtag base */
FtdiJtag *jtag = new FtdiJtag(cable, 1, 6000000);
/* SVF logic */
SVF_jtag *svf = new SVF_jtag(jtag);
/* epcq */
EPCQ epcq(cable.vid, cable.pid, 2, 6000000);
/* chain detection:
* GGM TODO: must be done before
*/
vector<int> listDev;
int found = jtag->detectChain(listDev, 5);
int idcode = listDev[0];
printf("found %d devices\n", found);
if (found > 1) {
cerr << "currently only one device is supported" << endl;
return EXIT_FAILURE;
}
printf("%x\n", idcode);
if (fpga_list.find(idcode) == fpga_list.end()) {
cout << "Error: device not supported" << endl;
return 1;
} else {
printf("idcode 0x%x\nfunder %s\nmodel %s\nfamily %s\n",
idcode,
fpga_list[idcode].funder.c_str(),
fpga_list[idcode].model.c_str(),
fpga_list[idcode].family.c_str());
}
string fab = fpga_list[idcode].funder;
/* display fpga and eeprom */
if (args.display) {
vector<int> listDev;
int found = jtag->detectChain(listDev, 5);
printf("found %d devices\n", found);
for (unsigned int z=0; z < listDev.size(); z++)
printf("%x\n", listDev[z]);
printf("\n\n");
svf->parse(BIT_FOR_FLASH);
printf("%x\n", epcq.detect());
}
if (prog_mem) {
if (file_format == SVF_FORMAT) {
svf->parse(args.bit_file);
} else {
printf("todo\n");
Xilinx xil(jtag, Device::MEM_MODE, args.bit_file);
printf("%x\n", xil.idCode());
xil.program();
//xil.reset();
}
} else if (prog_eeprom) {
svf->parse(BIT_FOR_FLASH);
epcq.program(args.offset, args.bit_file, reverse_order);
}
if (args.reset) {
if (fab == "xilinx") {
Xilinx xil(jtag, Device::NONE_MODE, "");
xil.reset();
} else {
reset_fpga(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 'r':
arguments->reset = true;
break;
case 'd':
arguments->display = true;
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;
default:
return ARGP_ERR_UNKNOWN;
}
return 0;
}

18
part.hpp Normal file
View File

@ -0,0 +1,18 @@
#ifndef PART_HPP
#define PART_HPP
#include <map>
#include <string>
typedef struct {
std::string funder;
std::string model;
std::string family;
} fpga_model;
static std::map <int, fpga_model> fpga_list = {
{0x0362D093, {"xilinx", "artix a7 35t", "XC7"}},
{0x020f30dd, {"altera", "cyclone 10 LP", "10CL025"}}
};
#endif

288
svf_jtag.cpp Normal file
View File

@ -0,0 +1,288 @@
#include <iostream>
#include <sstream>
#include <fstream>
#include <map>
#include <vector>
#include <ftdipp_mpsse/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):_verbose(false), _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;
}

60
svf_jtag.hpp Normal file
View File

@ -0,0 +1,60 @@
#include <iostream>
#include <vector>
using namespace std;
class SVF_jtag {
public:
SVF_jtag(FtdiJtag *jtag);
~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;
};

203
xilinx.cpp Normal file
View File

@ -0,0 +1,203 @@
#include <iostream>
#include <stdexcept>
#include "ftdijtag.hpp"
#include "bitparser.hpp"
#include "xilinx.hpp"
Xilinx::Xilinx(FtdiJtag *jtag, enum prog_mode mode, std::string filename):Device(jtag, mode, filename),
_bitfile(filename)
{
if (_mode == Device::SPI_MODE) {
throw std::runtime_error("SPI flash is not supported on xilinx devices");
}
if (_mode != Device::NONE_MODE){
_bitfile.parse();
}
}
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()
{
unsigned char instr;
/*unsigned char reset_seq[] = {
0xFF, 0xFF, 0xFF, 0xFF, // dummy word
0xAA, 0x99, 0x55, 0x66, // sync word
0x20, 0x00, 0x00, 0x00, // type1 noop
//0x30, 0x02, 0x00, 0x01,
//0x00, 0x00, 0x00, 0x00,
0x30, 0x00, 0x80, 0x01, // type1 write 1 word to CMD
0x00, 0x00, 0x00, 0x0F, // iprog cmd
0x20, 0x00, 0x00, 0x00}; // noop*/
unsigned char reset_seq[] = {
0xFF, 0xFF, 0xFF, 0xFF, // dummy word
0x55, 0x99, 0xAA, 0x66, // sync word
0x04, 0x00, 0x00, 0x00, // type1 noop
0x0C, 0x40, 0x00, 0x80,
0x00, 0x00, 0x00, 0x00,
0x0C, 0x00, 0x01, 0x80, // type1 write 1 word to CMD
0x00, 0x00, 0x00, 0xf0, // iprog cmd
0x04, 0x00, 0x00, 0x00}; // noop*/
instr = JSHUTDOWN;
_jtag->shiftIR(&instr, NULL, 6);
_jtag->toggleClk(16);
instr = CFG_IN;
_jtag->shiftIR(&instr, NULL, 6);
for (int i =0; i < 4*8; i++) {
printf("%x\n", reset_seq[i]);
}
_jtag->shiftDR(reset_seq, NULL, 4*8*8, FtdiJtag::UPDATE_DR );
_jtag->set_state(FtdiJtag::RUN_TEST_IDLE);
instr = JSTART;
_jtag->shiftIR(&instr, NULL, 6, FtdiJtag::UPDATE_IR);
//_jtag->toggleClk(32);
//instr = BYPASS;
//_jtag->shiftIR(&instr, NULL, 6);
//_jtag->toggleClk(1);
_jtag->set_state(FtdiJtag::RUN_TEST_IDLE);
_jtag->toggleClk(2000);
_jtag->go_test_logic_reset();
}
int Xilinx::idCode()
{
unsigned char tx_data = IDCODE;
unsigned char rx_data[4];
_jtag->go_test_logic_reset();
_jtag->shiftIR(&tx_data, NULL, 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::flow_enable()
{
unsigned char data;
data = ISC_ENABLE;
_jtag->shiftIR(&data, NULL, 6);
//data[0]=0x0;
//jtag->shiftDR(data,0,5);
//io->cycleTCK(tck_len);
}
void Xilinx::flow_disable()
{
unsigned char data;
data = ISC_DISABLE;
_jtag->shiftIR(&data, NULL, 6);
//io->cycleTCK(tck_len);
//jtag->shiftIR(&BYPASS);
//data[0]=0x0;
//jtag->shiftDR(data,0,1);
//io->cycleTCK(1);
}
void Xilinx::program()
{
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.
*/
tx_buf = JPROGRAM;
_jtag->shiftIR(&tx_buf, NULL, 6/*, FtdiJtag::TEST_LOGIC_RESET*/);
/* 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.
*/
tx_buf = CFG_IN;
_jtag->shiftIR(&tx_buf, NULL, 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
*/
tx_buf = JSTART;
_jtag->shiftIR(&tx_buf, NULL, 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();
}

24
xilinx.hpp Normal file
View File

@ -0,0 +1,24 @@
#ifndef XILINX_HPP
#define XILINX_HPP
#include "bitparser.hpp"
#include "device.hpp"
#include "ftdijtag.hpp"
class Xilinx: public Device {
public:
Xilinx(FtdiJtag *jtag, enum prog_mode mode, std::string filename);
~Xilinx();
void program();
int idCode();
void reset();
private:
void flow_enable();
void flow_disable();
//FtdiJtag *_jtag;
//std::string _filename;
BitParser _bitfile;
};
#endif