mirror of https://github.com/openXC7/prjxray.git
Merge pull request #1503 from antmicro/bits2rbt_and_aux
Add bits2rbt and aux data support to bitread
This commit is contained in:
commit
714f4d9e66
11
Makefile
11
Makefile
|
|
@ -48,7 +48,7 @@ install:
|
||||||
# ------------------------
|
# ------------------------
|
||||||
TEST_EXCLUDE = $(foreach x,$(ALL_EXCLUDE) docs fuzzers minitests experiments,--ignore $(x))
|
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
|
@true
|
||||||
|
|
||||||
test-py:
|
test-py:
|
||||||
|
|
@ -61,7 +61,10 @@ test-cpp:
|
||||||
cd build && ctest --no-compress-output -T Test -C RelWithDebInfo --output-on-failure
|
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
|
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
|
# Run HTML test
|
||||||
# ------------------------
|
# ------------------------
|
||||||
|
|
@ -73,7 +76,7 @@ test-htmlgen:
|
||||||
|
|
||||||
# Auto formatting of code.
|
# 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
|
CLANG_FORMAT ?= clang-format-5.0
|
||||||
format-cpp:
|
format-cpp:
|
||||||
|
|
@ -100,7 +103,7 @@ WS_CMD = sed -i '\''s@\s\+$$@@g'\''
|
||||||
# xargs later) is a file, and not a directory or link. Also filters out .bit
|
# 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
|
# files as these are the only binary files currently tracked by Git and we don't
|
||||||
# want to inadvertently change these at all.
|
# 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,
|
# 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.
|
# if a file does not match the filter, just returns true.
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
@ -68,6 +68,14 @@ class BitstreamReader {
|
||||||
static absl::optional<BitstreamReader<ArchType>> InitWithBytes(
|
static absl::optional<BitstreamReader<ArchType>> InitWithBytes(
|
||||||
T bitstream);
|
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_; };
|
const std::vector<uint32_t>& words() { return words_; };
|
||||||
|
|
||||||
// Returns an iterator that yields `ConfigurationPackets`
|
// Returns an iterator that yields `ConfigurationPackets`
|
||||||
|
|
@ -76,11 +84,59 @@ class BitstreamReader {
|
||||||
iterator end();
|
iterator end();
|
||||||
|
|
||||||
private:
|
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_;
|
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 ArchType>
|
||||||
template <typename T>
|
template <typename T>
|
||||||
absl::optional<BitstreamReader<ArchType>>
|
absl::optional<BitstreamReader<ArchType>>
|
||||||
|
|
@ -111,9 +167,19 @@ BitstreamReader<ArchType>::InitWithBytes(T bitstream) {
|
||||||
|
|
||||||
// Sync word as specified in UG470 page 81
|
// Sync word as specified in UG470 page 81
|
||||||
template <typename ArchType>
|
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};
|
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>
|
template <typename ArchType>
|
||||||
typename BitstreamReader<ArchType>::iterator
|
typename BitstreamReader<ArchType>::iterator
|
||||||
BitstreamReader<ArchType>::begin() {
|
BitstreamReader<ArchType>::begin() {
|
||||||
|
|
|
||||||
|
|
@ -68,6 +68,7 @@ class Configuration {
|
||||||
|
|
||||||
const typename ArchType::Part& part() const { return part_; }
|
const typename ArchType::Part& part() const { return part_; }
|
||||||
const FrameMap& frames() const { return frames_; }
|
const FrameMap& frames() const { return frames_; }
|
||||||
|
void PrintFrameAddresses(FILE* fp);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
typename ArchType::Part part_;
|
typename ArchType::Part part_;
|
||||||
|
|
@ -358,6 +359,19 @@ Configuration<ArchType>::InitWithPackets(const typename ArchType::Part& part,
|
||||||
return Configuration(part, frames);
|
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 xilinx
|
||||||
} // namespace prjxray
|
} // namespace prjxray
|
||||||
|
|
||||||
|
|
|
||||||
Binary file not shown.
|
|
@ -34,4 +34,6 @@ target_link_libraries(xc7frames2bit
|
||||||
libprjxray
|
libprjxray
|
||||||
)
|
)
|
||||||
|
|
||||||
|
add_subdirectory(bits2rbt)
|
||||||
|
|
||||||
install(TARGETS xc7frames2bit xc7patch gen_part_base_yaml frame_address_decoder bittool segmatch bitread DESTINATION bin)
|
install(TARGETS xc7frames2bit xc7patch gen_part_base_yaml frame_address_decoder bittool segmatch bitread DESTINATION bin)
|
||||||
|
|
|
||||||
|
|
@ -54,6 +54,10 @@ DEFINE_string(part_file, "", "YAML file describing a Xilinx part");
|
||||||
DEFINE_string(architecture,
|
DEFINE_string(architecture,
|
||||||
"Series7",
|
"Series7",
|
||||||
"Architecture of the provided bitstream");
|
"Architecture of the provided bitstream");
|
||||||
|
DEFINE_string(
|
||||||
|
aux,
|
||||||
|
"",
|
||||||
|
"write machine-readable output file with auxiliary bitstream data");
|
||||||
|
|
||||||
namespace xilinx = prjxray::xilinx;
|
namespace xilinx = prjxray::xilinx;
|
||||||
|
|
||||||
|
|
@ -115,6 +119,25 @@ struct BitReader {
|
||||||
fprintf(f, "\n");
|
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<std::vector<bool>> pgmdata;
|
||||||
std::vector<int> pgmsep;
|
std::vector<int> pgmsep;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,5 @@
|
||||||
|
add_executable(bits2rbt bits2rbt.cc
|
||||||
|
configuration_packets.cc
|
||||||
|
header.cc
|
||||||
|
ecc.cc)
|
||||||
|
target_link_libraries(bits2rbt absl::strings gflags)
|
||||||
|
|
@ -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;
|
||||||
|
}
|
||||||
|
|
@ -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));
|
||||||
|
}
|
||||||
|
|
@ -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_
|
||||||
|
|
@ -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;
|
||||||
|
}
|
||||||
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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_
|
||||||
|
|
@ -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);
|
||||||
|
}
|
||||||
|
|
@ -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_
|
||||||
Loading…
Reference in New Issue