Add bits2rbt tool

Signed-off-by: Tomasz Michalak <tmichalak@antmicro.com>
This commit is contained in:
Tomasz Michalak 2020-11-18 15:01:10 +01:00
parent 489e438627
commit 5b7d782737
10 changed files with 645 additions and 0 deletions

View File

@ -34,4 +34,6 @@ target_link_libraries(xc7frames2bit
libprjxray
)
add_subdirectory(bits2rbt)
install(TARGETS xc7frames2bit xc7patch gen_part_base_yaml frame_address_decoder bittool segmatch bitread DESTINATION bin)

View File

@ -0,0 +1,5 @@
add_executable(bits2rbt bits2rbt.cc
configuration_packets.cc
header.cc
ecc.cc)
target_link_libraries(bits2rbt absl::strings gflags)

View File

@ -0,0 +1,44 @@
#include <gflags/gflags.h>
#include <fstream>
#include <iostream>
#include "configuration_packets.h"
DEFINE_string(o, "", "Output RBT file");
DEFINE_string(aux, "", "Input auxiliary data file");
DEFINE_string(arch, "Series7", "FPGA architecture");
int main(int argc, char** argv) {
gflags::ParseCommandLineFlags(&argc, &argv, true);
if (argc != 2) {
std::cerr << "Error: Input bits file not specified"
<< std::endl;
return 1;
}
std::ofstream out_file;
if (!FLAGS_o.empty()) {
out_file.open(FLAGS_o.c_str(), std::ofstream::out);
if (!out_file.good()) {
std::cerr << "Error: Can't open output file\n";
return 1;
}
}
std::ostream& out = (FLAGS_o.empty()) ? std::cout : out_file;
try {
std::shared_ptr<ConfigurationPackets> packets =
ConfigurationPackets::InitFromFile(argv[1], FLAGS_arch);
packets->AddAuxData(FLAGS_aux);
packets->WriteBits(out);
} catch (std::runtime_error& e) {
std::cerr << "Error: " << e.what();
return 1;
}
if (!FLAGS_o.empty()) {
out_file.close();
}
return 0;
}

View File

