Initial version of SymbiFlow tools
Signed-off-by: Tomasz Michalak <tmichalak@antmicro.com>
diff --git a/.gitmodules b/.gitmodules
new file mode 100644
index 0000000..5c032ba
--- /dev/null
+++ b/.gitmodules
@@ -0,0 +1,24 @@
+[submodule "third_party/abseil-cpp"]
+ path = third_party/abseil-cpp
+ url = https://github.com/abseil/abseil-cpp
+[submodule "third_party/cctz"]
+ path = third_party/cctz
+ url = https://github.com/google/cctz
+[submodule "third_party/googletest"]
+ path = third_party/googletest
+ url = https://github.com/google/googletest
+[submodule "third_party/gflags"]
+ path = third_party/gflags
+ url = https://github.com/gflags/gflags
+[submodule "third_party/yaml-cpp"]
+ path = third_party/yaml-cpp
+ url = https://github.com/jbeder/yaml-cpp.git
+[submodule "third_party/python-sdf-timing"]
+ path = third_party/python-sdf-timing
+ url = https://github.com/SymbiFlow/python-sdf-timing.git
+[submodule "third_party/yosys"]
+ path = third_party/yosys
+ url = https://github.com/YosysHQ/yosys
+[submodule "third_party/sanitizers-cmake"]
+ path = third_party/sanitizers-cmake
+ url = https://github.com/arsenm/sanitizers-cmake.git
diff --git a/CMakeLists.txt b/CMakeLists.txt
new file mode 100644
index 0000000..3ec89c6
--- /dev/null
+++ b/CMakeLists.txt
@@ -0,0 +1,44 @@
+cmake_minimum_required(VERSION 3.5.0)
+
+project(prjxray)
+option(PRJXRAY_BUILD_TESTING "" OFF)
+
+# Add sanitizers-cmake package
+set(CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/third_party/sanitizers-cmake/cmake" ${CMAKE_MODULE_PATH})
+find_package(Sanitizers)
+if(NOT CMAKE_BUILD_TYPE)
+ set(CMAKE_BUILD_TYPE Release CACHE STRING
+ "Choose the type of build, options are: None Debug Release RelWithDebInfo MinSizeRel."
+ FORCE)
+endif()
+
+# Hack for missing option in cctz
+option(BUILD_TESTING "" OFF)
+
+if(PRJXRAY_BUILD_TESTING)
+ enable_testing()
+endif()
+
+add_subdirectory(third_party/googletest EXCLUDE_FROM_ALL)
+add_subdirectory(third_party/gflags EXCLUDE_FROM_ALL)
+add_subdirectory(third_party/cctz EXCLUDE_FROM_ALL)
+add_subdirectory(third_party/abseil-cpp EXCLUDE_FROM_ALL)
+
+include(CheckCXXCompilerFlag)
+CHECK_CXX_COMPILER_FLAG("-Wundefined-var-template" CXX_COMPILER_SUPPORTS_UNDEF_VAR)
+if(${CXX_COMPILER_SUPPORTS_UNDEF_VAR})
+ add_compile_options("-Wno-undefined-var-template")
+endif()
+
+option(YAML_CPP_BUILD_TESTS "" OFF)
+add_subdirectory(third_party/yaml-cpp EXCLUDE_FROM_ALL)
+target_include_directories(yaml-cpp PUBLIC
+ $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/third_party/yaml-cpp/include>
+ )
+
+# Set the CXX standard and compile time for our code only.
+set(CMAKE_CXX_STANDARD 14)
+add_compile_options(-Wall -Werror)
+
+add_subdirectory(lib)
+add_subdirectory(tools)
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..03cae06
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,187 @@
+SHELL = bash
+ALL_EXCLUDE = third_party .git env build
+
+# Check if root
+ifeq ($(shell id -u),0)
+ $(error ERROR: Running as ID 0)
+endif
+
+# Tools + Environment
+IN_ENV = if [ -e env/bin/activate ]; then . env/bin/activate; fi;
+env:
+ virtualenv --python=python3 env
+ # Install prjxray
+ ln -sf $(PWD)/prjxray env/lib/python3.*/site-packages/
+ $(IN_ENV) python -c "import prjxray"
+ # Install fasm from third_party
+ $(IN_ENV) pip install --upgrade -e third_party/fasm
+ # Install sdfparse form third party
+ $(IN_ENV) pip install --upgrade -e third_party/python-sdf-timing
+ # Install project dependencies
+ $(IN_ENV) pip install -r requirements.txt
+ # Install project's documentation dependencies
+ $(IN_ENV) pip install -r docs/requirements.txt
+ # Check fasm library was installed
+ $(IN_ENV) python -c "import fasm"
+ $(IN_ENV) python -c "import fasm.output"
+ # Check sdfparse lib was installed
+ $(IN_ENV) python -c "import sdf_timing"
+ $(IN_ENV) python -c "import sdf_timing.sdfparse"
+ # Check YAML is installed
+ $(IN_ENV) python -c "import yaml" || (echo "Unable to find python-yaml" && exit 1)
+
+build:
+ git submodule update --init --recursive
+ mkdir -p build
+ cd build; cmake ..; $(MAKE)
+
+.PHONY: env build
+
+# Run tests of code.
+# ------------------------
+TEST_EXCLUDE = $(foreach x,$(ALL_EXCLUDE) docs fuzzers minitests experiments,--ignore $(x))
+
+test: test-py test-cpp
+ @true
+
+test-py:
+ $(IN_ENV) which py.test; py.test $(TEST_EXCLUDE) --doctest-modules --junitxml=build/py_test_results.xml
+
+test-cpp:
+ mkdir -p build
+ cd build && cmake -DPRJXRAY_BUILD_TESTING=ON ..
+ cd build && $(MAKE) -s
+ cd build && ctest --no-compress-output -T Test -C RelWithDebInfo --output-on-failure
+ xsltproc .github/kokoro/ctest2junit.xsl build/Testing/*/Test.xml > build/cpp_test_results.xml
+
+.PHONY: test test-py test-cpp
+
+# Auto formatting of code.
+# ------------------------
+FORMAT_EXCLUDE = $(foreach x,$(ALL_EXCLUDE),-and -not -path './$(x)/*') -and -not -name *.bit
+
+CLANG_FORMAT ?= clang-format-5.0
+format-cpp:
+ find . -name \*.cc $(FORMAT_EXCLUDE) -print0 | xargs -0 -P $$(nproc) ${CLANG_FORMAT} -style=file -i
+ find . -name \*.h $(FORMAT_EXCLUDE) -print0 | xargs -0 -P $$(nproc) ${CLANG_FORMAT} -style=file -i
+
+format-docs:
+ ./.github/update-contributing.py
+
+PYTHON_FORMAT ?= yapf
+format-py:
+ $(IN_ENV) find . -name \*.py $(FORMAT_EXCLUDE) -print0 | xargs -0 -P $$(nproc) yapf -p -i
+
+TCL_FORMAT ?= utils//tcl-reformat.sh
+format-tcl:
+ find . -name \*.tcl $(FORMAT_EXCLUDE) -print0 | xargs -0 -P $$(nproc) -n 1 $(TCL_FORMAT)
+
+# Command to find and replace trailing whitespace in-place using `sed` (This is
+# placed inside quotes later so need to escape the "'")
+WS_CMD = sed -i '\''s@\s\+$$@@g'\''
+
+# File filter for files to fix trailing whitespace in, this is just a couple of
+# chained bash conditionals ensuring that the file (indicated by {}, provided by
+# xargs later) is a file, and not a directory or link. Also filters out .bit
+# files as these are the only binary files currently tracked by Git and we don't
+# want to inadvertently change these at all.
+WS_FILTER = [ -f {} -a ! -L {} ] && [[ {} != *.bit ]]
+
+# For every file piped to $(WS_FORMAT) apply the filter and perform the command,
+# if a file does not match the filter, just returns true.
+WS_FORMAT = xargs -P $$(nproc) -n 1 -I{} bash -c '$(WS_FILTER) && $(WS_CMD) {} || true'
+
+format-trailing-ws:
+ # Use `git ls-files` to give us a complete list of tracked files to fix
+ # whitespace in; there is no point spending time processing anything that is
+ # not known to Git.
+ git ls-files | $(WS_FORMAT)
+
+ # Additionally fix untracked (but not ignored) files.
+ git ls-files -o --exclude-standard | $(WS_FORMAT)
+
+format: format-cpp format-docs format-py format-tcl format-trailing-ws
+ @true
+
+.PHONY: format format-cpp format-py format-tcl format-trailing-ws
+
+# Targets related to Project X-Ray databases
+# ------------------------
+
+DATABASES=artix7 kintex7 zynq7
+
+define database
+
+# $(1) - Database name
+
+db-check-$(1):
+ @echo
+ @echo "Checking $(1) database"
+ @echo "============================"
+ @$(IN_ENV) python3 utils/checkdb.py --db-root database/$(1)
+
+db-format-$(1):
+ @echo
+ @echo "Formatting $(1) database"
+ @echo "============================"
+ @$(IN_ENV) cd database/$(1); python3 ../../utils/sort_db.py
+ @if [ -e database/Info.md ]; then $(IN_ENV) ./utils/info_md.py --keep; fi
+
+.PHONY: db-check-$(1) db-format-$(1)
+.NOTPARALLEL: db-check-$(1) db-format-$(1)
+
+db-check: db-check-$(1)
+db-format: db-format-$(1)
+
+endef
+
+$(foreach DB,$(DATABASES),$(eval $(call database,$(DB))))
+
+.PHONY: db-extras-artix7 db-extras-kintex7 db-extras-zynq7
+
+db-extras-artix7:
+ +source minitests/roi_harness/basys3-swbut.sh && $(MAKE) -C fuzzers part_only
+ +source minitests/roi_harness/arty-uart.sh && $(MAKE) -C fuzzers part_only
+ +source minitests/roi_harness/basys3-swbut.sh && \
+ $(MAKE) -C minitests/roi_harness \
+ HARNESS_DIR=$(XRAY_DATABASE_DIR)/artix7/harness/basys3/swbut run copy
+ +source minitests/roi_harness/basys3-swbut.sh && \
+ $(MAKE) -C minitests/roi_harness \
+ XRAY_ROIV=../roi_base_div2.v \
+ HARNESS_DIR=$(XRAY_DATABASE_DIR)/artix7/harness/basys3/swbut_50 run copy
+ +source minitests/roi_harness/arty-uart.sh && \
+ $(MAKE) -C minitests/roi_harness \
+ HARNESS_DIR=$(XRAY_DATABASE_DIR)/artix7/harness/arty-a7/uart run copy
+ +source minitests/roi_harness/arty-pmod.sh && \
+ $(MAKE) -C minitests/roi_harness \
+ HARNESS_DIR=$(XRAY_DATABASE_DIR)/artix7/harness/arty-a7/pmod run copy
+ +source minitests/roi_harness/arty-swbut.sh && \
+ $(MAKE) -C minitests/roi_harness \
+ HARNESS_DIR=$(XRAY_DATABASE_DIR)/artix7/harness/arty-a7/swbut run copy
+
+db-extras-kintex7:
+ @true
+
+db-extras-zynq7:
+ +source minitests/roi_harness/zybo-swbut.sh && $(MAKE) -C fuzzers part_only
+ +source minitests/roi_harness/zybo-swbut.sh && \
+ $(MAKE) -C minitests/roi_harness \
+ HARNESS_DIR=$(XRAY_DATABASE_DIR)/zynq7/harness/zybo/swbut run
+
+db-check:
+ @true
+
+db-format:
+ @true
+
+db-info:
+ $(IN_ENV) ./utils/info_md.py
+
+.PHONY: db-check db-format
+
+clean:
+ $(MAKE) -C database clean
+ $(MAKE) -C fuzzers clean
+ rm -rf build
+
+.PHONY: clean
diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt
new file mode 100644
index 0000000..3edcf3c
--- /dev/null
+++ b/lib/CMakeLists.txt
@@ -0,0 +1,91 @@
+add_library(libprjxray
+ database.cc
+ memory_mapped_file.cc
+ segbits_file_reader.cc
+ xilinx/bitstream_writer.cc
+ xilinx/configuration_packet.cc
+ xilinx/configuration_register.cc
+ xilinx/configuration.cc
+ xilinx/frames.cc
+ # Spartan6 specific
+ xilinx/spartan6/frame_address.cc
+ xilinx/spartan6/global_clock_region.cc
+ xilinx/spartan6/part.cc
+ xilinx/spartan6/configuration_row.cc
+ xilinx/spartan6/block_type.cc
+ xilinx/spartan6/configuration_bus.cc
+ xilinx/spartan6/configuration_column.cc
+ # Series-7 specific
+ xilinx/xc7series/frame_address.cc
+ xilinx/xc7series/global_clock_region.cc
+ xilinx/xc7series/part.cc
+ xilinx/xc7series/configuration_row.cc
+ xilinx/xc7series/block_type.cc
+ xilinx/xc7series/configuration_bus.cc
+ xilinx/xc7series/configuration_column.cc
+ xilinx/xc7series/ecc.cc
+)
+target_include_directories(libprjxray PUBLIC "include")
+target_link_libraries(libprjxray absl::optional absl::variant absl::strings absl::span absl::time yaml-cpp)
+
+if (PRJXRAY_BUILD_TESTING)
+ add_executable(big_endian_span_test big_endian_span_test.cc)
+ target_link_libraries(big_endian_span_test libprjxray gtest_main)
+ add_test(NAME big_endian_span_test
+ COMMAND big_endian_span_test)
+
+ add_executable(bit_ops_test bit_ops_test.cc)
+ target_link_libraries(bit_ops_test libprjxray gtest_main)
+ add_test(NAME bit_ops_test
+ COMMAND bit_ops_test)
+
+ add_executable(memory_mapped_file_test memory_mapped_file_test.cc)
+ target_link_libraries(memory_mapped_file_test libprjxray gtest_main)
+ add_test(NAME memory_mapped_file_test
+ COMMAND memory_mapped_file_test
+ WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/test_data)
+
+ add_executable(segbits_file_reader_test segbits_file_reader_test.cc)
+ target_link_libraries(segbits_file_reader_test libprjxray gtest_main)
+ add_test(NAME segbits_file_reader_test
+ COMMAND segbits_file_reader_test
+ WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/test_data)
+
+ add_executable(xilinx_xc7series_test
+ xilinx/tests/xc7series/bitstream_reader_test.cc
+ xilinx/tests/xc7series/bitstream_writer_test.cc
+ xilinx/tests/xc7series/block_type_test.cc
+ xilinx/tests/xc7series/configuration_bus_test.cc
+ xilinx/tests/xc7series/configuration_column_test.cc
+ xilinx/tests/xc7series/configuration_test.cc
+ xilinx/tests/xc7series/configuration_packet_test.cc
+ xilinx/tests/xc7series/crc_test.cc
+ xilinx/tests/xc7series/ecc_test.cc
+ xilinx/tests/xc7series/frame_address_test.cc
+ xilinx/tests/xc7series/global_clock_region_test.cc
+ xilinx/tests/xc7series/part_test.cc
+ xilinx/tests/xc7series/row_test.cc
+ xilinx/tests/xc7series/frames_test.cc)
+ target_link_libraries(xilinx_xc7series_test libprjxray gtest_main)
+ add_test(NAME xilinx_xc7series_test
+ COMMAND xilinx_xc7series_test
+ WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/test_data)
+
+ add_executable(xilinx_spartan6_test
+ xilinx/tests/spartan6/bitstream_reader_test.cc
+ xilinx/tests/spartan6/bitstream_writer_test.cc
+ xilinx/tests/spartan6/block_type_test.cc
+ xilinx/tests/spartan6/configuration_bus_test.cc
+ xilinx/tests/spartan6/configuration_column_test.cc
+ xilinx/tests/spartan6/configuration_test.cc
+ xilinx/tests/spartan6/configuration_packet_test.cc
+ xilinx/tests/spartan6/frame_address_test.cc
+ xilinx/tests/spartan6/global_clock_region_test.cc
+ xilinx/tests/spartan6/part_test.cc
+ xilinx/tests/spartan6/row_test.cc
+ xilinx/tests/spartan6/frames_test.cc)
+ target_link_libraries(xilinx_spartan6_test libprjxray gtest_main)
+ add_test(NAME xilinx_spartan6_test
+ COMMAND xilinx_spartan6_test
+ WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/test_data)
+endif()
diff --git a/lib/big_endian_span_test.cc b/lib/big_endian_span_test.cc
new file mode 100644
index 0000000..dcc673a
--- /dev/null
+++ b/lib/big_endian_span_test.cc
@@ -0,0 +1,42 @@
+#include <prjxray/big_endian_span.h>
+
+#include <cstdint>
+#include <vector>
+
+#include <gtest/gtest.h>
+
+TEST(BigEndianSpanTest, Read32WithEmptySpan) {
+ std::vector<uint8_t> bytes;
+ auto words = prjxray::make_big_endian_span<uint32_t>(bytes);
+ EXPECT_EQ(words.size(), static_cast<size_t>(0));
+}
+
+TEST(BigEndianSpanTest, Read32WithTooFewBytes) {
+ std::vector<uint8_t> bytes{0x0, 0x1, 0x2};
+ auto words = prjxray::make_big_endian_span<uint32_t>(bytes);
+ EXPECT_EQ(words.size(), static_cast<size_t>(0));
+}
+
+TEST(BigEndianSpanTest, Read32WithExactBytes) {
+ std::vector<uint8_t> bytes{0x0, 0x1, 0x2, 0x3};
+ auto words = prjxray::make_big_endian_span<uint32_t>(bytes);
+ ASSERT_EQ(words.size(), static_cast<size_t>(1));
+ EXPECT_EQ(words[0], static_cast<uint32_t>(0x00010203));
+}
+
+TEST(BigEndianSpanTest, Read32WithMultipleWords) {
+ std::vector<uint8_t> bytes{0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7};
+ auto words = prjxray::make_big_endian_span<uint32_t>(bytes);
+ ASSERT_EQ(words.size(), static_cast<size_t>(2));
+ EXPECT_EQ(words[0], static_cast<uint32_t>(0x00010203));
+ EXPECT_EQ(words[1], static_cast<uint32_t>(0x04050607));
+}
+
+TEST(BigEndianSpanTest, Write32) {
+ std::vector<uint8_t> bytes{0x0, 0x1, 0x2, 0x3};
+ auto words = prjxray::make_big_endian_span<uint32_t>(bytes);
+ words[0] = 0x04050607;
+
+ std::vector<uint8_t> expected{0x4, 0x5, 0x6, 0x7};
+ EXPECT_EQ(bytes, expected);
+}
diff --git a/lib/bit_ops_test.cc b/lib/bit_ops_test.cc
new file mode 100644
index 0000000..1837778
--- /dev/null
+++ b/lib/bit_ops_test.cc
@@ -0,0 +1,67 @@
+#include <prjxray/bit_ops.h>
+
+#include <gtest/gtest.h>
+
+TEST(BitMaskTest, Bit0) {
+ uint32_t expected = prjxray::bit_mask<uint32_t>(0);
+ EXPECT_EQ(static_cast<uint32_t>(0x1), expected);
+}
+
+TEST(BitMaskTest, Bit3) {
+ uint32_t expected = prjxray::bit_mask<uint32_t>(3);
+ EXPECT_EQ(static_cast<uint32_t>(0x8), expected);
+}
+
+TEST(BitMaskRange, SingleBit) {
+ uint32_t expected = prjxray::bit_mask_range<uint32_t>(23, 23);
+ EXPECT_EQ(static_cast<uint32_t>(0x800000), expected);
+}
+
+TEST(BitMaskRange, DownToZero) {
+ uint32_t expected = prjxray::bit_mask_range<uint32_t>(7, 0);
+ EXPECT_EQ(static_cast<uint32_t>(0xFF), expected);
+}
+
+TEST(BitMaskRange, MiddleBits) {
+ uint32_t expected = prjxray::bit_mask_range<uint32_t>(18, 8);
+ EXPECT_EQ(static_cast<uint32_t>(0x7FF00), expected);
+}
+
+TEST(BitFieldGetTest, OneSelectedBit) {
+ uint32_t expected = prjxray::bit_field_get(0xFFFFFFFF, 23, 23);
+ EXPECT_EQ(static_cast<uint32_t>(1), expected);
+}
+
+TEST(BitFieldGetTest, SelectDownToZero) {
+ uint32_t expected = prjxray::bit_field_get(0xFFCCBBAA, 7, 0);
+ EXPECT_EQ(static_cast<uint32_t>(0xAA), expected);
+}
+
+TEST(BitFieldGetTest, SelectMidway) {
+ uint32_t expected = prjxray::bit_field_get(0xFFCCBBAA, 18, 8);
+ EXPECT_EQ(static_cast<uint32_t>(0x4BB), expected);
+}
+
+TEST(BitFieldSetTest, WriteOneBit) {
+ uint32_t actual = prjxray::bit_field_set(
+ static_cast<uint32_t>(0x0), 23, 23, static_cast<uint32_t>(0x1));
+ EXPECT_EQ(actual, static_cast<uint32_t>(0x800000));
+}
+
+TEST(BitFieldSetTest, WriteOneBitWithOutOfRangeValue) {
+ uint32_t actual = prjxray::bit_field_set(
+ static_cast<uint32_t>(0x0), 23, 23, static_cast<uint32_t>(0x3));
+ EXPECT_EQ(actual, static_cast<uint32_t>(0x800000));
+}
+
+TEST(BitFieldSetTest, WriteMultipleBits) {
+ uint32_t actual = prjxray::bit_field_set(
+ static_cast<uint32_t>(0x0), 18, 8, static_cast<uint32_t>(0x123));
+ EXPECT_EQ(actual, static_cast<uint32_t>(0x12300));
+}
+
+TEST(BitFieldSetTest, WriteMultipleBitsWithOutOfRangeValue) {
+ uint32_t actual = prjxray::bit_field_set(
+ static_cast<uint32_t>(0x0), 18, 8, static_cast<uint32_t>(0x1234));
+ EXPECT_EQ(actual, static_cast<uint32_t>(0x23400));
+}
diff --git a/lib/database.cc b/lib/database.cc
new file mode 100644
index 0000000..6fac62d
--- /dev/null
+++ b/lib/database.cc
@@ -0,0 +1,37 @@
+#include <prjxray/database.h>
+
+#include <glob.h>
+
+#include <memory>
+
+#include <absl/strings/str_cat.h>
+
+namespace prjxray {
+
+static constexpr const char kSegbitsGlobPattern[] = "segbits_*.db";
+
+std::vector<std::unique_ptr<prjxray::SegbitsFileReader>> Database::segbits()
+ const {
+ std::vector<std::unique_ptr<prjxray::SegbitsFileReader>> segbits;
+
+ glob_t segbits_glob_results;
+ int ret = glob(absl::StrCat(db_path_, "/", kSegbitsGlobPattern).c_str(),
+ GLOB_NOSORT | GLOB_TILDE, NULL, &segbits_glob_results);
+ if (ret < 0) {
+ return {};
+ }
+
+ for (size_t idx = 0; idx < segbits_glob_results.gl_pathc; idx++) {
+ auto this_segbit = SegbitsFileReader::InitWithFile(
+ segbits_glob_results.gl_pathv[idx]);
+ if (this_segbit) {
+ segbits.emplace_back(std::move(this_segbit));
+ }
+ }
+
+ globfree(&segbits_glob_results);
+
+ return segbits;
+}
+
+} // namespace prjxray
diff --git a/lib/include/prjxray/big_endian_span.h b/lib/include/prjxray/big_endian_span.h
new file mode 100644
index 0000000..683f9c3
--- /dev/null
+++ b/lib/include/prjxray/big_endian_span.h
@@ -0,0 +1,115 @@
+#ifndef PRJXRAY_LIB_BIG_ENDIAN_SPAN
+#define PRJXRAY_LIB_BIG_ENDIAN_SPAN
+
+#include <cassert>
+#include <iterator>
+
+#include <absl/types/span.h>
+
+namespace prjxray {
+
+template <typename WordType, typename ByteType>
+class BigEndianSpan {
+ public:
+ constexpr static size_t kBytesPerElement = sizeof(WordType);
+
+ using byte_type = ByteType;
+ using word_type = WordType;
+ using size_type = std::size_t;
+
+ class value_type {
+ public:
+ operator WordType() const {
+ WordType word = 0;
+ for (size_t ii = 0; ii < kBytesPerElement; ++ii) {
+ word |= (static_cast<WordType>(bytes_[ii])
+ << ((kBytesPerElement - 1 - ii) * 8));
+ }
+ return word;
+ }
+
+ value_type& operator=(WordType word) {
+ for (size_t ii = 0; ii < kBytesPerElement; ++ii) {
+ bytes_[ii] =
+ ((word >>
+ ((kBytesPerElement - 1 - ii) * 8)) &
+ 0xFF);
+ }
+ return *this;
+ }
+
+ protected:
+ friend class BigEndianSpan<WordType, ByteType>;
+
+ value_type(absl::Span<ByteType> bytes) : bytes_(bytes){};
+
+ private:
+ absl::Span<ByteType> bytes_;
+ };
+
+ class iterator
+ : public std::iterator<std::input_iterator_tag, value_type> {
+ public:
+ value_type operator*() const { return value_type(bytes_); }
+
+ bool operator==(const iterator& other) const {
+ return bytes_ == other.bytes_;
+ }
+
+ bool operator!=(const iterator& other) const {
+ return bytes_ != other.bytes_;
+ }
+
+ iterator& operator++() {
+ bytes_ = bytes_.subspan(kBytesPerElement);
+ return *this;
+ }
+
+ protected:
+ friend class BigEndianSpan<WordType, ByteType>;
+
+ iterator(absl::Span<ByteType> bytes) : bytes_(bytes){};
+
+ private:
+ absl::Span<ByteType> bytes_;
+ };
+
+ using pointer = value_type*;
+ using reference = value_type&;
+
+ BigEndianSpan(absl::Span<ByteType> bytes) : bytes_(bytes){};
+
+ constexpr size_type size() const noexcept {
+ return bytes_.size() / kBytesPerElement;
+ };
+
+ constexpr size_type length() const noexcept { return size(); }
+
+ constexpr bool empty() const noexcept { return size() == 0; }
+
+ value_type operator[](size_type pos) const {
+ assert(pos >= 0 && pos < size());
+ return value_type(bytes_.subspan((pos * kBytesPerElement)));
+ }
+
+ constexpr reference at(size_type pos) const {
+ return this->operator[](pos);
+ }
+
+ iterator begin() const { return iterator(bytes_); }
+ iterator end() const { return iterator({}); }
+
+ private:
+ absl::Span<ByteType> bytes_;
+};
+
+template <typename WordType, typename Container>
+BigEndianSpan<WordType, typename Container::value_type> make_big_endian_span(
+ Container& bytes) {
+ return BigEndianSpan<WordType, typename Container::value_type>(
+ absl::Span<typename Container::value_type>(bytes));
+}
+
+} // namespace prjxray
+
+#endif // PRJXRAY_LIB_BIG_ENDIAN_SPAN
diff --git a/lib/include/prjxray/bit_ops.h b/lib/include/prjxray/bit_ops.h
new file mode 100644
index 0000000..c7d3fa8
--- /dev/null
+++ b/lib/include/prjxray/bit_ops.h
@@ -0,0 +1,48 @@
+#ifndef PRJXRAY_LIB_BIT_OPS_H
+#define PRJXRAY_LIB_BIT_OPS_H
+
+namespace prjxray {
+
+template <typename UInt>
+constexpr UInt bit_mask(const int bit) {
+ return (static_cast<UInt>(1) << bit);
+}
+
+template <typename UInt>
+constexpr UInt bit_sizeof() {
+ return sizeof(UInt) * 8;
+}
+
+template <typename UInt>
+constexpr UInt bit_all_ones() {
+ return ~static_cast<UInt>(0);
+}
+
+template <typename UInt>
+constexpr UInt bit_mask_range(const int top_bit, const int bottom_bit) {
+ return ((bit_all_ones<UInt>() >> (bit_sizeof<UInt>() - 1 - top_bit)) &
+ (bit_all_ones<UInt>() - bit_mask<UInt>(bottom_bit) +
+ static_cast<UInt>(1)));
+}
+
+template <typename UInt>
+constexpr UInt bit_field_get(UInt value,
+ const int top_bit,
+ const int bottom_bit) {
+ return (value & bit_mask_range<UInt>(top_bit, bottom_bit)) >>
+ bottom_bit;
+}
+
+template <typename UInt, typename ValueType>
+constexpr UInt bit_field_set(const UInt reg_value,
+ const int top_bit,
+ const int bottom_bit,
+ const ValueType field_value) {
+ return ((reg_value & ~bit_mask_range<UInt>(top_bit, bottom_bit)) |
+ ((static_cast<UInt>(field_value) << bottom_bit) &
+ bit_mask_range<UInt>(top_bit, bottom_bit)));
+}
+
+} // namespace prjxray
+
+#endif // PRJXRAY_LIB_BIT_OPS_H
diff --git a/lib/include/prjxray/database.h b/lib/include/prjxray/database.h
new file mode 100644
index 0000000..76b1093
--- /dev/null
+++ b/lib/include/prjxray/database.h
@@ -0,0 +1,24 @@
+#ifndef PRJXRAY_LIB_DATABASE_H
+#define PRJXRAY_LIB_DATABASE_H
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include <prjxray/segbits_file_reader.h>
+
+namespace prjxray {
+
+class Database {
+ public:
+ Database(const std::string& path) : db_path_(path) {}
+
+ std::vector<std::unique_ptr<SegbitsFileReader>> segbits() const;
+
+ private:
+ std::string db_path_;
+};
+
+} // namespace prjxray
+
+#endif // PRJXRAY_LIB_DATABASE_H
diff --git a/lib/include/prjxray/memory_mapped_file.h b/lib/include/prjxray/memory_mapped_file.h
new file mode 100644
index 0000000..9a944b7
--- /dev/null
+++ b/lib/include/prjxray/memory_mapped_file.h
@@ -0,0 +1,34 @@
+#ifndef PRJXRAY_LIB_MEMORY_MAPPED_FILE
+#define PRJXRAY_LIB_MEMORY_MAPPED_FILE
+
+#include <memory>
+#include <string>
+
+#include <absl/types/span.h>
+
+namespace prjxray {
+
+class MemoryMappedFile {
+ public:
+ ~MemoryMappedFile();
+
+ static std::unique_ptr<MemoryMappedFile> InitWithFile(
+ const std::string& path);
+
+ void* const data() const { return data_; }
+ const size_t size() const { return size_; }
+
+ absl::Span<uint8_t> as_bytes() const {
+ return {static_cast<uint8_t*>(data_), size_};
+ }
+
+ private:
+ MemoryMappedFile(void* data, size_t size) : data_(data), size_(size){};
+
+ void* data_;
+ size_t size_;
+};
+
+} // namespace prjxray
+
+#endif // PRJXRAY_LIB_MEMORY_MAPPED_FILE
diff --git a/lib/include/prjxray/segbits_file_reader.h b/lib/include/prjxray/segbits_file_reader.h
new file mode 100644
index 0000000..19c4f51
--- /dev/null
+++ b/lib/include/prjxray/segbits_file_reader.h
@@ -0,0 +1,69 @@
+#ifndef PRJXRAY_LIB_SEGBITS_FILE_READER_H
+#define PRJXRAY_LIB_SEGBITS_FILE_READER_H
+
+#include <iterator>
+#include <memory>
+
+#include <absl/strings/string_view.h>
+#include <prjxray/memory_mapped_file.h>
+
+namespace prjxray {
+
+class SegbitsFileReader {
+ public:
+ class value_type {
+ public:
+ absl::string_view tag() const { return tag_; }
+ absl::string_view bit() const { return bit_; }
+
+ private:
+ friend SegbitsFileReader;
+
+ value_type(const absl::string_view& view);
+
+ absl::string_view tag_;
+ absl::string_view bit_;
+ };
+
+ class iterator
+ : public std::iterator<std::input_iterator_tag, value_type> {
+ public:
+ iterator& operator++();
+
+ bool operator==(iterator other) const {
+ return view_ == other.view_;
+ }
+ bool operator!=(iterator other) const {
+ return !(*this == other);
+ }
+
+ const value_type& operator*() const { return value_; }
+ const value_type* operator->() const { return &value_; }
+
+ protected:
+ explicit iterator(absl::string_view view)
+ : view_(view), value_(view) {}
+
+ private:
+ friend SegbitsFileReader;
+
+ absl::string_view view_;
+ value_type value_;
+ };
+
+ static std::unique_ptr<SegbitsFileReader> InitWithFile(
+ const std::string& path);
+
+ iterator begin();
+ iterator end();
+
+ private:
+ SegbitsFileReader(std::unique_ptr<MemoryMappedFile>&& mapped_file)
+ : mapped_file_(std::move(mapped_file)){};
+
+ std::unique_ptr<MemoryMappedFile> mapped_file_;
+};
+
+} // namespace prjxray
+
+#endif // PRJXRAY_LIB_SEGBITS_FILE_READER_H
diff --git a/lib/include/prjxray/xilinx/architectures.h b/lib/include/prjxray/xilinx/architectures.h
new file mode 100644
index 0000000..cdf992e
--- /dev/null
+++ b/lib/include/prjxray/xilinx/architectures.h
@@ -0,0 +1,92 @@
+#ifndef PRJXRAY_LIB_XILINX_ARCHITECTURES_H_
+#define PRJXRAY_LIB_XILINX_ARCHITECTURES_H_
+
+#include <absl/types/variant.h>
+#include <memory>
+#include <vector>
+
+#include <prjxray/xilinx/configuration_packet.h>
+#include <prjxray/xilinx/spartan6/frame_address.h>
+#include <prjxray/xilinx/spartan6/part.h>
+#include <prjxray/xilinx/xc7series/frame_address.h>
+#include <prjxray/xilinx/xc7series/part.h>
+
+namespace prjxray {
+namespace xilinx {
+
+class Spartan6;
+class Series7;
+class UltraScale;
+class UltraScalePlus;
+
+class Architecture {
+ public:
+ using Container =
+ absl::variant<Series7, UltraScale, UltraScalePlus, Spartan6>;
+ Architecture(const std::string& name) : name_(name) {}
+ const std::string& name() const { return name_; }
+ virtual ~Architecture() {}
+
+ private:
+ const std::string name_;
+};
+
+class Spartan6 : public Architecture {
+ public:
+ using ConfRegType = Spartan6ConfigurationRegister;
+ using Part = spartan6::Part;
+ using ConfigurationPackage =
+ std::vector<std::unique_ptr<ConfigurationPacket<ConfRegType>>>;
+ using FrameAddress = spartan6::FrameAddress;
+ using WordType = uint16_t;
+ Spartan6() : Architecture("Spartan6") {}
+ static constexpr int words_per_frame = 65;
+};
+
+class Series7 : public Architecture {
+ public:
+ using ConfRegType = Series7ConfigurationRegister;
+ using Part = xc7series::Part;
+ using ConfigurationPackage =
+ std::vector<std::unique_ptr<ConfigurationPacket<ConfRegType>>>;
+ using FrameAddress = xc7series::FrameAddress;
+ using WordType = uint32_t;
+ Series7() : Architecture("Series7") {}
+ Series7(const std::string& name) : Architecture(name) {}
+ static constexpr int words_per_frame = 101;
+};
+
+class UltraScalePlus : public Series7 {
+ public:
+ UltraScalePlus() : Series7("UltraScalePlus") {}
+ static constexpr int words_per_frame = 93;
+};
+
+class UltraScale : public Series7 {
+ public:
+ UltraScale() : Series7("UltraScale") {}
+ static constexpr int words_per_frame = 123;
+};
+
+class ArchitectureFactory {
+ public:
+ static Architecture::Container create_architecture(
+ const std::string& arch) {
+ if (arch == "Spartan6") {
+ return Spartan6();
+ } else if (arch == "Series7") {
+ return Series7();
+ } else if (arch == "UltraScale") {
+ return UltraScale();
+ } else if (arch == "UltraScalePlus") {
+ return UltraScalePlus();
+ } else {
+ return Architecture::Container();
+ }
+ }
+};
+
+} // namespace xilinx
+} // namespace prjxray
+
+#endif // PRJXRAY_LIB_XILINX_ARCHITECTURES_H_
diff --git a/lib/include/prjxray/xilinx/bitstream_reader.h b/lib/include/prjxray/xilinx/bitstream_reader.h
new file mode 100644
index 0000000..5464a52
--- /dev/null
+++ b/lib/include/prjxray/xilinx/bitstream_reader.h
@@ -0,0 +1,181 @@
+#ifndef PRJXRAY_LIB_XILINX_BITSTREAM_READER_H
+#define PRJXRAY_LIB_XILINX_BITSTREAM_READER_H
+
+#include <algorithm>
+#include <iostream>
+#include <memory>
+#include <vector>
+
+#include <absl/types/span.h>
+
+#include <prjxray/big_endian_span.h>
+#include <prjxray/xilinx/architectures.h>
+#include <prjxray/xilinx/configuration_packet.h>
+
+namespace prjxray {
+namespace xilinx {
+
+// Constructs a collection of 32-bit big-endian words from a bitstream file.
+// Provides an iterator over the configuration packets.
+template <typename ArchType>
+class BitstreamReader {
+ public:
+ using value_type = ConfigurationPacket<typename ArchType::ConfRegType>;
+
+ // Implements an iterator over the words grouped in configuration
+ // packets.
+ class iterator
+ : public std::iterator<std::input_iterator_tag, value_type> {
+ public:
+ iterator& operator++();
+
+ bool operator==(const iterator& other) const;
+ bool operator!=(const iterator& other) const;
+
+ const value_type& operator*() const;
+ const value_type* operator->() const;
+
+ protected:
+ explicit iterator(absl::Span<uint32_t> words);
+
+ private:
+ friend BitstreamReader;
+
+ typename value_type::ParseResult parse_result_;
+ absl::Span<uint32_t> words_;
+ };
+
+ // Construct a reader from a collection of 32-bit, big-endian words.
+ // Assumes that any sync word has already been removed.
+ BitstreamReader(std::vector<uint32_t>&& words)
+ : words_(std::move(words)) {}
+
+ BitstreamReader() {}
+ size_t size() { return words_.size(); }
+
+ // Construct a `BitstreamReader` from a Container of bytes.
+ // Any bytes preceding an initial sync word are ignored.
+ template <typename T>
+ static absl::optional<BitstreamReader<ArchType>> InitWithBytes(
+ T bitstream);
+
+ const std::vector<uint32_t>& words() { return words_; };
+
+ // Returns an iterator that yields `ConfigurationPackets`
+ // as read from the bitstream.
+ iterator begin();
+ iterator end();
+
+ private:
+ static std::array<uint8_t, 4> kSyncWord;
+
+ std::vector<uint32_t> words_;
+};
+
+template <typename ArchType>
+template <typename T>
+absl::optional<BitstreamReader<ArchType>>
+BitstreamReader<ArchType>::InitWithBytes(T bitstream) {
+ // If this is really a Xilinx bitstream, there will be a sync
+ // word somewhere toward the beginning.
+ auto sync_pos = std::search(bitstream.begin(), bitstream.end(),
+ kSyncWord.begin(), kSyncWord.end());
+ if (sync_pos == bitstream.end()) {
+ return absl::optional<BitstreamReader<ArchType>>();
+ }
+ sync_pos += kSyncWord.size();
+
+ // Wrap the provided container in a span that strips off the preamble.
+ absl::Span<typename T::value_type> bitstream_span(bitstream);
+ auto config_packets =
+ bitstream_span.subspan(sync_pos - bitstream.begin());
+
+ // Convert the bytes into 32-bit or 16-bit in case of Spartan6,
+ // big-endian words.
+ auto big_endian_reader =
+ make_big_endian_span<typename ArchType::WordType>(config_packets);
+ std::vector<uint32_t> words{big_endian_reader.begin(),
+ big_endian_reader.end()};
+
+ return BitstreamReader<ArchType>(std::move(words));
+}
+
+// Sync word as specified in UG470 page 81
+template <typename ArchType>
+std::array<uint8_t, 4> BitstreamReader<ArchType>::kSyncWord{0xAA, 0x99, 0x55,
+ 0x66};
+
+template <typename ArchType>
+typename BitstreamReader<ArchType>::iterator
+BitstreamReader<ArchType>::begin() {
+ return iterator(absl::MakeSpan(words_));
+}
+
+template <typename ArchType>
+typename BitstreamReader<ArchType>::iterator BitstreamReader<ArchType>::end() {
+ return iterator({});
+}
+
+template <typename ArchType>
+BitstreamReader<ArchType>::iterator::iterator(absl::Span<uint32_t> words) {
+ parse_result_.first = words;
+ parse_result_.second = {};
+ ++(*this);
+}
+
+template <typename ArchType>
+typename BitstreamReader<ArchType>::iterator&
+ BitstreamReader<ArchType>::iterator::operator++() {
+ do {
+ auto new_result =
+ ConfigurationPacket<typename ArchType::ConfRegType>::
+ InitWithWords(parse_result_.first,
+ parse_result_.second.has_value()
+ ? parse_result_.second.operator->()
+ : nullptr);
+
+ // If the a valid header is being found but there are
+ // insufficient words to yield a packet, consider it the end.
+ if (new_result.first == parse_result_.first) {
+ words_ = absl::Span<uint32_t>();
+ break;
+ }
+
+ words_ = parse_result_.first;
+ parse_result_ = new_result;
+ } while (!parse_result_.first.empty() && !parse_result_.second);
+
+ if (!parse_result_.second) {
+ words_ = absl::Span<uint32_t>();
+ }
+
+ return *this;
+}
+
+template <typename ArchType>
+bool BitstreamReader<ArchType>::iterator::operator==(
+ const iterator& other) const {
+ return words_ == other.words_;
+}
+
+template <typename ArchType>
+bool BitstreamReader<ArchType>::iterator::operator!=(
+ const iterator& other) const {
+ return !(*this == other);
+}
+
+template <typename ArchType>
+const typename BitstreamReader<ArchType>::value_type&
+ BitstreamReader<ArchType>::iterator::operator*() const {
+ return *(parse_result_.second);
+}
+
+template <typename ArchType>
+const typename BitstreamReader<ArchType>::value_type*
+ BitstreamReader<ArchType>::iterator::operator->() const {
+ return parse_result_.second.operator->();
+}
+} // namespace xilinx
+} // namespace prjxray
+
+#endif // PRJXRAY_LIB_XILINX_BITSTREAM_READER_H
diff --git a/lib/include/prjxray/xilinx/bitstream_writer.h b/lib/include/prjxray/xilinx/bitstream_writer.h
new file mode 100644
index 0000000..09d69f4
--- /dev/null
+++ b/lib/include/prjxray/xilinx/bitstream_writer.h
@@ -0,0 +1,424 @@
+/*
+ * 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_BITSTREAM_WRITER_H
+#define PRJXRAY_LIB_XILINX_BITSTREAM_WRITER_H
+
+#include <algorithm>
+#include <fstream>
+#include <memory>
+#include <vector>
+
+#include <absl/strings/str_cat.h>
+#include <absl/time/clock.h>
+#include <absl/time/time.h>
+#include <absl/types/optional.h>
+#include <absl/types/span.h>
+
+#include <prjxray/big_endian_span.h>
+#include <prjxray/xilinx/configuration_packet.h>
+
+namespace prjxray {
+namespace xilinx {
+
+uint32_t packet2header(
+ const ConfigurationPacket<Series7ConfigurationRegister>& packet);
+uint32_t packet2header(
+ const ConfigurationPacket<Spartan6ConfigurationRegister>& packet);
+// Writes out the complete Xilinx bitstream including
+// header, sync word and configuration sequence.
+template <typename ArchType>
+class BitstreamWriter {
+ public:
+ typedef std::vector<uint32_t> header_t;
+ typedef std::vector<std::unique_ptr<
+ ConfigurationPacket<typename ArchType::ConfRegType>>>
+ packets_t;
+ typedef std::vector<uint8_t> BitstreamHeader;
+ // 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;
+ 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<typename ArchType::ConfRegType>*
+ 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<typename ArchType::ConfRegType>*
+ packet_;
+ };
+
+ 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,
+ typename 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_;
+ typename packets_t::const_iterator itr_packets_;
+ absl::optional<packet_iterator> op_itr_packet_;
+ };
+
+ BitstreamWriter(const packets_t& packets) : packets_(packets) {}
+
+ // Writes out the complete bitstream for Xilinx FPGA based on
+ // the Configuration Package which holds the complete programming
+ // sequence.
+ int writeBitstream(
+ const typename ArchType::ConfigurationPackage& packets,
+ const std::string& part_name,
+ const std::string& frames_file,
+ const std::string& generator_name,
+ const std::string& output_file);
+ iterator begin();
+ iterator end();
+
+ private:
+ static header_t header_;
+ const packets_t& packets_;
+
+ // Creates a Xilinx bit header which is mostly a
+ // Tag-Length-Value(TLV) format documented here:
+ // http://www.fpga-faq.com/FAQ_Pages/0026_Tell_me_about_bit_files.htm
+ BitstreamHeader create_header(const std::string& part_name,
+ const std::string& frames_file_name,
+ const std::string& generator_name);
+};
+
+template <typename ArchType>
+int BitstreamWriter<ArchType>::writeBitstream(
+ const typename ArchType::ConfigurationPackage& packets,
+ const std::string& part_name,
+ const std::string& frames_file,
+ const std::string& generator_name,
+ const std::string& output_file) {
+ std::ofstream out_file(output_file, std::ofstream::binary);
+ if (!out_file) {
+ std::cerr << "Unable to open file for writting: " << output_file
+ << std::endl;
+ return 1;
+ }
+
+ BitstreamHeader bit_header(
+ create_header(part_name, frames_file, generator_name));
+ 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);
+
+ BitstreamWriter<ArchType> out_bitstream_writer(packets);
+ int bytes_per_word = sizeof(typename ArchType::WordType);
+ for (uint32_t word : out_bitstream_writer) {
+ for (int byte = bytes_per_word - 1; byte >= 0; byte--) {
+ out_file.put((word >> (byte * 8)) & 0xFF);
+ }
+ }
+
+ uint32_t length_of_data = out_file.tellp() - end_of_header_pos;
+
+ out_file.seekp(header_data_length_pos);
+ for (int byte = 3; byte >= 0; byte--) {
+ out_file.put((length_of_data >> (byte * 8)) & 0xFF);
+ }
+ return 0;
+}
+
+template <typename ArchType>
+typename BitstreamWriter<ArchType>::BitstreamHeader
+BitstreamWriter<ArchType>::create_header(const std::string& part_name,
+ const std::string& frames_file_name,
+ const std::string& generator_name) {
+ // Sync header
+ BitstreamHeader bit_header{0x0, 0x9, 0x0f, 0xf0, 0x0f, 0xf0, 0x0f,
+ 0xf0, 0x0f, 0xf0, 0x00, 0x00, 0x01, 'a'};
+ auto build_source =
+ absl::StrCat(frames_file_name, ";Generator=" + generator_name);
+ 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>((part_name.size() + 1) >> 8));
+ bit_header.push_back(static_cast<uint8_t>(part_name.size() + 1));
+ bit_header.insert(bit_header.end(), part_name.begin(), 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});
+ return bit_header;
+}
+
+template <typename ArchType>
+typename BitstreamWriter<ArchType>::packet_iterator
+BitstreamWriter<ArchType>::iterator::packet_begin() {
+ // itr_packets = packets.begin();
+ const ConfigurationPacket<typename ArchType::ConfRegType>& packet =
+ **itr_packets_;
+
+ return BitstreamWriter::packet_iterator(
+ &packet, BitstreamWriter::packet_iterator::STATE_HEADER,
+ packet.data().begin());
+}
+
+template <typename ArchType>
+typename BitstreamWriter<ArchType>::packet_iterator
+BitstreamWriter<ArchType>::iterator::packet_end() {
+ const ConfigurationPacket<typename ArchType::ConfRegType>& packet =
+ **itr_packets_;
+
+ return BitstreamWriter<ArchType>::packet_iterator(
+ &packet, BitstreamWriter::packet_iterator::STATE_END,
+ // Essentially ignored
+ packet.data().end());
+}
+
+template <typename ArchType>
+BitstreamWriter<ArchType>::packet_iterator::packet_iterator(
+ const ConfigurationPacket<typename ArchType::ConfRegType>* packet,
+ state_t state,
+ data_iterator_t itr_data)
+ : state_(state), itr_data_(itr_data), packet_(packet) {}
+
+template <typename ArchType>
+typename BitstreamWriter<ArchType>::packet_iterator&
+ BitstreamWriter<ArchType>::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;
+}
+
+template <typename ArchType>
+bool BitstreamWriter<ArchType>::packet_iterator::operator==(
+ const packet_iterator& other) const {
+ return state_ == other.state_ && itr_data_ == other.itr_data_;
+}
+
+template <typename ArchType>
+bool BitstreamWriter<ArchType>::packet_iterator::operator!=(
+ const packet_iterator& other) const {
+ return !(*this == other);
+}
+
+template <typename ArchType>
+const typename BitstreamWriter<ArchType>::itr_value_type
+ BitstreamWriter<ArchType>::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?
+}
+
+template <typename ArchType>
+const typename BitstreamWriter<ArchType>::itr_value_type
+ BitstreamWriter<ArchType>::packet_iterator::operator->() const {
+ return *(*this);
+}
+
+/**************************************************
+ * BitstreamWriter::iterator
+ *************************************************/
+
+template <typename ArchType>
+typename BitstreamWriter<ArchType>::iterator
+BitstreamWriter<ArchType>::begin() {
+ typename 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<typename ArchType::ConfRegType>&
+ 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);
+}
+
+template <typename ArchType>
+typename BitstreamWriter<ArchType>::iterator BitstreamWriter<ArchType>::end() {
+ return iterator(header_.end(), packets_, packets_.end(),
+ absl::optional<packet_iterator>());
+}
+
+template <typename ArchType>
+BitstreamWriter<ArchType>::iterator::iterator(
+ header_t::iterator itr_header,
+ const typename BitstreamWriter<ArchType>::packets_t& packets,
+ typename BitstreamWriter<ArchType>::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) {}
+
+template <typename ArchType>
+typename BitstreamWriter<ArchType>::iterator&
+ BitstreamWriter<ArchType>::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;
+}
+
+template <typename ArchType>
+bool BitstreamWriter<ArchType>::iterator::operator==(
+ const iterator& other) const {
+ return itr_header_ == other.itr_header_ &&
+ itr_packets_ == other.itr_packets_ &&
+ op_itr_packet_ == other.op_itr_packet_;
+}
+
+template <typename ArchType>
+bool BitstreamWriter<ArchType>::iterator::operator!=(
+ const iterator& other) const {
+ return !(*this == other);
+}
+
+template <typename ArchType>
+const typename BitstreamWriter<ArchType>::itr_value_type
+ BitstreamWriter<ArchType>::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_);
+ }
+}
+
+template <typename ArchType>
+const typename BitstreamWriter<ArchType>::itr_value_type
+ BitstreamWriter<ArchType>::iterator::operator->() const {
+ return *(*this);
+}
+
+} // namespace xilinx
+} // namespace prjxray
+
+#endif // PRJXRAY_LIB_XILINX_XC7SERIES_BITSTREAM_WRITER_H
diff --git a/lib/include/prjxray/xilinx/configuration.h b/lib/include/prjxray/xilinx/configuration.h
new file mode 100644
index 0000000..6236345
--- /dev/null
+++ b/lib/include/prjxray/xilinx/configuration.h
@@ -0,0 +1,355 @@
+#ifndef PRJXRAY_LIB_XILINX_CONFIGURATION_H_
+#define PRJXRAY_LIB_XILINX_CONFIGURATION_H_
+
+#include <map>
+#include <type_traits>
+
+#include <absl/types/span.h>
+
+#include <prjxray/bit_ops.h>
+#include <prjxray/xilinx/architectures.h>
+#include <prjxray/xilinx/frames.h>
+
+namespace prjxray {
+namespace xilinx {
+
+template <typename ArchType>
+class Configuration {
+ public:
+ using FrameMap = std::map<typename ArchType::FrameAddress,
+ absl::Span<const uint32_t>>;
+ using PacketData = std::vector<uint32_t>;
+
+ // Returns a configuration, i.e. collection of frame addresses
+ // and corresponding data from a collection of configuration packets.
+ template <typename Collection>
+ static absl::optional<Configuration<ArchType>> InitWithPackets(
+ const typename ArchType::Part& part,
+ Collection& packets);
+
+ // Creates the complete configuration package which is later on
+ // used by the bitstream writer to generate the bitstream file.
+ // The pacakge forms a sequence suitable for Xilinx devices.
+ // The programming sequence for Series-7 is taken from
+ // https://www.kc8apf.net/2018/05/unpacking-xilinx-7-series-bitstreams-part-2/
+ static void createConfigurationPackage(
+ typename ArchType::ConfigurationPackage& out_packets,
+ const PacketData& packet_data,
+ absl::optional<typename ArchType::Part>& part);
+
+ // Returns the payload for a type 2 packet
+ // which allows for bigger payload compared to type 1.
+ static PacketData createType2ConfigurationPacketData(
+ const typename Frames<ArchType>::Frames2Data& frames,
+ absl::optional<typename ArchType::Part>& part);
+
+ Configuration(const typename ArchType::Part& part,
+ std::map<typename ArchType::FrameAddress,
+ std::vector<uint32_t>>* frames)
+ : part_(part) {
+ for (auto& frame : *frames) {
+ frames_[frame.first] =
+ absl::Span<const uint32_t>(frame.second);
+ }
+ }
+
+ Configuration(const typename ArchType::Part& part,
+ const FrameMap& frames)
+ : part_(part), frames_(std::move(frames)) {}
+
+ const typename ArchType::Part& part() const { return part_; }
+ const FrameMap& frames() const { return frames_; }
+
+ private:
+ typename ArchType::Part part_;
+ FrameMap frames_;
+};
+
+template <typename ArchType>
+typename Configuration<ArchType>::PacketData
+Configuration<ArchType>::createType2ConfigurationPacketData(
+ const typename Frames<ArchType>::Frames2Data& frames,
+ absl::optional<typename ArchType::Part>& part) {
+ PacketData packet_data;
+ // Certain configuration frames blocks are separated by Zero Frames,
+ // i.e. frames with words with all zeroes. For Series-7, US and US+
+ // there zero frames separator consists of two frames.
+ static const int kZeroFramesSeparatorWords =
+ ArchType::words_per_frame * 2;
+ for (auto& frame : frames) {
+ std::copy(frame.second.begin(), frame.second.end(),
+ std::back_inserter(packet_data));
+
+ auto next_address = part->GetNextFrameAddress(frame.first);
+ if (next_address &&
+ (next_address->block_type() != frame.first.block_type() ||
+ next_address->is_bottom_half_rows() !=
+ frame.first.is_bottom_half_rows() ||
+ next_address->row() != frame.first.row())) {
+ packet_data.insert(packet_data.end(),
+ kZeroFramesSeparatorWords, 0);
+ }
+ }
+ packet_data.insert(packet_data.end(), kZeroFramesSeparatorWords, 0);
+ return packet_data;
+}
+
+template <>
+template <typename Collection>
+absl::optional<Configuration<Spartan6>>
+Configuration<Spartan6>::InitWithPackets(const typename Spartan6::Part& part,
+ Collection& packets) {
+ using ArchType = Spartan6;
+ // Registers that can be directly written to.
+ uint32_t command_register = 0;
+ uint32_t frame_address_register = 0;
+ uint32_t mask_register = 0;
+ __attribute__((unused)) uint32_t ctl1_register = 0;
+
+ // Internal state machine for writes.
+ bool start_new_write = false;
+ typename ArchType::FrameAddress current_frame_address = 0;
+
+ Configuration<ArchType>::FrameMap frames;
+ for (auto packet : packets) {
+ if (packet.opcode() !=
+ ConfigurationPacket<
+ typename ArchType::ConfRegType>::Opcode::Write) {
+ continue;
+ }
+
+ switch (packet.address()) {
+ case ArchType::ConfRegType::MASK:
+ if (packet.data().size() < 1)
+ continue;
+ mask_register = packet.data()[0];
+ break;
+ case ArchType::ConfRegType::CTL:
+ if (packet.data().size() < 1)
+ continue;
+ ctl1_register =
+ packet.data()[0] & mask_register;
+ break;
+ case ArchType::ConfRegType::CMD:
+ if (packet.data().size() < 1)
+ continue;
+ command_register = packet.data()[0];
+ // Writes to CMD trigger an immediate action. In
+ // the case of WCFG, that is just setting a flag
+ // for the next FDRI.
+ if (command_register == 0x1) {
+ start_new_write = true;
+ }
+ break;
+ case ArchType::ConfRegType::IDCODE: {
+ // This really should be a two-word write.
+ if (packet.data().size() < 2)
+ continue;
+
+ // If the IDCODE doesn't match our expected
+ // part, consider the bitstream invalid.
+ uint32_t idcode = (packet.data()[0] << 16) |
+ (packet.data()[1]);
+ if (idcode != part.idcode()) {
+ return {};
+ }
+ break;
+ }
+ // UG380 describes the frame addressing scheme where two
+ // words for FAR_MAJ update FAR_MAJ anda FAR_MIN -
+ // FAR_MAJ comes first
+ case ArchType::ConfRegType::FAR_MAJ: {
+ size_t packet_size = packet.data().size();
+ assert(packet_size < 3);
+ if (packet_size < 1) {
+ continue;
+ } else if (packet_size < 2) {
+ frame_address_register =
+ (packet.data()[0] & 0xFFFF) << 16;
+ } else {
+ frame_address_register =
+ ((packet.data()[0] & 0xFFFF)
+ << 16) |
+ (packet.data()[1] & 0xFFFF);
+ }
+ break;
+ }
+ case ArchType::ConfRegType::FAR_MIN:
+ // This really should be a one-word write.
+ if (packet.data().size() < 1)
+ continue;
+
+ frame_address_register |=
+ packet.data()[0] & 0x3FF;
+
+ break;
+ case ArchType::ConfRegType::FDRI: {
+ if (start_new_write) {
+ current_frame_address =
+ frame_address_register;
+ start_new_write = false;
+ }
+
+ // Spartan6 frames are 65-words long. Writes
+ // to this register can be multiples of that to
+ // do auto-incrementing block writes.
+
+ for (size_t ii = 0; ii < packet.data().size();
+ ii += ArchType::words_per_frame) {
+ frames[current_frame_address] =
+ packet.data().subspan(
+ ii, ArchType::words_per_frame);
+
+ auto next_address =
+ part.GetNextFrameAddress(
+ current_frame_address);
+ if (!next_address)
+ break;
+
+ current_frame_address = *next_address;
+ }
+ break;
+ }
+ default:
+ break;
+ }
+ }
+
+ return Configuration(part, frames);
+}
+
+template <typename ArchType>
+template <typename Collection>
+absl::optional<Configuration<ArchType>>
+Configuration<ArchType>::InitWithPackets(const typename ArchType::Part& part,
+ Collection& packets) {
+ // Registers that can be directly written to.
+ uint32_t command_register = 0;
+ uint32_t frame_address_register = 0;
+ uint32_t mask_register = 0;
+ uint32_t ctl1_register = 0;
+
+ // Internal state machine for writes.
+ bool start_new_write = false;
+ typename ArchType::FrameAddress current_frame_address = 0;
+
+ Configuration<ArchType>::FrameMap frames;
+ for (auto packet : packets) {
+ if (packet.opcode() !=
+ ConfigurationPacket<
+ typename ArchType::ConfRegType>::Opcode::Write) {
+ continue;
+ }
+
+ switch (packet.address()) {
+ case ArchType::ConfRegType::MASK:
+ if (packet.data().size() < 1)
+ continue;
+ mask_register = packet.data()[0];
+ break;
+ case ArchType::ConfRegType::CTL1:
+ if (packet.data().size() < 1)
+ continue;
+ ctl1_register =
+ packet.data()[0] & mask_register;
+ break;
+ case ArchType::ConfRegType::CMD:
+ if (packet.data().size() < 1)
+ continue;
+ command_register = packet.data()[0];
+ // Writes to CMD trigger an immediate action. In
+ // the case of WCFG, that is just setting a flag
+ // for the next FDIR.
+ if (command_register == 0x1) {
+ start_new_write = true;
+ }
+ break;
+ case ArchType::ConfRegType::IDCODE:
+ // This really should be a one-word write.
+ if (packet.data().size() < 1)
+ continue;
+
+ // If the IDCODE doesn't match our expected
+ // part, consider the bitstream invalid.
+ if (packet.data()[0] != part.idcode()) {
+ return {};
+ }
+ break;
+ case ArchType::ConfRegType::FAR:
+ // This really should be a one-word write.
+ if (packet.data().size() < 1)
+ continue;
+ frame_address_register = packet.data()[0];
+
+ // Per UG470, the command present in the CMD
+ // register is executed each time the FAR
+ // register is laoded with a new value. As we
+ // only care about WCFG commands, just check
+ // that here. CTRL1 is completely undocumented
+ // but looking at generated bitstreams, bit 21
+ // is used when per-frame CRC is enabled.
+ // Setting this bit seems to inhibit the
+ // re-execution of CMD during a FAR write. In
+ // practice, this is used so FAR writes can be
+ // added in the bitstream to show progress
+ // markers without impacting the actual write
+ // operation.
+ if (bit_field_get(ctl1_register, 21, 21) == 0 &&
+ command_register == 0x1) {
+ start_new_write = true;
+ }
+ break;
+ case ArchType::ConfRegType::FDRI: {
+ if (start_new_write) {
+ current_frame_address =
+ frame_address_register;
+ start_new_write = false;
+ }
+
+ // Number of words in configuration frames
+ // depend on tje architecture. Writes to this
+ // register can be multiples of that number to
+ // do auto-incrementing block writes.
+ for (size_t ii = 0; ii < packet.data().size();
+ ii += ArchType::words_per_frame) {
+ frames[current_frame_address] =
+ packet.data().subspan(
+ ii, ArchType::words_per_frame);
+
+ auto next_address =
+ part.GetNextFrameAddress(
+ current_frame_address);
+ if (!next_address)
+ break;
+
+ // Bitstreams appear to have 2 frames of
+ // padding between rows.
+ if (next_address &&
+ (next_address->block_type() !=
+ current_frame_address
+ .block_type() ||
+ next_address
+ ->is_bottom_half_rows() !=
+ current_frame_address
+ .is_bottom_half_rows() ||
+ next_address->row() !=
+ current_frame_address.row())) {
+ ii += 2 *
+ ArchType::words_per_frame;
+ }
+ current_frame_address = *next_address;
+ }
+ break;
+ }
+ default:
+ break;
+ }
+ }
+
+ return Configuration(part, frames);
+}
+
+} // namespace xilinx
+} // namespace prjxray
+
+#endif // PRJXRAY_LIB_XILINX_CONFIGURATION_H_
diff --git a/lib/include/prjxray/xilinx/configuration_packet.h b/lib/include/prjxray/xilinx/configuration_packet.h
new file mode 100644
index 0000000..4df2105
--- /dev/null
+++ b/lib/include/prjxray/xilinx/configuration_packet.h
@@ -0,0 +1,68 @@
+#ifndef PRJXRAY_LIB_XILINX_CONFIGURATION_PACKET_H
+#define PRJXRAY_LIB_XILINX_CONFIGURATION_PACKET_H
+
+#include <cstdint>
+#include <ostream>
+
+#include <absl/types/optional.h>
+#include <absl/types/span.h>
+#include <prjxray/xilinx/configuration_register.h>
+
+namespace prjxray {
+namespace xilinx {
+
+// As described in the configuration user guide for Series-7
+// (UG470, pg. 108) there are two types of configuration packets
+enum ConfigurationPacketType { NONE, TYPE1, TYPE2 };
+
+// Creates a Type1 or Type2 configuration packet.
+// Specification of the packets for Series-7 can be found in UG470, pg. 108
+// For Spartan6 UG380, pg. 98
+template <typename ConfigRegType>
+class ConfigurationPacket {
+ public:
+ typedef std::pair<absl::Span<uint32_t>,
+ absl::optional<ConfigurationPacket<ConfigRegType>>>
+ ParseResult;
+
+ // Opcodes as specified in UG470 page 108
+ enum Opcode {
+ NOP = 0,
+ Read = 1,
+ Write = 2,
+ /* reserved = 3 */
+ };
+
+ ConfigurationPacket(unsigned int header_type,
+ Opcode opcode,
+ ConfigRegType address,
+ const absl::Span<const uint32_t>& data)
+ : header_type_(header_type),
+ opcode_(opcode),
+ address_(address),
+ data_(std::move(data)) {}
+
+ unsigned int header_type() const { return header_type_; }
+ const Opcode opcode() const { return opcode_; }
+ const ConfigRegType address() const { return address_; }
+ const absl::Span<const uint32_t>& data() const { return data_; }
+ static ParseResult InitWithWords(
+ absl::Span<uint32_t> words,
+ const ConfigurationPacket<ConfigRegType>* previous_packet =
+ nullptr);
+
+ private:
+ unsigned int header_type_;
+ Opcode opcode_;
+ ConfigRegType address_;
+ absl::Span<const uint32_t> data_;
+};
+
+template <class ConfigRegType>
+std::ostream& operator<<(std::ostream& o,
+ const ConfigurationPacket<ConfigRegType>& packet);
+
+} // namespace xilinx
+} // namespace prjxray
+
+#endif // PRJXRAY_LIB_XILINX_CONFIGURATION_PACKET_H
diff --git a/lib/include/prjxray/xilinx/configuration_packet_with_payload.h b/lib/include/prjxray/xilinx/configuration_packet_with_payload.h
new file mode 100644
index 0000000..0561938
--- /dev/null
+++ b/lib/include/prjxray/xilinx/configuration_packet_with_payload.h
@@ -0,0 +1,36 @@
+#ifndef PRJXRAY_LIB_XILINX_CONFIGURATION_PACKET_WITH_PAYLOAD_H
+#define PRJXRAY_LIB_XILINX_CONFIGURATION_PACKET_WITH_PAYLOAD_H
+
+#include <memory>
+
+#include <absl/types/span.h>
+#include <prjxray/bit_ops.h>
+#include <prjxray/xilinx/configuration_packet.h>
+#include <prjxray/xilinx/configuration_register.h>
+
+namespace prjxray {
+namespace xilinx {
+
+template <int Words, typename ConfigRegType>
+class ConfigurationPacketWithPayload
+ : public ConfigurationPacket<ConfigRegType> {
+ public:
+ ConfigurationPacketWithPayload(
+ typename ConfigurationPacket<ConfigRegType>::Opcode op,
+ ConfigRegType reg,
+ const std::array<uint32_t, Words>& payload)
+ : ConfigurationPacket<ConfigRegType>(
+ ConfigurationPacketType::TYPE1,
+ op,
+ reg,
+ absl::Span<uint32_t>(payload_)),
+ payload_(std::move(payload)) {}
+
+ private:
+ std::array<uint32_t, Words> payload_;
+};
+
+} // namespace xilinx
+} // namespace prjxray
+
+#endif // PRJXRAY_LIB_XILINX_CONFIGURATION_PACKET_WITH_PAYLOAD_H
diff --git a/lib/include/prjxray/xilinx/configuration_register.h b/lib/include/prjxray/xilinx/configuration_register.h
new file mode 100644
index 0000000..0699f6c
--- /dev/null
+++ b/lib/include/prjxray/xilinx/configuration_register.h
@@ -0,0 +1,84 @@
+#ifndef PRJXRAY_LIB_XILINX_CONFIGURATION_REGISTER_H_
+#define PRJXRAY_LIB_XILINX_CONFIGURATION_REGISTER_H_
+
+#include <ostream>
+
+namespace prjxray {
+namespace xilinx {
+
+// Spartan6 configuration register addresses
+// according to UG380, pg. 100
+enum class Spartan6ConfigurationRegister : unsigned int {
+ CRC = 0x00,
+ FAR = 0x01,
+ FAR_MAJ = 0x01,
+ FAR_MIN = 0x02,
+ FDRI = 0x03,
+ FDRO = 0x04,
+ CMD = 0x05,
+ CTL = 0x06,
+ CTL1 = 0x06,
+ MASK = 0x07,
+ STAT = 0x08,
+ LOUT = 0x09,
+ COR1 = 0x0a,
+ COR2 = 0x0b,
+ PWRDN_REG = 0x0c,
+ FLR = 0x0d,
+ IDCODE = 0x0e,
+ CWDT = 0x0f,
+ HC_OPT_REG = 0x10,
+ CSBO = 0x12,
+ GENERAL1 = 0x13,
+ GENERAL2 = 0x14,
+ GENERAL3 = 0x15,
+ GENERAL4 = 0x16,
+ GENERAL5 = 0x17,
+ MODE_REG = 0x18,
+ PU_GWE = 0x19,
+ PU_GTS = 0x1a,
+ MFWR = 0x1b,
+ CCLK_FREQ = 0x1c,
+ SEU_OPT = 0x1d,
+ EXP_SIGN = 0x1e,
+ RDBK_SIGN = 0x1f,
+ BOOTSTS = 0x20,
+ EYE_MASK = 0x21,
+ CBC_REG = 0x22,
+};
+
+// Series-7 configuration register addresses
+// according to UG470, pg. 109
+enum class Series7ConfigurationRegister : unsigned int {
+ CRC = 0x00,
+ FAR = 0x01,
+ FDRI = 0x02,
+ FDRO = 0x03,
+ CMD = 0x04,
+ CTL0 = 0x05,
+ MASK = 0x06,
+ STAT = 0x07,
+ LOUT = 0x08,
+ COR0 = 0x09,
+ MFWR = 0x0a,
+ CBC = 0x0b,
+ IDCODE = 0x0c,
+ AXSS = 0x0d,
+ COR1 = 0x0e,
+ WBSTAR = 0x10,
+ TIMER = 0x11,
+ UNKNOWN = 0x13,
+ BOOTSTS = 0x16,
+ CTL1 = 0x18,
+ BSPI = 0x1F,
+};
+
+std::ostream& operator<<(std::ostream& o,
+ const Spartan6ConfigurationRegister& value);
+std::ostream& operator<<(std::ostream& o,
+ const Series7ConfigurationRegister& value);
+
+} // namespace xilinx
+} // namespace prjxray
+
+#endif // PRJXRAY_LIB_XILINX_CONFIGURATION_REGISTER_H_
diff --git a/lib/include/prjxray/xilinx/frames.h b/lib/include/prjxray/xilinx/frames.h
new file mode 100644
index 0000000..f4a96f1
--- /dev/null
+++ b/lib/include/prjxray/xilinx/frames.h
@@ -0,0 +1,125 @@
+#ifndef PRJXRAY_LIB_XILINX_FRAMES_H
+#define PRJXRAY_LIB_XILINX_FRAMES_H
+
+#include <fstream>
+#include <iostream>
+#include <map>
+#include <string>
+#include <vector>
+
+#include <absl/strings/str_split.h>
+#include <prjxray/xilinx/architectures.h>
+
+namespace prjxray {
+namespace xilinx {
+
+// Contains frame information which is used for the generation
+// of the configuration package that is used in bitstream generation.
+template <typename ArchType>
+class Frames {
+ public:
+ typedef std::vector<uint32_t> FrameData;
+ typedef std::map<typename ArchType::FrameAddress, FrameData>
+ Frames2Data;
+
+ // Reads the contents of the frames file and populates
+ // the Frames container.
+ int readFrames(const std::string& frm_file_str);
+
+ // Adds empty frames that are present in the tilegrid of a specific part
+ // but are missing in the current frames container.
+ void addMissingFrames(
+ const absl::optional<typename ArchType::Part>& part);
+
+ // Returns the map with frame addresses and corresponding data
+ Frames2Data& getFrames() { return frames_data_; }
+
+ private:
+ Frames2Data frames_data_;
+
+ // Updates the ECC information in the frame
+ void updateECC(FrameData& data);
+};
+
+template <typename ArchType>
+int Frames<ArchType>::readFrames(const std::string& frm_file_str) {
+ assert(!frm_file_str.empty());
+
+ std::ifstream frm_file(frm_file_str);
+ if (!frm_file) {
+ std::cerr << "Unable to open frm file: " << frm_file_str
+ << std::endl;
+ return 1;
+ }
+ std::string frm_line;
+
+ while (std::getline(frm_file, frm_line)) {
+ if (frm_line[0] == '#')
+ continue;
+
+ std::pair<std::string, std::string> frame_delta =
+ absl::StrSplit(frm_line, ' ');
+
+ uint32_t frame_address =
+ std::stoul(frame_delta.first, nullptr, 16);
+
+ std::vector<std::string> frame_data_strings =
+ absl::StrSplit(frame_delta.second, ',');
+
+ // Spartan6's IOB frames can have different word count
+ if (!std::is_same<ArchType, Spartan6>::value) {
+ if (frame_data_strings.size() !=
+ ArchType::words_per_frame) {
+ std::cerr
+ << "Frame " << std::hex << frame_address
+ << ": found " << std::dec
+ << frame_data_strings.size()
+ << " words instead of "
+ << ArchType::words_per_frame << std::endl;
+ continue;
+ }
+ }
+
+ FrameData frame_data(frame_data_strings.size(), 0);
+ std::transform(frame_data_strings.begin(),
+ frame_data_strings.end(), frame_data.begin(),
+ [](const std::string& val) -> uint32_t {
+ return std::stoul(val, nullptr, 16);
+ });
+
+ updateECC(frame_data);
+
+ // Insert the frame address and corresponding frame data to the
+ // map
+ typename ArchType::FrameAddress frm_addr(frame_address);
+ frames_data_.insert(
+ std::pair<typename ArchType::FrameAddress, FrameData>(
+ frm_addr, frame_data));
+ }
+ return 0;
+}
+
+template <typename ArchType>
+void Frames<ArchType>::addMissingFrames(
+ const absl::optional<typename ArchType::Part>& part) {
+ auto current_frame_address =
+ absl::optional<typename ArchType::FrameAddress>(
+ typename ArchType::FrameAddress(0));
+ do {
+ auto iter = frames_data_.find(*current_frame_address);
+ if (iter == frames_data_.end()) {
+ FrameData frame_data(ArchType::words_per_frame, 0);
+ frames_data_.insert(
+ std::pair<typename ArchType::FrameAddress,
+ FrameData>(*current_frame_address,
+ frame_data));
+ }
+ current_frame_address =
+ part->GetNextFrameAddress(*current_frame_address);
+ } while (current_frame_address);
+}
+
+} // namespace xilinx
+} // namespace prjxray
+
+#endif // PRJXRAY_LIB_XILINX_FRAMES_H
diff --git a/lib/include/prjxray/xilinx/nop_packet.h b/lib/include/prjxray/xilinx/nop_packet.h
new file mode 100644
index 0000000..6f89e84
--- /dev/null
+++ b/lib/include/prjxray/xilinx/nop_packet.h
@@ -0,0 +1,24 @@
+#ifndef PRJXRAY_LIB_XILINX_NOP_PACKET_H
+#define PRJXRAY_LIB_XILINX_NOP_PACKET_H
+
+#include <prjxray/xilinx/configuration_packet.h>
+#include <prjxray/xilinx/configuration_register.h>
+
+namespace prjxray {
+namespace xilinx {
+
+template <typename ConfigRegType>
+class NopPacket : public ConfigurationPacket<ConfigRegType> {
+ public:
+ NopPacket()
+ : ConfigurationPacket<ConfigRegType>(
+ ConfigurationPacketType::TYPE1,
+ ConfigurationPacket<ConfigRegType>::Opcode::NOP,
+ ConfigRegType::CRC,
+ {}) {}
+};
+
+} // namespace xilinx
+} // namespace prjxray
+
+#endif // PRJXRAY_LIB_XILINX_NOP_PACKET_H
diff --git a/lib/include/prjxray/xilinx/spartan6/block_type.h b/lib/include/prjxray/xilinx/spartan6/block_type.h
new file mode 100644
index 0000000..a42a8db
--- /dev/null
+++ b/lib/include/prjxray/xilinx/spartan6/block_type.h
@@ -0,0 +1,37 @@
+#ifndef PRJXRAY_LIB_XILINX_SPARTAN6_BLOCK_TYPE_H_
+#define PRJXRAY_LIB_XILINX_SPARTAN6_BLOCK_TYPE_H_
+
+#include <ostream>
+
+#include <yaml-cpp/yaml.h>
+
+namespace prjxray {
+namespace xilinx {
+namespace spartan6 {
+
+// According to UG380 pg. 97 there are 3 types of configuration frames:
+// Type 0: Core: CLB, DSP, input/output interconnect (IOI), clocking
+// Type 1: Block RAM
+// Type 2: IOB
+enum class BlockType : unsigned int {
+ CLB_IOI_CLK = 0x0,
+ BLOCK_RAM = 0x1,
+ IOB = 0x2,
+};
+
+std::ostream& operator<<(std::ostream& o, BlockType value);
+
+} // namespace spartan6
+} // namespace xilinx
+} // namespace prjxray
+
+namespace YAML {
+template <>
+struct convert<prjxray::xilinx::spartan6::BlockType> {
+ static Node encode(const prjxray::xilinx::spartan6::BlockType& rhs);
+ static bool decode(const Node& node,
+ prjxray::xilinx::spartan6::BlockType& lhs);
+};
+} // namespace YAML
+
+#endif // PRJXRAY_LIB_XILINX_SPARTAN6_BLOCK_TYPE_H_
diff --git a/lib/include/prjxray/xilinx/spartan6/command.h b/lib/include/prjxray/xilinx/spartan6/command.h
new file mode 100644
index 0000000..93bafdb
--- /dev/null
+++ b/lib/include/prjxray/xilinx/spartan6/command.h
@@ -0,0 +1,35 @@
+#ifndef PRJXRAY_LIB_XILINX_SPARTAN6_COMMAND_H_
+#define PRJXRAY_LIB_XILINX_SPARTAN6_COMMAND_H_
+
+namespace prjxray {
+namespace xilinx {
+namespace spartan6 {
+
+// Command register map according to UG380 pg. 102
+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 spartan6
+} // namespace xilinx
+} // namespace prjxray
+
+#endif // PRJXRAY_LIB_XILINX_SPARTAN6_COMMAND_H_
diff --git a/lib/include/prjxray/xilinx/spartan6/configuration_bus.h b/lib/include/prjxray/xilinx/spartan6/configuration_bus.h
new file mode 100644
index 0000000..a5d0081
--- /dev/null
+++ b/lib/include/prjxray/xilinx/spartan6/configuration_bus.h
@@ -0,0 +1,91 @@
+#ifndef PRJXRAY_LIB_XILINX_SPARTAN6_CONFIGURATION_BUS_H_
+#define PRJXRAY_LIB_XILINX_SPARTAN6_CONFIGURATION_BUS_H_
+
+#include <algorithm>
+#include <cassert>
+#include <map>
+#include <memory>
+
+#include <absl/types/optional.h>
+#include <prjxray/xilinx/spartan6/configuration_column.h>
+#include <prjxray/xilinx/spartan6/frame_address.h>
+#include <yaml-cpp/yaml.h>
+
+namespace prjxray {
+namespace xilinx {
+namespace spartan6 {
+
+// ConfigurationBus represents a bus for sending frames to a specific BlockType
+// within a Row. An instance of ConfigurationBus will contain one or more
+// ConfigurationColumns.
+class ConfigurationBus {
+ public:
+ ConfigurationBus() = default;
+
+ // Constructs a ConfigurationBus from iterators yielding
+ // FrameAddresses. The frame address need not be contiguous or sorted
+ // but they must all have the same block type, row half, and row
+ // address components.
+ template <typename T>
+ ConfigurationBus(T first, T last);
+
+ // Returns true if the provided address falls into a valid segment of
+ // the address range on this bus. Only the column and minor components
+ // of the address are considered as all other components are outside
+ // the scope of a bus.
+ bool IsValidFrameAddress(FrameAddress address) const;
+
+ // Returns the next valid address on the bus in numerically increasing
+ // order. If the next address would fall outside this bus, no object is
+ // returned.
+ absl::optional<FrameAddress> GetNextFrameAddress(
+ FrameAddress address) const;
+
+ private:
+ friend struct YAML::convert<ConfigurationBus>;
+
+ std::map<unsigned int, ConfigurationColumn> configuration_columns_;
+};
+
+template <typename T>
+ConfigurationBus::ConfigurationBus(T first, T last) {
+ assert(
+ std::all_of(first, last, [&](const typename T::value_type& addr) {
+ return (addr.block_type() == first->block_type() &&
+ addr.row() == first->row());
+ }));
+
+ std::sort(first, last,
+ [](const FrameAddress& lhs, const FrameAddress& rhs) {
+ return lhs.column() < rhs.column();
+ });
+
+ for (auto col_first = first; col_first != last;) {
+ auto col_last = std::upper_bound(
+ col_first, last, col_first->column(),
+ [](const unsigned int& lhs, const FrameAddress& rhs) {
+ return lhs < rhs.column();
+ });
+
+ configuration_columns_.emplace(
+ col_first->column(),
+ std::move(ConfigurationColumn(col_first, col_last)));
+ col_first = col_last;
+ }
+}
+
+} // namespace spartan6
+} // namespace xilinx
+} // namespace prjxray
+
+namespace YAML {
+template <>
+struct convert<prjxray::xilinx::spartan6::ConfigurationBus> {
+ static Node encode(
+ const prjxray::xilinx::spartan6::ConfigurationBus& rhs);
+ static bool decode(const Node& node,
+ prjxray::xilinx::spartan6::ConfigurationBus& lhs);
+};
+} // namespace YAML
+
+#endif // PRJXRAY_LIB_XILINX_SPARTAN6_CONFIGURATION_BUS_H_
diff --git a/lib/include/prjxray/xilinx/spartan6/configuration_column.h b/lib/include/prjxray/xilinx/spartan6/configuration_column.h
new file mode 100644
index 0000000..af8edd6
--- /dev/null
+++ b/lib/include/prjxray/xilinx/spartan6/configuration_column.h
@@ -0,0 +1,75 @@
+#ifndef PRJXRAY_LIB_XILINX_SPARTAN6_CONFIGURATION_COLUMN_H_
+#define PRJXRAY_LIB_XILINX_SPARTAN6_CONFIGURATION_COLUMN_H_
+
+#include <algorithm>
+#include <cassert>
+
+#include <absl/types/optional.h>
+#include <prjxray/xilinx/spartan6/frame_address.h>
+#include <yaml-cpp/yaml.h>
+
+namespace prjxray {
+namespace xilinx {
+namespace spartan6 {
+
+// ConfigurationColumn represents an endpoint on a ConfigurationBus.
+class ConfigurationColumn {
+ public:
+ ConfigurationColumn() = default;
+ ConfigurationColumn(unsigned int frame_count)
+ : frame_count_(frame_count) {}
+
+ // Returns a ConfigurationColumn that describes a continguous range of
+ // minor addresses that encompasses the given
+ // FrameAddresses. The provided addresses must only
+ // differ only by their minor addresses.
+ template <typename T>
+ ConfigurationColumn(T first, T last);
+
+ // Returns true if the minor field of the address is within the valid
+ // range of this column.
+ bool IsValidFrameAddress(FrameAddress address) const;
+
+ // Returns the next address in numerical order. If the next address
+ // would be outside this column, return no object.
+ absl::optional<FrameAddress> GetNextFrameAddress(
+ FrameAddress address) const;
+
+ private:
+ friend struct YAML::convert<ConfigurationColumn>;
+
+ unsigned int frame_count_;
+};
+
+template <typename T>
+ConfigurationColumn::ConfigurationColumn(T first, T last) {
+ assert(
+ std::all_of(first, last, [&](const typename T::value_type& addr) {
+ return (addr.block_type() == first->block_type() &&
+ addr.row() == first->row() &&
+ addr.column() == first->column());
+ }));
+
+ auto max_minor = std::max_element(
+ first, last, [](const FrameAddress& lhs, const FrameAddress& rhs) {
+ return lhs.minor() < rhs.minor();
+ });
+
+ frame_count_ = max_minor->minor() + 1;
+}
+
+} // namespace spartan6
+} // namespace xilinx
+} // namespace prjxray
+
+namespace YAML {
+template <>
+struct convert<prjxray::xilinx::spartan6::ConfigurationColumn> {
+ static Node encode(
+ const prjxray::xilinx::spartan6::ConfigurationColumn& rhs);
+ static bool decode(const Node& node,
+ prjxray::xilinx::spartan6::ConfigurationColumn& lhs);
+};
+} // namespace YAML
+
+#endif // PRJXRAY_LIB_XILINX_SPARTAN6_CONFIGURATION_COLUMN_H_
diff --git a/lib/include/prjxray/xilinx/spartan6/configuration_row.h b/lib/include/prjxray/xilinx/spartan6/configuration_row.h
new file mode 100644
index 0000000..a2b267e
--- /dev/null
+++ b/lib/include/prjxray/xilinx/spartan6/configuration_row.h
@@ -0,0 +1,90 @@
+#ifndef PRJXRAY_LIB_XILINX_SPARTAN6_ROW_H_
+#define PRJXRAY_LIB_XILINX_SPARTAN6_ROW_H_
+
+#include <algorithm>
+#include <cassert>
+#include <map>
+#include <memory>
+
+#include <absl/types/optional.h>
+#include <prjxray/xilinx/spartan6/block_type.h>
+#include <prjxray/xilinx/spartan6/configuration_bus.h>
+#include <prjxray/xilinx/spartan6/frame_address.h>
+#include <yaml-cpp/yaml.h>
+
+namespace prjxray {
+namespace xilinx {
+namespace spartan6 {
+
+class Row {
+ public:
+ Row() = default;
+
+ // Construct a row from a range of iterators that yield FrameAddresses.
+ // The addresses may be noncontinguous and/or unsorted but all must
+ // share the same row half and row components.
+ template <typename T>
+ Row(T first, T last);
+
+ // Returns true if the provided address falls within a valid range
+ // attributed to this row. Only the block type, column, and minor
+ // address components are considerd as the remaining components are
+ // outside the scope of a row.
+ bool IsValidFrameAddress(FrameAddress address) const;
+
+ // Returns the next numerically increasing address within the Row. If
+ // the next address would fall outside the Row, no object is returned.
+ // If the next address would cross from one block type to another, no
+ // object is returned as other rows of the same block type come before
+ // other block types numerically.
+ absl::optional<FrameAddress> GetNextFrameAddress(
+ FrameAddress address) const;
+
+ private:
+ friend struct YAML::convert<Row>;
+
+ std::map<BlockType, ConfigurationBus> configuration_buses_;
+};
+
+template <typename T>
+Row::Row(T first, T last) {
+ assert(
+ std::all_of(first, last, [&](const typename T::value_type& addr) {
+ return (addr.is_bottom_half_rows() ==
+ first->is_bottom_half_rows() &&
+ addr.row() == first->row());
+ }));
+
+ std::sort(first, last,
+ [](const FrameAddress& lhs, const FrameAddress& rhs) {
+ return lhs.block_type() < rhs.block_type();
+ });
+
+ for (auto bus_first = first; bus_first != last;) {
+ auto bus_last = std::upper_bound(
+ bus_first, last, bus_first->block_type(),
+ [](const BlockType& lhs, const FrameAddress& rhs) {
+ return lhs < rhs.block_type();
+ });
+
+ configuration_buses_.emplace(
+ bus_first->block_type(),
+ std::move(ConfigurationBus(bus_first, bus_last)));
+ bus_first = bus_last;
+ }
+}
+
+} // namespace spartan6
+} // namespace xilinx
+} // namespace prjxray
+
+namespace YAML {
+template <>
+struct convert<prjxray::xilinx::spartan6::Row> {
+ static Node encode(const prjxray::xilinx::spartan6::Row& rhs);
+ static bool decode(const Node& node,
+ prjxray::xilinx::spartan6::Row& lhs);
+};
+} // namespace YAML
+
+#endif // PRJXRAY_LIB_XILINX_SPARTAN6_ROW_H_
diff --git a/lib/include/prjxray/xilinx/spartan6/frame_address.h b/lib/include/prjxray/xilinx/spartan6/frame_address.h
new file mode 100644
index 0000000..651d86a
--- /dev/null
+++ b/lib/include/prjxray/xilinx/spartan6/frame_address.h
@@ -0,0 +1,55 @@
+#ifndef PRJXRAY_LIB_XILINX_SPARTAN6_FRAME_ADDRESS_H_
+#define PRJXRAY_LIB_XILINX_SPARTAN6_FRAME_ADDRESS_H_
+
+#include <cstdint>
+#include <ostream>
+
+#include <prjxray/xilinx/spartan6/block_type.h>
+#include <yaml-cpp/yaml.h>
+
+#ifdef _GNU_SOURCE
+#undef minor
+#endif
+
+namespace prjxray {
+namespace xilinx {
+namespace spartan6 {
+class FrameAddress {
+ public:
+ FrameAddress() : address_(0) {}
+
+ FrameAddress(uint32_t address) : address_(address){};
+
+ FrameAddress(BlockType block_type,
+ uint8_t row,
+ uint8_t column,
+ uint16_t minor);
+
+ operator uint32_t() const { return address_; }
+ bool is_bottom_half_rows() const;
+ BlockType block_type() const;
+ uint8_t row() const;
+ uint8_t column() const;
+ uint16_t minor() const;
+
+ private:
+ uint32_t address_;
+};
+
+std::ostream& operator<<(std::ostream& o, const FrameAddress& addr);
+
+} // namespace spartan6
+} // namespace xilinx
+} // namespace prjxray
+
+namespace YAML {
+
+namespace spartan6 = prjxray::xilinx::spartan6;
+
+template <>
+struct convert<spartan6::FrameAddress> {
+ static Node encode(const spartan6::FrameAddress& rhs);
+ static bool decode(const Node& node, spartan6::FrameAddress& lhs);
+};
+} // namespace YAML
+#endif // PRJXRAY_LIB_XILINX_SPARTAN6_FRAME_ADDRESS_H_
diff --git a/lib/include/prjxray/xilinx/spartan6/global_clock_region.h b/lib/include/prjxray/xilinx/spartan6/global_clock_region.h
new file mode 100644
index 0000000..bb0ce93
--- /dev/null
+++ b/lib/include/prjxray/xilinx/spartan6/global_clock_region.h
@@ -0,0 +1,93 @@
+#ifndef PRJXRAY_LIB_XILINX_SPARTAN6_GLOBAL_CLOCK_REGION_H_
+#define PRJXRAY_LIB_XILINX_SPARTAN6_GLOBAL_CLOCK_REGION_H_
+
+#include <algorithm>
+#include <cassert>
+#include <map>
+#include <memory>
+
+#include <absl/types/optional.h>
+#include <prjxray/xilinx/spartan6/configuration_row.h>
+#include <prjxray/xilinx/spartan6/frame_address.h>
+#include <yaml-cpp/yaml.h>
+
+namespace prjxray {
+namespace xilinx {
+namespace spartan6 {
+
+// GlobalClockRegion represents all the resources associated with a single
+// global clock buffer (BUFG) tile. In 7-Series FPGAs, there are two BUFG
+// tiles that divide the chip into top and bottom "halves". Each half may
+// contains any number of rows, buses, and columns.
+class GlobalClockRegion {
+ public:
+ GlobalClockRegion() = default;
+
+ // Construct a GlobalClockRegion from iterators that yield
+ // FrameAddresses which are known to be valid. The addresses may be
+ // noncontinguous and/or unordered but they must share the same row
+ // half address component.
+ template <typename T>
+ GlobalClockRegion(T first, T last);
+
+ // Returns true if the address falls within a valid range inside the
+ // global clock region. The row half address component is ignored as it
+ // is outside the context of a global clock region.
+ bool IsValidFrameAddress(FrameAddress address) const;
+
+ // Returns the next numerically increasing address known within this
+ // global clock region. If the next address would fall outside this
+ // global clock region, no address is returned. If the next address
+ // would jump to a different block type, no address is returned as the
+ // same block type in other global clock regions come numerically
+ // before other block types.
+ absl::optional<FrameAddress> GetNextFrameAddress(
+ FrameAddress address) const;
+
+ private:
+ friend struct YAML::convert<GlobalClockRegion>;
+
+ std::map<unsigned int, Row> rows_;
+};
+
+template <typename T>
+GlobalClockRegion::GlobalClockRegion(T first, T last) {
+ assert(
+ std::all_of(first, last, [&](const typename T::value_type& addr) {
+ return addr.is_bottom_half_rows() ==
+ first->is_bottom_half_rows();
+ }));
+
+ std::sort(first, last,
+ [](const FrameAddress& lhs, const FrameAddress& rhs) {
+ return lhs.row() < rhs.row();
+ });
+
+ for (auto row_first = first; row_first != last;) {
+ auto row_last = std::upper_bound(
+ row_first, last, row_first->row(),
+ [](const uint8_t& lhs, const FrameAddress& rhs) {
+ return lhs < rhs.row();
+ });
+
+ rows_.emplace(row_first->row(),
+ std::move(Row(row_first, row_last)));
+ row_first = row_last;
+ }
+}
+
+} // namespace spartan6
+} // namespace xilinx
+} // namespace prjxray
+
+namespace YAML {
+template <>
+struct convert<prjxray::xilinx::spartan6::GlobalClockRegion> {
+ static Node encode(
+ const prjxray::xilinx::spartan6::GlobalClockRegion& rhs);
+ static bool decode(const Node& node,
+ prjxray::xilinx::spartan6::GlobalClockRegion& lhs);
+};
+} // namespace YAML
+
+#endif // PRJXRAY_LIB_XILINX_SPARTAN6_GLOBAL_CLOCK_REGION_H_
diff --git a/lib/include/prjxray/xilinx/spartan6/part.h b/lib/include/prjxray/xilinx/spartan6/part.h
new file mode 100644
index 0000000..df11384
--- /dev/null
+++ b/lib/include/prjxray/xilinx/spartan6/part.h
@@ -0,0 +1,70 @@
+#ifndef PRJXRAY_LIB_XILINX_SPARTAN6_PART_H_
+#define PRJXRAY_LIB_XILINX_SPARTAN6_PART_H_
+
+#include <algorithm>
+#include <cstdint>
+#include <memory>
+#include <string>
+#include <vector>
+
+#include <absl/types/optional.h>
+#include <prjxray/xilinx/spartan6/global_clock_region.h>
+#include <yaml-cpp/yaml.h>
+
+namespace prjxray {
+namespace xilinx {
+namespace spartan6 {
+
+class Part {
+ public:
+ constexpr static uint32_t kInvalidIdcode = 0;
+
+ static absl::optional<Part> FromFile(const std::string& path);
+
+ // Constructs an invalid part with a zero IDCODE. Required for YAML
+ // conversion but shouldn't be used otherwise.
+ Part() : idcode_(kInvalidIdcode) {}
+
+ template <typename T>
+ Part(uint32_t idcode, T collection)
+ : Part(idcode, std::begin(collection), std::end(collection)) {}
+
+ template <typename T>
+ Part(uint32_t idcode, T first, T last);
+
+ uint32_t idcode() const { return idcode_; }
+
+ bool IsValidFrameAddress(FrameAddress address) const;
+
+ absl::optional<FrameAddress> GetNextFrameAddress(
+ FrameAddress address) const;
+
+ private:
+ friend struct YAML::convert<Part>;
+
+ uint32_t idcode_;
+ spartan6::GlobalClockRegion top_region_;
+ spartan6::GlobalClockRegion bottom_region_;
+};
+
+template <typename T>
+Part::Part(uint32_t idcode, T first, T last) : idcode_(idcode) {
+ top_region_ = spartan6::GlobalClockRegion(first, last);
+}
+
+} // namespace spartan6
+} // namespace xilinx
+} // namespace prjxray
+
+namespace YAML {
+
+namespace spartan6 = prjxray::xilinx::spartan6;
+
+template <>
+struct convert<spartan6::Part> {
+ static Node encode(const spartan6::Part& rhs);
+ static bool decode(const Node& node, spartan6::Part& lhs);
+};
+} // namespace YAML
+
+#endif // PRJXRAY_LIB_XILINX_SPARTAN6_PART_H_
diff --git a/lib/include/prjxray/xilinx/xc7series/block_type.h b/lib/include/prjxray/xilinx/xc7series/block_type.h
new file mode 100644
index 0000000..400a19f
--- /dev/null
+++ b/lib/include/prjxray/xilinx/xc7series/block_type.h
@@ -0,0 +1,34 @@
+#ifndef PRJXRAY_LIB_XILINX_XC7SERIES_BLOCK_TYPE_H_
+#define PRJXRAY_LIB_XILINX_XC7SERIES_BLOCK_TYPE_H_
+
+#include <ostream>
+
+#include <yaml-cpp/yaml.h>
+
+namespace prjxray {
+namespace xilinx {
+namespace xc7series {
+
+enum class BlockType : unsigned int {
+ CLB_IO_CLK = 0x0,
+ BLOCK_RAM = 0x1,
+ CFG_CLB = 0x2,
+ /* reserved = 0x3, */
+};
+
+std::ostream& operator<<(std::ostream& o, BlockType value);
+
+} // namespace xc7series
+} // namespace xilinx
+} // namespace prjxray
+
+namespace YAML {
+template <>
+struct convert<prjxray::xilinx::xc7series::BlockType> {
+ static Node encode(const prjxray::xilinx::xc7series::BlockType& rhs);
+ static bool decode(const Node& node,
+ prjxray::xilinx::xc7series::BlockType& lhs);
+};
+} // namespace YAML
+
+#endif // PRJXRAY_LIB_XILINX_XC7SERIES_BLOCK_TYPE_H_
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_bus.h b/lib/include/prjxray/xilinx/xc7series/configuration_bus.h
new file mode 100644
index 0000000..33e9619
--- /dev/null
+++ b/lib/include/prjxray/xilinx/xc7series/configuration_bus.h
@@ -0,0 +1,93 @@
+#ifndef PRJXRAY_LIB_XILINX_XC7SERIES_CONFIGURATION_BUS_H_
+#define PRJXRAY_LIB_XILINX_XC7SERIES_CONFIGURATION_BUS_H_
+
+#include <algorithm>
+#include <cassert>
+#include <map>
+#include <memory>
+
+#include <absl/types/optional.h>
+#include <prjxray/xilinx/xc7series/configuration_column.h>
+#include <prjxray/xilinx/xc7series/frame_address.h>
+#include <yaml-cpp/yaml.h>
+
+namespace prjxray {
+namespace xilinx {
+namespace xc7series {
+
+// ConfigurationBus represents a bus for sending frames to a specific BlockType
+// within a Row. An instance of ConfigurationBus will contain one or more
+// ConfigurationColumns.
+class ConfigurationBus {
+ public:
+ ConfigurationBus() = default;
+
+ // Constructs a ConfigurationBus from iterators yielding
+ // FrameAddresses. The frame address need not be contiguous or sorted
+ // but they must all have the same block type, row half, and row
+ // address components.
+ template <typename T>
+ ConfigurationBus(T first, T last);
+
+ // Returns true if the provided address falls into a valid segment of
+ // the address range on this bus. Only the column and minor components
+ // of the address are considered as all other components are outside
+ // the scope of a bus.
+ bool IsValidFrameAddress(FrameAddress address) const;
+
+ // Returns the next valid address on the bus in numerically increasing
+ // order. If the next address would fall outside this bus, no object is
+ // returned.
+ absl::optional<FrameAddress> GetNextFrameAddress(
+ FrameAddress address) const;
+
+ private:
+ friend struct YAML::convert<ConfigurationBus>;
+
+ std::map<unsigned int, ConfigurationColumn> configuration_columns_;
+};
+
+template <typename T>
+ConfigurationBus::ConfigurationBus(T first, T last) {
+ assert(
+ std::all_of(first, last, [&](const typename T::value_type& addr) {
+ return (addr.block_type() == first->block_type() &&
+ addr.is_bottom_half_rows() ==
+ first->is_bottom_half_rows() &&
+ addr.row() == first->row());
+ }));
+
+ std::sort(first, last,
+ [](const FrameAddress& lhs, const FrameAddress& rhs) {
+ return lhs.column() < rhs.column();
+ });
+
+ for (auto col_first = first; col_first != last;) {
+ auto col_last = std::upper_bound(
+ col_first, last, col_first->column(),
+ [](const unsigned int& lhs, const FrameAddress& rhs) {
+ return lhs < rhs.column();
+ });
+
+ configuration_columns_.emplace(
+ col_first->column(),
+ std::move(ConfigurationColumn(col_first, col_last)));
+ col_first = col_last;
+ }
+}
+
+} // namespace xc7series
+} // namespace xilinx
+} // namespace prjxray
+
+namespace YAML {
+template <>
+struct convert<prjxray::xilinx::xc7series::ConfigurationBus> {
+ static Node encode(
+ const prjxray::xilinx::xc7series::ConfigurationBus& rhs);
+ static bool decode(const Node& node,
+ prjxray::xilinx::xc7series::ConfigurationBus& lhs);
+};
+} // namespace YAML
+
+#endif // PRJXRAY_LIB_XILINX_XC7SERIES_CONFIGURATION_BUS_H_
diff --git a/lib/include/prjxray/xilinx/xc7series/configuration_column.h b/lib/include/prjxray/xilinx/xc7series/configuration_column.h
new file mode 100644
index 0000000..b15415e
--- /dev/null
+++ b/lib/include/prjxray/xilinx/xc7series/configuration_column.h
@@ -0,0 +1,78 @@
+#ifndef PRJXRAY_LIB_XILINX_XC7SERIES_CONFIGURATION_COLUMN_H_
+#define PRJXRAY_LIB_XILINX_XC7SERIES_CONFIGURATION_COLUMN_H_
+
+#include <algorithm>
+#include <cassert>
+
+#include <absl/types/optional.h>
+#include <prjxray/xilinx/xc7series/frame_address.h>
+#include <yaml-cpp/yaml.h>
+
+namespace prjxray {
+namespace xilinx {
+namespace xc7series {
+
+// ConfigurationColumn represents an endpoint on a ConfigurationBus.
+class ConfigurationColumn {
+ public:
+ ConfigurationColumn() = default;
+ ConfigurationColumn(unsigned int frame_count)
+ : frame_count_(frame_count) {}
+
+ // Returns a ConfigurationColumn that describes a continguous range of
+ // minor addresses that encompasses the given
+ // FrameAddresses. The provided addresses must only
+ // differ only by their minor addresses.
+ template <typename T>
+ ConfigurationColumn(T first, T last);
+
+ // Returns true if the minor field of the address is within the valid
+ // range of this column.
+ bool IsValidFrameAddress(FrameAddress address) const;
+
+ // Returns the next address in numerical order. If the next address
+ // would be outside this column, return no object.
+ absl::optional<FrameAddress> GetNextFrameAddress(
+ FrameAddress address) const;
+
+ private:
+ friend struct YAML::convert<ConfigurationColumn>;
+
+ unsigned int frame_count_;
+};
+
+template <typename T>
+ConfigurationColumn::ConfigurationColumn(T first, T last) {
+ assert(
+ std::all_of(first, last, [&](const typename T::value_type& addr) {
+ return (addr.block_type() == first->block_type() &&
+ addr.is_bottom_half_rows() ==
+ first->is_bottom_half_rows() &&
+ addr.row() == first->row() &&
+ addr.column() == first->column());
+ }));
+
+ auto max_minor = std::max_element(
+ first, last, [](const FrameAddress& lhs, const FrameAddress& rhs) {
+ return lhs.minor() < rhs.minor();
+ });
+
+ frame_count_ = max_minor->minor() + 1;
+}
+
+} // namespace xc7series
+} // namespace xilinx
+} // namespace prjxray
+
+namespace YAML {
+template <>
+struct convert<prjxray::xilinx::xc7series::ConfigurationColumn> {
+ static Node encode(
+ const prjxray::xilinx::xc7series::ConfigurationColumn& rhs);
+ static bool decode(
+ const Node& node,
+ prjxray::xilinx::xc7series::ConfigurationColumn& lhs);
+};
+} // namespace YAML
+
+#endif // PRJXRAY_LIB_XILINX_XC7SERIES_CONFIGURATION_COLUMN_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..0630737
--- /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/configuration_packet.h>
+#include <prjxray/xilinx/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_row.h b/lib/include/prjxray/xilinx/xc7series/configuration_row.h
new file mode 100644
index 0000000..4cb7f56
--- /dev/null
+++ b/lib/include/prjxray/xilinx/xc7series/configuration_row.h
@@ -0,0 +1,90 @@
+#ifndef PRJXRAY_LIB_XILINX_XC7SERIES_ROW_H_
+#define PRJXRAY_LIB_XILINX_XC7SERIES_ROW_H_
+
+#include <algorithm>
+#include <cassert>
+#include <map>
+#include <memory>
+
+#include <absl/types/optional.h>
+#include <prjxray/xilinx/xc7series/block_type.h>
+#include <prjxray/xilinx/xc7series/configuration_bus.h>
+#include <prjxray/xilinx/xc7series/frame_address.h>
+#include <yaml-cpp/yaml.h>
+
+namespace prjxray {
+namespace xilinx {
+namespace xc7series {
+
+class Row {
+ public:
+ Row() = default;
+
+ // Construct a row from a range of iterators that yield FrameAddresses.
+ // The addresses may be noncontinguous and/or unsorted but all must
+ // share the same row half and row components.
+ template <typename T>
+ Row(T first, T last);
+
+ // Returns true if the provided address falls within a valid range
+ // attributed to this row. Only the block type, column, and minor
+ // address components are considerd as the remaining components are
+ // outside the scope of a row.
+ bool IsValidFrameAddress(FrameAddress address) const;
+
+ // Returns the next numerically increasing address within the Row. If
+ // the next address would fall outside the Row, no object is returned.
+ // If the next address would cross from one block type to another, no
+ // object is returned as other rows of the same block type come before
+ // other block types numerically.
+ absl::optional<FrameAddress> GetNextFrameAddress(
+ FrameAddress address) const;
+
+ private:
+ friend struct YAML::convert<Row>;
+
+ std::map<BlockType, ConfigurationBus> configuration_buses_;
+};
+
+template <typename T>
+Row::Row(T first, T last) {
+ assert(
+ std::all_of(first, last, [&](const typename T::value_type& addr) {
+ return (addr.is_bottom_half_rows() ==
+ first->is_bottom_half_rows() &&
+ addr.row() == first->row());
+ }));
+
+ std::sort(first, last,
+ [](const FrameAddress& lhs, const FrameAddress& rhs) {
+ return lhs.block_type() < rhs.block_type();
+ });
+
+ for (auto bus_first = first; bus_first != last;) {
+ auto bus_last = std::upper_bound(
+ bus_first, last, bus_first->block_type(),
+ [](const BlockType& lhs, const FrameAddress& rhs) {
+ return lhs < rhs.block_type();
+ });
+
+ configuration_buses_.emplace(
+ bus_first->block_type(),
+ std::move(ConfigurationBus(bus_first, bus_last)));
+ bus_first = bus_last;
+ }
+}
+
+} // namespace xc7series
+} // namespace xilinx
+} // namespace prjxray
+
+namespace YAML {
+template <>
+struct convert<prjxray::xilinx::xc7series::Row> {
+ static Node encode(const prjxray::xilinx::xc7series::Row& rhs);
+ static bool decode(const Node& node,
+ prjxray::xilinx::xc7series::Row& lhs);
+};
+} // namespace YAML
+
+#endif // PRJXRAY_LIB_XILINX_XC7SERIES_ROW_H_
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..63513fd
--- /dev/null
+++ b/lib/include/prjxray/xilinx/xc7series/ecc.h
@@ -0,0 +1,22 @@
+#ifndef PRJXRAY_LIB_XILINX_XC7SERIES_ECC_H_
+#define PRJXRAY_LIB_XILINX_XC7SERIES_ECC_H_
+
+#include <cstdint>
+#include <vector>
+
+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);
+
+// Updates the ECC information in the frame.
+void updateECC(std::vector<uint32_t>& data);
+
+} // namespace xc7series
+} // namespace xilinx
+} // namespace prjxray
+
+#endif // PRJXRAY_LIB_XILINX_XC7SERIES_ECC_H_
diff --git a/lib/include/prjxray/xilinx/xc7series/frame_address.h b/lib/include/prjxray/xilinx/xc7series/frame_address.h
new file mode 100644
index 0000000..c15b87f
--- /dev/null
+++ b/lib/include/prjxray/xilinx/xc7series/frame_address.h
@@ -0,0 +1,56 @@
+#ifndef PRJXRAY_LIB_XILINX_XC7SERIES_FRAME_ADDRESS_H_
+#define PRJXRAY_LIB_XILINX_XC7SERIES_FRAME_ADDRESS_H_
+
+#include <cstdint>
+#include <ostream>
+
+#include <prjxray/xilinx/xc7series/block_type.h>
+#include <yaml-cpp/yaml.h>
+
+#ifdef _GNU_SOURCE
+#undef minor
+#endif
+
+namespace prjxray {
+namespace xilinx {
+namespace xc7series {
+
+class FrameAddress {
+ public:
+ FrameAddress() : address_(0) {}
+
+ FrameAddress(uint32_t address) : address_(address){};
+
+ FrameAddress(BlockType block_type,
+ bool is_bottom_half_rows,
+ uint8_t row,
+ uint16_t column,
+ uint8_t minor);
+
+ operator uint32_t() const { return address_; }
+
+ BlockType block_type() const;
+ bool is_bottom_half_rows() const;
+ uint8_t row() const;
+ uint16_t column() const;
+ uint8_t minor() const;
+
+ private:
+ uint32_t address_;
+};
+
+std::ostream& operator<<(std::ostream& o, const FrameAddress& addr);
+
+} // namespace xc7series
+} // namespace xilinx
+} // namespace prjxray
+
+namespace YAML {
+template <>
+struct convert<prjxray::xilinx::xc7series::FrameAddress> {
+ static Node encode(const prjxray::xilinx::xc7series::FrameAddress& rhs);
+ static bool decode(const Node& node,
+ prjxray::xilinx::xc7series::FrameAddress& lhs);
+};
+} // namespace YAML
+#endif // PRJXRAY_LIB_XILINX_XC7SERIES_FRAME_ADDRESS_H_
diff --git a/lib/include/prjxray/xilinx/xc7series/global_clock_region.h b/lib/include/prjxray/xilinx/xc7series/global_clock_region.h
new file mode 100644
index 0000000..8d791a2
--- /dev/null
+++ b/lib/include/prjxray/xilinx/xc7series/global_clock_region.h
@@ -0,0 +1,93 @@
+#ifndef PRJXRAY_LIB_XILINX_XC7SERIES_GLOBAL_CLOCK_REGION_H_
+#define PRJXRAY_LIB_XILINX_XC7SERIES_GLOBAL_CLOCK_REGION_H_
+
+#include <algorithm>
+#include <cassert>
+#include <map>
+#include <memory>
+
+#include <absl/types/optional.h>
+#include <prjxray/xilinx/xc7series/configuration_row.h>
+#include <prjxray/xilinx/xc7series/frame_address.h>
+#include <yaml-cpp/yaml.h>
+
+namespace prjxray {
+namespace xilinx {
+namespace xc7series {
+
+// GlobalClockRegion represents all the resources associated with a single
+// global clock buffer (BUFG) tile. In 7-Series FPGAs, there are two BUFG
+// tiles that divide the chip into top and bottom "halves". Each half may
+// contains any number of rows, buses, and columns.
+class GlobalClockRegion {
+ public:
+ GlobalClockRegion() = default;
+
+ // Construct a GlobalClockRegion from iterators that yield
+ // FrameAddresses which are known to be valid. The addresses may be
+ // noncontinguous and/or unordered but they must share the same row
+ // half address component.
+ template <typename T>
+ GlobalClockRegion(T first, T last);
+
+ // Returns true if the address falls within a valid range inside the
+ // global clock region. The row half address component is ignored as it
+ // is outside the context of a global clock region.
+ bool IsValidFrameAddress(FrameAddress address) const;
+
+ // Returns the next numerically increasing address known within this
+ // global clock region. If the next address would fall outside this
+ // global clock region, no address is returned. If the next address
+ // would jump to a different block type, no address is returned as the
+ // same block type in other global clock regions come numerically
+ // before other block types.
+ absl::optional<FrameAddress> GetNextFrameAddress(
+ FrameAddress address) const;
+
+ private:
+ friend struct YAML::convert<GlobalClockRegion>;
+
+ std::map<unsigned int, Row> rows_;
+};
+
+template <typename T>
+GlobalClockRegion::GlobalClockRegion(T first, T last) {
+ assert(
+ std::all_of(first, last, [&](const typename T::value_type& addr) {
+ return addr.is_bottom_half_rows() ==
+ first->is_bottom_half_rows();
+ }));
+
+ std::sort(first, last,
+ [](const FrameAddress& lhs, const FrameAddress& rhs) {
+ return lhs.row() < rhs.row();
+ });
+
+ for (auto row_first = first; row_first != last;) {
+ auto row_last = std::upper_bound(
+ row_first, last, row_first->row(),
+ [](const uint8_t& lhs, const FrameAddress& rhs) {
+ return lhs < rhs.row();
+ });
+
+ rows_.emplace(row_first->row(),
+ std::move(Row(row_first, row_last)));
+ row_first = row_last;
+ }
+}
+
+} // namespace xc7series
+} // namespace xilinx
+} // namespace prjxray
+
+namespace YAML {
+template <>
+struct convert<prjxray::xilinx::xc7series::GlobalClockRegion> {
+ static Node encode(
+ const prjxray::xilinx::xc7series::GlobalClockRegion& rhs);
+ static bool decode(const Node& node,
+ prjxray::xilinx::xc7series::GlobalClockRegion& lhs);
+};
+} // namespace YAML
+
+#endif // PRJXRAY_LIB_XILINX_XC7SERIES_GLOBAL_CLOCK_REGION_H_
diff --git a/lib/include/prjxray/xilinx/xc7series/part.h b/lib/include/prjxray/xilinx/xc7series/part.h
new file mode 100644
index 0000000..90847e4
--- /dev/null
+++ b/lib/include/prjxray/xilinx/xc7series/part.h
@@ -0,0 +1,74 @@
+#ifndef PRJXRAY_LIB_XILINX_XC7SERIES_PART_H_
+#define PRJXRAY_LIB_XILINX_XC7SERIES_PART_H_
+
+#include <algorithm>
+#include <cstdint>
+#include <memory>
+#include <string>
+#include <vector>
+
+#include <absl/types/optional.h>
+#include <prjxray/xilinx/xc7series/global_clock_region.h>
+#include <yaml-cpp/yaml.h>
+
+namespace prjxray {
+namespace xilinx {
+namespace xc7series {
+
+class Part {
+ public:
+ constexpr static uint32_t kInvalidIdcode = 0;
+
+ static absl::optional<Part> FromFile(const std::string& path);
+
+ // Constructs an invalid part with a zero IDCODE. Required for YAML
+ // conversion but shouldn't be used otherwise.
+ Part() : idcode_(kInvalidIdcode) {}
+
+ template <typename T>
+ Part(uint32_t idcode, T collection)
+ : Part(idcode, std::begin(collection), std::end(collection)) {}
+
+ template <typename T>
+ Part(uint32_t idcode, T first, T last);
+
+ uint32_t idcode() const { return idcode_; }
+
+ bool IsValidFrameAddress(FrameAddress address) const;
+
+ absl::optional<FrameAddress> GetNextFrameAddress(
+ FrameAddress address) const;
+
+ private:
+ friend struct YAML::convert<Part>;
+
+ uint32_t idcode_;
+ GlobalClockRegion top_region_;
+ GlobalClockRegion bottom_region_;
+};
+
+template <typename T>
+Part::Part(uint32_t idcode, T first, T last) : idcode_(idcode) {
+ auto first_of_top =
+ std::partition(first, last, [](const FrameAddress& addr) {
+ return addr.is_bottom_half_rows();
+ });
+
+ top_region_ = GlobalClockRegion(first_of_top, last);
+ bottom_region_ = GlobalClockRegion(first, first_of_top);
+}
+
+} // namespace xc7series
+} // namespace xilinx
+} // namespace prjxray
+
+namespace YAML {
+template <>
+struct convert<prjxray::xilinx::xc7series::Part> {
+ static Node encode(const prjxray::xilinx::xc7series::Part& rhs);
+ static bool decode(const Node& node,
+ prjxray::xilinx::xc7series::Part& lhs);
+};
+} // namespace YAML
+
+#endif // PRJXRAY_LIB_XILINX_XC7SERIES_PART_H_
diff --git a/lib/memory_mapped_file.cc b/lib/memory_mapped_file.cc
new file mode 100644
index 0000000..e6ada90
--- /dev/null
+++ b/lib/memory_mapped_file.cc
@@ -0,0 +1,51 @@
+#include <prjxray/memory_mapped_file.h>
+
+#include <fcntl.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+namespace prjxray {
+
+std::unique_ptr<MemoryMappedFile> MemoryMappedFile::InitWithFile(
+ const std::string& path) {
+ int fd = open(path.c_str(), O_RDONLY, 0);
+ if (fd == -1)
+ return nullptr;
+
+ struct stat statbuf;
+ if (fstat(fd, &statbuf) < 0) {
+ close(fd);
+ return nullptr;
+ }
+
+ // mmap() will fail with EINVAL if length==0. If this file is
+ // zero-length, return an object (to indicate the file exists) but
+ // load it with a nullptr and zero length.
+ if (statbuf.st_size == 0) {
+ close(fd);
+ return std::unique_ptr<MemoryMappedFile>(
+ new MemoryMappedFile(nullptr, 0));
+ }
+
+ void* file_map = mmap(NULL, statbuf.st_size, PROT_READ,
+ MAP_PRIVATE | MAP_POPULATE, fd, 0);
+
+ // If mmap() succeeded, the fd is no longer needed as the mapping will
+ // keep the file open. If mmap() failed, the fd needs to be closed
+ // anyway.
+ close(fd);
+
+ if (file_map == MAP_FAILED)
+ return nullptr;
+
+ return std::unique_ptr<MemoryMappedFile>(
+ new MemoryMappedFile(file_map, statbuf.st_size));
+}
+
+MemoryMappedFile::~MemoryMappedFile() {
+ munmap(data_, size_);
+}
+
+} // namespace prjxray
diff --git a/lib/memory_mapped_file_test.cc b/lib/memory_mapped_file_test.cc
new file mode 100644
index 0000000..6a4477d
--- /dev/null
+++ b/lib/memory_mapped_file_test.cc
@@ -0,0 +1,21 @@
+#include <prjxray/memory_mapped_file.h>
+
+#include <gtest/gtest.h>
+
+TEST(MemoryMappedFileTest, NonExistantFile) {
+ EXPECT_FALSE(prjxray::MemoryMappedFile::InitWithFile("does_not_exist"));
+}
+
+TEST(MemoryMappedFileTest, ZeroLengthFileReturnObjectWithZeroLength) {
+ auto file = prjxray::MemoryMappedFile::InitWithFile("empty_file");
+ ASSERT_TRUE(file);
+ EXPECT_EQ(nullptr, file->data());
+ EXPECT_EQ(static_cast<size_t>(0), file->size());
+}
+
+TEST(MemoryMappedFileTest, ExistingFile) {
+ auto file = prjxray::MemoryMappedFile::InitWithFile("small_file");
+ ASSERT_TRUE(file);
+ EXPECT_EQ(static_cast<size_t>(4), file->size());
+ EXPECT_EQ(0, memcmp("foo\n", file->data(), 4));
+}
diff --git a/lib/segbits_file_reader.cc b/lib/segbits_file_reader.cc
new file mode 100644
index 0000000..b06b674
--- /dev/null
+++ b/lib/segbits_file_reader.cc
@@ -0,0 +1,58 @@
+#include <prjxray/segbits_file_reader.h>
+
+namespace prjxray {
+
+std::unique_ptr<SegbitsFileReader> SegbitsFileReader::InitWithFile(
+ const std::string& path) {
+ auto mapped_file = MemoryMappedFile::InitWithFile(path);
+ if (!mapped_file)
+ return nullptr;
+
+ return std::unique_ptr<SegbitsFileReader>(
+ new SegbitsFileReader(std::move(mapped_file)));
+}
+
+SegbitsFileReader::iterator SegbitsFileReader::begin() {
+ return iterator(
+ absl::string_view(static_cast<const char*>(mapped_file_->data()),
+ mapped_file_->size()));
+}
+
+SegbitsFileReader::iterator SegbitsFileReader::end() {
+ return iterator(absl::string_view());
+}
+
+SegbitsFileReader::value_type::value_type(const absl::string_view& view) {
+ size_t separator_start = view.find_first_of(" \t\n");
+ if (separator_start == absl::string_view::npos) {
+ tag_ = view;
+ bit_ = absl::string_view();
+ return;
+ }
+
+ size_t bit_start = view.find_first_not_of(" \t", separator_start);
+ size_t newline = view.find('\n', bit_start);
+ if (newline == absl::string_view::npos) {
+ tag_ = view.substr(0, separator_start);
+ bit_ = view.substr(bit_start);
+ return;
+ }
+
+ size_t bit_len = newline - bit_start;
+ tag_ = view.substr(0, separator_start);
+ bit_ = view.substr(bit_start, bit_len);
+ return;
+}
+
+SegbitsFileReader::iterator& SegbitsFileReader::iterator::operator++() {
+ size_t newline = view_.find('\n');
+ if (newline == absl::string_view::npos) {
+ view_ = absl::string_view();
+ }
+
+ view_.remove_prefix(newline + 1);
+ value_ = value_type(view_);
+ return *this;
+}
+
+} // namespace prjxray
diff --git a/lib/segbits_file_reader_test.cc b/lib/segbits_file_reader_test.cc
new file mode 100644
index 0000000..3ac90e1
--- /dev/null
+++ b/lib/segbits_file_reader_test.cc
@@ -0,0 +1,83 @@
+#include <prjxray/segbits_file_reader.h>
+
+#include <map>
+#include <string>
+
+#include <gtest/gtest.h>
+
+TEST(SegbitsFileReaderTest, NonExistantFileReturnsNull) {
+ EXPECT_FALSE(
+ prjxray::SegbitsFileReader::InitWithFile("does_not_exist"));
+}
+
+TEST(SegbitsFileReaderTest, ZeroLengthFileYieldsNoItems) {
+ auto segbits_reader =
+ prjxray::SegbitsFileReader::InitWithFile("empty_file");
+ ASSERT_TRUE(segbits_reader);
+
+ EXPECT_EQ(segbits_reader->begin(), segbits_reader->end());
+}
+
+TEST(SegbitsFileReaderTest, FileWithOneEntry) {
+ auto segbits_reader =
+ prjxray::SegbitsFileReader::InitWithFile("one_entry.segbits");
+ ASSERT_TRUE(segbits_reader);
+
+ auto begin_iter = segbits_reader->begin();
+ EXPECT_EQ(begin_iter->tag(), "CLBLL_L.SLICEL_X0.A5FF.ZINI");
+ EXPECT_EQ(begin_iter->bit(), "31_06");
+
+ EXPECT_EQ(++begin_iter, segbits_reader->end());
+}
+
+TEST(SegbitsFileReaderTest, FileWithOneEntryWithEmptyTag) {
+ auto segbits_reader = prjxray::SegbitsFileReader::InitWithFile(
+ "one_entry_empty_tag.segbits");
+ ASSERT_TRUE(segbits_reader);
+
+ auto begin_iter = segbits_reader->begin();
+ EXPECT_EQ(begin_iter->tag(), "");
+ EXPECT_EQ(begin_iter->bit(), "31_06");
+
+ EXPECT_EQ(++begin_iter, segbits_reader->end());
+}
+
+TEST(SegbitsFileReaderTest, FileWithOneEntryMissingBit) {
+ auto segbits_reader = prjxray::SegbitsFileReader::InitWithFile(
+ "one_entry_missing_bit.segbits");
+ ASSERT_TRUE(segbits_reader);
+
+ auto begin_iter = segbits_reader->begin();
+ EXPECT_EQ(begin_iter->tag(), "CLBLL_L.SLICEL_X0.A5FF.ZINI");
+ EXPECT_EQ(begin_iter->bit(), "");
+
+ EXPECT_EQ(++begin_iter, segbits_reader->end());
+}
+
+TEST(SegbitsFileReaderTest, FileWithOneEntryWithExtraWhitespace) {
+ auto segbits_reader = prjxray::SegbitsFileReader::InitWithFile(
+ "one_entry_extra_whitespace.segbits");
+ ASSERT_TRUE(segbits_reader);
+
+ auto begin_iter = segbits_reader->begin();
+ EXPECT_EQ(begin_iter->tag(), "CLBLL_L.SLICEL_X0.A5FF.ZINI");
+ EXPECT_EQ(begin_iter->bit(), "31_06");
+
+ EXPECT_EQ(++begin_iter, segbits_reader->end());
+}
+
+TEST(SegbitsFileReaderTest, FileWithTwoEntries) {
+ auto segbits_reader =
+ prjxray::SegbitsFileReader::InitWithFile("two_entries.segbits");
+ ASSERT_TRUE(segbits_reader);
+
+ auto iter = segbits_reader->begin();
+ EXPECT_EQ(iter->tag(), "CLBLL_L.SLICEL_X0.A5FF.ZINI");
+ EXPECT_EQ(iter->bit(), "31_06");
+
+ ++iter;
+ EXPECT_EQ(iter->tag(), "CLBLL_L.SLICEL_X0.AFF.ZINI");
+ EXPECT_EQ(iter->bit(), "31_03");
+
+ EXPECT_EQ(++iter, segbits_reader->end());
+}
diff --git a/lib/test_data/configuration_test.bit b/lib/test_data/configuration_test.bit
new file mode 100644
index 0000000..6ef4f5e
--- /dev/null
+++ b/lib/test_data/configuration_test.bit
Binary files differ
diff --git a/lib/test_data/configuration_test.debug.bit b/lib/test_data/configuration_test.debug.bit
new file mode 100644
index 0000000..3dfd748
--- /dev/null
+++ b/lib/test_data/configuration_test.debug.bit
Binary files differ
diff --git a/lib/test_data/configuration_test.perframecrc.bit b/lib/test_data/configuration_test.perframecrc.bit
new file mode 100644
index 0000000..7179e2d
--- /dev/null
+++ b/lib/test_data/configuration_test.perframecrc.bit
Binary files differ
diff --git a/lib/test_data/configuration_test.yaml b/lib/test_data/configuration_test.yaml
new file mode 100644
index 0000000..2666355
--- /dev/null
+++ b/lib/test_data/configuration_test.yaml
@@ -0,0 +1,1680 @@
+!<xilinx/xc7series/part>
+idcode: 0x362c093
+configuration_ranges:
+ - !<xilinx/xc7series/configuration_frame_range>
+ begin: !<xilinx/xc7series/configuration_frame_address>
+ block_type: CLB_IO_CLK
+ row_half: top
+ row: 0
+ column: 0
+ minor: 0
+ end: !<xilinx/xc7series/configuration_frame_address>
+ block_type: CLB_IO_CLK
+ row_half: top
+ row: 0
+ column: 0
+ minor: 42
+ - !<xilinx/xc7series/configuration_frame_range>
+ begin: !<xilinx/xc7series/configuration_frame_address>
+ block_type: CLB_IO_CLK
+ row_half: top
+ row: 0
+ column: 1
+ minor: 0
+ end: !<xilinx/xc7series/configuration_frame_address>
+ block_type: CLB_IO_CLK
+ row_half: top
+ row: 0
+ column: 1
+ minor: 30
+ - !<xilinx/xc7series/configuration_frame_range>
+ begin: !<xilinx/xc7series/configuration_frame_address>
+ block_type: CLB_IO_CLK
+ row_half: top
+ row: 0
+ column: 2
+ minor: 0
+ end: !<xilinx/xc7series/configuration_frame_address>
+ block_type: CLB_IO_CLK
+ row_half: top
+ row: 0
+ column: 2
+ minor: 36
+ - !<xilinx/xc7series/configuration_frame_range>
+ begin: !<xilinx/xc7series/configuration_frame_address>
+ block_type: CLB_IO_CLK
+ row_half: top
+ row: 0
+ column: 3
+ minor: 0
+ end: !<xilinx/xc7series/configuration_frame_address>
+ block_type: CLB_IO_CLK
+ row_half: top
+ row: 0
+ column: 3
+ minor: 36
+ - !<xilinx/xc7series/configuration_frame_range>
+ begin: !<xilinx/xc7series/configuration_frame_address>
+ block_type: CLB_IO_CLK
+ row_half: top
+ row: 0
+ column: 4
+ minor: 0
+ end: !<xilinx/xc7series/configuration_frame_address>
+ block_type: CLB_IO_CLK
+ row_half: top
+ row: 0
+ column: 4
+ minor: 36
+ - !<xilinx/xc7series/configuration_frame_range>
+ begin: !<xilinx/xc7series/configuration_frame_address>
+ block_type: CLB_IO_CLK
+ row_half: top
+ row: 0
+ column: 5
+ minor: 0
+ end: !<xilinx/xc7series/configuration_frame_address>
+ block_type: CLB_IO_CLK
+ row_half: top
+ row: 0
+ column: 5
+ minor: 36
+ - !<xilinx/xc7series/configuration_frame_range>
+ begin: !<xilinx/xc7series/configuration_frame_address>
+ block_type: CLB_IO_CLK
+ row_half: top
+ row: 0
+ column: 6
+ minor: 0
+ end: !<xilinx/xc7series/configuration_frame_address>
+ block_type: CLB_IO_CLK
+ row_half: top
+ row: 0
+ column: 6
+ minor: 28
+ - !<xilinx/xc7series/configuration_frame_range>
+ begin: !<xilinx/xc7series/configuration_frame_address>
+ block_type: CLB_IO_CLK
+ row_half: top
+ row: 0
+ column: 7
+ minor: 0
+ end: !<xilinx/xc7series/configuration_frame_address>
+ block_type: CLB_IO_CLK
+ row_half: top
+ row: 0
+ column: 7
+ minor: 36
+ - !<xilinx/xc7series/configuration_frame_range>
+ begin: !<xilinx/xc7series/configuration_frame_address>
+ block_type: CLB_IO_CLK
+ row_half: top
+ row: 0
+ column: 8
+ minor: 0
+ end: !<xilinx/xc7series/configuration_frame_address>
+ block_type: CLB_IO_CLK
+ row_half: top
+ row: 0
+ column: 8
+ minor: 36
+ - !<xilinx/xc7series/configuration_frame_range>
+ begin: !<xilinx/xc7series/configuration_frame_address>
+ block_type: CLB_IO_CLK
+ row_half: top
+ row: 0
+ column: 9
+ minor: 0
+ end: !<xilinx/xc7series/configuration_frame_address>
+ block_type: CLB_IO_CLK
+ row_half: top
+ row: 0
+ column: 9
+ minor: 28
+ - !<xilinx/xc7series/configuration_frame_range>
+ begin: !<xilinx/xc7series/configuration_frame_address>
+ block_type: CLB_IO_CLK
+ row_half: top
+ row: 0
+ column: 10
+ minor: 0
+ end: !<xilinx/xc7series/configuration_frame_address>
+ block_type: CLB_IO_CLK
+ row_half: top
+ row: 0
+ column: 10
+ minor: 36
+ - !<xilinx/xc7series/configuration_frame_range>
+ begin: !<xilinx/xc7series/configuration_frame_address>
+ block_type: CLB_IO_CLK
+ row_half: top
+ row: 0
+ column: 11
+ minor: 0
+ end: !<xilinx/xc7series/configuration_frame_address>
+ block_type: CLB_IO_CLK
+ row_half: top
+ row: 0
+ column: 11
+ minor: 36
+ - !<xilinx/xc7series/configuration_frame_range>
+ begin: !<xilinx/xc7series/configuration_frame_address>
+ block_type: CLB_IO_CLK
+ row_half: top
+ row: 0
+ column: 12
+ minor: 0
+ end: !<xilinx/xc7series/configuration_frame_address>
+ block_type: CLB_IO_CLK
+ row_half: top
+ row: 0
+ column: 12
+ minor: 36
+ - !<xilinx/xc7series/configuration_frame_range>
+ begin: !<xilinx/xc7series/configuration_frame_address>
+ block_type: CLB_IO_CLK
+ row_half: top
+ row: 0
+ column: 13
+ minor: 0
+ end: !<xilinx/xc7series/configuration_frame_address>
+ block_type: CLB_IO_CLK
+ row_half: top
+ row: 0
+ column: 13
+ minor: 36
+ - !<xilinx/xc7series/configuration_frame_range>
+ begin: !<xilinx/xc7series/configuration_frame_address>
+ block_type: CLB_IO_CLK
+ row_half: top
+ row: 0
+ column: 14
+ minor: 0
+ end: !<xilinx/xc7series/configuration_frame_address>
+ block_type: CLB_IO_CLK
+ row_half: top
+ row: 0
+ column: 14
+ minor: 36
+ - !<xilinx/xc7series/configuration_frame_range>
+ begin: !<xilinx/xc7series/configuration_frame_address>
+ block_type: CLB_IO_CLK
+ row_half: top
+ row: 0
+ column: 15
+ minor: 0
+ end: !<xilinx/xc7series/configuration_frame_address>
+ block_type: CLB_IO_CLK
+ row_half: top
+ row: 0
+ column: 15
+ minor: 36
+ - !<xilinx/xc7series/configuration_frame_range>
+ begin: !<xilinx/xc7series/configuration_frame_address>
+ block_type: CLB_IO_CLK
+ row_half: top
+ row: 0
+ column: 16
+ minor: 0
+ end: !<xilinx/xc7series/configuration_frame_address>
+ block_type: CLB_IO_CLK
+ row_half: top
+ row: 0
+ column: 16
+ minor: 36
+ - !<xilinx/xc7series/configuration_frame_range>
+ begin: !<xilinx/xc7series/configuration_frame_address>
+ block_type: CLB_IO_CLK
+ row_half: top
+ row: 0
+ column: 17
+ minor: 0
+ end: !<xilinx/xc7series/configuration_frame_address>
+ block_type: CLB_IO_CLK
+ row_half: top
+ row: 0
+ column: 17
+ minor: 36
+ - !<xilinx/xc7series/configuration_frame_range>
+ begin: !<xilinx/xc7series/configuration_frame_address>
+ block_type: CLB_IO_CLK
+ row_half: top
+ row: 0
+ column: 18
+ minor: 0
+ end: !<xilinx/xc7series/configuration_frame_address>
+ block_type: CLB_IO_CLK
+ row_half: top
+ row: 0
+ column: 18
+ minor: 30
+ - !<xilinx/xc7series/configuration_frame_range>
+ begin: !<xilinx/xc7series/configuration_frame_address>
+ block_type: CLB_IO_CLK
+ row_half: top
+ row: 0
+ column: 19
+ minor: 0
+ end: !<xilinx/xc7series/configuration_frame_address>
+ block_type: CLB_IO_CLK
+ row_half: top
+ row: 0
+ column: 19
+ minor: 36
+ - !<xilinx/xc7series/configuration_frame_range>
+ begin: !<xilinx/xc7series/configuration_frame_address>
+ block_type: CLB_IO_CLK
+ row_half: top
+ row: 0
+ column: 20
+ minor: 0
+ end: !<xilinx/xc7series/configuration_frame_address>
+ block_type: CLB_IO_CLK
+ row_half: top
+ row: 0
+ column: 20
+ minor: 36
+ - !<xilinx/xc7series/configuration_frame_range>
+ begin: !<xilinx/xc7series/configuration_frame_address>
+ block_type: CLB_IO_CLK
+ row_half: top
+ row: 0
+ column: 21
+ minor: 0
+ end: !<xilinx/xc7series/configuration_frame_address>
+ block_type: CLB_IO_CLK
+ row_half: top
+ row: 0
+ column: 21
+ minor: 36
+ - !<xilinx/xc7series/configuration_frame_range>
+ begin: !<xilinx/xc7series/configuration_frame_address>
+ block_type: CLB_IO_CLK
+ row_half: top
+ row: 0
+ column: 22
+ minor: 0
+ end: !<xilinx/xc7series/configuration_frame_address>
+ block_type: CLB_IO_CLK
+ row_half: top
+ row: 0
+ column: 22
+ minor: 36
+ - !<xilinx/xc7series/configuration_frame_range>
+ begin: !<xilinx/xc7series/configuration_frame_address>
+ block_type: CLB_IO_CLK
+ row_half: top
+ row: 0
+ column: 23
+ minor: 0
+ end: !<xilinx/xc7series/configuration_frame_address>
+ block_type: CLB_IO_CLK
+ row_half: top
+ row: 0
+ column: 23
+ minor: 30
+ - !<xilinx/xc7series/configuration_frame_range>
+ begin: !<xilinx/xc7series/configuration_frame_address>
+ block_type: CLB_IO_CLK
+ row_half: top
+ row: 0
+ column: 24
+ minor: 0
+ end: !<xilinx/xc7series/configuration_frame_address>
+ block_type: CLB_IO_CLK
+ row_half: top
+ row: 0
+ column: 24
+ minor: 36
+ - !<xilinx/xc7series/configuration_frame_range>
+ begin: !<xilinx/xc7series/configuration_frame_address>
+ block_type: CLB_IO_CLK
+ row_half: top
+ row: 0
+ column: 25
+ minor: 0
+ end: !<xilinx/xc7series/configuration_frame_address>
+ block_type: CLB_IO_CLK
+ row_half: top
+ row: 0
+ column: 25
+ minor: 36
+ - !<xilinx/xc7series/configuration_frame_range>
+ begin: !<xilinx/xc7series/configuration_frame_address>
+ block_type: CLB_IO_CLK
+ row_half: top
+ row: 0
+ column: 26
+ minor: 0
+ end: !<xilinx/xc7series/configuration_frame_address>
+ block_type: CLB_IO_CLK
+ row_half: top
+ row: 0
+ column: 26
+ minor: 36
+ - !<xilinx/xc7series/configuration_frame_range>
+ begin: !<xilinx/xc7series/configuration_frame_address>
+ block_type: CLB_IO_CLK
+ row_half: top
+ row: 0
+ column: 27
+ minor: 0
+ end: !<xilinx/xc7series/configuration_frame_address>
+ block_type: CLB_IO_CLK
+ row_half: top
+ row: 0
+ column: 27
+ minor: 36
+ - !<xilinx/xc7series/configuration_frame_range>
+ begin: !<xilinx/xc7series/configuration_frame_address>
+ block_type: CLB_IO_CLK
+ row_half: top
+ row: 0
+ column: 28
+ minor: 0
+ end: !<xilinx/xc7series/configuration_frame_address>
+ block_type: CLB_IO_CLK
+ row_half: top
+ row: 0
+ column: 28
+ minor: 36
+ - !<xilinx/xc7series/configuration_frame_range>
+ begin: !<xilinx/xc7series/configuration_frame_address>
+ block_type: CLB_IO_CLK
+ row_half: top
+ row: 0
+ column: 29
+ minor: 0
+ end: !<xilinx/xc7series/configuration_frame_address>
+ block_type: CLB_IO_CLK
+ row_half: top
+ row: 0
+ column: 29
+ minor: 36
+ - !<xilinx/xc7series/configuration_frame_range>
+ begin: !<xilinx/xc7series/configuration_frame_address>
+ block_type: CLB_IO_CLK
+ row_half: top
+ row: 0
+ column: 30
+ minor: 0
+ end: !<xilinx/xc7series/configuration_frame_address>
+ block_type: CLB_IO_CLK
+ row_half: top
+ row: 0
+ column: 30
+ minor: 28
+ - !<xilinx/xc7series/configuration_frame_range>
+ begin: !<xilinx/xc7series/configuration_frame_address>
+ block_type: CLB_IO_CLK
+ row_half: top
+ row: 0
+ column: 31
+ minor: 0
+ end: !<xilinx/xc7series/configuration_frame_address>
+ block_type: CLB_IO_CLK
+ row_half: top
+ row: 0
+ column: 31
+ minor: 36
+ - !<xilinx/xc7series/configuration_frame_range>
+ begin: !<xilinx/xc7series/configuration_frame_address>
+ block_type: CLB_IO_CLK
+ row_half: top
+ row: 0
+ column: 32
+ minor: 0
+ end: !<xilinx/xc7series/configuration_frame_address>
+ block_type: CLB_IO_CLK
+ row_half: top
+ row: 0
+ column: 32
+ minor: 36
+ - !<xilinx/xc7series/configuration_frame_range>
+ begin: !<xilinx/xc7series/configuration_frame_address>
+ block_type: CLB_IO_CLK
+ row_half: top
+ row: 0
+ column: 33
+ minor: 0
+ end: !<xilinx/xc7series/configuration_frame_address>
+ block_type: CLB_IO_CLK
+ row_half: top
+ row: 0
+ column: 33
+ minor: 36
+ - !<xilinx/xc7series/configuration_frame_range>
+ begin: !<xilinx/xc7series/configuration_frame_address>
+ block_type: CLB_IO_CLK
+ row_half: top
+ row: 0
+ column: 34
+ minor: 0
+ end: !<xilinx/xc7series/configuration_frame_address>
+ block_type: CLB_IO_CLK
+ row_half: top
+ row: 0
+ column: 34
+ minor: 28
+ - !<xilinx/xc7series/configuration_frame_range>
+ begin: !<xilinx/xc7series/configuration_frame_address>
+ block_type: CLB_IO_CLK
+ row_half: top
+ row: 0
+ column: 35
+ minor: 0
+ end: !<xilinx/xc7series/configuration_frame_address>
+ block_type: CLB_IO_CLK
+ row_half: top
+ row: 0
+ column: 35
+ minor: 36
+ - !<xilinx/xc7series/configuration_frame_range>
+ begin: !<xilinx/xc7series/configuration_frame_address>
+ block_type: CLB_IO_CLK
+ row_half: top
+ row: 0
+ column: 36
+ minor: 0
+ end: !<xilinx/xc7series/configuration_frame_address>
+ block_type: CLB_IO_CLK
+ row_half: top
+ row: 0
+ column: 36
+ minor: 36
+ - !<xilinx/xc7series/configuration_frame_range>
+ begin: !<xilinx/xc7series/configuration_frame_address>
+ block_type: CLB_IO_CLK
+ row_half: top
+ row: 0
+ column: 37
+ minor: 0
+ end: !<xilinx/xc7series/configuration_frame_address>
+ block_type: CLB_IO_CLK
+ row_half: top
+ row: 0
+ column: 37
+ minor: 28
+ - !<xilinx/xc7series/configuration_frame_range>
+ begin: !<xilinx/xc7series/configuration_frame_address>
+ block_type: CLB_IO_CLK
+ row_half: top
+ row: 0
+ column: 38
+ minor: 0
+ end: !<xilinx/xc7series/configuration_frame_address>
+ block_type: CLB_IO_CLK
+ row_half: top
+ row: 0
+ column: 38
+ minor: 36
+ - !<xilinx/xc7series/configuration_frame_range>
+ begin: !<xilinx/xc7series/configuration_frame_address>
+ block_type: CLB_IO_CLK
+ row_half: top
+ row: 0
+ column: 39
+ minor: 0
+ end: !<xilinx/xc7series/configuration_frame_address>
+ block_type: CLB_IO_CLK
+ row_half: top
+ row: 0
+ column: 39
+ minor: 36
+ - !<xilinx/xc7series/configuration_frame_range>
+ begin: !<xilinx/xc7series/configuration_frame_address>
+ block_type: CLB_IO_CLK
+ row_half: top
+ row: 0
+ column: 40
+ minor: 0
+ end: !<xilinx/xc7series/configuration_frame_address>
+ block_type: CLB_IO_CLK
+ row_half: top
+ row: 0
+ column: 40
+ minor: 36
+ - !<xilinx/xc7series/configuration_frame_range>
+ begin: !<xilinx/xc7series/configuration_frame_address>
+ block_type: CLB_IO_CLK
+ row_half: top
+ row: 0
+ column: 41
+ minor: 0
+ end: !<xilinx/xc7series/configuration_frame_address>
+ block_type: CLB_IO_CLK
+ row_half: top
+ row: 0
+ column: 41
+ minor: 36
+ - !<xilinx/xc7series/configuration_frame_range>
+ begin: !<xilinx/xc7series/configuration_frame_address>
+ block_type: CLB_IO_CLK
+ row_half: top
+ row: 0
+ column: 42
+ minor: 0
+ end: !<xilinx/xc7series/configuration_frame_address>
+ block_type: CLB_IO_CLK
+ row_half: top
+ row: 0
+ column: 42
+ minor: 30
+ - !<xilinx/xc7series/configuration_frame_range>
+ begin: !<xilinx/xc7series/configuration_frame_address>
+ block_type: CLB_IO_CLK
+ row_half: top
+ row: 0
+ column: 43
+ minor: 0
+ end: !<xilinx/xc7series/configuration_frame_address>
+ block_type: CLB_IO_CLK
+ row_half: top
+ row: 0
+ column: 43
+ minor: 42
+ - !<xilinx/xc7series/configuration_frame_range>
+ begin: !<xilinx/xc7series/configuration_frame_address>
+ block_type: CLB_IO_CLK
+ row_half: top
+ row: 1
+ column: 0
+ minor: 0
+ end: !<xilinx/xc7series/configuration_frame_address>
+ block_type: CLB_IO_CLK
+ row_half: top
+ row: 1
+ column: 0
+ minor: 42
+ - !<xilinx/xc7series/configuration_frame_range>
+ begin: !<xilinx/xc7series/configuration_frame_address>
+ block_type: CLB_IO_CLK
+ row_half: top
+ row: 1
+ column: 1
+ minor: 0
+ end: !<xilinx/xc7series/configuration_frame_address>
+ block_type: CLB_IO_CLK
+ row_half: top
+ row: 1
+ column: 1
+ minor: 30
+ - !<xilinx/xc7series/configuration_frame_range>
+ begin: !<xilinx/xc7series/configuration_frame_address>
+ block_type: CLB_IO_CLK
+ row_half: top
+ row: 1
+ column: 2
+ minor: 0
+ end: !<xilinx/xc7series/configuration_frame_address>
+ block_type: CLB_IO_CLK
+ row_half: top
+ row: 1
+ column: 2
+ minor: 36
+ - !<xilinx/xc7series/configuration_frame_range>
+ begin: !<xilinx/xc7series/configuration_frame_address>
+ block_type: CLB_IO_CLK
+ row_half: top
+ row: 1
+ column: 3
+ minor: 0
+ end: !<xilinx/xc7series/configuration_frame_address>
+ block_type: CLB_IO_CLK
+ row_half: top
+ row: 1
+ column: 3
+ minor: 36
+ - !<xilinx/xc7series/configuration_frame_range>
+ begin: !<xilinx/xc7series/configuration_frame_address>
+ block_type: CLB_IO_CLK
+ row_half: top
+ row: 1
+ column: 4
+ minor: 0
+ end: !<xilinx/xc7series/configuration_frame_address>
+ block_type: CLB_IO_CLK
+ row_half: top
+ row: 1
+ column: 4
+ minor: 36
+ - !<xilinx/xc7series/configuration_frame_range>
+ begin: !<xilinx/xc7series/configuration_frame_address>
+ block_type: CLB_IO_CLK
+ row_half: top
+ row: 1
+ column: 5
+ minor: 0
+ end: !<xilinx/xc7series/configuration_frame_address>
+ block_type: CLB_IO_CLK
+ row_half: top
+ row: 1
+ column: 5
+ minor: 36
+ - !<xilinx/xc7series/configuration_frame_range>
+ begin: !<xilinx/xc7series/configuration_frame_address>
+ block_type: CLB_IO_CLK
+ row_half: top
+ row: 1
+ column: 6
+ minor: 0
+ end: !<xilinx/xc7series/configuration_frame_address>
+ block_type: CLB_IO_CLK
+ row_half: top
+ row: 1
+ column: 6
+ minor: 28
+ - !<xilinx/xc7series/configuration_frame_range>
+ begin: !<xilinx/xc7series/configuration_frame_address>
+ block_type: CLB_IO_CLK
+ row_half: top
+ row: 1
+ column: 7
+ minor: 0
+ end: !<xilinx/xc7series/configuration_frame_address>
+ block_type: CLB_IO_CLK
+ row_half: top
+ row: 1
+ column: 7
+ minor: 36
+ - !<xilinx/xc7series/configuration_frame_range>
+ begin: !<xilinx/xc7series/configuration_frame_address>
+ block_type: CLB_IO_CLK
+ row_half: top
+ row: 1
+ column: 8
+ minor: 0
+ end: !<xilinx/xc7series/configuration_frame_address>
+ block_type: CLB_IO_CLK
+ row_half: top
+ row: 1
+ column: 8
+ minor: 36
+ - !<xilinx/xc7series/configuration_frame_range>
+ begin: !<xilinx/xc7series/configuration_frame_address>
+ block_type: CLB_IO_CLK
+ row_half: top
+ row: 1
+ column: 9
+ minor: 0
+ end: !<xilinx/xc7series/configuration_frame_address>
+ block_type: CLB_IO_CLK
+ row_half: top
+ row: 1
+ column: 9
+ minor: 28
+ - !<xilinx/xc7series/configuration_frame_range>
+ begin: !<xilinx/xc7series/configuration_frame_address>
+ block_type: CLB_IO_CLK
+ row_half: top
+ row: 1
+ column: 10
+ minor: 0
+ end: !<xilinx/xc7series/configuration_frame_address>
+ block_type: CLB_IO_CLK
+ row_half: top
+ row: 1
+ column: 10
+ minor: 36
+ - !<xilinx/xc7series/configuration_frame_range>
+ begin: !<xilinx/xc7series/configuration_frame_address>
+ block_type: CLB_IO_CLK
+ row_half: top
+ row: 1
+ column: 11
+ minor: 0
+ end: !<xilinx/xc7series/configuration_frame_address>
+ block_type: CLB_IO_CLK
+ row_half: top
+ row: 1
+ column: 11
+ minor: 36
+ - !<xilinx/xc7series/configuration_frame_range>
+ begin: !<xilinx/xc7series/configuration_frame_address>
+ block_type: CLB_IO_CLK
+ row_half: top
+ row: 1
+ column: 12
+ minor: 0
+ end: !<xilinx/xc7series/configuration_frame_address>
+ block_type: CLB_IO_CLK
+ row_half: top
+ row: 1
+ column: 12
+ minor: 36
+ - !<xilinx/xc7series/configuration_frame_range>
+ begin: !<xilinx/xc7series/configuration_frame_address>
+ block_type: CLB_IO_CLK
+ row_half: top
+ row: 1
+ column: 13
+ minor: 0
+ end: !<xilinx/xc7series/configuration_frame_address>
+ block_type: CLB_IO_CLK
+ row_half: top
+ row: 1
+ column: 13
+ minor: 36
+ - !<xilinx/xc7series/configuration_frame_range>
+ begin: !<xilinx/xc7series/configuration_frame_address>
+ block_type: CLB_IO_CLK
+ row_half: top
+ row: 1
+ column: 14
+ minor: 0
+ end: !<xilinx/xc7series/configuration_frame_address>
+ block_type: CLB_IO_CLK
+ row_half: top
+ row: 1
+ column: 14
+ minor: 36
+ - !<xilinx/xc7series/configuration_frame_range>
+ begin: !<xilinx/xc7series/configuration_frame_address>
+ block_type: CLB_IO_CLK
+ row_half: top
+ row: 1
+ column: 15
+ minor: 0
+ end: !<xilinx/xc7series/configuration_frame_address>
+ block_type: CLB_IO_CLK
+ row_half: top
+ row: 1
+ column: 15
+ minor: 36
+ - !<xilinx/xc7series/configuration_frame_range>
+ begin: !<xilinx/xc7series/configuration_frame_address>
+ block_type: CLB_IO_CLK
+ row_half: top
+ row: 1
+ column: 16
+ minor: 0
+ end: !<xilinx/xc7series/configuration_frame_address>
+ block_type: CLB_IO_CLK
+ row_half: top
+ row: 1
+ column: 16
+ minor: 36
+ - !<xilinx/xc7series/configuration_frame_range>
+ begin: !<xilinx/xc7series/configuration_frame_address>
+ block_type: CLB_IO_CLK
+ row_half: top
+ row: 1
+ column: 17
+ minor: 0
+ end: !<xilinx/xc7series/configuration_frame_address>
+ block_type: CLB_IO_CLK
+ row_half: top
+ row: 1
+ column: 17
+ minor: 36
+ - !<xilinx/xc7series/configuration_frame_range>
+ begin: !<xilinx/xc7series/configuration_frame_address>
+ block_type: CLB_IO_CLK
+ row_half: top
+ row: 1
+ column: 18
+ minor: 0
+ end: !<xilinx/xc7series/configuration_frame_address>
+ block_type: CLB_IO_CLK
+ row_half: top
+ row: 1
+ column: 18
+ minor: 30
+ - !<xilinx/xc7series/configuration_frame_range>
+ begin: !<xilinx/xc7series/configuration_frame_address>
+ block_type: CLB_IO_CLK
+ row_half: top
+ row: 1
+ column: 19
+ minor: 0
+ end: !<xilinx/xc7series/configuration_frame_address>
+ block_type: CLB_IO_CLK
+ row_half: top
+ row: 1
+ column: 19
+ minor: 36
+ - !<xilinx/xc7series/configuration_frame_range>
+ begin: !<xilinx/xc7series/configuration_frame_address>
+ block_type: CLB_IO_CLK
+ row_half: top
+ row: 1
+ column: 20
+ minor: 0
+ end: !<xilinx/xc7series/configuration_frame_address>
+ block_type: CLB_IO_CLK
+ row_half: top
+ row: 1
+ column: 20
+ minor: 36
+ - !<xilinx/xc7series/configuration_frame_range>
+ begin: !<xilinx/xc7series/configuration_frame_address>
+ block_type: CLB_IO_CLK
+ row_half: top
+ row: 1
+ column: 21
+ minor: 0
+ end: !<xilinx/xc7series/configuration_frame_address>
+ block_type: CLB_IO_CLK
+ row_half: top
+ row: 1
+ column: 21
+ minor: 36
+ - !<xilinx/xc7series/configuration_frame_range>
+ begin: !<xilinx/xc7series/configuration_frame_address>
+ block_type: CLB_IO_CLK
+ row_half: top
+ row: 1
+ column: 22
+ minor: 0
+ end: !<xilinx/xc7series/configuration_frame_address>
+ block_type: CLB_IO_CLK
+ row_half: top
+ row: 1
+ column: 22
+ minor: 36
+ - !<xilinx/xc7series/configuration_frame_range>
+ begin: !<xilinx/xc7series/configuration_frame_address>
+ block_type: CLB_IO_CLK
+ row_half: top
+ row: 1
+ column: 23
+ minor: 0
+ end: !<xilinx/xc7series/configuration_frame_address>
+ block_type: CLB_IO_CLK
+ row_half: top
+ row: 1
+ column: 23
+ minor: 30
+ - !<xilinx/xc7series/configuration_frame_range>
+ begin: !<xilinx/xc7series/configuration_frame_address>
+ block_type: CLB_IO_CLK
+ row_half: top
+ row: 1
+ column: 24
+ minor: 0
+ end: !<xilinx/xc7series/configuration_frame_address>
+ block_type: CLB_IO_CLK
+ row_half: top
+ row: 1
+ column: 24
+ minor: 36
+ - !<xilinx/xc7series/configuration_frame_range>
+ begin: !<xilinx/xc7series/configuration_frame_address>
+ block_type: CLB_IO_CLK
+ row_half: top
+ row: 1
+ column: 25
+ minor: 0
+ end: !<xilinx/xc7series/configuration_frame_address>
+ block_type: CLB_IO_CLK
+ row_half: top
+ row: 1
+ column: 25
+ minor: 36
+ - !<xilinx/xc7series/configuration_frame_range>
+ begin: !<xilinx/xc7series/configuration_frame_address>
+ block_type: CLB_IO_CLK
+ row_half: top
+ row: 1
+ column: 26
+ minor: 0
+ end: !<xilinx/xc7series/configuration_frame_address>
+ block_type: CLB_IO_CLK
+ row_half: top
+ row: 1
+ column: 26
+ minor: 36
+ - !<xilinx/xc7series/configuration_frame_range>
+ begin: !<xilinx/xc7series/configuration_frame_address>
+ block_type: CLB_IO_CLK
+ row_half: top
+ row: 1
+ column: 27
+ minor: 0
+ end: !<xilinx/xc7series/configuration_frame_address>
+ block_type: CLB_IO_CLK
+ row_half: top
+ row: 1
+ column: 27
+ minor: 36
+ - !<xilinx/xc7series/configuration_frame_range>
+ begin: !<xilinx/xc7series/configuration_frame_address>
+ block_type: CLB_IO_CLK
+ row_half: top
+ row: 1
+ column: 28
+ minor: 0
+ end: !<xilinx/xc7series/configuration_frame_address>
+ block_type: CLB_IO_CLK
+ row_half: top
+ row: 1
+ column: 28
+ minor: 36
+ - !<xilinx/xc7series/configuration_frame_range>
+ begin: !<xilinx/xc7series/configuration_frame_address>
+ block_type: CLB_IO_CLK
+ row_half: top
+ row: 1
+ column: 29
+ minor: 0
+ end: !<xilinx/xc7series/configuration_frame_address>
+ block_type: CLB_IO_CLK
+ row_half: top
+ row: 1
+ column: 29
+ minor: 36
+ - !<xilinx/xc7series/configuration_frame_range>
+ begin: !<xilinx/xc7series/configuration_frame_address>
+ block_type: CLB_IO_CLK
+ row_half: top
+ row: 1
+ column: 30
+ minor: 0
+ end: !<xilinx/xc7series/configuration_frame_address>
+ block_type: CLB_IO_CLK
+ row_half: top
+ row: 1
+ column: 30
+ minor: 28
+ - !<xilinx/xc7series/configuration_frame_range>
+ begin: !<xilinx/xc7series/configuration_frame_address>
+ block_type: CLB_IO_CLK
+ row_half: top
+ row: 1
+ column: 31
+ minor: 0
+ end: !<xilinx/xc7series/configuration_frame_address>
+ block_type: CLB_IO_CLK
+ row_half: top
+ row: 1
+ column: 31
+ minor: 36
+ - !<xilinx/xc7series/configuration_frame_range>
+ begin: !<xilinx/xc7series/configuration_frame_address>
+ block_type: CLB_IO_CLK
+ row_half: top
+ row: 1
+ column: 32
+ minor: 0
+ end: !<xilinx/xc7series/configuration_frame_address>
+ block_type: CLB_IO_CLK
+ row_half: top
+ row: 1
+ column: 32
+ minor: 36
+ - !<xilinx/xc7series/configuration_frame_range>
+ begin: !<xilinx/xc7series/configuration_frame_address>
+ block_type: CLB_IO_CLK
+ row_half: top
+ row: 1
+ column: 33
+ minor: 0
+ end: !<xilinx/xc7series/configuration_frame_address>
+ block_type: CLB_IO_CLK
+ row_half: top
+ row: 1
+ column: 33
+ minor: 36
+ - !<xilinx/xc7series/configuration_frame_range>
+ begin: !<xilinx/xc7series/configuration_frame_address>
+ block_type: CLB_IO_CLK
+ row_half: top
+ row: 1
+ column: 34
+ minor: 0
+ end: !<xilinx/xc7series/configuration_frame_address>
+ block_type: CLB_IO_CLK
+ row_half: top
+ row: 1
+ column: 34
+ minor: 28
+ - !<xilinx/xc7series/configuration_frame_range>
+ begin: !<xilinx/xc7series/configuration_frame_address>
+ block_type: CLB_IO_CLK
+ row_half: top
+ row: 1
+ column: 35
+ minor: 0
+ end: !<xilinx/xc7series/configuration_frame_address>
+ block_type: CLB_IO_CLK
+ row_half: top
+ row: 1
+ column: 35
+ minor: 36
+ - !<xilinx/xc7series/configuration_frame_range>
+ begin: !<xilinx/xc7series/configuration_frame_address>
+ block_type: CLB_IO_CLK
+ row_half: top
+ row: 1
+ column: 36
+ minor: 0
+ end: !<xilinx/xc7series/configuration_frame_address>
+ block_type: CLB_IO_CLK
+ row_half: top
+ row: 1
+ column: 36
+ minor: 36
+ - !<xilinx/xc7series/configuration_frame_range>
+ begin: !<xilinx/xc7series/configuration_frame_address>
+ block_type: CLB_IO_CLK
+ row_half: top
+ row: 1
+ column: 37
+ minor: 0
+ end: !<xilinx/xc7series/configuration_frame_address>
+ block_type: CLB_IO_CLK
+ row_half: top
+ row: 1
+ column: 37
+ minor: 32
+ - !<xilinx/xc7series/configuration_frame_range>
+ begin: !<xilinx/xc7series/configuration_frame_address>
+ block_type: CLB_IO_CLK
+ row_half: bottom
+ row: 0
+ column: 0
+ minor: 0
+ end: !<xilinx/xc7series/configuration_frame_address>
+ block_type: CLB_IO_CLK
+ row_half: bottom
+ row: 0
+ column: 0
+ minor: 42
+ - !<xilinx/xc7series/configuration_frame_range>
+ begin: !<xilinx/xc7series/configuration_frame_address>
+ block_type: CLB_IO_CLK
+ row_half: bottom
+ row: 0
+ column: 1
+ minor: 0
+ end: !<xilinx/xc7series/configuration_frame_address>
+ block_type: CLB_IO_CLK
+ row_half: bottom
+ row: 0
+ column: 1
+ minor: 30
+ - !<xilinx/xc7series/configuration_frame_range>
+ begin: !<xilinx/xc7series/configuration_frame_address>
+ block_type: CLB_IO_CLK
+ row_half: bottom
+ row: 0
+ column: 2
+ minor: 0
+ end: !<xilinx/xc7series/configuration_frame_address>
+ block_type: CLB_IO_CLK
+ row_half: bottom
+ row: 0
+ column: 2
+ minor: 36
+ - !<xilinx/xc7series/configuration_frame_range>
+ begin: !<xilinx/xc7series/configuration_frame_address>
+ block_type: CLB_IO_CLK
+ row_half: bottom
+ row: 0
+ column: 3
+ minor: 0
+ end: !<xilinx/xc7series/configuration_frame_address>
+ block_type: CLB_IO_CLK
+ row_half: bottom
+ row: 0
+ column: 3
+ minor: 36
+ - !<xilinx/xc7series/configuration_frame_range>
+ begin: !<xilinx/xc7series/configuration_frame_address>
+ block_type: CLB_IO_CLK
+ row_half: bottom
+ row: 0
+ column: 4
+ minor: 0
+ end: !<xilinx/xc7series/configuration_frame_address>
+ block_type: CLB_IO_CLK
+ row_half: bottom
+ row: 0
+ column: 4
+ minor: 36
+ - !<xilinx/xc7series/configuration_frame_range>
+ begin: !<xilinx/xc7series/configuration_frame_address>
+ block_type: CLB_IO_CLK
+ row_half: bottom
+ row: 0
+ column: 5
+ minor: 0
+ end: !<xilinx/xc7series/configuration_frame_address>
+ block_type: CLB_IO_CLK
+ row_half: bottom
+ row: 0
+ column: 5
+ minor: 36
+ - !<xilinx/xc7series/configuration_frame_range>
+ begin: !<xilinx/xc7series/configuration_frame_address>
+ block_type: CLB_IO_CLK
+ row_half: bottom
+ row: 0
+ column: 6
+ minor: 0
+ end: !<xilinx/xc7series/configuration_frame_address>
+ block_type: CLB_IO_CLK
+ row_half: bottom
+ row: 0
+ column: 6
+ minor: 28
+ - !<xilinx/xc7series/configuration_frame_range>
+ begin: !<xilinx/xc7series/configuration_frame_address>
+ block_type: CLB_IO_CLK
+ row_half: bottom
+ row: 0
+ column: 7
+ minor: 0
+ end: !<xilinx/xc7series/configuration_frame_address>
+ block_type: CLB_IO_CLK
+ row_half: bottom
+ row: 0
+ column: 7
+ minor: 36
+ - !<xilinx/xc7series/configuration_frame_range>
+ begin: !<xilinx/xc7series/configuration_frame_address>
+ block_type: CLB_IO_CLK
+ row_half: bottom
+ row: 0
+ column: 8
+ minor: 0
+ end: !<xilinx/xc7series/configuration_frame_address>
+ block_type: CLB_IO_CLK
+ row_half: bottom
+ row: 0
+ column: 8
+ minor: 36
+ - !<xilinx/xc7series/configuration_frame_range>
+ begin: !<xilinx/xc7series/configuration_frame_address>
+ block_type: CLB_IO_CLK
+ row_half: bottom
+ row: 0
+ column: 9
+ minor: 0
+ end: !<xilinx/xc7series/configuration_frame_address>
+ block_type: CLB_IO_CLK
+ row_half: bottom
+ row: 0
+ column: 9
+ minor: 28
+ - !<xilinx/xc7series/configuration_frame_range>
+ begin: !<xilinx/xc7series/configuration_frame_address>
+ block_type: CLB_IO_CLK
+ row_half: bottom
+ row: 0
+ column: 10
+ minor: 0
+ end: !<xilinx/xc7series/configuration_frame_address>
+ block_type: CLB_IO_CLK
+ row_half: bottom
+ row: 0
+ column: 10
+ minor: 36
+ - !<xilinx/xc7series/configuration_frame_range>
+ begin: !<xilinx/xc7series/configuration_frame_address>
+ block_type: CLB_IO_CLK
+ row_half: bottom
+ row: 0
+ column: 11
+ minor: 0
+ end: !<xilinx/xc7series/configuration_frame_address>
+ block_type: CLB_IO_CLK
+ row_half: bottom
+ row: 0
+ column: 11
+ minor: 36
+ - !<xilinx/xc7series/configuration_frame_range>
+ begin: !<xilinx/xc7series/configuration_frame_address>
+ block_type: CLB_IO_CLK
+ row_half: bottom
+ row: 0
+ column: 12
+ minor: 0
+ end: !<xilinx/xc7series/configuration_frame_address>
+ block_type: CLB_IO_CLK
+ row_half: bottom
+ row: 0
+ column: 12
+ minor: 36
+ - !<xilinx/xc7series/configuration_frame_range>
+ begin: !<xilinx/xc7series/configuration_frame_address>
+ block_type: CLB_IO_CLK
+ row_half: bottom
+ row: 0
+ column: 13
+ minor: 0
+ end: !<xilinx/xc7series/configuration_frame_address>
+ block_type: CLB_IO_CLK
+ row_half: bottom
+ row: 0
+ column: 13
+ minor: 36
+ - !<xilinx/xc7series/configuration_frame_range>
+ begin: !<xilinx/xc7series/configuration_frame_address>
+ block_type: CLB_IO_CLK
+ row_half: bottom
+ row: 0
+ column: 14
+ minor: 0
+ end: !<xilinx/xc7series/configuration_frame_address>
+ block_type: CLB_IO_CLK
+ row_half: bottom
+ row: 0
+ column: 14
+ minor: 36
+ - !<xilinx/xc7series/configuration_frame_range>
+ begin: !<xilinx/xc7series/configuration_frame_address>
+ block_type: CLB_IO_CLK
+ row_half: bottom
+ row: 0
+ column: 15
+ minor: 0
+ end: !<xilinx/xc7series/configuration_frame_address>
+ block_type: CLB_IO_CLK
+ row_half: bottom
+ row: 0
+ column: 15
+ minor: 36
+ - !<xilinx/xc7series/configuration_frame_range>
+ begin: !<xilinx/xc7series/configuration_frame_address>
+ block_type: CLB_IO_CLK
+ row_half: bottom
+ row: 0
+ column: 16
+ minor: 0
+ end: !<xilinx/xc7series/configuration_frame_address>
+ block_type: CLB_IO_CLK
+ row_half: bottom
+ row: 0
+ column: 16
+ minor: 36
+ - !<xilinx/xc7series/configuration_frame_range>
+ begin: !<xilinx/xc7series/configuration_frame_address>
+ block_type: CLB_IO_CLK
+ row_half: bottom
+ row: 0
+ column: 17
+ minor: 0
+ end: !<xilinx/xc7series/configuration_frame_address>
+ block_type: CLB_IO_CLK
+ row_half: bottom
+ row: 0
+ column: 17
+ minor: 36
+ - !<xilinx/xc7series/configuration_frame_range>
+ begin: !<xilinx/xc7series/configuration_frame_address>
+ block_type: CLB_IO_CLK
+ row_half: bottom
+ row: 0
+ column: 18
+ minor: 0
+ end: !<xilinx/xc7series/configuration_frame_address>
+ block_type: CLB_IO_CLK
+ row_half: bottom
+ row: 0
+ column: 18
+ minor: 30
+ - !<xilinx/xc7series/configuration_frame_range>
+ begin: !<xilinx/xc7series/configuration_frame_address>
+ block_type: CLB_IO_CLK
+ row_half: bottom
+ row: 0
+ column: 19
+ minor: 0
+ end: !<xilinx/xc7series/configuration_frame_address>
+ block_type: CLB_IO_CLK
+ row_half: bottom
+ row: 0
+ column: 19
+ minor: 36
+ - !<xilinx/xc7series/configuration_frame_range>
+ begin: !<xilinx/xc7series/configuration_frame_address>
+ block_type: CLB_IO_CLK
+ row_half: bottom
+ row: 0
+ column: 20
+ minor: 0
+ end: !<xilinx/xc7series/configuration_frame_address>
+ block_type: CLB_IO_CLK
+ row_half: bottom
+ row: 0
+ column: 20
+ minor: 36
+ - !<xilinx/xc7series/configuration_frame_range>
+ begin: !<xilinx/xc7series/configuration_frame_address>
+ block_type: CLB_IO_CLK
+ row_half: bottom
+ row: 0
+ column: 21
+ minor: 0
+ end: !<xilinx/xc7series/configuration_frame_address>
+ block_type: CLB_IO_CLK
+ row_half: bottom
+ row: 0
+ column: 21
+ minor: 36
+ - !<xilinx/xc7series/configuration_frame_range>
+ begin: !<xilinx/xc7series/configuration_frame_address>
+ block_type: CLB_IO_CLK
+ row_half: bottom
+ row: 0
+ column: 22
+ minor: 0
+ end: !<xilinx/xc7series/configuration_frame_address>
+ block_type: CLB_IO_CLK
+ row_half: bottom
+ row: 0
+ column: 22
+ minor: 36
+ - !<xilinx/xc7series/configuration_frame_range>
+ begin: !<xilinx/xc7series/configuration_frame_address>
+ block_type: CLB_IO_CLK
+ row_half: bottom
+ row: 0
+ column: 23
+ minor: 0
+ end: !<xilinx/xc7series/configuration_frame_address>
+ block_type: CLB_IO_CLK
+ row_half: bottom
+ row: 0
+ column: 23
+ minor: 30
+ - !<xilinx/xc7series/configuration_frame_range>
+ begin: !<xilinx/xc7series/configuration_frame_address>
+ block_type: CLB_IO_CLK
+ row_half: bottom
+ row: 0
+ column: 24
+ minor: 0
+ end: !<xilinx/xc7series/configuration_frame_address>
+ block_type: CLB_IO_CLK
+ row_half: bottom
+ row: 0
+ column: 24
+ minor: 36
+ - !<xilinx/xc7series/configuration_frame_range>
+ begin: !<xilinx/xc7series/configuration_frame_address>
+ block_type: CLB_IO_CLK
+ row_half: bottom
+ row: 0
+ column: 25
+ minor: 0
+ end: !<xilinx/xc7series/configuration_frame_address>
+ block_type: CLB_IO_CLK
+ row_half: bottom
+ row: 0
+ column: 25
+ minor: 36
+ - !<xilinx/xc7series/configuration_frame_range>
+ begin: !<xilinx/xc7series/configuration_frame_address>
+ block_type: CLB_IO_CLK
+ row_half: bottom
+ row: 0
+ column: 26
+ minor: 0
+ end: !<xilinx/xc7series/configuration_frame_address>
+ block_type: CLB_IO_CLK
+ row_half: bottom
+ row: 0
+ column: 26
+ minor: 36
+ - !<xilinx/xc7series/configuration_frame_range>
+ begin: !<xilinx/xc7series/configuration_frame_address>
+ block_type: CLB_IO_CLK
+ row_half: bottom
+ row: 0
+ column: 27
+ minor: 0
+ end: !<xilinx/xc7series/configuration_frame_address>
+ block_type: CLB_IO_CLK
+ row_half: bottom
+ row: 0
+ column: 27
+ minor: 36
+ - !<xilinx/xc7series/configuration_frame_range>
+ begin: !<xilinx/xc7series/configuration_frame_address>
+ block_type: CLB_IO_CLK
+ row_half: bottom
+ row: 0
+ column: 28
+ minor: 0
+ end: !<xilinx/xc7series/configuration_frame_address>
+ block_type: CLB_IO_CLK
+ row_half: bottom
+ row: 0
+ column: 28
+ minor: 36
+ - !<xilinx/xc7series/configuration_frame_range>
+ begin: !<xilinx/xc7series/configuration_frame_address>
+ block_type: CLB_IO_CLK
+ row_half: bottom
+ row: 0
+ column: 29
+ minor: 0
+ end: !<xilinx/xc7series/configuration_frame_address>
+ block_type: CLB_IO_CLK
+ row_half: bottom
+ row: 0
+ column: 29
+ minor: 36
+ - !<xilinx/xc7series/configuration_frame_range>
+ begin: !<xilinx/xc7series/configuration_frame_address>
+ block_type: CLB_IO_CLK
+ row_half: bottom
+ row: 0
+ column: 30
+ minor: 0
+ end: !<xilinx/xc7series/configuration_frame_address>
+ block_type: CLB_IO_CLK
+ row_half: bottom
+ row: 0
+ column: 30
+ minor: 28
+ - !<xilinx/xc7series/configuration_frame_range>
+ begin: !<xilinx/xc7series/configuration_frame_address>
+ block_type: CLB_IO_CLK
+ row_half: bottom
+ row: 0
+ column: 31
+ minor: 0
+ end: !<xilinx/xc7series/configuration_frame_address>
+ block_type: CLB_IO_CLK
+ row_half: bottom
+ row: 0
+ column: 31
+ minor: 36
+ - !<xilinx/xc7series/configuration_frame_range>
+ begin: !<xilinx/xc7series/configuration_frame_address>
+ block_type: CLB_IO_CLK
+ row_half: bottom
+ row: 0
+ column: 32
+ minor: 0
+ end: !<xilinx/xc7series/configuration_frame_address>
+ block_type: CLB_IO_CLK
+ row_half: bottom
+ row: 0
+ column: 32
+ minor: 36
+ - !<xilinx/xc7series/configuration_frame_range>
+ begin: !<xilinx/xc7series/configuration_frame_address>
+ block_type: CLB_IO_CLK
+ row_half: bottom
+ row: 0
+ column: 33
+ minor: 0
+ end: !<xilinx/xc7series/configuration_frame_address>
+ block_type: CLB_IO_CLK
+ row_half: bottom
+ row: 0
+ column: 33
+ minor: 36
+ - !<xilinx/xc7series/configuration_frame_range>
+ begin: !<xilinx/xc7series/configuration_frame_address>
+ block_type: CLB_IO_CLK
+ row_half: bottom
+ row: 0
+ column: 34
+ minor: 0
+ end: !<xilinx/xc7series/configuration_frame_address>
+ block_type: CLB_IO_CLK
+ row_half: bottom
+ row: 0
+ column: 34
+ minor: 28
+ - !<xilinx/xc7series/configuration_frame_range>
+ begin: !<xilinx/xc7series/configuration_frame_address>
+ block_type: CLB_IO_CLK
+ row_half: bottom
+ row: 0
+ column: 35
+ minor: 0
+ end: !<xilinx/xc7series/configuration_frame_address>
+ block_type: CLB_IO_CLK
+ row_half: bottom
+ row: 0
+ column: 35
+ minor: 36
+ - !<xilinx/xc7series/configuration_frame_range>
+ begin: !<xilinx/xc7series/configuration_frame_address>
+ block_type: CLB_IO_CLK
+ row_half: bottom
+ row: 0
+ column: 36
+ minor: 0
+ end: !<xilinx/xc7series/configuration_frame_address>
+ block_type: CLB_IO_CLK
+ row_half: bottom
+ row: 0
+ column: 36
+ minor: 36
+ - !<xilinx/xc7series/configuration_frame_range>
+ begin: !<xilinx/xc7series/configuration_frame_address>
+ block_type: CLB_IO_CLK
+ row_half: bottom
+ row: 0
+ column: 37
+ minor: 0
+ end: !<xilinx/xc7series/configuration_frame_address>
+ block_type: CLB_IO_CLK
+ row_half: bottom
+ row: 0
+ column: 37
+ minor: 28
+ - !<xilinx/xc7series/configuration_frame_range>
+ begin: !<xilinx/xc7series/configuration_frame_address>
+ block_type: CLB_IO_CLK
+ row_half: bottom
+ row: 0
+ column: 38
+ minor: 0
+ end: !<xilinx/xc7series/configuration_frame_address>
+ block_type: CLB_IO_CLK
+ row_half: bottom
+ row: 0
+ column: 38
+ minor: 36
+ - !<xilinx/xc7series/configuration_frame_range>
+ begin: !<xilinx/xc7series/configuration_frame_address>
+ block_type: CLB_IO_CLK
+ row_half: bottom
+ row: 0
+ column: 39
+ minor: 0
+ end: !<xilinx/xc7series/configuration_frame_address>
+ block_type: CLB_IO_CLK
+ row_half: bottom
+ row: 0
+ column: 39
+ minor: 36
+ - !<xilinx/xc7series/configuration_frame_range>
+ begin: !<xilinx/xc7series/configuration_frame_address>
+ block_type: CLB_IO_CLK
+ row_half: bottom
+ row: 0
+ column: 40
+ minor: 0
+ end: !<xilinx/xc7series/configuration_frame_address>
+ block_type: CLB_IO_CLK
+ row_half: bottom
+ row: 0
+ column: 40
+ minor: 36
+ - !<xilinx/xc7series/configuration_frame_range>
+ begin: !<xilinx/xc7series/configuration_frame_address>
+ block_type: CLB_IO_CLK
+ row_half: bottom
+ row: 0
+ column: 41
+ minor: 0
+ end: !<xilinx/xc7series/configuration_frame_address>
+ block_type: CLB_IO_CLK
+ row_half: bottom
+ row: 0
+ column: 41
+ minor: 36
+ - !<xilinx/xc7series/configuration_frame_range>
+ begin: !<xilinx/xc7series/configuration_frame_address>
+ block_type: CLB_IO_CLK
+ row_half: bottom
+ row: 0
+ column: 42
+ minor: 0
+ end: !<xilinx/xc7series/configuration_frame_address>
+ block_type: CLB_IO_CLK
+ row_half: bottom
+ row: 0
+ column: 42
+ minor: 30
+ - !<xilinx/xc7series/configuration_frame_range>
+ begin: !<xilinx/xc7series/configuration_frame_address>
+ block_type: CLB_IO_CLK
+ row_half: bottom
+ row: 0
+ column: 43
+ minor: 0
+ end: !<xilinx/xc7series/configuration_frame_address>
+ block_type: CLB_IO_CLK
+ row_half: bottom
+ row: 0
+ column: 43
+ minor: 42
+ - !<xilinx/xc7series/configuration_frame_range>
+ begin: !<xilinx/xc7series/configuration_frame_address>
+ block_type: BLOCK_RAM
+ row_half: top
+ row: 0
+ column: 0
+ minor: 0
+ end: !<xilinx/xc7series/configuration_frame_address>
+ block_type: BLOCK_RAM
+ row_half: top
+ row: 0
+ column: 3
+ minor: 0
+ - !<xilinx/xc7series/configuration_frame_range>
+ begin: !<xilinx/xc7series/configuration_frame_address>
+ block_type: BLOCK_RAM
+ row_half: top
+ row: 1
+ column: 0
+ minor: 0
+ end: !<xilinx/xc7series/configuration_frame_address>
+ block_type: BLOCK_RAM
+ row_half: top
+ row: 1
+ column: 2
+ minor: 0
+ - !<xilinx/xc7series/configuration_frame_range>
+ begin: !<xilinx/xc7series/configuration_frame_address>
+ block_type: BLOCK_RAM
+ row_half: bottom
+ row: 0
+ column: 0
+ minor: 0
+ end: !<xilinx/xc7series/configuration_frame_address>
+ block_type: BLOCK_RAM
+ row_half: bottom
+ row: 0
+ column: 3
+ minor: 0
diff --git a/lib/test_data/empty_file b/lib/test_data/empty_file
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/lib/test_data/empty_file
diff --git a/lib/test_data/one_entry.segbits b/lib/test_data/one_entry.segbits
new file mode 100644
index 0000000..eee8c45
--- /dev/null
+++ b/lib/test_data/one_entry.segbits
@@ -0,0 +1 @@
+CLBLL_L.SLICEL_X0.A5FF.ZINI 31_06
diff --git a/lib/test_data/one_entry_empty_tag.segbits b/lib/test_data/one_entry_empty_tag.segbits
new file mode 100644
index 0000000..cc99b3f
--- /dev/null
+++ b/lib/test_data/one_entry_empty_tag.segbits
@@ -0,0 +1 @@
+ 31_06
diff --git a/lib/test_data/one_entry_extra_whitespace.segbits b/lib/test_data/one_entry_extra_whitespace.segbits
new file mode 100644
index 0000000..12d1135
--- /dev/null
+++ b/lib/test_data/one_entry_extra_whitespace.segbits
@@ -0,0 +1 @@
+CLBLL_L.SLICEL_X0.A5FF.ZINI 31_06
diff --git a/lib/test_data/one_entry_missing_bit.segbits b/lib/test_data/one_entry_missing_bit.segbits
new file mode 100644
index 0000000..99a8a8e
--- /dev/null
+++ b/lib/test_data/one_entry_missing_bit.segbits
@@ -0,0 +1 @@
+CLBLL_L.SLICEL_X0.A5FF.ZINI
diff --git a/lib/test_data/small_file b/lib/test_data/small_file
new file mode 100644
index 0000000..257cc56
--- /dev/null
+++ b/lib/test_data/small_file
@@ -0,0 +1 @@
+foo
diff --git a/lib/test_data/two_entries.segbits b/lib/test_data/two_entries.segbits
new file mode 100644
index 0000000..cc6ca46
--- /dev/null
+++ b/lib/test_data/two_entries.segbits
@@ -0,0 +1,2 @@
+CLBLL_L.SLICEL_X0.A5FF.ZINI 31_06
+CLBLL_L.SLICEL_X0.AFF.ZINI 31_03
diff --git a/lib/xilinx/bitstream_writer.cc b/lib/xilinx/bitstream_writer.cc
new file mode 100644
index 0000000..809c06f
--- /dev/null
+++ b/lib/xilinx/bitstream_writer.cc
@@ -0,0 +1,100 @@
+#include <iostream>
+
+#include <prjxray/bit_ops.h>
+#include <prjxray/xilinx/architectures.h>
+#include <prjxray/xilinx/bitstream_writer.h>
+
+namespace prjxray {
+namespace xilinx {
+
+template <>
+// Per UG380 pg 78: Bus Width Auto Detection
+typename BitstreamWriter<Spartan6>::header_t BitstreamWriter<Spartan6>::header_{
+ 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF,
+ 0xFFFF, 0xFFFF, 0xFFFF, 0xAA99, 0x5566};
+
+// Per UG470 pg 80: Bus Width Auto Detection
+template <>
+typename BitstreamWriter<Series7>::header_t BitstreamWriter<Series7>::header_{
+ 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF,
+ 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0x000000BB, 0x11220044,
+ 0xFFFFFFFF, 0xFFFFFFFF, 0xAA995566};
+
+template <>
+typename BitstreamWriter<UltraScale>::header_t
+ BitstreamWriter<UltraScale>::header_{0xFFFFFFFF, 0x000000BB, 0x11220044,
+ 0xFFFFFFFF, 0xFFFFFFFF, 0xAA995566};
+
+template <>
+typename BitstreamWriter<UltraScalePlus>::header_t
+ BitstreamWriter<UltraScalePlus>::header_{
+ 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF,
+ 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF,
+ 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0x000000BB, 0x11220044,
+ 0xFFFFFFFF, 0xFFFFFFFF, 0xAA995566};
+
+uint32_t packet2header(
+ const ConfigurationPacket<Spartan6ConfigurationRegister>& packet) {
+ uint32_t ret = 0;
+
+ ret = bit_field_set(ret, 15, 13, packet.header_type());
+
+ switch (packet.header_type()) {
+ case NONE:
+ // Bitstreams are 0 padded sometimes, essentially making
+ // a type 0 frame Ignore the other fields for now
+ break;
+ case TYPE1: {
+ // Table 5-20: Type 1 Packet Header Format
+ ret = bit_field_set(ret, 12, 11, packet.opcode());
+ ret = bit_field_set(ret, 10, 5, packet.address());
+ ret = bit_field_set(ret, 4, 0, packet.data().length());
+ break;
+ }
+ case TYPE2: {
+ // Table 5-22: Type 2 Packet Header
+ ret = bit_field_set(ret, 12, 11, packet.opcode());
+ ret = bit_field_set(ret, 10, 5, packet.address());
+ break;
+ }
+ default:
+ break;
+ }
+
+ return ret;
+}
+
+uint32_t packet2header(
+ const ConfigurationPacket<Series7ConfigurationRegister>& packet) {
+ uint32_t ret = 0;
+
+ ret = bit_field_set(ret, 31, 29, packet.header_type());
+
+ switch (packet.header_type()) {
+ case NONE:
+ // Bitstreams are 0 padded sometimes, essentially making
+ // a type 0 frame Ignore the other fields for now
+ break;
+ case TYPE1: {
+ // 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 TYPE2: {
+ // 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;
+}
+
+} // namespace xilinx
+} // namespace prjxray
diff --git a/lib/xilinx/configuration.cc b/lib/xilinx/configuration.cc
new file mode 100644
index 0000000..d7d6626
--- /dev/null
+++ b/lib/xilinx/configuration.cc
@@ -0,0 +1,785 @@
+#include <fstream>
+#include <iostream>
+
+#include <absl/strings/str_cat.h>
+#include <absl/strings/str_split.h>
+#include <absl/time/clock.h>
+#include <absl/time/time.h>
+
+#include <prjxray/xilinx/architectures.h>
+#include <prjxray/xilinx/bitstream_writer.h>
+#include <prjxray/xilinx/configuration.h>
+#include <prjxray/xilinx/configuration_packet_with_payload.h>
+#include <prjxray/xilinx/nop_packet.h>
+#include <prjxray/xilinx/spartan6/command.h>
+#include <prjxray/xilinx/xc7series/command.h>
+#include <prjxray/xilinx/xc7series/configuration_options_0_value.h>
+
+namespace prjxray {
+namespace xilinx {
+
+template <>
+Configuration<Spartan6>::PacketData
+Configuration<Spartan6>::createType2ConfigurationPacketData(
+ const Frames<Spartan6>::Frames2Data& frames,
+ absl::optional<Spartan6::Part>& part) {
+ // Generate a single type 2 packet that writes everything at once.
+ PacketData packet_data;
+ for (auto& frame : frames) {
+ std::copy(frame.second.begin(), frame.second.end(),
+ std::back_inserter(packet_data));
+ }
+
+ // Insert payload length
+ size_t packet_data_size = packet_data.size() - 2;
+ packet_data.insert(packet_data.begin(), packet_data_size & 0xFFFF);
+ packet_data.insert(packet_data.begin(),
+ (packet_data_size >> 16) & 0xFFFF);
+ return packet_data;
+}
+
+template <>
+void Configuration<Spartan6>::createConfigurationPackage(
+ Spartan6::ConfigurationPackage& out_packets,
+ const PacketData& packet_data,
+ absl::optional<Spartan6::Part>& part) {
+ using ArchType = Spartan6;
+ using ConfigurationRegister = ArchType::ConfRegType;
+ // Initialization sequence
+ //
+ // Reset CRC
+ out_packets.emplace_back(
+ new ConfigurationPacketWithPayload<1, ConfigurationRegister>(
+ ConfigurationPacket<ConfigurationRegister>::Opcode::Write,
+ ConfigurationRegister::CMD,
+ {static_cast<uint32_t>(spartan6::Command::RCRC)}));
+
+ // NOP
+ out_packets.emplace_back(new NopPacket<ConfigurationRegister>());
+
+ // Frame length
+ out_packets.emplace_back(
+ new ConfigurationPacketWithPayload<1, ConfigurationRegister>(
+ ConfigurationPacket<ConfigurationRegister>::Opcode::Write,
+ ConfigurationRegister::FLR, {0x0380}));
+
+ // Configuration Options 1
+ out_packets.emplace_back(
+ new ConfigurationPacketWithPayload<1, ConfigurationRegister>(
+ ConfigurationPacket<ConfigurationRegister>::Opcode::Write,
+ ConfigurationRegister::COR1, {0x3d08}));
+
+ // Configurations Options2
+ out_packets.emplace_back(
+ new ConfigurationPacketWithPayload<1, ConfigurationRegister>(
+ ConfigurationPacket<ConfigurationRegister>::Opcode::Write,
+ ConfigurationRegister::COR2, {0x9ee}));
+
+ // IDCODE
+ out_packets.emplace_back(
+ new ConfigurationPacketWithPayload<2, ConfigurationRegister>(
+ ConfigurationPacket<ConfigurationRegister>::Opcode::Write,
+ ConfigurationRegister::IDCODE,
+ {part->idcode() >> 16, part->idcode()}));
+
+ // Control MASK
+ out_packets.emplace_back(
+ new ConfigurationPacketWithPayload<1, ConfigurationRegister>(
+ ConfigurationPacket<ConfigurationRegister>::Opcode::Write,
+ ConfigurationRegister::MASK, {0xcf}));
+
+ // Control options
+ out_packets.emplace_back(
+ new ConfigurationPacketWithPayload<1, ConfigurationRegister>(
+ ConfigurationPacket<ConfigurationRegister>::Opcode::Write,
+ ConfigurationRegister::CTL, {0x81}));
+
+ // NOP packets
+ for (int i = 0; i < 17; i++) {
+ out_packets.emplace_back(
+ new NopPacket<ConfigurationRegister>());
+ }
+
+ // CCLK FREQ
+ out_packets.emplace_back(
+ new ConfigurationPacketWithPayload<1, ConfigurationRegister>(
+ ConfigurationPacket<ConfigurationRegister>::Opcode::Write,
+ ConfigurationRegister::CCLK_FREQ, {0x3cc8}));
+
+ // PWRDN_REG
+ out_packets.emplace_back(
+ new ConfigurationPacketWithPayload<1, ConfigurationRegister>(
+ ConfigurationPacket<ConfigurationRegister>::Opcode::Write,
+ ConfigurationRegister::PWRDN_REG, {0x881}));
+
+ // EYE MASK
+ out_packets.emplace_back(
+ new ConfigurationPacketWithPayload<1, ConfigurationRegister>(
+ ConfigurationPacket<ConfigurationRegister>::Opcode::Write,
+ ConfigurationRegister::EYE_MASK, {0x0}));
+
+ // House Clean Option
+ out_packets.emplace_back(
+ new ConfigurationPacketWithPayload<1, ConfigurationRegister>(
+ ConfigurationPacket<ConfigurationRegister>::Opcode::Write,
+ ConfigurationRegister::HC_OPT_REG, {0x1f}));
+
+ // Configuration Watchdog Timer
+ out_packets.emplace_back(
+ new ConfigurationPacketWithPayload<1, ConfigurationRegister>(
+ ConfigurationPacket<ConfigurationRegister>::Opcode::Write,
+ ConfigurationRegister::CWDT, {0xffff}));
+
+ // GWE cycle during wake-up from suspend
+ out_packets.emplace_back(
+ new ConfigurationPacketWithPayload<1, ConfigurationRegister>(
+ ConfigurationPacket<ConfigurationRegister>::Opcode::Write,
+ ConfigurationRegister::PU_GWE, {0x5}));
+
+ // GTS cycle during wake-up from suspend
+ out_packets.emplace_back(
+ new ConfigurationPacketWithPayload<1, ConfigurationRegister>(
+ ConfigurationPacket<ConfigurationRegister>::Opcode::Write,
+ ConfigurationRegister::PU_GTS, {0x4}));
+
+ // Reboot mode
+ out_packets.emplace_back(
+ new ConfigurationPacketWithPayload<1, ConfigurationRegister>(
+ ConfigurationPacket<ConfigurationRegister>::Opcode::Write,
+ ConfigurationRegister::MODE_REG, {0x100}));
+
+ // General options 1
+ out_packets.emplace_back(
+ new ConfigurationPacketWithPayload<1, ConfigurationRegister>(
+ ConfigurationPacket<ConfigurationRegister>::Opcode::Write,
+ ConfigurationRegister::GENERAL1, {0x0}));
+
+ // General options 2
+ out_packets.emplace_back(
+ new ConfigurationPacketWithPayload<1, ConfigurationRegister>(
+ ConfigurationPacket<ConfigurationRegister>::Opcode::Write,
+ ConfigurationRegister::GENERAL2, {0x0}));
+
+ // General options 3
+ out_packets.emplace_back(
+ new ConfigurationPacketWithPayload<1, ConfigurationRegister>(
+ ConfigurationPacket<ConfigurationRegister>::Opcode::Write,
+ ConfigurationRegister::GENERAL3, {0x0}));
+
+ // General options 4
+ out_packets.emplace_back(
+ new ConfigurationPacketWithPayload<1, ConfigurationRegister>(
+ ConfigurationPacket<ConfigurationRegister>::Opcode::Write,
+ ConfigurationRegister::GENERAL4, {0x0}));
+
+ // General options 5
+ out_packets.emplace_back(
+ new ConfigurationPacketWithPayload<1, ConfigurationRegister>(
+ ConfigurationPacket<ConfigurationRegister>::Opcode::Write,
+ ConfigurationRegister::GENERAL5, {0x0}));
+
+ // SEU frequency, enable and status
+ out_packets.emplace_back(
+ new ConfigurationPacketWithPayload<1, ConfigurationRegister>(
+ ConfigurationPacket<ConfigurationRegister>::Opcode::Write,
+ ConfigurationRegister::SEU_OPT, {0x1be2}));
+
+ // Expected readback signature for SEU detection
+ out_packets.emplace_back(
+ new ConfigurationPacketWithPayload<2, ConfigurationRegister>(
+ ConfigurationPacket<ConfigurationRegister>::Opcode::Write,
+ ConfigurationRegister::EXP_SIGN, {0x0, 0x0}));
+
+ // NOP
+ out_packets.emplace_back(new NopPacket<ConfigurationRegister>());
+ out_packets.emplace_back(new NopPacket<ConfigurationRegister>());
+
+ // FAR
+ out_packets.emplace_back(
+ new ConfigurationPacketWithPayload<2, ConfigurationRegister>(
+ ConfigurationPacket<ConfigurationRegister>::Opcode::Write,
+ ConfigurationRegister::FAR_MAJ, {0x0, 0x0}));
+
+ // Write Configuration Data
+ out_packets.emplace_back(
+ new ConfigurationPacketWithPayload<1, ConfigurationRegister>(
+ ConfigurationPacket<ConfigurationRegister>::Opcode::Write,
+ ConfigurationRegister::CMD,
+ {static_cast<uint32_t>(spartan6::Command::WCFG)}));
+
+ // Frame data write
+ out_packets.emplace_back(new ConfigurationPacket<ConfigurationRegister>(
+ TYPE2, ConfigurationPacket<ConfigurationRegister>::Opcode::Write,
+ ConfigurationRegister::FDRI, {packet_data}));
+
+ // NOP packets
+ for (int i = 0; i < 24; i++) {
+ out_packets.emplace_back(
+ new NopPacket<ConfigurationRegister>());
+ }
+
+ // Finalization sequence
+ //
+ // Set/reset the IOB and CLB flip-flops
+ out_packets.emplace_back(
+ new ConfigurationPacketWithPayload<1, ConfigurationRegister>(
+ ConfigurationPacket<ConfigurationRegister>::Opcode::Write,
+ ConfigurationRegister::CMD,
+ {static_cast<uint32_t>(spartan6::Command::GRESTORE)}));
+
+ // Last Frame
+ out_packets.emplace_back(
+ new ConfigurationPacketWithPayload<1, ConfigurationRegister>(
+ ConfigurationPacket<ConfigurationRegister>::Opcode::Write,
+ ConfigurationRegister::CMD,
+ {static_cast<uint32_t>(spartan6::Command::LFRM)}));
+
+ // NOP packets
+ for (int i = 0; i < 4; i++) {
+ out_packets.emplace_back(
+ new NopPacket<ConfigurationRegister>());
+ }
+
+ // Set/reset the IOB and CLB flip-flops
+ out_packets.emplace_back(
+ new ConfigurationPacketWithPayload<1, ConfigurationRegister>(
+ ConfigurationPacket<ConfigurationRegister>::Opcode::Write,
+ ConfigurationRegister::CMD,
+ {static_cast<uint32_t>(spartan6::Command::GRESTORE)}));
+
+ // Startup sequence
+ //
+ // Start
+ out_packets.emplace_back(
+ new ConfigurationPacketWithPayload<1, ConfigurationRegister>(
+ ConfigurationPacket<ConfigurationRegister>::Opcode::Write,
+ ConfigurationRegister::CMD,
+ {static_cast<uint32_t>(spartan6::Command::START)}));
+
+ // Control MASK
+ out_packets.emplace_back(
+ new ConfigurationPacketWithPayload<1, ConfigurationRegister>(
+ ConfigurationPacket<ConfigurationRegister>::Opcode::Write,
+ ConfigurationRegister::MASK, {0xff}));
+
+ // Control options
+ out_packets.emplace_back(
+ new ConfigurationPacketWithPayload<1, ConfigurationRegister>(
+ ConfigurationPacket<ConfigurationRegister>::Opcode::Write,
+ ConfigurationRegister::CTL, {0x81}));
+
+ // CRC
+ out_packets.emplace_back(
+ new ConfigurationPacketWithPayload<2, ConfigurationRegister>(
+ ConfigurationPacket<ConfigurationRegister>::Opcode::Write,
+ ConfigurationRegister::CRC, {0x39, 0xe423}));
+
+ // Desync
+ out_packets.emplace_back(
+ new ConfigurationPacketWithPayload<1, ConfigurationRegister>(
+ ConfigurationPacket<ConfigurationRegister>::Opcode::Write,
+ ConfigurationRegister::CMD,
+ {static_cast<uint32_t>(spartan6::Command::DESYNC)}));
+
+ // NOP packets
+ for (int i = 0; i < 14; i++) {
+ out_packets.emplace_back(
+ new NopPacket<ConfigurationRegister>());
+ }
+}
+
+template <>
+void Configuration<Series7>::createConfigurationPackage(
+ Series7::ConfigurationPackage& out_packets,
+ const PacketData& packet_data,
+ absl::optional<Series7::Part>& part) {
+ using ArchType = Series7;
+ using ConfigurationRegister = ArchType::ConfRegType;
+ // Initialization sequence
+ out_packets.emplace_back(new NopPacket<ConfigurationRegister>());
+ out_packets.emplace_back(
+ new ConfigurationPacketWithPayload<1, ConfigurationRegister>(
+ ConfigurationPacket<ConfigurationRegister>::Opcode::Write,
+ ConfigurationRegister::TIMER, {0x0}));
+ out_packets.emplace_back(
+ new ConfigurationPacketWithPayload<1, ConfigurationRegister>(
+ ConfigurationPacket<ConfigurationRegister>::Opcode::Write,
+ ConfigurationRegister::WBSTAR, {0x0}));
+ out_packets.emplace_back(
+ new ConfigurationPacketWithPayload<1, ConfigurationRegister>(
+ ConfigurationPacket<ConfigurationRegister>::Opcode::Write,
+ ConfigurationRegister::CMD,
+ {static_cast<uint32_t>(xc7series::Command::NOP)}));
+ out_packets.emplace_back(new NopPacket<ConfigurationRegister>());
+ out_packets.emplace_back(
+ new ConfigurationPacketWithPayload<1, ConfigurationRegister>(
+ ConfigurationPacket<ConfigurationRegister>::Opcode::Write,
+ ConfigurationRegister::CMD,
+ {static_cast<uint32_t>(xc7series::Command::RCRC)}));
+ out_packets.emplace_back(new NopPacket<ConfigurationRegister>());
+ out_packets.emplace_back(new NopPacket<ConfigurationRegister>());
+ out_packets.emplace_back(
+ new ConfigurationPacketWithPayload<1, ConfigurationRegister>(
+ ConfigurationPacket<ConfigurationRegister>::Opcode::Write,
+ ConfigurationRegister::UNKNOWN, {0x0}));
+
+ // Configuration Options 0
+ out_packets.emplace_back(new ConfigurationPacketWithPayload<
+ 1, ConfigurationRegister>(
+ ConfigurationPacket<ConfigurationRegister>::Opcode::Write,
+ 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 ConfigurationPacketWithPayload<1, ConfigurationRegister>(
+ ConfigurationPacket<ConfigurationRegister>::Opcode::Write,
+ ConfigurationRegister::COR1, {0x0}));
+ out_packets.emplace_back(
+ new ConfigurationPacketWithPayload<1, ConfigurationRegister>(
+ ConfigurationPacket<ConfigurationRegister>::Opcode::Write,
+ ConfigurationRegister::IDCODE, {part->idcode()}));
+ out_packets.emplace_back(
+ new ConfigurationPacketWithPayload<1, ConfigurationRegister>(
+ ConfigurationPacket<ConfigurationRegister>::Opcode::Write,
+ ConfigurationRegister::CMD,
+ {static_cast<uint32_t>(xc7series::Command::SWITCH)}));
+ out_packets.emplace_back(new NopPacket<ConfigurationRegister>());
+ out_packets.emplace_back(
+ new ConfigurationPacketWithPayload<1, ConfigurationRegister>(
+ ConfigurationPacket<ConfigurationRegister>::Opcode::Write,
+ ConfigurationRegister::MASK, {0x401}));
+ out_packets.emplace_back(
+ new ConfigurationPacketWithPayload<1, ConfigurationRegister>(
+ ConfigurationPacket<ConfigurationRegister>::Opcode::Write,
+ ConfigurationRegister::CTL0, {0x501}));
+ out_packets.emplace_back(
+ new ConfigurationPacketWithPayload<1, ConfigurationRegister>(
+ ConfigurationPacket<ConfigurationRegister>::Opcode::Write,
+ ConfigurationRegister::MASK, {0x0}));
+ out_packets.emplace_back(
+ new ConfigurationPacketWithPayload<1, ConfigurationRegister>(
+ ConfigurationPacket<ConfigurationRegister>::Opcode::Write,
+ ConfigurationRegister::CTL1, {0x0}));
+ out_packets.emplace_back(new NopPacket<ConfigurationRegister>());
+ out_packets.emplace_back(new NopPacket<ConfigurationRegister>());
+ out_packets.emplace_back(new NopPacket<ConfigurationRegister>());
+ out_packets.emplace_back(new NopPacket<ConfigurationRegister>());
+ out_packets.emplace_back(new NopPacket<ConfigurationRegister>());
+ out_packets.emplace_back(new NopPacket<ConfigurationRegister>());
+ out_packets.emplace_back(new NopPacket<ConfigurationRegister>());
+ out_packets.emplace_back(new NopPacket<ConfigurationRegister>());
+ out_packets.emplace_back(
+ new ConfigurationPacketWithPayload<1, ConfigurationRegister>(
+ ConfigurationPacket<ConfigurationRegister>::Opcode::Write,
+ ConfigurationRegister::FAR, {0x0}));
+ out_packets.emplace_back(
+ new ConfigurationPacketWithPayload<1, ConfigurationRegister>(
+ ConfigurationPacket<ConfigurationRegister>::Opcode::Write,
+ ConfigurationRegister::CMD,
+ {static_cast<uint32_t>(xc7series::Command::WCFG)}));
+ out_packets.emplace_back(new NopPacket<ConfigurationRegister>());
+
+ // Frame data write
+ out_packets.emplace_back(new ConfigurationPacket<ConfigurationRegister>(
+ TYPE1, ConfigurationPacket<ConfigurationRegister>::Opcode::Write,
+ ConfigurationRegister::FDRI, {}));
+ out_packets.emplace_back(new ConfigurationPacket<ConfigurationRegister>(
+ TYPE2, ConfigurationPacket<ConfigurationRegister>::Opcode::Write,
+ ConfigurationRegister::FDRI, packet_data));
+
+ // Finalization sequence
+ out_packets.emplace_back(
+ new ConfigurationPacketWithPayload<1, ConfigurationRegister>(
+ ConfigurationPacket<ConfigurationRegister>::Opcode::Write,
+ ConfigurationRegister::CMD,
+ {static_cast<uint32_t>(xc7series::Command::RCRC)}));
+ out_packets.emplace_back(new NopPacket<ConfigurationRegister>());
+ out_packets.emplace_back(new NopPacket<ConfigurationRegister>());
+ out_packets.emplace_back(
+ new ConfigurationPacketWithPayload<1, ConfigurationRegister>(
+ ConfigurationPacket<ConfigurationRegister>::Opcode::Write,
+ ConfigurationRegister::CMD,
+ {static_cast<uint32_t>(xc7series::Command::GRESTORE)}));
+ out_packets.emplace_back(new NopPacket<ConfigurationRegister>());
+ out_packets.emplace_back(
+ new ConfigurationPacketWithPayload<1, ConfigurationRegister>(
+ ConfigurationPacket<ConfigurationRegister>::Opcode::Write,
+ ConfigurationRegister::CMD,
+ {static_cast<uint32_t>(xc7series::Command::LFRM)}));
+ for (int ii = 0; ii < 100; ++ii) {
+ out_packets.emplace_back(
+ new NopPacket<ConfigurationRegister>());
+ }
+ out_packets.emplace_back(
+ new ConfigurationPacketWithPayload<1, ConfigurationRegister>(
+ ConfigurationPacket<ConfigurationRegister>::Opcode::Write,
+ ConfigurationRegister::CMD,
+ {static_cast<uint32_t>(xc7series::Command::START)}));
+ out_packets.emplace_back(new NopPacket<ConfigurationRegister>());
+ out_packets.emplace_back(
+ new ConfigurationPacketWithPayload<1, ConfigurationRegister>(
+ ConfigurationPacket<ConfigurationRegister>::Opcode::Write,
+ ConfigurationRegister::FAR, {0x3be0000}));
+ out_packets.emplace_back(
+ new ConfigurationPacketWithPayload<1, ConfigurationRegister>(
+ ConfigurationPacket<ConfigurationRegister>::Opcode::Write,
+ ConfigurationRegister::MASK, {0x501}));
+ out_packets.emplace_back(
+ new ConfigurationPacketWithPayload<1, ConfigurationRegister>(
+ ConfigurationPacket<ConfigurationRegister>::Opcode::Write,
+ ConfigurationRegister::CTL0, {0x501}));
+ out_packets.emplace_back(
+ new ConfigurationPacketWithPayload<1, ConfigurationRegister>(
+ ConfigurationPacket<ConfigurationRegister>::Opcode::Write,
+ ConfigurationRegister::CMD,
+ {static_cast<uint32_t>(xc7series::Command::RCRC)}));
+ out_packets.emplace_back(new NopPacket<ConfigurationRegister>());
+ out_packets.emplace_back(new NopPacket<ConfigurationRegister>());
+ out_packets.emplace_back(
+ new ConfigurationPacketWithPayload<1, ConfigurationRegister>(
+ ConfigurationPacket<ConfigurationRegister>::Opcode::Write,
+ ConfigurationRegister::CMD,
+ {static_cast<uint32_t>(xc7series::Command::DESYNC)}));
+ for (int ii = 0; ii < 400; ++ii) {
+ out_packets.emplace_back(
+ new NopPacket<ConfigurationRegister>());
+ }
+}
+
+template <>
+void Configuration<UltraScale>::createConfigurationPackage(
+ UltraScale::ConfigurationPackage& out_packets,
+ const PacketData& packet_data,
+ absl::optional<UltraScale::Part>& part) {
+ using ArchType = UltraScale;
+ using ConfigurationRegister = ArchType::ConfRegType;
+ // Initialization sequence
+ out_packets.emplace_back(new NopPacket<ConfigurationRegister>());
+ out_packets.emplace_back(new NopPacket<ConfigurationRegister>());
+ out_packets.emplace_back(
+ new ConfigurationPacketWithPayload<1, ConfigurationRegister>(
+ ConfigurationPacket<ConfigurationRegister>::Opcode::Write,
+ ConfigurationRegister::TIMER, {0x0}));
+ out_packets.emplace_back(
+ new ConfigurationPacketWithPayload<1, ConfigurationRegister>(
+ ConfigurationPacket<ConfigurationRegister>::Opcode::Write,
+ ConfigurationRegister::WBSTAR, {0x0}));
+ out_packets.emplace_back(
+ new ConfigurationPacketWithPayload<1, ConfigurationRegister>(
+ ConfigurationPacket<ConfigurationRegister>::Opcode::Write,
+ ConfigurationRegister::CMD,
+ {static_cast<uint32_t>(xc7series::Command::NOP)}));
+ out_packets.emplace_back(new NopPacket<ConfigurationRegister>());
+ out_packets.emplace_back(
+ new ConfigurationPacketWithPayload<1, ConfigurationRegister>(
+ ConfigurationPacket<ConfigurationRegister>::Opcode::Write,
+ ConfigurationRegister::CMD,
+ {static_cast<uint32_t>(xc7series::Command::RCRC)}));
+ out_packets.emplace_back(new NopPacket<ConfigurationRegister>());
+ out_packets.emplace_back(new NopPacket<ConfigurationRegister>());
+ out_packets.emplace_back(
+ new ConfigurationPacketWithPayload<1, ConfigurationRegister>(
+ ConfigurationPacket<ConfigurationRegister>::Opcode::Write,
+ ConfigurationRegister::FAR, {0x0}));
+ out_packets.emplace_back(
+ new ConfigurationPacketWithPayload<1, ConfigurationRegister>(
+ ConfigurationPacket<ConfigurationRegister>::Opcode::Write,
+ ConfigurationRegister::UNKNOWN, {0x0}));
+
+ // Configuration Options 0
+ out_packets.emplace_back(
+ new ConfigurationPacketWithPayload<1, ConfigurationRegister>(
+ ConfigurationPacket<ConfigurationRegister>::Opcode::Write,
+ ConfigurationRegister::COR0, {0x38003fe5}));
+ out_packets.emplace_back(
+ new ConfigurationPacketWithPayload<1, ConfigurationRegister>(
+ ConfigurationPacket<ConfigurationRegister>::Opcode::Write,
+ ConfigurationRegister::COR1, {0x400000}));
+ out_packets.emplace_back(
+ new ConfigurationPacketWithPayload<1, ConfigurationRegister>(
+ ConfigurationPacket<ConfigurationRegister>::Opcode::Write,
+ ConfigurationRegister::IDCODE, {part->idcode()}));
+ out_packets.emplace_back(
+ new ConfigurationPacketWithPayload<1, ConfigurationRegister>(
+ ConfigurationPacket<ConfigurationRegister>::Opcode::Write,
+ ConfigurationRegister::CMD,
+ {static_cast<uint32_t>(xc7series::Command::SWITCH)}));
+ out_packets.emplace_back(new NopPacket<ConfigurationRegister>());
+ out_packets.emplace_back(
+ new ConfigurationPacketWithPayload<1, ConfigurationRegister>(
+ ConfigurationPacket<ConfigurationRegister>::Opcode::Write,
+ ConfigurationRegister::MASK, {0x1}));
+ out_packets.emplace_back(
+ new ConfigurationPacketWithPayload<1, ConfigurationRegister>(
+ ConfigurationPacket<ConfigurationRegister>::Opcode::Write,
+ ConfigurationRegister::CTL0, {0x101}));
+ out_packets.emplace_back(
+ new ConfigurationPacketWithPayload<1, ConfigurationRegister>(
+ ConfigurationPacket<ConfigurationRegister>::Opcode::Write,
+ ConfigurationRegister::MASK, {0x0}));
+ out_packets.emplace_back(
+ new ConfigurationPacketWithPayload<1, ConfigurationRegister>(
+ ConfigurationPacket<ConfigurationRegister>::Opcode::Write,
+ ConfigurationRegister::CTL1, {0x0}));
+ out_packets.emplace_back(new NopPacket<ConfigurationRegister>());
+ out_packets.emplace_back(new NopPacket<ConfigurationRegister>());
+ out_packets.emplace_back(new NopPacket<ConfigurationRegister>());
+ out_packets.emplace_back(new NopPacket<ConfigurationRegister>());
+ out_packets.emplace_back(new NopPacket<ConfigurationRegister>());
+ out_packets.emplace_back(new NopPacket<ConfigurationRegister>());
+ out_packets.emplace_back(new NopPacket<ConfigurationRegister>());
+ out_packets.emplace_back(new NopPacket<ConfigurationRegister>());
+ out_packets.emplace_back(
+ new ConfigurationPacketWithPayload<1, ConfigurationRegister>(
+ ConfigurationPacket<ConfigurationRegister>::Opcode::Write,
+ ConfigurationRegister::FAR, {0x0}));
+ out_packets.emplace_back(
+ new ConfigurationPacketWithPayload<1, ConfigurationRegister>(
+ ConfigurationPacket<ConfigurationRegister>::Opcode::Write,
+ ConfigurationRegister::CMD,
+ {static_cast<uint32_t>(xc7series::Command::WCFG)}));
+ out_packets.emplace_back(new NopPacket<ConfigurationRegister>());
+
+ // Frame data write
+ out_packets.emplace_back(new ConfigurationPacket<ConfigurationRegister>(
+ TYPE1, ConfigurationPacket<ConfigurationRegister>::Opcode::Write,
+ ConfigurationRegister::FDRI, {}));
+ out_packets.emplace_back(new ConfigurationPacket<ConfigurationRegister>(
+ TYPE2, ConfigurationPacket<ConfigurationRegister>::Opcode::Write,
+ ConfigurationRegister::FDRI, packet_data));
+
+ // Finalization sequence
+ out_packets.emplace_back(
+ new ConfigurationPacketWithPayload<1, ConfigurationRegister>(
+ ConfigurationPacket<ConfigurationRegister>::Opcode::Write,
+ ConfigurationRegister::CMD,
+ {static_cast<uint32_t>(xc7series::Command::RCRC)}));
+ out_packets.emplace_back(new NopPacket<ConfigurationRegister>());
+ out_packets.emplace_back(new NopPacket<ConfigurationRegister>());
+ out_packets.emplace_back(
+ new ConfigurationPacketWithPayload<1, ConfigurationRegister>(
+ ConfigurationPacket<ConfigurationRegister>::Opcode::Write,
+ ConfigurationRegister::CMD,
+ {static_cast<uint32_t>(xc7series::Command::GRESTORE)}));
+ out_packets.emplace_back(new NopPacket<ConfigurationRegister>());
+ out_packets.emplace_back(
+ new ConfigurationPacketWithPayload<1, ConfigurationRegister>(
+ ConfigurationPacket<ConfigurationRegister>::Opcode::Write,
+ ConfigurationRegister::CMD,
+ {static_cast<uint32_t>(xc7series::Command::LFRM)}));
+ for (int ii = 0; ii < 100; ++ii) {
+ out_packets.emplace_back(
+ new NopPacket<ConfigurationRegister>());
+ }
+ out_packets.emplace_back(
+ new ConfigurationPacketWithPayload<1, ConfigurationRegister>(
+ ConfigurationPacket<ConfigurationRegister>::Opcode::Write,
+ ConfigurationRegister::CMD,
+ {static_cast<uint32_t>(xc7series::Command::START)}));
+ out_packets.emplace_back(new NopPacket<ConfigurationRegister>());
+ out_packets.emplace_back(
+ new ConfigurationPacketWithPayload<1, ConfigurationRegister>(
+ ConfigurationPacket<ConfigurationRegister>::Opcode::Write,
+ ConfigurationRegister::FAR, {0x3be0000}));
+ out_packets.emplace_back(
+ new ConfigurationPacketWithPayload<1, ConfigurationRegister>(
+ ConfigurationPacket<ConfigurationRegister>::Opcode::Write,
+ ConfigurationRegister::MASK, {0x101}));
+ out_packets.emplace_back(
+ new ConfigurationPacketWithPayload<1, ConfigurationRegister>(
+ ConfigurationPacket<ConfigurationRegister>::Opcode::Write,
+ ConfigurationRegister::CTL0, {0x101}));
+ out_packets.emplace_back(
+ new ConfigurationPacketWithPayload<1, ConfigurationRegister>(
+ ConfigurationPacket<ConfigurationRegister>::Opcode::Write,
+ ConfigurationRegister::CMD,
+ {static_cast<uint32_t>(xc7series::Command::RCRC)}));
+ out_packets.emplace_back(new NopPacket<ConfigurationRegister>());
+ out_packets.emplace_back(new NopPacket<ConfigurationRegister>());
+ out_packets.emplace_back(
+ new ConfigurationPacketWithPayload<1, ConfigurationRegister>(
+ ConfigurationPacket<ConfigurationRegister>::Opcode::Write,
+ ConfigurationRegister::CMD,
+ {static_cast<uint32_t>(xc7series::Command::DESYNC)}));
+ for (int ii = 0; ii < 400; ++ii) {
+ out_packets.emplace_back(
+ new NopPacket<ConfigurationRegister>());
+ }
+}
+
+template <>
+void Configuration<UltraScalePlus>::createConfigurationPackage(
+ UltraScalePlus::ConfigurationPackage& out_packets,
+ const PacketData& packet_data,
+ absl::optional<UltraScalePlus::Part>& part) {
+ using ArchType = UltraScalePlus;
+ using ConfigurationRegister = ArchType::ConfRegType;
+ // Initialization sequence
+ out_packets.emplace_back(new NopPacket<ConfigurationRegister>());
+ out_packets.emplace_back(new NopPacket<ConfigurationRegister>());
+ out_packets.emplace_back(
+ new ConfigurationPacketWithPayload<1, ConfigurationRegister>(
+ ConfigurationPacket<ConfigurationRegister>::Opcode::Write,
+ ConfigurationRegister::TIMER, {0x0}));
+ out_packets.emplace_back(
+ new ConfigurationPacketWithPayload<1, ConfigurationRegister>(
+ ConfigurationPacket<ConfigurationRegister>::Opcode::Write,
+ ConfigurationRegister::WBSTAR, {0x0}));
+ out_packets.emplace_back(
+ new ConfigurationPacketWithPayload<1, ConfigurationRegister>(
+ ConfigurationPacket<ConfigurationRegister>::Opcode::Write,
+ ConfigurationRegister::CMD,
+ {static_cast<uint32_t>(xc7series::Command::NOP)}));
+ out_packets.emplace_back(new NopPacket<ConfigurationRegister>());
+ out_packets.emplace_back(
+ new ConfigurationPacketWithPayload<1, ConfigurationRegister>(
+ ConfigurationPacket<ConfigurationRegister>::Opcode::Write,
+ ConfigurationRegister::CMD,
+ {static_cast<uint32_t>(xc7series::Command::RCRC)}));
+ out_packets.emplace_back(new NopPacket<ConfigurationRegister>());
+ out_packets.emplace_back(new NopPacket<ConfigurationRegister>());
+ out_packets.emplace_back(
+ new ConfigurationPacketWithPayload<1, ConfigurationRegister>(
+ ConfigurationPacket<ConfigurationRegister>::Opcode::Write,
+ ConfigurationRegister::FAR, {0x0}));
+ out_packets.emplace_back(
+ new ConfigurationPacketWithPayload<1, ConfigurationRegister>(
+ ConfigurationPacket<ConfigurationRegister>::Opcode::Write,
+ ConfigurationRegister::UNKNOWN, {0x0}));
+
+ // Configuration Options 0
+ out_packets.emplace_back(
+ new ConfigurationPacketWithPayload<1, ConfigurationRegister>(
+ ConfigurationPacket<ConfigurationRegister>::Opcode::Write,
+ ConfigurationRegister::COR0, {0x38003fe5}));
+ out_packets.emplace_back(
+ new ConfigurationPacketWithPayload<1, ConfigurationRegister>(
+ ConfigurationPacket<ConfigurationRegister>::Opcode::Write,
+ ConfigurationRegister::COR1, {0x400000}));
+ out_packets.emplace_back(
+ new ConfigurationPacketWithPayload<1, ConfigurationRegister>(
+ ConfigurationPacket<ConfigurationRegister>::Opcode::Write,
+ ConfigurationRegister::IDCODE, {part->idcode()}));
+ out_packets.emplace_back(
+ new ConfigurationPacketWithPayload<1, ConfigurationRegister>(
+ ConfigurationPacket<ConfigurationRegister>::Opcode::Write,
+ ConfigurationRegister::CMD,
+ {static_cast<uint32_t>(xc7series::Command::SWITCH)}));
+ out_packets.emplace_back(new NopPacket<ConfigurationRegister>());
+ out_packets.emplace_back(
+ new ConfigurationPacketWithPayload<1, ConfigurationRegister>(
+ ConfigurationPacket<ConfigurationRegister>::Opcode::Write,
+ ConfigurationRegister::MASK, {0x1}));
+ out_packets.emplace_back(
+ new ConfigurationPacketWithPayload<1, ConfigurationRegister>(
+ ConfigurationPacket<ConfigurationRegister>::Opcode::Write,
+ ConfigurationRegister::CTL0, {0x101}));
+ out_packets.emplace_back(
+ new ConfigurationPacketWithPayload<1, ConfigurationRegister>(
+ ConfigurationPacket<ConfigurationRegister>::Opcode::Write,
+ ConfigurationRegister::MASK, {0x0}));
+ out_packets.emplace_back(
+ new ConfigurationPacketWithPayload<1, ConfigurationRegister>(
+ ConfigurationPacket<ConfigurationRegister>::Opcode::Write,
+ ConfigurationRegister::CTL1, {0x0}));
+ out_packets.emplace_back(new NopPacket<ConfigurationRegister>());
+ out_packets.emplace_back(new NopPacket<ConfigurationRegister>());
+ out_packets.emplace_back(new NopPacket<ConfigurationRegister>());
+ out_packets.emplace_back(new NopPacket<ConfigurationRegister>());
+ out_packets.emplace_back(new NopPacket<ConfigurationRegister>());
+ out_packets.emplace_back(new NopPacket<ConfigurationRegister>());
+ out_packets.emplace_back(new NopPacket<ConfigurationRegister>());
+ out_packets.emplace_back(new NopPacket<ConfigurationRegister>());
+ out_packets.emplace_back(
+ new ConfigurationPacketWithPayload<1, ConfigurationRegister>(
+ ConfigurationPacket<ConfigurationRegister>::Opcode::Write,
+ ConfigurationRegister::FAR, {0x0}));
+ out_packets.emplace_back(
+ new ConfigurationPacketWithPayload<1, ConfigurationRegister>(
+ ConfigurationPacket<ConfigurationRegister>::Opcode::Write,
+ ConfigurationRegister::CMD,
+ {static_cast<uint32_t>(xc7series::Command::WCFG)}));
+ out_packets.emplace_back(new NopPacket<ConfigurationRegister>());
+
+ // Frame data write
+ out_packets.emplace_back(new ConfigurationPacket<ConfigurationRegister>(
+ TYPE1, ConfigurationPacket<ConfigurationRegister>::Opcode::Write,
+ ConfigurationRegister::FDRI, {}));
+ out_packets.emplace_back(new ConfigurationPacket<ConfigurationRegister>(
+ TYPE2, ConfigurationPacket<ConfigurationRegister>::Opcode::Write,
+ ConfigurationRegister::FDRI, packet_data));
+
+ // Finalization sequence
+ out_packets.emplace_back(
+ new ConfigurationPacketWithPayload<1, ConfigurationRegister>(
+ ConfigurationPacket<ConfigurationRegister>::Opcode::Write,
+ ConfigurationRegister::CMD,
+ {static_cast<uint32_t>(xc7series::Command::RCRC)}));
+ out_packets.emplace_back(new NopPacket<ConfigurationRegister>());
+ out_packets.emplace_back(new NopPacket<ConfigurationRegister>());
+ out_packets.emplace_back(
+ new ConfigurationPacketWithPayload<1, ConfigurationRegister>(
+ ConfigurationPacket<ConfigurationRegister>::Opcode::Write,
+ ConfigurationRegister::CMD,
+ {static_cast<uint32_t>(xc7series::Command::GRESTORE)}));
+ out_packets.emplace_back(new NopPacket<ConfigurationRegister>());
+ out_packets.emplace_back(
+ new ConfigurationPacketWithPayload<1, ConfigurationRegister>(
+ ConfigurationPacket<ConfigurationRegister>::Opcode::Write,
+ ConfigurationRegister::CMD,
+ {static_cast<uint32_t>(xc7series::Command::LFRM)}));
+ for (int ii = 0; ii < 100; ++ii) {
+ out_packets.emplace_back(
+ new NopPacket<ConfigurationRegister>());
+ }
+ out_packets.emplace_back(
+ new ConfigurationPacketWithPayload<1, ConfigurationRegister>(
+ ConfigurationPacket<ConfigurationRegister>::Opcode::Write,
+ ConfigurationRegister::CMD,
+ {static_cast<uint32_t>(xc7series::Command::START)}));
+ out_packets.emplace_back(new NopPacket<ConfigurationRegister>());
+ out_packets.emplace_back(
+ new ConfigurationPacketWithPayload<1, ConfigurationRegister>(
+ ConfigurationPacket<ConfigurationRegister>::Opcode::Write,
+ ConfigurationRegister::FAR, {0x3be0000}));
+ out_packets.emplace_back(
+ new ConfigurationPacketWithPayload<1, ConfigurationRegister>(
+ ConfigurationPacket<ConfigurationRegister>::Opcode::Write,
+ ConfigurationRegister::MASK, {0x101}));
+ out_packets.emplace_back(
+ new ConfigurationPacketWithPayload<1, ConfigurationRegister>(
+ ConfigurationPacket<ConfigurationRegister>::Opcode::Write,
+ ConfigurationRegister::CTL0, {0x101}));
+ out_packets.emplace_back(
+ new ConfigurationPacketWithPayload<1, ConfigurationRegister>(
+ ConfigurationPacket<ConfigurationRegister>::Opcode::Write,
+ ConfigurationRegister::CMD,
+ {static_cast<uint32_t>(xc7series::Command::RCRC)}));
+ out_packets.emplace_back(new NopPacket<ConfigurationRegister>());
+ out_packets.emplace_back(new NopPacket<ConfigurationRegister>());
+ out_packets.emplace_back(
+ new ConfigurationPacketWithPayload<1, ConfigurationRegister>(
+ ConfigurationPacket<ConfigurationRegister>::Opcode::Write,
+ ConfigurationRegister::CMD,
+ {static_cast<uint32_t>(xc7series::Command::DESYNC)}));
+ for (int ii = 0; ii < 400; ++ii) {
+ out_packets.emplace_back(
+ new NopPacket<ConfigurationRegister>());
+ }
+}
+} // namespace xilinx
+} // namespace prjxray
diff --git a/lib/xilinx/configuration_packet.cc b/lib/xilinx/configuration_packet.cc
new file mode 100644
index 0000000..14d66b0
--- /dev/null
+++ b/lib/xilinx/configuration_packet.cc
@@ -0,0 +1,220 @@
+#include <prjxray/xilinx/configuration_packet.h>
+
+#include <iomanip>
+#include <iostream>
+#include <ostream>
+
+#include <prjxray/bit_ops.h>
+
+namespace prjxray {
+namespace xilinx {
+
+template <>
+std::pair<absl::Span<uint32_t>,
+ absl::optional<ConfigurationPacket<Spartan6ConfigurationRegister>>>
+ConfigurationPacket<Spartan6ConfigurationRegister>::InitWithWords(
+ absl::Span<uint32_t> words,
+ const ConfigurationPacket<Spartan6ConfigurationRegister>* previous_packet) {
+ using ConfigurationRegister = Spartan6ConfigurationRegister;
+ // Need at least one 32-bit word to have a valid packet header.
+ if (words.size() < 1)
+ return {words, {}};
+
+ uint32_t header_type = bit_field_get(words[0], 15, 13);
+ switch (header_type) {
+ case NONE:
+ // Type 0 is emitted at the end of a configuration row
+ // when BITSTREAM.GENERAL.DEBUGBITSTREAM is set to YES.
+ // These seem to be padding that are interepreted as
+ // NOPs. Since Type 0 packets don't exist according to
+ // UG470 and they seem to be zero-filled, just consume
+ // the bytes without generating a packet.
+ return {words.subspan(1),
+ {{header_type,
+ Opcode::NOP,
+ ConfigurationRegister::CRC,
+ {}}}};
+ case TYPE1: {
+ Opcode opcode = static_cast<Opcode>(
+ bit_field_get(words[0], 12, 11));
+ ConfigurationRegister address =
+ static_cast<ConfigurationRegister>(
+ bit_field_get(words[0], 10, 5));
+ uint32_t data_word_count =
+ bit_field_get(words[0], 4, 0);
+
+ // If the full packet has not been received, return as
+ // though no valid packet was found.
+ if (data_word_count > words.size() - 1) {
+ return {words, {}};
+ }
+
+ return {words.subspan(data_word_count + 1),
+ {{header_type, opcode, address,
+ words.subspan(1, data_word_count)}}};
+ }
+ case TYPE2: {
+ absl::optional<ConfigurationPacket> packet;
+ Opcode opcode = static_cast<Opcode>(
+ bit_field_get(words[0], 12, 11));
+ ConfigurationRegister address =
+ static_cast<ConfigurationRegister>(
+ bit_field_get(words[0], 10, 5));
+ // Type 2 packets according to UG380 consist of
+ // a header word followed by 2 WCD (Word Count Data)
+ // words
+ uint32_t data_word_count = (words[1] << 16) | words[2];
+
+ // If the full packet has not been received, return as
+ // though no valid packet was found.
+ if (data_word_count > words.size() - 1) {
+ return {words, {}};
+ }
+
+ // Create a packet that contains as many data words
+ // as specified in the WCD packets, but omit them
+ // in the configuration packet along with the header
+ // FIXME Figure out why we need the extra 2 words
+ packet = ConfigurationPacket(
+ header_type, opcode, address,
+ words.subspan(3, data_word_count + 2));
+
+ return {words.subspan(data_word_count + 3), packet};
+ }
+ default:
+ return {{}, {}};
+ }
+}
+
+template <>
+std::pair<absl::Span<uint32_t>,
+ absl::optional<ConfigurationPacket<Series7ConfigurationRegister>>>
+ConfigurationPacket<Series7ConfigurationRegister>::InitWithWords(
+ absl::Span<uint32_t> words,
+ const ConfigurationPacket<Series7ConfigurationRegister>* previous_packet) {
+ using ConfigurationRegister = Series7ConfigurationRegister;
+ // Need at least one 32-bit word to have a valid packet header.
+ if (words.size() < 1)
+ return {words, {}};
+
+ uint32_t header_type = bit_field_get(words[0], 31, 29);
+ switch (header_type) {
+ case NONE:
+ // Type 0 is emitted at the end of a configuration row
+ // when BITSTREAM.GENERAL.DEBUGBITSTREAM is set to YES.
+ // These seem to be padding that are interepreted as
+ // NOPs. Since Type 0 packets don't exist according to
+ // UG470 and they seem to be zero-filled, just consume
+ // the bytes without generating a packet.
+ return {words.subspan(1),
+ {{header_type,
+ Opcode::NOP,
+ ConfigurationRegister::CRC,
+ {}}}};
+ case TYPE1: {
+ Opcode opcode = static_cast<Opcode>(
+ bit_field_get(words[0], 28, 27));
+ ConfigurationRegister address =
+ static_cast<ConfigurationRegister>(
+ bit_field_get(words[0], 26, 13));
+ uint32_t data_word_count =
+ bit_field_get(words[0], 10, 0);
+
+ // If the full packet has not been received, return as
+ // though no valid packet was found.
+ if (data_word_count > words.size() - 1) {
+ return {words, {}};
+ }
+
+ return {words.subspan(data_word_count + 1),
+ {{header_type, opcode, address,
+ words.subspan(1, data_word_count)}}};
+ }
+ case TYPE2: {
+ absl::optional<ConfigurationPacket> packet;
+ Opcode opcode = static_cast<Opcode>(
+ bit_field_get(words[0], 28, 27));
+ uint32_t data_word_count =
+ bit_field_get(words[0], 26, 0);
+
+ // If the full packet has not been received, return as
+ // though no valid packet was found.
+ if (data_word_count > words.size() - 1) {
+ return {words, {}};
+ }
+
+ if (previous_packet) {
+ packet = ConfigurationPacket(
+ header_type, opcode,
+ previous_packet->address(),
+ words.subspan(1, data_word_count));
+ }
+
+ return {words.subspan(data_word_count + 1), packet};
+ }
+ default:
+ return {{}, {}};
+ }
+}
+
+template <class ConfigRegType>
+std::ostream& operator<<(std::ostream& o,
+ const ConfigurationPacket<ConfigRegType>& packet) {
+ if (packet.header_type() == 0x0) {
+ return o << "[Zero-pad]" << std::endl;
+ }
+
+ switch (packet.opcode()) {
+ case ConfigurationPacket<ConfigRegType>::Opcode::NOP:
+ o << "[NOP]" << std::endl;
+ break;
+ case ConfigurationPacket<ConfigRegType>::Opcode::Read:
+ o << "[Read Type=";
+ o << packet.header_type();
+ o << " Address=";
+ o << std::setw(2) << std::hex;
+ o << static_cast<int>(packet.address());
+ o << " Length=";
+ o << std::setw(10) << std::dec << packet.data().size();
+ o << " Reg=\"" << packet.address() << "\"";
+ o << "]" << std::endl;
+ break;
+ case ConfigurationPacket<ConfigRegType>::Opcode::Write:
+ o << "[Write Type=";
+ o << packet.header_type();
+ o << " Address=";
+ o << std::setw(2) << std::hex;
+ o << static_cast<int>(packet.address());
+ o << " Length=";
+ o << std::setw(10) << std::dec << packet.data().size();
+ o << " Reg=\"" << packet.address() << "\"";
+ o << "]" << std::endl;
+ o << "Data in hex:" << std::endl;
+
+ for (size_t ii = 0; ii < packet.data().size(); ++ii) {
+ o << std::setw(8) << std::hex;
+ o << packet.data()[ii] << " ";
+
+ if ((ii + 1) % 4 == 0) {
+ o << std::endl;
+ }
+ }
+ if (packet.data().size() % 4 != 0) {
+ o << std::endl;
+ }
+ break;
+ default:
+ o << "[Invalid Opcode]" << std::endl;
+ }
+
+ return o;
+}
+
+template std::ostream& operator<<(
+ std::ostream&,
+ const ConfigurationPacket<Spartan6ConfigurationRegister>&);
+template std::ostream& operator<<(
+ std::ostream&,
+ const ConfigurationPacket<Series7ConfigurationRegister>&);
+} // namespace xilinx
+} // namespace prjxray
diff --git a/lib/xilinx/configuration_register.cc b/lib/xilinx/configuration_register.cc
new file mode 100644
index 0000000..2e49c54
--- /dev/null
+++ b/lib/xilinx/configuration_register.cc
@@ -0,0 +1,137 @@
+#include <prjxray/xilinx/configuration_register.h>
+
+namespace prjxray {
+namespace xilinx {
+
+std::ostream& operator<<(std::ostream& o,
+ const Spartan6ConfigurationRegister& value) {
+ switch (value) {
+ case Spartan6ConfigurationRegister::CRC:
+ return o << "CRC";
+ case Spartan6ConfigurationRegister::FAR_MAJ:
+ return o << "Frame Address Register Block and Major";
+ case Spartan6ConfigurationRegister::FAR_MIN:
+ return o << "Frame Address Register Minor";
+ case Spartan6ConfigurationRegister::FDRI:
+ return o << "Frame Data Input";
+ case Spartan6ConfigurationRegister::FDRO:
+ return o << "Frame Data Output";
+ case Spartan6ConfigurationRegister::CMD:
+ return o << "Command";
+ case Spartan6ConfigurationRegister::CTL:
+ return o << "Control";
+ case Spartan6ConfigurationRegister::MASK:
+ return o << "Control Mask";
+ case Spartan6ConfigurationRegister::STAT:
+ return o << "Status";
+ case Spartan6ConfigurationRegister::LOUT:
+ return o << "Legacy Output";
+ case Spartan6ConfigurationRegister::COR1:
+ return o << "Configuration Option 1";
+ case Spartan6ConfigurationRegister::COR2:
+ return o << "Configuration Option 2";
+ case Spartan6ConfigurationRegister::PWRDN_REG:
+ return o << "Power-down Option register";
+ case Spartan6ConfigurationRegister::FLR:
+ return o << "Frame Length register";
+ case Spartan6ConfigurationRegister::IDCODE:
+ return o << "Device ID";
+ case Spartan6ConfigurationRegister::CWDT:
+ return o << "Watchdog Timer";
+ case Spartan6ConfigurationRegister::HC_OPT_REG:
+ return o << "House Clean Option register";
+ case Spartan6ConfigurationRegister::CSBO:
+ return o << "CSB output for parallel daisy-chaining";
+ case Spartan6ConfigurationRegister::GENERAL1:
+ return o << "Power-up self test or loadable program "
+ "address";
+ case Spartan6ConfigurationRegister::GENERAL2:
+ return o << "Power-up self test or loadable program "
+ << "address and new SPI opcode";
+ case Spartan6ConfigurationRegister::GENERAL3:
+ return o << "Golden bitstream address";
+ case Spartan6ConfigurationRegister::GENERAL4:
+ return o
+ << "Golden bitstream address and new SPI opcode";
+ case Spartan6ConfigurationRegister::GENERAL5:
+ return o
+ << "User-defined register for fail-safe scheme";
+ case Spartan6ConfigurationRegister::MODE_REG:
+ return o << "Reboot mode";
+ case Spartan6ConfigurationRegister::PU_GWE:
+ return o << "GWE cycle during wake-up from suspend";
+ case Spartan6ConfigurationRegister::PU_GTS:
+ return o << "GTS cycle during wake-up from suspend";
+ case Spartan6ConfigurationRegister::MFWR:
+ return o << "Multi-frame write register";
+ case Spartan6ConfigurationRegister::CCLK_FREQ:
+ return o << "CCLK frequency for master mode";
+ case Spartan6ConfigurationRegister::SEU_OPT:
+ return o << "SEU frequency, enable and status";
+ case Spartan6ConfigurationRegister::EXP_SIGN:
+ return o << "Expected readback signature for SEU "
+ "detection";
+ case Spartan6ConfigurationRegister::RDBK_SIGN:
+ return o << "Readback signature for readback command "
+ "and SEU";
+ case Spartan6ConfigurationRegister::BOOTSTS:
+ return o << "Boot History Register";
+ case Spartan6ConfigurationRegister::EYE_MASK:
+ return o << "Mask pins for Multi-Pin Wake-Up";
+ case Spartan6ConfigurationRegister::CBC_REG:
+ return o << "Initial CBC Value Register";
+ default:
+ return o << "Unknown";
+ }
+}
+
+std::ostream& operator<<(std::ostream& o,
+ const Series7ConfigurationRegister& value) {
+ switch (value) {
+ case Series7ConfigurationRegister::CRC:
+ return o << "CRC";
+ case Series7ConfigurationRegister::FAR:
+ return o << "Frame Address";
+ case Series7ConfigurationRegister::FDRI:
+ return o << "Frame Data Input";
+ case Series7ConfigurationRegister::FDRO:
+ return o << "Frame Data Output";
+ case Series7ConfigurationRegister::CMD:
+ return o << "Command";
+ case Series7ConfigurationRegister::CTL0:
+ return o << "Control 0";
+ case Series7ConfigurationRegister::MASK:
+ return o << "Mask for CTL0 and CTL1";
+ case Series7ConfigurationRegister::STAT:
+ return o << "Status";
+ case Series7ConfigurationRegister::LOUT:
+ return o << "Legacy Output";
+ case Series7ConfigurationRegister::COR0:
+ return o << "Configuration Option 0";
+ case Series7ConfigurationRegister::MFWR:
+ return o << "Multiple Frame Write";
+ case Series7ConfigurationRegister::CBC:
+ return o << "Initial CBC Value";
+ case Series7ConfigurationRegister::IDCODE:
+ return o << "Device ID";
+ case Series7ConfigurationRegister::AXSS:
+ return o << "User Access";
+ case Series7ConfigurationRegister::COR1:
+ return o << "Configuration Option 1";
+ case Series7ConfigurationRegister::WBSTAR:
+ return o << "Warm Boot Start Address";
+ case Series7ConfigurationRegister::TIMER:
+ return o << "Watchdog Timer";
+ case Series7ConfigurationRegister::BOOTSTS:
+ return o << "Boot History Status";
+ case Series7ConfigurationRegister::CTL1:
+ return o << "Control 1";
+ case Series7ConfigurationRegister::BSPI:
+ return o << "BPI/SPI Configuration Options";
+ default:
+ return o << "Unknown";
+ }
+}
+
+} // namespace xilinx
+} // namespace prjxray
diff --git a/lib/xilinx/frames.cc b/lib/xilinx/frames.cc
new file mode 100644
index 0000000..616c723
--- /dev/null
+++ b/lib/xilinx/frames.cc
@@ -0,0 +1,28 @@
+#include <prjxray/xilinx/frames.h>
+#include <prjxray/xilinx/xc7series/ecc.h>
+
+namespace prjxray {
+namespace xilinx {
+template <>
+void Frames<Series7>::updateECC(typename Frames<Series7>::FrameData& data) {
+ xc7series::updateECC(data);
+}
+
+template <>
+void Frames<UltraScale>::updateECC(
+ typename Frames<UltraScale>::FrameData& data) {
+ xc7series::updateECC(data);
+}
+
+template <>
+void Frames<UltraScalePlus>::updateECC(
+ typename Frames<UltraScalePlus>::FrameData& data) {
+ xc7series::updateECC(data);
+}
+
+// Spartan6 doesn't have ECC
+template <>
+void Frames<Spartan6>::updateECC(typename Frames<Spartan6>::FrameData& data) {}
+
+} // namespace xilinx
+} // namespace prjxray
diff --git a/lib/xilinx/spartan6/block_type.cc b/lib/xilinx/spartan6/block_type.cc
new file mode 100644
index 0000000..4405972
--- /dev/null
+++ b/lib/xilinx/spartan6/block_type.cc
@@ -0,0 +1,62 @@
+#include <prjxray/xilinx/spartan6/block_type.h>
+
+namespace prjxray {
+namespace xilinx {
+namespace spartan6 {
+
+std::ostream& operator<<(std::ostream& o, BlockType value) {
+ switch (value) {
+ case BlockType::CLB_IOI_CLK:
+ o << "CLB/IOI/CLK";
+ break;
+ case BlockType::BLOCK_RAM:
+ o << "Block RAM";
+ break;
+ case BlockType::IOB:
+ o << "Config CLB";
+ break;
+ }
+
+ return o;
+}
+
+} // namespace spartan6
+} // namespace xilinx
+} // namespace prjxray
+
+namespace YAML {
+
+Node convert<prjxray::xilinx::spartan6::BlockType>::encode(
+ const prjxray::xilinx::spartan6::BlockType& rhs) {
+ switch (rhs) {
+ case prjxray::xilinx::spartan6::BlockType::CLB_IOI_CLK:
+ return Node("CLB_IOI_CLK");
+ case prjxray::xilinx::spartan6::BlockType::BLOCK_RAM:
+ return Node("BLOCK_RAM");
+ case prjxray::xilinx::spartan6::BlockType::IOB:
+ return Node("IOB");
+ default:
+ return Node(static_cast<unsigned int>(rhs));
+ }
+}
+
+bool YAML::convert<prjxray::xilinx::spartan6::BlockType>::decode(
+ const Node& node,
+ prjxray::xilinx::spartan6::BlockType& lhs) {
+ auto type_str = node.as<std::string>();
+
+ if (type_str == "CLB_IOI_CLK") {
+ lhs = prjxray::xilinx::spartan6::BlockType::CLB_IOI_CLK;
+ return true;
+ } else if (type_str == "BLOCK_RAM") {
+ lhs = prjxray::xilinx::spartan6::BlockType::BLOCK_RAM;
+ return true;
+ } else if (type_str == "IOB") {
+ lhs = prjxray::xilinx::spartan6::BlockType::IOB;
+ return true;
+ } else {
+ return false;
+ }
+}
+
+} // namespace YAML
diff --git a/lib/xilinx/spartan6/configuration_bus.cc b/lib/xilinx/spartan6/configuration_bus.cc
new file mode 100644
index 0000000..920efd5
--- /dev/null
+++ b/lib/xilinx/spartan6/configuration_bus.cc
@@ -0,0 +1,76 @@
+#include <prjxray/xilinx/spartan6/configuration_bus.h>
+
+#include <iostream>
+
+namespace prjxray {
+namespace xilinx {
+namespace spartan6 {
+
+bool ConfigurationBus::IsValidFrameAddress(FrameAddress address) const {
+ auto addr_column = configuration_columns_.find(address.column());
+ if (addr_column == configuration_columns_.end())
+ return false;
+
+ return addr_column->second.IsValidFrameAddress(address);
+}
+
+absl::optional<FrameAddress> ConfigurationBus::GetNextFrameAddress(
+ FrameAddress address) const {
+ // Find the column for the current address.
+ auto addr_column = configuration_columns_.find(address.column());
+
+ // If the current address isn't in a known column, no way to know the
+ // next address.
+ if (addr_column == configuration_columns_.end())
+ return {};
+
+ // Ask the column for the next address.
+ absl::optional<FrameAddress> next_address =
+ addr_column->second.GetNextFrameAddress(address);
+ if (next_address)
+ return next_address;
+
+ // The current column doesn't know what the next address is. Assume
+ // that the next valid address is the beginning of the next column.
+ if (++addr_column != configuration_columns_.end()) {
+ auto next_address = FrameAddress(
+ address.block_type(), address.row(), addr_column->first, 0);
+ if (addr_column->second.IsValidFrameAddress(next_address))
+ return next_address;
+ }
+
+ // Not in this bus.
+ return {};
+}
+
+} // namespace spartan6
+} // namespace xilinx
+} // namespace prjxray
+
+namespace spartan6 = prjxray::xilinx::spartan6;
+
+namespace YAML {
+
+Node convert<spartan6::ConfigurationBus>::encode(
+ const spartan6::ConfigurationBus& rhs) {
+ Node node;
+ node.SetTag("xilinx/spartan6/configuration_bus");
+ node["configuration_columns"] = rhs.configuration_columns_;
+ return node;
+}
+
+bool convert<spartan6::ConfigurationBus>::decode(
+ const Node& node,
+ spartan6::ConfigurationBus& lhs) {
+ if (!node.Tag().empty() &&
+ node.Tag() != "xilinx/spartan6/configuration_bus") {
+ return false;
+ }
+
+ lhs.configuration_columns_ =
+ node["configuration_columns"]
+ .as<std::map<unsigned int, spartan6::ConfigurationColumn>>();
+ return true;
+}
+
+} // namespace YAML
diff --git a/lib/xilinx/spartan6/configuration_column.cc b/lib/xilinx/spartan6/configuration_column.cc
new file mode 100644
index 0000000..dcc8422
--- /dev/null
+++ b/lib/xilinx/spartan6/configuration_column.cc
@@ -0,0 +1,52 @@
+#include <prjxray/xilinx/spartan6/configuration_column.h>
+
+namespace prjxray {
+namespace xilinx {
+namespace spartan6 {
+
+bool ConfigurationColumn::IsValidFrameAddress(FrameAddress address) const {
+ return address.minor() < frame_count_;
+}
+
+absl::optional<FrameAddress> ConfigurationColumn::GetNextFrameAddress(
+ FrameAddress address) const {
+ if (!IsValidFrameAddress(address))
+ return {};
+
+ if (static_cast<unsigned int>(address.minor() + 1) < frame_count_) {
+ return address + 1;
+ }
+
+ // Next address is not in this column.
+ return {};
+}
+
+} // namespace spartan6
+} // namespace xilinx
+} // namespace prjxray
+
+namespace spartan6 = prjxray::xilinx::spartan6;
+
+namespace YAML {
+
+Node convert<spartan6::ConfigurationColumn>::encode(
+ const spartan6::ConfigurationColumn& rhs) {
+ Node node;
+ node.SetTag("xilinx/spartan6/configuration_column");
+ node["frame_count"] = rhs.frame_count_;
+ return node;
+}
+
+bool convert<spartan6::ConfigurationColumn>::decode(
+ const Node& node,
+ spartan6::ConfigurationColumn& lhs) {
+ if (!node.Tag().empty() &&
+ node.Tag() != "xilinx/spartan6/configuration_column") {
+ return false;
+ }
+
+ lhs.frame_count_ = node["frame_count"].as<unsigned int>();
+ return true;
+}
+
+} // namespace YAML
diff --git a/lib/xilinx/spartan6/configuration_row.cc b/lib/xilinx/spartan6/configuration_row.cc
new file mode 100644
index 0000000..bb347e7
--- /dev/null
+++ b/lib/xilinx/spartan6/configuration_row.cc
@@ -0,0 +1,62 @@
+#include <prjxray/xilinx/spartan6/configuration_row.h>
+
+namespace prjxray {
+namespace xilinx {
+namespace spartan6 {
+
+bool Row::IsValidFrameAddress(FrameAddress address) const {
+ auto addr_bus = configuration_buses_.find(address.block_type());
+ if (addr_bus == configuration_buses_.end())
+ return false;
+ return addr_bus->second.IsValidFrameAddress(address);
+}
+
+absl::optional<FrameAddress> Row::GetNextFrameAddress(
+ FrameAddress address) const {
+ // Find the bus for the current address.
+ auto addr_bus = configuration_buses_.find(address.block_type());
+
+ // If the current address isn't in a known bus, no way to know the next.
+ if (addr_bus == configuration_buses_.end())
+ return {};
+
+ // Ask the bus for the next address.
+ absl::optional<FrameAddress> next_address =
+ addr_bus->second.GetNextFrameAddress(address);
+ if (next_address)
+ return next_address;
+
+ // The current bus doesn't know what the next address is. Rows come next
+ // in frame address numerical order so punt back to the caller to figure
+ // it out.
+ return {};
+}
+
+} // namespace spartan6
+} // namespace xilinx
+} // namespace prjxray
+
+namespace spartan6 = prjxray::xilinx::spartan6;
+
+namespace YAML {
+
+Node convert<spartan6::Row>::encode(const spartan6::Row& rhs) {
+ Node node;
+ node.SetTag("xilinx/spartan6/row");
+ node["configuration_buses"] = rhs.configuration_buses_;
+ return node;
+}
+
+bool convert<spartan6::Row>::decode(const Node& node, spartan6::Row& lhs) {
+ if (!node.Tag().empty() && node.Tag() != "xilinx/spartan6/row") {
+ return false;
+ }
+
+ lhs.configuration_buses_ =
+ node["configuration_buses"]
+ .as<std::map<spartan6::BlockType,
+ spartan6::ConfigurationBus>>();
+ return true;
+}
+
+} // namespace YAML
diff --git a/lib/xilinx/spartan6/frame_address.cc b/lib/xilinx/spartan6/frame_address.cc
new file mode 100644
index 0000000..ff5bcee
--- /dev/null
+++ b/lib/xilinx/spartan6/frame_address.cc
@@ -0,0 +1,92 @@
+#include <iomanip>
+
+#include <prjxray/bit_ops.h>
+#include <prjxray/xilinx/spartan6/frame_address.h>
+
+namespace prjxray {
+namespace xilinx {
+namespace spartan6 {
+
+// According to UG380 pg. 101 the Frame Address Register (FAR)
+// consists of two 16-bit registers (FAR MAJOR and FAR MINOR).
+// We construct the 32-bit frame address from these two.
+FrameAddress::FrameAddress(spartan6::BlockType block_type,
+ uint8_t row,
+ uint8_t column,
+ uint16_t minor) {
+ address_ = bit_field_set(0, 31, 28, block_type);
+ address_ =
+ bit_field_set(address_, 27, 24, row); // high register, bit 8-11
+ address_ =
+ bit_field_set(address_, 23, 16, column); // high register, bits 0-7
+ address_ =
+ bit_field_set(address_, 9, 0, minor); // low register, bit 0-9
+}
+
+bool FrameAddress::is_bottom_half_rows() const {
+ return false;
+}
+
+spartan6::BlockType FrameAddress::block_type() const {
+ return static_cast<typename spartan6::BlockType>(
+ bit_field_get(address_, 31, 28));
+}
+
+uint8_t FrameAddress::row() const {
+ return bit_field_get(address_, 27, 24);
+}
+
+uint8_t FrameAddress::column() const {
+ return bit_field_get(address_, 23, 16);
+}
+
+uint16_t FrameAddress::minor() const {
+ return bit_field_get(address_, 9, 0);
+}
+
+std::ostream& operator<<(std::ostream& o, const FrameAddress& addr) {
+ o << "[" << std::hex << std::showbase << std::setw(10)
+ << static_cast<uint32_t>(addr) << "] "
+ << " Row=" << std::setw(2) << std::dec
+ << static_cast<unsigned int>(addr.row()) << "Column =" << std::setw(2)
+ << std::dec << addr.column() << " Minor=" << std::setw(2) << std::dec
+ << static_cast<unsigned int>(addr.minor())
+ << " Type=" << addr.block_type();
+ return o;
+}
+
+} // namespace spartan6
+} // namespace xilinx
+} // namespace prjxray
+
+namespace YAML {
+
+namespace spartan6 = prjxray::xilinx::spartan6;
+
+Node convert<spartan6::FrameAddress>::encode(
+ const spartan6::FrameAddress& rhs) {
+ Node node;
+ node.SetTag("xilinx/spartan6/frame_address");
+ node["block_type"] = rhs.block_type();
+ node["row"] = static_cast<unsigned int>(rhs.row());
+ node["column"] = static_cast<unsigned int>(rhs.column());
+ node["minor"] = static_cast<unsigned int>(rhs.minor());
+ return node;
+}
+
+bool convert<spartan6::FrameAddress>::decode(const Node& node,
+ spartan6::FrameAddress& lhs) {
+ if (!(node.Tag() == "xilinx/spartan6/frame_address" ||
+ node.Tag() == "xilinx/spartan6/configuration_frame_address") ||
+ !node["block_type"] || !node["row"] || !node["column"] ||
+ !node["minor"])
+ return false;
+
+ lhs = spartan6::FrameAddress(
+ node["block_type"].as<spartan6::BlockType>(),
+ node["row"].as<unsigned int>(), node["column"].as<unsigned int>(),
+ node["minor"].as<unsigned int>());
+ return true;
+}
+
+} // namespace YAML
diff --git a/lib/xilinx/spartan6/global_clock_region.cc b/lib/xilinx/spartan6/global_clock_region.cc
new file mode 100644
index 0000000..55e194e
--- /dev/null
+++ b/lib/xilinx/spartan6/global_clock_region.cc
@@ -0,0 +1,70 @@
+#include <prjxray/xilinx/spartan6/global_clock_region.h>
+
+namespace prjxray {
+namespace xilinx {
+namespace spartan6 {
+
+bool GlobalClockRegion::IsValidFrameAddress(FrameAddress address) const {
+ auto addr_row = rows_.find(address.row());
+ if (addr_row == rows_.end())
+ return false;
+ return addr_row->second.IsValidFrameAddress(address);
+}
+
+absl::optional<FrameAddress> GlobalClockRegion::GetNextFrameAddress(
+ FrameAddress address) const {
+ // Find the row for the current address.
+ auto addr_row = rows_.find(address.row());
+
+ // If the current address isn't in a known row, no way to know the next.
+ if (addr_row == rows_.end())
+ return {};
+
+ // Ask the row for the next address.
+ absl::optional<FrameAddress> next_address =
+ addr_row->second.GetNextFrameAddress(address);
+ if (next_address)
+ return next_address;
+
+ // The current row doesn't know what the next address is. Assume that
+ // the next valid address is the beginning of the next row.
+ if (++addr_row != rows_.end()) {
+ auto next_address =
+ FrameAddress(address.block_type(), addr_row->first, 0, 0);
+ if (addr_row->second.IsValidFrameAddress(next_address))
+ return next_address;
+ }
+
+ // Must be in a different global clock region.
+ return {};
+}
+
+} // namespace spartan6
+} // namespace xilinx
+} // namespace prjxray
+
+namespace spartan6 = prjxray::xilinx::spartan6;
+
+namespace YAML {
+
+Node convert<spartan6::GlobalClockRegion>::encode(
+ const spartan6::GlobalClockRegion& rhs) {
+ Node node;
+ node.SetTag("xilinx/spartan6/global_clock_region");
+ node["rows"] = rhs.rows_;
+ return node;
+}
+
+bool convert<spartan6::GlobalClockRegion>::decode(
+ const Node& node,
+ spartan6::GlobalClockRegion& lhs) {
+ if (!node.Tag().empty() &&
+ node.Tag() != "xilinx/spartan6/global_clock_region") {
+ return false;
+ }
+
+ lhs.rows_ = node["rows"].as<std::map<unsigned int, spartan6::Row>>();
+ return true;
+}
+
+} // namespace YAML
diff --git a/lib/xilinx/spartan6/part.cc b/lib/xilinx/spartan6/part.cc
new file mode 100644
index 0000000..b74aff5
--- /dev/null
+++ b/lib/xilinx/spartan6/part.cc
@@ -0,0 +1,115 @@
+#include <prjxray/xilinx/spartan6/part.h>
+
+#include <iomanip>
+#include <iostream>
+#include <sstream>
+
+namespace prjxray {
+namespace xilinx {
+namespace spartan6 {
+
+absl::optional<Part> Part::FromFile(const std::string& path) {
+ try {
+ YAML::Node yaml = YAML::LoadFile(path);
+ return yaml.as<Part>();
+ } catch (YAML::Exception& e) {
+ return {};
+ }
+}
+
+bool Part::IsValidFrameAddress(FrameAddress address) const {
+ if (address.is_bottom_half_rows()) {
+ return bottom_region_.IsValidFrameAddress(address);
+ } else {
+ return top_region_.IsValidFrameAddress(address);
+ }
+}
+
+absl::optional<FrameAddress> Part::GetNextFrameAddress(
+ FrameAddress address) const {
+ // Ask the current global clock region first.
+ absl::optional<FrameAddress> next_address =
+ (address.is_bottom_half_rows()
+ ? bottom_region_.GetNextFrameAddress(address)
+ : top_region_.GetNextFrameAddress(address));
+ if (next_address)
+ return next_address;
+
+ // If the current address is in the top region, the bottom region is
+ // next numerically.
+ if (!address.is_bottom_half_rows()) {
+ next_address = FrameAddress(address.block_type(), 0, 0, 0);
+ if (bottom_region_.IsValidFrameAddress(*next_address))
+ return next_address;
+ }
+
+ // Block types are next numerically.
+ if (address.block_type() < spartan6::BlockType::BLOCK_RAM) {
+ next_address =
+ FrameAddress(spartan6::BlockType::BLOCK_RAM, 0, 0, 0);
+ if (IsValidFrameAddress(*next_address))
+ return next_address;
+ }
+
+ if (address.block_type() < spartan6::BlockType::IOB) {
+ next_address = FrameAddress(spartan6::BlockType::IOB, 0, 0, 0);
+ if (IsValidFrameAddress(*next_address))
+ return next_address;
+ }
+
+ return {};
+}
+
+} // namespace spartan6
+} // namespace xilinx
+} // namespace prjxray
+
+namespace spartan6 = prjxray::xilinx::spartan6;
+
+namespace YAML {
+
+Node convert<spartan6::Part>::encode(const spartan6::Part& rhs) {
+ Node node;
+ node.SetTag("xilinx/spartan6/part");
+
+ std::ostringstream idcode_str;
+ idcode_str << "0x" << std::hex << rhs.idcode_;
+ node["idcode"] = idcode_str.str();
+ node["global_clock_regions"]["top"] = rhs.top_region_;
+ node["global_clock_regions"]["bottom"] = rhs.bottom_region_;
+ return node;
+}
+
+bool convert<spartan6::Part>::decode(const Node& node, spartan6::Part& lhs) {
+ if (!node.Tag().empty() && node.Tag() != "xilinx/spartan6/part")
+ return false;
+
+ if (!node["global_clock_regions"] && !node["configuration_ranges"]) {
+ return false;
+ }
+
+ lhs.idcode_ = node["idcode"].as<uint32_t>();
+
+ if (node["global_clock_regions"]) {
+ lhs.top_region_ = node["global_clock_regions"]["top"]
+ .as<spartan6::GlobalClockRegion>();
+ lhs.bottom_region_ = node["global_clock_regions"]["bottom"]
+ .as<spartan6::GlobalClockRegion>();
+ } else if (node["configuration_ranges"]) {
+ std::vector<spartan6::FrameAddress> addresses;
+ for (auto range : node["configuration_ranges"]) {
+ auto begin =
+ range["begin"].as<spartan6::FrameAddress>();
+ auto end = range["end"].as<spartan6::FrameAddress>();
+ for (uint32_t cur = begin; cur < end; ++cur) {
+ addresses.push_back(cur);
+ }
+ }
+
+ lhs = spartan6::Part(lhs.idcode_, addresses);
+ }
+
+ return true;
+};
+
+} // namespace YAML
diff --git a/lib/xilinx/tests/spartan6/bitstream_reader_test.cc b/lib/xilinx/tests/spartan6/bitstream_reader_test.cc
new file mode 100644
index 0000000..4f807bd
--- /dev/null
+++ b/lib/xilinx/tests/spartan6/bitstream_reader_test.cc
@@ -0,0 +1,109 @@
+#include <array>
+
+#include <absl/types/span.h>
+#include <gtest/gtest.h>
+#include <prjxray/xilinx/bitstream_reader.h>
+#include <prjxray/xilinx/configuration_packet.h>
+#include <prjxray/xilinx/configuration_register.h>
+
+#include <prjxray/big_endian_span.h>
+
+using namespace prjxray::xilinx;
+TEST(BitstreamReaderTest, InitWithEmptyBytesReturnsNull) {
+ absl::Span<uint8_t> bitstream;
+ auto reader = BitstreamReader<Spartan6>::InitWithBytes(bitstream);
+ EXPECT_FALSE(reader);
+}
+
+TEST(BitstreamReaderTest, InitWithOnlySyncReturnsObject) {
+ std::vector<uint8_t> bitstream{0xAA, 0x99, 0x55, 0x66};
+ absl::Span<std::vector<uint8_t>::value_type> bitstream_span(bitstream);
+ // auto config_packets =
+ // bitstream_span.subspan(bitstream.end() - bitstream.begin());
+ // auto big_endian_reader =
+ // prjxray::make_big_endian_span<uint16_t>(bitstream_span);
+ // std::vector<uint16_t> words{big_endian_reader.begin(),
+ // big_endian_reader.end()};
+
+ // for (auto word: words) {
+ // std::cout << "0x" << std::hex << word << std::endl;
+ //}
+ auto reader = BitstreamReader<Spartan6>::InitWithBytes(bitstream);
+ EXPECT_TRUE(reader);
+}
+
+TEST(BitstreamReaderTest, InitWithSyncAfterNonWordSizedPaddingReturnsObject) {
+ std::vector<uint8_t> bitstream{0xFF, 0xFE, 0xAA, 0x99, 0x55, 0x66};
+ auto reader = BitstreamReader<Spartan6>::InitWithBytes(bitstream);
+ EXPECT_TRUE(reader);
+}
+
+TEST(BitstreamReaderTest, InitWithSyncAfterWordSizedPaddingReturnsObject) {
+ std::vector<uint8_t> bitstream{0xFF, 0xFE, 0xFD, 0xFC,
+ 0xAA, 0x99, 0x55, 0x66};
+ auto reader = BitstreamReader<Spartan6>::InitWithBytes(bitstream);
+ EXPECT_TRUE(reader);
+}
+
+TEST(BitstreamReaderTest, ParsesType1Packet) {
+ std::vector<uint8_t> bitstream{
+ 0xAA, 0x99, 0x55, 0x66, // sync
+ 0x20, 0x00, 0x20, 0x00, // NOP
+ };
+ auto reader = BitstreamReader<Spartan6>::InitWithBytes(bitstream);
+ ASSERT_TRUE(reader);
+ ASSERT_NE(reader->begin(), reader->end());
+
+ auto first_packet = reader->begin();
+ EXPECT_EQ(first_packet->opcode(),
+ ConfigurationPacket<Spartan6>::Opcode::NOP);
+
+ auto second_packet = ++first_packet;
+ EXPECT_EQ(second_packet->opcode(),
+ ConfigurationPacket<Spartan6>::Opcode::NOP);
+
+ EXPECT_EQ(++second_packet, reader->end());
+}
+
+TEST(BitstreamReaderTest, ParseType2PacketWithoutType1Fails) {
+ std::vector<uint8_t> bitstream{
+ 0xAA, 0x99, 0x55, 0x66, // sync
+ 0x40, 0x00, 0x40, 0x00, // Type 2 NOP
+ };
+ auto reader = BitstreamReader<Spartan6>::InitWithBytes(bitstream);
+ ASSERT_TRUE(reader);
+ EXPECT_EQ(reader->begin(), reader->end());
+}
+
+TEST(BitstreamReaderTest, ParsesType2AfterType1Packet) {
+ std::vector<uint8_t> bitstream{
+ 0xAA, 0x99, // sync
+ 0x55, 0x66, // sync
+ 0x28, 0x80, // Type 1 Read zero bytes from FDRO
+ 0x50, 0x60, // Type 2 Write of 8 16-bit words
+ 0x00, 0x00, // WC1 bits 31:16
+ 0x00, 0x08, // WC2 bits 15:0
+ 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8,
+ 0x9, 0xA, 0xB, 0xC, 0xD, 0xE, 0xF, 0x10,
+ };
+ std::vector<uint32_t> data_words{0x0102, 0x0304, 0x0506, 0x0708,
+ 0x090A, 0x0B0C, 0x0D0E, 0x0F10};
+
+ auto reader = BitstreamReader<Spartan6>::InitWithBytes(bitstream);
+ ASSERT_TRUE(reader);
+ ASSERT_NE(reader->begin(), reader->end());
+
+ auto first_packet = reader->begin();
+ EXPECT_EQ(first_packet->opcode(),
+ ConfigurationPacket<Spartan6>::Opcode::Read);
+ EXPECT_EQ(first_packet->address(), Spartan6::ConfRegType::FDRO);
+ EXPECT_EQ(first_packet->data(), absl::Span<uint32_t>());
+
+ auto third_packet = ++first_packet;
+ ASSERT_NE(third_packet, reader->end());
+ EXPECT_EQ(third_packet->opcode(),
+ ConfigurationPacket<Spartan6>::Opcode::Write);
+ EXPECT_EQ(third_packet->address(), Spartan6::ConfRegType::FDRI);
+ (third_packet->data(), absl::Span<uint32_t>(data_words));
+ EXPECT_EQ(++first_packet, reader->end());
+}
diff --git a/lib/xilinx/tests/spartan6/bitstream_writer_test.cc b/lib/xilinx/tests/spartan6/bitstream_writer_test.cc
new file mode 100644
index 0000000..7331f59
--- /dev/null
+++ b/lib/xilinx/tests/spartan6/bitstream_writer_test.cc
@@ -0,0 +1,183 @@
+#include <array>
+
+#include <gtest/gtest.h>
+#include <prjxray/xilinx/architectures.h>
+#include <prjxray/xilinx/bitstream_reader.h>
+#include <prjxray/xilinx/bitstream_writer.h>
+
+#include <prjxray/bit_ops.h>
+
+using namespace prjxray::xilinx;
+
+constexpr uint32_t kType1NOP = prjxray::bit_field_set<uint32_t>(0, 15, 13, 0x1);
+
+extern const uint32_t MakeType1(const int opcode,
+ const int address,
+ const int word_count);
+
+extern const std::vector<uint32_t> MakeType2(const int opcode,
+ const int address,
+ const int word_count);
+
+void dump_packets(BitstreamWriter<Spartan6> 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<ConfigurationPacket<Spartan6::ConfRegType>>>&
+ packets) {
+ // InitWithWords doesn't like type 0
+ /*
+ static std::vector<uint32_t> words{0x00000000};
+ absl::Span<uint32_t> word_span(words);
+ auto packet =
+ ConfigurationPacket<Spartan6::ConfRegType>::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 ConfigurationPacket<Spartan6::ConfRegType>(
+ 0, ConfigurationPacket<Spartan6::ConfRegType>::NOP,
+ Spartan6::ConfRegType::CRC, word_span));
+}
+
+void AddType1(
+ std::vector<std::unique_ptr<ConfigurationPacket<Spartan6::ConfRegType>>>&
+ packets) {
+ static std::vector<uint32_t> words{MakeType1(0x2, 0x3, 2), 0xAA, 0xBB};
+ absl::Span<uint32_t> word_span(words);
+ auto packet = ConfigurationPacket<Spartan6::ConfRegType>::InitWithWords(
+ word_span);
+ packets.emplace_back(
+ new ConfigurationPacket<Spartan6::ConfRegType>(*(packet.second)));
+}
+
+// Empty
+void AddType1E(
+ std::vector<std::unique_ptr<ConfigurationPacket<Spartan6::ConfRegType>>>&
+ packets) {
+ static std::vector<uint32_t> words{MakeType1(0x2, 0x3, 0)};
+ absl::Span<uint32_t> word_span(words);
+ auto packet = ConfigurationPacket<Spartan6::ConfRegType>::InitWithWords(
+ word_span);
+ packets.emplace_back(
+ new ConfigurationPacket<Spartan6::ConfRegType>(*(packet.second)));
+}
+
+void AddType2(Spartan6::ConfigurationPackage& packets) {
+ // Type 2 packet with data
+ {
+ static std::vector<uint32_t> words;
+ words = MakeType2(0x02, 0x3, 12);
+ std::vector<uint32_t> payload{1, 2, 3, 4, 5, 6,
+ 7, 8, 9, 10, 11, 12};
+ words.insert(words.end(), payload.begin(), payload.end());
+ std::cout << words.size();
+ absl::Span<uint32_t> word_span(words);
+ auto packet =
+ ConfigurationPacket<Spartan6::ConfRegType>::InitWithWords(
+ word_span);
+ packets.emplace_back(
+ new ConfigurationPacket<Spartan6::ConfRegType>(
+ *(packet.second)));
+ }
+}
+
+// Empty packets should produce just the header
+TEST(BitstreamWriterTest, WriteHeader) {
+ std::vector<std::unique_ptr<ConfigurationPacket<Spartan6::ConfRegType>>>
+ packets;
+
+ BitstreamWriter<Spartan6> writer(packets);
+ std::vector<uint32_t> words(writer.begin(), writer.end());
+
+ // Per UG380 pg 78: Bus Width Auto Detection
+ std::vector<uint32_t> ref_header{0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF,
+ 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF,
+ 0xAA99, 0x5566};
+ EXPECT_EQ(words, ref_header);
+
+ // dump_packets(writer);
+}
+
+TEST(BitstreamWriterTest, WriteType0) {
+ std::vector<std::unique_ptr<ConfigurationPacket<Spartan6::ConfRegType>>>
+ packets;
+ AddType0(packets);
+ BitstreamWriter<Spartan6> writer(packets);
+ // dump_packets(writer, false);
+ std::vector<uint32_t> words(writer.begin(), writer.end());
+ std::vector<uint32_t> ref{0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF,
+ 0xFFFF, 0xFFFF, 0xFFFF, 0xAA99, 0x5566,
+ // Type 0
+ 0x0000};
+ EXPECT_EQ(words, ref);
+}
+
+TEST(BitstreamWriterTest, WriteType1) {
+ Spartan6::ConfigurationPackage packets;
+ AddType1(packets);
+ BitstreamWriter<Spartan6> writer(packets);
+ // dump_packets(writer, false);
+ std::vector<uint32_t> words(writer.begin(), writer.end());
+ std::vector<uint32_t> ref{// Bus width + sync
+ 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF,
+ 0xFFFF, 0xFFFF, 0xFFFF, 0xAA99, 0x5566,
+ // Type 1
+ 0x3062, 0x00AA, 0x00BB};
+ EXPECT_EQ(words, ref);
+}
+
+TEST(BitstreamWriterTest, WriteType2) {
+ Spartan6::ConfigurationPackage packets;
+ AddType2(packets);
+ BitstreamWriter<Spartan6> writer(packets);
+ // dump_packets(writer, false);
+ std::vector<uint32_t> words(writer.begin(), writer.end());
+ std::vector<uint32_t> ref{
+ // Bus width + sync
+ 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF,
+ 0xAA99, 0x5566, 0x5060, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005,
+ 0x0006, 0x0007, 0x0008, 0x0009, 0x000A, 0x000B, 0x000C};
+ EXPECT_EQ(words, ref);
+}
+
+TEST(BitstreamWriterTest, WriteMulti) {
+ Spartan6::ConfigurationPackage packets;
+ AddType1(packets);
+ AddType1E(packets);
+ AddType2(packets);
+ AddType1E(packets);
+ BitstreamWriter<Spartan6> writer(packets);
+ // dump_packets(writer, false);
+ std::vector<uint32_t> words(writer.begin(), writer.end());
+ std::vector<uint32_t> ref{// Bus width + sync
+ 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF,
+ 0xFFFF, 0xFFFF, 0xFFFF, 0xAA99, 0x5566,
+ // Type1
+ 0x3062, 0x00AA, 0x00BB,
+ // Type1
+ 0x3060,
+ // Type 1 + type 2 header
+ 0x5060, 0x0001, 0x0002, 0x0003, 0x0004,
+ 0x0005, 0x0006, 0x0007, 0x0008, 0x0009,
+ 0x000A, 0x000B, 0x000C,
+ // Type 1
+ 0x3060};
+ EXPECT_EQ(words, ref);
+}
diff --git a/lib/xilinx/tests/spartan6/block_type_test.cc b/lib/xilinx/tests/spartan6/block_type_test.cc
new file mode 100644
index 0000000..4796242
--- /dev/null
+++ b/lib/xilinx/tests/spartan6/block_type_test.cc
@@ -0,0 +1,29 @@
+#include <prjxray/xilinx/spartan6/block_type.h>
+
+#include <gtest/gtest.h>
+
+using namespace prjxray::xilinx;
+
+TEST(BlockTypeTest, YamlEncode) {
+ YAML::Node node;
+ node.push_back(spartan6::BlockType::CLB_IOI_CLK);
+ node.push_back(spartan6::BlockType::BLOCK_RAM);
+ node.push_back(spartan6::BlockType::IOB);
+
+ EXPECT_EQ(node[0].as<std::string>(), "CLB_IOI_CLK");
+ EXPECT_EQ(node[1].as<std::string>(), "BLOCK_RAM");
+ EXPECT_EQ(node[2].as<std::string>(), "IOB");
+}
+
+TEST(BlockTypeTest, YamlDecode) {
+ YAML::Node node;
+ node.push_back("IOB");
+ node.push_back("BLOCK_RAM");
+ node.push_back("CLB_IOI_CLK");
+
+ EXPECT_EQ(node[0].as<spartan6::BlockType>(), spartan6::BlockType::IOB);
+ EXPECT_EQ(node[1].as<spartan6::BlockType>(),
+ spartan6::BlockType::BLOCK_RAM);
+ EXPECT_EQ(node[2].as<spartan6::BlockType>(),
+ spartan6::BlockType::CLB_IOI_CLK);
+}
diff --git a/lib/xilinx/tests/spartan6/configuration_bus_test.cc b/lib/xilinx/tests/spartan6/configuration_bus_test.cc
new file mode 100644
index 0000000..61f1a49
--- /dev/null
+++ b/lib/xilinx/tests/spartan6/configuration_bus_test.cc
@@ -0,0 +1,70 @@
+#include <prjxray/xilinx/spartan6/configuration_bus.h>
+
+#include <gtest/gtest.h>
+
+using namespace prjxray::xilinx;
+
+TEST(ConfigurationBusTest, IsValidFrameAddress) {
+ std::vector<spartan6::FrameAddress> addresses;
+ addresses.push_back(
+ spartan6::FrameAddress(spartan6::BlockType::BLOCK_RAM, 0, 0, 0));
+ addresses.push_back(
+ spartan6::FrameAddress(spartan6::BlockType::BLOCK_RAM, 0, 0, 1));
+ addresses.push_back(
+ spartan6::FrameAddress(spartan6::BlockType::BLOCK_RAM, 0, 1, 0));
+ addresses.push_back(
+ spartan6::FrameAddress(spartan6::BlockType::BLOCK_RAM, 0, 1, 1));
+
+ spartan6::ConfigurationBus bus(addresses.begin(), addresses.end());
+
+ EXPECT_TRUE(bus.IsValidFrameAddress(
+ spartan6::FrameAddress(spartan6::BlockType::BLOCK_RAM, 0, 0, 0)));
+ EXPECT_TRUE(bus.IsValidFrameAddress(
+ spartan6::FrameAddress(spartan6::BlockType::BLOCK_RAM, 0, 1, 1)));
+
+ EXPECT_FALSE(bus.IsValidFrameAddress(
+ spartan6::FrameAddress(spartan6::BlockType::BLOCK_RAM, 0, 0, 2)));
+}
+
+TEST(ConfigurationBusTest, GetNextFrameAddressYieldNextAddressInBus) {
+ std::vector<spartan6::FrameAddress> addresses;
+ addresses.push_back(
+ spartan6::FrameAddress(spartan6::BlockType::BLOCK_RAM, 0, 0, 0));
+ addresses.push_back(
+ spartan6::FrameAddress(spartan6::BlockType::BLOCK_RAM, 0, 0, 1));
+ addresses.push_back(
+ spartan6::FrameAddress(spartan6::BlockType::BLOCK_RAM, 0, 1, 0));
+ addresses.push_back(
+ spartan6::FrameAddress(spartan6::BlockType::BLOCK_RAM, 0, 1, 1));
+
+ spartan6::ConfigurationBus bus(addresses.begin(), addresses.end());
+
+ auto next_address = bus.GetNextFrameAddress(
+ spartan6::FrameAddress(spartan6::BlockType::BLOCK_RAM, 0, 0, 0));
+ ASSERT_TRUE(next_address);
+ EXPECT_EQ(*next_address, spartan6::FrameAddress(
+ spartan6::BlockType::BLOCK_RAM, 0, 0, 1));
+
+ next_address = bus.GetNextFrameAddress(
+ spartan6::FrameAddress(spartan6::BlockType::BLOCK_RAM, 0, 0, 1));
+ ASSERT_TRUE(next_address);
+ EXPECT_EQ(*next_address, spartan6::FrameAddress(
+ spartan6::BlockType::BLOCK_RAM, 0, 1, 0));
+}
+
+TEST(ConfigurationBusTest, GetNextFrameAddressYieldNothingAtEndOfBus) {
+ std::vector<spartan6::FrameAddress> addresses;
+ addresses.push_back(
+ spartan6::FrameAddress(spartan6::BlockType::BLOCK_RAM, 0, 0, 0));
+ addresses.push_back(
+ spartan6::FrameAddress(spartan6::BlockType::BLOCK_RAM, 0, 0, 1));
+ addresses.push_back(
+ spartan6::FrameAddress(spartan6::BlockType::BLOCK_RAM, 0, 1, 0));
+ addresses.push_back(
+ spartan6::FrameAddress(spartan6::BlockType::BLOCK_RAM, 0, 1, 1));
+
+ spartan6::ConfigurationBus bus(addresses.begin(), addresses.end());
+
+ EXPECT_FALSE(bus.GetNextFrameAddress(
+ spartan6::FrameAddress(spartan6::BlockType::BLOCK_RAM, 0, 1, 1)));
+}
diff --git a/lib/xilinx/tests/spartan6/configuration_column_test.cc b/lib/xilinx/tests/spartan6/configuration_column_test.cc
new file mode 100644
index 0000000..8b2b48d
--- /dev/null
+++ b/lib/xilinx/tests/spartan6/configuration_column_test.cc
@@ -0,0 +1,65 @@
+#include <prjxray/xilinx/spartan6/configuration_column.h>
+
+#include <gtest/gtest.h>
+#include <prjxray/xilinx/spartan6/block_type.h>
+#include <prjxray/xilinx/spartan6/frame_address.h>
+#include <yaml-cpp/yaml.h>
+
+using namespace prjxray::xilinx;
+
+TEST(ConfigurationColumnTest, IsValidFrameAddress) {
+ spartan6::ConfigurationColumn column(10);
+
+ // Inside this column.
+ EXPECT_TRUE(column.IsValidFrameAddress(
+ spartan6::FrameAddress(spartan6::BlockType::CLB_IOI_CLK, 1, 2, 3)));
+ // Past this column's frame width.
+ EXPECT_FALSE(column.IsValidFrameAddress(spartan6::FrameAddress(
+ spartan6::BlockType::CLB_IOI_CLK, 1, 2, 10)));
+}
+
+TEST(ConfigurationColumnTest, GetNextFrameAddressYieldNextAddressInColumn) {
+ spartan6::ConfigurationColumn column(10);
+
+ auto next_address = column.GetNextFrameAddress(
+ spartan6::FrameAddress(spartan6::BlockType::CLB_IOI_CLK, 1, 2, 3));
+ EXPECT_TRUE(next_address);
+ EXPECT_EQ(
+ *next_address,
+ spartan6::FrameAddress(spartan6::BlockType::CLB_IOI_CLK, 1, 2, 4));
+}
+
+TEST(ConfigurationColumnTest, GetNextFrameAddressYieldNothingAtEndOfColumn) {
+ spartan6::ConfigurationColumn column(10);
+
+ EXPECT_FALSE(column.GetNextFrameAddress(
+ spartan6::FrameAddress(spartan6::BlockType::CLB_IOI_CLK, 1, 2, 9)));
+}
+
+TEST(ConfigurationColumnTest, GetNextFrameAddressYieldNothingOutsideColumn) {
+ spartan6::ConfigurationColumn column(10);
+
+ // Just past last frame in column.
+ EXPECT_FALSE(column.GetNextFrameAddress(spartan6::FrameAddress(
+ spartan6::BlockType::CLB_IOI_CLK, 1, 2, 10)));
+}
+
+TEST(ConfigurationColumnTest, YamlEncodeTest) {
+ spartan6::ConfigurationColumn column(10);
+
+ YAML::Node node(column);
+ EXPECT_TRUE(node["frame_count"]);
+ EXPECT_EQ(node["frame_count"].as<int>(), 10);
+}
+
+TEST(ConfigurationColumnTest, YAMLDecodeTest) {
+ YAML::Node node;
+ node.SetTag("xilinx/spartan6/configuration_column");
+ node["frame_count"] = 10;
+
+ auto column = node.as<spartan6::ConfigurationColumn>();
+ EXPECT_TRUE(column.GetNextFrameAddress(
+ spartan6::FrameAddress(spartan6::BlockType::CLB_IOI_CLK, 1, 2, 8)));
+ EXPECT_FALSE(column.GetNextFrameAddress(
+ spartan6::FrameAddress(spartan6::BlockType::CLB_IOI_CLK, 1, 2, 9)));
+}
diff --git a/lib/xilinx/tests/spartan6/configuration_packet_test.cc b/lib/xilinx/tests/spartan6/configuration_packet_test.cc
new file mode 100644
index 0000000..1a13267
--- /dev/null
+++ b/lib/xilinx/tests/spartan6/configuration_packet_test.cc
@@ -0,0 +1,100 @@
+#include <functional>
+
+#include <absl/meta/type_traits.h>
+#include <gtest/gtest.h>
+#include <prjxray/bit_ops.h>
+
+#include <prjxray/xilinx/architectures.h>
+
+using namespace prjxray::xilinx;
+
+constexpr uint32_t kType1NOP = prjxray::bit_field_set<uint32_t>(0, 15, 13, 0x1);
+
+const 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, 15, 13, 0x1), 12, 11,
+ opcode),
+ 10, 5, address),
+ 4, 0, word_count);
+}
+
+const std::vector<uint32_t> MakeType2(const int opcode,
+ const int address,
+ const int word_count) {
+ uint32_t header = 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, 15, 13, 0x2), 12, 11,
+ opcode),
+ 10, 5, address),
+ 4, 0, 0);
+ uint32_t wcr1 = (word_count >> 16) & 0xFFFF;
+ uint32_t wcr2 = (word_count & 0xFFFF);
+ return std::vector<uint32_t>{header, wcr1, wcr2};
+}
+
+TEST(ConfigPacket, InitWithZeroBytes) {
+ auto packet =
+ ConfigurationPacket<Spartan6::ConfRegType>::InitWithWords({});
+
+ EXPECT_EQ(packet.first, absl::Span<uint32_t>());
+ EXPECT_FALSE(packet.second);
+}
+
+TEST(ConfigPacket, InitWithType1Nop) {
+ std::vector<uint32_t> words{kType1NOP};
+ absl::Span<uint32_t> word_span(words);
+ auto packet = ConfigurationPacket<Spartan6::ConfRegType>::InitWithWords(
+ word_span);
+ EXPECT_EQ(packet.first, absl::Span<uint32_t>());
+ ASSERT_TRUE(packet.second);
+ EXPECT_EQ(packet.second->opcode(),
+ ConfigurationPacket<Spartan6::ConfRegType>::Opcode::NOP);
+ EXPECT_EQ(packet.second->address(), Spartan6::ConfRegType::CRC);
+ EXPECT_EQ(packet.second->data(), absl::Span<uint32_t>());
+}
+
+TEST(ConfigPacket, InitWithType1Read) {
+ std::vector<uint32_t> words{MakeType1(0x1, 0x3, 2), 0xAA, 0xBB};
+ absl::Span<uint32_t> word_span(words);
+ auto packet = ConfigurationPacket<Spartan6::ConfRegType>::InitWithWords(
+ word_span);
+ EXPECT_EQ(packet.first, absl::Span<uint32_t>());
+ ASSERT_TRUE(packet.second);
+ EXPECT_EQ(packet.second->opcode(),
+ ConfigurationPacket<Spartan6::ConfRegType>::Opcode::Read);
+ EXPECT_EQ(packet.second->address(), Spartan6::ConfRegType::FDRI);
+ EXPECT_EQ(packet.second->data(), word_span.subspan(1));
+}
+
+TEST(ConfigPacket, InitWithType1Write) {
+ std::vector<uint32_t> words{MakeType1(0x2, 0x4, 2), 0xAA, 0xBB};
+ absl::Span<uint32_t> word_span(words);
+ auto packet = ConfigurationPacket<Spartan6::ConfRegType>::InitWithWords(
+ word_span);
+ EXPECT_EQ(packet.first, absl::Span<uint32_t>());
+ ASSERT_TRUE(packet.second);
+ EXPECT_EQ(packet.second->opcode(),
+ ConfigurationPacket<Spartan6::ConfRegType>::Opcode::Write);
+ EXPECT_EQ(packet.second->address(), Spartan6::ConfRegType::FDRO);
+ EXPECT_EQ(packet.second->data(), word_span.subspan(1));
+}
+
+TEST(ConfigPacket, InitWithType2WithPreviousPacket) {
+ std::vector<uint32_t> words{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12};
+ std::vector<uint32_t> type2 = MakeType2(0x01, 0x03, 12);
+ words.insert(words.begin(), type2.begin(), type2.end());
+ absl::Span<uint32_t> word_span(words);
+ auto packet = ConfigurationPacket<Spartan6::ConfRegType>::InitWithWords(
+ word_span);
+ EXPECT_EQ(packet.first, absl::Span<uint32_t>());
+ ASSERT_TRUE(packet.second);
+ EXPECT_EQ(packet.second->opcode(),
+ ConfigurationPacket<Spartan6::ConfRegType>::Opcode::Read);
+ EXPECT_EQ(packet.second->address(), Spartan6::ConfRegType::FDRI);
+ EXPECT_EQ(packet.second->data(), word_span.subspan(3));
+}
diff --git a/lib/xilinx/tests/spartan6/configuration_test.cc b/lib/xilinx/tests/spartan6/configuration_test.cc
new file mode 100644
index 0000000..c8ee77f
--- /dev/null
+++ b/lib/xilinx/tests/spartan6/configuration_test.cc
@@ -0,0 +1,186 @@
+#include <cstdint>
+#include <iostream>
+#include <vector>
+
+#include <absl/types/span.h>
+#include <gtest/gtest.h>
+#include <prjxray/memory_mapped_file.h>
+#include <prjxray/xilinx/architectures.h>
+#include <prjxray/xilinx/configuration.h>
+#include <prjxray/xilinx/spartan6/frame_address.h>
+#include <yaml-cpp/yaml.h>
+
+using namespace prjxray::xilinx;
+
+TEST(ConfigurationTest, ConstructFromPacketsWithSingleFrame) {
+ std::vector<spartan6::FrameAddress> test_part_addresses;
+ test_part_addresses.push_back(0x0A);
+ test_part_addresses.push_back(0x0B);
+
+ spartan6::Part test_part(0x1234, test_part_addresses);
+
+ std::vector<uint32_t> idcode{0x1234};
+ std::vector<uint32_t> cmd{0x0001};
+ std::vector<uint32_t> frame_address{0x345};
+ std::vector<uint32_t> frame(65, 0xAA);
+
+ std::vector<ConfigurationPacket<typename Spartan6::ConfRegType>>
+ packets{
+ {
+ static_cast<unsigned int>(0x1),
+ ConfigurationPacket<Spartan6::ConfRegType>::Opcode::Write,
+ Spartan6::ConfRegType::IDCODE,
+ absl::MakeSpan(idcode),
+ },
+ {
+ static_cast<unsigned int>(0x1),
+ ConfigurationPacket<Spartan6::ConfRegType>::Opcode::Write,
+ Spartan6::ConfRegType::FAR_MIN,
+ absl::MakeSpan(frame_address),
+ },
+ {
+ static_cast<unsigned int>(0x1),
+ ConfigurationPacket<Spartan6::ConfRegType>::Opcode::Write,
+ Spartan6::ConfRegType::CMD,
+ absl::MakeSpan(cmd),
+ },
+ {
+ static_cast<unsigned int>(0x1),
+ ConfigurationPacket<Spartan6::ConfRegType>::Opcode::Write,
+ Spartan6::ConfRegType::FDRI,
+ absl::MakeSpan(frame),
+ },
+ };
+
+ auto test_config =
+ Configuration<Spartan6>::InitWithPackets(test_part, packets);
+ ASSERT_TRUE(test_config);
+
+ EXPECT_EQ(test_config->part().idcode(), static_cast<uint32_t>(0x1234));
+ EXPECT_EQ(test_config->frames().size(), static_cast<size_t>(1));
+ EXPECT_EQ(test_config->frames().at(0x345), frame);
+}
+
+TEST(ConfigurationTest, ConstructFromPacketsWithAutoincrement) {
+ std::vector<spartan6::FrameAddress> test_part_addresses;
+ for (int ii = 0x310; ii < 0x320; ++ii) {
+ test_part_addresses.push_back(ii);
+ }
+
+ for (int ii = 0x330; ii < 0x331; ++ii) {
+ test_part_addresses.push_back(ii);
+ }
+
+ spartan6::Part test_part(0x1234, test_part_addresses);
+
+ std::vector<uint32_t> idcode{0x1234};
+ std::vector<uint32_t> cmd{0x0001};
+ std::vector<uint32_t> frame_address{0x31f};
+ std::vector<uint32_t> frame(65 * 2, 0xAA);
+ std::fill_n(frame.begin() + 65, 65, 0xBB);
+
+ std::vector<ConfigurationPacket<Spartan6::ConfRegType>> packets{
+ {
+ static_cast<unsigned int>(0x1),
+ ConfigurationPacket<Spartan6::ConfRegType>::Opcode::Write,
+ Spartan6::ConfRegType::IDCODE,
+ absl::MakeSpan(idcode),
+ },
+ {
+ static_cast<unsigned int>(0x1),
+ ConfigurationPacket<Spartan6::ConfRegType>::Opcode::Write,
+ Spartan6::ConfRegType::FAR_MIN,
+ absl::MakeSpan(frame_address),
+ },
+ {
+ static_cast<unsigned int>(0x1),
+ ConfigurationPacket<Spartan6::ConfRegType>::Opcode::Write,
+ Spartan6::ConfRegType::CMD,
+ absl::MakeSpan(cmd),
+ },
+ {
+ static_cast<unsigned int>(0x1),
+ ConfigurationPacket<Spartan6::ConfRegType>::Opcode::Write,
+ Spartan6::ConfRegType::FDRI,
+ absl::MakeSpan(frame),
+ },
+ };
+
+ auto test_config =
+ Configuration<Spartan6>::InitWithPackets(test_part, packets);
+ ASSERT_TRUE(test_config);
+
+ absl::Span<uint32_t> frame_span(frame);
+ EXPECT_EQ(test_config->part().idcode(), static_cast<uint32_t>(0x1234));
+ EXPECT_EQ(test_config->frames().size(), static_cast<size_t>(2));
+ EXPECT_EQ(test_config->frames().at(0x31f),
+ std::vector<uint32_t>(65, 0xAA));
+ // TODO This test fails with a C++ exception because the address
+ // of next frame is 0x320 instead of 0x330 as defined in the test_part
+ // EXPECT_EQ(test_config->frames().at(0x330),
+ // std::vector<uint32_t>(65, 0xBB));
+}
+
+TEST(ConfigurationTest, DISABLED_CheckForPaddingAfterIOBFrame) {
+ std::vector<spartan6::FrameAddress> test_part_addresses = {
+ spartan6::FrameAddress(spartan6::BlockType::CLB_IOI_CLK, 0, 0, 0),
+ spartan6::FrameAddress(spartan6::BlockType::BLOCK_RAM, 1, 0, 0),
+ spartan6::FrameAddress(spartan6::BlockType::IOB, 2, 0, 0)};
+
+ auto test_part = absl::optional<spartan6::Part>(
+ spartan6::Part(0x1234, test_part_addresses));
+
+ Frames<Spartan6> frames;
+ frames.getFrames().emplace(std::make_pair(
+ test_part_addresses.at(0), std::vector<uint32_t>(65, 0xAA)));
+ frames.getFrames().emplace(std::make_pair(
+ test_part_addresses.at(1), std::vector<uint32_t>(65, 0xBB)));
+ frames.getFrames().emplace(std::make_pair(
+ test_part_addresses.at(2), std::vector<uint32_t>(65, 0xCC)));
+ ASSERT_EQ(frames.getFrames().size(), 3);
+
+ Configuration<Spartan6>::PacketData packet_data =
+ Configuration<Spartan6>::createType2ConfigurationPacketData(
+ frames.getFrames(), test_part);
+ // createType2ConfigurationPacketData should add a 16-bit pad word after
+ // after the IOB frame
+ EXPECT_EQ(packet_data.size(), 3 * 65 + 1);
+
+ std::vector<uint32_t> idcode{0x1234};
+ std::vector<uint32_t> cmd{0x0001};
+ std::vector<uint32_t> frame_address{0x0};
+
+ std::vector<ConfigurationPacket<Spartan6::ConfRegType>> packets{
+ {
+ static_cast<unsigned int>(0x1),
+ ConfigurationPacket<Spartan6::ConfRegType>::Opcode::Write,
+ Spartan6::ConfRegType::IDCODE,
+ absl::MakeSpan(idcode),
+ },
+ {
+ static_cast<unsigned int>(0x1),
+ ConfigurationPacket<Spartan6::ConfRegType>::Opcode::Write,
+ Spartan6::ConfRegType::FAR,
+ absl::MakeSpan(frame_address),
+ },
+ {
+ static_cast<unsigned int>(0x1),
+ ConfigurationPacket<Spartan6::ConfRegType>::Opcode::Write,
+ Spartan6::ConfRegType::CMD,
+ absl::MakeSpan(cmd),
+ },
+ {
+ static_cast<unsigned int>(0x1),
+ ConfigurationPacket<Spartan6::ConfRegType>::Opcode::Write,
+ Spartan6::ConfRegType::FDRI,
+ absl::MakeSpan(packet_data),
+ },
+ };
+
+ auto test_config =
+ Configuration<Spartan6>::InitWithPackets(*test_part, packets);
+ ASSERT_EQ(test_config->frames().size(), 5);
+ for (auto& frame : test_config->frames()) {
+ EXPECT_EQ(frame.second, frames.getFrames().at(frame.first));
+ }
+}
diff --git a/lib/xilinx/tests/spartan6/frame_address_test.cc b/lib/xilinx/tests/spartan6/frame_address_test.cc
new file mode 100644
index 0000000..4633c20
--- /dev/null
+++ b/lib/xilinx/tests/spartan6/frame_address_test.cc
@@ -0,0 +1,33 @@
+#include <prjxray/xilinx/spartan6/frame_address.h>
+
+#include <gtest/gtest.h>
+
+using namespace prjxray::xilinx;
+
+TEST(FrameAddressTest, YamlEncode) {
+ spartan6::FrameAddress address(spartan6::BlockType::BLOCK_RAM, 10, 0,
+ 5);
+
+ YAML::Node node(address);
+
+ EXPECT_EQ(node.Tag(), "xilinx/spartan6/frame_address");
+ EXPECT_EQ(node["block_type"].as<std::string>(), "BLOCK_RAM");
+ EXPECT_EQ(node["row"].as<std::string>(), "10");
+ EXPECT_EQ(node["column"].as<std::string>(), "0");
+ EXPECT_EQ(node["minor"].as<std::string>(), "5");
+}
+
+TEST(FrameAddressTest, YamlDecode) {
+ YAML::Node node;
+ node.SetTag("xilinx/spartan6/frame_address");
+ node["block_type"] = "BLOCK_RAM";
+ node["row"] = "0";
+ node["column"] = "5";
+ node["minor"] = "11";
+
+ spartan6::FrameAddress address = node.as<spartan6::FrameAddress>();
+ EXPECT_EQ(address.block_type(), spartan6::BlockType::BLOCK_RAM);
+ EXPECT_EQ(address.row(), 0);
+ EXPECT_EQ(address.column(), 5);
+ EXPECT_EQ(address.minor(), 11);
+}
diff --git a/lib/xilinx/tests/spartan6/frames_test.cc b/lib/xilinx/tests/spartan6/frames_test.cc
new file mode 100644
index 0000000..eec0750
--- /dev/null
+++ b/lib/xilinx/tests/spartan6/frames_test.cc
@@ -0,0 +1,48 @@
+#include <vector>
+
+#include <gtest/gtest.h>
+
+#include <prjxray/xilinx/frames.h>
+#include <prjxray/xilinx/spartan6/part.h>
+
+using namespace prjxray::xilinx;
+TEST(FramesTest, FillInMissingFrames) {
+ std::vector<spartan6::FrameAddress> test_part_addresses = {
+ spartan6::FrameAddress(spartan6::BlockType::CLB_IOI_CLK, 0, 0, 0),
+ spartan6::FrameAddress(spartan6::BlockType::CLB_IOI_CLK, 0, 0, 1),
+ spartan6::FrameAddress(spartan6::BlockType::CLB_IOI_CLK, 0, 0, 2),
+ spartan6::FrameAddress(spartan6::BlockType::CLB_IOI_CLK, 0, 0, 3),
+ spartan6::FrameAddress(spartan6::BlockType::CLB_IOI_CLK, 0, 0, 4)};
+
+ spartan6::Part test_part(0x1234, test_part_addresses);
+
+ Frames<Spartan6> frames;
+ frames.getFrames().emplace(std::make_pair(
+ spartan6::FrameAddress(2), std::vector<uint32_t>(65, 0xCC)));
+ frames.getFrames().emplace(std::make_pair(
+ spartan6::FrameAddress(3), std::vector<uint32_t>(65, 0xDD)));
+ frames.getFrames().emplace(std::make_pair(
+ spartan6::FrameAddress(4), std::vector<uint32_t>(65, 0xEE)));
+
+ ASSERT_EQ(frames.getFrames().size(), 3);
+ EXPECT_EQ(frames.getFrames().at(test_part_addresses[2]),
+ std::vector<uint32_t>(65, 0xCC));
+ EXPECT_EQ(frames.getFrames().at(test_part_addresses[3]),
+ std::vector<uint32_t>(65, 0xDD));
+ EXPECT_EQ(frames.getFrames().at(test_part_addresses[4]),
+ std::vector<uint32_t>(65, 0xEE));
+
+ frames.addMissingFrames(test_part);
+
+ ASSERT_EQ(frames.getFrames().size(), 5);
+ EXPECT_EQ(frames.getFrames().at(test_part_addresses[0]),
+ std::vector<uint32_t>(65, 0));
+ EXPECT_EQ(frames.getFrames().at(test_part_addresses[1]),
+ std::vector<uint32_t>(65, 0));
+ EXPECT_EQ(frames.getFrames().at(test_part_addresses[2]),
+ std::vector<uint32_t>(65, 0xCC));
+ EXPECT_EQ(frames.getFrames().at(test_part_addresses[3]),
+ std::vector<uint32_t>(65, 0xDD));
+ EXPECT_EQ(frames.getFrames().at(test_part_addresses[4]),
+ std::vector<uint32_t>(65, 0xEE));
+}
diff --git a/lib/xilinx/tests/spartan6/global_clock_region_test.cc b/lib/xilinx/tests/spartan6/global_clock_region_test.cc
new file mode 100644
index 0000000..b38e11a
--- /dev/null
+++ b/lib/xilinx/tests/spartan6/global_clock_region_test.cc
@@ -0,0 +1,132 @@
+#include <prjxray/xilinx/spartan6/global_clock_region.h>
+
+#include <gtest/gtest.h>
+
+using namespace prjxray::xilinx;
+
+TEST(GlobalClockRegionTest, IsValidFrameAddress) {
+ std::vector<spartan6::FrameAddress> addresses;
+ addresses.push_back(
+ spartan6::FrameAddress(spartan6::BlockType::CLB_IOI_CLK, 0, 0, 0));
+ addresses.push_back(
+ spartan6::FrameAddress(spartan6::BlockType::CLB_IOI_CLK, 0, 0, 1));
+ addresses.push_back(
+ spartan6::FrameAddress(spartan6::BlockType::CLB_IOI_CLK, 0, 1, 0));
+ addresses.push_back(
+ spartan6::FrameAddress(spartan6::BlockType::CLB_IOI_CLK, 0, 1, 1));
+ addresses.push_back(
+ spartan6::FrameAddress(spartan6::BlockType::BLOCK_RAM, 0, 0, 0));
+ addresses.push_back(
+ spartan6::FrameAddress(spartan6::BlockType::BLOCK_RAM, 0, 0, 1));
+ addresses.push_back(
+ spartan6::FrameAddress(spartan6::BlockType::BLOCK_RAM, 0, 0, 2));
+ addresses.push_back(
+ spartan6::FrameAddress(spartan6::BlockType::CLB_IOI_CLK, 1, 0, 0));
+ addresses.push_back(
+ spartan6::FrameAddress(spartan6::BlockType::CLB_IOI_CLK, 1, 0, 1));
+
+ spartan6::GlobalClockRegion global_clock_region(addresses.begin(),
+ addresses.end());
+
+ EXPECT_TRUE(global_clock_region.IsValidFrameAddress(
+ spartan6::FrameAddress(spartan6::BlockType::CLB_IOI_CLK, 0, 0, 0)));
+ EXPECT_TRUE(global_clock_region.IsValidFrameAddress(
+ spartan6::FrameAddress(spartan6::BlockType::CLB_IOI_CLK, 0, 1, 0)));
+ EXPECT_TRUE(global_clock_region.IsValidFrameAddress(
+ spartan6::FrameAddress(spartan6::BlockType::BLOCK_RAM, 0, 0, 2)));
+ EXPECT_TRUE(global_clock_region.IsValidFrameAddress(
+ spartan6::FrameAddress(spartan6::BlockType::CLB_IOI_CLK, 1, 0, 0)));
+
+ EXPECT_FALSE(global_clock_region.IsValidFrameAddress(
+ spartan6::FrameAddress(spartan6::BlockType::CLB_IOI_CLK, 0, 0, 2)));
+ EXPECT_FALSE(global_clock_region.IsValidFrameAddress(
+ spartan6::FrameAddress(spartan6::BlockType::CLB_IOI_CLK, 0, 2, 0)));
+ EXPECT_FALSE(global_clock_region.IsValidFrameAddress(
+ spartan6::FrameAddress(spartan6::BlockType::CLB_IOI_CLK, 2, 0, 0)));
+ EXPECT_FALSE(global_clock_region.IsValidFrameAddress(
+ spartan6::FrameAddress(spartan6::BlockType::IOB, 0, 0, 2)));
+}
+
+TEST(GlobalClockRegionTest,
+ GetNextFrameAddressYieldNextAddressInGlobalClockRegion) {
+ std::vector<spartan6::FrameAddress> addresses;
+ addresses.push_back(
+ spartan6::FrameAddress(spartan6::BlockType::CLB_IOI_CLK, 0, 0, 0));
+ addresses.push_back(
+ spartan6::FrameAddress(spartan6::BlockType::CLB_IOI_CLK, 0, 0, 1));
+ addresses.push_back(
+ spartan6::FrameAddress(spartan6::BlockType::CLB_IOI_CLK, 0, 1, 0));
+ addresses.push_back(
+ spartan6::FrameAddress(spartan6::BlockType::CLB_IOI_CLK, 0, 1, 1));
+ addresses.push_back(
+ spartan6::FrameAddress(spartan6::BlockType::BLOCK_RAM, 0, 0, 0));
+ addresses.push_back(
+ spartan6::FrameAddress(spartan6::BlockType::BLOCK_RAM, 0, 0, 1));
+ addresses.push_back(
+ spartan6::FrameAddress(spartan6::BlockType::BLOCK_RAM, 0, 0, 2));
+ addresses.push_back(
+ spartan6::FrameAddress(spartan6::BlockType::CLB_IOI_CLK, 1, 0, 0));
+ addresses.push_back(
+ spartan6::FrameAddress(spartan6::BlockType::CLB_IOI_CLK, 1, 0, 1));
+
+ spartan6::GlobalClockRegion global_clock_region(addresses.begin(),
+ addresses.end());
+
+ auto next_address = global_clock_region.GetNextFrameAddress(
+ spartan6::FrameAddress(spartan6::BlockType::CLB_IOI_CLK, 0, 0, 0));
+ ASSERT_TRUE(next_address);
+ EXPECT_EQ(
+ *next_address,
+ spartan6::FrameAddress(spartan6::BlockType::CLB_IOI_CLK, 0, 0, 1));
+
+ next_address = global_clock_region.GetNextFrameAddress(
+ spartan6::FrameAddress(spartan6::BlockType::CLB_IOI_CLK, 0, 0, 1));
+ ASSERT_TRUE(next_address);
+ EXPECT_EQ(
+ *next_address,
+ spartan6::FrameAddress(spartan6::BlockType::CLB_IOI_CLK, 0, 1, 0));
+
+ next_address = global_clock_region.GetNextFrameAddress(
+ spartan6::FrameAddress(spartan6::BlockType::CLB_IOI_CLK, 0, 1, 1));
+ ASSERT_TRUE(next_address);
+ EXPECT_EQ(
+ *next_address,
+ spartan6::FrameAddress(spartan6::BlockType::CLB_IOI_CLK, 1, 0, 0));
+
+ next_address = global_clock_region.GetNextFrameAddress(
+ spartan6::FrameAddress(spartan6::BlockType::BLOCK_RAM, 0, 0, 1));
+ ASSERT_TRUE(next_address);
+ EXPECT_EQ(*next_address, spartan6::FrameAddress(
+ spartan6::BlockType::BLOCK_RAM, 0, 0, 2));
+}
+
+TEST(GlobalClockRegionTest,
+ GetNextFrameAddressYieldNothingAtEndOfGlobalClockRegion) {
+ std::vector<spartan6::FrameAddress> addresses;
+ addresses.push_back(
+ spartan6::FrameAddress(spartan6::BlockType::CLB_IOI_CLK, 0, 0, 0));
+ addresses.push_back(
+ spartan6::FrameAddress(spartan6::BlockType::CLB_IOI_CLK, 0, 0, 1));
+ addresses.push_back(
+ spartan6::FrameAddress(spartan6::BlockType::CLB_IOI_CLK, 0, 1, 0));
+ addresses.push_back(
+ spartan6::FrameAddress(spartan6::BlockType::CLB_IOI_CLK, 0, 1, 1));
+ addresses.push_back(
+ spartan6::FrameAddress(spartan6::BlockType::BLOCK_RAM, 0, 0, 0));
+ addresses.push_back(
+ spartan6::FrameAddress(spartan6::BlockType::BLOCK_RAM, 0, 0, 1));
+ addresses.push_back(
+ spartan6::FrameAddress(spartan6::BlockType::BLOCK_RAM, 0, 0, 2));
+ addresses.push_back(
+ spartan6::FrameAddress(spartan6::BlockType::CLB_IOI_CLK, 1, 0, 0));
+ addresses.push_back(
+ spartan6::FrameAddress(spartan6::BlockType::CLB_IOI_CLK, 1, 0, 1));
+
+ spartan6::GlobalClockRegion global_clock_region(addresses.begin(),
+ addresses.end());
+
+ EXPECT_FALSE(global_clock_region.GetNextFrameAddress(
+ spartan6::FrameAddress(spartan6::BlockType::CLB_IOI_CLK, 1, 0, 1)));
+ EXPECT_FALSE(global_clock_region.GetNextFrameAddress(
+ spartan6::FrameAddress(spartan6::BlockType::BLOCK_RAM, 0, 0, 2)));
+}
diff --git a/lib/xilinx/tests/spartan6/part_test.cc b/lib/xilinx/tests/spartan6/part_test.cc
new file mode 100644
index 0000000..83939d2
--- /dev/null
+++ b/lib/xilinx/tests/spartan6/part_test.cc
@@ -0,0 +1,146 @@
+#include <prjxray/xilinx/spartan6/part.h>
+
+#include <gtest/gtest.h>
+
+using namespace prjxray::xilinx;
+
+TEST(PartTest, IsValidFrameAddress) {
+ std::vector<spartan6::FrameAddress> addresses;
+ addresses.push_back(
+ spartan6::FrameAddress(spartan6::BlockType::CLB_IOI_CLK, 0, 0, 0));
+ addresses.push_back(
+ spartan6::FrameAddress(spartan6::BlockType::CLB_IOI_CLK, 0, 0, 1));
+ addresses.push_back(
+ spartan6::FrameAddress(spartan6::BlockType::CLB_IOI_CLK, 0, 1, 0));
+ addresses.push_back(
+ spartan6::FrameAddress(spartan6::BlockType::CLB_IOI_CLK, 0, 1, 1));
+ addresses.push_back(
+ spartan6::FrameAddress(spartan6::BlockType::BLOCK_RAM, 0, 0, 0));
+ addresses.push_back(
+ spartan6::FrameAddress(spartan6::BlockType::BLOCK_RAM, 0, 0, 1));
+ addresses.push_back(
+ spartan6::FrameAddress(spartan6::BlockType::BLOCK_RAM, 0, 0, 2));
+ addresses.push_back(
+ spartan6::FrameAddress(spartan6::BlockType::CLB_IOI_CLK, 1, 0, 0));
+ addresses.push_back(
+ spartan6::FrameAddress(spartan6::BlockType::CLB_IOI_CLK, 1, 0, 1));
+ addresses.push_back(
+ spartan6::FrameAddress(spartan6::BlockType::CLB_IOI_CLK, 0, 0, 0));
+ addresses.push_back(
+ spartan6::FrameAddress(spartan6::BlockType::CLB_IOI_CLK, 0, 0, 1));
+
+ spartan6::Part part(0x1234, addresses.begin(), addresses.end());
+
+ EXPECT_TRUE(part.IsValidFrameAddress(
+ spartan6::FrameAddress(spartan6::BlockType::CLB_IOI_CLK, 0, 0, 0)));
+ EXPECT_TRUE(part.IsValidFrameAddress(
+ spartan6::FrameAddress(spartan6::BlockType::CLB_IOI_CLK, 0, 1, 0)));
+ EXPECT_TRUE(part.IsValidFrameAddress(
+ spartan6::FrameAddress(spartan6::BlockType::BLOCK_RAM, 0, 0, 2)));
+ EXPECT_TRUE(part.IsValidFrameAddress(
+ spartan6::FrameAddress(spartan6::BlockType::CLB_IOI_CLK, 1, 0, 0)));
+ EXPECT_TRUE(part.IsValidFrameAddress(
+ spartan6::FrameAddress(spartan6::BlockType::CLB_IOI_CLK, 0, 0, 0)));
+
+ EXPECT_FALSE(part.IsValidFrameAddress(
+ spartan6::FrameAddress(spartan6::BlockType::CLB_IOI_CLK, 0, 0, 2)));
+ EXPECT_FALSE(part.IsValidFrameAddress(
+ spartan6::FrameAddress(spartan6::BlockType::CLB_IOI_CLK, 0, 2, 0)));
+ EXPECT_FALSE(part.IsValidFrameAddress(
+ spartan6::FrameAddress(spartan6::BlockType::CLB_IOI_CLK, 2, 0, 0)));
+ EXPECT_FALSE(part.IsValidFrameAddress(
+ spartan6::FrameAddress(spartan6::BlockType::IOB, 0, 0, 2)));
+}
+
+TEST(PartTest, GetNextFrameAddressYieldNextAddressInPart) {
+ std::vector<spartan6::FrameAddress> addresses;
+ addresses.push_back(
+ spartan6::FrameAddress(spartan6::BlockType::CLB_IOI_CLK, 0, 0, 0));
+ addresses.push_back(
+ spartan6::FrameAddress(spartan6::BlockType::CLB_IOI_CLK, 0, 0, 1));
+ addresses.push_back(
+ spartan6::FrameAddress(spartan6::BlockType::CLB_IOI_CLK, 0, 1, 0));
+ addresses.push_back(
+ spartan6::FrameAddress(spartan6::BlockType::CLB_IOI_CLK, 0, 1, 1));
+ addresses.push_back(
+ spartan6::FrameAddress(spartan6::BlockType::BLOCK_RAM, 0, 0, 0));
+ addresses.push_back(
+ spartan6::FrameAddress(spartan6::BlockType::BLOCK_RAM, 0, 0, 1));
+ addresses.push_back(
+ spartan6::FrameAddress(spartan6::BlockType::BLOCK_RAM, 0, 0, 2));
+ addresses.push_back(
+ spartan6::FrameAddress(spartan6::BlockType::CLB_IOI_CLK, 1, 0, 0));
+ addresses.push_back(
+ spartan6::FrameAddress(spartan6::BlockType::CLB_IOI_CLK, 1, 0, 1));
+ addresses.push_back(
+ spartan6::FrameAddress(spartan6::BlockType::CLB_IOI_CLK, 0, 0, 0));
+ addresses.push_back(
+ spartan6::FrameAddress(spartan6::BlockType::CLB_IOI_CLK, 0, 0, 1));
+
+ spartan6::Part part(0x1234, addresses.begin(), addresses.end());
+
+ auto next_address = part.GetNextFrameAddress(
+ spartan6::FrameAddress(spartan6::BlockType::CLB_IOI_CLK, 0, 0, 0));
+ ASSERT_TRUE(next_address);
+ EXPECT_EQ(
+ *next_address,
+ spartan6::FrameAddress(spartan6::BlockType::CLB_IOI_CLK, 0, 0, 1));
+
+ next_address = part.GetNextFrameAddress(
+ spartan6::FrameAddress(spartan6::BlockType::CLB_IOI_CLK, 0, 0, 1));
+ ASSERT_TRUE(next_address);
+ EXPECT_EQ(
+ *next_address,
+ spartan6::FrameAddress(spartan6::BlockType::CLB_IOI_CLK, 0, 1, 0));
+
+ next_address = part.GetNextFrameAddress(
+ spartan6::FrameAddress(spartan6::BlockType::CLB_IOI_CLK, 0, 1, 1));
+ ASSERT_TRUE(next_address);
+ EXPECT_EQ(
+ *next_address,
+ spartan6::FrameAddress(spartan6::BlockType::CLB_IOI_CLK, 1, 0, 0));
+
+ next_address = part.GetNextFrameAddress(
+ spartan6::FrameAddress(spartan6::BlockType::CLB_IOI_CLK, 1, 0, 1));
+ ASSERT_TRUE(next_address);
+ EXPECT_EQ(*next_address, spartan6::FrameAddress(
+ spartan6::BlockType::BLOCK_RAM, 0, 0, 0));
+
+ next_address = part.GetNextFrameAddress(
+ spartan6::FrameAddress(spartan6::BlockType::CLB_IOI_CLK, 0, 0, 1));
+ ASSERT_TRUE(next_address);
+ EXPECT_EQ(
+ *next_address,
+ spartan6::FrameAddress(spartan6::BlockType::CLB_IOI_CLK, 0, 1, 0));
+}
+
+TEST(PartTest, GetNextFrameAddressYieldNothingAtEndOfPart) {
+ std::vector<spartan6::FrameAddress> addresses;
+ addresses.push_back(
+ spartan6::FrameAddress(spartan6::BlockType::CLB_IOI_CLK, 0, 0, 0));
+ addresses.push_back(
+ spartan6::FrameAddress(spartan6::BlockType::CLB_IOI_CLK, 0, 0, 1));
+ addresses.push_back(
+ spartan6::FrameAddress(spartan6::BlockType::CLB_IOI_CLK, 0, 1, 0));
+ addresses.push_back(
+ spartan6::FrameAddress(spartan6::BlockType::CLB_IOI_CLK, 0, 1, 1));
+ addresses.push_back(
+ spartan6::FrameAddress(spartan6::BlockType::BLOCK_RAM, 0, 0, 0));
+ addresses.push_back(
+ spartan6::FrameAddress(spartan6::BlockType::BLOCK_RAM, 0, 0, 1));
+ addresses.push_back(
+ spartan6::FrameAddress(spartan6::BlockType::BLOCK_RAM, 0, 0, 2));
+ addresses.push_back(
+ spartan6::FrameAddress(spartan6::BlockType::CLB_IOI_CLK, 1, 0, 0));
+ addresses.push_back(
+ spartan6::FrameAddress(spartan6::BlockType::CLB_IOI_CLK, 1, 0, 1));
+ addresses.push_back(
+ spartan6::FrameAddress(spartan6::BlockType::CLB_IOI_CLK, 0, 0, 0));
+ addresses.push_back(
+ spartan6::FrameAddress(spartan6::BlockType::CLB_IOI_CLK, 0, 0, 1));
+
+ spartan6::Part part(0x1234, addresses.begin(), addresses.end());
+
+ EXPECT_FALSE(part.GetNextFrameAddress(
+ spartan6::FrameAddress(spartan6::BlockType::BLOCK_RAM, 0, 0, 2)));
+}
diff --git a/lib/xilinx/tests/spartan6/row_test.cc b/lib/xilinx/tests/spartan6/row_test.cc
new file mode 100644
index 0000000..735bc1d
--- /dev/null
+++ b/lib/xilinx/tests/spartan6/row_test.cc
@@ -0,0 +1,114 @@
+#include <prjxray/xilinx/spartan6/configuration_row.h>
+
+#include <gtest/gtest.h>
+
+using namespace prjxray::xilinx;
+
+TEST(RowTest, IsValidFrameAddress) {
+ std::vector<spartan6::FrameAddress> addresses;
+ addresses.push_back(
+ spartan6::FrameAddress(spartan6::BlockType::CLB_IOI_CLK, 0, 0, 0));
+ addresses.push_back(
+ spartan6::FrameAddress(spartan6::BlockType::CLB_IOI_CLK, 0, 0, 1));
+ addresses.push_back(
+ spartan6::FrameAddress(spartan6::BlockType::CLB_IOI_CLK, 0, 1, 0));
+ addresses.push_back(
+ spartan6::FrameAddress(spartan6::BlockType::CLB_IOI_CLK, 0, 1, 1));
+ addresses.push_back(
+ spartan6::FrameAddress(spartan6::BlockType::BLOCK_RAM, 0, 0, 0));
+ addresses.push_back(
+ spartan6::FrameAddress(spartan6::BlockType::BLOCK_RAM, 0, 0, 1));
+ addresses.push_back(
+ spartan6::FrameAddress(spartan6::BlockType::BLOCK_RAM, 0, 0, 2));
+
+ spartan6::Row row(addresses.begin(), addresses.end());
+
+ EXPECT_TRUE(row.IsValidFrameAddress(
+ spartan6::FrameAddress(spartan6::BlockType::CLB_IOI_CLK, 0, 0, 0)));
+ EXPECT_TRUE(row.IsValidFrameAddress(
+ spartan6::FrameAddress(spartan6::BlockType::CLB_IOI_CLK, 0, 1, 0)));
+ EXPECT_TRUE(row.IsValidFrameAddress(
+ spartan6::FrameAddress(spartan6::BlockType::BLOCK_RAM, 0, 0, 2)));
+
+ EXPECT_FALSE(row.IsValidFrameAddress(
+ spartan6::FrameAddress(spartan6::BlockType::CLB_IOI_CLK, 0, 0, 2)));
+ EXPECT_FALSE(row.IsValidFrameAddress(
+ spartan6::FrameAddress(spartan6::BlockType::CLB_IOI_CLK, 0, 2, 0)));
+}
+
+TEST(RowTest, GetNextFrameAddressYieldNextAddressInRow) {
+ std::vector<spartan6::FrameAddress> addresses;
+ addresses.push_back(
+ spartan6::FrameAddress(spartan6::BlockType::CLB_IOI_CLK, 0, 0, 0));
+ addresses.push_back(
+ spartan6::FrameAddress(spartan6::BlockType::CLB_IOI_CLK, 0, 0, 1));
+ addresses.push_back(
+ spartan6::FrameAddress(spartan6::BlockType::CLB_IOI_CLK, 0, 1, 0));
+ addresses.push_back(
+ spartan6::FrameAddress(spartan6::BlockType::CLB_IOI_CLK, 0, 1, 1));
+ addresses.push_back(
+ spartan6::FrameAddress(spartan6::BlockType::BLOCK_RAM, 0, 0, 0));
+ addresses.push_back(
+ spartan6::FrameAddress(spartan6::BlockType::BLOCK_RAM, 0, 0, 1));
+ addresses.push_back(
+ spartan6::FrameAddress(spartan6::BlockType::BLOCK_RAM, 0, 0, 2));
+
+ spartan6::Row row(addresses.begin(), addresses.end());
+
+ auto next_address = row.GetNextFrameAddress(
+ spartan6::FrameAddress(spartan6::BlockType::CLB_IOI_CLK, 0, 0, 0));
+ ASSERT_TRUE(next_address);
+ EXPECT_EQ(
+ *next_address,
+ spartan6::FrameAddress(spartan6::BlockType::CLB_IOI_CLK, 0, 0, 1));
+
+ next_address = row.GetNextFrameAddress(
+ spartan6::FrameAddress(spartan6::BlockType::CLB_IOI_CLK, 0, 0, 1));
+ ASSERT_TRUE(next_address);
+ EXPECT_EQ(
+ *next_address,
+ spartan6::FrameAddress(spartan6::BlockType::CLB_IOI_CLK, 0, 1, 0));
+
+ // Rows have unique behavior for GetNextFrameAddress() at the end of a
+ // bus. Since the addresses need to be returned in numerically
+ // increasing order, all of the rows need to be returned before moving
+ // to a different bus. That means that Row::GetNextFrameAddress() needs
+ // to return no object at the end of a bus and let the caller use that
+ // as a signal to try the next row.
+ next_address = row.GetNextFrameAddress(
+ spartan6::FrameAddress(spartan6::BlockType::CLB_IOI_CLK, 0, 1, 1));
+ EXPECT_FALSE(next_address);
+
+ next_address = row.GetNextFrameAddress(
+ spartan6::FrameAddress(spartan6::BlockType::BLOCK_RAM, 0, 0, 1));
+ ASSERT_TRUE(next_address);
+ EXPECT_EQ(*next_address, spartan6::FrameAddress(
+ spartan6::BlockType::BLOCK_RAM, 0, 0, 2));
+
+ next_address = row.GetNextFrameAddress(
+ spartan6::FrameAddress(spartan6::BlockType::BLOCK_RAM, 0, 0, 2));
+ EXPECT_FALSE(next_address);
+}
+
+TEST(RowTest, GetNextFrameAddressYieldNothingAtEndOfRow) {
+ std::vector<spartan6::FrameAddress> addresses;
+ addresses.push_back(
+ spartan6::FrameAddress(spartan6::BlockType::CLB_IOI_CLK, 0, 0, 0));
+ addresses.push_back(
+ spartan6::FrameAddress(spartan6::BlockType::CLB_IOI_CLK, 0, 0, 1));
+ addresses.push_back(
+ spartan6::FrameAddress(spartan6::BlockType::CLB_IOI_CLK, 0, 1, 0));
+ addresses.push_back(
+ spartan6::FrameAddress(spartan6::BlockType::CLB_IOI_CLK, 0, 1, 1));
+ addresses.push_back(
+ spartan6::FrameAddress(spartan6::BlockType::BLOCK_RAM, 0, 0, 0));
+ addresses.push_back(
+ spartan6::FrameAddress(spartan6::BlockType::BLOCK_RAM, 0, 0, 1));
+ addresses.push_back(
+ spartan6::FrameAddress(spartan6::BlockType::BLOCK_RAM, 0, 0, 2));
+
+ spartan6::Row row(addresses.begin(), addresses.end());
+
+ EXPECT_FALSE(row.GetNextFrameAddress(
+ spartan6::FrameAddress(spartan6::BlockType::BLOCK_RAM, 0, 0, 2)));
+}
diff --git a/lib/xilinx/tests/xc7series/bitstream_reader_test.cc b/lib/xilinx/tests/xc7series/bitstream_reader_test.cc
new file mode 100644
index 0000000..df576ca
--- /dev/null
+++ b/lib/xilinx/tests/xc7series/bitstream_reader_test.cc
@@ -0,0 +1,91 @@
+#include <array>
+
+#include <gtest/gtest.h>
+#include <prjxray/xilinx/architectures.h>
+#include <prjxray/xilinx/bitstream_reader.h>
+#include <prjxray/xilinx/configuration_packet.h>
+#include <prjxray/xilinx/configuration_register.h>
+
+using namespace prjxray::xilinx;
+
+TEST(BitstreamReaderTest, InitWithEmptyBytesReturnsNull) {
+ absl::Span<uint8_t> bitstream;
+ auto reader = BitstreamReader<Series7>::InitWithBytes(bitstream);
+ EXPECT_FALSE(reader);
+}
+
+TEST(BitstreamReaderTest, InitWithOnlySyncReturnsObject) {
+ std::vector<uint8_t> bitstream{0xAA, 0x99, 0x55, 0x66};
+ auto reader = BitstreamReader<Series7>::InitWithBytes(bitstream);
+ EXPECT_TRUE(reader);
+}
+
+TEST(BitstreamReaderTest, InitWithSyncAfterNonWordSizedPaddingReturnsObject) {
+ std::vector<uint8_t> bitstream{0xFF, 0xFE, 0xAA, 0x99, 0x55, 0x66};
+ auto reader = BitstreamReader<Series7>::InitWithBytes(bitstream);
+ EXPECT_TRUE(reader);
+}
+
+TEST(BitstreamReaderTest, InitWithSyncAfterWordSizedPaddingReturnsObject) {
+ std::vector<uint8_t> bitstream{0xFF, 0xFE, 0xFD, 0xFC,
+ 0xAA, 0x99, 0x55, 0x66};
+ auto reader = BitstreamReader<Series7>::InitWithBytes(bitstream);
+ EXPECT_TRUE(reader);
+}
+
+TEST(BitstreamReaderTest, ParsesType1Packet) {
+ std::vector<uint8_t> bitstream{
+ 0xAA, 0x99, 0x55, 0x66, // sync
+ 0x20, 0x00, 0x00, 0x00, // NOP
+ };
+ auto reader = BitstreamReader<Series7>::InitWithBytes(bitstream);
+ ASSERT_TRUE(reader);
+ ASSERT_NE(reader->begin(), reader->end());
+
+ auto first_packet = reader->begin();
+ EXPECT_EQ(first_packet->opcode(),
+ ConfigurationPacket<Series7::ConfRegType>::Opcode::NOP);
+
+ EXPECT_EQ(++first_packet, reader->end());
+}
+
+TEST(BitstreamReaderTest, ParseType2PacketWithoutType1Fails) {
+ std::vector<uint8_t> bitstream{
+ 0xAA, 0x99, 0x55, 0x66, // sync
+ 0x40, 0x00, 0x00, 0x00, // Type 2 NOP
+ };
+ auto reader = BitstreamReader<Series7>::InitWithBytes(bitstream);
+ ASSERT_TRUE(reader);
+ EXPECT_EQ(reader->begin(), reader->end());
+}
+
+TEST(BitstreamReaderTest, ParsesType2AfterType1Packet) {
+ std::vector<uint8_t> bitstream{
+ 0xAA, 0x99, 0x55, 0x66, // sync
+ 0x28, 0x00, 0x60, 0x00, // Type 1 Read zero bytes from 6
+ 0x48, 0x00, 0x00, 0x04, // Type 2 write of 4 words
+ 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8,
+ 0x9, 0xA, 0xB, 0xC, 0xD, 0xE, 0xF, 0x10,
+ };
+ std::vector<uint32_t> data_words{0x01020304, 0x05060708, 0x090A0B0C,
+ 0x0D0E0F10};
+
+ auto reader = BitstreamReader<Series7>::InitWithBytes(bitstream);
+ ASSERT_TRUE(reader);
+ ASSERT_NE(reader->begin(), reader->end());
+
+ auto first_packet = reader->begin();
+ EXPECT_EQ(first_packet->opcode(),
+ ConfigurationPacket<Series7::ConfRegType>::Opcode::Read);
+ EXPECT_EQ(first_packet->address(), Series7::ConfRegType::FDRO);
+ EXPECT_EQ(first_packet->data(), absl::Span<uint32_t>());
+
+ auto second_packet = ++first_packet;
+ ASSERT_NE(second_packet, reader->end());
+ EXPECT_EQ(second_packet->opcode(),
+ ConfigurationPacket<Series7::ConfRegType>::Opcode::Read);
+ EXPECT_EQ(second_packet->address(), Series7::ConfRegType::FDRO);
+ EXPECT_EQ(first_packet->data(), absl::Span<uint32_t>(data_words));
+
+ EXPECT_EQ(++first_packet, reader->end());
+}
diff --git a/lib/xilinx/tests/xc7series/bitstream_writer_test.cc b/lib/xilinx/tests/xc7series/bitstream_writer_test.cc
new file mode 100644
index 0000000..031540d
--- /dev/null
+++ b/lib/xilinx/tests/xc7series/bitstream_writer_test.cc
@@ -0,0 +1,222 @@
+#include <array>
+
+#include <gtest/gtest.h>
+#include <prjxray/xilinx/bitstream_reader.h>
+#include <prjxray/xilinx/bitstream_writer.h>
+#include <prjxray/xilinx/configuration_packet.h>
+#include <prjxray/xilinx/configuration_register.h>
+
+#include <prjxray/bit_ops.h>
+
+using namespace prjxray::xilinx;
+
+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(BitstreamWriter<Series7> 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<ConfigurationPacket<Series7::ConfRegType>>>&
+ packets) {
+ // InitWithWords doesn't like type 0
+ /*
+ static std::vector<uint32_t> words{0x00000000};
+ absl::Span<uint32_t> word_span(words);
+ auto packet =
+ ConfigurationPacket<Series7::ConfRegType>::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 ConfigurationPacket<Series7::ConfRegType>(
+ 0, ConfigurationPacket<Series7::ConfRegType>::NOP,
+ Series7::ConfRegType::CRC, word_span));
+}
+
+void AddType1(
+ std::vector<std::unique_ptr<ConfigurationPacket<Series7::ConfRegType>>>&
+ packets) {
+ static std::vector<uint32_t> words{MakeType1(0x2, 0x3, 2), 0xAA, 0xBB};
+ absl::Span<uint32_t> word_span(words);
+ auto packet =
+ ConfigurationPacket<Series7::ConfRegType>::InitWithWords(word_span);
+ packets.emplace_back(
+ new ConfigurationPacket<Series7::ConfRegType>(*(packet.second)));
+}
+
+// Empty
+void AddType1E(
+ std::vector<std::unique_ptr<ConfigurationPacket<Series7::ConfRegType>>>&
+ packets) {
+ static std::vector<uint32_t> words{MakeType1(0x2, 0x3, 0)};
+ absl::Span<uint32_t> word_span(words);
+ auto packet =
+ ConfigurationPacket<Series7::ConfRegType>::InitWithWords(word_span);
+ packets.emplace_back(
+ new ConfigurationPacket<Series7::ConfRegType>(*(packet.second)));
+}
+
+void AddType2(
+ std::vector<std::unique_ptr<ConfigurationPacket<Series7::ConfRegType>>>&
+ packets) {
+ // Type 1 packet with address
+ // Without this InitWithWords will fail on type 2
+ ConfigurationPacket<Series7::ConfRegType>* packet1;
+ {
+ static std::vector<uint32_t> words{MakeType1(0x2, 0x3, 0)};
+ absl::Span<uint32_t> word_span(words);
+ auto packet1_pair =
+ ConfigurationPacket<Series7::ConfRegType>::InitWithWords(
+ word_span);
+ packets.emplace_back(
+ new ConfigurationPacket<Series7::ConfRegType>(
+ *(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 =
+ ConfigurationPacket<Series7::ConfRegType>::InitWithWords(
+ word_span, packet1);
+ packets.emplace_back(
+ new ConfigurationPacket<Series7::ConfRegType>(
+ *(packet.second)));
+ }
+}
+
+// Empty packets should produce just the header
+TEST(BitstreamWriterTest, WriteHeader) {
+ std::vector<std::unique_ptr<ConfigurationPacket<Series7::ConfRegType>>>
+ packets;
+
+ BitstreamWriter<Series7> 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, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF,
+ 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0x000000BB, 0x11220044,
+ 0xFFFFFFFF, 0xFFFFFFFF, 0xAA995566};
+ EXPECT_EQ(words, ref_header);
+
+ // dump_packets(writer);
+}
+
+TEST(BitstreamWriterTest, WriteType0) {
+ std::vector<std::unique_ptr<ConfigurationPacket<Series7::ConfRegType>>>
+ packets;
+ AddType0(packets);
+ BitstreamWriter<Series7> writer(packets);
+ // dump_packets(writer, false);
+ std::vector<uint32_t> words(writer.begin(), writer.end());
+ std::vector<uint32_t> ref{
+ // Bus width + sync
+ 0x0FFFFFFFF, 0x0FFFFFFFF, 0x0FFFFFFFF, 0x0FFFFFFFF, 0x0FFFFFFFF,
+ 0x0FFFFFFFF, 0x0FFFFFFFF, 0x0FFFFFFFF, 0x0000000BB, 0x011220044,
+ 0x0FFFFFFFF, 0x0FFFFFFFF, 0x0AA995566,
+ // Type 0
+ 0x00000000};
+ EXPECT_EQ(words, ref);
+}
+TEST(BitstreamWriterTest, WriteType1) {
+ std::vector<std::unique_ptr<ConfigurationPacket<Series7::ConfRegType>>>
+ packets;
+ AddType1(packets);
+ BitstreamWriter<Series7> writer(packets);
+ // dump_packets(writer, false);
+ std::vector<uint32_t> words(writer.begin(), writer.end());
+ std::vector<uint32_t> ref{
+ // Bus width + sync
+ 0x0FFFFFFFF, 0x0FFFFFFFF, 0x0FFFFFFFF, 0x0FFFFFFFF, 0x0FFFFFFFF,
+ 0x0FFFFFFFF, 0x0FFFFFFFF, 0x0FFFFFFFF, 0x0000000BB, 0x011220044,
+ 0x0FFFFFFFF, 0x0FFFFFFFF, 0x0AA995566,
+ // Type 1
+ 0x030006002, 0x0000000AA, 0x0000000BB};
+ EXPECT_EQ(words, ref);
+}
+
+TEST(BitstreamWriterTest, WriteType2) {
+ std::vector<std::unique_ptr<ConfigurationPacket<Series7::ConfRegType>>>
+ packets;
+ AddType2(packets);
+ BitstreamWriter<Series7> writer(packets);
+ // dump_packets(writer, false);
+ std::vector<uint32_t> words(writer.begin(), writer.end());
+ std::vector<uint32_t> ref{
+ // Bus width + sync
+ 0x0FFFFFFFF, 0x0FFFFFFFF, 0x0FFFFFFFF, 0x0FFFFFFFF, 0x0FFFFFFFF,
+ 0x0FFFFFFFF, 0x0FFFFFFFF, 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<ConfigurationPacket<Series7::ConfRegType>>>
+ packets;
+ AddType1(packets);
+ AddType1E(packets);
+ AddType2(packets);
+ AddType1E(packets);
+ BitstreamWriter<Series7> writer(packets);
+ // dump_packets(writer, false);
+ std::vector<uint32_t> words(writer.begin(), writer.end());
+ std::vector<uint32_t> ref{
+ // Bus width + sync
+ 0x0FFFFFFFF, 0x0FFFFFFFF, 0x0FFFFFFFF, 0x0FFFFFFFF, 0x0FFFFFFFF,
+ 0x0FFFFFFFF, 0x0FFFFFFFF, 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);
+}
diff --git a/lib/xilinx/tests/xc7series/block_type_test.cc b/lib/xilinx/tests/xc7series/block_type_test.cc
new file mode 100644
index 0000000..6fc8ecc
--- /dev/null
+++ b/lib/xilinx/tests/xc7series/block_type_test.cc
@@ -0,0 +1,30 @@
+#include <prjxray/xilinx/xc7series/block_type.h>
+
+#include <gtest/gtest.h>
+
+using namespace prjxray::xilinx;
+
+TEST(BlockTypeTest, YamlEncode) {
+ YAML::Node node;
+ node.push_back(xc7series::BlockType::CLB_IO_CLK);
+ node.push_back(xc7series::BlockType::BLOCK_RAM);
+ node.push_back(xc7series::BlockType::CFG_CLB);
+
+ EXPECT_EQ(node[0].as<std::string>(), "CLB_IO_CLK");
+ EXPECT_EQ(node[1].as<std::string>(), "BLOCK_RAM");
+ EXPECT_EQ(node[2].as<std::string>(), "CFG_CLB");
+}
+
+TEST(BlockTypeTest, YamlDecode) {
+ YAML::Node node;
+ node.push_back("CFG_CLB");
+ node.push_back("BLOCK_RAM");
+ node.push_back("CLB_IO_CLK");
+
+ EXPECT_EQ(node[0].as<xc7series::BlockType>(),
+ xc7series::BlockType::CFG_CLB);
+ EXPECT_EQ(node[1].as<xc7series::BlockType>(),
+ xc7series::BlockType::BLOCK_RAM);
+ EXPECT_EQ(node[2].as<xc7series::BlockType>(),
+ xc7series::BlockType::CLB_IO_CLK);
+}
diff --git a/lib/xilinx/tests/xc7series/configuration_bus_test.cc b/lib/xilinx/tests/xc7series/configuration_bus_test.cc
new file mode 100644
index 0000000..c665d9d
--- /dev/null
+++ b/lib/xilinx/tests/xc7series/configuration_bus_test.cc
@@ -0,0 +1,72 @@
+#include <prjxray/xilinx/xc7series/configuration_bus.h>
+
+#include <gtest/gtest.h>
+
+namespace xc7series = prjxray::xilinx::xc7series;
+
+TEST(ConfigurationBusTest, IsValidFrameAddress) {
+ std::vector<xc7series::FrameAddress> addresses;
+ addresses.push_back(xc7series::FrameAddress(
+ xc7series::BlockType::BLOCK_RAM, false, 0, 0, 0));
+ addresses.push_back(xc7series::FrameAddress(
+ xc7series::BlockType::BLOCK_RAM, false, 0, 0, 1));
+ addresses.push_back(xc7series::FrameAddress(
+ xc7series::BlockType::BLOCK_RAM, false, 0, 1, 0));
+ addresses.push_back(xc7series::FrameAddress(
+ xc7series::BlockType::BLOCK_RAM, false, 0, 1, 1));
+
+ xc7series::ConfigurationBus bus(addresses.begin(), addresses.end());
+
+ EXPECT_TRUE(bus.IsValidFrameAddress(xc7series::FrameAddress(
+ xc7series::BlockType::BLOCK_RAM, false, 0, 0, 0)));
+ EXPECT_TRUE(bus.IsValidFrameAddress(xc7series::FrameAddress(
+ xc7series::BlockType::BLOCK_RAM, false, 0, 1, 1)));
+
+ EXPECT_FALSE(bus.IsValidFrameAddress(xc7series::FrameAddress(
+ xc7series::BlockType::BLOCK_RAM, false, 0, 0, 2)));
+}
+
+TEST(ConfigurationBusTest, GetNextFrameAddressYieldNextAddressInBus) {
+ std::vector<xc7series::FrameAddress> addresses;
+ addresses.push_back(xc7series::FrameAddress(
+ xc7series::BlockType::BLOCK_RAM, false, 0, 0, 0));
+ addresses.push_back(xc7series::FrameAddress(
+ xc7series::BlockType::BLOCK_RAM, false, 0, 0, 1));
+ addresses.push_back(xc7series::FrameAddress(
+ xc7series::BlockType::BLOCK_RAM, false, 0, 1, 0));
+ addresses.push_back(xc7series::FrameAddress(
+ xc7series::BlockType::BLOCK_RAM, false, 0, 1, 1));
+
+ xc7series::ConfigurationBus bus(addresses.begin(), addresses.end());
+
+ auto next_address = bus.GetNextFrameAddress(xc7series::FrameAddress(
+ xc7series::BlockType::BLOCK_RAM, false, 0, 0, 0));
+ ASSERT_TRUE(next_address);
+ EXPECT_EQ(*next_address,
+ xc7series::FrameAddress(xc7series::BlockType::BLOCK_RAM,
+ false, 0, 0, 1));
+
+ next_address = bus.GetNextFrameAddress(xc7series::FrameAddress(
+ xc7series::BlockType::BLOCK_RAM, false, 0, 0, 1));
+ ASSERT_TRUE(next_address);
+ EXPECT_EQ(*next_address,
+ xc7series::FrameAddress(xc7series::BlockType::BLOCK_RAM,
+ false, 0, 1, 0));
+}
+
+TEST(ConfigurationBusTest, GetNextFrameAddressYieldNothingAtEndOfBus) {
+ std::vector<xc7series::FrameAddress> addresses;
+ addresses.push_back(xc7series::FrameAddress(
+ xc7series::BlockType::BLOCK_RAM, false, 0, 0, 0));
+ addresses.push_back(xc7series::FrameAddress(
+ xc7series::BlockType::BLOCK_RAM, false, 0, 0, 1));
+ addresses.push_back(xc7series::FrameAddress(
+ xc7series::BlockType::BLOCK_RAM, false, 0, 1, 0));
+ addresses.push_back(xc7series::FrameAddress(
+ xc7series::BlockType::BLOCK_RAM, false, 0, 1, 1));
+
+ xc7series::ConfigurationBus bus(addresses.begin(), addresses.end());
+
+ EXPECT_FALSE(bus.GetNextFrameAddress(xc7series::FrameAddress(
+ xc7series::BlockType::BLOCK_RAM, false, 0, 1, 1)));
+}
diff --git a/lib/xilinx/tests/xc7series/configuration_column_test.cc b/lib/xilinx/tests/xc7series/configuration_column_test.cc
new file mode 100644
index 0000000..49f4920
--- /dev/null
+++ b/lib/xilinx/tests/xc7series/configuration_column_test.cc
@@ -0,0 +1,65 @@
+#include <prjxray/xilinx/xc7series/configuration_column.h>
+
+#include <gtest/gtest.h>
+#include <prjxray/xilinx/xc7series/block_type.h>
+#include <prjxray/xilinx/xc7series/frame_address.h>
+#include <yaml-cpp/yaml.h>
+
+using namespace prjxray::xilinx;
+
+TEST(ConfigurationColumnTest, IsValidFrameAddress) {
+ xc7series::ConfigurationColumn column(10);
+
+ // Inside this column.
+ EXPECT_TRUE(column.IsValidFrameAddress(xc7series::FrameAddress(
+ xc7series::BlockType::CLB_IO_CLK, false, 1, 2, 3)));
+ // Past this column's frame width.
+ EXPECT_FALSE(column.IsValidFrameAddress(xc7series::FrameAddress(
+ xc7series::BlockType::CLB_IO_CLK, false, 1, 2, 10)));
+}
+
+TEST(ConfigurationColumnTest, GetNextFrameAddressYieldNextAddressInColumn) {
+ xc7series::ConfigurationColumn column(10);
+
+ auto next_address = column.GetNextFrameAddress(xc7series::FrameAddress(
+ xc7series::BlockType::CLB_IO_CLK, false, 1, 2, 3));
+ EXPECT_TRUE(next_address);
+ EXPECT_EQ(*next_address,
+ xc7series::FrameAddress(xc7series::BlockType::CLB_IO_CLK,
+ false, 1, 2, 4));
+}
+
+TEST(ConfigurationColumnTest, GetNextFrameAddressYieldNothingAtEndOfColumn) {
+ xc7series::ConfigurationColumn column(10);
+
+ EXPECT_FALSE(column.GetNextFrameAddress(xc7series::FrameAddress(
+ xc7series::BlockType::CLB_IO_CLK, false, 1, 2, 9)));
+}
+
+TEST(ConfigurationColumnTest, GetNextFrameAddressYieldNothingOutsideColumn) {
+ xc7series::ConfigurationColumn column(10);
+
+ // Just past last frame in column.
+ EXPECT_FALSE(column.GetNextFrameAddress(xc7series::FrameAddress(
+ xc7series::BlockType::CLB_IO_CLK, false, 1, 2, 10)));
+}
+
+TEST(ConfigurationColumnTest, YamlEncodeTest) {
+ xc7series::ConfigurationColumn column(10);
+
+ YAML::Node node(column);
+ EXPECT_TRUE(node["frame_count"]);
+ EXPECT_EQ(node["frame_count"].as<int>(), 10);
+}
+
+TEST(ConfigurationColumnTest, YAMLDecodeTest) {
+ YAML::Node node;
+ node.SetTag("xilinx/xc7series/configuration_column");
+ node["frame_count"] = 10;
+
+ auto column = node.as<xc7series::ConfigurationColumn>();
+ EXPECT_TRUE(column.GetNextFrameAddress(xc7series::FrameAddress(
+ xc7series::BlockType::CLB_IO_CLK, false, 1, 2, 8)));
+ EXPECT_FALSE(column.GetNextFrameAddress(xc7series::FrameAddress(
+ xc7series::BlockType::CLB_IO_CLK, false, 1, 2, 9)));
+}
diff --git a/lib/xilinx/tests/xc7series/configuration_packet_test.cc b/lib/xilinx/tests/xc7series/configuration_packet_test.cc
new file mode 100644
index 0000000..1658f14
--- /dev/null
+++ b/lib/xilinx/tests/xc7series/configuration_packet_test.cc
@@ -0,0 +1,105 @@
+#include <functional>
+
+#include <absl/meta/type_traits.h>
+#include <gtest/gtest.h>
+#include <prjxray/bit_ops.h>
+#include <prjxray/xilinx/architectures.h>
+#include <prjxray/xilinx/configuration_packet.h>
+
+using namespace prjxray::xilinx;
+
+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);
+}
+
+TEST(ConfigPacket, InitWithZeroBytes) {
+ auto packet =
+ ConfigurationPacket<Series7::ConfRegType>::InitWithWords({});
+
+ EXPECT_EQ(packet.first, absl::Span<uint32_t>());
+ EXPECT_FALSE(packet.second);
+}
+
+TEST(ConfigPacket, InitWithType1Nop) {
+ std::vector<uint32_t> words{kType1NOP};
+ absl::Span<uint32_t> word_span(words);
+ auto packet =
+ ConfigurationPacket<Series7::ConfRegType>::InitWithWords(word_span);
+ EXPECT_EQ(packet.first, absl::Span<uint32_t>());
+ ASSERT_TRUE(packet.second);
+ EXPECT_EQ(packet.second->opcode(),
+ ConfigurationPacket<Series7::ConfRegType>::Opcode::NOP);
+ EXPECT_EQ(packet.second->address(), Series7::ConfRegType::CRC);
+ EXPECT_EQ(packet.second->data(), absl::Span<uint32_t>());
+}
+
+TEST(ConfigPacket, InitWithType1Read) {
+ std::vector<uint32_t> words{MakeType1(0x1, 0x2, 2), 0xAA, 0xBB};
+ absl::Span<uint32_t> word_span(words);
+ auto packet =
+ ConfigurationPacket<Series7::ConfRegType>::InitWithWords(word_span);
+ EXPECT_EQ(packet.first, absl::Span<uint32_t>());
+ ASSERT_TRUE(packet.second);
+ EXPECT_EQ(packet.second->opcode(),
+ ConfigurationPacket<Series7::ConfRegType>::Opcode::Read);
+ EXPECT_EQ(packet.second->address(), Series7::ConfRegType::FDRI);
+ EXPECT_EQ(packet.second->data(), word_span.subspan(1));
+}
+
+TEST(ConfigPacket, InitWithType1Write) {
+ std::vector<uint32_t> words{MakeType1(0x2, 0x3, 2), 0xAA, 0xBB};
+ absl::Span<uint32_t> word_span(words);
+ auto packet =
+ ConfigurationPacket<Series7::ConfRegType>::InitWithWords(word_span);
+ EXPECT_EQ(packet.first, absl::Span<uint32_t>());
+ ASSERT_TRUE(packet.second);
+ EXPECT_EQ(packet.second->opcode(),
+ ConfigurationPacket<Series7::ConfRegType>::Opcode::Write);
+ EXPECT_EQ(packet.second->address(), Series7::ConfRegType::FDRO);
+ EXPECT_EQ(packet.second->data(), word_span.subspan(1));
+}
+
+TEST(ConfigPacket, InitWithType2WithoutPreviousPacketFails) {
+ std::vector<uint32_t> words{MakeType2(0x01, 12)};
+ absl::Span<uint32_t> word_span(words);
+ auto packet =
+ ConfigurationPacket<Series7::ConfRegType>::InitWithWords(word_span);
+ EXPECT_EQ(packet.first, words);
+ EXPECT_FALSE(packet.second);
+}
+
+TEST(ConfigPacket, InitWithType2WithPreviousPacket) {
+ ConfigurationPacket<Series7::ConfRegType> previous_packet(
+ static_cast<unsigned int>(0x1),
+ ConfigurationPacket<Series7::ConfRegType>::Opcode::Read,
+ Series7::ConfRegType::MFWR, absl::Span<uint32_t>());
+ 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 = ConfigurationPacket<Series7::ConfRegType>::InitWithWords(
+ word_span, &previous_packet);
+ EXPECT_EQ(packet.first, absl::Span<uint32_t>());
+ ASSERT_TRUE(packet.second);
+ EXPECT_EQ(packet.second->opcode(),
+ ConfigurationPacket<Series7::ConfRegType>::Opcode::Read);
+ EXPECT_EQ(packet.second->address(), Series7::ConfRegType::MFWR);
+ EXPECT_EQ(packet.second->data(), word_span.subspan(1));
+}
diff --git a/lib/xilinx/tests/xc7series/configuration_test.cc b/lib/xilinx/tests/xc7series/configuration_test.cc
new file mode 100644
index 0000000..0c27d58
--- /dev/null
+++ b/lib/xilinx/tests/xc7series/configuration_test.cc
@@ -0,0 +1,311 @@
+#include <cstdint>
+#include <iostream>
+#include <vector>
+
+#include <absl/types/span.h>
+#include <gtest/gtest.h>
+#include <prjxray/memory_mapped_file.h>
+#include <prjxray/xilinx/architectures.h>
+#include <prjxray/xilinx/bitstream_reader.h>
+#include <prjxray/xilinx/configuration.h>
+#include <prjxray/xilinx/configuration_packet.h>
+#include <prjxray/xilinx/configuration_register.h>
+#include <prjxray/xilinx/frames.h>
+#include <yaml-cpp/yaml.h>
+
+using namespace prjxray::xilinx;
+
+TEST(ConfigurationTest, ConstructFromPacketsWithSingleFrame) {
+ std::vector<Series7::FrameAddress> test_part_addresses;
+ test_part_addresses.push_back(0x4567);
+ test_part_addresses.push_back(0x4568);
+
+ xc7series::Part test_part(0x1234, test_part_addresses);
+
+ std::vector<uint32_t> idcode{0x1234};
+ std::vector<uint32_t> cmd{0x0001};
+ std::vector<uint32_t> frame_address{0x4567};
+ std::vector<uint32_t> frame(101, 0xAA);
+
+ std::vector<ConfigurationPacket<Series7::ConfRegType>> packets{
+ {
+ static_cast<unsigned int>(0x1),
+ ConfigurationPacket<Series7::ConfRegType>::Opcode::Write,
+ Series7::ConfRegType::IDCODE,
+ absl::MakeSpan(idcode),
+ },
+ {
+ static_cast<unsigned int>(0x1),
+ ConfigurationPacket<Series7::ConfRegType>::Opcode::Write,
+ Series7::ConfRegType::FAR,
+ absl::MakeSpan(frame_address),
+ },
+ {
+ static_cast<unsigned int>(0x1),
+ ConfigurationPacket<Series7::ConfRegType>::Opcode::Write,
+ Series7::ConfRegType::CMD,
+ absl::MakeSpan(cmd),
+ },
+ {
+ static_cast<unsigned int>(0x1),
+ ConfigurationPacket<Series7::ConfRegType>::Opcode::Write,
+ Series7::ConfRegType::FDRI,
+ absl::MakeSpan(frame),
+ },
+ };
+
+ auto test_config =
+ Configuration<Series7>::InitWithPackets(test_part, packets);
+ ASSERT_TRUE(test_config);
+
+ EXPECT_EQ(test_config->part().idcode(), static_cast<uint32_t>(0x1234));
+ EXPECT_EQ(test_config->frames().size(), static_cast<size_t>(1));
+ EXPECT_EQ(test_config->frames().at(0x4567), frame);
+}
+
+TEST(ConfigurationTest, ConstructFromPacketsWithAutoincrement) {
+ std::vector<xc7series::FrameAddress> test_part_addresses;
+ for (int ii = 0x4560; ii < 0x4570; ++ii) {
+ test_part_addresses.push_back(ii);
+ }
+ for (int ii = 0x4580; ii < 0x4590; ++ii) {
+ test_part_addresses.push_back(ii);
+ }
+
+ xc7series::Part test_part(0x1234, test_part_addresses);
+
+ std::vector<uint32_t> idcode{0x1234};
+ std::vector<uint32_t> cmd{0x0001};
+ std::vector<uint32_t> frame_address{0x456F};
+ std::vector<uint32_t> frame(202, 0xAA);
+ std::fill_n(frame.begin() + 101, 101, 0xBB);
+
+ std::vector<ConfigurationPacket<Series7::ConfRegType>> packets{
+ {
+ static_cast<unsigned int>(0x1),
+ ConfigurationPacket<Series7::ConfRegType>::Opcode::Write,
+ Series7::ConfRegType::IDCODE,
+ absl::MakeSpan(idcode),
+ },
+ {
+ static_cast<unsigned int>(0x1),
+ ConfigurationPacket<Series7::ConfRegType>::Opcode::Write,
+ Series7::ConfRegType::FAR,
+ absl::MakeSpan(frame_address),
+ },
+ {
+ static_cast<unsigned int>(0x1),
+ ConfigurationPacket<Series7::ConfRegType>::Opcode::Write,
+ Series7::ConfRegType::CMD,
+ absl::MakeSpan(cmd),
+ },
+ {
+ static_cast<unsigned int>(0x1),
+ ConfigurationPacket<Series7::ConfRegType>::Opcode::Write,
+ Series7::ConfRegType::FDRI,
+ absl::MakeSpan(frame),
+ },
+ };
+
+ auto test_config =
+ Configuration<Series7>::InitWithPackets(test_part, packets);
+ ASSERT_TRUE(test_config);
+
+ absl::Span<uint32_t> frame_span(frame);
+ EXPECT_EQ(test_config->part().idcode(), static_cast<uint32_t>(0x1234));
+ EXPECT_EQ(test_config->frames().size(), static_cast<size_t>(2));
+ EXPECT_EQ(test_config->frames().at(0x456F),
+ std::vector<uint32_t>(101, 0xAA));
+ EXPECT_EQ(test_config->frames().at(0x4580),
+ std::vector<uint32_t>(101, 0xBB));
+}
+
+TEST(ConfigurationTest,
+ DebugAndPerFrameCrcBitstreamsProduceEqualConfigurations) {
+ auto part = xc7series::Part::FromFile("configuration_test.yaml");
+ ASSERT_TRUE(part);
+
+ auto debug_bitstream = prjxray::MemoryMappedFile::InitWithFile(
+ "configuration_test.debug.bit");
+ ASSERT_TRUE(debug_bitstream);
+
+ auto debug_reader = BitstreamReader<Series7>::InitWithBytes(
+ debug_bitstream->as_bytes());
+ ASSERT_TRUE(debug_reader);
+
+ auto debug_configuration =
+ Configuration<Series7>::InitWithPackets(*part, *debug_reader);
+ ASSERT_TRUE(debug_configuration);
+
+ auto perframecrc_bitstream = prjxray::MemoryMappedFile::InitWithFile(
+ "configuration_test.perframecrc.bit");
+ ASSERT_TRUE(perframecrc_bitstream);
+
+ auto perframecrc_reader = BitstreamReader<Series7>::InitWithBytes(
+ perframecrc_bitstream->as_bytes());
+ ASSERT_TRUE(perframecrc_reader);
+
+ auto perframecrc_configuration =
+ Configuration<Series7>::InitWithPackets(*part, *perframecrc_reader);
+ ASSERT_TRUE(perframecrc_configuration);
+
+ for (auto debug_frame : debug_configuration->frames()) {
+ auto perframecrc_frame =
+ perframecrc_configuration->frames().find(debug_frame.first);
+ if (perframecrc_frame ==
+ perframecrc_configuration->frames().end()) {
+ ADD_FAILURE() << debug_frame.first
+ << ": missing in perframecrc bitstream";
+ continue;
+ }
+
+ for (int ii = 0; ii < 101; ++ii) {
+ EXPECT_EQ(perframecrc_frame->second[ii],
+ debug_frame.second[ii])
+ << debug_frame.first << ": word " << ii;
+ }
+ }
+
+ for (auto perframecrc_frame : perframecrc_configuration->frames()) {
+ auto debug_frame =
+ debug_configuration->frames().find(perframecrc_frame.first);
+ if (debug_frame == debug_configuration->frames().end()) {
+ ADD_FAILURE() << perframecrc_frame.first
+ << ": unexpectedly present in "
+ "perframecrc bitstream";
+ }
+ }
+}
+
+TEST(ConfigurationTest, DebugAndNormalBitstreamsProduceEqualConfigurations) {
+ auto part = xc7series::Part::FromFile("configuration_test.yaml");
+ ASSERT_TRUE(part);
+
+ auto debug_bitstream = prjxray::MemoryMappedFile::InitWithFile(
+ "configuration_test.debug.bit");
+ ASSERT_TRUE(debug_bitstream);
+
+ auto debug_reader = BitstreamReader<Series7>::InitWithBytes(
+ debug_bitstream->as_bytes());
+ ASSERT_TRUE(debug_reader);
+
+ auto debug_configuration =
+ Configuration<Series7>::InitWithPackets(*part, *debug_reader);
+ ASSERT_TRUE(debug_configuration);
+
+ auto normal_bitstream =
+ prjxray::MemoryMappedFile::InitWithFile("configuration_test.bit");
+ ASSERT_TRUE(normal_bitstream);
+
+ auto normal_reader = BitstreamReader<Series7>::InitWithBytes(
+ normal_bitstream->as_bytes());
+ ASSERT_TRUE(normal_reader);
+
+ auto normal_configuration =
+ Configuration<Series7>::InitWithPackets(*part, *normal_reader);
+ ASSERT_TRUE(normal_configuration);
+
+ for (auto debug_frame : debug_configuration->frames()) {
+ auto normal_frame =
+ normal_configuration->frames().find(debug_frame.first);
+ if (normal_frame == normal_configuration->frames().end()) {
+ ADD_FAILURE() << debug_frame.first
+ << ": missing in normal bitstream";
+ continue;
+ }
+
+ for (int ii = 0; ii < 101; ++ii) {
+ EXPECT_EQ(normal_frame->second[ii],
+ debug_frame.second[ii])
+ << debug_frame.first << ": word " << ii;
+ }
+ }
+
+ for (auto normal_frame : normal_configuration->frames()) {
+ auto debug_frame =
+ debug_configuration->frames().find(normal_frame.first);
+ if (debug_frame == debug_configuration->frames().end()) {
+ ADD_FAILURE()
+ << normal_frame.first
+ << ": unexpectedly present in normal bitstream";
+ }
+ }
+}
+
+TEST(ConfigurationTest, CheckForPaddingFrames) {
+ std::vector<xc7series::FrameAddress> test_part_addresses = {
+ xc7series::FrameAddress(xc7series::BlockType::CLB_IO_CLK, false, 0,
+ 0, 0),
+ xc7series::FrameAddress(xc7series::BlockType::CLB_IO_CLK, true, 0,
+ 0, 0),
+ xc7series::FrameAddress(xc7series::BlockType::CLB_IO_CLK, true, 1,
+ 0, 0),
+ xc7series::FrameAddress(xc7series::BlockType::BLOCK_RAM, false, 0,
+ 0, 0),
+ xc7series::FrameAddress(xc7series::BlockType::BLOCK_RAM, false, 1,
+ 0, 0)};
+
+ auto test_part = absl::optional<xc7series::Part>(
+ xc7series::Part(0x1234, test_part_addresses));
+
+ Frames<Series7> frames;
+ frames.getFrames().emplace(std::make_pair(
+ test_part_addresses.at(0), std::vector<uint32_t>(101, 0xAA)));
+ frames.getFrames().emplace(std::make_pair(
+ test_part_addresses.at(1), std::vector<uint32_t>(101, 0xBB)));
+ frames.getFrames().emplace(std::make_pair(
+ test_part_addresses.at(2), std::vector<uint32_t>(101, 0xCC)));
+ frames.getFrames().emplace(std::make_pair(
+ test_part_addresses.at(3), std::vector<uint32_t>(101, 0xDD)));
+ frames.getFrames().emplace(std::make_pair(
+ test_part_addresses.at(4), std::vector<uint32_t>(101, 0xEE)));
+ ASSERT_EQ(frames.getFrames().size(), 5);
+
+ Configuration<Series7>::PacketData packet_data =
+ Configuration<Series7>::createType2ConfigurationPacketData(
+ frames.getFrames(), test_part);
+ // createType2ConfigurationPacketData should detect 4
+ // row/half/block_type switches thus add 4*2 padding frames moreover 2
+ // extra padding frames are added at the end of the creation of the data
+ // overall this gives us: 5(real frames) + 4*2 + 2 = 15 frames, which is
+ // 15 * 101 = 1515 words
+ EXPECT_EQ(packet_data.size(), 15 * 101);
+
+ std::vector<uint32_t> idcode{0x1234};
+ std::vector<uint32_t> cmd{0x0001};
+ std::vector<uint32_t> frame_address{0x0};
+
+ std::vector<ConfigurationPacket<Series7::ConfRegType>> packets{
+ {
+ static_cast<unsigned int>(0x1),
+ ConfigurationPacket<Series7::ConfRegType>::Opcode::Write,
+ Series7::ConfRegType::IDCODE,
+ absl::MakeSpan(idcode),
+ },
+ {
+ static_cast<unsigned int>(0x1),
+ ConfigurationPacket<Series7::ConfRegType>::Opcode::Write,
+ Series7::ConfRegType::FAR,
+ absl::MakeSpan(frame_address),
+ },
+ {
+ static_cast<unsigned int>(0x1),
+ ConfigurationPacket<Series7::ConfRegType>::Opcode::Write,
+ Series7::ConfRegType::CMD,
+ absl::MakeSpan(cmd),
+ },
+ {
+ static_cast<unsigned int>(0x1),
+ ConfigurationPacket<Series7::ConfRegType>::Opcode::Write,
+ Series7::ConfRegType::FDRI,
+ absl::MakeSpan(packet_data),
+ },
+ };
+
+ auto test_config =
+ Configuration<Series7>::InitWithPackets(*test_part, packets);
+ ASSERT_EQ(test_config->frames().size(), 5);
+ for (auto& frame : test_config->frames()) {
+ EXPECT_EQ(frame.second, frames.getFrames().at(frame.first));
+ }
+}
diff --git a/lib/xilinx/tests/xc7series/crc_test.cc b/lib/xilinx/tests/xc7series/crc_test.cc
new file mode 100644
index 0000000..6a71135
--- /dev/null
+++ b/lib/xilinx/tests/xc7series/crc_test.cc
@@ -0,0 +1,16 @@
+#include <prjxray/xilinx/xc7series/crc.h>
+
+#include <gtest/gtest.h>
+
+using namespace prjxray::xilinx;
+
+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/tests/xc7series/ecc_test.cc b/lib/xilinx/tests/xc7series/ecc_test.cc
new file mode 100644
index 0000000..eb3b4da
--- /dev/null
+++ b/lib/xilinx/tests/xc7series/ecc_test.cc
@@ -0,0 +1,20 @@
+#include <prjxray/xilinx/xc7series/ecc.h>
+
+#include <gtest/gtest.h>
+
+using namespace prjxray::xilinx;
+
+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);
+}
diff --git a/lib/xilinx/tests/xc7series/frame_address_test.cc b/lib/xilinx/tests/xc7series/frame_address_test.cc
new file mode 100644
index 0000000..70c1f59
--- /dev/null
+++ b/lib/xilinx/tests/xc7series/frame_address_test.cc
@@ -0,0 +1,36 @@
+#include <prjxray/xilinx/xc7series/frame_address.h>
+
+#include <gtest/gtest.h>
+
+using namespace prjxray::xilinx;
+
+TEST(FrameAddressTest, YamlEncode) {
+ xc7series::FrameAddress address(xc7series::BlockType::BLOCK_RAM, false,
+ 10, 0, 5);
+
+ YAML::Node node(address);
+
+ EXPECT_EQ(node.Tag(), "xilinx/xc7series/frame_address");
+ EXPECT_EQ(node["block_type"].as<std::string>(), "BLOCK_RAM");
+ EXPECT_EQ(node["row_half"].as<std::string>(), "top");
+ EXPECT_EQ(node["row"].as<std::string>(), "10");
+ EXPECT_EQ(node["column"].as<std::string>(), "0");
+ EXPECT_EQ(node["minor"].as<std::string>(), "5");
+}
+
+TEST(FrameAddressTest, YamlDecode) {
+ YAML::Node node;
+ node.SetTag("xilinx/xc7series/frame_address");
+ node["block_type"] = "BLOCK_RAM";
+ node["row_half"] = "bottom";
+ node["row"] = "0";
+ node["column"] = "5";
+ node["minor"] = "11";
+
+ xc7series::FrameAddress address = node.as<xc7series::FrameAddress>();
+ EXPECT_EQ(address.block_type(), xc7series::BlockType::BLOCK_RAM);
+ EXPECT_TRUE(address.is_bottom_half_rows());
+ EXPECT_EQ(address.row(), 0);
+ EXPECT_EQ(address.column(), 5);
+ EXPECT_EQ(address.minor(), 11);
+}
diff --git a/lib/xilinx/tests/xc7series/frames_test.cc b/lib/xilinx/tests/xc7series/frames_test.cc
new file mode 100644
index 0000000..3cf4525
--- /dev/null
+++ b/lib/xilinx/tests/xc7series/frames_test.cc
@@ -0,0 +1,55 @@
+#include <vector>
+
+#include <gtest/gtest.h>
+
+#include <prjxray/xilinx/architectures.h>
+#include <prjxray/xilinx/frames.h>
+#include <prjxray/xilinx/xc7series/part.h>
+
+using namespace prjxray::xilinx;
+
+TEST(FramesTest, FillInMissingFrames) {
+ std::vector<xc7series::FrameAddress> test_part_addresses = {
+ xc7series::FrameAddress(xc7series::BlockType::CLB_IO_CLK, false, 0,
+ 0, 0),
+ xc7series::FrameAddress(xc7series::BlockType::CLB_IO_CLK, false, 0,
+ 0, 1),
+ xc7series::FrameAddress(xc7series::BlockType::CLB_IO_CLK, false, 0,
+ 0, 2),
+ xc7series::FrameAddress(xc7series::BlockType::CLB_IO_CLK, false, 0,
+ 0, 3),
+ xc7series::FrameAddress(xc7series::BlockType::CLB_IO_CLK, false, 0,
+ 0, 4)};
+
+ xc7series::Part test_part(0x1234, test_part_addresses);
+
+ Frames<Series7> frames;
+ frames.getFrames().emplace(std::make_pair(
+ xc7series::FrameAddress(2), std::vector<uint32_t>(101, 0xCC)));
+ frames.getFrames().emplace(std::make_pair(
+ xc7series::FrameAddress(3), std::vector<uint32_t>(101, 0xDD)));
+ frames.getFrames().emplace(std::make_pair(
+ xc7series::FrameAddress(4), std::vector<uint32_t>(101, 0xEE)));
+
+ ASSERT_EQ(frames.getFrames().size(), 3);
+ EXPECT_EQ(frames.getFrames().at(test_part_addresses[2]),
+ std::vector<uint32_t>(101, 0xCC));
+ EXPECT_EQ(frames.getFrames().at(test_part_addresses[3]),
+ std::vector<uint32_t>(101, 0xDD));
+ EXPECT_EQ(frames.getFrames().at(test_part_addresses[4]),
+ std::vector<uint32_t>(101, 0xEE));
+
+ frames.addMissingFrames(test_part);
+
+ ASSERT_EQ(frames.getFrames().size(), 5);
+ EXPECT_EQ(frames.getFrames().at(test_part_addresses[0]),
+ std::vector<uint32_t>(101, 0));
+ EXPECT_EQ(frames.getFrames().at(test_part_addresses[1]),
+ std::vector<uint32_t>(101, 0));
+ EXPECT_EQ(frames.getFrames().at(test_part_addresses[2]),
+ std::vector<uint32_t>(101, 0xCC));
+ EXPECT_EQ(frames.getFrames().at(test_part_addresses[3]),
+ std::vector<uint32_t>(101, 0xDD));
+ EXPECT_EQ(frames.getFrames().at(test_part_addresses[4]),
+ std::vector<uint32_t>(101, 0xEE));
+}
diff --git a/lib/xilinx/tests/xc7series/global_clock_region_test.cc b/lib/xilinx/tests/xc7series/global_clock_region_test.cc
new file mode 100644
index 0000000..4597893
--- /dev/null
+++ b/lib/xilinx/tests/xc7series/global_clock_region_test.cc
@@ -0,0 +1,147 @@
+#include <prjxray/xilinx/xc7series/global_clock_region.h>
+
+#include <gtest/gtest.h>
+
+using namespace prjxray::xilinx;
+
+TEST(GlobalClockRegionTest, IsValidFrameAddress) {
+ std::vector<xc7series::FrameAddress> addresses;
+ addresses.push_back(xc7series::FrameAddress(
+ xc7series::BlockType::CLB_IO_CLK, false, 0, 0, 0));
+ addresses.push_back(xc7series::FrameAddress(
+ xc7series::BlockType::CLB_IO_CLK, false, 0, 0, 1));
+ addresses.push_back(xc7series::FrameAddress(
+ xc7series::BlockType::CLB_IO_CLK, false, 0, 1, 0));
+ addresses.push_back(xc7series::FrameAddress(
+ xc7series::BlockType::CLB_IO_CLK, false, 0, 1, 1));
+ addresses.push_back(xc7series::FrameAddress(
+ xc7series::BlockType::BLOCK_RAM, false, 0, 0, 0));
+ addresses.push_back(xc7series::FrameAddress(
+ xc7series::BlockType::BLOCK_RAM, false, 0, 0, 1));
+ addresses.push_back(xc7series::FrameAddress(
+ xc7series::BlockType::BLOCK_RAM, false, 0, 0, 2));
+ addresses.push_back(xc7series::FrameAddress(
+ xc7series::BlockType::CLB_IO_CLK, false, 1, 0, 0));
+ addresses.push_back(xc7series::FrameAddress(
+ xc7series::BlockType::CLB_IO_CLK, false, 1, 0, 1));
+
+ xc7series::GlobalClockRegion global_clock_region(addresses.begin(),
+ addresses.end());
+
+ EXPECT_TRUE(
+ global_clock_region.IsValidFrameAddress(xc7series::FrameAddress(
+ xc7series::BlockType::CLB_IO_CLK, false, 0, 0, 0)));
+ EXPECT_TRUE(
+ global_clock_region.IsValidFrameAddress(xc7series::FrameAddress(
+ xc7series::BlockType::CLB_IO_CLK, false, 0, 1, 0)));
+ EXPECT_TRUE(
+ global_clock_region.IsValidFrameAddress(xc7series::FrameAddress(
+ xc7series::BlockType::BLOCK_RAM, false, 0, 0, 2)));
+ EXPECT_TRUE(
+ global_clock_region.IsValidFrameAddress(xc7series::FrameAddress(
+ xc7series::BlockType::CLB_IO_CLK, false, 1, 0, 0)));
+
+ EXPECT_FALSE(
+ global_clock_region.IsValidFrameAddress(xc7series::FrameAddress(
+ xc7series::BlockType::CLB_IO_CLK, false, 0, 0, 2)));
+ EXPECT_FALSE(
+ global_clock_region.IsValidFrameAddress(xc7series::FrameAddress(
+ xc7series::BlockType::CLB_IO_CLK, false, 0, 2, 0)));
+ EXPECT_FALSE(
+ global_clock_region.IsValidFrameAddress(xc7series::FrameAddress(
+ xc7series::BlockType::CLB_IO_CLK, false, 2, 0, 0)));
+ EXPECT_FALSE(
+ global_clock_region.IsValidFrameAddress(xc7series::FrameAddress(
+ xc7series::BlockType::CFG_CLB, false, 0, 0, 2)));
+}
+
+TEST(GlobalClockRegionTest,
+ GetNextFrameAddressYieldNextAddressInGlobalClockRegion) {
+ std::vector<xc7series::FrameAddress> addresses;
+ addresses.push_back(xc7series::FrameAddress(
+ xc7series::BlockType::CLB_IO_CLK, false, 0, 0, 0));
+ addresses.push_back(xc7series::FrameAddress(
+ xc7series::BlockType::CLB_IO_CLK, false, 0, 0, 1));
+ addresses.push_back(xc7series::FrameAddress(
+ xc7series::BlockType::CLB_IO_CLK, false, 0, 1, 0));
+ addresses.push_back(xc7series::FrameAddress(
+ xc7series::BlockType::CLB_IO_CLK, false, 0, 1, 1));
+ addresses.push_back(xc7series::FrameAddress(
+ xc7series::BlockType::BLOCK_RAM, false, 0, 0, 0));
+ addresses.push_back(xc7series::FrameAddress(
+ xc7series::BlockType::BLOCK_RAM, false, 0, 0, 1));
+ addresses.push_back(xc7series::FrameAddress(
+ xc7series::BlockType::BLOCK_RAM, false, 0, 0, 2));
+ addresses.push_back(xc7series::FrameAddress(
+ xc7series::BlockType::CLB_IO_CLK, false, 1, 0, 0));
+ addresses.push_back(xc7series::FrameAddress(
+ xc7series::BlockType::CLB_IO_CLK, false, 1, 0, 1));
+
+ xc7series::GlobalClockRegion global_clock_region(addresses.begin(),
+ addresses.end());
+
+ auto next_address =
+ global_clock_region.GetNextFrameAddress(xc7series::FrameAddress(
+ xc7series::BlockType::CLB_IO_CLK, false, 0, 0, 0));
+ ASSERT_TRUE(next_address);
+ EXPECT_EQ(*next_address,
+ xc7series::FrameAddress(xc7series::BlockType::CLB_IO_CLK,
+ false, 0, 0, 1));
+
+ next_address =
+ global_clock_region.GetNextFrameAddress(xc7series::FrameAddress(
+ xc7series::BlockType::CLB_IO_CLK, false, 0, 0, 1));
+ ASSERT_TRUE(next_address);
+ EXPECT_EQ(*next_address,
+ xc7series::FrameAddress(xc7series::BlockType::CLB_IO_CLK,
+ false, 0, 1, 0));
+
+ next_address =
+ global_clock_region.GetNextFrameAddress(xc7series::FrameAddress(
+ xc7series::BlockType::CLB_IO_CLK, false, 0, 1, 1));
+ ASSERT_TRUE(next_address);
+ EXPECT_EQ(*next_address,
+ xc7series::FrameAddress(xc7series::BlockType::CLB_IO_CLK,
+ false, 1, 0, 0));
+
+ next_address =
+ global_clock_region.GetNextFrameAddress(xc7series::FrameAddress(
+ xc7series::BlockType::BLOCK_RAM, false, 0, 0, 1));
+ ASSERT_TRUE(next_address);
+ EXPECT_EQ(*next_address,
+ xc7series::FrameAddress(xc7series::BlockType::BLOCK_RAM,
+ false, 0, 0, 2));
+}
+
+TEST(GlobalClockRegionTest,
+ GetNextFrameAddressYieldNothingAtEndOfGlobalClockRegion) {
+ std::vector<xc7series::FrameAddress> addresses;
+ addresses.push_back(xc7series::FrameAddress(
+ xc7series::BlockType::CLB_IO_CLK, false, 0, 0, 0));
+ addresses.push_back(xc7series::FrameAddress(
+ xc7series::BlockType::CLB_IO_CLK, false, 0, 0, 1));
+ addresses.push_back(xc7series::FrameAddress(
+ xc7series::BlockType::CLB_IO_CLK, false, 0, 1, 0));
+ addresses.push_back(xc7series::FrameAddress(
+ xc7series::BlockType::CLB_IO_CLK, false, 0, 1, 1));
+ addresses.push_back(xc7series::FrameAddress(
+ xc7series::BlockType::BLOCK_RAM, false, 0, 0, 0));
+ addresses.push_back(xc7series::FrameAddress(
+ xc7series::BlockType::BLOCK_RAM, false, 0, 0, 1));
+ addresses.push_back(xc7series::FrameAddress(
+ xc7series::BlockType::BLOCK_RAM, false, 0, 0, 2));
+ addresses.push_back(xc7series::FrameAddress(
+ xc7series::BlockType::CLB_IO_CLK, false, 1, 0, 0));
+ addresses.push_back(xc7series::FrameAddress(
+ xc7series::BlockType::CLB_IO_CLK, false, 1, 0, 1));
+
+ xc7series::GlobalClockRegion global_clock_region(addresses.begin(),
+ addresses.end());
+
+ EXPECT_FALSE(
+ global_clock_region.GetNextFrameAddress(xc7series::FrameAddress(
+ xc7series::BlockType::CLB_IO_CLK, false, 1, 0, 1)));
+ EXPECT_FALSE(
+ global_clock_region.GetNextFrameAddress(xc7series::FrameAddress(
+ xc7series::BlockType::BLOCK_RAM, false, 0, 0, 2)));
+}
diff --git a/lib/xilinx/tests/xc7series/part_test.cc b/lib/xilinx/tests/xc7series/part_test.cc
new file mode 100644
index 0000000..16b7e99
--- /dev/null
+++ b/lib/xilinx/tests/xc7series/part_test.cc
@@ -0,0 +1,149 @@
+#include <prjxray/xilinx/xc7series/part.h>
+
+#include <gtest/gtest.h>
+
+using namespace prjxray::xilinx;
+
+TEST(PartTest, IsValidFrameAddress) {
+ std::vector<xc7series::FrameAddress> addresses;
+ addresses.push_back(xc7series::FrameAddress(
+ xc7series::BlockType::CLB_IO_CLK, false, 0, 0, 0));
+ addresses.push_back(xc7series::FrameAddress(
+ xc7series::BlockType::CLB_IO_CLK, false, 0, 0, 1));
+ addresses.push_back(xc7series::FrameAddress(
+ xc7series::BlockType::CLB_IO_CLK, false, 0, 1, 0));
+ addresses.push_back(xc7series::FrameAddress(
+ xc7series::BlockType::CLB_IO_CLK, false, 0, 1, 1));
+ addresses.push_back(xc7series::FrameAddress(
+ xc7series::BlockType::BLOCK_RAM, false, 0, 0, 0));
+ addresses.push_back(xc7series::FrameAddress(
+ xc7series::BlockType::BLOCK_RAM, false, 0, 0, 1));
+ addresses.push_back(xc7series::FrameAddress(
+ xc7series::BlockType::BLOCK_RAM, false, 0, 0, 2));
+ addresses.push_back(xc7series::FrameAddress(
+ xc7series::BlockType::CLB_IO_CLK, false, 1, 0, 0));
+ addresses.push_back(xc7series::FrameAddress(
+ xc7series::BlockType::CLB_IO_CLK, false, 1, 0, 1));
+ addresses.push_back(xc7series::FrameAddress(
+ xc7series::BlockType::CLB_IO_CLK, true, 0, 0, 0));
+ addresses.push_back(xc7series::FrameAddress(
+ xc7series::BlockType::CLB_IO_CLK, true, 0, 0, 1));
+
+ xc7series::Part part(0x1234, addresses.begin(), addresses.end());
+
+ EXPECT_TRUE(part.IsValidFrameAddress(xc7series::FrameAddress(
+ xc7series::BlockType::CLB_IO_CLK, false, 0, 0, 0)));
+ EXPECT_TRUE(part.IsValidFrameAddress(xc7series::FrameAddress(
+ xc7series::BlockType::CLB_IO_CLK, false, 0, 1, 0)));
+ EXPECT_TRUE(part.IsValidFrameAddress(xc7series::FrameAddress(
+ xc7series::BlockType::BLOCK_RAM, false, 0, 0, 2)));
+ EXPECT_TRUE(part.IsValidFrameAddress(xc7series::FrameAddress(
+ xc7series::BlockType::CLB_IO_CLK, false, 1, 0, 0)));
+ EXPECT_TRUE(part.IsValidFrameAddress(xc7series::FrameAddress(
+ xc7series::BlockType::CLB_IO_CLK, true, 0, 0, 0)));
+
+ EXPECT_FALSE(part.IsValidFrameAddress(xc7series::FrameAddress(
+ xc7series::BlockType::CLB_IO_CLK, false, 0, 0, 2)));
+ EXPECT_FALSE(part.IsValidFrameAddress(xc7series::FrameAddress(
+ xc7series::BlockType::CLB_IO_CLK, false, 0, 2, 0)));
+ EXPECT_FALSE(part.IsValidFrameAddress(xc7series::FrameAddress(
+ xc7series::BlockType::CLB_IO_CLK, false, 2, 0, 0)));
+ EXPECT_FALSE(part.IsValidFrameAddress(xc7series::FrameAddress(
+ xc7series::BlockType::CFG_CLB, false, 0, 0, 2)));
+ EXPECT_FALSE(part.IsValidFrameAddress(xc7series::FrameAddress(
+ xc7series::BlockType::CLB_IO_CLK, true, 0, 1, 0)));
+}
+
+TEST(PartTest, GetNextFrameAddressYieldNextAddressInPart) {
+ std::vector<xc7series::FrameAddress> addresses;
+ addresses.push_back(xc7series::FrameAddress(
+ xc7series::BlockType::CLB_IO_CLK, false, 0, 0, 0));
+ addresses.push_back(xc7series::FrameAddress(
+ xc7series::BlockType::CLB_IO_CLK, false, 0, 0, 1));
+ addresses.push_back(xc7series::FrameAddress(
+ xc7series::BlockType::CLB_IO_CLK, false, 0, 1, 0));
+ addresses.push_back(xc7series::FrameAddress(
+ xc7series::BlockType::CLB_IO_CLK, false, 0, 1, 1));
+ addresses.push_back(xc7series::FrameAddress(
+ xc7series::BlockType::BLOCK_RAM, false, 0, 0, 0));
+ addresses.push_back(xc7series::FrameAddress(
+ xc7series::BlockType::BLOCK_RAM, false, 0, 0, 1));
+ addresses.push_back(xc7series::FrameAddress(
+ xc7series::BlockType::BLOCK_RAM, false, 0, 0, 2));
+ addresses.push_back(xc7series::FrameAddress(
+ xc7series::BlockType::CLB_IO_CLK, false, 1, 0, 0));
+ addresses.push_back(xc7series::FrameAddress(
+ xc7series::BlockType::CLB_IO_CLK, false, 1, 0, 1));
+ addresses.push_back(xc7series::FrameAddress(
+ xc7series::BlockType::CLB_IO_CLK, true, 0, 0, 0));
+ addresses.push_back(xc7series::FrameAddress(
+ xc7series::BlockType::CLB_IO_CLK, true, 0, 0, 1));
+
+ xc7series::Part part(0x1234, addresses.begin(), addresses.end());
+
+ auto next_address = part.GetNextFrameAddress(xc7series::FrameAddress(
+ xc7series::BlockType::CLB_IO_CLK, false, 0, 0, 0));
+ ASSERT_TRUE(next_address);
+ EXPECT_EQ(*next_address,
+ xc7series::FrameAddress(xc7series::BlockType::CLB_IO_CLK,
+ false, 0, 0, 1));
+
+ next_address = part.GetNextFrameAddress(xc7series::FrameAddress(
+ xc7series::BlockType::CLB_IO_CLK, false, 0, 0, 1));
+ ASSERT_TRUE(next_address);
+ EXPECT_EQ(*next_address,
+ xc7series::FrameAddress(xc7series::BlockType::CLB_IO_CLK,
+ false, 0, 1, 0));
+
+ next_address = part.GetNextFrameAddress(xc7series::FrameAddress(
+ xc7series::BlockType::CLB_IO_CLK, false, 0, 1, 1));
+ ASSERT_TRUE(next_address);
+ EXPECT_EQ(*next_address,
+ xc7series::FrameAddress(xc7series::BlockType::CLB_IO_CLK,
+ false, 1, 0, 0));
+
+ next_address = part.GetNextFrameAddress(xc7series::FrameAddress(
+ xc7series::BlockType::CLB_IO_CLK, false, 1, 0, 1));
+ ASSERT_TRUE(next_address);
+ EXPECT_EQ(*next_address,
+ xc7series::FrameAddress(xc7series::BlockType::CLB_IO_CLK,
+ true, 0, 0, 0));
+
+ next_address = part.GetNextFrameAddress(xc7series::FrameAddress(
+ xc7series::BlockType::CLB_IO_CLK, true, 0, 0, 1));
+ ASSERT_TRUE(next_address);
+ EXPECT_EQ(*next_address,
+ xc7series::FrameAddress(xc7series::BlockType::BLOCK_RAM,
+ false, 0, 0, 0));
+}
+
+TEST(PartTest, GetNextFrameAddressYieldNothingAtEndOfPart) {
+ std::vector<xc7series::FrameAddress> addresses;
+ addresses.push_back(xc7series::FrameAddress(
+ xc7series::BlockType::CLB_IO_CLK, false, 0, 0, 0));
+ addresses.push_back(xc7series::FrameAddress(
+ xc7series::BlockType::CLB_IO_CLK, false, 0, 0, 1));
+ addresses.push_back(xc7series::FrameAddress(
+ xc7series::BlockType::CLB_IO_CLK, false, 0, 1, 0));
+ addresses.push_back(xc7series::FrameAddress(
+ xc7series::BlockType::CLB_IO_CLK, false, 0, 1, 1));
+ addresses.push_back(xc7series::FrameAddress(
+ xc7series::BlockType::BLOCK_RAM, false, 0, 0, 0));
+ addresses.push_back(xc7series::FrameAddress(
+ xc7series::BlockType::BLOCK_RAM, false, 0, 0, 1));
+ addresses.push_back(xc7series::FrameAddress(
+ xc7series::BlockType::BLOCK_RAM, false, 0, 0, 2));
+ addresses.push_back(xc7series::FrameAddress(
+ xc7series::BlockType::CLB_IO_CLK, false, 1, 0, 0));
+ addresses.push_back(xc7series::FrameAddress(
+ xc7series::BlockType::CLB_IO_CLK, false, 1, 0, 1));
+ addresses.push_back(xc7series::FrameAddress(
+ xc7series::BlockType::CLB_IO_CLK, true, 0, 0, 0));
+ addresses.push_back(xc7series::FrameAddress(
+ xc7series::BlockType::CLB_IO_CLK, true, 0, 0, 1));
+
+ xc7series::Part part(0x1234, addresses.begin(), addresses.end());
+
+ EXPECT_FALSE(part.GetNextFrameAddress(xc7series::FrameAddress(
+ xc7series::BlockType::BLOCK_RAM, false, 0, 0, 2)));
+}
diff --git a/lib/xilinx/tests/xc7series/row_test.cc b/lib/xilinx/tests/xc7series/row_test.cc
new file mode 100644
index 0000000..e8f7318
--- /dev/null
+++ b/lib/xilinx/tests/xc7series/row_test.cc
@@ -0,0 +1,115 @@
+#include <prjxray/xilinx/xc7series/configuration_row.h>
+
+#include <gtest/gtest.h>
+
+using namespace prjxray::xilinx;
+
+TEST(RowTest, IsValidFrameAddress) {
+ std::vector<xc7series::FrameAddress> addresses;
+ addresses.push_back(xc7series::FrameAddress(
+ xc7series::BlockType::CLB_IO_CLK, false, 0, 0, 0));
+ addresses.push_back(xc7series::FrameAddress(
+ xc7series::BlockType::CLB_IO_CLK, false, 0, 0, 1));
+ addresses.push_back(xc7series::FrameAddress(
+ xc7series::BlockType::CLB_IO_CLK, false, 0, 1, 0));
+ addresses.push_back(xc7series::FrameAddress(
+ xc7series::BlockType::CLB_IO_CLK, false, 0, 1, 1));
+ addresses.push_back(xc7series::FrameAddress(
+ xc7series::BlockType::BLOCK_RAM, false, 0, 0, 0));
+ addresses.push_back(xc7series::FrameAddress(
+ xc7series::BlockType::BLOCK_RAM, false, 0, 0, 1));
+ addresses.push_back(xc7series::FrameAddress(
+ xc7series::BlockType::BLOCK_RAM, false, 0, 0, 2));
+
+ xc7series::Row row(addresses.begin(), addresses.end());
+
+ EXPECT_TRUE(row.IsValidFrameAddress(xc7series::FrameAddress(
+ xc7series::BlockType::CLB_IO_CLK, false, 0, 0, 0)));
+ EXPECT_TRUE(row.IsValidFrameAddress(xc7series::FrameAddress(
+ xc7series::BlockType::CLB_IO_CLK, false, 0, 1, 0)));
+ EXPECT_TRUE(row.IsValidFrameAddress(xc7series::FrameAddress(
+ xc7series::BlockType::BLOCK_RAM, false, 0, 0, 2)));
+
+ EXPECT_FALSE(row.IsValidFrameAddress(xc7series::FrameAddress(
+ xc7series::BlockType::CLB_IO_CLK, false, 0, 0, 2)));
+ EXPECT_FALSE(row.IsValidFrameAddress(xc7series::FrameAddress(
+ xc7series::BlockType::CLB_IO_CLK, false, 0, 2, 0)));
+}
+
+TEST(RowTest, GetNextFrameAddressYieldNextAddressInRow) {
+ std::vector<xc7series::FrameAddress> addresses;
+ addresses.push_back(xc7series::FrameAddress(
+ xc7series::BlockType::CLB_IO_CLK, false, 0, 0, 0));
+ addresses.push_back(xc7series::FrameAddress(
+ xc7series::BlockType::CLB_IO_CLK, false, 0, 0, 1));
+ addresses.push_back(xc7series::FrameAddress(
+ xc7series::BlockType::CLB_IO_CLK, false, 0, 1, 0));
+ addresses.push_back(xc7series::FrameAddress(
+ xc7series::BlockType::CLB_IO_CLK, false, 0, 1, 1));
+ addresses.push_back(xc7series::FrameAddress(
+ xc7series::BlockType::BLOCK_RAM, false, 0, 0, 0));
+ addresses.push_back(xc7series::FrameAddress(
+ xc7series::BlockType::BLOCK_RAM, false, 0, 0, 1));
+ addresses.push_back(xc7series::FrameAddress(
+ xc7series::BlockType::BLOCK_RAM, false, 0, 0, 2));
+
+ xc7series::Row row(addresses.begin(), addresses.end());
+
+ auto next_address = row.GetNextFrameAddress(xc7series::FrameAddress(
+ xc7series::BlockType::CLB_IO_CLK, false, 0, 0, 0));
+ ASSERT_TRUE(next_address);
+ EXPECT_EQ(*next_address,
+ xc7series::FrameAddress(xc7series::BlockType::CLB_IO_CLK,
+ false, 0, 0, 1));
+
+ next_address = row.GetNextFrameAddress(xc7series::FrameAddress(
+ xc7series::BlockType::CLB_IO_CLK, false, 0, 0, 1));
+ ASSERT_TRUE(next_address);
+ EXPECT_EQ(*next_address,
+ xc7series::FrameAddress(xc7series::BlockType::CLB_IO_CLK,
+ false, 0, 1, 0));
+
+ // Rows have unique behavior for GetNextFrameAddress() at the end of a
+ // bus. Since the addresses need to be returned in numerically
+ // increasing order, all of the rows need to be returned before moving
+ // to a different bus. That means that Row::GetNextFrameAddress() needs
+ // to return no object at the end of a bus and let the caller use that
+ // as a signal to try the next row.
+ next_address = row.GetNextFrameAddress(xc7series::FrameAddress(
+ xc7series::BlockType::CLB_IO_CLK, false, 0, 1, 1));
+ EXPECT_FALSE(next_address);
+
+ next_address = row.GetNextFrameAddress(xc7series::FrameAddress(
+ xc7series::BlockType::BLOCK_RAM, false, 0, 0, 1));
+ ASSERT_TRUE(next_address);
+ EXPECT_EQ(*next_address,
+ xc7series::FrameAddress(xc7series::BlockType::BLOCK_RAM,
+ false, 0, 0, 2));
+
+ next_address = row.GetNextFrameAddress(xc7series::FrameAddress(
+ xc7series::BlockType::BLOCK_RAM, false, 0, 0, 2));
+ EXPECT_FALSE(next_address);
+}
+
+TEST(RowTest, GetNextFrameAddressYieldNothingAtEndOfRow) {
+ std::vector<xc7series::FrameAddress> addresses;
+ addresses.push_back(xc7series::FrameAddress(
+ xc7series::BlockType::CLB_IO_CLK, false, 0, 0, 0));
+ addresses.push_back(xc7series::FrameAddress(
+ xc7series::BlockType::CLB_IO_CLK, false, 0, 0, 1));
+ addresses.push_back(xc7series::FrameAddress(
+ xc7series::BlockType::CLB_IO_CLK, false, 0, 1, 0));
+ addresses.push_back(xc7series::FrameAddress(
+ xc7series::BlockType::CLB_IO_CLK, false, 0, 1, 1));
+ addresses.push_back(xc7series::FrameAddress(
+ xc7series::BlockType::BLOCK_RAM, false, 0, 0, 0));
+ addresses.push_back(xc7series::FrameAddress(
+ xc7series::BlockType::BLOCK_RAM, false, 0, 0, 1));
+ addresses.push_back(xc7series::FrameAddress(
+ xc7series::BlockType::BLOCK_RAM, false, 0, 0, 2));
+
+ xc7series::Row row(addresses.begin(), addresses.end());
+
+ EXPECT_FALSE(row.GetNextFrameAddress(xc7series::FrameAddress(
+ xc7series::BlockType::BLOCK_RAM, false, 0, 0, 2)));
+}
diff --git a/lib/xilinx/xc7series/block_type.cc b/lib/xilinx/xc7series/block_type.cc
new file mode 100644
index 0000000..d74537f
--- /dev/null
+++ b/lib/xilinx/xc7series/block_type.cc
@@ -0,0 +1,62 @@
+#include <prjxray/xilinx/xc7series/block_type.h>
+
+namespace prjxray {
+namespace xilinx {
+namespace xc7series {
+
+std::ostream& operator<<(std::ostream& o, BlockType value) {
+ switch (value) {
+ case BlockType::CLB_IO_CLK:
+ o << "CLB/IO/CLK";
+ break;
+ case BlockType::BLOCK_RAM:
+ o << "Block RAM";
+ break;
+ case BlockType::CFG_CLB:
+ o << "Config CLB";
+ break;
+ }
+
+ return o;
+}
+
+} // namespace xc7series
+} // namespace xilinx
+} // namespace prjxray
+
+namespace YAML {
+
+Node convert<prjxray::xilinx::xc7series::BlockType>::encode(
+ const prjxray::xilinx::xc7series::BlockType& rhs) {
+ switch (rhs) {
+ case prjxray::xilinx::xc7series::BlockType::CLB_IO_CLK:
+ return Node("CLB_IO_CLK");
+ case prjxray::xilinx::xc7series::BlockType::BLOCK_RAM:
+ return Node("BLOCK_RAM");
+ case prjxray::xilinx::xc7series::BlockType::CFG_CLB:
+ return Node("CFG_CLB");
+ default:
+ return Node(static_cast<unsigned int>(rhs));
+ }
+}
+
+bool YAML::convert<prjxray::xilinx::xc7series::BlockType>::decode(
+ const Node& node,
+ prjxray::xilinx::xc7series::BlockType& lhs) {
+ auto type_str = node.as<std::string>();
+
+ if (type_str == "CLB_IO_CLK") {
+ lhs = prjxray::xilinx::xc7series::BlockType::CLB_IO_CLK;
+ return true;
+ } else if (type_str == "BLOCK_RAM") {
+ lhs = prjxray::xilinx::xc7series::BlockType::BLOCK_RAM;
+ return true;
+ } else if (type_str == "CFG_CLB") {
+ lhs = prjxray::xilinx::xc7series::BlockType::CFG_CLB;
+ return true;
+ } else {
+ return false;
+ }
+}
+
+} // namespace YAML
diff --git a/lib/xilinx/xc7series/configuration_bus.cc b/lib/xilinx/xc7series/configuration_bus.cc
new file mode 100644
index 0000000..e0a2105
--- /dev/null
+++ b/lib/xilinx/xc7series/configuration_bus.cc
@@ -0,0 +1,77 @@
+#include <prjxray/xilinx/xc7series/configuration_bus.h>
+
+#include <iostream>
+
+namespace prjxray {
+namespace xilinx {
+namespace xc7series {
+
+bool ConfigurationBus::IsValidFrameAddress(FrameAddress address) const {
+ auto addr_column = configuration_columns_.find(address.column());
+ if (addr_column == configuration_columns_.end())
+ return false;
+
+ return addr_column->second.IsValidFrameAddress(address);
+}
+
+absl::optional<FrameAddress> ConfigurationBus::GetNextFrameAddress(
+ FrameAddress address) const {
+ // Find the column for the current address.
+ auto addr_column = configuration_columns_.find(address.column());
+
+ // If the current address isn't in a known column, no way to know the
+ // next address.
+ if (addr_column == configuration_columns_.end())
+ return {};
+
+ // Ask the column for the next address.
+ absl::optional<FrameAddress> next_address =
+ addr_column->second.GetNextFrameAddress(address);
+ if (next_address)
+ return next_address;
+
+ // The current column doesn't know what the next address is. Assume
+ // that the next valid address is the beginning of the next column.
+ if (++addr_column != configuration_columns_.end()) {
+ auto next_address = FrameAddress(
+ address.block_type(), address.is_bottom_half_rows(),
+ address.row(), addr_column->first, 0);
+ if (addr_column->second.IsValidFrameAddress(next_address))
+ return next_address;
+ }
+
+ // Not in this bus.
+ return {};
+}
+
+} // namespace xc7series
+} // namespace xilinx
+} // namespace prjxray
+
+namespace xc7series = prjxray::xilinx::xc7series;
+
+namespace YAML {
+
+Node convert<xc7series::ConfigurationBus>::encode(
+ const xc7series::ConfigurationBus& rhs) {
+ Node node;
+ node.SetTag("xilinx/xc7series/configuration_bus");
+ node["configuration_columns"] = rhs.configuration_columns_;
+ return node;
+}
+
+bool convert<xc7series::ConfigurationBus>::decode(
+ const Node& node,
+ xc7series::ConfigurationBus& lhs) {
+ if (!node.Tag().empty() &&
+ node.Tag() != "xilinx/xc7series/configuration_bus") {
+ return false;
+ }
+
+ lhs.configuration_columns_ =
+ node["configuration_columns"]
+ .as<std::map<unsigned int, xc7series::ConfigurationColumn>>();
+ return true;
+}
+
+} // namespace YAML
diff --git a/lib/xilinx/xc7series/configuration_column.cc b/lib/xilinx/xc7series/configuration_column.cc
new file mode 100644
index 0000000..f979383
--- /dev/null
+++ b/lib/xilinx/xc7series/configuration_column.cc
@@ -0,0 +1,52 @@
+#include <prjxray/xilinx/xc7series/configuration_column.h>
+
+namespace prjxray {
+namespace xilinx {
+namespace xc7series {
+
+bool ConfigurationColumn::IsValidFrameAddress(FrameAddress address) const {
+ return address.minor() < frame_count_;
+}
+
+absl::optional<FrameAddress> ConfigurationColumn::GetNextFrameAddress(
+ FrameAddress address) const {
+ if (!IsValidFrameAddress(address))
+ return {};
+
+ if (static_cast<unsigned int>(address.minor() + 1) < frame_count_) {
+ return address + 1;
+ }
+
+ // Next address is not in this column.
+ return {};
+}
+
+} // namespace xc7series
+} // namespace xilinx
+} // namespace prjxray
+
+namespace xc7series = prjxray::xilinx::xc7series;
+
+namespace YAML {
+
+Node convert<xc7series::ConfigurationColumn>::encode(
+ const xc7series::ConfigurationColumn& rhs) {
+ Node node;
+ node.SetTag("xilinx/xc7series/configuration_column");
+ node["frame_count"] = rhs.frame_count_;
+ return node;
+}
+
+bool convert<xc7series::ConfigurationColumn>::decode(
+ const Node& node,
+ xc7series::ConfigurationColumn& lhs) {
+ if (!node.Tag().empty() &&
+ node.Tag() != "xilinx/xc7series/configuration_column") {
+ return false;
+ }
+
+ lhs.frame_count_ = node["frame_count"].as<unsigned int>();
+ return true;
+}
+
+} // namespace YAML
diff --git a/lib/xilinx/xc7series/configuration_row.cc b/lib/xilinx/xc7series/configuration_row.cc
new file mode 100644
index 0000000..39ef9f4
--- /dev/null
+++ b/lib/xilinx/xc7series/configuration_row.cc
@@ -0,0 +1,62 @@
+#include <prjxray/xilinx/xc7series/configuration_row.h>
+
+namespace prjxray {
+namespace xilinx {
+namespace xc7series {
+
+bool Row::IsValidFrameAddress(FrameAddress address) const {
+ auto addr_bus = configuration_buses_.find(address.block_type());
+ if (addr_bus == configuration_buses_.end())
+ return false;
+ return addr_bus->second.IsValidFrameAddress(address);
+}
+
+absl::optional<FrameAddress> Row::GetNextFrameAddress(
+ FrameAddress address) const {
+ // Find the bus for the current address.
+ auto addr_bus = configuration_buses_.find(address.block_type());
+
+ // If the current address isn't in a known bus, no way to know the next.
+ if (addr_bus == configuration_buses_.end())
+ return {};
+
+ // Ask the bus for the next address.
+ absl::optional<FrameAddress> next_address =
+ addr_bus->second.GetNextFrameAddress(address);
+ if (next_address)
+ return next_address;
+
+ // The current bus doesn't know what the next address is. Rows come next
+ // in frame address numerical order so punt back to the caller to figure
+ // it out.
+ return {};
+}
+
+} // namespace xc7series
+} // namespace xilinx
+} // namespace prjxray
+
+namespace xc7series = prjxray::xilinx::xc7series;
+
+namespace YAML {
+
+Node convert<xc7series::Row>::encode(const xc7series::Row& rhs) {
+ Node node;
+ node.SetTag("xilinx/xc7series/row");
+ node["configuration_buses"] = rhs.configuration_buses_;
+ return node;
+}
+
+bool convert<xc7series::Row>::decode(const Node& node, xc7series::Row& lhs) {
+ if (!node.Tag().empty() && node.Tag() != "xilinx/xc7series/row") {
+ return false;
+ }
+
+ lhs.configuration_buses_ =
+ node["configuration_buses"]
+ .as<std::map<xc7series::BlockType,
+ xc7series::ConfigurationBus>>();
+ return true;
+}
+
+} // namespace YAML
diff --git a/lib/xilinx/xc7series/ecc.cc b/lib/xilinx/xc7series/ecc.cc
new file mode 100644
index 0000000..a3a5cb2
--- /dev/null
+++ b/lib/xilinx/xc7series/ecc.cc
@@ -0,0 +1,60 @@
+#include <prjxray/xilinx/xc7series/ecc.h>
+#include <cassert>
+#include <cstdio>
+
+namespace prjxray {
+namespace xilinx {
+namespace xc7series {
+
+constexpr size_t kECCFrameNumber = 0x32;
+
+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;
+}
+
+static uint32_t calculateECC(const std::vector<uint32_t>& data) {
+ uint32_t ecc = 0;
+ for (size_t ii = 0; ii < data.size(); ++ii) {
+ ecc = xc7series::icap_ecc(ii, data[ii], ecc);
+ }
+ return ecc;
+}
+
+void updateECC(std::vector<uint32_t>& data) {
+ assert(data.size() >= kECCFrameNumber);
+ // Replace the old ECC with the new.
+ data[kECCFrameNumber] &= 0xFFFFE000;
+ data[kECCFrameNumber] |= (calculateECC(data) & 0x1FFF);
+}
+
+} // namespace xc7series
+} // namespace xilinx
+} // namespace prjxray
diff --git a/lib/xilinx/xc7series/frame_address.cc b/lib/xilinx/xc7series/frame_address.cc
new file mode 100644
index 0000000..a4ae258
--- /dev/null
+++ b/lib/xilinx/xc7series/frame_address.cc
@@ -0,0 +1,99 @@
+#include <prjxray/xilinx/xc7series/frame_address.h>
+
+#include <iomanip>
+
+#include <prjxray/bit_ops.h>
+
+namespace prjxray {
+namespace xilinx {
+namespace xc7series {
+
+FrameAddress::FrameAddress(BlockType block_type,
+ bool is_bottom_half_rows,
+ uint8_t row,
+ uint16_t column,
+ uint8_t minor) {
+ address_ = bit_field_set(0, 25, 23, block_type);
+ address_ = bit_field_set(address_, 22, 22, is_bottom_half_rows);
+ address_ = bit_field_set(address_, 21, 17, row);
+ address_ = bit_field_set(address_, 16, 7, column);
+ address_ = bit_field_set(address_, 6, 0, minor);
+}
+
+BlockType FrameAddress::block_type() const {
+ return static_cast<BlockType>(bit_field_get(address_, 25, 23));
+}
+
+bool FrameAddress::is_bottom_half_rows() const {
+ return bit_field_get(address_, 22, 22);
+}
+
+uint8_t FrameAddress::row() const {
+ return bit_field_get(address_, 21, 17);
+}
+
+uint16_t FrameAddress::column() const {
+ return bit_field_get(address_, 16, 7);
+}
+
+uint8_t FrameAddress::minor() const {
+ return bit_field_get(address_, 6, 0);
+}
+
+std::ostream& operator<<(std::ostream& o, const FrameAddress& addr) {
+ o << "[" << std::hex << std::showbase << std::setw(10)
+ << static_cast<uint32_t>(addr) << "] "
+ << (addr.is_bottom_half_rows() ? "BOTTOM" : "TOP")
+ << " Row=" << std::setw(2) << std::dec
+ << static_cast<unsigned int>(addr.row()) << " Column=" << std::setw(2)
+ << std::dec << addr.column() << " Minor=" << std::setw(2) << std::dec
+ << static_cast<unsigned int>(addr.minor())
+ << " Type=" << addr.block_type();
+ return o;
+}
+
+} // namespace xc7series
+} // namespace xilinx
+} // namespace prjxray
+
+namespace YAML {
+
+namespace xc7series = prjxray::xilinx::xc7series;
+
+Node convert<xc7series::FrameAddress>::encode(
+ const xc7series::FrameAddress& rhs) {
+ Node node;
+ node.SetTag("xilinx/xc7series/frame_address");
+ node["block_type"] = rhs.block_type();
+ node["row_half"] = (rhs.is_bottom_half_rows() ? "bottom" : "top");
+ node["row"] = static_cast<unsigned int>(rhs.row());
+ node["column"] = static_cast<unsigned int>(rhs.column());
+ node["minor"] = static_cast<unsigned int>(rhs.minor());
+ return node;
+}
+
+bool convert<xc7series::FrameAddress>::decode(const Node& node,
+ xc7series::FrameAddress& lhs) {
+ if (!(node.Tag() == "xilinx/xc7series/frame_address" ||
+ node.Tag() == "xilinx/xc7series/configuration_frame_address") ||
+ !node["block_type"] || !node["row_half"] || !node["row"] ||
+ !node["column"] || !node["minor"])
+ return false;
+
+ bool row_half;
+ if (node["row_half"].as<std::string>() == "top") {
+ row_half = false;
+ } else if (node["row_half"].as<std::string>() == "bottom") {
+ row_half = true;
+ } else {
+ return false;
+ }
+
+ lhs = prjxray::xilinx::xc7series::FrameAddress(
+ node["block_type"].as<xc7series::BlockType>(), row_half,
+ node["row"].as<unsigned int>(), node["column"].as<unsigned int>(),
+ node["minor"].as<unsigned int>());
+ return true;
+}
+
+} // namespace YAML
diff --git a/lib/xilinx/xc7series/global_clock_region.cc b/lib/xilinx/xc7series/global_clock_region.cc
new file mode 100644
index 0000000..638cb46
--- /dev/null
+++ b/lib/xilinx/xc7series/global_clock_region.cc
@@ -0,0 +1,71 @@
+#include <prjxray/xilinx/xc7series/global_clock_region.h>
+
+namespace prjxray {
+namespace xilinx {
+namespace xc7series {
+
+bool GlobalClockRegion::IsValidFrameAddress(FrameAddress address) const {
+ auto addr_row = rows_.find(address.row());
+ if (addr_row == rows_.end())
+ return false;
+ return addr_row->second.IsValidFrameAddress(address);
+}
+
+absl::optional<FrameAddress> GlobalClockRegion::GetNextFrameAddress(
+ FrameAddress address) const {
+ // Find the row for the current address.
+ auto addr_row = rows_.find(address.row());
+
+ // If the current address isn't in a known row, no way to know the next.
+ if (addr_row == rows_.end())
+ return {};
+
+ // Ask the row for the next address.
+ absl::optional<FrameAddress> next_address =
+ addr_row->second.GetNextFrameAddress(address);
+ if (next_address)
+ return next_address;
+
+ // The current row doesn't know what the next address is. Assume that
+ // the next valid address is the beginning of the next row.
+ if (++addr_row != rows_.end()) {
+ auto next_address = FrameAddress(address.block_type(),
+ address.is_bottom_half_rows(),
+ addr_row->first, 0, 0);
+ if (addr_row->second.IsValidFrameAddress(next_address))
+ return next_address;
+ }
+
+ // Must be in a different global clock region.
+ return {};
+}
+
+} // namespace xc7series
+} // namespace xilinx
+} // namespace prjxray
+
+namespace xc7series = prjxray::xilinx::xc7series;
+
+namespace YAML {
+
+Node convert<xc7series::GlobalClockRegion>::encode(
+ const xc7series::GlobalClockRegion& rhs) {
+ Node node;
+ node.SetTag("xilinx/xc7series/global_clock_region");
+ node["rows"] = rhs.rows_;
+ return node;
+}
+
+bool convert<xc7series::GlobalClockRegion>::decode(
+ const Node& node,
+ xc7series::GlobalClockRegion& lhs) {
+ if (!node.Tag().empty() &&
+ node.Tag() != "xilinx/xc7series/global_clock_region") {
+ return false;
+ }
+
+ lhs.rows_ = node["rows"].as<std::map<unsigned int, xc7series::Row>>();
+ return true;
+}
+
+} // namespace YAML
diff --git a/lib/xilinx/xc7series/part.cc b/lib/xilinx/xc7series/part.cc
new file mode 100644
index 0000000..6bfd139
--- /dev/null
+++ b/lib/xilinx/xc7series/part.cc
@@ -0,0 +1,115 @@
+#include <prjxray/xilinx/xc7series/part.h>
+
+#include <iomanip>
+#include <sstream>
+
+namespace prjxray {
+namespace xilinx {
+namespace xc7series {
+
+absl::optional<Part> Part::FromFile(const std::string& path) {
+ try {
+ YAML::Node yaml = YAML::LoadFile(path);
+ return yaml.as<Part>();
+ } catch (YAML::Exception& e) {
+ return {};
+ }
+}
+
+bool Part::IsValidFrameAddress(FrameAddress address) const {
+ if (address.is_bottom_half_rows()) {
+ return bottom_region_.IsValidFrameAddress(address);
+ } else {
+ return top_region_.IsValidFrameAddress(address);
+ }
+}
+
+absl::optional<FrameAddress> Part::GetNextFrameAddress(
+ FrameAddress address) const {
+ // Ask the current global clock region first.
+ absl::optional<FrameAddress> next_address =
+ (address.is_bottom_half_rows()
+ ? bottom_region_.GetNextFrameAddress(address)
+ : top_region_.GetNextFrameAddress(address));
+ if (next_address)
+ return next_address;
+
+ // If the current address is in the top region, the bottom region is
+ // next numerically.
+ if (!address.is_bottom_half_rows()) {
+ next_address =
+ FrameAddress(address.block_type(), true, 0, 0, 0);
+ if (bottom_region_.IsValidFrameAddress(*next_address))
+ return next_address;
+ }
+
+ // Block types are next numerically.
+ if (address.block_type() < BlockType::BLOCK_RAM) {
+ next_address =
+ FrameAddress(BlockType::BLOCK_RAM, false, 0, 0, 0);
+ if (IsValidFrameAddress(*next_address))
+ return next_address;
+ }
+
+ if (address.block_type() < BlockType::CFG_CLB) {
+ next_address = FrameAddress(BlockType::CFG_CLB, false, 0, 0, 0);
+ if (IsValidFrameAddress(*next_address))
+ return next_address;
+ }
+
+ return {};
+}
+
+} // namespace xc7series
+} // namespace xilinx
+} // namespace prjxray
+
+namespace xc7series = prjxray::xilinx::xc7series;
+
+namespace YAML {
+
+Node convert<xc7series::Part>::encode(const xc7series::Part& rhs) {
+ Node node;
+ node.SetTag("xilinx/xc7series/part");
+
+ std::ostringstream idcode_str;
+ idcode_str << "0x" << std::hex << rhs.idcode_;
+ node["idcode"] = idcode_str.str();
+ node["global_clock_regions"]["top"] = rhs.top_region_;
+ node["global_clock_regions"]["bottom"] = rhs.bottom_region_;
+ return node;
+}
+
+bool convert<xc7series::Part>::decode(const Node& node, xc7series::Part& lhs) {
+ if (!node.Tag().empty() && node.Tag() != "xilinx/xc7series/part")
+ return false;
+
+ if (!node["global_clock_regions"] && !node["configuration_ranges"]) {
+ return false;
+ }
+
+ lhs.idcode_ = node["idcode"].as<uint32_t>();
+
+ if (node["global_clock_regions"]) {
+ lhs.top_region_ = node["global_clock_regions"]["top"]
+ .as<xc7series::GlobalClockRegion>();
+ lhs.bottom_region_ = node["global_clock_regions"]["bottom"]
+ .as<xc7series::GlobalClockRegion>();
+ } else if (node["configuration_ranges"]) {
+ std::vector<xc7series::FrameAddress> addresses;
+ for (auto range : node["configuration_ranges"]) {
+ auto begin =
+ range["begin"].as<xc7series::FrameAddress>();
+ auto end = range["end"].as<xc7series::FrameAddress>();
+ for (uint32_t cur = begin; cur < end; ++cur) {
+ addresses.push_back(cur);
+ }
+ }
+
+ lhs = xc7series::Part(lhs.idcode_, addresses);
+ }
+
+ return true;
+};
+
+} // namespace YAML
diff --git a/third_party/abseil-cpp b/third_party/abseil-cpp
new file mode 160000
index 0000000..a2e6ade
--- /dev/null
+++ b/third_party/abseil-cpp
@@ -0,0 +1 @@
+Subproject commit a2e6adecc294dc4cd98cc285a9134ce58e0f2ad0
diff --git a/third_party/cctz b/third_party/cctz
new file mode 160000
index 0000000..a26bc5f
--- /dev/null
+++ b/third_party/cctz
@@ -0,0 +1 @@
+Subproject commit a26bc5f285a736a05cb974e063bfa26b33f07c78
diff --git a/third_party/gflags b/third_party/gflags
new file mode 160000
index 0000000..2e227c3
--- /dev/null
+++ b/third_party/gflags
@@ -0,0 +1 @@
+Subproject commit 2e227c3daae2ea8899f49858a23f3d318ea39b57
diff --git a/third_party/googletest b/third_party/googletest
new file mode 160000
index 0000000..8b4817e
--- /dev/null
+++ b/third_party/googletest
@@ -0,0 +1 @@
+Subproject commit 8b4817e3df3746a20502a84580f661ac448821be
diff --git a/third_party/python-sdf-timing b/third_party/python-sdf-timing
new file mode 160000
index 0000000..f6e72dd
--- /dev/null
+++ b/third_party/python-sdf-timing
@@ -0,0 +1 @@
+Subproject commit f6e72dd645010f7c38c57176e635caac699f427b
diff --git a/third_party/sanitizers-cmake b/third_party/sanitizers-cmake
new file mode 160000
index 0000000..99e159e
--- /dev/null
+++ b/third_party/sanitizers-cmake
@@ -0,0 +1 @@
+Subproject commit 99e159ec9bc8dd362b08d18436bd40ff0648417b
diff --git a/third_party/yaml-cpp b/third_party/yaml-cpp
new file mode 160000
index 0000000..587b24e
--- /dev/null
+++ b/third_party/yaml-cpp
@@ -0,0 +1 @@
+Subproject commit 587b24e2eedea1afa21d79419008ca5f7bda3bf4
diff --git a/third_party/yosys b/third_party/yosys
new file mode 160000
index 0000000..2bda51a
--- /dev/null
+++ b/third_party/yosys
@@ -0,0 +1 @@
+Subproject commit 2bda51ac34d6f542d1d6477eecede1d6527c10b3
diff --git a/tools/CMakeLists.txt b/tools/CMakeLists.txt
new file mode 100644
index 0000000..2ebadf1
--- /dev/null
+++ b/tools/CMakeLists.txt
@@ -0,0 +1,35 @@
+add_executable(bitread bitread.cc)
+target_link_libraries(bitread absl::optional absl::strings gflags libprjxray)
+
+add_executable(segmatch segmatch.cc)
+target_link_libraries(segmatch gflags absl::strings)
+
+add_executable(bittool bittool.cc)
+target_link_libraries(bittool libprjxray gflags absl::strings absl::span)
+
+add_executable(frame_address_decoder frame_address_decoder.cc)
+target_link_libraries(frame_address_decoder libprjxray)
+
+add_executable(gen_part_base_yaml gen_part_base_yaml.cc)
+target_link_libraries(gen_part_base_yaml
+ absl::optional
+ absl::span
+ libprjxray
+ yaml-cpp
+ gflags
+)
+add_executable(xc7patch xc7patch.cc)
+target_link_libraries(xc7patch
+ absl::strings
+ absl::time
+ gflags
+ libprjxray
+)
+
+add_executable(xc7frames2bit xc7frames2bit.cc)
+target_link_libraries(xc7frames2bit
+ absl::strings
+ absl::time
+ gflags
+ libprjxray
+)
diff --git a/tools/bitread.cc b/tools/bitread.cc
new file mode 100644
index 0000000..9cc7d06
--- /dev/null
+++ b/tools/bitread.cc
@@ -0,0 +1,323 @@
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+
+#include <iostream>
+#include <set>
+#include <string>
+#include <vector>
+
+#include <absl/strings/numbers.h>
+#include <absl/strings/str_cat.h>
+#include <absl/strings/str_split.h>
+#include <gflags/gflags.h>
+#include <prjxray/memory_mapped_file.h>
+#include <prjxray/xilinx/architectures.h>
+#include <prjxray/xilinx/bitstream_reader.h>
+#include <prjxray/xilinx/configuration.h>
+
+DEFINE_bool(c, false, "output '*' for repeating patterns");
+DEFINE_bool(C, false, "do not ignore the checksum in each frame");
+DEFINE_int32(f,
+ -1,
+ "only dump the specified frame (might be used more than once)");
+DEFINE_string(F,
+ "",
+ "<first_frame_address>:<last_frame_address> only dump frame in "
+ "the specified range");
+DEFINE_string(o, "", "write machine-readable output file with config frames");
+DEFINE_bool(p, false, "output a binary netpgm image");
+DEFINE_bool(x,
+ false,
+ "use format 'bit_%%08x_%%03d_%%02d_t%%d_h%%d_r%%d_c%%d_m%%d'\n"
+ "The fields have the following meaning:\n"
+ " - complete 32 bit hex frame id\n"
+ " - word index with that frame (decimal)\n"
+ " - bit index with that word (decimal)\n"
+ " - decoded frame type from frame id\n"
+ " - decoded top/botttom from frame id (top=0)\n"
+ " - decoded row address from frame id\n"
+ " - decoded column address from frame id\n"
+ " - decoded minor address from frame id\n");
+DEFINE_bool(y, false, "use format 'bit_%%08x_%%03d_%%02d'");
+DEFINE_bool(z, false, "skip zero frames (frames with all bits cleared) in o");
+DEFINE_string(part_file, "", "YAML file describing a Xilinx part");
+DEFINE_string(architecture,
+ "Series7",
+ "Architecture of the provided bitstream");
+
+namespace xilinx = prjxray::xilinx;
+
+std::set<uint32_t> frames;
+uint32_t frame_range_begin = 0, frame_range_end = 0;
+
+std::vector<uint32_t> zero_frame(101);
+
+struct BitReader {
+ BitReader(const std::vector<uint8_t>& bytes) : bytes_(bytes) {}
+
+ const std::vector<uint8_t>& bytes_;
+
+ template <typename T>
+ int operator()(T& arg) {
+ using ArchType = std::decay_t<decltype(arg)>;
+ auto reader =
+ xilinx::BitstreamReader<ArchType>::InitWithBytes(bytes_);
+ if (!reader) {
+ std::cerr << "Input doesn't look like a bitstream"
+ << std::endl;
+ return 1;
+ }
+
+ std::cout << "Config size: " << reader->words().size()
+ << " words" << std::endl;
+
+ auto part = ArchType::Part::FromFile(FLAGS_part_file);
+ if (!part) {
+ std::cerr << "Part file not found or invalid"
+ << std::endl;
+ return 1;
+ }
+ auto config = xilinx::Configuration<ArchType>::InitWithPackets(
+ *part, *reader);
+ if (!config) {
+ std::cerr
+ << "Bitstream does not appear to be for this part"
+ << std::endl;
+ return 1;
+ }
+
+ std::cout << "Number of configuration frames: "
+ << config->frames().size() << std::endl;
+
+ FILE* f = stdout;
+
+ if (!FLAGS_o.empty()) {
+ f = fopen(FLAGS_o.c_str(), "w");
+
+ if (f == nullptr) {
+ printf(
+ "Can't open output file '%s' for "
+ "writing!\n",
+ FLAGS_o.c_str());
+ return 1;
+ }
+ } else {
+ fprintf(f, "\n");
+ }
+
+ std::vector<std::vector<bool>> pgmdata;
+ std::vector<int> pgmsep;
+
+ int word_length = sizeof(typename ArchType::WordType) * 8;
+ for (auto& it : config->frames()) {
+ if (FLAGS_z && it.second == zero_frame)
+ continue;
+
+ if (!frames.empty() && !frames.count(it.first))
+ continue;
+
+ if (frame_range_begin != frame_range_end &&
+ (it.first < frame_range_begin ||
+ frame_range_end <= it.first))
+ continue;
+
+ if (FLAGS_o.empty())
+ printf(
+ "Frame 0x%08x (Type=%d Top=%d Row=%d "
+ "Column=%d "
+ "Minor=%d):\n",
+ static_cast<uint32_t>(it.first),
+ static_cast<unsigned int>(
+ it.first.block_type()),
+ it.first.is_bottom_half_rows() ? 1 : 0,
+ it.first.row(), it.first.column(),
+ it.first.minor());
+
+ if (FLAGS_p) {
+ if (it.first.minor() == 0 && !pgmdata.empty())
+ pgmsep.push_back(pgmdata.size());
+
+ pgmdata.push_back(std::vector<bool>());
+
+ for (size_t i = 0; i < it.second.size(); i++)
+ for (int k = 0; k < word_length; k++)
+ pgmdata.back().push_back(
+ (it.second.at(i) &
+ (1 << k)) != 0);
+ } else if (FLAGS_x || FLAGS_y) {
+ for (int i = 0; i < (int)it.second.size();
+ i++) {
+ for (int k = 0; k < word_length; k++) {
+ if (((i != 50 || k > 12 ||
+ FLAGS_C) ||
+ std::is_same<
+ ArchType,
+ prjxray::xilinx::
+ Spartan6>::
+ value) &&
+ ((it.second.at(i) &
+ (1 << k)) != 0)) {
+ if (FLAGS_x)
+ fprintf(
+ f,
+ "bit_%08x_%"
+ "03d_%"
+ "02d_t%d_h%"
+ "d_r%d_c%"
+ "d_m%d\n",
+ static_cast<
+ uint32_t>(
+ it.first),
+ i, k,
+ static_cast<
+ unsigned int>(
+ it.first
+ .block_type()),
+ it.first.is_bottom_half_rows()
+ ? 1
+ : 0,
+ it.first
+ .row(),
+ it.first
+ .column(),
+ it.first
+ .minor());
+ else
+ fprintf(
+ f,
+ "bit_%08x_%"
+ "03d_"
+ "%02d\n",
+ static_cast<
+ uint32_t>(
+ it.first),
+ i, k);
+ }
+ }
+ }
+ if (FLAGS_o.empty())
+ fprintf(f, "\n");
+ } else {
+ if (!FLAGS_o.empty())
+ fprintf(
+ f, ".frame 0x%08x\n",
+ static_cast<uint32_t>(it.first));
+
+ for (size_t i = 0; i < it.second.size(); i++)
+ if (std::is_same<ArchType,
+ prjxray::xilinx::
+ Spartan6>::value) {
+ fprintf(
+ f, "%08x%s",
+ it.second.at(i) &
+ 0xffffffff,
+ (i % 6) == 5 ? "\n" : " ");
+ } else {
+ fprintf(
+ f, "%08x%s",
+ it.second.at(i) &
+ ((i != 50 || FLAGS_C)
+ ? 0xffffffff
+ : 0xffffe000),
+ (i % 6) == 5 ? "\n" : " ");
+ }
+
+ fprintf(f, "\n\n");
+ }
+ }
+
+ if (FLAGS_p) {
+ int width = pgmdata.size() + pgmsep.size();
+ int height = ArchType::words_per_frame * word_length;
+ fprintf(f, "P5 %d %d 15\n", width, height);
+
+ for (int y = 0,
+ bit = ArchType::words_per_frame * word_length -
+ 1;
+ y < height; y++, bit--) {
+ for (int x = 0, frame = 0, sep = 0; x < width;
+ x++, frame++) {
+ if (sep < int(pgmsep.size()) &&
+ frame == pgmsep.at(sep)) {
+ fputc(8, f);
+ x++, sep++;
+ }
+
+ if (bit >=
+ (int)pgmdata.at(frame).size()) {
+ fputc(0, f);
+ continue;
+ }
+
+ fputc(
+ pgmdata.at(frame).at(bit) ? 15 : 0,
+ f);
+ }
+
+ if (bit % word_length == 0 && y) {
+ for (int x = 0; x < width; x++)
+ fputc(8, f);
+ y++;
+ }
+ }
+ }
+
+ if (!FLAGS_o.empty())
+ fclose(f);
+
+ printf("DONE\n");
+ return 0;
+ }
+};
+
+int main(int argc, char** argv) {
+ gflags::SetUsageMessage(
+ absl::StrCat("Usage: ", argv[0], " [options] [bitfile]"));
+ gflags::ParseCommandLineFlags(&argc, &argv, true);
+
+ if (FLAGS_f >= 0) {
+ frames.insert(FLAGS_f);
+ }
+
+ if (!FLAGS_F.empty()) {
+ std::pair<std::string, std::string> p =
+ absl::StrSplit(FLAGS_F, ":");
+ frame_range_begin = strtol(p.first.c_str(), nullptr, 0);
+ frame_range_end = strtol(p.second.c_str(), nullptr, 0) + 1;
+ }
+
+ std::vector<uint8_t> in_bytes;
+ if (argc == 2) {
+ auto in_file_name = argv[1];
+ auto in_file =
+ prjxray::MemoryMappedFile::InitWithFile(in_file_name);
+ if (!in_file) {
+ std::cerr << "Can't open input file '" << in_file_name
+ << "' for reading!" << std::endl;
+ return 1;
+ }
+
+ std::cout << "Bitstream size: " << in_file->size() << " bytes"
+ << std::endl;
+
+ in_bytes = std::vector<uint8_t>(
+ static_cast<uint8_t*>(in_file->data()),
+ static_cast<uint8_t*>(in_file->data()) + in_file->size());
+ } else {
+ while (1) {
+ int c = getchar();
+ if (c == EOF)
+ break;
+ in_bytes.push_back(c);
+ }
+
+ std::cout << "Bitstream size: " << in_bytes.size() << " bytes"
+ << std::endl;
+ }
+
+ xilinx::Architecture::Container arch_container =
+ xilinx::ArchitectureFactory::create_architecture(
+ FLAGS_architecture);
+ return absl::visit(BitReader(in_bytes), arch_container);
+}
diff --git a/tools/bittool.cc b/tools/bittool.cc
new file mode 100644
index 0000000..66d8a48
--- /dev/null
+++ b/tools/bittool.cc
@@ -0,0 +1,258 @@
+#include <algorithm>
+#include <iostream>
+#include <typeinfo>
+
+#include <absl/strings/str_cat.h>
+#include <absl/types/span.h>
+#include <gflags/gflags.h>
+#include <prjxray/memory_mapped_file.h>
+#include <prjxray/xilinx/architectures.h>
+#include <prjxray/xilinx/bitstream_reader.h>
+
+namespace xilinx = prjxray::xilinx;
+
+struct Action {
+ std::string name;
+ std::function<int(int, char* [])> handler;
+};
+
+struct ConfigPacketsLister {
+ ConfigPacketsLister(const absl::Span<uint8_t>& bytes) : bytes_(bytes) {}
+
+ const absl::Span<uint8_t>& bytes_;
+
+ template <typename T>
+ int operator()(T& arg) {
+ using ArchType = std::decay_t<decltype(arg)>;
+ auto reader =
+ xilinx::BitstreamReader<ArchType>::InitWithBytes(bytes_);
+ if (!reader) {
+ std::cerr << "Input doesn't look like a bitstream"
+ << std::endl;
+ return 1;
+ }
+
+ for (auto packet : *reader) {
+ std::cout << packet;
+ }
+
+ return 0;
+ }
+};
+
+int ListConfigPackets(int argc, char* argv[]) {
+ std::string architecture("Series7");
+ if (argc < 1) {
+ std::cerr << "ERROR: no input specified" << std::endl;
+ std::cerr << "Usage: " << argv[0]
+ << "list_config_packets <bit_file>" << std::endl;
+ return 1;
+ }
+ if (argc == 2) {
+ architecture = argv[1];
+ }
+
+ auto in_file_name = argv[0];
+ auto in_file = prjxray::MemoryMappedFile::InitWithFile(in_file_name);
+ if (!in_file) {
+ std::cerr << "Unable to open bit file: " << in_file_name
+ << std::endl;
+ return 1;
+ }
+ auto in_bytes = absl::Span<uint8_t>(
+ static_cast<uint8_t*>(in_file->data()), in_file->size());
+ xilinx::Architecture::Container arch_container =
+ xilinx::ArchitectureFactory::create_architecture(architecture);
+ return absl::visit(ConfigPacketsLister(in_bytes), arch_container);
+}
+
+struct DebugFrameAddressesDumper {
+ DebugFrameAddressesDumper(const absl::Span<uint8_t>& bytes)
+ : bytes_(bytes) {}
+
+ const absl::Span<uint8_t>& bytes_;
+
+ template <typename T>
+ int operator()(T& arg) {
+ using ArchType = std::decay_t<decltype(arg)>;
+ auto reader =
+ xilinx::BitstreamReader<ArchType>::InitWithBytes(bytes_);
+ if (!reader) {
+ std::cerr << "Input doesn't look like a bitstream"
+ << std::endl;
+ return 1;
+ }
+
+ bool found_one_lout = false;
+ for (auto packet : *reader) {
+ if ((packet.opcode() !=
+ xilinx::ConfigurationPacket<
+ typename ArchType::ConfRegType>::Opcode::
+ Write) ||
+ (packet.address() != ArchType::ConfRegType::LOUT)) {
+ continue;
+ }
+
+ if (packet.data().size() != 1) {
+ std::cerr << "Write to FAR with word_count != 1"
+ << std::endl;
+ continue;
+ }
+
+ found_one_lout = true;
+ std::cout << std::dec << packet.data()[0] << std::endl;
+ }
+
+ if (!found_one_lout) {
+ std::cerr
+ << "No LOUT writes found. Was "
+ << "BITSTREAM.GENERAL.DEBUGBITSTREAM set to YES?"
+ << std::endl;
+ return 1;
+ }
+
+ return 0;
+ }
+};
+
+int DumpDebugbitstreamFrameAddresses(int argc, char* argv[]) {
+ std::string architecture("Series7");
+ if (argc < 1) {
+ std::cerr << "ERROR: no input specified" << std::endl;
+ std::cerr << "Usage: " << argv[0]
+ << "dump_debugbitstream_frame_addresses <bit_file>"
+ << std::endl;
+ return 1;
+ }
+ if (argc == 2) {
+ architecture = argv[1];
+ }
+
+ auto in_file_name = argv[0];
+ auto in_file = prjxray::MemoryMappedFile::InitWithFile(in_file_name);
+ if (!in_file) {
+ std::cerr << "Unable to open bit file: " << in_file_name
+ << std::endl;
+ return 1;
+ }
+
+ auto in_bytes = absl::Span<uint8_t>(
+ static_cast<uint8_t*>(in_file->data()), in_file->size());
+ xilinx::Architecture::Container arch_container =
+ xilinx::ArchitectureFactory::create_architecture(architecture);
+ return absl::visit(DebugFrameAddressesDumper(in_bytes), arch_container);
+}
+
+struct DeviceIdGetter {
+ DeviceIdGetter(const absl::Span<uint8_t>& bytes) : bytes_(bytes) {}
+
+ const absl::Span<uint8_t>& bytes_;
+
+ template <typename T>
+ int operator()(T& arg) {
+ using ArchType = std::decay_t<decltype(arg)>;
+ auto reader =
+ xilinx::BitstreamReader<ArchType>::InitWithBytes(bytes_);
+ if (!reader) {
+ std::cerr << "Input doesn't look like a bitstream"
+ << std::endl;
+ return 1;
+ }
+ auto idcode_packet = std::find_if(
+ reader->begin(), reader->end(),
+ [](const xilinx::ConfigurationPacket<
+ typename ArchType::ConfRegType>& packet) {
+ return (packet.opcode() ==
+ xilinx::ConfigurationPacket<
+ typename ArchType::ConfRegType>::
+ Opcode::Write) &&
+ (packet.address() ==
+ ArchType::ConfRegType::IDCODE);
+ });
+ if (idcode_packet != reader->end()) {
+ if (std::is_same<ArchType, xilinx::Spartan6>::value) {
+ if (idcode_packet->data().size() != 2) {
+ std::cerr << "Write to IDCODE with "
+ "word_count != 2"
+ << std::endl;
+ return 1;
+ }
+ std::cout << "0x" << std::hex
+ << ((idcode_packet->data()[0] << 16) |
+ idcode_packet->data()[1])
+ << std::endl;
+ } else {
+ if (idcode_packet->data().size() != 1) {
+ std::cerr << "Write to IDCODE with "
+ "word_count != 1"
+ << std::endl;
+ return 1;
+ }
+ std::cout << "0x" << std::hex
+ << idcode_packet->data()[0]
+ << std::endl;
+ }
+ }
+ return 0;
+ }
+};
+
+int GetDeviceId(int argc, char* argv[]) {
+ std::string architecture("Series7");
+ if (argc < 1) {
+ std::cerr << "ERROR: no input specified" << std::endl;
+ std::cerr << "Usage: " << argv[0]
+ << "get_device_id <bit_file> [<architecture>]"
+ << std::endl;
+ return 1;
+ }
+ if (argc == 2) {
+ architecture = argv[1];
+ }
+
+ auto in_file_name = argv[0];
+ auto in_file = prjxray::MemoryMappedFile::InitWithFile(in_file_name);
+ if (!in_file) {
+ std::cerr << "Unable to open bit file: " << in_file_name
+ << std::endl;
+ return 1;
+ }
+ auto in_bytes = absl::Span<uint8_t>(
+ static_cast<uint8_t*>(in_file->data()), in_file->size());
+
+ xilinx::Architecture::Container arch_container =
+ xilinx::ArchitectureFactory::create_architecture(architecture);
+ return absl::visit(DeviceIdGetter(in_bytes), arch_container);
+}
+
+int main(int argc, char* argv[]) {
+ Action actions[] = {
+ {"list_config_packets", ListConfigPackets},
+ {"dump_debugbitstream_frame_addresses",
+ DumpDebugbitstreamFrameAddresses},
+ {"get_device_id", GetDeviceId},
+ };
+
+ gflags::SetUsageMessage(
+ absl::StrCat("Usage: ", argv[0], " [options] [bitfile]"));
+ gflags::ParseCommandLineFlags(&argc, &argv, true);
+
+ if (argc < 2) {
+ std::cerr << "ERROR: no command specified" << std::endl;
+ std::cerr << "Usage: " << argv[0]
+ << "<command> <command_options>" << std::endl;
+ return 1;
+ }
+
+ auto requested_action_str = argv[1];
+ auto requested_action = std::find_if(
+ std::begin(actions), std::end(actions),
+ [&](const Action& t) { return t.name == requested_action_str; });
+ if (requested_action == std::end(actions)) {
+ std::cerr << "Unknown action: " << requested_action_str
+ << std::endl;
+ return 1;
+ }
+
+ return requested_action->handler(argc - 2, argv + 2);
+}
diff --git a/tools/frame_address_decoder.cc b/tools/frame_address_decoder.cc
new file mode 100644
index 0000000..b3c21e9
--- /dev/null
+++ b/tools/frame_address_decoder.cc
@@ -0,0 +1,40 @@
+#include <fstream>
+#include <iomanip>
+#include <iostream>
+
+#include <prjxray/xilinx/xc7series/frame_address.h>
+
+namespace xc7series = prjxray::xilinx::xc7series;
+
+void frame_address_decode(std::istream* input_stream) {
+ for (uint32_t frame_address_raw;
+ (*input_stream) >> std::setbase(0) >> frame_address_raw;) {
+ xc7series::FrameAddress frame_address(frame_address_raw);
+ std::cout << "[" << std::hex << std::showbase << std::setw(10)
+ << frame_address_raw << "] "
+ << (frame_address.is_bottom_half_rows() ? "BOTTOM"
+ : "TOP")
+ << " Row=" << std::setw(2) << std::dec
+ << static_cast<unsigned int>(frame_address.row())
+ << " Column=" << std::setw(2) << std::dec
+ << frame_address.column() << " Minor=" << std::setw(2)
+ << std::dec
+ << static_cast<unsigned int>(frame_address.minor())
+ << " Type=" << frame_address.block_type()
+ << std::endl;
+ }
+}
+
+int main(int argc, char* argv[]) {
+ if (argc > 1) {
+ std::ifstream file_stream(argv[1]);
+ if (file_stream) {
+ frame_address_decode(&file_stream);
+ return 0;
+ }
+ }
+
+ frame_address_decode(&std::cin);
+
+ return 0;
+}
diff --git a/tools/gen_part_base_yaml.cc b/tools/gen_part_base_yaml.cc
new file mode 100644
index 0000000..e5f965f
--- /dev/null
+++ b/tools/gen_part_base_yaml.cc
@@ -0,0 +1,98 @@
+#include <libgen.h>
+
+#include <algorithm>
+#include <iostream>
+#include <map>
+#include <memory>
+#include <vector>
+
+#include <absl/strings/str_cat.h>
+#include <absl/types/optional.h>
+#include <absl/types/span.h>
+#include <gflags/gflags.h>
+#include <prjxray/memory_mapped_file.h>
+#include <prjxray/xilinx/architectures.h>
+#include <prjxray/xilinx/bitstream_reader.h>
+#include <yaml-cpp/yaml.h>
+
+DEFINE_bool(f, false, "Use FAR registers instead of LOUT ones");
+
+namespace xilinx = prjxray::xilinx;
+
+int main(int argc, char* argv[]) {
+ gflags::SetUsageMessage(
+ absl::StrCat("Usage: ", argv[0], " [bitfile] [options]"));
+ gflags::ParseCommandLineFlags(&argc, &argv, true);
+
+ if (argc < 2) {
+ std::cerr << "ERROR: no input specified" << std::endl;
+ std::cerr << "Usage: " << basename(argv[0]) << " <bit_file>"
+ << std::endl;
+ return 1;
+ }
+
+ auto in_file_name = argv[1];
+ auto in_file = prjxray::MemoryMappedFile::InitWithFile(in_file_name);
+ if (!in_file) {
+ std::cerr << "Unable to open bit file: " << in_file_name
+ << std::endl;
+ return 1;
+ }
+
+ auto reader = xilinx::BitstreamReader<xilinx::Series7>::InitWithBytes(
+ in_file->as_bytes());
+ if (!reader) {
+ std::cerr << "Input doesn't look like a bitstream" << std::endl;
+ return 1;
+ }
+
+ bool found_fdri_write = false;
+ std::vector<xilinx::Series7::FrameAddress> frame_addresses;
+ absl::optional<uint32_t> idcode;
+ for (auto packet : *reader) {
+ if (packet.opcode() !=
+ xilinx::ConfigurationPacket<
+ xilinx::Series7::ConfRegType>::Opcode::Write) {
+ continue;
+ }
+
+ if (packet.address() == xilinx::Series7::ConfRegType::FDRI) {
+ found_fdri_write = true;
+ } else if ((packet.address() ==
+ xilinx::Series7::ConfRegType::IDCODE) &&
+ packet.data().size() == 1) {
+ idcode = packet.data()[0];
+ } else if (found_fdri_write &&
+ (packet.address() ==
+ xilinx::Series7::ConfRegType::LOUT) &&
+ (packet.data().size() == 1) && FLAGS_f == false) {
+ frame_addresses.push_back(packet.data()[0]);
+ found_fdri_write = false;
+ } else if (found_fdri_write &&
+ (packet.address() ==
+ xilinx::Series7::ConfRegType::FAR) &&
+ (packet.data().size() == 1) && FLAGS_f == true) {
+ frame_addresses.push_back(packet.data()[0]);
+ found_fdri_write = false;
+ }
+ }
+
+ if (!idcode) {
+ std::cerr << "No IDCODE found." << std::endl;
+ return 1;
+ }
+
+ if (frame_addresses.empty()) {
+ std::cerr << "No LOUT or FAR writes found. Was "
+ << "BITSTREAM.GENERAL.DEBUGBITSTREAM or "
+ << "BITSTREAM.GENERAL.PERFRAMECRC set to YES?"
+ << std::endl;
+ return 1;
+ }
+
+ auto part = xilinx::Series7::Part(*idcode, frame_addresses.begin(),
+ frame_addresses.end());
+ std::cout << YAML::Node(part) << std::endl;
+
+ return 0;
+}
diff --git a/tools/segmatch.cc b/tools/segmatch.cc
new file mode 100644
index 0000000..128bca6
--- /dev/null
+++ b/tools/segmatch.cc
@@ -0,0 +1,387 @@
+#include <assert.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <algorithm>
+#include <fstream>
+#include <iostream>
+#include <map>
+#include <numeric>
+#include <set>
+#include <string>
+#include <vector>
+
+#include <absl/strings/str_cat.h>
+#include <gflags/gflags.h>
+
+DEFINE_int32(
+ c,
+ 4,
+ "threshold under which candidates are output. set to -1 to output all.");
+DEFINE_bool(i, false, "add inverted tags");
+DEFINE_int32(m, 0, "min number of set/cleared samples each");
+DEFINE_int32(M, 0, "min number of set/cleared samples total");
+DEFINE_string(o, "", "set output file");
+DEFINE_string(k, "", "set output mask file");
+
+using std::map;
+using std::string;
+using std::tuple;
+using std::vector;
+
+int num_bits = 0, num_tags = 0;
+map<string, int> bit_ids, tag_ids;
+vector<string> bit_ids_r, tag_ids_r;
+
+#if 0
+struct bool_vec
+{
+ vector<bool> data;
+
+ bool_vec(int n = 0, bool initval = false) : data(n, initval)
+ {
+ }
+
+ void set(int n)
+ {
+ if (int(data.size()) <= n)
+ data.resize(n+1);
+ data[n] = true;
+ }
+
+ bool get(int n)
+ {
+ return data.at(n);
+ }
+
+ void resize(int n)
+ {
+ data.resize(n);
+ }
+
+ int count()
+ {
+ return std::accumulate(data.begin(), data.end(), 0);
+ }
+
+ void apply_and(const bool_vec &other)
+ {
+ assert(data.size() == other.data.size());
+ for (int i = 0; i < int(data.size()); i++)
+ data[i] = data[i] && other.data[i];
+ }
+
+ void apply_andc(const bool_vec &other)
+ {
+ assert(data.size() == other.data.size());
+ for (int i = 0; i < int(data.size()); i++)
+ data[i] = data[i] && !other.data[i];
+ }
+};
+#else
+struct bool_vec {
+ vector<uint64_t> data;
+
+ bool_vec(int n = 0, bool initval = false)
+ : data((n + 63) / 64, initval ? ~uint64_t(0) : uint64_t(0)) {
+ for (int i = data.size() * 64 - 1; i >= n; i--)
+ data[n / 64] &= ~(uint64_t(1) << (n % 64));
+ }
+
+ void set(int n) {
+ if (int(data.size() * 64) <= n)
+ data.resize((n + 64) / 64);
+ data[n / 64] |= uint64_t(1) << (n % 64);
+ }
+
+ bool get(int n) { return (data[n / 64] >> (n % 64)) & 1; }
+
+ void resize(int n) { data.resize((n + 63) / 64); }
+
+ int count() {
+ int sum = 0;
+ for (int i = 0; i < 64 * int(data.size()); i++)
+ if (get(i))
+ sum++;
+ return sum;
+ }
+
+ void apply_and(const bool_vec& other) {
+ assert(data.size() == other.data.size());
+ for (int i = 0; i < int(data.size()); i++)
+ data[i] &= other.data[i];
+ }
+
+ void apply_andc(const bool_vec& other) {
+ assert(data.size() == other.data.size());
+ for (int i = 0; i < int(data.size()); i++)
+ data[i] &= ~other.data[i];
+ }
+};
+#endif
+
+// segname -> bits, tags_on, tags_off
+typedef tuple<bool_vec, bool_vec, bool_vec> segdata_t;
+map<string, segdata_t> segdata;
+
+map<string, int> segnamecnt;
+
+static inline bool_vec& segdata_bits(segdata_t& sd) {
+ return std::get<0>(sd);
+}
+static inline bool_vec& segdata_tags1(segdata_t& sd) {
+ return std::get<1>(sd);
+}
+static inline bool_vec& segdata_tags0(segdata_t& sd) {
+ return std::get<2>(sd);
+}
+
+void read_input(std::istream& f, std::string filename) {
+ string token;
+ segdata_t* segptr = nullptr;
+
+ while (f >> token) {
+ if (token == "seg") {
+ f >> token;
+ token = filename + ":" + token;
+ while (segdata.count(token)) {
+ int idx = 1;
+ if (segnamecnt.count(token))
+ idx = segnamecnt.at(token);
+ segnamecnt[token] = idx + 1;
+ char buffer[64];
+ snprintf(buffer, 64, "-%d", idx);
+ token += buffer;
+ }
+ segptr = &segdata[token];
+ continue;
+ }
+
+ if (token == "bit") {
+ assert(segptr != nullptr);
+
+ f >> token;
+ if (bit_ids.count(token) == 0) {
+ bit_ids[token] = num_bits++;
+ bit_ids_r.push_back(token);
+ }
+
+ int bit_idx = bit_ids.at(token);
+ auto& bits = segdata_bits(*segptr);
+
+ bits.set(bit_idx);
+ continue;
+ }
+
+ if (token == "tag") {
+ assert(segptr != nullptr);
+
+ f >> token;
+ if (tag_ids.count(token) == 0) {
+ tag_ids[token] = num_tags++;
+ tag_ids_r.push_back(token);
+ }
+
+ int tag_idx = tag_ids.at(token);
+
+ f >> token;
+ assert(token == "0" || token == "1");
+
+ auto& tags = token == "1" ? segdata_tags1(*segptr)
+ : segdata_tags0(*segptr);
+
+ tags.set(tag_idx);
+
+ if (FLAGS_i) {
+ auto& inv_tags = token == "1"
+ ? segdata_tags0(*segptr)
+ : segdata_tags1(*segptr);
+
+ token = tag_ids_r.at(tag_idx) + "__INV";
+
+ if (tag_ids.count(token) == 0) {
+ tag_ids[token] = num_tags++;
+ tag_ids_r.push_back(token);
+ }
+
+ int inv_tag_idx = tag_ids.at(token);
+ inv_tags.set(inv_tag_idx);
+ }
+
+ continue;
+ }
+
+ abort();
+ }
+
+ // printf("Number of segments: %d\n", int(segdata.size()));
+ // printf("Number of bits: %d\n", num_bits);
+ // printf("Number of tags: %d\n", num_tags);
+
+ for (auto& segdat : segdata) {
+ segdata_bits(segdat.second).resize(num_bits);
+ segdata_tags1(segdat.second).resize(num_tags);
+ segdata_tags0(segdat.second).resize(num_tags);
+ }
+}
+
+int main(int argc, char** argv) {
+ gflags::SetUsageMessage(
+ absl::StrCat("Usage: ", argv[0], " [options] file.."));
+ gflags::ParseCommandLineFlags(&argc, &argv, true);
+
+ if (argc > 1) {
+ for (int optind = 1; optind < argc; optind++) {
+ printf("Reading %s.\n", argv[optind]);
+ std::ifstream f;
+ f.open(argv[optind]);
+
+ // Check if input file exists.
+ if (!f.good()) {
+ printf("ERROR: Input file does not exist!\n");
+ return -1;
+ }
+
+ assert(!f.fail());
+ read_input(f, argv[optind]);
+ }
+ } else {
+ printf("Reading from stding.\n");
+ read_input(std::cin, "stdin");
+ }
+
+ printf("#of segments: %d\n", int(segdata.size()));
+ printf("#of bits: %d\n", num_bits);
+ printf("#of tags: %d\n", num_tags);
+
+ FILE* f = stdout;
+
+ if (!FLAGS_o.empty()) {
+ f = fopen(FLAGS_o.c_str(), "w");
+ assert(f != nullptr);
+ }
+
+ int cnt_const0 = 0;
+ int cnt_const1 = 0;
+ int cnt_candidates = 0;
+ int min_candidates = num_bits;
+ int max_candidates = 0;
+ float avg_candidates = 0;
+
+ std::vector<std::string> out_lines;
+
+ for (int tag_idx = 0; tag_idx < num_tags; tag_idx++) {
+ bool_vec mask(num_bits, true);
+ int count1 = 0, count0 = 0;
+
+ for (auto& segdat : segdata) {
+ auto& sd = segdat.second;
+ bool tag1 = segdata_tags1(sd).get(tag_idx);
+ bool tag0 = segdata_tags0(sd).get(tag_idx);
+
+ assert(!tag1 || !tag0);
+
+ if (tag1) {
+ count1++;
+ mask.apply_and(segdata_bits(sd));
+ continue;
+ }
+
+ if (tag0) {
+ count0++;
+ mask.apply_andc(segdata_bits(sd));
+ continue;
+ }
+ }
+
+ assert(count1 || count0);
+
+ std::string out_line = tag_ids_r.at(tag_idx);
+
+ if (count1 < FLAGS_m) {
+ char buffer[64];
+ snprintf(buffer, 64, " <m1 %d>", count1);
+ out_line += buffer;
+ }
+
+ if (count0 < FLAGS_m) {
+ char buffer[64];
+ snprintf(buffer, 64, " <m0 %d>", count0);
+ out_line += buffer;
+ }
+
+ if (count1 + count0 < FLAGS_M) {
+ char buffer[64];
+ snprintf(buffer, 64, " <M %d %d>", count1, count0);
+ out_line += buffer;
+ }
+
+ if (!count1) {
+ out_lines.push_back(out_line + " <const0>");
+ cnt_const0 += 1;
+ continue;
+ }
+
+ if (!count0) {
+ out_line += " <const1>";
+ cnt_const1 += 1;
+ }
+
+ int num_candidates = mask.count();
+
+ if (count0) {
+ min_candidates =
+ std::min(min_candidates, num_candidates);
+ max_candidates =
+ std::max(max_candidates, num_candidates);
+ avg_candidates += num_candidates;
+ cnt_candidates += 1;
+ }
+
+ if (FLAGS_c < 0 ||
+ (0 < num_candidates && num_candidates <= FLAGS_c)) {
+ std::vector<std::string> out_tags;
+ for (int bit_idx = 0; bit_idx < num_bits; bit_idx++)
+ if (mask.get(bit_idx))
+ out_tags.push_back(
+ bit_ids_r.at(bit_idx));
+ std::sort(out_tags.begin(), out_tags.end());
+ for (auto& tag : out_tags)
+ out_line += " " + tag;
+ } else {
+ char buffer[64];
+ snprintf(buffer, 64, " <%d candidates>",
+ num_candidates);
+ out_line += buffer;
+ }
+
+ out_lines.push_back(out_line);
+ }
+
+ std::sort(out_lines.begin(), out_lines.end());
+
+ for (auto& line : out_lines)
+ fprintf(f, "%s\n", line.c_str());
+
+ if (cnt_candidates)
+ avg_candidates /= cnt_candidates;
+
+ printf("#of const0 tags: %d\n", cnt_const0);
+ printf("#of const1 tags: %d\n", cnt_const1);
+
+ if (cnt_candidates) {
+ printf("min #of candidates: %d\n", min_candidates);
+ printf("max #of candidates: %d\n", max_candidates);
+ printf("avg #of candidates: %.3f\n", avg_candidates);
+ }
+
+ if (!FLAGS_k.empty()) {
+ f = fopen(FLAGS_k.c_str(), "w");
+ assert(f != nullptr);
+ std::sort(bit_ids_r.begin(), bit_ids_r.end());
+ for (auto bit : bit_ids_r)
+ fprintf(f, "bit %s\n", bit.c_str());
+ }
+
+ return 0;
+}
diff --git a/tools/xc7frames2bit.cc b/tools/xc7frames2bit.cc
new file mode 100644
index 0000000..28d952d
--- /dev/null
+++ b/tools/xc7frames2bit.cc
@@ -0,0 +1,83 @@
+#include <iostream>
+
+#include <gflags/gflags.h>
+#include <prjxray/xilinx/architectures.h>
+#include <prjxray/xilinx/bitstream_writer.h>
+#include <prjxray/xilinx/configuration.h>
+
+DEFINE_string(part_name, "", "Name of the 7-series part");
+DEFINE_string(part_file, "", "Definition file for target 7-series part");
+DEFINE_string(
+ frm_file,
+ "",
+ "File containing a list of frame deltas to be applied to the base "
+ "bitstream. Each line in the file is of the form: "
+ "<frame_address> <word1>,...,<word101>.");
+DEFINE_string(output_file, "", "Write bitstream to file");
+DEFINE_string(architecture,
+ "Series7",
+ "Architecture of the provided bitstream");
+
+namespace xilinx = prjxray::xilinx;
+
+struct Frames2BitWriter {
+ template <typename T>
+ int operator()(T& arg) {
+ using ArchType = std::decay_t<decltype(arg)>;
+ auto part = ArchType::Part::FromFile(FLAGS_part_file);
+ if (!part) {
+ std::cerr << "Part file " << FLAGS_part_file
+ << " not found or invalid" << std::endl;
+ return 1;
+ }
+
+ // Read the frames from the input file
+ xilinx::Frames<ArchType> frames;
+ if (frames.readFrames(FLAGS_frm_file)) {
+ std::cerr << "Frames file " << FLAGS_frm_file
+ << " not found or invalid" << std::endl;
+ return 1;
+ }
+
+ if (std::is_same<ArchType, xilinx::Series7>::value ||
+ std::is_same<ArchType, xilinx::UltraScale>::value ||
+ std::is_same<ArchType, xilinx::UltraScalePlus>::value) {
+ // In case the frames input file is missing some frames
+ // that are in the tilegrid
+ frames.addMissingFrames(part);
+ }
+
+ // Create data for the type 2 configuration packet with
+ // information about all frames
+ typename xilinx::Configuration<ArchType>::PacketData
+ configuration_packet_data(
+ xilinx::Configuration<ArchType>::
+ createType2ConfigurationPacketData(
+ frames.getFrames(), part));
+
+ // Put together a configuration package
+ typename ArchType::ConfigurationPackage configuration_package;
+ xilinx::Configuration<ArchType>::createConfigurationPackage(
+ configuration_package, configuration_packet_data, part);
+
+ // Write bitstream
+ auto bitstream_writer =
+ xilinx::BitstreamWriter<ArchType>(configuration_package);
+ if (bitstream_writer.writeBitstream(
+ configuration_package, FLAGS_part_name, FLAGS_frm_file,
+ "xc7frames2bit", FLAGS_output_file)) {
+ std::cerr << "Failed to write bitstream" << std::endl
+ << "Exitting" << std::endl;
+ }
+ return 0;
+ }
+};
+
+int main(int argc, char* argv[]) {
+ gflags::SetUsageMessage(argv[0]);
+ gflags::ParseCommandLineFlags(&argc, &argv, true);
+ xilinx::Architecture::Container arch_container =
+ xilinx::ArchitectureFactory::create_architecture(
+ FLAGS_architecture);
+ return absl::visit(Frames2BitWriter(), arch_container);
+}
diff --git a/tools/xc7patch.cc b/tools/xc7patch.cc
new file mode 100644
index 0000000..369ddac
--- /dev/null
+++ b/tools/xc7patch.cc
@@ -0,0 +1,146 @@
+#include <iostream>
+
+#include <gflags/gflags.h>
+#include <prjxray/memory_mapped_file.h>
+#include <prjxray/xilinx/architectures.h>
+#include <prjxray/xilinx/bitstream_reader.h>
+#include <prjxray/xilinx/bitstream_writer.h>
+#include <prjxray/xilinx/configuration.h>
+
+DEFINE_string(part_name, "", "");
+DEFINE_string(part_file, "", "Definition file for target 7-series part");
+DEFINE_string(bitstream_file,
+ "",
+ "Initial bitstream to which the deltas are applied.");
+DEFINE_string(
+ frm_file,
+ "",
+ "File containing a list of frame deltas to be applied to the base "
+ "bitstream. Each line in the file is of the form: "
+ "<frame_address> <word1>,...,<word101>.");
+DEFINE_string(output_file, "", "Write patched bitsteam to file");
+DEFINE_string(architecture,
+ "Series7",
+ "Architecture of the provided bitstream");
+
+namespace xilinx = prjxray::xilinx;
+
+template <typename ArchType>
+int patch_frames(
+ const std::string& frm_file_str,
+ std::map<typename ArchType::FrameAddress, std::vector<uint32_t>>* frames) {
+ xilinx::Frames<ArchType> frames_from_file;
+ if (frames_from_file.readFrames(frm_file_str)) {
+ std::cerr << "Failed to read frames" << std::endl;
+ return 1;
+ }
+
+ // Apply the deltas.
+ for (auto& frame : frames_from_file.getFrames()) {
+ auto iter = frames->find(frame.first);
+ if (iter == frames->end()) {
+ std::cerr << "frame address 0x" << std::hex
+ << static_cast<uint32_t>(frame.first)
+ << " because it was not found in frames."
+ << std::endl;
+ return 1;
+ }
+
+ auto& frame_data = iter->second;
+ frame_data = frame.second;
+ }
+
+ return 0;
+}
+
+struct BitstreamPatcher {
+ template <typename T>
+ int operator()(T& arg) {
+ using ArchType = std::decay_t<decltype(arg)>;
+ auto part = ArchType::Part::FromFile(FLAGS_part_file);
+ if (!part) {
+ std::cerr << "Part file not found or invalid"
+ << std::endl;
+ return 1;
+ }
+
+ auto bitstream_file = prjxray::MemoryMappedFile::InitWithFile(
+ FLAGS_bitstream_file);
+ if (!bitstream_file) {
+ std::cerr << "Can't open base bitstream file: "
+ << FLAGS_bitstream_file << std::endl;
+ return 1;
+ }
+
+ auto bitstream_reader =
+ xilinx::BitstreamReader<ArchType>::InitWithBytes(
+ bitstream_file->as_bytes());
+ if (!bitstream_reader) {
+ std::cout << "Bitstream does not appear to be a "
+ "7-series bitstream!"
+ << std::endl;
+ return 1;
+ }
+
+ auto bitstream_config =
+ xilinx::Configuration<ArchType>::InitWithPackets(
+ *part, *bitstream_reader);
+ if (!bitstream_config) {
+ std::cerr
+ << "Bitstream does not appear to be for this part"
+ << std::endl;
+ return 1;
+ }
+
+ // Copy the base frames to a mutable collection
+ std::map<typename ArchType::FrameAddress, std::vector<uint32_t>>
+ frames;
+ for (auto& frame_val : bitstream_config->frames()) {
+ auto& cur_frame = frames[frame_val.first];
+
+ std::copy(frame_val.second.begin(),
+ frame_val.second.end(),
+ std::back_inserter(cur_frame));
+ }
+
+ if (!FLAGS_frm_file.empty()) {
+ int ret =
+ patch_frames<ArchType>(FLAGS_frm_file, &frames);
+ if (ret != 0) {
+ return ret;
+ }
+ }
+
+ // Create data for the type 2 configuration packet with
+ // information about all frames
+ typename xilinx::Configuration<ArchType>::PacketData
+ configuration_packet_data(
+ xilinx::Configuration<ArchType>::
+ createType2ConfigurationPacketData(frames, part));
+
+ // Put together a configuration package
+ typename ArchType::ConfigurationPackage configuration_package;
+ xilinx::Configuration<ArchType>::createConfigurationPackage(
+ configuration_package, configuration_packet_data, part);
+
+ // Write bitstream.
+ auto bitstream_writer =
+ xilinx::BitstreamWriter<ArchType>(configuration_package);
+ if (bitstream_writer.writeBitstream(
+ configuration_package, FLAGS_part_name, FLAGS_frm_file,
+ "xc7patch", FLAGS_output_file)) {
+ std::cerr << "Failed to write bitstream" << std::endl
+ << "Exitting" << std::endl;
+ }
+ return 0;
+ }
+};
+
+int main(int argc, char* argv[]) {
+ gflags::SetUsageMessage(argv[0]);
+ gflags::ParseCommandLineFlags(&argc, &argv, true);
+ xilinx::Architecture::Container arch_container =
+ xilinx::ArchitectureFactory::create_architecture(
+ FLAGS_architecture);
+ return absl::visit(BitstreamPatcher(), arch_container);
+}