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); +}