@ -0,0 +1,241 @@
#include <absl/strings/str_split.h>
#include <algorithm>
#include <bitset>
#include <fstream>
#include <iomanip>
#include <iostream>
#include <stdexcept>
#include <string>
#include "configuration_packets.h"
#include "crc.h"
#include "ecc.h"
#include "header.h"
ConfigurationPackets::ConfigurationPackets(const std::string& arch)
: words_per_frame_(words_in_architecture.at(arch)),
architecture_(arch),
ecc_(arch, words_per_frame_) {}
const std::unordered_map<std::string, size_t>
ConfigurationPackets::words_in_architecture = {{"Series7", 101},
{"UltraScale", 123},
{"UltraScalePlus", 93}};
std::shared_ptr<ConfigurationPackets> ConfigurationPackets::InitFromFile(
const std::string& file,
const std::string& arch) {
std::ifstream ifs(file, std::ifstream::in);
if (!ifs.good()) {
throw std::runtime_error("Couldn't open bits file\n");
}
if (words_in_architecture.find(arch) == words_in_architecture.end()) {
throw std::runtime_error("Error: Unrecognized architecture " +
arch + "\n");
}
std::shared_ptr<ConfigurationPackets> packets =
std::make_shared<ConfigurationPackets>(arch);
packets->SetBits(ifs);
packets->UpdateECCs();
return packets;
}
void ConfigurationPackets::SetBits(std::ifstream& ifs) {
while (!ifs.eof()) {
std::string line;
getline(ifs, line);
SetBit(line);
}
}
void ConfigurationPackets::WriteBits(std::ostream& out) {
header_->Write(out);
WriteFpgaConfiguration(out, fpga_configuration_head_);
WriteConfiguration(out);
WriteFpgaConfiguration(out, fpga_configuration_tail_);
}
void ConfigurationPackets::WriteFpgaConfiguration(
std::ostream& out,
const std::vector<uint32_t>& vect) {
for (auto& word : vect) {
out << std::bitset<32>(word) << std::endl;
}
}
void ConfigurationPackets::WriteConfiguration(std::ostream& out) {
for (auto packet = configuration_data_packets_.cbegin();
packet != configuration_data_packets_.cend(); ++packet) {
WritePacket(out, packet);
if (IsDifferentRow(packet, std::next(packet))) {
// Write the zero frame at the end of each row
WritePacket(out, packet);
}
}
}
void ConfigurationPackets::WritePacket(
std::ostream& out,
ConfigurationFrames::const_iterator frame_citr) const {
bool is_new_row(false);
if ((frame_citr == configuration_data_packets_.cbegin()) ||
IsDifferentRow(frame_citr, std::prev(frame_citr))) {
// Write the WCFG command followed by a FAR write with address
// of the next frame WCFG command write
out << std::bitset<32>(kCmdWrite | 0x1) << std::endl;
out << std::bitset<32>(0x1) << std::endl;
out << std::bitset<32>(kNop) << std::endl;
// FAR Write of the next frame followed by frame address
out << std::bitset<32>(kFarWrite | 0x1) << std::endl;
out << std::bitset<32>(frame_citr->first) << std::endl;
if (architecture_ == "Series7") {
out << std::bitset<32>(kNop) << std::endl;
}
is_new_row = true;
}
uint32_t crc(GetCRC(frame_citr, is_new_row));
// FDRI Write
out << std::bitset<32>(kFdriWrite | words_per_frame_) << std::endl;
// Declared number of configuration words
for (auto& word : frame_citr->second) {
out << std::bitset<32>(word) << std::endl;
}
// FAR Write followed by frame address
out << std::bitset<32>(kFarWrite | 0x1) << std::endl;
out << std::bitset<32>(frame_citr->first) << std::endl;
// CRC Write followed by packet CRC
out << std::bitset<32>(kCrcWrite | 0x1) << std::endl;
out << std::bitset<32>(crc) << std::endl;
}
void ConfigurationPackets::SetBit(const std::string& line) {
if (line.empty()) {
return;
}
uint32_t frame_address, word, bit;
sscanf(line.c_str(), "bit_%08x_%03u_%02u", &frame_address, &word, &bit);
if (configuration_data_packets_.find(frame_address) ==
configuration_data_packets_.end()) {
configuration_data_packets_[frame_address] =
std::vector<uint32_t>(words_per_frame_, 0x0);
}
configuration_data_packets_[frame_address].at(word) |= (1 << bit);
}
void ConfigurationPackets::Line2Vector(const std::string& line,
std::vector<uint32_t>& vect) {
static std::function<uint32_t(const std::string& str)> str_to_uint =
[](const std::string& str) -> uint32_t {
assert(!str.empty());
return std::stoul(str, nullptr, 16);
};
// Skip the line content description before the conversion
std::vector<std::string> str_vector =
absl::StrSplit(line.substr(line.find(":") + 2), " ");
std::transform(str_vector.begin(), str_vector.end(),
std::back_inserter(vect), str_to_uint);
}
void ConfigurationPackets::AddAuxData(const std::string& file) {
std::ifstream ifs(file, std::ifstream::in);
if (!ifs.good()) {
throw std::runtime_error("Couldn't open auxiliary data file\n");
}
std::string line;
getline(ifs, line);
InitializeHeader(line);
getline(ifs, line);
InitializeFpgaConfigurationHead(line);
getline(ifs, line);
InitializeFpgaConfigurationTail(line);
getline(ifs, line);
InitializeConfigurationData(line);
}
uint32_t ConfigurationPackets::GetFpgaConfigurationCRC() const {
uint32_t crc(0);
auto far = std::search(fpga_configuration_head_.begin(),
fpga_configuration_head_.end(), kRCrcCmd.begin(),
kRCrcCmd.end());
for (auto itr = far + kRCrcCmd.size();
itr != fpga_configuration_head_.end(); ++itr) {
if (*itr == kNop)
continue;
// Check if it is a write packet
assert(*itr & 0x30000000);
// Get the packet address
uint32_t addr = (*itr >> 13) & 0x1F;
std::advance(itr, 1);
assert(itr != fpga_configuration_head_.end());
crc = icap_crc(addr, *itr, crc);
}
return crc;
}
uint32_t ConfigurationPackets::GetCRC(
ConfigurationFrames::const_iterator frame_citr,
bool is_new_row) const {
uint32_t crc = 0;
if (is_new_row) {
if (frame_citr == configuration_data_packets_.begin()) {
crc = GetFpgaConfigurationCRC();
}
crc = icap_crc(kCmdReg, kWcfgCmd, crc);
crc = icap_crc(kFarReg, frame_citr->first, crc);
}
for (const auto& word : frame_citr->second) {
crc = icap_crc(kFdriReg, word, crc);
}
crc = icap_crc(kFarReg, frame_citr->first, crc);
return crc;
}
void ConfigurationPackets::UpdateECCs() {
for (auto& packet : configuration_data_packets_) {
auto& data = packet.second;
ecc_.UpdateFrameECC(data);
}
}
void ConfigurationPackets::InitializeConfigurationData(
const std::string& line) {
std::function<void(const uint32_t&)> update_packets =
[this](const uint32_t& addr) {
if (configuration_data_packets_.find(addr) ==
configuration_data_packets_.end()) {
configuration_data_packets_[addr] =
std::vector<uint32_t>(words_per_frame_, 0x0);
}
};
std::vector<uint32_t> frames_addr;
Line2Vector(line, frames_addr);
std::for_each(frames_addr.begin(), frames_addr.end(), update_packets);
}
void ConfigurationPackets::InitializeFpgaConfigurationHead(
const std::string& line) {
Line2Vector(line, fpga_configuration_head_);
}
void ConfigurationPackets::InitializeFpgaConfigurationTail(
const std::string& line) {
Line2Vector(line, fpga_configuration_tail_);
}
void ConfigurationPackets::InitializeHeader(const std::string& line) {
// FIXME Remove the configuration data head part once the aux data is
// fixed
header_ = std::make_unique<Header>(line, fpga_configuration_head_);
}
bool ConfigurationPackets::IsDifferentRow(
ConfigurationFrames::const_iterator frame_citr1,
ConfigurationFrames::const_iterator frame_citr2) const {
auto get_row = [this](const uint32_t& address) {
size_t row_shift =
(architecture_ == "UltraScalePlus") ? 18 : 17;
return (address >> row_shift) & 0x3FF;
};
return (get_row(frame_citr1->first) != get_row(frame_citr2->first));
}

