mirror of https://github.com/openXC7/prjxray.git
181 lines
5.2 KiB
C++
181 lines
5.2 KiB
C++
#ifndef PRJXRAY_LIB_XILINX_BITSTREAM_READER_H
|
|
#define PRJXRAY_LIB_XILINX_BITSTREAM_READER_H
|
|
|
|
#include <algorithm>
|
|
#include <iostream>
|
|
#include <memory>
|
|
#include <vector>
|
|
|
|
#include <absl/types/span.h>
|
|
|
|
#include <prjxray/big_endian_span.h>
|
|
#include <prjxray/xilinx/architectures.h>
|
|
#include <prjxray/xilinx/configuration_packet.h>
|
|
|
|
namespace prjxray {
|
|
namespace xilinx {
|
|
|
|
// Constructs a collection of 32-bit big-endian words from a bitstream file.
|
|
// Provides an iterator over the configuration packets.
|
|
template <typename ArchType>
|
|
class BitstreamReader {
|
|
public:
|
|
using value_type = ConfigurationPacket<typename ArchType::ConfRegType>;
|
|
|
|
// Implements an iterator over the words grouped in configuration
|
|
// packets.
|
|
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 BitstreamReader;
|
|
|
|
typename value_type::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.
|
|
BitstreamReader(std::vector<uint32_t>&& words)
|
|
: words_(std::move(words)) {}
|
|
|
|
BitstreamReader() {}
|
|
size_t size() { return words_.size(); }
|
|
|
|
// Construct a `BitstreamReader` from a Container of bytes.
|
|
// Any bytes preceding an initial sync word are ignored.
|
|
template <typename T>
|
|
static absl::optional<BitstreamReader<ArchType>> InitWithBytes(
|
|
T bitstream);
|
|
|
|
const std::vector<uint32_t>& words() { return words_; };
|
|
|
|
// Returns an iterator that yields `ConfigurationPackets`
|
|
// as read from the bitstream.
|
|
iterator begin();
|
|
iterator end();
|
|
|
|
private:
|
|
static std::array<uint8_t, 4> kSyncWord;
|
|
|
|
std::vector<uint32_t> words_;
|
|
};
|
|
|
|
template <typename ArchType>
|
|
template <typename T>
|
|
absl::optional<BitstreamReader<ArchType>>
|
|
BitstreamReader<ArchType>::InitWithBytes(T bitstream) {
|
|
// 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 absl::optional<BitstreamReader<ArchType>>();
|
|
}
|
|
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<typename ArchType::WordType>(config_packets);
|
|
std::vector<uint32_t> words{big_endian_reader.begin(),
|
|
big_endian_reader.end()};
|
|
|
|
return BitstreamReader<ArchType>(std::move(words));
|
|
}
|
|
|
|
// Sync word as specified in UG470 page 81
|
|
template <typename ArchType>
|
|
std::array<uint8_t, 4> BitstreamReader<ArchType>::kSyncWord{0xAA, 0x99, 0x55,
|
|
0x66};
|
|
|
|
template <typename ArchType>
|
|
typename BitstreamReader<ArchType>::iterator
|
|
BitstreamReader<ArchType>::begin() {
|
|
return iterator(absl::MakeSpan(words_));
|
|
}
|
|
|
|
template <typename ArchType>
|
|
typename BitstreamReader<ArchType>::iterator BitstreamReader<ArchType>::end() {
|
|
return iterator({});
|
|
}
|
|
|
|
template <typename ArchType>
|
|
BitstreamReader<ArchType>::iterator::iterator(absl::Span<uint32_t> words) {
|
|
parse_result_.first = words;
|
|
parse_result_.second = {};
|
|
++(*this);
|
|
}
|
|
|
|
template <typename ArchType>
|
|
typename BitstreamReader<ArchType>::iterator&
|
|
BitstreamReader<ArchType>::iterator::operator++() {
|
|
do {
|
|
auto new_result =
|
|
ConfigurationPacket<typename ArchType::ConfRegType>::
|
|
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;
|
|
}
|
|
|
|
template <typename ArchType>
|
|
bool BitstreamReader<ArchType>::iterator::operator==(
|
|
const iterator& other) const {
|
|
return words_ == other.words_;
|
|
}
|
|
|
|
template <typename ArchType>
|
|
bool BitstreamReader<ArchType>::iterator::operator!=(
|
|
const iterator& other) const {
|
|
return !(*this == other);
|
|
}
|
|
|
|
template <typename ArchType>
|
|
const typename BitstreamReader<ArchType>::value_type&
|
|
BitstreamReader<ArchType>::iterator::operator*() const {
|
|
return *(parse_result_.second);
|
|
}
|
|
|
|
template <typename ArchType>
|
|
const typename BitstreamReader<ArchType>::value_type*
|
|
BitstreamReader<ArchType>::iterator::operator->() const {
|
|
return parse_result_.second.operator->();
|
|
}
|
|
} // namespace xilinx
|
|
} // namespace prjxray
|
|
|
|
#endif // PRJXRAY_LIB_XILINX_BITSTREAM_READER_H
|