xilinxMapParser: fuse mapping for xc2c jed
This commit is contained in:
parent
a38d0b2d36
commit
1233459528
|
|
@ -66,7 +66,6 @@ set(OPENFPGALOADER_SOURCE
|
||||||
src/ftdiJtagMPSSE.cpp
|
src/ftdiJtagMPSSE.cpp
|
||||||
src/configBitstreamParser.cpp
|
src/configBitstreamParser.cpp
|
||||||
src/ftdipp_mpsse.cpp
|
src/ftdipp_mpsse.cpp
|
||||||
src/xilinx.cpp
|
|
||||||
src/main.cpp
|
src/main.cpp
|
||||||
src/latticeBitParser.cpp
|
src/latticeBitParser.cpp
|
||||||
src/gowin.cpp
|
src/gowin.cpp
|
||||||
|
|
@ -78,6 +77,8 @@ set(OPENFPGALOADER_SOURCE
|
||||||
src/ftdispi.cpp
|
src/ftdispi.cpp
|
||||||
src/altera.cpp
|
src/altera.cpp
|
||||||
src/bitparser.cpp
|
src/bitparser.cpp
|
||||||
|
src/xilinx.cpp
|
||||||
|
src/xilinxMapParser.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
set(OPENFPGALOADER_HEADERS
|
set(OPENFPGALOADER_HEADERS
|
||||||
|
|
@ -113,7 +114,6 @@ set(OPENFPGALOADER_HEADERS
|
||||||
src/epcq.hpp
|
src/epcq.hpp
|
||||||
src/spiInterface.hpp
|
src/spiInterface.hpp
|
||||||
src/svf_jtag.hpp
|
src/svf_jtag.hpp
|
||||||
src/xilinx.hpp
|
|
||||||
src/configBitstreamParser.hpp
|
src/configBitstreamParser.hpp
|
||||||
src/device.hpp
|
src/device.hpp
|
||||||
src/gowin.hpp
|
src/gowin.hpp
|
||||||
|
|
@ -121,6 +121,8 @@ set(OPENFPGALOADER_HEADERS
|
||||||
src/ftdispi.hpp
|
src/ftdispi.hpp
|
||||||
src/lattice.hpp
|
src/lattice.hpp
|
||||||
src/latticeBitParser.hpp
|
src/latticeBitParser.hpp
|
||||||
|
src/xilinx.hpp
|
||||||
|
src/xilinxMapParser.hpp
|
||||||
)
|
)
|
||||||
|
|
||||||
add_executable(openFPGALoader
|
add_executable(openFPGALoader
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,134 @@
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2021 Gwenhael Goavec-Merou <gwenhael.goavec-merou@trabucayre.com>
|
||||||
|
*/
|
||||||
|
#include <algorithm>
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
#include <cstdio>
|
||||||
|
#include <cstdlib>
|
||||||
|
#include <cstring>
|
||||||
|
#include <sstream>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#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<JedParser *>(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;
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,72 @@
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2021 Gwenhael Goavec-Merou <gwenhael.goavec-merou@trabucayre.com>
|
||||||
|
*/
|
||||||
|
#ifndef SRC_XILINXMAPPARSER_HPP_
|
||||||
|
#define SRC_XILINXMAPPARSER_HPP_
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#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<std::string> 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<std::vector<int>> _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<std::string> _cfg_data; /**< fuse array after built */
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // SRC_XILINXMAPPARSER_HPP_
|
||||||
Loading…
Reference in New Issue