View File

@ -0,0 +1,63 @@
#ifndef CONFIGURATION_PACKETS_H_
#define CONFIGURATION_PACKETS_H_
#include <array>
#include <map>
#include <memory>
#include <unordered_map>
#include "ecc.h"
#include "header.h"
class ConfigurationPackets {
public:
ConfigurationPackets(const std::string& arch);
static const std::unordered_map<std::string, size_t>
words_in_architecture;
static std::shared_ptr<ConfigurationPackets> InitFromFile(
const std::string& file,
const std::string& arch);
void AddAuxData(const std::string& file);
void WriteBits(std::ostream& out);
void SetBits(std::ifstream& ifs);
private:
using ConfigurationFrame = std::pair<uint32_t, std::vector<uint32_t>>;
using ConfigurationFrames = std::map<uint32_t, std::vector<uint32_t>>;
const uint32_t kCmdWrite = 0x30008000;
const uint32_t kFdriWrite = 0x30004000;
const uint32_t kFarWrite = 0x30002000;
const uint32_t kCrcWrite = 0x30000000;
const uint32_t kNop = 0x20000000;
const uint32_t kFarReg = 0x1;
const uint32_t kFdriReg = 0x2;
const uint32_t kCmdReg = 0x4;
const uint32_t kWcfgCmd = 0x1;
const std::vector<uint32_t> kRCrcCmd = {0x30008001, 0x7};
size_t words_per_frame_;
std::string architecture_;
ConfigurationFrames configuration_data_packets_;
std::vector<uint32_t> fpga_configuration_head_;
std::vector<uint32_t> fpga_configuration_tail_;
std::unique_ptr<Header> header_;
ECC ecc_;
void InitializeConfigurationData(const std::string& line);
void InitializeFpgaConfigurationHead(const std::string& line);
void InitializeFpgaConfigurationTail(const std::string& line);
void InitializeHeader(const std::string& line);
uint32_t GetCRC(ConfigurationFrames::const_iterator frame_citr,
bool is_new_row = false) const;
uint32_t GetFpgaConfigurationCRC() const;
void UpdateECCs();
void SetBit(const std::string& line);
void WriteConfiguration(std::ostream& out);
void WriteFpgaConfiguration(std::ostream& out,
const std::vector<uint32_t>& vect);
void WritePacket(std::ostream& out,
ConfigurationFrames::const_iterator frame_citr) const;
bool IsDifferentRow(
ConfigurationFrames::const_iterator frame_citr1,
ConfigurationFrames::const_iterator frame_citr2) const;
void Line2Vector(const std::string& line, std::vector<uint32_t>& vect);
};
#endif // CONFIGURATION_PACKETS_H_

18
tools/bits2rbt/crc.h Normal file
View File

@ -0,0 +1,18 @@
uint32_t icap_crc(uint32_t addr, uint32_t data, uint32_t prev) {
int kAddressBitWidth = 5;
constexpr int kDataBitWidth = 32;
constexpr uint32_t kCrc32CastagnoliPolynomial = 0x82F63B78;
uint64_t poly = static_cast<uint64_t>(kCrc32CastagnoliPolynomial) << 1;
uint64_t val = (static_cast<uint64_t>(addr) << 32) | data;
uint64_t crc = prev;
for (int i = 0; i < kAddressBitWidth + kDataBitWidth; i++) {
if ((val & 1) != (crc & 1))
crc ^= poly;
val >>= 1;
crc >>= 1;
}
return crc;
}

