diff --git a/CMakeLists.txt b/CMakeLists.txt index 66b379a..e1b1911 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -66,7 +66,6 @@ set(OPENFPGALOADER_SOURCE src/ftdiJtagMPSSE.cpp src/configBitstreamParser.cpp src/ftdipp_mpsse.cpp - src/xilinx.cpp src/main.cpp src/latticeBitParser.cpp src/gowin.cpp @@ -78,6 +77,8 @@ set(OPENFPGALOADER_SOURCE src/ftdispi.cpp src/altera.cpp src/bitparser.cpp + src/xilinx.cpp + src/xilinxMapParser.cpp ) set(OPENFPGALOADER_HEADERS @@ -113,7 +114,6 @@ set(OPENFPGALOADER_HEADERS src/epcq.hpp src/spiInterface.hpp src/svf_jtag.hpp - src/xilinx.hpp src/configBitstreamParser.hpp src/device.hpp src/gowin.hpp @@ -121,6 +121,8 @@ set(OPENFPGALOADER_HEADERS src/ftdispi.hpp src/lattice.hpp src/latticeBitParser.hpp + src/xilinx.hpp + src/xilinxMapParser.hpp ) add_executable(openFPGALoader diff --git a/src/xilinxMapParser.cpp b/src/xilinxMapParser.cpp new file mode 100644 index 0000000..6cb8c4d --- /dev/null +++ b/src/xilinxMapParser.cpp @@ -0,0 +1,134 @@ +// SPDX-License-Identifier: Apache-2.0 +/* + * Copyright (C) 2021 Gwenhael Goavec-Merou + */ +#include +#include + +#include +#include +#include +#include +#include + +#include "jedParser.hpp" +#include "xilinxMapParser.hpp" + +using namespace std; + +XilinxMapParser::XilinxMapParser(const string &filename, + uint16_t num_row, uint16_t num_col, + const JedParser *jed, const uint32_t usercode, + bool verbose): ConfigBitstreamParser(filename, + ConfigBitstreamParser::BIN_MODE, verbose), + _num_row(num_row), _num_col(num_col), _usercode(usercode) +{ + _jed = reinterpret_case(jed); + _map_table.resize(_num_row); + for (int i = 0; i < _num_row; i++) + _map_table[i].resize(_num_col); + _bit_data.reserve(_num_row * _num_col); +} + +/* extract info from map file + * see XILINX PROGRAMMER QUALIFICATION SPECIFICATION 4.4.1 + */ +int XilinxMapParser::parse() +{ + int col = 0; + std::stringstream ss; + ss.str(_raw_data); + std::string line; + + while(std::getline(ss, line, '\n')) { + bool empty = true; // to said if line contains only '\t' (empty) + // or one or more blank in a non empty line + size_t prev_pos = 0, next_pos; + int row = 0; + /* suppress potential '\r' (thanks windows) */ + if (line.back() == '\r') + line.pop_back(); + + do { + int map_val = 0; + next_pos = line.find_first_of('\t', prev_pos); + size_t end_pos = (next_pos == std::string::npos) ? line.size():next_pos; + if (end_pos == prev_pos) { /* blank (transfer) bit ('\t') */ + if (empty) // no data before + map_val = BIT_ZERO; + else // blank into non empty line + map_val = BIT_ONE; + } else { + empty = false; // current line is not fully empty + int len = end_pos - prev_pos; // section len + string cnt = line.substr(prev_pos, len); // line substring + if (cnt[0] <= '9' && cnt[0] >= '0') { // numeric value (index) + map_val = std::stoi(cnt, nullptr, 10); + } else { // information (done, spare, user, ...) + if (!strncmp(cnt.c_str(), "spare", 5)) { + map_val = BIT_ONE; + } else if (!strncmp(cnt.c_str(), "sec_", 4)) { + map_val = BIT_ONE; + /* done is known don't wait for fuse array construct */ + } else if (!strncmp(cnt.c_str(), "done", 4)) { + map_val = (cnt[5] == '0') ? BIT_ONE: BIT_ZERO; + /* fill usercode now instead of waiting for fuse placement */ + } else if (!strncmp(cnt.c_str(), "user", 4)) { + int idx = stoi(cnt.substr(5)); + map_val = ((_usercode >> idx) & 0x01) ? BIT_ONE : BIT_ZERO; + } else { + printf("unknown %s %s\n", cnt.c_str(), line.c_str()); + return false; + } + } + } + _map_table[row][col] = map_val; + row++; + prev_pos = next_pos + 1; + } while (next_pos != std::string::npos); + + col++; // update col after parsing each line + } + + return jedApplyMap(); +} + +/* cfg_data build. + * for fuse(x,y) set to 0, 1 or jed value (when map_data contains offset) + * usercode and done bits are set to 0 or 1 at parse step + */ +bool XilinxMapParser::jedApplyMap() +{ + std::string listfuse = _jed->get_fuselist(); + std::string tmp; + int row = 0; + + tmp.reserve(_map_table[0].size()); + _cfg_data.clear(); + _cfg_data.resize(_map_table.size()); + + for (auto & fuse_row : _map_table) { + tmp.clear(); + for (auto &map_bit : fuse_row) { + int32_t map_val = map_bit; + uint8_t bit_val; + switch (map_val) { + case BIT_ZERO: + bit_val = 0; + break; + case BIT_ONE: + bit_val = 1; + break; + default: // map_val is an offset: get bit value from jed + bit_val = listfuse[map_val] - '0'; + } + tmp += bit_val; + _bit_length++; + } + /* insert each row in back order (last bit 1st to send) */ + std::copy(tmp.rbegin(), tmp.rend(), std::back_inserter(_cfg_data[row])); + row++; + } + + return true; +} diff --git a/src/xilinxMapParser.hpp b/src/xilinxMapParser.hpp new file mode 100644 index 0000000..749084a --- /dev/null +++ b/src/xilinxMapParser.hpp @@ -0,0 +1,72 @@ +// SPDX-License-Identifier: Apache-2.0 +/* + * Copyright (C) 2021 Gwenhael Goavec-Merou + */ +#ifndef SRC_XILINXMAPPARSER_HPP_ +#define SRC_XILINXMAPPARSER_HPP_ + +#include + +#include +#include + +#include "configBitstreamParser.hpp" +#include "jedParser.hpp" + +/*! + * \file xilinxMapParser.hpp + * \class XilinxMapParser (based on "XILINX PROGRAMMER QUALIFICATION SPECIFICATION ") + * \brief xilinx map parser. Used to place jed content into flash memory order + * \author Gwenhael Goavec-Merou + */ + +class XilinxMapParser: public ConfigBitstreamParser { + public: + /*! + * \brief XilinxMapParser constructor + * \param[in] filename: absolute map file path + * \param[in] num_row: device x size + * \param[in] num_col: device y size (with usercode and done/sec rows) + * \param[in] jed: input configuration data + * \param[in] usercode: usercode to place in usercode area + * \param[in] verbose: verbose level + */ + XilinxMapParser(const std::string &filename, + const uint16_t num_row, const uint16_t num_col, + const JedParser *jed, + const uint32_t usercode, bool verbose = false); + + /*! + * \brief parse map file to construct _map_table and call jedApplyMap + * \return false for unknown code, true otherwise + */ + int parse() override; + + /*! + * \brief return 2 dimension configuration data reorganized + * according to map file + * \return configuration data array + */ + std::vector cfg_data() { return _cfg_data;} + + private: + /*! + * \brief build _cfg_data by using cfg_data content. + * \return false for unknown code in map, true otherwise + */ + bool jedApplyMap(); + + enum { + BIT_ZERO = -1, /* empty bit with only blank before ie '\t' */ + BIT_ONE = -2, /* empty bit with non blank before */ + }; + + std::vector> _map_table; /**< map array */ + JedParser *_jed; /**< raw jed content */ + uint16_t _num_row; /**< bitstream number of row */ + uint16_t _num_col; /**< bitsteam number of col */ + uint32_t _usercode; /**< usercode to add into corresponding area */ + std::vector _cfg_data; /**< fuse array after built */ +}; + +#endif // SRC_XILINXMAPPARSER_HPP_