From 82a989cfc95204243fe1ab4fc61d668c814e80c4 Mon Sep 17 00:00:00 2001 From: Rick Altherr Date: Mon, 4 Dec 2017 15:49:58 -0800 Subject: [PATCH] lib: Parser for Xilinx bitstream file format Reads the raw bitstream into configuration packets that represent internal register writes. Signed-off-by: Rick Altherr Signed-off-by: Tim 'mithro' Ansell --- lib/CMakeLists.txt | 8 ++ .../prjxray/xilinx_7series_bitstream_reader.h | 87 +++++++++++++++++ lib/xilinx_7series_bitstream_reader.cc | 73 ++++++++++++++ lib/xilinx_7series_bitstream_reader_test.cc | 96 +++++++++++++++++++ 4 files changed, 264 insertions(+) create mode 100644 lib/include/prjxray/xilinx_7series_bitstream_reader.h create mode 100644 lib/xilinx_7series_bitstream_reader.cc create mode 100644 lib/xilinx_7series_bitstream_reader_test.cc diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt index bbbf5bda..b1a4b5b4 100644 --- a/lib/CMakeLists.txt +++ b/lib/CMakeLists.txt @@ -3,6 +3,7 @@ add_library(libprjxray memory_mapped_file.cc segbits_file_reader.cc xilinx_7series_configuration_packet.cc + xilinx_7series_bitstream_reader.cc ) target_include_directories(libprjxray PUBLIC "include") target_link_libraries(libprjxray absl::optional absl::strings absl::span) @@ -30,6 +31,13 @@ if (PRJXRAY_BUILD_TESTING) COMMAND segbits_file_reader_test WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/test_data) + add_executable(xilinx_7series_bitstream_reader_test + xilinx_7series_bitstream_reader_test.cc) + target_link_libraries(xilinx_7series_bitstream_reader_test + libprjxray gtest_main) + add_test(NAME xilinx_7series_bitstream_reader_test + COMMAND xilinx_7series_bitstream_reader_test) + add_executable(xilinx_7series_configuration_packet_test xilinx_7series_configuration_packet_test.cc) target_link_libraries(xilinx_7series_configuration_packet_test diff --git a/lib/include/prjxray/xilinx_7series_bitstream_reader.h b/lib/include/prjxray/xilinx_7series_bitstream_reader.h new file mode 100644 index 00000000..b432febc --- /dev/null +++ b/lib/include/prjxray/xilinx_7series_bitstream_reader.h @@ -0,0 +1,87 @@ +#ifndef PRJXRAY_LIB_XILINX_7SERIES_BITSTREAM_READER +#define PRJXRAY_LIB_XILINX_7SERIES_BITSTREAM_READER + +#include +#include +#include + +#include +#include + +#include +#include + +namespace prjxray { + +class Xilinx7SeriesBitstreamReader { + public: + using value_type = Xilinx7SeriesConfigurationPacket; + + class iterator + : public std::iterator { + public: + iterator& operator++(); + + bool operator==(const iterator &other) const; + bool operator!=(const iterator &other) const; + + const value_type& operator*() const; + const value_type* operator->() const; + + protected: + explicit iterator(absl::Span words); + + private: + friend Xilinx7SeriesBitstreamReader; + + Xilinx7SeriesConfigurationPacket::ParseResult parse_result_; + absl::Span words_; + }; + + // Construct a reader from a collection of 32-bit, big-endian words. + // Assumes that any sync word has already been removed. + Xilinx7SeriesBitstreamReader(std::vector &&words); + + // Construct a `Xilinx7SeriesBitstreamReader` from a Container of bytes. + // Any bytes preceding an initial sync word are ignored. + template + static absl::optional InitWithBytes( + T &bitstream); + + // Returns an iterator that yields `Xilinx7SeriesConfigurationPackets` + // as read from the bitstream. + iterator begin(); + iterator end(); + private: + static std::array kSyncWord; + + std::vector words_; +}; + +template +absl::optional +Xilinx7SeriesBitstreamReader::InitWithBytes(T &bitstream) { + // If this is really a Xilinx 7-Series 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 absl::optional(); + } + sync_pos += kSyncWord.size(); + + // Wrap the provided container in a span that strips off the preamble. + absl::Span bitstream_span(bitstream); + auto config_packets = bitstream_span.subspan(sync_pos - bitstream.begin()); + + // Convert the bytes into 32-bit, big-endian words. + auto big_endian_reader = make_big_endian_span(config_packets); + std::vector words{big_endian_reader.begin(), + big_endian_reader.end()}; + + return Xilinx7SeriesBitstreamReader(std::move(words)); +} + +} // namespace prjxray + +#endif // PRJXRAY_LIB_XILINX_7SERIES_BITSTREAM_READER diff --git a/lib/xilinx_7series_bitstream_reader.cc b/lib/xilinx_7series_bitstream_reader.cc new file mode 100644 index 00000000..d32dacc8 --- /dev/null +++ b/lib/xilinx_7series_bitstream_reader.cc @@ -0,0 +1,73 @@ +#include + +namespace prjxray { + +std::array Xilinx7SeriesBitstreamReader::kSyncWord{ + 0xAA, 0x99, 0x55, 0x66}; + +Xilinx7SeriesBitstreamReader::Xilinx7SeriesBitstreamReader( + std::vector &&words) + : words_(std::move(words)) {} + +Xilinx7SeriesBitstreamReader::iterator Xilinx7SeriesBitstreamReader::begin() { + return iterator(absl::MakeSpan(words_)); +} + +Xilinx7SeriesBitstreamReader::iterator Xilinx7SeriesBitstreamReader::end() { + return iterator({}); +} + +Xilinx7SeriesBitstreamReader::iterator::iterator(absl::Span words) { + parse_result_.first = words; + parse_result_.second = {}; + ++(*this); +} + +Xilinx7SeriesBitstreamReader::iterator& +Xilinx7SeriesBitstreamReader::iterator::operator++() { + do { + auto new_result = Xilinx7SeriesConfigurationPacket::InitWithWords( + parse_result_.first, + parse_result_.second.has_value() ? + parse_result_.second.operator->() : + nullptr); + + // If the a valid header is being found but there are + // insufficient words to yield a packet, consider it the end. + if (new_result.first == parse_result_.first) { + words_ = absl::Span(); + break; + } + + words_ = parse_result_.first; + parse_result_ = new_result; + } while (!parse_result_.first.empty() && + !parse_result_.second); + + if (!parse_result_.second) { + words_ = absl::Span(); + } + + return *this; +} + +bool Xilinx7SeriesBitstreamReader::iterator::operator==(const iterator &other) const { + return words_ == other.words_; +} + +bool Xilinx7SeriesBitstreamReader::iterator::operator!=(const iterator &other) const { + return !(*this == other); +} + +const Xilinx7SeriesBitstreamReader::value_type& +Xilinx7SeriesBitstreamReader::iterator::operator*() const { + return *(parse_result_.second); +} + +const Xilinx7SeriesBitstreamReader::value_type* +Xilinx7SeriesBitstreamReader::iterator::operator->() const { + return parse_result_.second.operator->(); +} + + +} // namespace prjxray diff --git a/lib/xilinx_7series_bitstream_reader_test.cc b/lib/xilinx_7series_bitstream_reader_test.cc new file mode 100644 index 00000000..43802efe --- /dev/null +++ b/lib/xilinx_7series_bitstream_reader_test.cc @@ -0,0 +1,96 @@ +#include + +#include +#include +#include + +using prjxray::Xilinx7SeriesBitstreamReader; +using prjxray::Xilinx7SeriesConfigurationPacket; + +TEST(Xilinx7SeriesBitstreamReaderTest, InitWithEmptyBytesReturnsNull) { + absl::Span bitstream; + auto reader = Xilinx7SeriesBitstreamReader::InitWithBytes(bitstream); + EXPECT_FALSE(reader); +} + +TEST(Xilinx7SeriesBitstreamReaderTest, InitWithOnlySyncReturnsObject) { + std::vector bitstream{0xAA, 0x99, 0x55, 0x66}; + auto reader = Xilinx7SeriesBitstreamReader::InitWithBytes(bitstream); + EXPECT_TRUE(reader); +} + +TEST(Xilinx7SeriesBitstreamReaderTest, + InitWithSyncAfterNonWordSizedPaddingReturnsObject) { + std::vector bitstream{ + 0xFF, 0xFE, + 0xAA, 0x99, 0x55, 0x66 + }; + auto reader = Xilinx7SeriesBitstreamReader::InitWithBytes(bitstream); + EXPECT_TRUE(reader); +} + +TEST(Xilinx7SeriesBitstreamReaderTest, + InitWithSyncAfterWordSizedPaddingReturnsObject) { + std::vector bitstream{ + 0xFF, 0xFE, 0xFD, 0xFC, + 0xAA, 0x99, 0x55, 0x66 + }; + auto reader = Xilinx7SeriesBitstreamReader::InitWithBytes(bitstream); + EXPECT_TRUE(reader); +} + +TEST(Xilinx7SeriesBitstreamReaderTest, ParsesType1Packet) { + std::vector bitstream{ + 0xAA, 0x99, 0x55, 0x66, // sync + 0b001'00'000, 0b00000000, 0b000'00'000, 0b00000000, // NOP + }; + auto reader = Xilinx7SeriesBitstreamReader::InitWithBytes(bitstream); + ASSERT_TRUE(reader); + ASSERT_NE(reader->begin(), reader->end()); + + auto first_packet = reader->begin(); + EXPECT_EQ(first_packet->opcode(), Xilinx7SeriesConfigurationPacket::Opcode::NOP); + + EXPECT_EQ(++first_packet, reader->end()); +} + +TEST(Xilinx7SeriesBitstreamReaderTest, ParseType2PacketWithoutType1Fails) { + std::vector bitstream{ + 0xAA, 0x99, 0x55, 0x66, // sync + 0b010'00'000, 0b00000000, 0b000'00'000, 0b00000000, // NOP + }; + auto reader = Xilinx7SeriesBitstreamReader::InitWithBytes(bitstream); + ASSERT_TRUE(reader); + EXPECT_EQ(reader->begin(), reader->end()); +} + + +TEST(Xilinx7SeriesBitstreamReaderTest, ParsesType2AfterType1Packet) { + std::vector bitstream{ + 0xAA, 0x99, 0x55, 0x66, // sync + 0b001'01'000, 0b00000000, 0b011'00'000, 0b00000000, // Read + 0b010'01'000, 0b00000000, 0b0000000000, 0b00000100, + 0x1, 0x2, 0x3, 0x4, + 0x5, 0x6, 0x7, 0x8, + 0x9, 0xA, 0xB, 0xC, + 0xD, 0xE, 0xF, 0x10, + }; + std::vector data_words{ 0x01020304, 0x05060708, 0x090A0B0C, 0x0D0E0F10}; + + auto reader = Xilinx7SeriesBitstreamReader::InitWithBytes(bitstream); + ASSERT_TRUE(reader); + ASSERT_NE(reader->begin(), reader->end()); + + auto first_packet = reader->begin(); + EXPECT_EQ(first_packet->opcode(), Xilinx7SeriesConfigurationPacket::Opcode::Read); + EXPECT_EQ(first_packet->address(), static_cast(0x3)); + EXPECT_EQ(first_packet->data(), absl::Span()); + + auto second_packet = ++first_packet; + ASSERT_NE(second_packet, reader->end()); + EXPECT_EQ(second_packet->opcode(), Xilinx7SeriesConfigurationPacket::Opcode::Read); + EXPECT_EQ(second_packet->address(), static_cast(0x3)); + EXPECT_EQ(first_packet->data(), absl::Span(data_words)); + + EXPECT_EQ(++first_packet, reader->end()); +}