107
tools/bits2rbt/ecc.cc Normal file
View File

@ -0,0 +1,107 @@
#include "ecc.h"
#include <cassert>
#include <iostream>
const std::unordered_map<std::string, size_t> ECC::ecc_word_per_architecture = {
{"Series7", 50},
{"UltraScale", 60},
{"UltraScalePlus", 45}};
uint32_t ECC::GetSeries7WordEcc(uint32_t idx,
uint32_t data,
uint32_t ecc) const {
uint32_t val = idx * 32; // bit offset
if (idx > 0x25) // avoid 0x800
val += 0x1360;
else if (idx > 0x6) // avoid 0x400
val += 0x1340;
else // avoid lower
val += 0x1320;
if (idx == 0x32) // mask ECC
data &= 0xFFFFE000;
for (int i = 0; i < 32; i++) {
if (data & 1)
ecc ^= val + i;
data >>= 1;
}
if (idx == 0x64) { // last index
uint32_t v = ecc & 0xFFF;
v ^= v >> 8;
v ^= v >> 4;
v ^= v >> 2;
v ^= v >> 1;
ecc ^= (v & 1) << 12; // parity
}
return ecc;
}
uint64_t ECC::GetUSEccFrameOffset(int word, int bit) const {
int nib = bit / 4;
int nibbit = bit % 4;
// ECC offset is expanded to 1 bit per nibble,
// and then shifted based on the bit index in nibble
// e.g. word 3, bit 9
// offset: 0b10100110010 - concatenate (3 + (255 - last_frame_index(e.g.
// 92 for US+)) [frame offset] and 9/4 [nibble offset] becomes:
// 0x10100110010 shifted by bit in nibble (9%4): 0x20200220020
uint32_t offset = (word + (255 - (frame_words_ - 1))) << 3 | nib;
uint64_t exp_offset = 0;
// Odd parity
offset ^= (1 << 11);
for (int i = 0; i < 11; i++)
if (offset & (1 << i))
offset ^= (1 << 11);
// Expansion
for (int i = 0; i < 12; i++)
if (offset & (1 << i))
exp_offset |= (1ULL << (4 * i));
return exp_offset << nibbit;
};
uint64_t ECC::GetUSWordEcc(uint32_t idx, uint32_t data, uint64_t ecc) const {
if (idx == ecc_word_) {
data = 0x0;
}
if (idx == ecc_word_ + 1) {
data &= 0xffff0000;
}
for (int i = 0; i < 32; i++) {
if (data & 1) {
ecc ^= GetUSEccFrameOffset(idx, i);
}
data >>= 1;
}
return ecc;
}
uint64_t ECC::CalculateECC(const std::vector<uint32_t>& data) const {
uint64_t ecc = 0;
for (size_t w = 0; w < data.size(); ++w) {
const uint32_t& word = data.at(w);
if (architecture_ == "Series7") {
ecc = GetSeries7WordEcc(w, word, ecc);
} else {
ecc = GetUSWordEcc(w, word, ecc);
}
}
return ecc;
}
void ECC::UpdateFrameECC(std::vector<uint32_t>& data) const {
assert(data.size() >= ecc_word_);
uint64_t ecc(CalculateECC(data));
if (architecture_ == "Series7") {
data[ecc_word_] &= 0xffffe000;
data[ecc_word_] |= ecc & 0x1fff;
} else {
data[ecc_word_] = ecc;
data[ecc_word_ + 1] &= 0xffff0000;
data[ecc_word_ + 1] |= (ecc >> 32) & 0xffff;
}
}

33
tools/bits2rbt/ecc.h Normal file
View File

@ -0,0 +1,33 @@
#ifndef ECC_H_
#define ECC_H_
#include <cstdint>
#include <string>
#include <unordered_map>
#include <vector>
class ECC {
public:
ECC(const std::string& architecture, size_t words_per_frame)
: architecture_(architecture),
frame_words_(words_per_frame),
ecc_word_(ecc_word_per_architecture.at(architecture)) {}
void UpdateFrameECC(std::vector<uint32_t>& data) const;
private:
const std::string architecture_;
const size_t frame_words_;
const size_t ecc_word_;
static const std::unordered_map<std::string, size_t>
ecc_word_per_architecture;
uint64_t CalculateECC(const std::vector<uint32_t>& words) const;
uint64_t GetUSEccFrameOffset(int word, int bit) const;
uint32_t GetSeries7WordEcc(uint32_t idx,
uint32_t data,
uint32_t ecc) const;
uint64_t GetUSWordEcc(uint32_t idx, uint32_t data, uint64_t ecc) const;
};
#endif // ECC_H_

