Merge pull request #1503 from antmicro/bits2rbt_and_aux
Add bits2rbt and aux data support to bitread
diff --git a/Makefile b/Makefile
index 293407a..b76f07a 100644
--- a/Makefile
+++ b/Makefile
@@ -48,7 +48,7 @@
# ------------------------
TEST_EXCLUDE = $(foreach x,$(ALL_EXCLUDE) docs fuzzers minitests experiments,--ignore $(x))
-test: test-py test-cpp
+test: test-py test-cpp test-tools
@true
test-py:
@@ -61,7 +61,10 @@
cd build && ctest --no-compress-output -T Test -C RelWithDebInfo --output-on-failure
xsltproc .github/kokoro/ctest2junit.xsl build/Testing/*/Test.xml > build/cpp_test_results.xml
-.PHONY: test test-py test-cpp
+test-tools:
+ $(MAKE) -f Makefile.tools_tests
+
+.PHONY: test test-py test-cpp test-tools
# Run HTML test
# ------------------------
@@ -73,7 +76,7 @@
# Auto formatting of code.
# ------------------------
-FORMAT_EXCLUDE = $(foreach x,$(ALL_EXCLUDE),-and -not -path './$(x)/*') -and -not -name *.bit
+FORMAT_EXCLUDE = $(foreach x,$(ALL_EXCLUDE),-and -not -path './$(x)/*') -and -not -name *.bit -and -not -name *.tar.gz
CLANG_FORMAT ?= clang-format-5.0
format-cpp:
@@ -100,7 +103,7 @@
# xargs later) is a file, and not a directory or link. Also filters out .bit
# files as these are the only binary files currently tracked by Git and we don't
# want to inadvertently change these at all.
-WS_FILTER = [ -f {} -a ! -L {} ] && [[ {} != *.bit ]]
+WS_FILTER = [ -f {} -a ! -L {} ] && [[ {} != *.bit ]] && [[ {} != *.tar.gz ]]
# For every file piped to $(WS_FORMAT) apply the filter and perform the command,
# if a file does not match the filter, just returns true.
diff --git a/Makefile.tools_tests b/Makefile.tools_tests
new file mode 100644
index 0000000..6022623
--- /dev/null
+++ b/Makefile.tools_tests
@@ -0,0 +1,45 @@
+SHELL=/bin/bash
+BITREAD = build/tools/bitread
+BITS2RBT = build/tools/bits2rbt/bits2rbt
+TEST_DATA_PATH = lib/test_data
+
+#FIXME Uncomment bits2rbt_bram_xc7 once https://github.com/SymbiFlow/prjxray/issues/1285 is fixed
+BITS2RBT_TESTS = bits2rbt_xc7 #bits2rbt_bram_xc7
+
+TESTS = $(BITS2RBT_TESTS)
+
+bits2rbt_xc7_ARGS_1 = -part_file build/Series7/part.yaml -architecture Series7 -y -o build/xc7.bits -aux build/xc7.aux build/Series7/design.bit
+bits2rbt_xc7_ARGS_2 = -arch Series7 -aux build/xc7.aux -o build/xc7.rbt build/xc7.bits
+bits2rbt_xc7_TEST = diff <(tail -n +8 build/Series7/design.rbt) <(tail -n +8 build/xc7.rbt)
+
+bits2rbt_bram_xc7_ARGS_1 = -part_file build/Series7/part.yaml -architecture Series7 -y -o build/bram_xc7.bits -aux build/bram_xc7.aux build/Series7/bram.bit
+bits2rbt_bram_xc7_ARGS_2 = -arch Series7 -aux build/bram_xc7.aux -o build/bram_xc7.rbt build/bram_xc7.bits
+bits2rbt_bram_xc7_TEST = diff <(tail -n +8 build/Series7/bram.rbt) <(tail -n +8 build/bram_xc7.rbt)
+
+all: $(TESTS)
+
+test_data: $(TEST_DATA_PATH)/ToolsTestData.tar.gz
+ @echo "Unpacking test data"
+ tar -zxf $< -C build
+
+define bitread_test_tpl =
+$(1): $(2) $(3) test_data
+ @echo "Started test $(1)"
+ $(2) $$($(1)_ARGS_1) > /dev/null && $(3) $$($(1)_ARGS_2) > /dev/null && $$($(1)_TEST); \
+ RETVAL=$$$$? ; \
+ if [ $$$$RETVAL -eq 0 ]; then \
+ echo "$(1) PASS"; \
+ true; \
+ else \
+ echo "$(1) FAIL"; \
+ false; \
+ fi
+
+$(1)_clean:
+ rm -rf build/*.frm build/*.aux build/*.bits build/*.rbt
+
+endef
+$(foreach test,$(BITS2RBT_TESTS),$(eval $(call bitread_test_tpl,$(test),$(BITREAD),$(BITS2RBT))))
+
+clean: $(foreach test,$(TESTS),$(test)_clean)
+ @true
diff --git a/lib/include/prjxray/xilinx/bitstream_reader.h b/lib/include/prjxray/xilinx/bitstream_reader.h
index bae16bc..e13447b 100644
--- a/lib/include/prjxray/xilinx/bitstream_reader.h
+++ b/lib/include/prjxray/xilinx/bitstream_reader.h
@@ -68,6 +68,14 @@
static absl::optional<BitstreamReader<ArchType>> InitWithBytes(
T bitstream);
+ // Extract information from bitstream necessary to reconstruct RBT
+ // header and add it to the AUX data
+ template <typename T>
+ static void PrintHeader(T bitstream, FILE* aux_fp);
+
+ // Extract configuration logic data and add to the AUX data
+ void PrintFpgaConfigurationLogicData(FILE* aux_fp);
+
const std::vector<uint32_t>& words() { return words_; };
// Returns an iterator that yields `ConfigurationPackets`
@@ -76,11 +84,59 @@
iterator end();
private:
- static std::array<uint8_t, 4> kSyncWord;
+ static const std::array<uint8_t, 4> kSyncWord;
+ static const std::array<uint32_t, 2> kWcfgCmd;
+ static const std::array<uint32_t, 2> kNullCmd;
std::vector<uint32_t> words_;
};
+// Extract FPGA configuration logic information
+template <typename ArchType>
+void BitstreamReader<ArchType>::PrintFpgaConfigurationLogicData(
+ FILE* aux_fp) {
+ // Get the data before the first FDRI_WRITE command packet
+ const auto fpga_conf_end = std::search(
+ words_.cbegin(), words_.cend(), kWcfgCmd.cbegin(), kWcfgCmd.cend());
+ fprintf(aux_fp, "FPGA configuration logic prefix:");
+ for (auto it = words_.cbegin(); it != fpga_conf_end; ++it) {
+ fprintf(aux_fp, " %08X", *it);
+ }
+ fprintf(aux_fp, "\n");
+
+ // Get the data after the last Null Command packet
+ const auto last_null_cmd = std::find_end(
+ words_.cbegin(), words_.cend(), kNullCmd.cbegin(), kNullCmd.cend());
+ fprintf(aux_fp, "FPGA configuration logic suffix:");
+ for (auto it = last_null_cmd; it != words_.cend(); ++it) {
+ fprintf(aux_fp, " %08X", *it);
+ }
+ fprintf(aux_fp, "\n");
+}
+
+template <typename ArchType>
+template <typename T>
+void BitstreamReader<ArchType>::PrintHeader(T bitstream, FILE* aux_fp) {
+ // If this is really a Xilinx bitstream, there will be a sync
+ // word somewhere toward the beginning.
+ auto sync_pos = std::search(bitstream.begin(), bitstream.end(),
+ kSyncWord.begin(), kSyncWord.end());
+ if (sync_pos == bitstream.end()) {
+ return;
+ }
+ sync_pos += kSyncWord.size();
+ // Wrap the provided container in a span that strips off the preamble.
+ absl::Span<typename T::value_type> bitstream_span(bitstream);
+ auto header_packets =
+ bitstream_span.subspan(0, sync_pos - bitstream.begin());
+
+ fprintf(aux_fp, "Header bytes:");
+ for (auto& word : header_packets) {
+ fprintf(aux_fp, " %02X", word);
+ }
+ fprintf(aux_fp, "\n");
+}
+
template <typename ArchType>
template <typename T>
absl::optional<BitstreamReader<ArchType>>
@@ -111,9 +167,19 @@
// Sync word as specified in UG470 page 81
template <typename ArchType>
-std::array<uint8_t, 4> BitstreamReader<ArchType>::kSyncWord{0xAA, 0x99, 0x55,
+const std::array<uint8_t, 4> BitstreamReader<ArchType>::kSyncWord{0xAA, 0x99, 0x55,
0x66};
+// Writing the WCFG(0x1) command in type 1 packet with 1 word to the CMD register (0x30008001)
+// Refer to UG470 page 110
+template <typename ArchType>
+const std::array<uint32_t, 2> BitstreamReader<ArchType>::kWcfgCmd = {0x30008001, 0x1};
+
+// Writing the NULL(0x0) command in type 1 packet with 1 word to the CMD register (0x30008001)
+// Refer to UG470 page 110
+template <typename ArchType>
+const std::array<uint32_t, 2> BitstreamReader<ArchType>::kNullCmd = {0x30008001, 0x0};
+
template <typename ArchType>
typename BitstreamReader<ArchType>::iterator
BitstreamReader<ArchType>::begin() {
diff --git a/lib/include/prjxray/xilinx/configuration.h b/lib/include/prjxray/xilinx/configuration.h
index 89fad61..0a88354 100644
--- a/lib/include/prjxray/xilinx/configuration.h
+++ b/lib/include/prjxray/xilinx/configuration.h
@@ -68,6 +68,7 @@
const typename ArchType::Part& part() const { return part_; }
const FrameMap& frames() const { return frames_; }
+ void PrintFrameAddresses(FILE* fp);
private:
typename ArchType::Part part_;
@@ -358,6 +359,19 @@
return Configuration(part, frames);
}
+template <typename ArchType>
+void Configuration<ArchType>::PrintFrameAddresses(FILE* fp) {
+ fprintf(fp, "Frame addresses in bitstream: ");
+ for (auto frame = frames_.begin(); frame != frames_.end(); ++frame) {
+ fprintf(fp, "%08X", (int)frame->first);
+ if (std::next(frame) != frames_.end()) {
+ fprintf(fp, " ");
+ } else {
+ fprintf(fp, "\n");
+ }
+ }
+}
+
} // namespace xilinx
} // namespace prjxray
diff --git a/lib/test_data/ToolsTestData.tar.gz b/lib/test_data/ToolsTestData.tar.gz
new file mode 100644
index 0000000..36c5b60
--- /dev/null
+++ b/lib/test_data/ToolsTestData.tar.gz
Binary files differ
diff --git a/tools/CMakeLists.txt b/tools/CMakeLists.txt
index 6a166eb..30650fd 100644
--- a/tools/CMakeLists.txt
+++ b/tools/CMakeLists.txt
@@ -34,4 +34,6 @@
libprjxray
)
+add_subdirectory(bits2rbt)
+
install(TARGETS xc7frames2bit xc7patch gen_part_base_yaml frame_address_decoder bittool segmatch bitread DESTINATION bin)
diff --git a/tools/bitread.cc b/tools/bitread.cc
index f5aa7e0..796b8c7 100644
--- a/tools/bitread.cc
+++ b/tools/bitread.cc
@@ -54,6 +54,10 @@
DEFINE_string(architecture,
"Series7",
"Architecture of the provided bitstream");
+DEFINE_string(
+ aux,
+ "",
+ "write machine-readable output file with auxiliary bitstream data");
namespace xilinx = prjxray::xilinx;
@@ -115,6 +119,25 @@
fprintf(f, "\n");
}
+ if (!FLAGS_aux.empty()) {
+ FILE* aux_file = fopen(FLAGS_aux.c_str(), "w");
+ if (aux_file == nullptr) {
+ printf(
+ "Can't open aux output file '%s' for "
+ "writing!\n",
+ FLAGS_aux.c_str());
+ return 1;
+ }
+ // Extract and decode header information as in RBT file
+ xilinx::BitstreamReader<ArchType>::PrintHeader(
+ bytes_, aux_file);
+ // Extract FPGA configuration logic information
+ reader->PrintFpgaConfigurationLogicData(aux_file);
+ // Extract configuration frames' addresses
+ config->PrintFrameAddresses(aux_file);
+ fclose(aux_file);
+ }
+
std::vector<std::vector<bool>> pgmdata;
std::vector<int> pgmsep;
diff --git a/tools/bits2rbt/CMakeLists.txt b/tools/bits2rbt/CMakeLists.txt
new file mode 100644
index 0000000..206fbbc
--- /dev/null
+++ b/tools/bits2rbt/CMakeLists.txt
@@ -0,0 +1,5 @@
+add_executable(bits2rbt bits2rbt.cc
+ configuration_packets.cc
+ header.cc
+ ecc.cc)
+target_link_libraries(bits2rbt absl::strings gflags)
diff --git a/tools/bits2rbt/bits2rbt.cc b/tools/bits2rbt/bits2rbt.cc
new file mode 100644
index 0000000..e08fa23
--- /dev/null
+++ b/tools/bits2rbt/bits2rbt.cc
@@ -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;
+}
diff --git a/tools/bits2rbt/configuration_packets.cc b/tools/bits2rbt/configuration_packets.cc
new file mode 100644
index 0000000..6dfa871
--- /dev/null
+++ b/tools/bits2rbt/configuration_packets.cc
@@ -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));
+}
diff --git a/tools/bits2rbt/configuration_packets.h b/tools/bits2rbt/configuration_packets.h
new file mode 100644
index 0000000..61f45f6
--- /dev/null
+++ b/tools/bits2rbt/configuration_packets.h
@@ -0,0 +1,65 @@
+#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>>;
+ // Refer to UG470 page 109 for address of configuration registers and commands
+ 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;
+ // Writing the RCRC(0x7) command in type 1 packet with 1 word to the CMD register (0x30008001)
+ const std::array<uint32_t, 2> 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_
diff --git a/tools/bits2rbt/crc.h b/tools/bits2rbt/crc.h
new file mode 100644
index 0000000..04af724
--- /dev/null
+++ b/tools/bits2rbt/crc.h
@@ -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;
+}
diff --git a/tools/bits2rbt/ecc.cc b/tools/bits2rbt/ecc.cc
new file mode 100644
index 0000000..7fe865d
--- /dev/null
+++ b/tools/bits2rbt/ecc.cc
@@ -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;
+ }
+}
diff --git a/tools/bits2rbt/ecc.h b/tools/bits2rbt/ecc.h
new file mode 100644
index 0000000..aca3577
--- /dev/null
+++ b/tools/bits2rbt/ecc.h
@@ -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_
diff --git a/tools/bits2rbt/header.cc b/tools/bits2rbt/header.cc
new file mode 100644
index 0000000..b3c1456
--- /dev/null
+++ b/tools/bits2rbt/header.cc
@@ -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);
+}
diff --git a/tools/bits2rbt/header.h b/tools/bits2rbt/header.h
new file mode 100644
index 0000000..3c42fb4
--- /dev/null
+++ b/tools/bits2rbt/header.h
@@ -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_