xcutools: separation of xc7 in xc7, xcu and xcup libraries Signed-off-by: Alessandro Comodi <acomodi@antmicro.com>
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..ad3e0d3 --- /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["global_clock_regions"] && !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..4f7e92e --- /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["global_clock_regions"] && !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; }