text: #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<std::unique_ptr<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.emplace_back(new xc7series::ConfigurationPacket(
  	    0, xc7series::ConfigurationPacket::NOP,
  	    xc7series::ConfigurationRegister::CRC, word_span));
  }
  
  void AddType1(
      std::vector<std::unique_ptr<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.emplace_back(
  	    new xc7series::ConfigurationPacket(*(packet.second)));
  }
  
  // Empty
  void AddType1E(
      std::vector<std::unique_ptr<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.emplace_back(
  	    new xc7series::ConfigurationPacket(*(packet.second)));
  }
  
  void AddType2(
      std::vector<std::unique_ptr<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.emplace_back(
  		    new xc7series::ConfigurationPacket(*(packet1_pair.second)));
  		packet1 = packets[0].get();
  	}
  	// 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.emplace_back(
  		    new xc7series::ConfigurationPacket(*(packet.second)));
  	}
  }
  
  // Empty packets should produce just the header
  TEST(BitstreamWriterTest, WriteHeader) {
  	std::vector<std::unique_ptr<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<std::unique_ptr<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<std::unique_ptr<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<std::unique_ptr<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<std::unique_ptr<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);
  }
  