diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt index 49056db3..4b751d68 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/xc7series/bitstream_reader.cc + xilinx/xc7series/bitstream_writer.cc xilinx/xc7series/block_type.cc xilinx/xc7series/configuration_bus.cc xilinx/xc7series/configuration_column.cc @@ -42,6 +43,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 diff --git a/lib/include/prjxray/xilinx/xc7series/bitstream_writer.h b/lib/include/prjxray/xilinx/xc7series/bitstream_writer.h new file mode 100644 index 00000000..73d6c134 --- /dev/null +++ b/lib/include/prjxray/xilinx/xc7series/bitstream_writer.h @@ -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 +#include +#include + +#include +#include + +#include +#include + +namespace prjxray { +namespace xilinx { +namespace xc7series { + +class BitstreamWriter { + public: + typedef std::array header_t; + typedef std::vector packets_t; + // Only defined if a packet exists + typedef absl::optional> op_data_t; + typedef absl::Span::iterator data_iterator_t; + using itr_value_type = uint32_t; + + class packet_iterator + : public std::iterator { + 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 { + 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 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 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 diff --git a/lib/xilinx/xc7series/bitstream_writer.cc b/lib/xilinx/xc7series/bitstream_writer.cc new file mode 100644 index 00000000..3a7da30d --- /dev/null +++ b/lib/xilinx/xc7series/bitstream_writer.cc @@ -0,0 +1,225 @@ +/* + * TODO + * -Finish type 1/2 support + * -Review sample bitstream padding. What are they for? + */ +#include + +#include + +namespace prjxray { +namespace xilinx { +namespace xc7series { + +// Per UG470 pg 80: Bus Width Auto Detection +std::array 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 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()); +} + +BitstreamWriter::iterator::iterator(header_t::iterator itr_header, + const packets_t& packets, + packets_t::const_iterator itr_packets, + absl::optional 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 diff --git a/lib/xilinx/xc7series/bitstream_writer_test.cc b/lib/xilinx/xc7series/bitstream_writer_test.cc new file mode 100644 index 00000000..752fa4c9 --- /dev/null +++ b/lib/xilinx/xc7series/bitstream_writer_test.cc @@ -0,0 +1,190 @@ +#include + +#include +#include +#include +#include +#include + +#include + +namespace xc7series = prjxray::xilinx::xc7series; + +constexpr uint32_t kType1NOP = prjxray::bit_field_set(0, 31, 29, 0x1); + +constexpr uint32_t MakeType1(const int opcode, + const int address, + const int word_count) { + return prjxray::bit_field_set( + prjxray::bit_field_set( + prjxray::bit_field_set( + prjxray::bit_field_set(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( + prjxray::bit_field_set( + prjxray::bit_field_set(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& packets) { + // InitWithWords doesn't like type 0 + /* + static std::vector words{0x00000000}; + absl::Span word_span(words); + auto packet = xc7series::ConfigurationPacket::InitWithWords(word_span); + packets.push_back(*(packet.second)); + */ + static std::vector words{}; + absl::Span 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& packets) { + static std::vector words{MakeType1(0x2, 0x3, 2), 0xAA, 0xBB}; + absl::Span word_span(words); + auto packet = xc7series::ConfigurationPacket::InitWithWords(word_span); + packets.push_back(*(packet.second)); +} + +// Empty +void AddType1E(std::vector& packets) { + static std::vector words{MakeType1(0x2, 0x3, 0)}; + absl::Span word_span(words); + auto packet = xc7series::ConfigurationPacket::InitWithWords(word_span); + packets.push_back(*(packet.second)); +} + +void AddType2(std::vector& packets) { + // Type 1 packet with address + // Without this InitWithWords will fail on type 2 + xc7series::ConfigurationPacket* packet1; + { + static std::vector words{MakeType1(0x2, 0x3, 0)}; + absl::Span 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 words{ + MakeType2(0x01, 12), 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12}; + absl::Span 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 packets; + + xc7series::BitstreamWriter writer(packets); + std::vector words(writer.begin(), writer.end()); + + // Per UG470 pg 80: Bus Width Auto Detection + std::vector ref_header{0xFFFFFFFF, 0x000000BB, 0x11220044, + 0xFFFFFFFF, 0xFFFFFFFF, 0xAA995566}; + EXPECT_EQ(words, ref_header); + + // dump_packets(writer); +} + +TEST(BitstreamWriterTest, WriteType0) { + std::vector packets; + AddType0(packets); + xc7series::BitstreamWriter writer(packets); + // dump_packets(writer, false); + std::vector words(writer.begin(), writer.end()); + std::vector ref{// Bus width + sync + 0x0FFFFFFFF, 0x0000000BB, 0x011220044, + 0x0FFFFFFFF, 0x0FFFFFFFF, 0x0AA995566, + // Type 0 + 0x00000000}; + EXPECT_EQ(words, ref); +} +TEST(BitstreamWriterTest, WriteType1) { + std::vector packets; + AddType1(packets); + xc7series::BitstreamWriter writer(packets); + // dump_packets(writer, false); + std::vector words(writer.begin(), writer.end()); + std::vector ref{// Bus width + sync + 0x0FFFFFFFF, 0x0000000BB, 0x011220044, + 0x0FFFFFFFF, 0x0FFFFFFFF, 0x0AA995566, + // Type 1 + 0x030006002, 0x0000000AA, 0x0000000BB}; + EXPECT_EQ(words, ref); +} + +TEST(BitstreamWriterTest, WriteType2) { + std::vector packets; + AddType2(packets); + xc7series::BitstreamWriter writer(packets); + // dump_packets(writer, false); + std::vector words(writer.begin(), writer.end()); + std::vector 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 packets; + AddType1(packets); + AddType1E(packets); + AddType2(packets); + AddType1E(packets); + xc7series::BitstreamWriter writer(packets); + // dump_packets(writer, false); + std::vector words(writer.begin(), writer.end()); + std::vector 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); +}