mirror of https://github.com/openXC7/prjxray.git
bitstream writer utility class
Signed-off-by: John McMaster <johndmcmaster@gmail.com>
This commit is contained in:
parent
02d28f2920
commit
fe49ac6cee
|
|
@ -3,6 +3,7 @@ add_library(libprjxray
|
|||
memory_mapped_file.cc
|
||||
segbits_file_reader.cc
|
||||
xilinx/xc7series/bitstream_reader.cc
|
||||
xilinx/xc7series/bitstream_writer.cc
|
||||
xilinx/xc7series/block_type.cc
|
||||
xilinx/xc7series/configuration_bus.cc
|
||||
xilinx/xc7series/configuration_column.cc
|
||||
|
|
@ -41,6 +42,7 @@ if (PRJXRAY_BUILD_TESTING)
|
|||
|
||||
add_executable(xilinx_xc7series_test
|
||||
xilinx/xc7series/bitstream_reader_test.cc
|
||||
xilinx/xc7series/bitstream_writer_test.cc
|
||||
xilinx/xc7series/block_type_test.cc
|
||||
xilinx/xc7series/frame_address_test.cc
|
||||
xilinx/xc7series/configuration_bus_test.cc
|
||||
|
|
|
|||
|
|
@ -0,0 +1,113 @@
|
|||
/*
|
||||
* Takes in a collection of ConfigurationPacket and writes them to specified
|
||||
* file This includes the following: -Bus auto detection -Sync Word -FPGA
|
||||
* configuration
|
||||
*/
|
||||
#ifndef PRJXRAY_LIB_XILINX_XC7SERIES_BITSTREAM_WRITER_H
|
||||
#define PRJXRAY_LIB_XILINX_XC7SERIES_BITSTREAM_WRITER_H
|
||||
|
||||
#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/xc7series/configuration_packet.h>
|
||||
|
||||
namespace prjxray {
|
||||
namespace xilinx {
|
||||
namespace xc7series {
|
||||
|
||||
class BitstreamWriter {
|
||||
public:
|
||||
typedef std::array<uint32_t, 6> header_t;
|
||||
typedef std::vector<ConfigurationPacket> packets_t;
|
||||
// Only defined if a packet exists
|
||||
typedef absl::optional<absl::Span<uint32_t>> op_data_t;
|
||||
typedef absl::Span<uint32_t>::iterator data_iterator_t;
|
||||
using itr_value_type = uint32_t;
|
||||
|
||||
class packet_iterator
|
||||
: public std::iterator<std::input_iterator_tag, itr_value_type> {
|
||||
public:
|
||||
packet_iterator& operator++();
|
||||
|
||||
bool operator==(const packet_iterator& other) const;
|
||||
bool operator!=(const packet_iterator& other) const;
|
||||
|
||||
const itr_value_type operator*() const;
|
||||
const itr_value_type operator->() const;
|
||||
|
||||
typedef enum {
|
||||
STATE_HEADER = 1,
|
||||
STATE_DATA = 2,
|
||||
STATE_END = 3,
|
||||
} state_t;
|
||||
|
||||
protected:
|
||||
explicit packet_iterator(const ConfigurationPacket* packet,
|
||||
state_t state,
|
||||
data_iterator_t itr_data);
|
||||
|
||||
private:
|
||||
friend iterator;
|
||||
friend BitstreamWriter;
|
||||
|
||||
// Data iterators
|
||||
// First over the fixed header, then the configuration data
|
||||
state_t state_;
|
||||
// Over packet.data()
|
||||
data_iterator_t itr_data_;
|
||||
|
||||
const ConfigurationPacket* packet_;
|
||||
};
|
||||
|
||||
using value_type = uint32_t;
|
||||
class iterator
|
||||
: public std::iterator<std::input_iterator_tag, itr_value_type> {
|
||||
public:
|
||||
iterator& operator++();
|
||||
|
||||
bool operator==(const iterator& other) const;
|
||||
bool operator!=(const iterator& other) const;
|
||||
|
||||
const itr_value_type operator*() const;
|
||||
const itr_value_type operator->() const;
|
||||
|
||||
packet_iterator packet_begin();
|
||||
packet_iterator packet_end();
|
||||
|
||||
protected:
|
||||
explicit iterator(
|
||||
header_t::iterator itr_header,
|
||||
const packets_t& packets,
|
||||
packets_t::const_iterator itr_packets,
|
||||
absl::optional<packet_iterator> op_itr_packet);
|
||||
|
||||
private:
|
||||
friend BitstreamWriter;
|
||||
// Data iterators
|
||||
// First over the fixed header, then the configuration data
|
||||
header_t::iterator itr_header_;
|
||||
const packets_t& packets_;
|
||||
packets_t::const_iterator itr_packets_;
|
||||
absl::optional<packet_iterator> op_itr_packet_;
|
||||
};
|
||||
|
||||
BitstreamWriter(const packets_t& packets);
|
||||
|
||||
iterator begin();
|
||||
iterator end();
|
||||
|
||||
private:
|
||||
static header_t header_;
|
||||
const packets_t& packets_;
|
||||
};
|
||||
|
||||
} // namespace xc7series
|
||||
} // namespace xilinx
|
||||
} // namespace prjxray
|
||||
|
||||
#endif // PRJXRAY_LIB_XILINX_XC7SERIES_BITSTREAM_WRITER_H
|
||||
|
|
@ -0,0 +1,225 @@
|
|||
/*
|
||||
* TODO
|
||||
* -Finish type 1/2 support
|
||||
* -Review sample bitstream padding. What are they for?
|
||||
*/
|
||||
#include <prjxray/xilinx/xc7series/bitstream_writer.h>
|
||||
|
||||
#include <prjxray/bit_ops.h>
|
||||
|
||||
namespace prjxray {
|
||||
namespace xilinx {
|
||||
namespace xc7series {
|
||||
|
||||
// Per UG470 pg 80: Bus Width Auto Detection
|
||||
std::array<uint32_t, 6> BitstreamWriter::header_{
|
||||
0xFFFFFFFF, 0x000000BB, 0x11220044, 0xFFFFFFFF, 0xFFFFFFFF, 0xAA995566};
|
||||
|
||||
/**************************************************
|
||||
* BitstreamWriter::BitstreamWriter
|
||||
*************************************************/
|
||||
|
||||
BitstreamWriter::BitstreamWriter(const packets_t& packets)
|
||||
: packets_(packets) {}
|
||||
|
||||
/**************************************************
|
||||
*
|
||||
*************************************************/
|
||||
|
||||
BitstreamWriter::packet_iterator BitstreamWriter::iterator::packet_begin() {
|
||||
// itr_packets = packets.begin();
|
||||
const ConfigurationPacket& packet = *itr_packets_;
|
||||
|
||||
return BitstreamWriter::packet_iterator(
|
||||
&packet, BitstreamWriter::packet_iterator::STATE_HEADER,
|
||||
packet.data().begin());
|
||||
}
|
||||
|
||||
BitstreamWriter::packet_iterator BitstreamWriter::iterator::packet_end() {
|
||||
const ConfigurationPacket& packet = *itr_packets_;
|
||||
|
||||
return BitstreamWriter::packet_iterator(
|
||||
&packet, BitstreamWriter::packet_iterator::STATE_END,
|
||||
// Essentially ignored
|
||||
packet.data().end());
|
||||
}
|
||||
|
||||
BitstreamWriter::packet_iterator::packet_iterator(
|
||||
const ConfigurationPacket* packet,
|
||||
state_t state,
|
||||
data_iterator_t itr_data)
|
||||
: state_(state), itr_data_(itr_data), packet_(packet) {}
|
||||
|
||||
BitstreamWriter::packet_iterator& BitstreamWriter::packet_iterator::
|
||||
operator++() {
|
||||
if (state_ == STATE_HEADER) {
|
||||
itr_data_ = packet_->data().begin();
|
||||
if (itr_data_ == packet_->data().end()) {
|
||||
state_ = STATE_END;
|
||||
} else {
|
||||
state_ = STATE_DATA;
|
||||
}
|
||||
} else if (state_ == STATE_DATA) {
|
||||
/// Advance. data must be valid while not at end
|
||||
itr_data_++;
|
||||
// Reached this end of this packet?
|
||||
if (itr_data_ == packet_->data().end()) {
|
||||
state_ = STATE_END;
|
||||
}
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool BitstreamWriter::packet_iterator::operator==(
|
||||
const packet_iterator& other) const {
|
||||
return state_ == other.state_ && itr_data_ == other.itr_data_;
|
||||
}
|
||||
|
||||
bool BitstreamWriter::packet_iterator::operator!=(
|
||||
const packet_iterator& other) const {
|
||||
return !(*this == other);
|
||||
}
|
||||
|
||||
uint32_t packet2header(const ConfigurationPacket& packet) {
|
||||
uint32_t ret = 0;
|
||||
|
||||
ret = bit_field_set(ret, 31, 29, packet.header_type());
|
||||
|
||||
switch (packet.header_type()) {
|
||||
case 0x0:
|
||||
// Bitstreams are 0 padded sometimes, essentially making
|
||||
// a type 0 frame Ignore the other fields for now
|
||||
break;
|
||||
case 0x1: {
|
||||
// Table 5-20: Type 1 Packet Header Format
|
||||
ret = bit_field_set(ret, 28, 27, packet.opcode());
|
||||
ret = bit_field_set(ret, 26, 13, packet.address());
|
||||
ret = bit_field_set(ret, 10, 0, packet.data().length());
|
||||
break;
|
||||
}
|
||||
case 0x2: {
|
||||
// Table 5-22: Type 2 Packet Header
|
||||
// Note address is from previous type 1 header
|
||||
ret = bit_field_set(ret, 28, 27, packet.opcode());
|
||||
ret = bit_field_set(ret, 26, 0, packet.data().length());
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
const BitstreamWriter::itr_value_type BitstreamWriter::packet_iterator::
|
||||
operator*() const {
|
||||
if (state_ == STATE_HEADER) {
|
||||
return packet2header(*packet_);
|
||||
} else if (state_ == STATE_DATA) {
|
||||
return *itr_data_;
|
||||
}
|
||||
return 0; // XXX: assert or something?
|
||||
}
|
||||
|
||||
const BitstreamWriter::itr_value_type BitstreamWriter::packet_iterator::
|
||||
operator->() const {
|
||||
return *(*this);
|
||||
}
|
||||
|
||||
/**************************************************
|
||||
* BitstreamWriter::iterator
|
||||
*************************************************/
|
||||
|
||||
BitstreamWriter::iterator BitstreamWriter::begin() {
|
||||
packets_t::const_iterator itr_packets = packets_.begin();
|
||||
absl::optional<packet_iterator> op_packet_itr;
|
||||
|
||||
// May have no packets
|
||||
if (itr_packets != packets_.end()) {
|
||||
// op_packet_itr = packet_begin();
|
||||
// FIXME: de-duplicate this
|
||||
const ConfigurationPacket& packet = *itr_packets;
|
||||
packet_iterator packet_itr =
|
||||
packet_iterator(&packet, packet_iterator::STATE_HEADER,
|
||||
packet.data().begin());
|
||||
op_packet_itr = packet_itr;
|
||||
}
|
||||
return iterator(header_.begin(), packets_, itr_packets, op_packet_itr);
|
||||
}
|
||||
|
||||
BitstreamWriter::iterator BitstreamWriter::end() {
|
||||
return iterator(header_.end(), packets_, packets_.end(),
|
||||
absl::optional<packet_iterator>());
|
||||
}
|
||||
|
||||
BitstreamWriter::iterator::iterator(header_t::iterator itr_header,
|
||||
const packets_t& packets,
|
||||
packets_t::const_iterator itr_packets,
|
||||
absl::optional<packet_iterator> itr_packet)
|
||||
: itr_header_(itr_header),
|
||||
packets_(packets),
|
||||
itr_packets_(itr_packets),
|
||||
op_itr_packet_(itr_packet) {}
|
||||
|
||||
BitstreamWriter::iterator& BitstreamWriter::iterator::operator++() {
|
||||
// Still generating header?
|
||||
if (itr_header_ != header_.end()) {
|
||||
itr_header_++;
|
||||
// Finished header?
|
||||
// Will advance to initialized itr_packets value
|
||||
// XXX: maybe should just overwrite here
|
||||
if (itr_header_ == header_.end()) {
|
||||
itr_packets_ = packets_.begin();
|
||||
if (itr_packets_ != packets_.end()) {
|
||||
op_itr_packet_ = packet_begin();
|
||||
}
|
||||
}
|
||||
// Then somewhere in packets
|
||||
} else {
|
||||
// We are either at end() in which case this operation is
|
||||
// invalid Or there is a packet in progress packet in progress?
|
||||
// Advance it
|
||||
++(*op_itr_packet_);
|
||||
// Done with this packet?
|
||||
if (*op_itr_packet_ == packet_end()) {
|
||||
itr_packets_++;
|
||||
if (itr_packets_ == packets_.end()) {
|
||||
// we are at the very end
|
||||
// invalidate data to be neat
|
||||
op_itr_packet_.reset();
|
||||
} else {
|
||||
op_itr_packet_ = packet_begin();
|
||||
}
|
||||
}
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool BitstreamWriter::iterator::operator==(const iterator& other) const {
|
||||
return itr_header_ == other.itr_header_ &&
|
||||
itr_packets_ == other.itr_packets_ &&
|
||||
op_itr_packet_ == other.op_itr_packet_;
|
||||
}
|
||||
|
||||
bool BitstreamWriter::iterator::operator!=(const iterator& other) const {
|
||||
return !(*this == other);
|
||||
}
|
||||
|
||||
const BitstreamWriter::itr_value_type BitstreamWriter::iterator::operator*()
|
||||
const {
|
||||
if (itr_header_ != header_.end()) {
|
||||
return *itr_header_;
|
||||
} else {
|
||||
// Iterating over packets, get data from current packet position
|
||||
return *(*op_itr_packet_);
|
||||
}
|
||||
}
|
||||
|
||||
const BitstreamWriter::itr_value_type BitstreamWriter::iterator::operator->()
|
||||
const {
|
||||
return *(*this);
|
||||
}
|
||||
|
||||
} // namespace xc7series
|
||||
} // namespace xilinx
|
||||
} // namespace prjxray
|
||||
|
|
@ -0,0 +1,190 @@
|
|||
#include <array>
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
#include <prjxray/xilinx/xc7series/bitstream_reader.h>
|
||||
#include <prjxray/xilinx/xc7series/bitstream_writer.h>
|
||||
#include <prjxray/xilinx/xc7series/configuration_packet.h>
|
||||
#include <prjxray/xilinx/xc7series/configuration_register.h>
|
||||
|
||||
#include <prjxray/bit_ops.h>
|
||||
|
||||
namespace xc7series = prjxray::xilinx::xc7series;
|
||||
|
||||
constexpr uint32_t kType1NOP = prjxray::bit_field_set<uint32_t>(0, 31, 29, 0x1);
|
||||
|
||||
constexpr uint32_t MakeType1(const int opcode,
|
||||
const int address,
|
||||
const int word_count) {
|
||||
return prjxray::bit_field_set<uint32_t>(
|
||||
prjxray::bit_field_set<uint32_t>(
|
||||
prjxray::bit_field_set<uint32_t>(
|
||||
prjxray::bit_field_set<uint32_t>(0x0, 31, 29, 0x1), 28, 27,
|
||||
opcode),
|
||||
26, 13, address),
|
||||
10, 0, word_count);
|
||||
}
|
||||
|
||||
constexpr uint32_t MakeType2(const int opcode, const int word_count) {
|
||||
return prjxray::bit_field_set<uint32_t>(
|
||||
prjxray::bit_field_set<uint32_t>(
|
||||
prjxray::bit_field_set<uint32_t>(0x0, 31, 29, 0x2), 28, 27,
|
||||
opcode),
|
||||
26, 0, word_count);
|
||||
}
|
||||
|
||||
void dump_packets(xc7series::BitstreamWriter writer, bool nl = true) {
|
||||
int i = 0;
|
||||
// for (uint32_t x : itr) {
|
||||
for (auto itr = writer.begin(); itr != writer.end(); ++itr) {
|
||||
if (nl) {
|
||||
printf("% 3d: 0x0%08X\n", i, *itr);
|
||||
} else {
|
||||
printf("0x0%08X, ", *itr);
|
||||
}
|
||||
fflush(stdout);
|
||||
++i;
|
||||
}
|
||||
if (!nl) {
|
||||
printf("\n");
|
||||
}
|
||||
}
|
||||
|
||||
// Special all 0's
|
||||
void AddType0(std::vector<xc7series::ConfigurationPacket>& packets) {
|
||||
// InitWithWords doesn't like type 0
|
||||
/*
|
||||
static std::vector<uint32_t> words{0x00000000};
|
||||
absl::Span<uint32_t> word_span(words);
|
||||
auto packet = xc7series::ConfigurationPacket::InitWithWords(word_span);
|
||||
packets.push_back(*(packet.second));
|
||||
*/
|
||||
static std::vector<uint32_t> words{};
|
||||
absl::Span<uint32_t> word_span(words);
|
||||
// CRC is config value 0
|
||||
packets.push_back(xc7series::ConfigurationPacket(
|
||||
0, xc7series::ConfigurationPacket::NOP,
|
||||
xc7series::ConfigurationRegister::CRC, word_span));
|
||||
}
|
||||
|
||||
void AddType1(std::vector<xc7series::ConfigurationPacket>& packets) {
|
||||
static std::vector<uint32_t> words{MakeType1(0x2, 0x3, 2), 0xAA, 0xBB};
|
||||
absl::Span<uint32_t> word_span(words);
|
||||
auto packet = xc7series::ConfigurationPacket::InitWithWords(word_span);
|
||||
packets.push_back(*(packet.second));
|
||||
}
|
||||
|
||||
// Empty
|
||||
void AddType1E(std::vector<xc7series::ConfigurationPacket>& packets) {
|
||||
static std::vector<uint32_t> words{MakeType1(0x2, 0x3, 0)};
|
||||
absl::Span<uint32_t> word_span(words);
|
||||
auto packet = xc7series::ConfigurationPacket::InitWithWords(word_span);
|
||||
packets.push_back(*(packet.second));
|
||||
}
|
||||
|
||||
void AddType2(std::vector<xc7series::ConfigurationPacket>& packets) {
|
||||
// Type 1 packet with address
|
||||
// Without this InitWithWords will fail on type 2
|
||||
xc7series::ConfigurationPacket* packet1;
|
||||
{
|
||||
static std::vector<uint32_t> words{MakeType1(0x2, 0x3, 0)};
|
||||
absl::Span<uint32_t> word_span(words);
|
||||
auto packet1_pair =
|
||||
xc7series::ConfigurationPacket::InitWithWords(word_span);
|
||||
packets.push_back(*(packet1_pair.second));
|
||||
packet1 = &packets[0];
|
||||
}
|
||||
// Type 2 packet with data
|
||||
{
|
||||
static std::vector<uint32_t> words{
|
||||
MakeType2(0x01, 12), 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12};
|
||||
absl::Span<uint32_t> word_span(words);
|
||||
auto packet = xc7series::ConfigurationPacket::InitWithWords(
|
||||
word_span, packet1);
|
||||
packets.push_back(*(packet.second));
|
||||
}
|
||||
}
|
||||
|
||||
// Empty packets should produce just the header
|
||||
TEST(BitstreamWriterTest, WriteHeader) {
|
||||
std::vector<xc7series::ConfigurationPacket> packets;
|
||||
|
||||
xc7series::BitstreamWriter writer(packets);
|
||||
std::vector<uint32_t> words(writer.begin(), writer.end());
|
||||
|
||||
// Per UG470 pg 80: Bus Width Auto Detection
|
||||
std::vector<uint32_t> ref_header{0xFFFFFFFF, 0x000000BB, 0x11220044,
|
||||
0xFFFFFFFF, 0xFFFFFFFF, 0xAA995566};
|
||||
EXPECT_EQ(words, ref_header);
|
||||
|
||||
// dump_packets(writer);
|
||||
}
|
||||
|
||||
TEST(BitstreamWriterTest, WriteType0) {
|
||||
std::vector<xc7series::ConfigurationPacket> packets;
|
||||
AddType0(packets);
|
||||
xc7series::BitstreamWriter writer(packets);
|
||||
// dump_packets(writer, false);
|
||||
std::vector<uint32_t> words(writer.begin(), writer.end());
|
||||
std::vector<uint32_t> ref{// Bus width + sync
|
||||
0x0FFFFFFFF, 0x0000000BB, 0x011220044,
|
||||
0x0FFFFFFFF, 0x0FFFFFFFF, 0x0AA995566,
|
||||
// Type 0
|
||||
0x00000000};
|
||||
EXPECT_EQ(words, ref);
|
||||
}
|
||||
TEST(BitstreamWriterTest, WriteType1) {
|
||||
std::vector<xc7series::ConfigurationPacket> packets;
|
||||
AddType1(packets);
|
||||
xc7series::BitstreamWriter writer(packets);
|
||||
// dump_packets(writer, false);
|
||||
std::vector<uint32_t> words(writer.begin(), writer.end());
|
||||
std::vector<uint32_t> ref{// Bus width + sync
|
||||
0x0FFFFFFFF, 0x0000000BB, 0x011220044,
|
||||
0x0FFFFFFFF, 0x0FFFFFFFF, 0x0AA995566,
|
||||
// Type 1
|
||||
0x030006002, 0x0000000AA, 0x0000000BB};
|
||||
EXPECT_EQ(words, ref);
|
||||
}
|
||||
|
||||
TEST(BitstreamWriterTest, WriteType2) {
|
||||
std::vector<xc7series::ConfigurationPacket> packets;
|
||||
AddType2(packets);
|
||||
xc7series::BitstreamWriter writer(packets);
|
||||
// dump_packets(writer, false);
|
||||
std::vector<uint32_t> words(writer.begin(), writer.end());
|
||||
std::vector<uint32_t> ref{
|
||||
// Bus width + sync
|
||||
0x0FFFFFFFF, 0x0000000BB, 0x011220044, 0x0FFFFFFFF, 0x0FFFFFFFF,
|
||||
0x0AA995566,
|
||||
// Type 1 + type 2 header
|
||||
0x030006000, 0x04800000C, 0x000000001, 0x000000002, 0x000000003,
|
||||
0x000000004, 0x000000005, 0x000000006, 0x000000007, 0x000000008,
|
||||
0x000000009, 0x00000000A, 0x00000000B, 0x00000000C};
|
||||
EXPECT_EQ(words, ref);
|
||||
}
|
||||
|
||||
TEST(BitstreamWriterTest, WriteMulti) {
|
||||
std::vector<xc7series::ConfigurationPacket> packets;
|
||||
AddType1(packets);
|
||||
AddType1E(packets);
|
||||
AddType2(packets);
|
||||
AddType1E(packets);
|
||||
xc7series::BitstreamWriter writer(packets);
|
||||
// dump_packets(writer, false);
|
||||
std::vector<uint32_t> words(writer.begin(), writer.end());
|
||||
std::vector<uint32_t> ref{
|
||||
// Bus width + sync
|
||||
0x0FFFFFFFF, 0x0000000BB, 0x011220044, 0x0FFFFFFFF, 0x0FFFFFFFF,
|
||||
0x0AA995566,
|
||||
// Type1
|
||||
0x030006002, 0x0000000AA, 0x0000000BB,
|
||||
// Type1
|
||||
0x030006000,
|
||||
// Type 1 + type 2 header
|
||||
0x030006000, 0x04800000C, 0x000000001, 0x000000002, 0x000000003,
|
||||
0x000000004, 0x000000005, 0x000000006, 0x000000007, 0x000000008,
|
||||
0x000000009, 0x00000000A, 0x00000000B, 0x00000000C,
|
||||
// Type 1
|
||||
0x030006000};
|
||||
EXPECT_EQ(words, ref);
|
||||
}
|
||||
Loading…
Reference in New Issue