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