Merge pull request #71 from mcmasterg/demoscene

roi_harness Basys3 demo
diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt
index 4b751d6..bc53f61 100644
--- a/lib/CMakeLists.txt
+++ b/lib/CMakeLists.txt
@@ -45,12 +45,14 @@
 		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
 		xilinx/xc7series/configuration_column_test.cc
 		xilinx/xc7series/configuration_test.cc
 		xilinx/xc7series/configuration_packet_test.cc
 		xilinx/xc7series/configuration_packetizer_test.cc
+		xilinx/xc7series/crc_test.cc
+		xilinx/xc7series/ecc_test.cc
+		xilinx/xc7series/frame_address_test.cc
 		xilinx/xc7series/global_clock_region_test.cc
 		xilinx/xc7series/part_test.cc
 		xilinx/xc7series/row_test.cc)
diff --git a/lib/include/prjxray/xilinx/xc7series/crc.h b/lib/include/prjxray/xilinx/xc7series/crc.h
new file mode 100644
index 0000000..df6bcac
--- /dev/null
+++ b/lib/include/prjxray/xilinx/xc7series/crc.h
@@ -0,0 +1,40 @@
+#ifndef PRJXRAY_LIB_XILINX_XC7SERIES_CRC_H_
+#define PRJXRAY_LIB_XILINX_XC7SERIES_CRC_H_
+
+#include <cstdint>
+
+constexpr uint32_t kCrc32CastagnoliPolynomial = 0x82F63B78;
+
+namespace prjxray {
+namespace xilinx {
+namespace xc7series {
+
+// The CRC is calculated from each written data word and the current
+// register address the data is written to.
+
+// Extend the current CRC value with one register address (5bit) and
+// frame data (32bit) pair and return the newly computed CRC value.
+
+uint32_t icap_crc(uint32_t addr, uint32_t data, uint32_t prev) {
+	constexpr int kAddressBitWidth = 5;
+	constexpr int kDataBitWidth = 32;
+
+	uint64_t poly = static_cast<uint64_t>(kCrc32CastagnoliPolynomial) << 1;
+	uint64_t val = (static_cast<uint64_t>(addr) << 32) | data;
+	uint64_t crc = prev;
+
+	for (int i = 0; i < kAddressBitWidth + kDataBitWidth; i++) {
+		if ((val & 1) != (crc & 1))
+			crc ^= poly;
+
+		val >>= 1;
+		crc >>= 1;
+	}
+	return crc;
+}
+
+}  // namespace xc7series
+}  // namespace xilinx
+}  // namespace prjxray
+
+#endif  // PRJXRAY_LIB_XILINX_XC7SERIES_CRC_H_
diff --git a/lib/include/prjxray/xilinx/xc7series/ecc.h b/lib/include/prjxray/xilinx/xc7series/ecc.h
new file mode 100644
index 0000000..6abab63
--- /dev/null
+++ b/lib/include/prjxray/xilinx/xc7series/ecc.h
@@ -0,0 +1,49 @@
+#ifndef PRJXRAY_LIB_XILINX_XC7SERIES_ECC_H_
+#define PRJXRAY_LIB_XILINX_XC7SERIES_ECC_H_
+
+#include <cstdint>
+
+namespace prjxray {
+namespace xilinx {
+namespace xc7series {
+
+// Extend the current ECC code with one data word (32 bit) at a given
+// word index in the configuration frame and return the new ECC code.
+
+uint32_t icap_ecc(uint32_t idx, uint32_t data, uint32_t ecc) {
+	uint32_t val = idx * 32;  // bit offset
+
+	if (idx > 0x25)  // avoid 0x800
+		val += 0x1360;
+	else if (idx > 0x6)  // avoid 0x400
+		val += 0x1340;
+	else  // avoid lower
+		val += 0x1320;
+
+	if (idx == 0x32)  // mask ECC
+		data &= 0xFFFFE000;
+
+	for (int i = 0; i < 32; i++) {
+		if (data & 1)
+			ecc ^= val + i;
+
+		data >>= 1;
+	}
+
+	if (idx == 0x64) {  // last index
+		uint32_t v = ecc & 0xFFF;
+		v ^= v >> 8;
+		v ^= v >> 4;
+		v ^= v >> 2;
+		v ^= v >> 1;
+		ecc ^= (v & 1) << 12;  // parity
+	}
+
+	return ecc;
+}
+
+}  // namespace xc7series
+}  // namespace xilinx
+}  // namespace prjxray
+
+#endif  // PRJXRAY_LIB_XILINX_XC7SERIES_ECC_H_
diff --git a/lib/xilinx/xc7series/crc_test.cc b/lib/xilinx/xc7series/crc_test.cc
new file mode 100644
index 0000000..4e83367
--- /dev/null
+++ b/lib/xilinx/xc7series/crc_test.cc
@@ -0,0 +1,16 @@
+#include <prjxray/xilinx/xc7series/crc.h>
+
+#include <gtest/gtest.h>
+
+namespace xc7series = prjxray::xilinx::xc7series;
+
+TEST(IcapCrcTest, SimpleTests) {
+	// CRC for Zero Data
+	EXPECT_EQ(xc7series::icap_crc(0, 0, 0), 0x0L);
+	// Polynomial (single bit operation)
+	EXPECT_EQ(xc7series::icap_crc(1 << 4, 0, 0), 0x82F63B78);
+	// All Reg/Data bits
+	EXPECT_EQ(xc7series::icap_crc(~0, ~0, 0), 0xBF86D4DF);
+	// All CRC bits
+	EXPECT_EQ(xc7series::icap_crc(0, 0, ~0), 0xC631E365);
+}
diff --git a/lib/xilinx/xc7series/ecc_test.cc b/lib/xilinx/xc7series/ecc_test.cc
new file mode 100644
index 0000000..ae08cc8
--- /dev/null
+++ b/lib/xilinx/xc7series/ecc_test.cc
@@ -0,0 +1,20 @@
+#include <prjxray/xilinx/xc7series/ecc.h>
+
+#include <gtest/gtest.h>
+
+namespace xc7series = prjxray::xilinx::xc7series;
+
+TEST(IcapEccTest, SimpleTests) {
+	// ECC for Zero Data
+	EXPECT_EQ(xc7series::icap_ecc(0, 0, 0), (uint32_t)0x0);
+	// 0x1320 - 0x13FF (avoid lower)
+	EXPECT_EQ(xc7series::icap_ecc(0, 1, 0), (uint32_t)0x1320);
+	// 0x1420 - 0x17FF (avoid 0x400)
+	EXPECT_EQ(xc7series::icap_ecc(0x7, 1, 0), (uint32_t)0x1420);
+	// 0x1820 - 0x1FFF (avoid 0x800)
+	EXPECT_EQ(xc7series::icap_ecc(0x26, 1, 0), (uint32_t)0x1820);
+	// Masked ECC Value
+	EXPECT_EQ(xc7series::icap_ecc(0x32, ~0, 0), (uint32_t)0x000019AC);
+	// Final ECC Parity
+	EXPECT_EQ(xc7series::icap_ecc(0x64, 0, 1), (uint32_t)0x00001001);
+}