lib: Parser for Xilinx bitstream file format

Reads the raw bitstream into configuration packets that represent
internal register writes.

Signed-off-by: Rick Altherr <kc8apf@kc8apf.net>
Signed-off-by: Tim 'mithro' Ansell <mithro@mithis.com>
This commit is contained in:
Rick Altherr 2017-12-04 15:49:58 -08:00 committed by Tim 'mithro' Ansell
parent e0094d8538
commit 82a989cfc9
4 changed files with 264 additions and 0 deletions

View File

@ -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

View File

@ -0,0 +1,87 @@
#ifndef PRJXRAY_LIB_XILINX_7SERIES_BITSTREAM_READER
#define PRJXRAY_LIB_XILINX_7SERIES_BITSTREAM_READER
#include <algorithm>
#include <memory>
#include <vector>
#include <absl/types/optional.h>
#include <absl/types/span.h>
#include <prjxray/big_endian_span.h>
#include <prjxray/xilinx_7series_configuration_packet.h>
namespace prjxray {
class Xilinx7SeriesBitstreamReader {
public:
using value_type = Xilinx7SeriesConfigurationPacket;
class iterator
: public std::iterator<std::input_iterator_tag, value_type> {
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<uint32_t> words);
private:
friend Xilinx7SeriesBitstreamReader;
Xilinx7SeriesConfigurationPacket::ParseResult parse_result_;
absl::Span<uint32_t> 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<uint32_t> &&words);
// Construct a `Xilinx7SeriesBitstreamReader` from a Container of bytes.
// Any bytes preceding an initial sync word are ignored.
template<typename T>
static absl::optional<Xilinx7SeriesBitstreamReader> InitWithBytes(
T &bitstream);
// Returns an iterator that yields `Xilinx7SeriesConfigurationPackets`
// as read from the bitstream.
iterator begin();
iterator end();
private:
static std::array<uint8_t, 4> kSyncWord;
std::vector<uint32_t> words_;
};
template<typename T>
absl::optional<Xilinx7SeriesBitstreamReader>
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<Xilinx7SeriesBitstreamReader>();
}
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 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<uint32_t>(config_packets);
std::vector<uint32_t> words{big_endian_reader.begin(),
big_endian_reader.end()};
return Xilinx7SeriesBitstreamReader(std::move(words));
}
} // namespace prjxray
#endif // PRJXRAY_LIB_XILINX_7SERIES_BITSTREAM_READER

View File

@ -0,0 +1,73 @@
#include <prjxray/xilinx_7series_bitstream_reader.h>
namespace prjxray {
std::array<uint8_t, 4> Xilinx7SeriesBitstreamReader::kSyncWord{
0xAA, 0x99, 0x55, 0x66};
Xilinx7SeriesBitstreamReader::Xilinx7SeriesBitstreamReader(
std::vector<uint32_t> &&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<uint32_t> 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<uint32_t>();
break;
}
words_ = parse_result_.first;
parse_result_ = new_result;
} while (!parse_result_.first.empty() &&
!parse_result_.second);
if (!parse_result_.second) {
words_ = absl::Span<uint32_t>();
}
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

View File

@ -0,0 +1,96 @@
#include <array>
#include <gtest/gtest.h>
#include <prjxray/xilinx_7series_bitstream_reader.h>
#include <prjxray/xilinx_7series_configuration_packet.h>
using prjxray::Xilinx7SeriesBitstreamReader;
using prjxray::Xilinx7SeriesConfigurationPacket;
TEST(Xilinx7SeriesBitstreamReaderTest, InitWithEmptyBytesReturnsNull) {
absl::Span<uint8_t> bitstream;
auto reader = Xilinx7SeriesBitstreamReader::InitWithBytes(bitstream);
EXPECT_FALSE(reader);
}
TEST(Xilinx7SeriesBitstreamReaderTest, InitWithOnlySyncReturnsObject) {
std::vector<uint8_t> bitstream{0xAA, 0x99, 0x55, 0x66};
auto reader = Xilinx7SeriesBitstreamReader::InitWithBytes(bitstream);
EXPECT_TRUE(reader);
}
TEST(Xilinx7SeriesBitstreamReaderTest,
InitWithSyncAfterNonWordSizedPaddingReturnsObject) {
std::vector<uint8_t> bitstream{
0xFF, 0xFE,
0xAA, 0x99, 0x55, 0x66
};
auto reader = Xilinx7SeriesBitstreamReader::InitWithBytes(bitstream);
EXPECT_TRUE(reader);
}
TEST(Xilinx7SeriesBitstreamReaderTest,
InitWithSyncAfterWordSizedPaddingReturnsObject) {
std::vector<uint8_t> bitstream{
0xFF, 0xFE, 0xFD, 0xFC,
0xAA, 0x99, 0x55, 0x66
};
auto reader = Xilinx7SeriesBitstreamReader::InitWithBytes(bitstream);
EXPECT_TRUE(reader);
}
TEST(Xilinx7SeriesBitstreamReaderTest, ParsesType1Packet) {
std::vector<uint8_t> 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<uint8_t> 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<uint8_t> 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<uint32_t> 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<uint32_t>(0x3));
EXPECT_EQ(first_packet->data(), absl::Span<uint32_t>());
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<uint32_t>(0x3));
EXPECT_EQ(first_packet->data(), absl::Span<uint32_t>(data_words));
EXPECT_EQ(++first_packet, reader->end());
}