Merge branch 'separate-xcu-tools' into 'master'
xcutools: separation of xc7 in xc7, xcu and xcup libraries
See merge request repositories/google-prjuray-tools!2
diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt
index 3edcf3c..bebc2d1 100644
--- a/lib/CMakeLists.txt
+++ b/lib/CMakeLists.txt
@@ -24,6 +24,22 @@
xilinx/xc7series/configuration_bus.cc
xilinx/xc7series/configuration_column.cc
xilinx/xc7series/ecc.cc
+ # UltraScale specific
+ xilinx/xcuseries/frame_address.cc
+ xilinx/xcuseries/part.cc
+ xilinx/xcuseries/configuration_row.cc
+ xilinx/xcuseries/block_type.cc
+ xilinx/xcuseries/configuration_bus.cc
+ xilinx/xcuseries/configuration_column.cc
+ xilinx/xcuseries/ecc.cc
+ # UltraScalePlus specific
+ xilinx/xcupseries/frame_address.cc
+ xilinx/xcupseries/part.cc
+ xilinx/xcupseries/configuration_row.cc
+ xilinx/xcupseries/block_type.cc
+ xilinx/xcupseries/configuration_bus.cc
+ xilinx/xcupseries/configuration_column.cc
+ xilinx/xcupseries/ecc.cc
)
target_include_directories(libprjxray PUBLIC "include")
target_link_libraries(libprjxray absl::optional absl::variant absl::strings absl::span absl::time yaml-cpp)
diff --git a/lib/include/prjxray/xilinx/architectures.h b/lib/include/prjxray/xilinx/architectures.h
index 9459f7a..c0a8212 100644
--- a/lib/include/prjxray/xilinx/architectures.h
+++ b/lib/include/prjxray/xilinx/architectures.h
@@ -10,6 +10,10 @@
#include <prjxray/xilinx/spartan6/part.h>
#include <prjxray/xilinx/xc7series/frame_address.h>
#include <prjxray/xilinx/xc7series/part.h>
+#include <prjxray/xilinx/xcuseries/frame_address.h>
+#include <prjxray/xilinx/xcuseries/part.h>
+#include <prjxray/xilinx/xcupseries/frame_address.h>
+#include <prjxray/xilinx/xcupseries/part.h>
namespace prjxray {
namespace xilinx {
@@ -56,18 +60,22 @@
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") {}
+ using Part = xcuseries::Part;
+ using FrameAddress = xcuseries::FrameAddress;
static constexpr int words_per_frame = 123;
};
+class UltraScalePlus : public Series7 {
+ public:
+ UltraScalePlus() : Series7("UltraScalePlus") {}
+ using Part = xcupseries::Part;
+ using FrameAddress = xcupseries::FrameAddress;
+ static constexpr int words_per_frame = 93;
+};
+
class ArchitectureFactory {
public:
static Architecture::Container create_architecture(
diff --git a/lib/include/prjxray/xilinx/configuration_register.h b/lib/include/prjxray/xilinx/configuration_register.h
index 0699f6c..22eb81a 100644
--- a/lib/include/prjxray/xilinx/configuration_register.h
+++ b/lib/include/prjxray/xilinx/configuration_register.h
@@ -49,6 +49,8 @@
// Series-7 configuration register addresses
// according to UG470, pg. 109
+// UltraScale and UltraScalePlus configuration register addresses
+// according to UG570, pg. 162-163
enum class Series7ConfigurationRegister : unsigned int {
CRC = 0x00,
FAR = 0x01,
diff --git a/lib/include/prjxray/xilinx/xcupseries/block_type.h b/lib/include/prjxray/xilinx/xcupseries/block_type.h
new file mode 100644
index 0000000..dadf49d
--- /dev/null
+++ b/lib/include/prjxray/xilinx/xcupseries/block_type.h
@@ -0,0 +1,34 @@
+#ifndef PRJXRAY_LIB_XILINX_XCUPSERIES_BLOCK_TYPE_H_
+#define PRJXRAY_LIB_XILINX_XCUPSERIES_BLOCK_TYPE_H_
+
+#include <ostream>
+
+#include <yaml-cpp/yaml.h>
+
+namespace prjxray {
+namespace xilinx {
+namespace xcupseries {
+
+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 xcupseries
+} // namespace xilinx
+} // namespace prjxray
+
+namespace YAML {
+template <>
+struct convert<prjxray::xilinx::xcupseries::BlockType> {
+ static Node encode(const prjxray::xilinx::xcupseries::BlockType& rhs);
+ static bool decode(const Node& node,
+ prjxray::xilinx::xcupseries::BlockType& lhs);
+};
+} // namespace YAML
+
+#endif // PRJXRAY_LIB_XILINX_XCUPSERIES_BLOCK_TYPE_H_
diff --git a/lib/include/prjxray/xilinx/xcupseries/command.h b/lib/include/prjxray/xilinx/xcupseries/command.h
new file mode 100644
index 0000000..016990d
--- /dev/null
+++ b/lib/include/prjxray/xilinx/xcupseries/command.h
@@ -0,0 +1,34 @@
+#ifndef PRJXRAY_LIB_XILINX_XCUPSERIES_COMMAND_H_
+#define PRJXRAY_LIB_XILINX_XCUPSERIES_COMMAND_H_
+
+namespace prjxray {
+namespace xilinx {
+namespace xcupseries {
+
+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 xcupseries
+} // namespace xilinx
+} // namespace prjxray
+
+#endif // PRJXRAY_LIB_XILINX_XCUPSERIES_COMMAND_H_
diff --git a/lib/include/prjxray/xilinx/xcupseries/configuration_bus.h b/lib/include/prjxray/xilinx/xcupseries/configuration_bus.h
new file mode 100644
index 0000000..b1fafdc
--- /dev/null
+++ b/lib/include/prjxray/xilinx/xcupseries/configuration_bus.h
@@ -0,0 +1,91 @@
+#ifndef PRJXRAY_LIB_XILINX_XCUPSERIES_CONFIGURATION_BUS_H_
+#define PRJXRAY_LIB_XILINX_XCUPSERIES_CONFIGURATION_BUS_H_
+
+#include <algorithm>
+#include <cassert>
+#include <map>
+#include <memory>
+
+#include <absl/types/optional.h>
+#include <prjxray/xilinx/xcupseries/configuration_column.h>
+#include <prjxray/xilinx/xcupseries/frame_address.h>
+#include <yaml-cpp/yaml.h>
+
+namespace prjxray {
+namespace xilinx {
+namespace xcupseries {
+
+// 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 xcupseries
+} // namespace xilinx
+} // namespace prjxray
+
+namespace YAML {
+template <>
+struct convert<prjxray::xilinx::xcupseries::ConfigurationBus> {
+ static Node encode(
+ const prjxray::xilinx::xcupseries::ConfigurationBus& rhs);
+ static bool decode(const Node& node,
+ prjxray::xilinx::xcupseries::ConfigurationBus& lhs);
+};
+} // namespace YAML
+
+#endif // PRJXRAY_LIB_XILINX_XCUPSERIES_CONFIGURATION_BUS_H_
diff --git a/lib/include/prjxray/xilinx/xcupseries/configuration_column.h b/lib/include/prjxray/xilinx/xcupseries/configuration_column.h
new file mode 100644
index 0000000..33696c2
--- /dev/null
+++ b/lib/include/prjxray/xilinx/xcupseries/configuration_column.h
@@ -0,0 +1,78 @@
+#ifndef PRJXRAY_LIB_XILINX_XCUPSERIES_CONFIGURATION_COLUMN_H_
+#define PRJXRAY_LIB_XILINX_XCUPSERIES_CONFIGURATION_COLUMN_H_
+
+#include <algorithm>
+#include <cassert>
+
+#include <absl/types/optional.h>
+#include <prjxray/xilinx/xcupseries/frame_address.h>
+#include <yaml-cpp/yaml.h>
+
+namespace prjxray {
+namespace xilinx {
+namespace xcupseries {
+
+// 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 xcupseries
+} // namespace xilinx
+} // namespace prjxray
+
+namespace YAML {
+template <>
+struct convert<prjxray::xilinx::xcupseries::ConfigurationColumn> {
+ static Node encode(
+ const prjxray::xilinx::xcupseries::ConfigurationColumn& rhs);
+ static bool decode(
+ const Node& node,
+ prjxray::xilinx::xcupseries::ConfigurationColumn& lhs);
+};
+} // namespace YAML
+
+#endif // PRJXRAY_LIB_XILINX_XCUPSERIES_CONFIGURATION_COLUMN_H_
diff --git a/lib/include/prjxray/xilinx/xcupseries/configuration_options_0_value.h b/lib/include/prjxray/xilinx/xcupseries/configuration_options_0_value.h
new file mode 100644
index 0000000..124d54a
--- /dev/null
+++ b/lib/include/prjxray/xilinx/xcupseries/configuration_options_0_value.h
@@ -0,0 +1,122 @@
+#ifndef PRJXRAY_LIB_XILINX_XCUPSERIES_CONFIGURATION_OPTIONS_0_VALUE_H
+#define PRJXRAY_LIB_XILINX_XCUPSERIES_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 xcupseries {
+
+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 xcupseries
+
+} // namespace xcupseries
+} // namespace xilinx
+} // namespace prjxray
+
+#endif // PRJXRAY_LIB_XILINX_XCUPSERIES_CONFIGURATION_OPTIONS_0_VALUE_H
diff --git a/lib/include/prjxray/xilinx/xcupseries/configuration_row.h b/lib/include/prjxray/xilinx/xcupseries/configuration_row.h
new file mode 100644
index 0000000..7b72655
--- /dev/null
+++ b/lib/include/prjxray/xilinx/xcupseries/configuration_row.h
@@ -0,0 +1,88 @@
+#ifndef PRJXRAY_LIB_XILINX_XCUPSERIES_ROW_H_
+#define PRJXRAY_LIB_XILINX_XCUPSERIES_ROW_H_
+
+#include <algorithm>
+#include <cassert>
+#include <map>
+#include <memory>
+
+#include <absl/types/optional.h>
+#include <prjxray/xilinx/xcupseries/block_type.h>
+#include <prjxray/xilinx/xcupseries/configuration_bus.h>
+#include <prjxray/xilinx/xcupseries/frame_address.h>
+#include <yaml-cpp/yaml.h>
+
+namespace prjxray {
+namespace xilinx {
+namespace xcupseries {
+
+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.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 xcupseries
+} // namespace xilinx
+} // namespace prjxray
+
+namespace YAML {
+template <>
+struct convert<prjxray::xilinx::xcupseries::Row> {
+ static Node encode(const prjxray::xilinx::xcupseries::Row& rhs);
+ static bool decode(const Node& node,
+ prjxray::xilinx::xcupseries::Row& lhs);
+};
+} // namespace YAML
+
+#endif // PRJXRAY_LIB_XILINX_XCUPSERIES_ROW_H_
diff --git a/lib/include/prjxray/xilinx/xcupseries/crc.h b/lib/include/prjxray/xilinx/xcupseries/crc.h
new file mode 100644
index 0000000..3f0fd1c
--- /dev/null
+++ b/lib/include/prjxray/xilinx/xcupseries/crc.h
@@ -0,0 +1,40 @@
+#ifndef PRJXRAY_LIB_XILINX_XCUPSERIES_CRC_H_
+#define PRJXRAY_LIB_XILINX_XCUPSERIES_CRC_H_
+
+#include <cstdint>
+
+constexpr uint32_t kCrc32CastagnoliPolynomial = 0x82F63B78;
+
+namespace prjxray {
+namespace xilinx {
+namespace xcupseries {
+
+// 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 xcupseries
+} // namespace xilinx
+} // namespace prjxray
+
+#endif // PRJXRAY_LIB_XILINX_XCUPSERIES_CRC_H_
diff --git a/lib/include/prjxray/xilinx/xcupseries/ecc.h b/lib/include/prjxray/xilinx/xcupseries/ecc.h
new file mode 100644
index 0000000..10f0173
--- /dev/null
+++ b/lib/include/prjxray/xilinx/xcupseries/ecc.h
@@ -0,0 +1,22 @@
+#ifndef PRJXRAY_LIB_XILINX_XCUPSERIES_ECC_H_
+#define PRJXRAY_LIB_XILINX_XCUPSERIES_ECC_H_
+
+#include <cstdint>
+#include <vector>
+
+namespace prjxray {
+namespace xilinx {
+namespace xcupseries {
+
+// 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 xcupseries
+} // namespace xilinx
+} // namespace prjxray
+
+#endif // PRJXRAY_LIB_XILINX_XCUPSERIES_ECC_H_
diff --git a/lib/include/prjxray/xilinx/xcupseries/frame_address.h b/lib/include/prjxray/xilinx/xcupseries/frame_address.h
new file mode 100644
index 0000000..9ceba19
--- /dev/null
+++ b/lib/include/prjxray/xilinx/xcupseries/frame_address.h
@@ -0,0 +1,68 @@
+#ifndef PRJXRAY_LIB_XILINX_XCUPSERIES_FRAME_ADDRESS_H_
+#define PRJXRAY_LIB_XILINX_XCUPSERIES_FRAME_ADDRESS_H_
+
+#include <cstdint>
+#include <ostream>
+
+#include <prjxray/xilinx/xcupseries/block_type.h>
+#include <yaml-cpp/yaml.h>
+
+#ifdef _GNU_SOURCE
+#undef minor
+#endif
+
+namespace prjxray {
+namespace xilinx {
+namespace xcupseries {
+
+enum Ranges {
+ BLOCK_TYPE_HIGH = 26,
+ BLOCK_TYPE_LOW = 24,
+ ROW_HIGH = 23,
+ ROW_LOW = 18,
+ COLUMN_HIGH = 17,
+ COLUMN_LOW = 8,
+ MINOR_HIGH = 7,
+ MINOR_LOW = 0
+};
+
+class FrameAddress {
+ public:
+ FrameAddress() : address_(0) {}
+
+ FrameAddress(uint32_t address) : address_(address){};
+
+ FrameAddress(BlockType block_type,
+ 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 xcupseries
+} // namespace xilinx
+} // namespace prjxray
+
+namespace YAML {
+template <>
+struct convert<prjxray::xilinx::xcupseries::FrameAddress> {
+ static Node encode(const prjxray::xilinx::xcupseries::FrameAddress& rhs);
+ static bool decode(const Node& node,
+ prjxray::xilinx::xcupseries::FrameAddress& lhs);
+};
+
+} // namespace YAML
+
+#endif // PRJXRAY_LIB_XILINX_XCUPSERIES_FRAME_ADDRESS_H_
diff --git a/lib/include/prjxray/xilinx/xcupseries/part.h b/lib/include/prjxray/xilinx/xcupseries/part.h
new file mode 100644
index 0000000..5bad16d
--- /dev/null
+++ b/lib/include/prjxray/xilinx/xcupseries/part.h
@@ -0,0 +1,83 @@
+#ifndef PRJXRAY_LIB_XILINX_XCUPSERIES_PART_H_
+#define PRJXRAY_LIB_XILINX_XCUPSERIES_PART_H_
+
+#include <algorithm>
+#include <cstdint>
+#include <memory>
+#include <string>
+#include <vector>
+
+#include <absl/types/optional.h>
+#include <prjxray/xilinx/xcupseries/configuration_row.h>
+#include <prjxray/xilinx/xcupseries/frame_address.h>
+#include <yaml-cpp/yaml.h>
+
+namespace prjxray {
+namespace xilinx {
+namespace xcupseries {
+
+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_;
+ std::map<unsigned int, Row> rows_;
+};
+
+template <typename T>
+Part::Part(uint32_t idcode, T first, T last) : idcode_(idcode) {
+ 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 xcupseries
+} // namespace xilinx
+} // namespace prjxray
+
+namespace YAML {
+template <>
+struct convert<prjxray::xilinx::xcupseries::Part> {
+ static Node encode(const prjxray::xilinx::xcupseries::Part& rhs);
+ static bool decode(const Node& node,
+ prjxray::xilinx::xcupseries::Part& lhs);
+};
+} // namespace YAML
+
+#endif // PRJXRAY_LIB_XILINX_XCUPSERIES_PART_H_
diff --git a/lib/include/prjxray/xilinx/xcuseries/block_type.h b/lib/include/prjxray/xilinx/xcuseries/block_type.h
new file mode 100644
index 0000000..de473e9
--- /dev/null
+++ b/lib/include/prjxray/xilinx/xcuseries/block_type.h
@@ -0,0 +1,34 @@
+#ifndef PRJXRAY_LIB_XILINX_XCUSERIES_BLOCK_TYPE_H_
+#define PRJXRAY_LIB_XILINX_XCUSERIES_BLOCK_TYPE_H_
+
+#include <ostream>
+
+#include <yaml-cpp/yaml.h>
+
+namespace prjxray {
+namespace xilinx {
+namespace xcuseries {
+
+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 xcuseries
+} // namespace xilinx
+} // namespace prjxray
+
+namespace YAML {
+template <>
+struct convert<prjxray::xilinx::xcuseries::BlockType> {
+ static Node encode(const prjxray::xilinx::xcuseries::BlockType& rhs);
+ static bool decode(const Node& node,
+ prjxray::xilinx::xcuseries::BlockType& lhs);
+};
+} // namespace YAML
+
+#endif // PRJXRAY_LIB_XILINX_XCUSERIES_BLOCK_TYPE_H_
diff --git a/lib/include/prjxray/xilinx/xcuseries/command.h b/lib/include/prjxray/xilinx/xcuseries/command.h
new file mode 100644
index 0000000..50a92d7
--- /dev/null
+++ b/lib/include/prjxray/xilinx/xcuseries/command.h
@@ -0,0 +1,34 @@
+#ifndef PRJXRAY_LIB_XILINX_XCUSERIES_COMMAND_H_
+#define PRJXRAY_LIB_XILINX_XCUSERIES_COMMAND_H_
+
+namespace prjxray {
+namespace xilinx {
+namespace xcuseries {
+
+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 xcuseries
+} // namespace xilinx
+} // namespace prjxray
+
+#endif // PRJXRAY_LIB_XILINX_XCUSERIES_COMMAND_H_
diff --git a/lib/include/prjxray/xilinx/xcuseries/configuration_bus.h b/lib/include/prjxray/xilinx/xcuseries/configuration_bus.h
new file mode 100644
index 0000000..64b19a9
--- /dev/null
+++ b/lib/include/prjxray/xilinx/xcuseries/configuration_bus.h
@@ -0,0 +1,91 @@
+#ifndef PRJXRAY_LIB_XILINX_XCUSERIES_CONFIGURATION_BUS_H_
+#define PRJXRAY_LIB_XILINX_XCUSERIES_CONFIGURATION_BUS_H_
+
+#include <algorithm>
+#include <cassert>
+#include <map>
+#include <memory>
+
+#include <absl/types/optional.h>
+#include <prjxray/xilinx/xcuseries/configuration_column.h>
+#include <prjxray/xilinx/xcuseries/frame_address.h>
+#include <yaml-cpp/yaml.h>
+
+namespace prjxray {
+namespace xilinx {
+namespace xcuseries {
+
+// 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 xcuseries
+} // namespace xilinx
+} // namespace prjxray
+
+namespace YAML {
+template <>
+struct convert<prjxray::xilinx::xcuseries::ConfigurationBus> {
+ static Node encode(
+ const prjxray::xilinx::xcuseries::ConfigurationBus& rhs);
+ static bool decode(const Node& node,
+ prjxray::xilinx::xcuseries::ConfigurationBus& lhs);
+};
+} // namespace YAML
+
+#endif // PRJXRAY_LIB_XILINX_XCUSERIES_CONFIGURATION_BUS_H_
diff --git a/lib/include/prjxray/xilinx/xcuseries/configuration_column.h b/lib/include/prjxray/xilinx/xcuseries/configuration_column.h
new file mode 100644
index 0000000..da054bb
--- /dev/null
+++ b/lib/include/prjxray/xilinx/xcuseries/configuration_column.h
@@ -0,0 +1,78 @@
+#ifndef PRJXRAY_LIB_XILINX_XCUSERIES_CONFIGURATION_COLUMN_H_
+#define PRJXRAY_LIB_XILINX_XCUSERIES_CONFIGURATION_COLUMN_H_
+
+#include <algorithm>
+#include <cassert>
+
+#include <absl/types/optional.h>
+#include <prjxray/xilinx/xcuseries/frame_address.h>
+#include <yaml-cpp/yaml.h>
+
+namespace prjxray {
+namespace xilinx {
+namespace xcuseries {
+
+// 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 xcuseries
+} // namespace xilinx
+} // namespace prjxray
+
+namespace YAML {
+template <>
+struct convert<prjxray::xilinx::xcuseries::ConfigurationColumn> {
+ static Node encode(
+ const prjxray::xilinx::xcuseries::ConfigurationColumn& rhs);
+ static bool decode(
+ const Node& node,
+ prjxray::xilinx::xcuseries::ConfigurationColumn& lhs);
+};
+} // namespace YAML
+
+#endif // PRJXRAY_LIB_XILINX_XCUSERIES_CONFIGURATION_COLUMN_H_
diff --git a/lib/include/prjxray/xilinx/xcuseries/configuration_options_0_value.h b/lib/include/prjxray/xilinx/xcuseries/configuration_options_0_value.h
new file mode 100644
index 0000000..d759e53
--- /dev/null
+++ b/lib/include/prjxray/xilinx/xcuseries/configuration_options_0_value.h
@@ -0,0 +1,122 @@
+#ifndef PRJXRAY_LIB_XILINX_XCUSERIES_CONFIGURATION_OPTIONS_0_VALUE_H
+#define PRJXRAY_LIB_XILINX_XCUSERIES_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 xcuseries {
+
+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 xcuseries
+
+} // namespace xcuseries
+} // namespace xilinx
+} // namespace prjxray
+
+#endif // PRJXRAY_LIB_XILINX_XCUSERIES_CONFIGURATION_OPTIONS_0_VALUE_H
diff --git a/lib/include/prjxray/xilinx/xcuseries/configuration_row.h b/lib/include/prjxray/xilinx/xcuseries/configuration_row.h
new file mode 100644
index 0000000..f8566ca
--- /dev/null
+++ b/lib/include/prjxray/xilinx/xcuseries/configuration_row.h
@@ -0,0 +1,88 @@
+#ifndef PRJXRAY_LIB_XILINX_XCUSERIES_ROW_H_
+#define PRJXRAY_LIB_XILINX_XCUSERIES_ROW_H_
+
+#include <algorithm>
+#include <cassert>
+#include <map>
+#include <memory>
+
+#include <absl/types/optional.h>
+#include <prjxray/xilinx/xcuseries/block_type.h>
+#include <prjxray/xilinx/xcuseries/configuration_bus.h>
+#include <prjxray/xilinx/xcuseries/frame_address.h>
+#include <yaml-cpp/yaml.h>
+
+namespace prjxray {
+namespace xilinx {
+namespace xcuseries {
+
+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.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 xcuseries
+} // namespace xilinx
+} // namespace prjxray
+
+namespace YAML {
+template <>
+struct convert<prjxray::xilinx::xcuseries::Row> {
+ static Node encode(const prjxray::xilinx::xcuseries::Row& rhs);
+ static bool decode(const Node& node,
+ prjxray::xilinx::xcuseries::Row& lhs);
+};
+} // namespace YAML
+
+#endif // PRJXRAY_LIB_XILINX_XCUSERIES_ROW_H_
diff --git a/lib/include/prjxray/xilinx/xcuseries/crc.h b/lib/include/prjxray/xilinx/xcuseries/crc.h
new file mode 100644
index 0000000..3b11929
--- /dev/null
+++ b/lib/include/prjxray/xilinx/xcuseries/crc.h
@@ -0,0 +1,40 @@
+#ifndef PRJXRAY_LIB_XILINX_XCUSERIES_CRC_H_
+#define PRJXRAY_LIB_XILINX_XCUSERIES_CRC_H_
+
+#include <cstdint>
+
+constexpr uint32_t kCrc32CastagnoliPolynomial = 0x82F63B78;
+
+namespace prjxray {
+namespace xilinx {
+namespace xcuseries {
+
+// 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 xcuseries
+} // namespace xilinx
+} // namespace prjxray
+
+#endif // PRJXRAY_LIB_XILINX_XCUSERIES_CRC_H_
diff --git a/lib/include/prjxray/xilinx/xcuseries/ecc.h b/lib/include/prjxray/xilinx/xcuseries/ecc.h
new file mode 100644
index 0000000..ad704a2
--- /dev/null
+++ b/lib/include/prjxray/xilinx/xcuseries/ecc.h
@@ -0,0 +1,22 @@
+#ifndef PRJXRAY_LIB_XILINX_XCUSERIES_ECC_H_
+#define PRJXRAY_LIB_XILINX_XCUSERIES_ECC_H_
+
+#include <cstdint>
+#include <vector>
+
+namespace prjxray {
+namespace xilinx {
+namespace xcuseries {
+
+// 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 xcuseries
+} // namespace xilinx
+} // namespace prjxray
+
+#endif // PRJXRAY_LIB_XILINX_XCUSERIES_ECC_H_
diff --git a/lib/include/prjxray/xilinx/xcuseries/frame_address.h b/lib/include/prjxray/xilinx/xcuseries/frame_address.h
new file mode 100644
index 0000000..45358ec
--- /dev/null
+++ b/lib/include/prjxray/xilinx/xcuseries/frame_address.h
@@ -0,0 +1,71 @@
+#ifndef PRJXRAY_LIB_XILINX_XCUSERIES_FRAME_ADDRESS_H_
+#define PRJXRAY_LIB_XILINX_XCUSERIES_FRAME_ADDRESS_H_
+
+#include <cstdint>
+#include <ostream>
+
+#include <prjxray/xilinx/xcuseries/block_type.h>
+#include <yaml-cpp/yaml.h>
+
+#ifdef _GNU_SOURCE
+#undef minor
+#endif
+
+namespace prjxray {
+namespace xilinx {
+namespace xcuseries {
+
+enum Ranges {
+ BLOCK_TYPE_HIGH = 25,
+ BLOCK_TYPE_LOW = 23,
+
+ ROW_HIGH = 22,
+ ROW_LOW = 17,
+
+ COLUMN_HIGH = 16,
+ COLUMN_LOW = 7,
+
+ MINOR_HIGH = 6,
+ MINOR_LOW = 0
+};
+
+class FrameAddress {
+ public:
+ FrameAddress() : address_(0) {}
+
+ FrameAddress(uint32_t address) : address_(address){};
+
+ FrameAddress(BlockType block_type,
+ 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 xcuseries
+} // namespace xilinx
+} // namespace prjxray
+
+namespace YAML {
+template <>
+struct convert<prjxray::xilinx::xcuseries::FrameAddress> {
+ static Node encode(const prjxray::xilinx::xcuseries::FrameAddress& rhs);
+ static bool decode(const Node& node,
+ prjxray::xilinx::xcuseries::FrameAddress& lhs);
+};
+
+} // namespace YAML
+
+#endif // PRJXRAY_LIB_XILINX_XCUSERIES_FRAME_ADDRESS_H_
diff --git a/lib/include/prjxray/xilinx/xcuseries/part.h b/lib/include/prjxray/xilinx/xcuseries/part.h
new file mode 100644
index 0000000..219b9b1
--- /dev/null
+++ b/lib/include/prjxray/xilinx/xcuseries/part.h
@@ -0,0 +1,83 @@
+#ifndef PRJXRAY_LIB_XILINX_XCUSERIES_PART_H_
+#define PRJXRAY_LIB_XILINX_XCUSERIES_PART_H_
+
+#include <algorithm>
+#include <cstdint>
+#include <memory>
+#include <string>
+#include <vector>
+
+#include <absl/types/optional.h>
+#include <prjxray/xilinx/xcuseries/configuration_row.h>
+#include <prjxray/xilinx/xcuseries/frame_address.h>
+#include <yaml-cpp/yaml.h>
+
+namespace prjxray {
+namespace xilinx {
+namespace xcuseries {
+
+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_;
+ std::map<unsigned int, Row> rows_;
+};
+
+template <typename T>
+Part::Part(uint32_t idcode, T first, T last) : idcode_(idcode) {
+ 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 xcuseries
+} // namespace xilinx
+} // namespace prjxray
+
+namespace YAML {
+template <>
+struct convert<prjxray::xilinx::xcuseries::Part> {
+ static Node encode(const prjxray::xilinx::xcuseries::Part& rhs);
+ static bool decode(const Node& node,
+ prjxray::xilinx::xcuseries::Part& lhs);
+};
+} // namespace YAML
+
+#endif // PRJXRAY_LIB_XILINX_XCUSERIES_PART_H_
diff --git a/lib/xilinx/xcupseries/block_type.cc b/lib/xilinx/xcupseries/block_type.cc
new file mode 100644
index 0000000..ced79e2
--- /dev/null
+++ b/lib/xilinx/xcupseries/block_type.cc
@@ -0,0 +1,62 @@
+#include <prjxray/xilinx/xcupseries/block_type.h>
+
+namespace prjxray {
+namespace xilinx {
+namespace xcupseries {
+
+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 xcupseries
+} // namespace xilinx
+} // namespace prjxray
+
+namespace YAML {
+
+Node convert<prjxray::xilinx::xcupseries::BlockType>::encode(
+ const prjxray::xilinx::xcupseries::BlockType& rhs) {
+ switch (rhs) {
+ case prjxray::xilinx::xcupseries::BlockType::CLB_IO_CLK:
+ return Node("CLB_IO_CLK");
+ case prjxray::xilinx::xcupseries::BlockType::BLOCK_RAM:
+ return Node("BLOCK_RAM");
+ case prjxray::xilinx::xcupseries::BlockType::CFG_CLB:
+ return Node("CFG_CLB");
+ default:
+ return Node(static_cast<unsigned int>(rhs));
+ }
+}
+
+bool YAML::convert<prjxray::xilinx::xcupseries::BlockType>::decode(
+ const Node& node,
+ prjxray::xilinx::xcupseries::BlockType& lhs) {
+ auto type_str = node.as<std::string>();
+
+ if (type_str == "CLB_IO_CLK") {
+ lhs = prjxray::xilinx::xcupseries::BlockType::CLB_IO_CLK;
+ return true;
+ } else if (type_str == "BLOCK_RAM") {
+ lhs = prjxray::xilinx::xcupseries::BlockType::BLOCK_RAM;
+ return true;
+ } else if (type_str == "CFG_CLB") {
+ lhs = prjxray::xilinx::xcupseries::BlockType::CFG_CLB;
+ return true;
+ } else {
+ return false;
+ }
+}
+
+} // namespace YAML
diff --git a/lib/xilinx/xcupseries/configuration_bus.cc b/lib/xilinx/xcupseries/configuration_bus.cc
new file mode 100644
index 0000000..cb7f5ea
--- /dev/null
+++ b/lib/xilinx/xcupseries/configuration_bus.cc
@@ -0,0 +1,78 @@
+#include <prjxray/xilinx/xcupseries/configuration_bus.h>
+
+#include <iostream>
+
+namespace prjxray {
+namespace xilinx {
+namespace xcupseries {
+
+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 xcupseries
+} // namespace xilinx
+} // namespace prjxray
+
+namespace xcupseries = prjxray::xilinx::xcupseries;
+
+namespace YAML {
+
+Node convert<xcupseries::ConfigurationBus>::encode(
+ const xcupseries::ConfigurationBus& rhs) {
+ Node node;
+ node.SetTag("xilinx/xcupseries/configuration_bus");
+ node["configuration_columns"] = rhs.configuration_columns_;
+ return node;
+}
+
+bool convert<xcupseries::ConfigurationBus>::decode(
+ const Node& node,
+ xcupseries::ConfigurationBus& lhs) {
+ if (!node.Tag().empty() &&
+ node.Tag() != "xilinx/xcupseries/configuration_bus") {
+ return false;
+ }
+
+ lhs.configuration_columns_ =
+ node["configuration_columns"]
+ .as<std::map<unsigned int, xcupseries::ConfigurationColumn>>();
+ return true;
+}
+
+} // namespace YAML
diff --git a/lib/xilinx/xcupseries/configuration_column.cc b/lib/xilinx/xcupseries/configuration_column.cc
new file mode 100644
index 0000000..5ca5e6a
--- /dev/null
+++ b/lib/xilinx/xcupseries/configuration_column.cc
@@ -0,0 +1,52 @@
+#include <prjxray/xilinx/xcupseries/configuration_column.h>
+
+namespace prjxray {
+namespace xilinx {
+namespace xcupseries {
+
+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 xcupseries
+} // namespace xilinx
+} // namespace prjxray
+
+namespace xcupseries = prjxray::xilinx::xcupseries;
+
+namespace YAML {
+
+Node convert<xcupseries::ConfigurationColumn>::encode(
+ const xcupseries::ConfigurationColumn& rhs) {
+ Node node;
+ node.SetTag("xilinx/xcupseries/configuration_column");
+ node["frame_count"] = rhs.frame_count_;
+ return node;
+}
+
+bool convert<xcupseries::ConfigurationColumn>::decode(
+ const Node& node,
+ xcupseries::ConfigurationColumn& lhs) {
+ if (!node.Tag().empty() &&
+ node.Tag() != "xilinx/xcupseries/configuration_column") {
+ return false;
+ }
+
+ lhs.frame_count_ = node["frame_count"].as<unsigned int>();
+ return true;
+}
+
+} // namespace YAML
diff --git a/lib/xilinx/xcupseries/configuration_row.cc b/lib/xilinx/xcupseries/configuration_row.cc
new file mode 100644
index 0000000..258b0ff
--- /dev/null
+++ b/lib/xilinx/xcupseries/configuration_row.cc
@@ -0,0 +1,62 @@
+#include <prjxray/xilinx/xcupseries/configuration_row.h>
+
+namespace prjxray {
+namespace xilinx {
+namespace xcupseries {
+
+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 xcupseries
+} // namespace xilinx
+} // namespace prjxray
+
+namespace xcupseries = prjxray::xilinx::xcupseries;
+
+namespace YAML {
+
+Node convert<xcupseries::Row>::encode(const xcupseries::Row& rhs) {
+ Node node;
+ node.SetTag("xilinx/xcupseries/row");
+ node["configuration_buses"] = rhs.configuration_buses_;
+ return node;
+}
+
+bool convert<xcupseries::Row>::decode(const Node& node, xcupseries::Row& lhs) {
+ if (!node.Tag().empty() && node.Tag() != "xilinx/xcupseries/row") {
+ return false;
+ }
+
+ lhs.configuration_buses_ =
+ node["configuration_buses"]
+ .as<std::map<xcupseries::BlockType,
+ xcupseries::ConfigurationBus>>();
+ return true;
+}
+
+} // namespace YAML
diff --git a/lib/xilinx/xcupseries/ecc.cc b/lib/xilinx/xcupseries/ecc.cc
new file mode 100644
index 0000000..9004438
--- /dev/null
+++ b/lib/xilinx/xcupseries/ecc.cc
@@ -0,0 +1,60 @@
+#include <prjxray/xilinx/xcupseries/ecc.h>
+#include <cassert>
+#include <cstdio>
+
+namespace prjxray {
+namespace xilinx {
+namespace xcupseries {
+
+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 = xcupseries::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 xcupseries
+} // namespace xilinx
+} // namespace prjxray
diff --git a/lib/xilinx/xcupseries/frame_address.cc b/lib/xilinx/xcupseries/frame_address.cc
new file mode 100644
index 0000000..f9a2ac5
--- /dev/null
+++ b/lib/xilinx/xcupseries/frame_address.cc
@@ -0,0 +1,101 @@
+#include <prjxray/xilinx/xcupseries/frame_address.h>
+
+#include <iomanip>
+
+#include <prjxray/bit_ops.h>
+
+namespace prjxray {
+namespace xilinx {
+namespace xcupseries {
+
+FrameAddress::FrameAddress(BlockType block_type,
+ uint8_t row,
+ uint16_t column,
+ uint8_t minor) {
+ address_ = bit_field_set(0,
+ BLOCK_TYPE_HIGH,
+ BLOCK_TYPE_LOW,
+ block_type);
+
+ address_ = bit_field_set(address_,
+ ROW_HIGH,
+ ROW_LOW,
+ row);
+
+ address_ = bit_field_set(address_,
+ COLUMN_HIGH,
+ COLUMN_LOW,
+ column);
+
+ address_ = bit_field_set(address_,
+ MINOR_HIGH,
+ MINOR_LOW,
+ minor);
+}
+
+BlockType FrameAddress::block_type() const {
+ return static_cast<BlockType>(bit_field_get(address_, BLOCK_TYPE_HIGH, BLOCK_TYPE_LOW));
+}
+
+bool FrameAddress::is_bottom_half_rows() const {
+ return bit_field_get(address_, ROW_HIGH, ROW_HIGH);
+}
+
+uint8_t FrameAddress::row() const {
+ return bit_field_get(address_, ROW_HIGH, ROW_LOW);
+}
+
+uint16_t FrameAddress::column() const {
+ return bit_field_get(address_, COLUMN_HIGH, COLUMN_LOW);
+}
+
+uint8_t FrameAddress::minor() const {
+ return bit_field_get(address_, MINOR_HIGH, MINOR_LOW);
+}
+
+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 xcupseries
+} // namespace xilinx
+} // namespace prjxray
+
+namespace YAML {
+
+namespace xcupseries = prjxray::xilinx::xcupseries;
+
+Node convert<xcupseries::FrameAddress>::encode(
+ const xcupseries::FrameAddress& rhs) {
+ Node node;
+ node.SetTag("xilinx/xcupseries/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<xcupseries::FrameAddress>::decode(const Node& node,
+ xcupseries::FrameAddress& lhs) {
+ if (!(node.Tag() == "xilinx/xcupseries/frame_address" ||
+ node.Tag() == "xilinx/xcupseries/configuration_frame_address") ||
+ !node["block_type"] || !node["row"] ||
+ !node["column"] || !node["minor"])
+ return false;
+
+ lhs = prjxray::xilinx::xcupseries::FrameAddress(
+ node["block_type"].as<xcupseries::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/xcupseries/part.cc b/lib/xilinx/xcupseries/part.cc
new file mode 100644
index 0000000..71ca577
--- /dev/null
+++ b/lib/xilinx/xcupseries/part.cc
@@ -0,0 +1,113 @@
+#include <prjxray/xilinx/xcupseries/part.h>
+
+#include <iomanip>
+#include <sstream>
+
+namespace prjxray {
+namespace xilinx {
+namespace xcupseries {
+
+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 {
+ auto addr_row = rows_.find(address.row());
+ if (addr_row == rows_.end())
+ return false;
+ return addr_row->second.IsValidFrameAddress(address);
+}
+
+absl::optional<FrameAddress> Part::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;
+ }
+
+ // Block types are next numerically.
+ if (address.block_type() < BlockType::BLOCK_RAM) {
+ next_address =
+ FrameAddress(BlockType::BLOCK_RAM, 0, 0, 0);
+ if (IsValidFrameAddress(*next_address))
+ return next_address;
+ }
+
+ if (address.block_type() < BlockType::CFG_CLB) {
+ next_address = FrameAddress(BlockType::CFG_CLB, 0, 0, 0);
+ if (IsValidFrameAddress(*next_address))
+ return next_address;
+ }
+
+ return {};
+}
+
+} // namespace xcupseries
+} // namespace xilinx
+} // namespace prjxray
+
+namespace xcupseries = prjxray::xilinx::xcupseries;
+
+namespace YAML {
+
+Node convert<xcupseries::Part>::encode(const xcupseries::Part& rhs) {
+ Node node;
+ node.SetTag("xilinx/xcupseries/part");
+
+ std::ostringstream idcode_str;
+ idcode_str << "0x" << std::hex << rhs.idcode_;
+ node["idcode"] = idcode_str.str();
+ node["rows"] = rhs.rows_;
+ return node;
+}
+
+bool convert<xcupseries::Part>::decode(const Node& node, xcupseries::Part& lhs) {
+ if (!node.Tag().empty() && node.Tag() != "xilinx/xcupseries/part")
+ return false;
+
+ if (!node["rows"] && !node["configuration_ranges"]) {
+ return false;
+ }
+
+ lhs.idcode_ = node["idcode"].as<uint32_t>();
+ if (node["rows"]) {
+ lhs.rows_ = node["rows"].as<std::map<unsigned int, xcupseries::Row>>();
+ } else if (node["configuration_ranges"]) {
+ std::vector<xcupseries::FrameAddress> addresses;
+ for (auto range : node["configuration_ranges"]) {
+ auto begin =
+ range["begin"].as<xcupseries::FrameAddress>();
+ auto end = range["end"].as<xcupseries::FrameAddress>();
+ for (uint32_t cur = begin; cur < end; ++cur) {
+ addresses.push_back(cur);
+ }
+ }
+
+ lhs = xcupseries::Part(lhs.idcode_, addresses);
+ }
+
+ return true;
+};
+
+} // namespace YAML
diff --git a/lib/xilinx/xcuseries/block_type.cc b/lib/xilinx/xcuseries/block_type.cc
new file mode 100644
index 0000000..18cc8ff
--- /dev/null
+++ b/lib/xilinx/xcuseries/block_type.cc
@@ -0,0 +1,62 @@
+#include <prjxray/xilinx/xcuseries/block_type.h>
+
+namespace prjxray {
+namespace xilinx {
+namespace xcuseries {
+
+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 xcuseries
+} // namespace xilinx
+} // namespace prjxray
+
+namespace YAML {
+
+Node convert<prjxray::xilinx::xcuseries::BlockType>::encode(
+ const prjxray::xilinx::xcuseries::BlockType& rhs) {
+ switch (rhs) {
+ case prjxray::xilinx::xcuseries::BlockType::CLB_IO_CLK:
+ return Node("CLB_IO_CLK");
+ case prjxray::xilinx::xcuseries::BlockType::BLOCK_RAM:
+ return Node("BLOCK_RAM");
+ case prjxray::xilinx::xcuseries::BlockType::CFG_CLB:
+ return Node("CFG_CLB");
+ default:
+ return Node(static_cast<unsigned int>(rhs));
+ }
+}
+
+bool YAML::convert<prjxray::xilinx::xcuseries::BlockType>::decode(
+ const Node& node,
+ prjxray::xilinx::xcuseries::BlockType& lhs) {
+ auto type_str = node.as<std::string>();
+
+ if (type_str == "CLB_IO_CLK") {
+ lhs = prjxray::xilinx::xcuseries::BlockType::CLB_IO_CLK;
+ return true;
+ } else if (type_str == "BLOCK_RAM") {
+ lhs = prjxray::xilinx::xcuseries::BlockType::BLOCK_RAM;
+ return true;
+ } else if (type_str == "CFG_CLB") {
+ lhs = prjxray::xilinx::xcuseries::BlockType::CFG_CLB;
+ return true;
+ } else {
+ return false;
+ }
+}
+
+} // namespace YAML
diff --git a/lib/xilinx/xcuseries/configuration_bus.cc b/lib/xilinx/xcuseries/configuration_bus.cc
new file mode 100644
index 0000000..f960f8a
--- /dev/null
+++ b/lib/xilinx/xcuseries/configuration_bus.cc
@@ -0,0 +1,78 @@
+#include <prjxray/xilinx/xcuseries/configuration_bus.h>
+
+#include <iostream>
+
+namespace prjxray {
+namespace xilinx {
+namespace xcuseries {
+
+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 xcuseries
+} // namespace xilinx
+} // namespace prjxray
+
+namespace xcuseries = prjxray::xilinx::xcuseries;
+
+namespace YAML {
+
+Node convert<xcuseries::ConfigurationBus>::encode(
+ const xcuseries::ConfigurationBus& rhs) {
+ Node node;
+ node.SetTag("xilinx/xcuseries/configuration_bus");
+ node["configuration_columns"] = rhs.configuration_columns_;
+ return node;
+}
+
+bool convert<xcuseries::ConfigurationBus>::decode(
+ const Node& node,
+ xcuseries::ConfigurationBus& lhs) {
+ if (!node.Tag().empty() &&
+ node.Tag() != "xilinx/xcuseries/configuration_bus") {
+ return false;
+ }
+
+ lhs.configuration_columns_ =
+ node["configuration_columns"]
+ .as<std::map<unsigned int, xcuseries::ConfigurationColumn>>();
+ return true;
+}
+
+} // namespace YAML
diff --git a/lib/xilinx/xcuseries/configuration_column.cc b/lib/xilinx/xcuseries/configuration_column.cc
new file mode 100644
index 0000000..e767c57
--- /dev/null
+++ b/lib/xilinx/xcuseries/configuration_column.cc
@@ -0,0 +1,52 @@
+#include <prjxray/xilinx/xcuseries/configuration_column.h>
+
+namespace prjxray {
+namespace xilinx {
+namespace xcuseries {
+
+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 xcuseries
+} // namespace xilinx
+} // namespace prjxray
+
+namespace xcuseries = prjxray::xilinx::xcuseries;
+
+namespace YAML {
+
+Node convert<xcuseries::ConfigurationColumn>::encode(
+ const xcuseries::ConfigurationColumn& rhs) {
+ Node node;
+ node.SetTag("xilinx/xcuseries/configuration_column");
+ node["frame_count"] = rhs.frame_count_;
+ return node;
+}
+
+bool convert<xcuseries::ConfigurationColumn>::decode(
+ const Node& node,
+ xcuseries::ConfigurationColumn& lhs) {
+ if (!node.Tag().empty() &&
+ node.Tag() != "xilinx/xcuseries/configuration_column") {
+ return false;
+ }
+
+ lhs.frame_count_ = node["frame_count"].as<unsigned int>();
+ return true;
+}
+
+} // namespace YAML
diff --git a/lib/xilinx/xcuseries/configuration_row.cc b/lib/xilinx/xcuseries/configuration_row.cc
new file mode 100644
index 0000000..3d47216
--- /dev/null
+++ b/lib/xilinx/xcuseries/configuration_row.cc
@@ -0,0 +1,62 @@
+#include <prjxray/xilinx/xcuseries/configuration_row.h>
+
+namespace prjxray {
+namespace xilinx {
+namespace xcuseries {
+
+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 xcuseries
+} // namespace xilinx
+} // namespace prjxray
+
+namespace xcuseries = prjxray::xilinx::xcuseries;
+
+namespace YAML {
+
+Node convert<xcuseries::Row>::encode(const xcuseries::Row& rhs) {
+ Node node;
+ node.SetTag("xilinx/xcuseries/row");
+ node["configuration_buses"] = rhs.configuration_buses_;
+ return node;
+}
+
+bool convert<xcuseries::Row>::decode(const Node& node, xcuseries::Row& lhs) {
+ if (!node.Tag().empty() && node.Tag() != "xilinx/xcuseries/row") {
+ return false;
+ }
+
+ lhs.configuration_buses_ =
+ node["configuration_buses"]
+ .as<std::map<xcuseries::BlockType,
+ xcuseries::ConfigurationBus>>();
+ return true;
+}
+
+} // namespace YAML
diff --git a/lib/xilinx/xcuseries/ecc.cc b/lib/xilinx/xcuseries/ecc.cc
new file mode 100644
index 0000000..3f4455a
--- /dev/null
+++ b/lib/xilinx/xcuseries/ecc.cc
@@ -0,0 +1,60 @@
+#include <prjxray/xilinx/xcuseries/ecc.h>
+#include <cassert>
+#include <cstdio>
+
+namespace prjxray {
+namespace xilinx {
+namespace xcuseries {
+
+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 = xcuseries::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 xcuseries
+} // namespace xilinx
+} // namespace prjxray
diff --git a/lib/xilinx/xcuseries/frame_address.cc b/lib/xilinx/xcuseries/frame_address.cc
new file mode 100644
index 0000000..e59c93b
--- /dev/null
+++ b/lib/xilinx/xcuseries/frame_address.cc
@@ -0,0 +1,101 @@
+#include <prjxray/xilinx/xcuseries/frame_address.h>
+
+#include <iomanip>
+
+#include <prjxray/bit_ops.h>
+
+namespace prjxray {
+namespace xilinx {
+namespace xcuseries {
+
+FrameAddress::FrameAddress(BlockType block_type,
+ uint8_t row,
+ uint16_t column,
+ uint8_t minor) {
+ address_ = bit_field_set(0,
+ BLOCK_TYPE_HIGH,
+ BLOCK_TYPE_LOW,
+ block_type);
+
+ address_ = bit_field_set(address_,
+ ROW_HIGH,
+ ROW_LOW,
+ row);
+
+ address_ = bit_field_set(address_,
+ COLUMN_HIGH,
+ COLUMN_LOW,
+ column);
+
+ address_ = bit_field_set(address_,
+ MINOR_HIGH,
+ MINOR_LOW,
+ minor);
+}
+
+BlockType FrameAddress::block_type() const {
+ return static_cast<BlockType>(bit_field_get(address_, BLOCK_TYPE_HIGH, BLOCK_TYPE_LOW));
+}
+
+bool FrameAddress::is_bottom_half_rows() const {
+ return bit_field_get(address_, ROW_HIGH, ROW_HIGH);
+}
+
+uint8_t FrameAddress::row() const {
+ return bit_field_get(address_, ROW_HIGH, ROW_LOW);
+}
+
+uint16_t FrameAddress::column() const {
+ return bit_field_get(address_, COLUMN_HIGH, COLUMN_LOW);
+}
+
+uint8_t FrameAddress::minor() const {
+ return bit_field_get(address_, MINOR_HIGH, MINOR_LOW);
+}
+
+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 xcuseries
+} // namespace xilinx
+} // namespace prjxray
+
+namespace YAML {
+
+namespace xcuseries = prjxray::xilinx::xcuseries;
+
+Node convert<xcuseries::FrameAddress>::encode(
+ const xcuseries::FrameAddress& rhs) {
+ Node node;
+ node.SetTag("xilinx/xcuseries/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<xcuseries::FrameAddress>::decode(const Node& node,
+ xcuseries::FrameAddress& lhs) {
+ if (!(node.Tag() == "xilinx/xcuseries/frame_address" ||
+ node.Tag() == "xilinx/xcuseries/configuration_frame_address") ||
+ !node["block_type"] || !node["row"] ||
+ !node["column"] || !node["minor"])
+ return false;
+
+ lhs = prjxray::xilinx::xcuseries::FrameAddress(
+ node["block_type"].as<xcuseries::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/xcuseries/part.cc b/lib/xilinx/xcuseries/part.cc
new file mode 100644
index 0000000..277ba7c
--- /dev/null
+++ b/lib/xilinx/xcuseries/part.cc
@@ -0,0 +1,113 @@
+#include <prjxray/xilinx/xcuseries/part.h>
+
+#include <iomanip>
+#include <sstream>
+
+namespace prjxray {
+namespace xilinx {
+namespace xcuseries {
+
+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 {
+ auto addr_row = rows_.find(address.row());
+ if (addr_row == rows_.end())
+ return false;
+ return addr_row->second.IsValidFrameAddress(address);
+}
+
+absl::optional<FrameAddress> Part::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;
+ }
+
+ // Block types are next numerically.
+ if (address.block_type() < BlockType::BLOCK_RAM) {
+ next_address =
+ FrameAddress(BlockType::BLOCK_RAM, 0, 0, 0);
+ if (IsValidFrameAddress(*next_address))
+ return next_address;
+ }
+
+ if (address.block_type() < BlockType::CFG_CLB) {
+ next_address = FrameAddress(BlockType::CFG_CLB, 0, 0, 0);
+ if (IsValidFrameAddress(*next_address))
+ return next_address;
+ }
+
+ return {};
+}
+
+} // namespace xcuseries
+} // namespace xilinx
+} // namespace prjxray
+
+namespace xcuseries = prjxray::xilinx::xcuseries;
+
+namespace YAML {
+
+Node convert<xcuseries::Part>::encode(const xcuseries::Part& rhs) {
+ Node node;
+ node.SetTag("xilinx/xcuseries/part");
+
+ std::ostringstream idcode_str;
+ idcode_str << "0x" << std::hex << rhs.idcode_;
+ node["idcode"] = idcode_str.str();
+ node["rows"] = rhs.rows_;
+ return node;
+}
+
+bool convert<xcuseries::Part>::decode(const Node& node, xcuseries::Part& lhs) {
+ if (!node.Tag().empty() && node.Tag() != "xilinx/xcuseries/part")
+ return false;
+
+ if (!node["rows"] && !node["configuration_ranges"]) {
+ return false;
+ }
+
+ lhs.idcode_ = node["idcode"].as<uint32_t>();
+ if (node["rows"]) {
+ lhs.rows_ = node["rows"].as<std::map<unsigned int, xcuseries::Row>>();
+ } else if (node["configuration_ranges"]) {
+ std::vector<xcuseries::FrameAddress> addresses;
+ for (auto range : node["configuration_ranges"]) {
+ auto begin =
+ range["begin"].as<xcuseries::FrameAddress>();
+ auto end = range["end"].as<xcuseries::FrameAddress>();
+ for (uint32_t cur = begin; cur < end; ++cur) {
+ addresses.push_back(cur);
+ }
+ }
+
+ lhs = xcuseries::Part(lhs.idcode_, addresses);
+ }
+
+ return true;
+};
+
+} // namespace YAML
diff --git a/tools/CMakeLists.txt b/tools/CMakeLists.txt
index 69c5508..3ea6ef0 100644
--- a/tools/CMakeLists.txt
+++ b/tools/CMakeLists.txt
@@ -7,8 +7,11 @@
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(xc7_frame_address_decoder xcu_frame_address_decoder.cc)
+target_link_libraries(xc7_frame_address_decoder libprjxray)
+
+add_executable(xcu_frame_address_decoder xc7_frame_address_decoder.cc)
+target_link_libraries(xcu_frame_address_decoder libprjxray)
add_executable(gen_part_base_yaml gen_part_base_yaml.cc)
target_link_libraries(gen_part_base_yaml
@@ -18,16 +21,16 @@
yaml-cpp
gflags
)
-add_executable(xc7patch xc7patch.cc)
-target_link_libraries(xc7patch
+add_executable(xcpatch xcpatch.cc)
+target_link_libraries(xcpatch
absl::strings
absl::time
gflags
libprjxray
)
-add_executable(xc7frames2bit xc7frames2bit.cc)
-target_link_libraries(xc7frames2bit
+add_executable(xcframes2bit xcframes2bit.cc)
+target_link_libraries(xcframes2bit
absl::strings
absl::time
gflags
diff --git a/tools/frame_address_decoder.cc b/tools/xc7_frame_address_decoder.cc
similarity index 88%
rename from tools/frame_address_decoder.cc
rename to tools/xc7_frame_address_decoder.cc
index b3c21e9..79c429e 100644
--- a/tools/frame_address_decoder.cc
+++ b/tools/xc7_frame_address_decoder.cc
@@ -6,7 +6,7 @@
namespace xc7series = prjxray::xilinx::xc7series;
-void frame_address_decode(std::istream* input_stream) {
+void xc7_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);
@@ -29,12 +29,12 @@
if (argc > 1) {
std::ifstream file_stream(argv[1]);
if (file_stream) {
- frame_address_decode(&file_stream);
+ xc7_frame_address_decode(&file_stream);
return 0;
}
}
- frame_address_decode(&std::cin);
+ xc7_frame_address_decode(&std::cin);
return 0;
}
diff --git a/tools/xc7frames2bit.cc b/tools/xcframes2bit.cc
similarity index 100%
rename from tools/xc7frames2bit.cc
rename to tools/xcframes2bit.cc
diff --git a/tools/xc7patch.cc b/tools/xcpatch.cc
similarity index 95%
rename from tools/xc7patch.cc
rename to tools/xcpatch.cc
index 369ddac..6069584 100644
--- a/tools/xc7patch.cc
+++ b/tools/xcpatch.cc
@@ -8,7 +8,7 @@
#include <prjxray/xilinx/configuration.h>
DEFINE_string(part_name, "", "");
-DEFINE_string(part_file, "", "Definition file for target 7-series part");
+DEFINE_string(part_file, "", "Definition file for target part.");
DEFINE_string(bitstream_file,
"",
"Initial bitstream to which the deltas are applied.");
@@ -76,8 +76,8 @@
xilinx::BitstreamReader<ArchType>::InitWithBytes(
bitstream_file->as_bytes());
if (!bitstream_reader) {
- std::cout << "Bitstream does not appear to be a "
- "7-series bitstream!"
+ std::cout << "Bitstream does not appear to be compatible"
+ "with the current architecture!"
<< std::endl;
return 1;
}
@@ -128,7 +128,7 @@
xilinx::BitstreamWriter<ArchType>(configuration_package);
if (bitstream_writer.writeBitstream(
configuration_package, FLAGS_part_name, FLAGS_frm_file,
- "xc7patch", FLAGS_output_file)) {
+ "xcpatch", FLAGS_output_file)) {
std::cerr << "Failed to write bitstream" << std::endl
<< "Exitting" << std::endl;
}
diff --git a/tools/frame_address_decoder.cc b/tools/xcu_frame_address_decoder.cc
similarity index 66%
copy from tools/frame_address_decoder.cc
copy to tools/xcu_frame_address_decoder.cc
index b3c21e9..ffe02e8 100644
--- a/tools/frame_address_decoder.cc
+++ b/tools/xcu_frame_address_decoder.cc
@@ -2,18 +2,16 @@
#include <iomanip>
#include <iostream>
-#include <prjxray/xilinx/xc7series/frame_address.h>
+#include <prjxray/xilinx/xcuseries/frame_address.h>
-namespace xc7series = prjxray::xilinx::xc7series;
+namespace xcuseries = prjxray::xilinx::xcuseries;
-void frame_address_decode(std::istream* input_stream) {
+void xcu_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);
+ xcuseries::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
@@ -29,12 +27,12 @@
if (argc > 1) {
std::ifstream file_stream(argv[1]);
if (file_stream) {
- frame_address_decode(&file_stream);
+ xcu_frame_address_decode(&file_stream);
return 0;
}
}
- frame_address_decode(&std::cin);
+ xcu_frame_address_decode(&std::cin);
return 0;
}