mirror of https://github.com/openXC7/prjxray.git
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:
parent
e0094d8538
commit
82a989cfc9
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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());
|
||||
}
|
||||
Loading…
Reference in New Issue