105
tools/bits2rbt/header.cc Normal file
View File

@ -0,0 +1,105 @@
#include "header.h"
#include <algorithm>
#include <ctime>
#include <sstream>
Header::Header(const std::string& line,
std::vector<uint32_t>& fpga_config_packets) {
absl::string_view header_str(line);
// Go to tag 'a' of the TLV formatted header
header_str.remove_prefix(header_str.find("61"));
bool tlv_header_end = false;
while (!tlv_header_end) {
char tag = char(GetByteAndAdvance(header_str));
switch (tag) {
case 'a':
design_name_ = GetTLVHeaderValue(header_str);
break;
case 'b':
part_ = GetTLVHeaderValue(header_str);
break;
case 'c':
date_ = GetTLVHeaderValue(header_str);
break;
case 'd':
date_ += " " + GetTLVHeaderValue(header_str);
break;
case 'e':
// Get number of bytes in bitstream and multiply
// by 8 to obtain number of bits
no_bits_ = GetWord(header_str) * 8;
tlv_header_end = true;
break;
default:
assert(false);
}
}
while (!header_str.empty()) {
fpga_config_packets.emplace_back(GetWord(header_str));
}
}
std::string Header::GetDate() {
int year, month, day, hour, min, sec;
std::replace_if(date_.begin(), date_.end(),
[](char c) { return c == '/' or c == ':'; }, ' ');
std::istringstream(date_) >> year >> month >> day >> hour >> min >> sec;
std::tm time_raw = {sec, min, hour, day, month - 1, year - 1900};
time_t time = mktime(&time_raw);
const std::tm* time_out = std::localtime(&time);
return std::string(std::asctime(time_out));
}
std::string Header::GetArchitecture() {
if (part_.find("xczu") != std::string::npos) {
return "zynquplus";
}
if (part_.find("7a") != std::string::npos) {
return "artix7";
}
if (part_.find("xcku") != std::string::npos) {
return "kintexu";
}
return "Unknown architecture";
}
void Header::Write(std::ostream& out) {
out << "Xilinx ASCII Bitstream" << std::endl;
out << "Created by" << std::endl;
out << "Design name: " << design_name_ << std::endl;
out << "Architecture: " << GetArchitecture() << std::endl;
out << "Part: " << part_ << std::endl;
out << "Date: " << GetDate();
out << "Bits: " << no_bits_ << std::endl;
}
size_t Header::GetByteAndAdvance(absl::string_view& str_view) {
size_t space_pos(str_view.find(" "));
size_t byte =
std::stoul(std::string(str_view.substr(0, space_pos)), nullptr, 16);
str_view.remove_prefix((space_pos != absl::string_view::npos)
? space_pos + 1
: str_view.size());
return byte;
}
size_t Header::GetTLVHeaderLength(absl::string_view& str_view) {
return (GetByteAndAdvance(str_view) << 8) | GetByteAndAdvance(str_view);
}
std::string Header::GetTLVHeaderValue(absl::string_view& str_view) {
size_t length(GetTLVHeaderLength(str_view));
std::string value;
for (size_t i = 0; i < length; i++) {
value += char(GetByteAndAdvance(str_view));
}
// Lose trailing 0x00
value.pop_back();
return value;
}
uint32_t Header::GetWord(absl::string_view& str_view) {
return (GetByteAndAdvance(str_view) << 24) |
(GetByteAndAdvance(str_view) << 16) |
(GetByteAndAdvance(str_view) << 8) | GetByteAndAdvance(str_view);
}

27
tools/bits2rbt/header.h Normal file
View File

@ -0,0 +1,27 @@
#ifndef HEADER_H_
#define HEADER_H_
#include <absl/strings/string_view.h>
#include <iostream>
#include <string>
#include <vector>
class Header {
public:
Header(const std::string& line,
std::vector<uint32_t>& fpga_config_packets);
void Write(std::ostream& out);
private:
std::string design_name_;
std::string part_;
std::string date_;
uint32_t no_bits_;
std::string GetTLVHeaderValue(absl::string_view& str_view);
uint32_t GetWord(absl::string_view& str_view);
size_t GetTLVHeaderLength(absl::string_view& str_view);
size_t GetByteAndAdvance(absl::string_view& str_view);
std::string GetDate();
std::string GetArchitecture();
};
#endif // HEADER_H_