Merge pull request #84 from kc8apf/arch_docs

docs: low-level configuration details
diff --git a/lib/include/prjxray/xilinx/xc7series/bitstream_writer.h b/lib/include/prjxray/xilinx/xc7series/bitstream_writer.h
index ae0634d..806cc9a 100644
--- a/lib/include/prjxray/xilinx/xc7series/bitstream_writer.h
+++ b/lib/include/prjxray/xilinx/xc7series/bitstream_writer.h
@@ -23,7 +23,7 @@
 class BitstreamWriter {
        public:
 	typedef std::array<uint32_t, 6> header_t;
-	typedef std::vector<ConfigurationPacket> packets_t;
+	typedef std::vector<std::unique_ptr<ConfigurationPacket>> packets_t;
 	// Only defined if a packet exists
 	typedef absl::optional<absl::Span<const uint32_t>> op_data_t;
 	typedef absl::Span<const uint32_t>::iterator data_iterator_t;
diff --git a/lib/include/prjxray/xilinx/xc7series/command.h b/lib/include/prjxray/xilinx/xc7series/command.h
new file mode 100644
index 0000000..2ff16e8
--- /dev/null
+++ b/lib/include/prjxray/xilinx/xc7series/command.h
@@ -0,0 +1,34 @@
+#ifndef PRJXRAY_LIB_XILINX_XC7SERIES_COMMAND_H_
+#define PRJXRAY_LIB_XILINX_XC7SERIES_COMMAND_H_
+
+namespace prjxray {
+namespace xilinx {
+namespace xc7series {
+
+enum class Command : uint32_t {
+	NOP = 0x0,
+	WCFG = 0x1,
+	MFW = 0x2,
+	LFRM = 0x3,
+	RCFG = 0x4,
+	START = 0x5,
+	RCAP = 0x6,
+	RCRC = 0x7,
+	AGHIGH = 0x8,
+	SWITCH = 0x9,
+	GRESTORE = 0xA,
+	SHUTDOWN = 0xB,
+	GCAPTURE = 0xC,
+	DESYNC = 0xD,
+	IPROG = 0xF,
+	CRCC = 0x10,
+	LTIMER = 0x11,
+	BSPI_READ = 0x12,
+	FALL_EDGE = 0x13,
+};
+
+}  // namespace xc7series
+}  // namespace xilinx
+}  // namespace prjxray
+
+#endif  // PRJXRAY_LIB_XILINX_XC7SERIES_COMMAND_H_
diff --git a/lib/include/prjxray/xilinx/xc7series/configuration_options_0_value.h b/lib/include/prjxray/xilinx/xc7series/configuration_options_0_value.h
new file mode 100644
index 0000000..1b7c1c1
--- /dev/null
+++ b/lib/include/prjxray/xilinx/xc7series/configuration_options_0_value.h
@@ -0,0 +1,122 @@
+#ifndef PRJXRAY_LIB_XILINX_XC7SERIES_CONFIGURATION_OPTIONS_0_VALUE_H
+#define PRJXRAY_LIB_XILINX_XC7SERIES_CONFIGURATION_OPTIONS_0_VALUE_H
+
+#include <prjxray/bit_ops.h>
+#include <prjxray/xilinx/xc7series/configuration_packet.h>
+#include <prjxray/xilinx/xc7series/configuration_register.h>
+
+namespace prjxray {
+namespace xilinx {
+namespace xc7series {
+
+class ConfigurationOptions0Value {
+       public:
+	enum class StartupClockSource : uint32_t {
+		CCLK = 0x0,
+		User = 0x1,
+		JTAG = 0x2,
+	};
+
+	enum class SignalReleaseCycle : uint32_t {
+		Phase1 = 0x0,
+		Phase2 = 0x1,
+		Phase3 = 0x2,
+		Phase4 = 0x3,
+		Phase5 = 0x4,
+		Phase6 = 0x5,
+		TrackDone = 0x6,
+		Keep = 0x7,
+	};
+
+	enum class StallCycle : uint32_t {
+		Phase0 = 0x0,
+		Phase1 = 0x1,
+		Phase2 = 0x2,
+		Phase3 = 0x3,
+		Phase4 = 0x4,
+		Phase5 = 0x5,
+		Phase6 = 0x6,
+		NoWait = 0x7,
+	};
+
+	ConfigurationOptions0Value() : value_(0) {}
+
+	operator uint32_t() const { return value_; }
+
+	ConfigurationOptions0Value& SetUseDonePinAsPowerdownStatus(
+	    bool enabled) {
+		value_ = bit_field_set(value_, 27, 27, enabled ? 1 : 0);
+		return *this;
+	}
+
+	ConfigurationOptions0Value& SetAddPipelineStageForDoneIn(bool enabled) {
+		value_ = bit_field_set(value_, 25, 25, enabled ? 1 : 0);
+		return *this;
+	}
+
+	ConfigurationOptions0Value& SetDriveDoneHigh(bool enabled) {
+		value_ = bit_field_set(value_, 24, 24, enabled);
+		return *this;
+	}
+
+	ConfigurationOptions0Value& SetReadbackIsSingleShot(bool enabled) {
+		value_ = bit_field_set(value_, 23, 23, enabled);
+		return *this;
+	}
+
+	ConfigurationOptions0Value& SetCclkFrequency(uint32_t mhz) {
+		value_ = bit_field_set(value_, 22, 17, mhz);
+		return *this;
+	}
+
+	ConfigurationOptions0Value& SetStartupClockSource(
+	    StartupClockSource source) {
+		value_ = bit_field_set(value_, 16, 15,
+		                       static_cast<uint32_t>(source));
+		return *this;
+	}
+
+	ConfigurationOptions0Value& SetReleaseDonePinAtStartupCycle(
+	    SignalReleaseCycle cycle) {
+		value_ =
+		    bit_field_set(value_, 14, 12, static_cast<uint32_t>(cycle));
+		return *this;
+	}
+
+	ConfigurationOptions0Value& SetStallAtStartupCycleUntilDciMatch(
+	    StallCycle cycle) {
+		value_ =
+		    bit_field_set(value_, 11, 9, static_cast<uint32_t>(cycle));
+		return *this;
+	};
+
+	ConfigurationOptions0Value& SetStallAtStartupCycleUntilMmcmLock(
+	    StallCycle cycle) {
+		value_ =
+		    bit_field_set(value_, 8, 6, static_cast<uint32_t>(cycle));
+		return *this;
+	};
+
+	ConfigurationOptions0Value& SetReleaseGtsSignalAtStartupCycle(
+	    SignalReleaseCycle cycle) {
+		value_ =
+		    bit_field_set(value_, 5, 3, static_cast<uint32_t>(cycle));
+		return *this;
+	}
+
+	ConfigurationOptions0Value& SetReleaseGweSignalAtStartupCycle(
+	    SignalReleaseCycle cycle) {
+		value_ =
+		    bit_field_set(value_, 2, 0, static_cast<uint32_t>(cycle));
+		return *this;
+	}
+
+       private:
+	uint32_t value_;
+};  // namespace xc7series
+
+}  // namespace xc7series
+}  // namespace xilinx
+}  // namespace prjxray
+
+#endif  // PRJXRAY_LIB_XILINX_XC7SERIES_CONFIGURATION_OPTIONS_0_VALUE_H
diff --git a/lib/include/prjxray/xilinx/xc7series/configuration_packet_with_payload.h b/lib/include/prjxray/xilinx/xc7series/configuration_packet_with_payload.h
new file mode 100644
index 0000000..bdcdc3c
--- /dev/null
+++ b/lib/include/prjxray/xilinx/xc7series/configuration_packet_with_payload.h
@@ -0,0 +1,33 @@
+#ifndef PRJXRAY_LIB_XILINX_XC7SERIES_g_CONFIGURATION_PACKET_WITH_PAYLOAD_H
+#define PRJXRAY_LIB_XILINX_XC7SERIES_g_CONFIGURATION_PACKET_WITH_PAYLOAD_H
+
+#include <memory>
+
+#include <absl/types/span.h>
+#include <prjxray/bit_ops.h>
+#include <prjxray/xilinx/xc7series/configuration_packet.h>
+#include <prjxray/xilinx/xc7series/configuration_register.h>
+
+namespace prjxray {
+namespace xilinx {
+namespace xc7series {
+
+template <int Words>
+class ConfigurationPacketWithPayload : public ConfigurationPacket {
+       public:
+	ConfigurationPacketWithPayload(
+	    Opcode op,
+	    ConfigurationRegister reg,
+	    const std::array<uint32_t, Words>& payload)
+	    : ConfigurationPacket(1, op, reg, absl::Span<uint32_t>(payload_)),
+	      payload_(std::move(payload)) {}
+
+       private:
+	std::array<uint32_t, Words> payload_;
+};  // namespace xc7series
+
+}  // namespace xc7series
+}  // namespace xilinx
+}  // namespace prjxray
+
+#endif  // PRJXRAY_LIB_XILINX_XC7SERIES_g_CONFIGURATION_PACKET_WITH_PAYLOAD_H
diff --git a/lib/include/prjxray/xilinx/xc7series/configuration_register.h b/lib/include/prjxray/xilinx/xc7series/configuration_register.h
index 9f75a09..4fbc5d4 100644
--- a/lib/include/prjxray/xilinx/xc7series/configuration_register.h
+++ b/lib/include/prjxray/xilinx/xc7series/configuration_register.h
@@ -25,6 +25,7 @@
 	COR1 = 0x0e,
 	WBSTAR = 0x10,
 	TIMER = 0x11,
+	UNKNOWN = 0x13,
 	BOOTSTS = 0x16,
 	CTL1 = 0x18,
 	BSPI = 0x1F,
diff --git a/lib/include/prjxray/xilinx/xc7series/nop_packet.h b/lib/include/prjxray/xilinx/xc7series/nop_packet.h
new file mode 100644
index 0000000..6533988
--- /dev/null
+++ b/lib/include/prjxray/xilinx/xc7series/nop_packet.h
@@ -0,0 +1,24 @@
+#ifndef PRJXRAY_LIB_XILINX_XC7SERIES_NOP_PACKET_H
+#define PRJXRAY_LIB_XILINX_XC7SERIES_NOP_PACKET_H
+
+#include <prjxray/xilinx/xc7series/configuration_packet.h>
+#include <prjxray/xilinx/xc7series/configuration_register.h>
+
+namespace prjxray {
+namespace xilinx {
+namespace xc7series {
+
+class NopPacket : public ConfigurationPacket {
+       public:
+	NopPacket()
+	    : ConfigurationPacket(1,
+	                          Opcode::NOP,
+	                          ConfigurationRegister::CRC,
+	                          {}) {}
+};
+
+}  // namespace xc7series
+}  // namespace xilinx
+}  // namespace prjxray
+
+#endif  // PRJXRAY_LIB_XILINX_XC7SERIES_NOP_PACKET_H
diff --git a/lib/xilinx/xc7series/bitstream_writer.cc b/lib/xilinx/xc7series/bitstream_writer.cc
index 3a7da30..eb388ca 100644
--- a/lib/xilinx/xc7series/bitstream_writer.cc
+++ b/lib/xilinx/xc7series/bitstream_writer.cc
@@ -28,7 +28,7 @@
 
 BitstreamWriter::packet_iterator BitstreamWriter::iterator::packet_begin() {
 	// itr_packets = packets.begin();
-	const ConfigurationPacket& packet = *itr_packets_;
+	const ConfigurationPacket& packet = **itr_packets_;
 
 	return BitstreamWriter::packet_iterator(
 	    &packet, BitstreamWriter::packet_iterator::STATE_HEADER,
@@ -36,7 +36,7 @@
 }
 
 BitstreamWriter::packet_iterator BitstreamWriter::iterator::packet_end() {
-	const ConfigurationPacket& packet = *itr_packets_;
+	const ConfigurationPacket& packet = **itr_packets_;
 
 	return BitstreamWriter::packet_iterator(
 	    &packet, BitstreamWriter::packet_iterator::STATE_END,
@@ -138,7 +138,7 @@
 	if (itr_packets != packets_.end()) {
 		// op_packet_itr = packet_begin();
 		// FIXME: de-duplicate this
-		const ConfigurationPacket& packet = *itr_packets;
+		const ConfigurationPacket& packet = **itr_packets;
 		packet_iterator packet_itr =
 		    packet_iterator(&packet, packet_iterator::STATE_HEADER,
 		                    packet.data().begin());
diff --git a/lib/xilinx/xc7series/bitstream_writer_test.cc b/lib/xilinx/xc7series/bitstream_writer_test.cc
index 752fa4c..be2d41e 100644
--- a/lib/xilinx/xc7series/bitstream_writer_test.cc
+++ b/lib/xilinx/xc7series/bitstream_writer_test.cc
@@ -50,7 +50,8 @@
 }
 
 // Special all 0's
-void AddType0(std::vector<xc7series::ConfigurationPacket>& packets) {
+void AddType0(
+    std::vector<std::unique_ptr<xc7series::ConfigurationPacket>>& packets) {
 	// InitWithWords doesn't like type 0
 	/*
 	static std::vector<uint32_t> words{0x00000000};
@@ -61,27 +62,32 @@
 	static std::vector<uint32_t> words{};
 	absl::Span<uint32_t> word_span(words);
 	// CRC is config value 0
-	packets.push_back(xc7series::ConfigurationPacket(
+	packets.emplace_back(new xc7series::ConfigurationPacket(
 	    0, xc7series::ConfigurationPacket::NOP,
 	    xc7series::ConfigurationRegister::CRC, word_span));
 }
 
-void AddType1(std::vector<xc7series::ConfigurationPacket>& packets) {
+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.push_back(*(packet.second));
+	packets.emplace_back(
+	    new xc7series::ConfigurationPacket(*(packet.second)));
 }
 
 // Empty
-void AddType1E(std::vector<xc7series::ConfigurationPacket>& packets) {
+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.push_back(*(packet.second));
+	packets.emplace_back(
+	    new xc7series::ConfigurationPacket(*(packet.second)));
 }
 
-void AddType2(std::vector<xc7series::ConfigurationPacket>& packets) {
+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;
@@ -90,8 +96,9 @@
 		absl::Span<uint32_t> word_span(words);
 		auto packet1_pair =
 		    xc7series::ConfigurationPacket::InitWithWords(word_span);
-		packets.push_back(*(packet1_pair.second));
-		packet1 = &packets[0];
+		packets.emplace_back(
+		    new xc7series::ConfigurationPacket(*(packet1_pair.second)));
+		packet1 = packets[0].get();
 	}
 	// Type 2 packet with data
 	{
@@ -100,13 +107,14 @@
 		absl::Span<uint32_t> word_span(words);
 		auto packet = xc7series::ConfigurationPacket::InitWithWords(
 		    word_span, packet1);
-		packets.push_back(*(packet.second));
+		packets.emplace_back(
+		    new xc7series::ConfigurationPacket(*(packet.second)));
 	}
 }
 
 // Empty packets should produce just the header
 TEST(BitstreamWriterTest, WriteHeader) {
-	std::vector<xc7series::ConfigurationPacket> packets;
+	std::vector<std::unique_ptr<xc7series::ConfigurationPacket>> packets;
 
 	xc7series::BitstreamWriter writer(packets);
 	std::vector<uint32_t> words(writer.begin(), writer.end());
@@ -120,7 +128,7 @@
 }
 
 TEST(BitstreamWriterTest, WriteType0) {
-	std::vector<xc7series::ConfigurationPacket> packets;
+	std::vector<std::unique_ptr<xc7series::ConfigurationPacket>> packets;
 	AddType0(packets);
 	xc7series::BitstreamWriter writer(packets);
 	// dump_packets(writer, false);
@@ -133,7 +141,7 @@
 	EXPECT_EQ(words, ref);
 }
 TEST(BitstreamWriterTest, WriteType1) {
-	std::vector<xc7series::ConfigurationPacket> packets;
+	std::vector<std::unique_ptr<xc7series::ConfigurationPacket>> packets;
 	AddType1(packets);
 	xc7series::BitstreamWriter writer(packets);
 	// dump_packets(writer, false);
@@ -147,7 +155,7 @@
 }
 
 TEST(BitstreamWriterTest, WriteType2) {
-	std::vector<xc7series::ConfigurationPacket> packets;
+	std::vector<std::unique_ptr<xc7series::ConfigurationPacket>> packets;
 	AddType2(packets);
 	xc7series::BitstreamWriter writer(packets);
 	// dump_packets(writer, false);
@@ -164,7 +172,7 @@
 }
 
 TEST(BitstreamWriterTest, WriteMulti) {
-	std::vector<xc7series::ConfigurationPacket> packets;
+	std::vector<std::unique_ptr<xc7series::ConfigurationPacket>> packets;
 	AddType1(packets);
 	AddType1E(packets);
 	AddType2(packets);
diff --git a/minitests/partial_reconfig_flow/Makefile b/minitests/partial_reconfig_flow/Makefile
index f451f81..239cac4 100644
--- a/minitests/partial_reconfig_flow/Makefile
+++ b/minitests/partial_reconfig_flow/Makefile
@@ -1,4 +1,4 @@
-.PRECIOUS: harness_impl.dcp %_impl.dcp %.bit
+.PRECIOUS: harness_impl.dcp %_impl.dcp %.bit %_roi_partial.bit
 
 # Top-level target for generating a programmable bitstream.  Given a .fasm
 # file, calling make with the .fasm extension replaced with _hand_crafted.bit
@@ -6,49 +6,14 @@
 # ready for programming to a board.  For example,
 # 'make inv_hand_crafted.bit' will generate a bitstream that includes the
 # design from roi_noninv.fasm. 
-%_hand_crafted.bit: init_sequence.bit %_no_headers.bin final_sequence.bin
-	cat $^ > $@
-
-%_no_headers.bin: %_patched.bin
-	# WARNING: these values need to be tweaked if anything about the
-	# Vivado-generated design changes.
-	xxd -p -s 0x18 $< | xxd -r -p - $@
-
-%_patched.bin: %_roi_partial.frm harness.bit
+%_hand_crafted.bit: %_roi_partial.frm harness.bit
 	${XRAY_TOOLS_DIR}/xc7patch \
-		--part_file ${XRAY_PART_YAML} \
+		--part_name "${XRAY_PART}" \
+		--part_file "${XRAY_PART_YAML}" \
 		--bitstream_file harness.bit \
 		--frm_file $< \
 		--output_file $@
 
-# xc7patch currently only generates the actual frame writes which is
-# insufficient to program a device.  Grab the initialization and finalization
-# sequences from the harness bitstream so they can be tacked on to the
-# xc7patch-generated bitstream to create a programmable bitstream.
-#
-# The offsets used below were determined by manually inspecting
-# harness.bit with a hex editor.  init_sequence.bit is the beginning of
-# the file until just before the actual frame data is sent via a write to FDRI.
-# final_sequence.bin is from just after the frame data write to the end of the
-# file.  Note that final_sequence.bin normally includes at least one CRC check.
-# The sed command replaces any CRC checks with a Reset CRC command which is the
-# same behavior as setting BITSTREAM.GENERAL.CRC to Disabled.  These offset
-# should not change unless you alter the bitstream format used (i.e. setting
-# BITSTREAM.GENERAL.DEBUGBITSTREAM or BITSTREAM.GENERAL.PERFRAMECRC to YES).
-init_sequence.bit: harness.bit
-	# WARNING: these values need to be tweaked if anything about the
-	# Vivado-generated design changes.
-	xxd -p -l 0x147 $< | xxd -r -p - $@
-
-final_sequence.bin: harness.bit
-	# WARNING: these values need to be tweaked if anything about the
-	# Vivado-generated design changes.
-	xxd -p -s 0x216abf $< | \
-		tr -d '\n' | \
-		sed -e 's/30000001.\{8\}/3000800100000007/g' | \
-		fold -w 40 | \
-		xxd -r -p - $@
-
 # Generate a suitable harness by using Vivado's partial reconfiguration
 # feature.  inv.v is used as a sample reconfiguration design as one is
 # required to generate a partial reconfiguration design.
diff --git a/minitests/roi_harness/fasm2bit.sh b/minitests/roi_harness/fasm2bit.sh
index 3e7798c..5beb90f 100755
--- a/minitests/roi_harness/fasm2bit.sh
+++ b/minitests/roi_harness/fasm2bit.sh
@@ -30,28 +30,11 @@
 ${XRAY_DIR}/tools/fasm2frame.py $fasm_in roi_partial.frm
 
 ${XRAY_TOOLS_DIR}/xc7patch \
+	--part_name ${XRAY_PART} \
 	--part_file ${XRAY_PART_YAML} \
 	--bitstream_file $bit_in \
 	--frm_file roi_partial.frm \
-	--output_file patched.bin
-
-# WARNING: these values need to be tweaked if anything about the
-# Vivado-generated design changes.
-xxd -p -l 0x147 $bit_in | xxd -r -p - init_sequence.bit
-
-# WARNING: these values need to be tweaked if anything about the
-# Vivado-generated design changes.
-xxd -p -s 0x18 patched.bin | xxd -r -p - no_headers.bin
-
-# WARNING: these values need to be tweaked if anything about the
-# Vivado-generated design changes.
-xxd -p -s 0x216abf $bit_in | \
-	tr -d '\n' | \
-	sed -e 's/30000001.\{8\}/3000800100000007/g' | \
-	fold -w 40 | \
-	xxd -r -p - final_sequence.bin
-
-cat init_sequence.bit no_headers.bin final_sequence.bin >$bit_out
+	--output_file $bit_out
 
 #openocd -f $XRAY_DIR/utils/openocd/board-digilent-basys3.cfg -c "init; pld load 0 $bit_out; exit"
 
diff --git a/tools/CMakeLists.txt b/tools/CMakeLists.txt
index 06dc18c..85023a9 100644
--- a/tools/CMakeLists.txt
+++ b/tools/CMakeLists.txt
@@ -21,6 +21,7 @@
 add_executable(xc7patch xc7patch.cc)
 target_link_libraries(xc7patch
 	absl::strings
+	absl::time
 	gflags
 	libprjxray
 )
diff --git a/tools/xc7patch.cc b/tools/xc7patch.cc
index 3e0c549..9ea3962 100644
--- a/tools/xc7patch.cc
+++ b/tools/xc7patch.cc
@@ -5,14 +5,23 @@
 #include <string>
 #include <vector>
 
+#include <absl/strings/str_cat.h>
 #include <absl/strings/str_split.h>
+#include <absl/time/clock.h>
+#include <absl/time/time.h>
 #include <gflags/gflags.h>
 #include <prjxray/memory_mapped_file.h>
 #include <prjxray/xilinx/xc7series/bitstream_reader.h>
 #include <prjxray/xilinx/xc7series/bitstream_writer.h>
+#include <prjxray/xilinx/xc7series/command.h>
 #include <prjxray/xilinx/xc7series/configuration.h>
+#include <prjxray/xilinx/xc7series/configuration_options_0_value.h>
+#include <prjxray/xilinx/xc7series/configuration_packet_with_payload.h>
+#include <prjxray/xilinx/xc7series/ecc.h>
+#include <prjxray/xilinx/xc7series/nop_packet.h>
 #include <prjxray/xilinx/xc7series/part.h>
 
+DEFINE_string(part_name, "", "");
 DEFINE_string(part_file, "", "Definition file for target 7-series part");
 DEFINE_string(bitstream_file,
               "",
@@ -111,54 +120,16 @@
 
 		uint32_t ecc = 0;
 		for (size_t ii = 0; ii < frame_data.size(); ++ii) {
-			uint32_t word = frame_data[ii];
-			uint32_t offset = ii * 32;
-			if (ii > 0x25) {
-				offset += 0x1360;
-			} else if (ii > 0x6) {
-				offset += 0x1340;
-			} else {
-				offset += 0x1320;
-			}
-
-			// Mask out where the ECC should be.
-			if (ii == 0x32) {
-				word &= 0xFFFFE000;
-			}
-
-			for (int jj = 0; jj < 32; ++jj) {
-				if ((word & 1) == 1) {
-					ecc ^= offset + jj;
-				}
-				word >>= 1;
-			}
+			ecc = xc7series::icap_ecc(ii, frame_data[ii], ecc);
 		}
 
-		uint32_t v = ecc & 0xFFF;
-		v ^= v >> 8;
-		v ^= v >> 4;
-		v ^= v >> 2;
-		v ^= v >> 1;
-		ecc ^= (v & 1) << 12;
-
 		// Replace the old ECC with the new.
 		frame_data[0x32] &= 0xFFFFE000;
 		frame_data[0x32] |= (ecc & 0x1FFF);
 	}
 
-#if 0
-	for (auto& frame : frames) {
-		std::cout << "0x" << std::hex
-		          << static_cast<uint32_t>(frame.first) << " ";
-
-		for (auto& word : frame.second) {
-			std::cout << "0x" << std::hex << word << ",";
-		}
-
-		std::cout << std::endl;
-	}
-#endif
-	std::vector<xc7series::ConfigurationPacket> out_packets;
+	std::vector<std::unique_ptr<xc7series::ConfigurationPacket>>
+	    out_packets;
 
 	// Generate a single type 2 packet that writes everything at once.
 	std::vector<uint32_t> packet_data;
@@ -177,18 +148,167 @@
 	}
 	packet_data.insert(packet_data.end(), 202, 0);
 
-	out_packets.push_back(xc7series::ConfigurationPacket(
+	// Initialization sequence
+	out_packets.emplace_back(new xc7series::NopPacket());
+	out_packets.emplace_back(
+	    new xc7series::ConfigurationPacketWithPayload<1>(
+	        xc7series::ConfigurationPacket::Opcode::Write,
+	        xc7series::ConfigurationRegister::TIMER, {0x0}));
+	out_packets.emplace_back(
+	    new xc7series::ConfigurationPacketWithPayload<1>(
+	        xc7series::ConfigurationPacket::Opcode::Write,
+	        xc7series::ConfigurationRegister::WBSTAR, {0x0}));
+	out_packets.emplace_back(
+	    new xc7series::ConfigurationPacketWithPayload<1>(
+	        xc7series::ConfigurationPacket::Opcode::Write,
+	        xc7series::ConfigurationRegister::CMD,
+	        {static_cast<uint32_t>(xc7series::Command::NOP)}));
+	out_packets.emplace_back(new xc7series::NopPacket());
+	out_packets.emplace_back(
+	    new xc7series::ConfigurationPacketWithPayload<1>(
+	        xc7series::ConfigurationPacket::Opcode::Write,
+	        xc7series::ConfigurationRegister::CMD,
+	        {static_cast<uint32_t>(xc7series::Command::RCRC)}));
+	out_packets.emplace_back(new xc7series::NopPacket());
+	out_packets.emplace_back(new xc7series::NopPacket());
+	out_packets.emplace_back(
+	    new xc7series::ConfigurationPacketWithPayload<1>(
+	        xc7series::ConfigurationPacket::Opcode::Write,
+	        xc7series::ConfigurationRegister::UNKNOWN, {0x0}));
+
+	// Configuration Options 0
+	out_packets.emplace_back(new xc7series::ConfigurationPacketWithPayload<
+	                         1>(
+	    xc7series::ConfigurationPacket::Opcode::Write,
+	    xc7series::ConfigurationRegister::COR0,
+	    {xc7series::ConfigurationOptions0Value()
+	         .SetAddPipelineStageForDoneIn(true)
+	         .SetReleaseDonePinAtStartupCycle(
+	             xc7series::ConfigurationOptions0Value::SignalReleaseCycle::
+	                 Phase4)
+	         .SetStallAtStartupCycleUntilDciMatch(
+	             xc7series::ConfigurationOptions0Value::StallCycle::NoWait)
+	         .SetStallAtStartupCycleUntilMmcmLock(
+	             xc7series::ConfigurationOptions0Value::StallCycle::NoWait)
+	         .SetReleaseGtsSignalAtStartupCycle(
+	             xc7series::ConfigurationOptions0Value::SignalReleaseCycle::
+	                 Phase5)
+	         .SetReleaseGweSignalAtStartupCycle(
+	             xc7series::ConfigurationOptions0Value::SignalReleaseCycle::
+	                 Phase6)}));
+
+	out_packets.emplace_back(
+	    new xc7series::ConfigurationPacketWithPayload<1>(
+	        xc7series::ConfigurationPacket::Opcode::Write,
+	        xc7series::ConfigurationRegister::COR1, {0x0}));
+	out_packets.emplace_back(
+	    new xc7series::ConfigurationPacketWithPayload<1>(
+	        xc7series::ConfigurationPacket::Opcode::Write,
+	        xc7series::ConfigurationRegister::IDCODE, {part->idcode()}));
+	out_packets.emplace_back(
+	    new xc7series::ConfigurationPacketWithPayload<1>(
+	        xc7series::ConfigurationPacket::Opcode::Write,
+	        xc7series::ConfigurationRegister::CMD,
+	        {static_cast<uint32_t>(xc7series::Command::SWITCH)}));
+	out_packets.emplace_back(new xc7series::NopPacket());
+	out_packets.emplace_back(
+	    new xc7series::ConfigurationPacketWithPayload<1>(
+	        xc7series::ConfigurationPacket::Opcode::Write,
+	        xc7series::ConfigurationRegister::MASK, {0x401}));
+	out_packets.emplace_back(
+	    new xc7series::ConfigurationPacketWithPayload<1>(
+	        xc7series::ConfigurationPacket::Opcode::Write,
+	        xc7series::ConfigurationRegister::CTL0, {0x501}));
+	out_packets.emplace_back(
+	    new xc7series::ConfigurationPacketWithPayload<1>(
+	        xc7series::ConfigurationPacket::Opcode::Write,
+	        xc7series::ConfigurationRegister::MASK, {0x0}));
+	out_packets.emplace_back(
+	    new xc7series::ConfigurationPacketWithPayload<1>(
+	        xc7series::ConfigurationPacket::Opcode::Write,
+	        xc7series::ConfigurationRegister::CTL1, {0x0}));
+	out_packets.emplace_back(new xc7series::NopPacket());
+	out_packets.emplace_back(new xc7series::NopPacket());
+	out_packets.emplace_back(new xc7series::NopPacket());
+	out_packets.emplace_back(new xc7series::NopPacket());
+	out_packets.emplace_back(new xc7series::NopPacket());
+	out_packets.emplace_back(new xc7series::NopPacket());
+	out_packets.emplace_back(new xc7series::NopPacket());
+	out_packets.emplace_back(new xc7series::NopPacket());
+	out_packets.emplace_back(
+	    new xc7series::ConfigurationPacketWithPayload<1>(
+	        xc7series::ConfigurationPacket::Opcode::Write,
+	        xc7series::ConfigurationRegister::FAR, {0x0}));
+	out_packets.emplace_back(
+	    new xc7series::ConfigurationPacketWithPayload<1>(
+	        xc7series::ConfigurationPacket::Opcode::Write,
+	        xc7series::ConfigurationRegister::CMD,
+	        {static_cast<uint32_t>(xc7series::Command::WCFG)}));
+	out_packets.emplace_back(new xc7series::NopPacket());
+
+	// Frame data write
+	out_packets.emplace_back(new xc7series::ConfigurationPacket(
 	    1, xc7series::ConfigurationPacket::Opcode::Write,
 	    xc7series::ConfigurationRegister::FDRI, {}));
-	out_packets.push_back(xc7series::ConfigurationPacket(
+	out_packets.emplace_back(new xc7series::ConfigurationPacket(
 	    2, xc7series::ConfigurationPacket::Opcode::Write,
 	    xc7series::ConfigurationRegister::FDRI, packet_data));
 
-#if 0
-	for (auto& packet : out_packets) {
-		std::cout << packet << std::endl;
+	// Finalization sequence
+	out_packets.emplace_back(
+	    new xc7series::ConfigurationPacketWithPayload<1>(
+	        xc7series::ConfigurationPacket::Opcode::Write,
+	        xc7series::ConfigurationRegister::CMD,
+	        {static_cast<uint32_t>(xc7series::Command::RCRC)}));
+	out_packets.emplace_back(new xc7series::NopPacket());
+	out_packets.emplace_back(new xc7series::NopPacket());
+	out_packets.emplace_back(
+	    new xc7series::ConfigurationPacketWithPayload<1>(
+	        xc7series::ConfigurationPacket::Opcode::Write,
+	        xc7series::ConfigurationRegister::CMD,
+	        {static_cast<uint32_t>(xc7series::Command::GRESTORE)}));
+	out_packets.emplace_back(new xc7series::NopPacket());
+	out_packets.emplace_back(
+	    new xc7series::ConfigurationPacketWithPayload<1>(
+	        xc7series::ConfigurationPacket::Opcode::Write,
+	        xc7series::ConfigurationRegister::CMD,
+	        {static_cast<uint32_t>(xc7series::Command::LFRM)}));
+	for (int ii = 0; ii < 100; ++ii) {
+		out_packets.emplace_back(new xc7series::NopPacket());
 	}
-#endif
+	out_packets.emplace_back(
+	    new xc7series::ConfigurationPacketWithPayload<1>(
+	        xc7series::ConfigurationPacket::Opcode::Write,
+	        xc7series::ConfigurationRegister::CMD,
+	        {static_cast<uint32_t>(xc7series::Command::START)}));
+	out_packets.emplace_back(new xc7series::NopPacket());
+	out_packets.emplace_back(
+	    new xc7series::ConfigurationPacketWithPayload<1>(
+	        xc7series::ConfigurationPacket::Opcode::Write,
+	        xc7series::ConfigurationRegister::FAR, {0x3be0000}));
+	out_packets.emplace_back(
+	    new xc7series::ConfigurationPacketWithPayload<1>(
+	        xc7series::ConfigurationPacket::Opcode::Write,
+	        xc7series::ConfigurationRegister::MASK, {0x501}));
+	out_packets.emplace_back(
+	    new xc7series::ConfigurationPacketWithPayload<1>(
+	        xc7series::ConfigurationPacket::Opcode::Write,
+	        xc7series::ConfigurationRegister::CTL0, {0x501}));
+	out_packets.emplace_back(
+	    new xc7series::ConfigurationPacketWithPayload<1>(
+	        xc7series::ConfigurationPacket::Opcode::Write,
+	        xc7series::ConfigurationRegister::CMD,
+	        {static_cast<uint32_t>(xc7series::Command::RCRC)}));
+	out_packets.emplace_back(new xc7series::NopPacket());
+	out_packets.emplace_back(new xc7series::NopPacket());
+	out_packets.emplace_back(
+	    new xc7series::ConfigurationPacketWithPayload<1>(
+	        xc7series::ConfigurationPacket::Opcode::Write,
+	        xc7series::ConfigurationRegister::CMD,
+	        {static_cast<uint32_t>(xc7series::Command::DESYNC)}));
+	for (int ii = 0; ii < 400; ++ii) {
+		out_packets.emplace_back(new xc7series::NopPacket());
+	}
 
 	// Write bitstream.
 	xc7series::BitstreamWriter out_bitstream_writer(out_packets);
@@ -199,6 +319,61 @@
 		return 1;
 	}
 
+	// Xilinx BIT header.
+	// Sync header
+	std::vector<uint8_t> bit_header{0x0,  0x9,  0x0f, 0xf0, 0x0f,
+	                                0xf0, 0x0f, 0xf0, 0x0f, 0xf0,
+	                                0x00, 0x00, 0x01, 'a'};
+	auto build_source = absl::StrCat(FLAGS_frm_file, ";Generator=xc7patch");
+	bit_header.push_back(
+	    static_cast<uint8_t>((build_source.size() + 1) >> 8));
+	bit_header.push_back(static_cast<uint8_t>(build_source.size() + 1));
+	bit_header.insert(bit_header.end(), build_source.begin(),
+	                  build_source.end());
+	bit_header.push_back(0x0);
+
+	// Source file.
+	bit_header.push_back('b');
+	bit_header.push_back(
+	    static_cast<uint8_t>((FLAGS_part_name.size() + 1) >> 8));
+	bit_header.push_back(static_cast<uint8_t>(FLAGS_part_name.size() + 1));
+	bit_header.insert(bit_header.end(), FLAGS_part_name.begin(),
+	                  FLAGS_part_name.end());
+	bit_header.push_back(0x0);
+
+	// Build timestamp.
+	auto build_time = absl::Now();
+	auto build_date_string =
+	    absl::FormatTime("%E4Y/%m/%d", build_time, absl::UTCTimeZone());
+	auto build_time_string =
+	    absl::FormatTime("%H:%M:%S", build_time, absl::UTCTimeZone());
+
+	bit_header.push_back('c');
+	bit_header.push_back(
+	    static_cast<uint8_t>((build_date_string.size() + 1) >> 8));
+	bit_header.push_back(
+	    static_cast<uint8_t>(build_date_string.size() + 1));
+	bit_header.insert(bit_header.end(), build_date_string.begin(),
+	                  build_date_string.end());
+	bit_header.push_back(0x0);
+
+	bit_header.push_back('d');
+	bit_header.push_back(
+	    static_cast<uint8_t>((build_time_string.size() + 1) >> 8));
+	bit_header.push_back(
+	    static_cast<uint8_t>(build_time_string.size() + 1));
+	bit_header.insert(bit_header.end(), build_time_string.begin(),
+	                  build_time_string.end());
+	bit_header.push_back(0x0);
+
+	bit_header.insert(bit_header.end(), {'e', 0x0, 0x0, 0x0, 0x0});
+	out_file.write(reinterpret_cast<const char*>(bit_header.data()),
+	               bit_header.size());
+
+	auto end_of_header_pos = out_file.tellp();
+	auto header_data_length_pos =
+	    end_of_header_pos - static_cast<std::ofstream::off_type>(4);
+
 	for (uint32_t word : out_bitstream_writer) {
 		out_file.put((word >> 24) & 0xFF);
 		out_file.put((word >> 16) & 0xFF);
@@ -206,5 +381,13 @@
 		out_file.put((word)&0xFF);
 	}
 
+	uint32_t length_of_data = out_file.tellp() - end_of_header_pos;
+
+	out_file.seekp(header_data_length_pos);
+	out_file.put((length_of_data >> 24) & 0xFF);
+	out_file.put((length_of_data >> 16) & 0xFF);
+	out_file.put((length_of_data >> 8) & 0xFF);
+	out_file.put((length_of_data)&0xFF);
+
 	return 0;
 }