From 4530942e177ce87f3f4a71db8991396d696f0cca Mon Sep 17 00:00:00 2001 From: Gwenhael Goavec-Merou Date: Thu, 26 Sep 2019 18:29:20 +0200 Subject: [PATCH] initial commit --- altera.cpp | 13 ++ altera.hpp | 19 ++ bitparser.cpp | 182 ++++++++++++++++ bitparser.hpp | 29 +++ board.hpp | 12 ++ cable.hpp | 14 ++ device.cpp | 27 +++ device.hpp | 30 +++ epcq.cpp | 267 +++++++++++++++++++++++ epcq.hpp | 45 ++++ ftdijtag.cpp | 547 +++++++++++++++++++++++++++++++++++++++++++++++ ftdijtag.hpp | 59 +++++ ftdipp_mpsse.cpp | 261 ++++++++++++++++++++++ ftdipp_mpsse.hpp | 47 ++++ ftdispi.cpp | 388 +++++++++++++++++++++++++++++++++ ftdispi.hpp | 41 ++++ main.cpp | 262 +++++++++++++++++++++++ part.hpp | 18 ++ svf_jtag.cpp | 288 +++++++++++++++++++++++++ svf_jtag.hpp | 60 ++++++ xilinx.cpp | 203 ++++++++++++++++++ xilinx.hpp | 24 +++ 22 files changed, 2836 insertions(+) create mode 100644 altera.cpp create mode 100644 altera.hpp create mode 100644 bitparser.cpp create mode 100644 bitparser.hpp create mode 100644 board.hpp create mode 100644 cable.hpp create mode 100644 device.cpp create mode 100644 device.hpp create mode 100644 epcq.cpp create mode 100644 epcq.hpp create mode 100644 ftdijtag.cpp create mode 100644 ftdijtag.hpp create mode 100644 ftdipp_mpsse.cpp create mode 100644 ftdipp_mpsse.hpp create mode 100644 ftdispi.cpp create mode 100644 ftdispi.hpp create mode 100644 main.cpp create mode 100644 part.hpp create mode 100644 svf_jtag.cpp create mode 100644 svf_jtag.hpp create mode 100644 xilinx.cpp create mode 100644 xilinx.hpp diff --git a/altera.cpp b/altera.cpp new file mode 100644 index 0000000..c37a74e --- /dev/null +++ b/altera.cpp @@ -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() +{} diff --git a/altera.hpp b/altera.hpp new file mode 100644 index 0000000..ed3c580 --- /dev/null +++ b/altera.hpp @@ -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 diff --git a/bitparser.cpp b/bitparser.cpp new file mode 100644 index 0000000..fbd665d --- /dev/null +++ b/bitparser.cpp @@ -0,0 +1,182 @@ +#include "bitparser.hpp" +#include +#include +#include +#include + +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; +} diff --git a/bitparser.hpp b/bitparser.hpp new file mode 100644 index 0000000..c276784 --- /dev/null +++ b/bitparser.hpp @@ -0,0 +1,29 @@ +#ifndef BITPARSER_H +#define BITPARSER_H + +#include + +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 diff --git a/board.hpp b/board.hpp new file mode 100644 index 0000000..233637f --- /dev/null +++ b/board.hpp @@ -0,0 +1,12 @@ +#ifndef BOARD_HPP +#define BOARD_HPP + +#include + +static std::map board_list = { + {"arty", "digilent"}, + {"cyc1000", "ft2232"}, + {"de0nano", "usbblaster"} +}; + +#endif diff --git a/cable.hpp b/cable.hpp new file mode 100644 index 0000000..bd4e549 --- /dev/null +++ b/cable.hpp @@ -0,0 +1,14 @@ +#ifndef CABLE_HPP +#define CABLE_HPP + +#include + +#include "ftdipp_mpsse.hpp" + +static std::map cable_list = { + {"digilent", {0x0403, 0x6010, 0xe8, 0xeb, 0x00, 0x60}}, + {"ft2232", {0x0403, 0x6010, 0x08, 0x0B, 0x08, 0x0B}}, + {"altera", {0xcafe, 0xbebe, 0x08, 0x0B, 0x08, 0x0B}} +}; + +#endif diff --git a/device.cpp b/device.cpp new file mode 100644 index 0000000..0955063 --- /dev/null +++ b/device.cpp @@ -0,0 +1,27 @@ +#include +#include + +#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"); +} + diff --git a/device.hpp b/device.hpp new file mode 100644 index 0000000..5e1a158 --- /dev/null +++ b/device.hpp @@ -0,0 +1,30 @@ +#ifndef DEVICE_HPP +#define DEVICE_HPP + +#include + +#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 diff --git a/epcq.cpp b/epcq.cpp new file mode 100644 index 0000000..ee0ba61 --- /dev/null +++ b/epcq.cpp @@ -0,0 +1,267 @@ +#include +#include +#include +//#include +//#include + +#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 +#include +#include +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 +}; diff --git a/ftdijtag.cpp b/ftdijtag.cpp new file mode 100644 index 0000000..6d49740 --- /dev/null +++ b/ftdijtag.cpp @@ -0,0 +1,547 @@ + +#include +#include +#include +#include +#include +#include + +#include + +#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 &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"; + } +} diff --git a/ftdijtag.hpp b/ftdijtag.hpp new file mode 100644 index 0000000..791e538 --- /dev/null +++ b/ftdijtag.hpp @@ -0,0 +1,59 @@ +#ifndef FTDIJTAG_H +#define FTDIJTAG_H +#include +#include +#include + +#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 &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 diff --git a/ftdipp_mpsse.cpp b/ftdipp_mpsse.cpp new file mode 100644 index 0000000..579cbe7 --- /dev/null +++ b/ftdipp_mpsse.cpp @@ -0,0 +1,261 @@ +#include +#include + +#include + +#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; +} diff --git a/ftdipp_mpsse.hpp b/ftdipp_mpsse.hpp new file mode 100644 index 0000000..4df0897 --- /dev/null +++ b/ftdipp_mpsse.hpp @@ -0,0 +1,47 @@ +#ifndef _FTDIPP_MPSSE_H +#define _FTDIPP_MPSSE_H +#include + +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 diff --git a/ftdispi.cpp b/ftdispi.cpp new file mode 100644 index 0000000..9d38869 --- /dev/null +++ b/ftdispi.cpp @@ -0,0 +1,388 @@ + +#include +#include +#include +#include +#include +#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; +} diff --git a/ftdispi.hpp b/ftdispi.hpp new file mode 100644 index 0000000..3286ed3 --- /dev/null +++ b/ftdispi.hpp @@ -0,0 +1,41 @@ +#include +#include +#include + +#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; +}; diff --git a/main.cpp b/main.cpp new file mode 100644 index 0000000..3ae5fbe --- /dev/null +++ b/main.cpp @@ -0,0 +1,262 @@ +/* + * Copyright (C) 2019 Gwenhael Goavec-Merou + * + * 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 . + */ +#include +#include +#include +#include +#include +#include +#include +#include + +#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 = ""; +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 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 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; +} + diff --git a/part.hpp b/part.hpp new file mode 100644 index 0000000..8ccbe13 --- /dev/null +++ b/part.hpp @@ -0,0 +1,18 @@ +#ifndef PART_HPP +#define PART_HPP + +#include +#include + +typedef struct { + std::string funder; + std::string model; + std::string family; +} fpga_model; + +static std::map fpga_list = { + {0x0362D093, {"xilinx", "artix a7 35t", "XC7"}}, + {0x020f30dd, {"altera", "cyclone 10 LP", "10CL025"}} +}; + +#endif diff --git a/svf_jtag.cpp b/svf_jtag.cpp new file mode 100644 index 0000000..00bcbec --- /dev/null +++ b/svf_jtag.cpp @@ -0,0 +1,288 @@ +#include +#include +#include +#include +#include + +#include + +#include + +using namespace std; + +void SVF_jtag::split_str(string const &str, vector &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 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 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 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 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; + +} diff --git a/svf_jtag.hpp b/svf_jtag.hpp new file mode 100644 index 0000000..ad20503 --- /dev/null +++ b/svf_jtag.hpp @@ -0,0 +1,60 @@ +#include +#include +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 &vparse); + void clear_XYR(svf_XYR &t); + void parse_XYR(vector const &vstr/*, svf_stat &svfs*/, svf_XYR &t); + void parse_runtest(vector const &vstr); + void handle_instruction(vector const &vstr); + + map 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; +}; diff --git a/xilinx.cpp b/xilinx.cpp new file mode 100644 index 0000000..9202ea1 --- /dev/null +++ b/xilinx.cpp @@ -0,0 +1,203 @@ +#include +#include + +#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(); +} diff --git a/xilinx.hpp b/xilinx.hpp new file mode 100644 index 0000000..4789fd7 --- /dev/null +++ b/xilinx.hpp @@ -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