diff --git a/tools/CMakeLists.txt b/tools/CMakeLists.txt index 6a166ebe..30650fd4 100644 --- a/tools/CMakeLists.txt +++ b/tools/CMakeLists.txt @@ -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) diff --git a/tools/bits2rbt/CMakeLists.txt b/tools/bits2rbt/CMakeLists.txt new file mode 100644 index 00000000..206fbbc9 --- /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 00000000..e08fa23e --- /dev/null +++ b/tools/bits2rbt/bits2rbt.cc @@ -0,0 +1,44 @@ +#include +#include +#include + +#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 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 00000000..6dfa8715 --- /dev/null +++ b/tools/bits2rbt/configuration_packets.cc @@ -0,0 +1,241 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +#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 + ConfigurationPackets::words_in_architecture = {{"Series7", 101}, + {"UltraScale", 123}, + {"UltraScalePlus", 93}}; + +std::shared_ptr 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 packets = + std::make_shared(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& 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(words_per_frame_, 0x0); + } + configuration_data_packets_[frame_address].at(word) |= (1 << bit); +} + +void ConfigurationPackets::Line2Vector(const std::string& line, + std::vector& vect) { + static std::function 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 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 update_packets = + [this](const uint32_t& addr) { + if (configuration_data_packets_.find(addr) == + configuration_data_packets_.end()) { + configuration_data_packets_[addr] = + std::vector(words_per_frame_, 0x0); + } + }; + std::vector 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
(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 00000000..af954ac6 --- /dev/null +++ b/tools/bits2rbt/configuration_packets.h @@ -0,0 +1,63 @@ +#ifndef CONFIGURATION_PACKETS_H_ +#define CONFIGURATION_PACKETS_H_ +#include +#include +#include +#include + +#include "ecc.h" +#include "header.h" + +class ConfigurationPackets { + public: + ConfigurationPackets(const std::string& arch); + static const std::unordered_map + words_in_architecture; + static std::shared_ptr 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>; + using ConfigurationFrames = std::map>; + 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 kRCrcCmd = {0x30008001, 0x7}; + size_t words_per_frame_; + std::string architecture_; + ConfigurationFrames configuration_data_packets_; + std::vector fpga_configuration_head_; + std::vector fpga_configuration_tail_; + std::unique_ptr
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& 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& vect); +}; +#endif // CONFIGURATION_PACKETS_H_ diff --git a/tools/bits2rbt/crc.h b/tools/bits2rbt/crc.h new file mode 100644 index 00000000..04af7247 --- /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(kCrc32CastagnoliPolynomial) << 1; + uint64_t val = (static_cast(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 00000000..7fe865da --- /dev/null +++ b/tools/bits2rbt/ecc.cc @@ -0,0 +1,107 @@ +#include "ecc.h" +#include +#include + +const std::unordered_map 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& 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& 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 00000000..aca3577f --- /dev/null +++ b/tools/bits2rbt/ecc.h @@ -0,0 +1,33 @@ +#ifndef ECC_H_ +#define ECC_H_ + +#include +#include +#include +#include + +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& data) const; + + private: + const std::string architecture_; + const size_t frame_words_; + const size_t ecc_word_; + static const std::unordered_map + ecc_word_per_architecture; + + uint64_t CalculateECC(const std::vector& 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 00000000..b3c14567 --- /dev/null +++ b/tools/bits2rbt/header.cc @@ -0,0 +1,105 @@ +#include "header.h" +#include +#include +#include + +Header::Header(const std::string& line, + std::vector& 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 00000000..3c42fb43 --- /dev/null +++ b/tools/bits2rbt/header.h @@ -0,0 +1,27 @@ +#ifndef HEADER_H_ +#define HEADER_H_ +#include +#include +#include +#include + +class Header { + public: + Header(const std::string& line, + std::vector& 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_