// 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; }