mirror of https://github.com/openXC7/prjxray.git
xc7series: refactor Part()
ConfigurationFrameAddress row-half and row fields translate directly to
the clock region structure used in 7-series parts. Break these concepts
out into separate classes and encode them as such in Part YAML.
Columns within a row are more complicated. Column indices are relative
to a BlockType. Think of each BlockType as a data bus that terminates at
some particular tile type (e.g. CLB_IO_CLK maps to INT_{L,R} tiles).
Column indices act as addresses of endpoints on the associated BlockType
bus. As the bus is 1 frame (101 words, 404 bytes) wide, each endpoint
feeds frames into multiple tiles simultaneously.
Minor addresses are frame addresses within a BlockType bus endpoint.
These can refer to frames either stored within the endpoint tiles
or in tiles chained behind them.
Note that a given tile can be connected on multiple BlockType buses.
For example, block RAMs appear to be attached both by being chained
behind an INT_{L,R} on the CLB_IO_CLK bus as well as being a direct
endpoint on the BLOCK_RAM bus. Due to this, tiles conceptually belong
to the row rather than a single column.
Signed-off-by: Rick Altherr <kc8apf@kc8apf.net>
This commit is contained in:
parent
53d7f1f80b
commit
2b34fbb35d
|
|
@ -4,11 +4,15 @@ add_library(libprjxray
|
||||||
segbits_file_reader.cc
|
segbits_file_reader.cc
|
||||||
xilinx/xc7series/bitstream_reader.cc
|
xilinx/xc7series/bitstream_reader.cc
|
||||||
xilinx/xc7series/block_type.cc
|
xilinx/xc7series/block_type.cc
|
||||||
xilinx/xc7series/frame_address.cc
|
xilinx/xc7series/configuration_bus.cc
|
||||||
|
xilinx/xc7series/configuration_column.cc
|
||||||
xilinx/xc7series/configuration_frame_range.cc
|
xilinx/xc7series/configuration_frame_range.cc
|
||||||
xilinx/xc7series/configuration_packet.cc
|
xilinx/xc7series/configuration_packet.cc
|
||||||
xilinx/xc7series/configuration_register.cc
|
xilinx/xc7series/configuration_register.cc
|
||||||
|
xilinx/xc7series/frame_address.cc
|
||||||
|
xilinx/xc7series/global_clock_region.cc
|
||||||
xilinx/xc7series/part.cc
|
xilinx/xc7series/part.cc
|
||||||
|
xilinx/xc7series/row.cc
|
||||||
)
|
)
|
||||||
target_include_directories(libprjxray PUBLIC "include")
|
target_include_directories(libprjxray PUBLIC "include")
|
||||||
target_link_libraries(libprjxray absl::optional absl::strings absl::span yaml-cpp)
|
target_link_libraries(libprjxray absl::optional absl::strings absl::span yaml-cpp)
|
||||||
|
|
@ -40,9 +44,13 @@ if (PRJXRAY_BUILD_TESTING)
|
||||||
xilinx/xc7series/bitstream_reader_test.cc
|
xilinx/xc7series/bitstream_reader_test.cc
|
||||||
xilinx/xc7series/block_type_test.cc
|
xilinx/xc7series/block_type_test.cc
|
||||||
xilinx/xc7series/frame_address_test.cc
|
xilinx/xc7series/frame_address_test.cc
|
||||||
|
xilinx/xc7series/configuration_bus_test.cc
|
||||||
|
xilinx/xc7series/configuration_column_test.cc
|
||||||
xilinx/xc7series/configuration_test.cc
|
xilinx/xc7series/configuration_test.cc
|
||||||
xilinx/xc7series/configuration_packet_test.cc
|
xilinx/xc7series/configuration_packet_test.cc
|
||||||
xilinx/xc7series/part_test.cc)
|
xilinx/xc7series/global_clock_region_test.cc
|
||||||
|
xilinx/xc7series/part_test.cc
|
||||||
|
xilinx/xc7series/row_test.cc)
|
||||||
target_link_libraries(xilinx_xc7series_test libprjxray gtest_main)
|
target_link_libraries(xilinx_xc7series_test libprjxray gtest_main)
|
||||||
add_test(NAME xilinx_xc7series_test
|
add_test(NAME xilinx_xc7series_test
|
||||||
COMMAND xilinx_xc7series_test
|
COMMAND xilinx_xc7series_test
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,93 @@
|
||||||
|
#ifndef PRJXRAY_LIB_XILINX_XC7SERIES_CONFIGURATION_BUS_H_
|
||||||
|
#define PRJXRAY_LIB_XILINX_XC7SERIES_CONFIGURATION_BUS_H_
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <cassert>
|
||||||
|
#include <map>
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
#include <absl/types/optional.h>
|
||||||
|
#include <prjxray/xilinx/xc7series/configuration_column.h>
|
||||||
|
#include <prjxray/xilinx/xc7series/frame_address.h>
|
||||||
|
#include <yaml-cpp/yaml.h>
|
||||||
|
|
||||||
|
namespace prjxray {
|
||||||
|
namespace xilinx {
|
||||||
|
namespace xc7series {
|
||||||
|
|
||||||
|
// ConfigurationBus represents a bus for sending frames to a specific BlockType
|
||||||
|
// within a Row. An instance of ConfigurationBus will contain one or more
|
||||||
|
// ConfigurationColumns.
|
||||||
|
class ConfigurationBus {
|
||||||
|
public:
|
||||||
|
ConfigurationBus() = default;
|
||||||
|
|
||||||
|
// Constructs a ConfigurationBus from iterators yielding
|
||||||
|
// FrameAddresses. The frame address need not be contiguous or sorted
|
||||||
|
// but they must all have the same block type, row half, and row
|
||||||
|
// address components.
|
||||||
|
template <typename T>
|
||||||
|
ConfigurationBus(T first, T last);
|
||||||
|
|
||||||
|
// Returns true if the provided address falls into a valid segment of
|
||||||
|
// the address range on this bus. Only the column and minor components
|
||||||
|
// of the address are considered as all other components are outside
|
||||||
|
// the scope of a bus.
|
||||||
|
bool IsValidFrameAddress(FrameAddress address) const;
|
||||||
|
|
||||||
|
// Returns the next valid address on the bus in numerically increasing
|
||||||
|
// order. If the next address would fall outside this bus, no object is
|
||||||
|
// returned.
|
||||||
|
absl::optional<FrameAddress> GetNextFrameAddress(
|
||||||
|
FrameAddress address) const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
friend class YAML::convert<ConfigurationBus>;
|
||||||
|
|
||||||
|
std::map<unsigned int, ConfigurationColumn> configuration_columns_;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
ConfigurationBus::ConfigurationBus(T first, T last) {
|
||||||
|
assert(
|
||||||
|
std::all_of(first, last, [&](const typename T::value_type& addr) {
|
||||||
|
return (addr.block_type() == first->block_type() &&
|
||||||
|
addr.is_bottom_half_rows() ==
|
||||||
|
first->is_bottom_half_rows() &&
|
||||||
|
addr.row() == first->row());
|
||||||
|
}));
|
||||||
|
|
||||||
|
std::sort(first, last,
|
||||||
|
[](const FrameAddress& lhs, const FrameAddress& rhs) {
|
||||||
|
return lhs.column() < rhs.column();
|
||||||
|
});
|
||||||
|
|
||||||
|
for (auto col_first = first; col_first != last;) {
|
||||||
|
auto col_last = std::upper_bound(
|
||||||
|
col_first, last, col_first->column(),
|
||||||
|
[](const unsigned int& lhs, const FrameAddress& rhs) {
|
||||||
|
return lhs < rhs.column();
|
||||||
|
});
|
||||||
|
|
||||||
|
configuration_columns_.emplace(
|
||||||
|
col_first->column(),
|
||||||
|
std::move(ConfigurationColumn(col_first, col_last)));
|
||||||
|
col_first = col_last;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace xc7series
|
||||||
|
} // namespace xilinx
|
||||||
|
} // namespace prjxray
|
||||||
|
|
||||||
|
namespace YAML {
|
||||||
|
template <>
|
||||||
|
struct convert<prjxray::xilinx::xc7series::ConfigurationBus> {
|
||||||
|
static Node encode(
|
||||||
|
const prjxray::xilinx::xc7series::ConfigurationBus& rhs);
|
||||||
|
static bool decode(const Node& node,
|
||||||
|
prjxray::xilinx::xc7series::ConfigurationBus& lhs);
|
||||||
|
};
|
||||||
|
} // namespace YAML
|
||||||
|
|
||||||
|
#endif // PRJXRAY_LIB_XILINX_XC7SERIES_CONFIGURATION_BUS_H_
|
||||||
|
|
@ -0,0 +1,78 @@
|
||||||
|
#ifndef PRJXRAY_LIB_XILINX_XC7SERIES_CONFIGURATION_COLUMN_H_
|
||||||
|
#define PRJXRAY_LIB_XILINX_XC7SERIES_CONFIGURATION_COLUMN_H_
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <cassert>
|
||||||
|
|
||||||
|
#include <absl/types/optional.h>
|
||||||
|
#include <prjxray/xilinx/xc7series/frame_address.h>
|
||||||
|
#include <yaml-cpp/yaml.h>
|
||||||
|
|
||||||
|
namespace prjxray {
|
||||||
|
namespace xilinx {
|
||||||
|
namespace xc7series {
|
||||||
|
|
||||||
|
// ConfigurationColumn represents an endpoint on a ConfigurationBus.
|
||||||
|
class ConfigurationColumn {
|
||||||
|
public:
|
||||||
|
ConfigurationColumn() = default;
|
||||||
|
ConfigurationColumn(unsigned int frame_count)
|
||||||
|
: frame_count_(frame_count) {}
|
||||||
|
|
||||||
|
// Returns a ConfigurationColumn that describes a continguous range of
|
||||||
|
// minor addresses that encompasses the given
|
||||||
|
// FrameAddresses. The provided addresses must only
|
||||||
|
// differ only by their minor addresses.
|
||||||
|
template <typename T>
|
||||||
|
ConfigurationColumn(T first, T last);
|
||||||
|
|
||||||
|
// Returns true if the minor field of the address is within the valid
|
||||||
|
// range of this column.
|
||||||
|
bool IsValidFrameAddress(FrameAddress address) const;
|
||||||
|
|
||||||
|
// Returns the next address in numerical order. If the next address
|
||||||
|
// would be outside this column, return no object.
|
||||||
|
absl::optional<FrameAddress> GetNextFrameAddress(
|
||||||
|
FrameAddress address) const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
friend class YAML::convert<ConfigurationColumn>;
|
||||||
|
|
||||||
|
unsigned int frame_count_;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
ConfigurationColumn::ConfigurationColumn(T first, T last) {
|
||||||
|
assert(
|
||||||
|
std::all_of(first, last, [&](const typename T::value_type& addr) {
|
||||||
|
return (addr.block_type() == first->block_type() &&
|
||||||
|
addr.is_bottom_half_rows() ==
|
||||||
|
first->is_bottom_half_rows() &&
|
||||||
|
addr.row() == first->row() &&
|
||||||
|
addr.column() == first->column());
|
||||||
|
}));
|
||||||
|
|
||||||
|
auto max_minor = std::max_element(
|
||||||
|
first, last, [](const FrameAddress& lhs, const FrameAddress& rhs) {
|
||||||
|
return lhs.minor() < rhs.minor();
|
||||||
|
});
|
||||||
|
|
||||||
|
frame_count_ = max_minor->minor() + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace xc7series
|
||||||
|
} // namespace xilinx
|
||||||
|
} // namespace prjxray
|
||||||
|
|
||||||
|
namespace YAML {
|
||||||
|
template <>
|
||||||
|
struct convert<prjxray::xilinx::xc7series::ConfigurationColumn> {
|
||||||
|
static Node encode(
|
||||||
|
const prjxray::xilinx::xc7series::ConfigurationColumn& rhs);
|
||||||
|
static bool decode(
|
||||||
|
const Node& node,
|
||||||
|
prjxray::xilinx::xc7series::ConfigurationColumn& lhs);
|
||||||
|
};
|
||||||
|
} // namespace YAML
|
||||||
|
|
||||||
|
#endif // PRJXRAY_LIB_XILINX_XC7SERIES_CONFIGURATION_COLUMN_H_
|
||||||
|
|
@ -0,0 +1,93 @@
|
||||||
|
#ifndef PRJXRAY_LIB_XILINX_XC7SERIES_GLOBAL_CLOCK_REGION_H_
|
||||||
|
#define PRJXRAY_LIB_XILINX_XC7SERIES_GLOBAL_CLOCK_REGION_H_
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <cassert>
|
||||||
|
#include <map>
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
#include <absl/types/optional.h>
|
||||||
|
#include <prjxray/xilinx/xc7series/frame_address.h>
|
||||||
|
#include <prjxray/xilinx/xc7series/row.h>
|
||||||
|
#include <yaml-cpp/yaml.h>
|
||||||
|
|
||||||
|
namespace prjxray {
|
||||||
|
namespace xilinx {
|
||||||
|
namespace xc7series {
|
||||||
|
|
||||||
|
// GlobalClockRegion represents all the resources associated with a single
|
||||||
|
// global clock buffer (BUFG) tile. In 7-Series FPGAs, there are two BUFG
|
||||||
|
// tiles that divide the chip into top and bottom "halves". Each half may
|
||||||
|
// contains any number of rows, buses, and columns.
|
||||||
|
class GlobalClockRegion {
|
||||||
|
public:
|
||||||
|
GlobalClockRegion() = default;
|
||||||
|
|
||||||
|
// Construct a GlobalClockRegion from iterators that yield
|
||||||
|
// FrameAddresses which are known to be valid. The addresses may be
|
||||||
|
// noncontinguous and/or unordered but they must share the same row
|
||||||
|
// half address component.
|
||||||
|
template <typename T>
|
||||||
|
GlobalClockRegion(T first, T last);
|
||||||
|
|
||||||
|
// Returns true if the address falls within a valid range inside the
|
||||||
|
// global clock region. The row half address component is ignored as it
|
||||||
|
// is outside the context of a global clock region.
|
||||||
|
bool IsValidFrameAddress(FrameAddress address) const;
|
||||||
|
|
||||||
|
// Returns the next numerically increasing address known within this
|
||||||
|
// global clock region. If the next address would fall outside this
|
||||||
|
// global clock region, no address is returned. If the next address
|
||||||
|
// would jump to a different block type, no address is returned as the
|
||||||
|
// same block type in other global clock regions come numerically
|
||||||
|
// before other block types.
|
||||||
|
absl::optional<FrameAddress> GetNextFrameAddress(
|
||||||
|
FrameAddress address) const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
friend class YAML::convert<GlobalClockRegion>;
|
||||||
|
|
||||||
|
std::map<unsigned int, Row> rows_;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
GlobalClockRegion::GlobalClockRegion(T first, T last) {
|
||||||
|
assert(
|
||||||
|
std::all_of(first, last, [&](const typename T::value_type& addr) {
|
||||||
|
return addr.is_bottom_half_rows() ==
|
||||||
|
first->is_bottom_half_rows();
|
||||||
|
}));
|
||||||
|
|
||||||
|
std::sort(first, last,
|
||||||
|
[](const FrameAddress& lhs, const FrameAddress& rhs) {
|
||||||
|
return lhs.row() < rhs.row();
|
||||||
|
});
|
||||||
|
|
||||||
|
for (auto row_first = first; row_first != last;) {
|
||||||
|
auto row_last = std::upper_bound(
|
||||||
|
row_first, last, row_first->row(),
|
||||||
|
[](const uint8_t& lhs, const FrameAddress& rhs) {
|
||||||
|
return lhs < rhs.row();
|
||||||
|
});
|
||||||
|
|
||||||
|
rows_.emplace(row_first->row(),
|
||||||
|
std::move(Row(row_first, row_last)));
|
||||||
|
row_first = row_last;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace xc7series
|
||||||
|
} // namespace xilinx
|
||||||
|
} // namespace prjxray
|
||||||
|
|
||||||
|
namespace YAML {
|
||||||
|
template <>
|
||||||
|
struct convert<prjxray::xilinx::xc7series::GlobalClockRegion> {
|
||||||
|
static Node encode(
|
||||||
|
const prjxray::xilinx::xc7series::GlobalClockRegion& rhs);
|
||||||
|
static bool decode(const Node& node,
|
||||||
|
prjxray::xilinx::xc7series::GlobalClockRegion& lhs);
|
||||||
|
};
|
||||||
|
} // namespace YAML
|
||||||
|
|
||||||
|
#endif // PRJXRAY_LIB_XILINX_XC7SERIES_GLOBAL_CLOCK_REGION_H_
|
||||||
|
|
@ -1,11 +1,15 @@
|
||||||
#ifndef PRJXRAY_LIB_XILINX_XC7SERIES_PART_H_
|
#ifndef PRJXRAY_LIB_XILINX_XC7SERIES_PART_H_
|
||||||
#define PRJXRAY_LIB_XILINX_XC7SERIES_PART_H_
|
#define PRJXRAY_LIB_XILINX_XC7SERIES_PART_H_
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <cstdint>
|
||||||
|
#include <memory>
|
||||||
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include <absl/types/optional.h>
|
#include <absl/types/optional.h>
|
||||||
#include <prjxray/xilinx/xc7series/configuration_frame_range.h>
|
#include <prjxray/xilinx/xc7series/global_clock_region.h>
|
||||||
#include <prjxray/xilinx/xc7series/frame_address.h>
|
#include <yaml-cpp/yaml.h>
|
||||||
|
|
||||||
namespace prjxray {
|
namespace prjxray {
|
||||||
namespace xilinx {
|
namespace xilinx {
|
||||||
|
|
@ -13,33 +17,47 @@ namespace xc7series {
|
||||||
|
|
||||||
class Part {
|
class Part {
|
||||||
public:
|
public:
|
||||||
|
constexpr static uint32_t kInvalidIdcode = 0;
|
||||||
|
|
||||||
static absl::optional<Part> FromFile(const std::string& path);
|
static absl::optional<Part> FromFile(const std::string& path);
|
||||||
|
|
||||||
// Constructs an invalid part with a zero IDCODE. Required for YAML
|
// Constructs an invalid part with a zero IDCODE. Required for YAML
|
||||||
// conversion but shouldn't be used otherwise.
|
// conversion but shouldn't be used otherwise.
|
||||||
Part() : idcode_(0), frame_ranges_() {}
|
Part() : idcode_(kInvalidIdcode) {}
|
||||||
|
|
||||||
Part(uint32_t idcode,
|
template <typename T>
|
||||||
const std::vector<ConfigurationFrameRange>& ranges)
|
Part(uint32_t idcode, T collection)
|
||||||
: idcode_(idcode), frame_ranges_(std::move(ranges)) {}
|
: 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_; }
|
uint32_t idcode() const { return idcode_; }
|
||||||
|
|
||||||
const std::vector<ConfigurationFrameRange>& configuration_frame_ranges()
|
bool IsValidFrameAddress(FrameAddress address) const;
|
||||||
const {
|
|
||||||
return frame_ranges_;
|
|
||||||
}
|
|
||||||
|
|
||||||
absl::optional<FrameAddress> GetNextFrameAddress(
|
absl::optional<FrameAddress> GetNextFrameAddress(
|
||||||
FrameAddress address) const;
|
FrameAddress address) const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
uint32_t idcode_;
|
friend class YAML::convert<Part>;
|
||||||
|
|
||||||
// Ranges are expected to be non-overlapping.
|
uint32_t idcode_;
|
||||||
std::vector<ConfigurationFrameRange> frame_ranges_;
|
GlobalClockRegion top_region_;
|
||||||
|
GlobalClockRegion bottom_region_;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
Part::Part(uint32_t idcode, T first, T last) : idcode_(idcode) {
|
||||||
|
auto first_of_top =
|
||||||
|
std::partition(first, last, [](const FrameAddress& addr) {
|
||||||
|
return addr.is_bottom_half_rows();
|
||||||
|
});
|
||||||
|
|
||||||
|
top_region_ = GlobalClockRegion(first_of_top, last);
|
||||||
|
bottom_region_ = GlobalClockRegion(first, first_of_top);
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace xc7series
|
} // namespace xc7series
|
||||||
} // namespace xilinx
|
} // namespace xilinx
|
||||||
} // namespace prjxray
|
} // namespace prjxray
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,90 @@
|
||||||
|
#ifndef PRJXRAY_LIB_XILINX_XC7SERIES_ROW_H_
|
||||||
|
#define PRJXRAY_LIB_XILINX_XC7SERIES_ROW_H_
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <cassert>
|
||||||
|
#include <map>
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
#include <absl/types/optional.h>
|
||||||
|
#include <prjxray/xilinx/xc7series/block_type.h>
|
||||||
|
#include <prjxray/xilinx/xc7series/configuration_bus.h>
|
||||||
|
#include <prjxray/xilinx/xc7series/frame_address.h>
|
||||||
|
#include <yaml-cpp/yaml.h>
|
||||||
|
|
||||||
|
namespace prjxray {
|
||||||
|
namespace xilinx {
|
||||||
|
namespace xc7series {
|
||||||
|
|
||||||
|
class Row {
|
||||||
|
public:
|
||||||
|
Row() = default;
|
||||||
|
|
||||||
|
// Construct a row from a range of iterators that yield FrameAddresses.
|
||||||
|
// The addresses may be noncontinguous and/or unsorted but all must
|
||||||
|
// share the same row half and row components.
|
||||||
|
template <typename T>
|
||||||
|
Row(T first, T last);
|
||||||
|
|
||||||
|
// Returns true if the provided address falls within a valid range
|
||||||
|
// attributed to this row. Only the block type, column, and minor
|
||||||
|
// address components are considerd as the remaining components are
|
||||||
|
// outside the scope of a row.
|
||||||
|
bool IsValidFrameAddress(FrameAddress address) const;
|
||||||
|
|
||||||
|
// Returns the next numerically increasing address within the Row. If
|
||||||
|
// the next address would fall outside the Row, no object is returned.
|
||||||
|
// If the next address would cross from one block type to another, no
|
||||||
|
// object is returned as other rows of the same block type come before
|
||||||
|
// other block types numerically.
|
||||||
|
absl::optional<FrameAddress> GetNextFrameAddress(
|
||||||
|
FrameAddress address) const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
friend class YAML::convert<Row>;
|
||||||
|
|
||||||
|
std::map<BlockType, ConfigurationBus> configuration_buses_;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
Row::Row(T first, T last) {
|
||||||
|
assert(
|
||||||
|
std::all_of(first, last, [&](const typename T::value_type& addr) {
|
||||||
|
return (addr.is_bottom_half_rows() ==
|
||||||
|
first->is_bottom_half_rows() &&
|
||||||
|
addr.row() == first->row());
|
||||||
|
}));
|
||||||
|
|
||||||
|
std::sort(first, last,
|
||||||
|
[](const FrameAddress& lhs, const FrameAddress& rhs) {
|
||||||
|
return lhs.block_type() < rhs.block_type();
|
||||||
|
});
|
||||||
|
|
||||||
|
for (auto bus_first = first; bus_first != last;) {
|
||||||
|
auto bus_last = std::upper_bound(
|
||||||
|
bus_first, last, bus_first->block_type(),
|
||||||
|
[](const BlockType& lhs, const FrameAddress& rhs) {
|
||||||
|
return lhs < rhs.block_type();
|
||||||
|
});
|
||||||
|
|
||||||
|
configuration_buses_.emplace(
|
||||||
|
bus_first->block_type(),
|
||||||
|
std::move(ConfigurationBus(bus_first, bus_last)));
|
||||||
|
bus_first = bus_last;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace xc7series
|
||||||
|
} // namespace xilinx
|
||||||
|
} // namespace prjxray
|
||||||
|
|
||||||
|
namespace YAML {
|
||||||
|
template <>
|
||||||
|
struct convert<prjxray::xilinx::xc7series::Row> {
|
||||||
|
static Node encode(const prjxray::xilinx::xc7series::Row& rhs);
|
||||||
|
static bool decode(const Node& node,
|
||||||
|
prjxray::xilinx::xc7series::Row& lhs);
|
||||||
|
};
|
||||||
|
} // namespace YAML
|
||||||
|
|
||||||
|
#endif // PRJXRAY_LIB_XILINX_XC7SERIES_ROW_H_
|
||||||
|
|
@ -0,0 +1,77 @@
|
||||||
|
#include <prjxray/xilinx/xc7series/configuration_bus.h>
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
namespace prjxray {
|
||||||
|
namespace xilinx {
|
||||||
|
namespace xc7series {
|
||||||
|
|
||||||
|
bool ConfigurationBus::IsValidFrameAddress(FrameAddress address) const {
|
||||||
|
auto addr_column = configuration_columns_.find(address.column());
|
||||||
|
if (addr_column == configuration_columns_.end())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return addr_column->second.IsValidFrameAddress(address);
|
||||||
|
}
|
||||||
|
|
||||||
|
absl::optional<FrameAddress> ConfigurationBus::GetNextFrameAddress(
|
||||||
|
FrameAddress address) const {
|
||||||
|
// Find the column for the current address.
|
||||||
|
auto addr_column = configuration_columns_.find(address.column());
|
||||||
|
|
||||||
|
// If the current address isn't in a known column, no way to know the
|
||||||
|
// next address.
|
||||||
|
if (addr_column == configuration_columns_.end())
|
||||||
|
return {};
|
||||||
|
|
||||||
|
// Ask the column for the next address.
|
||||||
|
absl::optional<FrameAddress> next_address =
|
||||||
|
addr_column->second.GetNextFrameAddress(address);
|
||||||
|
if (next_address)
|
||||||
|
return next_address;
|
||||||
|
|
||||||
|
// The current column doesn't know what the next address is. Assume
|
||||||
|
// that the next valid address is the beginning of the next column.
|
||||||
|
if (++addr_column != configuration_columns_.end()) {
|
||||||
|
auto next_address = FrameAddress(
|
||||||
|
address.block_type(), address.is_bottom_half_rows(),
|
||||||
|
address.row(), addr_column->first, 0);
|
||||||
|
if (addr_column->second.IsValidFrameAddress(next_address))
|
||||||
|
return next_address;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Not in this bus.
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace xc7series
|
||||||
|
} // namespace xilinx
|
||||||
|
} // namespace prjxray
|
||||||
|
|
||||||
|
namespace xc7series = prjxray::xilinx::xc7series;
|
||||||
|
|
||||||
|
namespace YAML {
|
||||||
|
|
||||||
|
Node convert<xc7series::ConfigurationBus>::encode(
|
||||||
|
const xc7series::ConfigurationBus& rhs) {
|
||||||
|
Node node;
|
||||||
|
node.SetTag("xilinx/xc7series/configuration_bus");
|
||||||
|
node["configuration_columns"] = rhs.configuration_columns_;
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool convert<xc7series::ConfigurationBus>::decode(
|
||||||
|
const Node& node,
|
||||||
|
xc7series::ConfigurationBus& lhs) {
|
||||||
|
if (!node.Tag().empty() &&
|
||||||
|
node.Tag() != "xilinx/xc7series/configuration_bus") {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
lhs.configuration_columns_ =
|
||||||
|
node["configuration_columns"]
|
||||||
|
.as<std::map<unsigned int, xc7series::ConfigurationColumn>>();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace YAML
|
||||||
|
|
@ -0,0 +1,72 @@
|
||||||
|
#include <prjxray/xilinx/xc7series/configuration_bus.h>
|
||||||
|
|
||||||
|
#include <gtest/gtest.h>
|
||||||
|
|
||||||
|
namespace xc7series = prjxray::xilinx::xc7series;
|
||||||
|
|
||||||
|
TEST(ConfigurationBusTest, IsValidFrameAddress) {
|
||||||
|
std::vector<xc7series::FrameAddress> addresses;
|
||||||
|
addresses.push_back(xc7series::FrameAddress(
|
||||||
|
xc7series::BlockType::BLOCK_RAM, false, 0, 0, 0));
|
||||||
|
addresses.push_back(xc7series::FrameAddress(
|
||||||
|
xc7series::BlockType::BLOCK_RAM, false, 0, 0, 1));
|
||||||
|
addresses.push_back(xc7series::FrameAddress(
|
||||||
|
xc7series::BlockType::BLOCK_RAM, false, 0, 1, 0));
|
||||||
|
addresses.push_back(xc7series::FrameAddress(
|
||||||
|
xc7series::BlockType::BLOCK_RAM, false, 0, 1, 1));
|
||||||
|
|
||||||
|
xc7series::ConfigurationBus bus(addresses.begin(), addresses.end());
|
||||||
|
|
||||||
|
EXPECT_TRUE(bus.IsValidFrameAddress(xc7series::FrameAddress(
|
||||||
|
xc7series::BlockType::BLOCK_RAM, false, 0, 0, 0)));
|
||||||
|
EXPECT_TRUE(bus.IsValidFrameAddress(xc7series::FrameAddress(
|
||||||
|
xc7series::BlockType::BLOCK_RAM, false, 0, 1, 1)));
|
||||||
|
|
||||||
|
EXPECT_FALSE(bus.IsValidFrameAddress(xc7series::FrameAddress(
|
||||||
|
xc7series::BlockType::BLOCK_RAM, false, 0, 0, 2)));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(ConfigurationBusTest, GetNextFrameAddressYieldNextAddressInBus) {
|
||||||
|
std::vector<xc7series::FrameAddress> addresses;
|
||||||
|
addresses.push_back(xc7series::FrameAddress(
|
||||||
|
xc7series::BlockType::BLOCK_RAM, false, 0, 0, 0));
|
||||||
|
addresses.push_back(xc7series::FrameAddress(
|
||||||
|
xc7series::BlockType::BLOCK_RAM, false, 0, 0, 1));
|
||||||
|
addresses.push_back(xc7series::FrameAddress(
|
||||||
|
xc7series::BlockType::BLOCK_RAM, false, 0, 1, 0));
|
||||||
|
addresses.push_back(xc7series::FrameAddress(
|
||||||
|
xc7series::BlockType::BLOCK_RAM, false, 0, 1, 1));
|
||||||
|
|
||||||
|
xc7series::ConfigurationBus bus(addresses.begin(), addresses.end());
|
||||||
|
|
||||||
|
auto next_address = bus.GetNextFrameAddress(xc7series::FrameAddress(
|
||||||
|
xc7series::BlockType::BLOCK_RAM, false, 0, 0, 0));
|
||||||
|
ASSERT_TRUE(next_address);
|
||||||
|
EXPECT_EQ(*next_address,
|
||||||
|
xc7series::FrameAddress(xc7series::BlockType::BLOCK_RAM,
|
||||||
|
false, 0, 0, 1));
|
||||||
|
|
||||||
|
next_address = bus.GetNextFrameAddress(xc7series::FrameAddress(
|
||||||
|
xc7series::BlockType::BLOCK_RAM, false, 0, 0, 1));
|
||||||
|
ASSERT_TRUE(next_address);
|
||||||
|
EXPECT_EQ(*next_address,
|
||||||
|
xc7series::FrameAddress(xc7series::BlockType::BLOCK_RAM,
|
||||||
|
false, 0, 1, 0));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(ConfigurationBusTest, GetNextFrameAddressYieldNothingAtEndOfBus) {
|
||||||
|
std::vector<xc7series::FrameAddress> addresses;
|
||||||
|
addresses.push_back(xc7series::FrameAddress(
|
||||||
|
xc7series::BlockType::BLOCK_RAM, false, 0, 0, 0));
|
||||||
|
addresses.push_back(xc7series::FrameAddress(
|
||||||
|
xc7series::BlockType::BLOCK_RAM, false, 0, 0, 1));
|
||||||
|
addresses.push_back(xc7series::FrameAddress(
|
||||||
|
xc7series::BlockType::BLOCK_RAM, false, 0, 1, 0));
|
||||||
|
addresses.push_back(xc7series::FrameAddress(
|
||||||
|
xc7series::BlockType::BLOCK_RAM, false, 0, 1, 1));
|
||||||
|
|
||||||
|
xc7series::ConfigurationBus bus(addresses.begin(), addresses.end());
|
||||||
|
|
||||||
|
EXPECT_FALSE(bus.GetNextFrameAddress(xc7series::FrameAddress(
|
||||||
|
xc7series::BlockType::BLOCK_RAM, false, 0, 1, 1)));
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,52 @@
|
||||||
|
#include <prjxray/xilinx/xc7series/configuration_column.h>
|
||||||
|
|
||||||
|
namespace prjxray {
|
||||||
|
namespace xilinx {
|
||||||
|
namespace xc7series {
|
||||||
|
|
||||||
|
bool ConfigurationColumn::IsValidFrameAddress(FrameAddress address) const {
|
||||||
|
return address.minor() < frame_count_;
|
||||||
|
}
|
||||||
|
|
||||||
|
absl::optional<FrameAddress> ConfigurationColumn::GetNextFrameAddress(
|
||||||
|
FrameAddress address) const {
|
||||||
|
if (!IsValidFrameAddress(address))
|
||||||
|
return {};
|
||||||
|
|
||||||
|
if (static_cast<unsigned int>(address.minor() + 1) < frame_count_) {
|
||||||
|
return address + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Next address is not in this column.
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace xc7series
|
||||||
|
} // namespace xilinx
|
||||||
|
} // namespace prjxray
|
||||||
|
|
||||||
|
namespace xc7series = prjxray::xilinx::xc7series;
|
||||||
|
|
||||||
|
namespace YAML {
|
||||||
|
|
||||||
|
Node convert<xc7series::ConfigurationColumn>::encode(
|
||||||
|
const xc7series::ConfigurationColumn& rhs) {
|
||||||
|
Node node;
|
||||||
|
node.SetTag("xilinx/xc7series/configuration_column");
|
||||||
|
node["frame_count"] = rhs.frame_count_;
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool convert<xc7series::ConfigurationColumn>::decode(
|
||||||
|
const Node& node,
|
||||||
|
xc7series::ConfigurationColumn& lhs) {
|
||||||
|
if (!node.Tag().empty() &&
|
||||||
|
node.Tag() != "xilinx/xc7series/configuration_column") {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
lhs.frame_count_ = node["frame_count"].as<unsigned int>();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace YAML
|
||||||
|
|
@ -0,0 +1,65 @@
|
||||||
|
#include <prjxray/xilinx/xc7series/configuration_column.h>
|
||||||
|
|
||||||
|
#include <gtest/gtest.h>
|
||||||
|
#include <prjxray/xilinx/xc7series/block_type.h>
|
||||||
|
#include <prjxray/xilinx/xc7series/frame_address.h>
|
||||||
|
#include <yaml-cpp/yaml.h>
|
||||||
|
|
||||||
|
namespace xc7series = prjxray::xilinx::xc7series;
|
||||||
|
|
||||||
|
TEST(ConfigurationColumnTest, IsValidFrameAddress) {
|
||||||
|
xc7series::ConfigurationColumn column(10);
|
||||||
|
|
||||||
|
// Inside this column.
|
||||||
|
EXPECT_TRUE(column.IsValidFrameAddress(xc7series::FrameAddress(
|
||||||
|
xc7series::BlockType::CLB_IO_CLK, false, 1, 2, 3)));
|
||||||
|
// Past this column's frame width.
|
||||||
|
EXPECT_FALSE(column.IsValidFrameAddress(xc7series::FrameAddress(
|
||||||
|
xc7series::BlockType::CLB_IO_CLK, false, 1, 2, 10)));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(ConfigurationColumnTest, GetNextFrameAddressYieldNextAddressInColumn) {
|
||||||
|
xc7series::ConfigurationColumn column(10);
|
||||||
|
|
||||||
|
auto next_address = column.GetNextFrameAddress(xc7series::FrameAddress(
|
||||||
|
xc7series::BlockType::CLB_IO_CLK, false, 1, 2, 3));
|
||||||
|
EXPECT_TRUE(next_address);
|
||||||
|
EXPECT_EQ(*next_address,
|
||||||
|
xc7series::FrameAddress(xc7series::BlockType::CLB_IO_CLK,
|
||||||
|
false, 1, 2, 4));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(ConfigurationColumnTest, GetNextFrameAddressYieldNothingAtEndOfColumn) {
|
||||||
|
xc7series::ConfigurationColumn column(10);
|
||||||
|
|
||||||
|
EXPECT_FALSE(column.GetNextFrameAddress(xc7series::FrameAddress(
|
||||||
|
xc7series::BlockType::CLB_IO_CLK, false, 1, 2, 9)));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(ConfigurationColumnTest, GetNextFrameAddressYieldNothingOutsideColumn) {
|
||||||
|
xc7series::ConfigurationColumn column(10);
|
||||||
|
|
||||||
|
// Just past last frame in column.
|
||||||
|
EXPECT_FALSE(column.GetNextFrameAddress(xc7series::FrameAddress(
|
||||||
|
xc7series::BlockType::CLB_IO_CLK, false, 1, 2, 10)));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(ConfigurationColumnTest, YamlEncodeTest) {
|
||||||
|
xc7series::ConfigurationColumn column(10);
|
||||||
|
|
||||||
|
YAML::Node node(column);
|
||||||
|
EXPECT_TRUE(node["frame_count"]);
|
||||||
|
EXPECT_EQ(node["frame_count"].as<int>(), 10);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(ConfigurationColumnTest, YAMLDecodeTest) {
|
||||||
|
YAML::Node node;
|
||||||
|
node.SetTag("xilinx/xc7series/configuration_column");
|
||||||
|
node["frame_count"] = 10;
|
||||||
|
|
||||||
|
auto column = node.as<xc7series::ConfigurationColumn>();
|
||||||
|
EXPECT_TRUE(column.GetNextFrameAddress(xc7series::FrameAddress(
|
||||||
|
xc7series::BlockType::CLB_IO_CLK, false, 1, 2, 8)));
|
||||||
|
EXPECT_FALSE(column.GetNextFrameAddress(xc7series::FrameAddress(
|
||||||
|
xc7series::BlockType::CLB_IO_CLK, false, 1, 2, 9)));
|
||||||
|
}
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
#include <prjxray/xilinx/xc7series/configuration.h>
|
#include <prjxray/xilinx/xc7series/configuration.h>
|
||||||
|
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
|
#include <iostream>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include <absl/types/span.h>
|
#include <absl/types/span.h>
|
||||||
|
|
@ -8,16 +9,18 @@
|
||||||
#include <prjxray/memory_mapped_file.h>
|
#include <prjxray/memory_mapped_file.h>
|
||||||
#include <prjxray/xilinx/xc7series/configuration_packet.h>
|
#include <prjxray/xilinx/xc7series/configuration_packet.h>
|
||||||
#include <prjxray/xilinx/xc7series/configuration_register.h>
|
#include <prjxray/xilinx/xc7series/configuration_register.h>
|
||||||
|
#include <prjxray/xilinx/xc7series/frame_address.h>
|
||||||
#include <prjxray/xilinx/xc7series/part.h>
|
#include <prjxray/xilinx/xc7series/part.h>
|
||||||
|
#include <yaml-cpp/yaml.h>
|
||||||
|
|
||||||
namespace xc7series = prjxray::xilinx::xc7series;
|
namespace xc7series = prjxray::xilinx::xc7series;
|
||||||
|
|
||||||
TEST(ConfigurationTest, ConstructFromPacketsWithSingleFrame) {
|
TEST(ConfigurationTest, ConstructFromPacketsWithSingleFrame) {
|
||||||
std::vector<xc7series::ConfigurationFrameRange> test_part_ranges;
|
std::vector<xc7series::FrameAddress> test_part_addresses;
|
||||||
test_part_ranges.push_back(
|
test_part_addresses.push_back(0x4567);
|
||||||
xc7series::ConfigurationFrameRange(0x4567, 0x4568));
|
test_part_addresses.push_back(0x4568);
|
||||||
|
|
||||||
xc7series::Part test_part(0x1234, test_part_ranges);
|
xc7series::Part test_part(0x1234, test_part_addresses);
|
||||||
|
|
||||||
std::vector<uint32_t> idcode{0x1234};
|
std::vector<uint32_t> idcode{0x1234};
|
||||||
std::vector<uint32_t> cmd{0x0001};
|
std::vector<uint32_t> cmd{0x0001};
|
||||||
|
|
@ -61,13 +64,15 @@ TEST(ConfigurationTest, ConstructFromPacketsWithSingleFrame) {
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(ConfigurationTest, ConstructFromPacketsWithAutoincrement) {
|
TEST(ConfigurationTest, ConstructFromPacketsWithAutoincrement) {
|
||||||
std::vector<xc7series::ConfigurationFrameRange> test_part_ranges;
|
std::vector<xc7series::FrameAddress> test_part_addresses;
|
||||||
test_part_ranges.push_back(
|
for (int ii = 0x4560; ii < 0x4570; ++ii) {
|
||||||
xc7series::ConfigurationFrameRange(0x4560, 0x4570));
|
test_part_addresses.push_back(ii);
|
||||||
test_part_ranges.push_back(
|
}
|
||||||
xc7series::ConfigurationFrameRange(0x4580, 0x4590));
|
for (int ii = 0x4580; ii < 0x4590; ++ii) {
|
||||||
|
test_part_addresses.push_back(ii);
|
||||||
|
}
|
||||||
|
|
||||||
xc7series::Part test_part(0x1234, test_part_ranges);
|
xc7series::Part test_part(0x1234, test_part_addresses);
|
||||||
|
|
||||||
std::vector<uint32_t> idcode{0x1234};
|
std::vector<uint32_t> idcode{0x1234};
|
||||||
std::vector<uint32_t> cmd{0x0001};
|
std::vector<uint32_t> cmd{0x0001};
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,71 @@
|
||||||
|
#include <prjxray/xilinx/xc7series/global_clock_region.h>
|
||||||
|
|
||||||
|
namespace prjxray {
|
||||||
|
namespace xilinx {
|
||||||
|
namespace xc7series {
|
||||||
|
|
||||||
|
bool GlobalClockRegion::IsValidFrameAddress(FrameAddress address) const {
|
||||||
|
auto addr_row = rows_.find(address.row());
|
||||||
|
if (addr_row == rows_.end())
|
||||||
|
return false;
|
||||||
|
return addr_row->second.IsValidFrameAddress(address);
|
||||||
|
}
|
||||||
|
|
||||||
|
absl::optional<FrameAddress> GlobalClockRegion::GetNextFrameAddress(
|
||||||
|
FrameAddress address) const {
|
||||||
|
// Find the row for the current address.
|
||||||
|
auto addr_row = rows_.find(address.row());
|
||||||
|
|
||||||
|
// If the current address isn't in a known row, no way to know the next.
|
||||||
|
if (addr_row == rows_.end())
|
||||||
|
return {};
|
||||||
|
|
||||||
|
// Ask the row for the next address.
|
||||||
|
absl::optional<FrameAddress> next_address =
|
||||||
|
addr_row->second.GetNextFrameAddress(address);
|
||||||
|
if (next_address)
|
||||||
|
return next_address;
|
||||||
|
|
||||||
|
// The current row doesn't know what the next address is. Assume that
|
||||||
|
// the next valid address is the beginning of the next row.
|
||||||
|
if (++addr_row != rows_.end()) {
|
||||||
|
auto next_address = FrameAddress(address.block_type(),
|
||||||
|
address.is_bottom_half_rows(),
|
||||||
|
addr_row->first, 0, 0);
|
||||||
|
if (addr_row->second.IsValidFrameAddress(next_address))
|
||||||
|
return next_address;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Must be in a different global clock region.
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace xc7series
|
||||||
|
} // namespace xilinx
|
||||||
|
} // namespace prjxray
|
||||||
|
|
||||||
|
namespace xc7series = prjxray::xilinx::xc7series;
|
||||||
|
|
||||||
|
namespace YAML {
|
||||||
|
|
||||||
|
Node convert<xc7series::GlobalClockRegion>::encode(
|
||||||
|
const xc7series::GlobalClockRegion& rhs) {
|
||||||
|
Node node;
|
||||||
|
node.SetTag("xilinx/xc7series/global_clock_region");
|
||||||
|
node["rows"] = rhs.rows_;
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool convert<xc7series::GlobalClockRegion>::decode(
|
||||||
|
const Node& node,
|
||||||
|
xc7series::GlobalClockRegion& lhs) {
|
||||||
|
if (!node.Tag().empty() &&
|
||||||
|
node.Tag() != "xilinx/xc7series/global_clock_region") {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
lhs.rows_ = node["rows"].as<std::map<unsigned int, xc7series::Row>>();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace YAML
|
||||||
|
|
@ -0,0 +1,147 @@
|
||||||
|
#include <prjxray/xilinx/xc7series/global_clock_region.h>
|
||||||
|
|
||||||
|
#include <gtest/gtest.h>
|
||||||
|
|
||||||
|
namespace xc7series = prjxray::xilinx::xc7series;
|
||||||
|
|
||||||
|
TEST(GlobalClockRegionTest, IsValidFrameAddress) {
|
||||||
|
std::vector<xc7series::FrameAddress> addresses;
|
||||||
|
addresses.push_back(xc7series::FrameAddress(
|
||||||
|
xc7series::BlockType::CLB_IO_CLK, false, 0, 0, 0));
|
||||||
|
addresses.push_back(xc7series::FrameAddress(
|
||||||
|
xc7series::BlockType::CLB_IO_CLK, false, 0, 0, 1));
|
||||||
|
addresses.push_back(xc7series::FrameAddress(
|
||||||
|
xc7series::BlockType::CLB_IO_CLK, false, 0, 1, 0));
|
||||||
|
addresses.push_back(xc7series::FrameAddress(
|
||||||
|
xc7series::BlockType::CLB_IO_CLK, false, 0, 1, 1));
|
||||||
|
addresses.push_back(xc7series::FrameAddress(
|
||||||
|
xc7series::BlockType::BLOCK_RAM, false, 0, 0, 0));
|
||||||
|
addresses.push_back(xc7series::FrameAddress(
|
||||||
|
xc7series::BlockType::BLOCK_RAM, false, 0, 0, 1));
|
||||||
|
addresses.push_back(xc7series::FrameAddress(
|
||||||
|
xc7series::BlockType::BLOCK_RAM, false, 0, 0, 2));
|
||||||
|
addresses.push_back(xc7series::FrameAddress(
|
||||||
|
xc7series::BlockType::CLB_IO_CLK, false, 1, 0, 0));
|
||||||
|
addresses.push_back(xc7series::FrameAddress(
|
||||||
|
xc7series::BlockType::CLB_IO_CLK, false, 1, 0, 1));
|
||||||
|
|
||||||
|
xc7series::GlobalClockRegion global_clock_region(addresses.begin(),
|
||||||
|
addresses.end());
|
||||||
|
|
||||||
|
EXPECT_TRUE(
|
||||||
|
global_clock_region.IsValidFrameAddress(xc7series::FrameAddress(
|
||||||
|
xc7series::BlockType::CLB_IO_CLK, false, 0, 0, 0)));
|
||||||
|
EXPECT_TRUE(
|
||||||
|
global_clock_region.IsValidFrameAddress(xc7series::FrameAddress(
|
||||||
|
xc7series::BlockType::CLB_IO_CLK, false, 0, 1, 0)));
|
||||||
|
EXPECT_TRUE(
|
||||||
|
global_clock_region.IsValidFrameAddress(xc7series::FrameAddress(
|
||||||
|
xc7series::BlockType::BLOCK_RAM, false, 0, 0, 2)));
|
||||||
|
EXPECT_TRUE(
|
||||||
|
global_clock_region.IsValidFrameAddress(xc7series::FrameAddress(
|
||||||
|
xc7series::BlockType::CLB_IO_CLK, false, 1, 0, 0)));
|
||||||
|
|
||||||
|
EXPECT_FALSE(
|
||||||
|
global_clock_region.IsValidFrameAddress(xc7series::FrameAddress(
|
||||||
|
xc7series::BlockType::CLB_IO_CLK, false, 0, 0, 2)));
|
||||||
|
EXPECT_FALSE(
|
||||||
|
global_clock_region.IsValidFrameAddress(xc7series::FrameAddress(
|
||||||
|
xc7series::BlockType::CLB_IO_CLK, false, 0, 2, 0)));
|
||||||
|
EXPECT_FALSE(
|
||||||
|
global_clock_region.IsValidFrameAddress(xc7series::FrameAddress(
|
||||||
|
xc7series::BlockType::CLB_IO_CLK, false, 2, 0, 0)));
|
||||||
|
EXPECT_FALSE(
|
||||||
|
global_clock_region.IsValidFrameAddress(xc7series::FrameAddress(
|
||||||
|
xc7series::BlockType::CFG_CLB, false, 0, 0, 2)));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(GlobalClockRegionTest,
|
||||||
|
GetNextFrameAddressYieldNextAddressInGlobalClockRegion) {
|
||||||
|
std::vector<xc7series::FrameAddress> addresses;
|
||||||
|
addresses.push_back(xc7series::FrameAddress(
|
||||||
|
xc7series::BlockType::CLB_IO_CLK, false, 0, 0, 0));
|
||||||
|
addresses.push_back(xc7series::FrameAddress(
|
||||||
|
xc7series::BlockType::CLB_IO_CLK, false, 0, 0, 1));
|
||||||
|
addresses.push_back(xc7series::FrameAddress(
|
||||||
|
xc7series::BlockType::CLB_IO_CLK, false, 0, 1, 0));
|
||||||
|
addresses.push_back(xc7series::FrameAddress(
|
||||||
|
xc7series::BlockType::CLB_IO_CLK, false, 0, 1, 1));
|
||||||
|
addresses.push_back(xc7series::FrameAddress(
|
||||||
|
xc7series::BlockType::BLOCK_RAM, false, 0, 0, 0));
|
||||||
|
addresses.push_back(xc7series::FrameAddress(
|
||||||
|
xc7series::BlockType::BLOCK_RAM, false, 0, 0, 1));
|
||||||
|
addresses.push_back(xc7series::FrameAddress(
|
||||||
|
xc7series::BlockType::BLOCK_RAM, false, 0, 0, 2));
|
||||||
|
addresses.push_back(xc7series::FrameAddress(
|
||||||
|
xc7series::BlockType::CLB_IO_CLK, false, 1, 0, 0));
|
||||||
|
addresses.push_back(xc7series::FrameAddress(
|
||||||
|
xc7series::BlockType::CLB_IO_CLK, false, 1, 0, 1));
|
||||||
|
|
||||||
|
xc7series::GlobalClockRegion global_clock_region(addresses.begin(),
|
||||||
|
addresses.end());
|
||||||
|
|
||||||
|
auto next_address =
|
||||||
|
global_clock_region.GetNextFrameAddress(xc7series::FrameAddress(
|
||||||
|
xc7series::BlockType::CLB_IO_CLK, false, 0, 0, 0));
|
||||||
|
ASSERT_TRUE(next_address);
|
||||||
|
EXPECT_EQ(*next_address,
|
||||||
|
xc7series::FrameAddress(xc7series::BlockType::CLB_IO_CLK,
|
||||||
|
false, 0, 0, 1));
|
||||||
|
|
||||||
|
next_address =
|
||||||
|
global_clock_region.GetNextFrameAddress(xc7series::FrameAddress(
|
||||||
|
xc7series::BlockType::CLB_IO_CLK, false, 0, 0, 1));
|
||||||
|
ASSERT_TRUE(next_address);
|
||||||
|
EXPECT_EQ(*next_address,
|
||||||
|
xc7series::FrameAddress(xc7series::BlockType::CLB_IO_CLK,
|
||||||
|
false, 0, 1, 0));
|
||||||
|
|
||||||
|
next_address =
|
||||||
|
global_clock_region.GetNextFrameAddress(xc7series::FrameAddress(
|
||||||
|
xc7series::BlockType::CLB_IO_CLK, false, 0, 1, 1));
|
||||||
|
ASSERT_TRUE(next_address);
|
||||||
|
EXPECT_EQ(*next_address,
|
||||||
|
xc7series::FrameAddress(xc7series::BlockType::CLB_IO_CLK,
|
||||||
|
false, 1, 0, 0));
|
||||||
|
|
||||||
|
next_address =
|
||||||
|
global_clock_region.GetNextFrameAddress(xc7series::FrameAddress(
|
||||||
|
xc7series::BlockType::BLOCK_RAM, false, 0, 0, 1));
|
||||||
|
ASSERT_TRUE(next_address);
|
||||||
|
EXPECT_EQ(*next_address,
|
||||||
|
xc7series::FrameAddress(xc7series::BlockType::BLOCK_RAM,
|
||||||
|
false, 0, 0, 2));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(GlobalClockRegionTest,
|
||||||
|
GetNextFrameAddressYieldNothingAtEndOfGlobalClockRegion) {
|
||||||
|
std::vector<xc7series::FrameAddress> addresses;
|
||||||
|
addresses.push_back(xc7series::FrameAddress(
|
||||||
|
xc7series::BlockType::CLB_IO_CLK, false, 0, 0, 0));
|
||||||
|
addresses.push_back(xc7series::FrameAddress(
|
||||||
|
xc7series::BlockType::CLB_IO_CLK, false, 0, 0, 1));
|
||||||
|
addresses.push_back(xc7series::FrameAddress(
|
||||||
|
xc7series::BlockType::CLB_IO_CLK, false, 0, 1, 0));
|
||||||
|
addresses.push_back(xc7series::FrameAddress(
|
||||||
|
xc7series::BlockType::CLB_IO_CLK, false, 0, 1, 1));
|
||||||
|
addresses.push_back(xc7series::FrameAddress(
|
||||||
|
xc7series::BlockType::BLOCK_RAM, false, 0, 0, 0));
|
||||||
|
addresses.push_back(xc7series::FrameAddress(
|
||||||
|
xc7series::BlockType::BLOCK_RAM, false, 0, 0, 1));
|
||||||
|
addresses.push_back(xc7series::FrameAddress(
|
||||||
|
xc7series::BlockType::BLOCK_RAM, false, 0, 0, 2));
|
||||||
|
addresses.push_back(xc7series::FrameAddress(
|
||||||
|
xc7series::BlockType::CLB_IO_CLK, false, 1, 0, 0));
|
||||||
|
addresses.push_back(xc7series::FrameAddress(
|
||||||
|
xc7series::BlockType::CLB_IO_CLK, false, 1, 0, 1));
|
||||||
|
|
||||||
|
xc7series::GlobalClockRegion global_clock_region(addresses.begin(),
|
||||||
|
addresses.end());
|
||||||
|
|
||||||
|
EXPECT_FALSE(
|
||||||
|
global_clock_region.GetNextFrameAddress(xc7series::FrameAddress(
|
||||||
|
xc7series::BlockType::CLB_IO_CLK, false, 1, 0, 1)));
|
||||||
|
EXPECT_FALSE(
|
||||||
|
global_clock_region.GetNextFrameAddress(xc7series::FrameAddress(
|
||||||
|
xc7series::BlockType::BLOCK_RAM, false, 0, 0, 2)));
|
||||||
|
}
|
||||||
|
|
@ -16,32 +16,48 @@ absl::optional<Part> Part::FromFile(const std::string& path) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Part::IsValidFrameAddress(FrameAddress address) const {
|
||||||
|
if (address.is_bottom_half_rows()) {
|
||||||
|
return bottom_region_.IsValidFrameAddress(address);
|
||||||
|
} else {
|
||||||
|
return top_region_.IsValidFrameAddress(address);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
absl::optional<FrameAddress> Part::GetNextFrameAddress(
|
absl::optional<FrameAddress> Part::GetNextFrameAddress(
|
||||||
FrameAddress address) const {
|
FrameAddress address) const {
|
||||||
// Start with the next linear address.
|
// Ask the current global clock region first.
|
||||||
FrameAddress target_address(address + 1);
|
absl::optional<FrameAddress> next_address =
|
||||||
|
(address.is_bottom_half_rows()
|
||||||
|
? bottom_region_.GetNextFrameAddress(address)
|
||||||
|
: top_region_.GetNextFrameAddress(address));
|
||||||
|
if (next_address)
|
||||||
|
return next_address;
|
||||||
|
|
||||||
// The address space is non-continguous. If the next linear address
|
// If the current address is in the top region, the bottom region is
|
||||||
// happens to fall in a valid range, that's the next address.
|
// next numerically.
|
||||||
// Otherwise, find the closest valid range and use it's beginning
|
if (!address.is_bottom_half_rows()) {
|
||||||
// address.
|
next_address =
|
||||||
absl::optional<FrameAddress> closest_address;
|
FrameAddress(address.block_type(), true, 0, 0, 0);
|
||||||
int32_t closest_distance;
|
if (bottom_region_.IsValidFrameAddress(*next_address))
|
||||||
for (auto iter = frame_ranges_.begin(); iter != frame_ranges_.end();
|
return next_address;
|
||||||
++iter) {
|
|
||||||
if (iter->Contains(target_address)) {
|
|
||||||
return target_address;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int32_t distance = iter->begin() - target_address;
|
// Block types are next numerically.
|
||||||
if (distance > 0 &&
|
if (address.block_type() < BlockType::BLOCK_RAM) {
|
||||||
(!closest_address || distance < closest_distance)) {
|
next_address =
|
||||||
closest_address = iter->begin();
|
FrameAddress(BlockType::BLOCK_RAM, false, 0, 0, 0);
|
||||||
closest_distance = distance;
|
if (IsValidFrameAddress(*next_address))
|
||||||
}
|
return next_address;
|
||||||
}
|
}
|
||||||
|
|
||||||
return closest_address;
|
if (address.block_type() < BlockType::CFG_CLB) {
|
||||||
|
next_address = FrameAddress(BlockType::CFG_CLB, false, 0, 0, 0);
|
||||||
|
if (IsValidFrameAddress(*next_address))
|
||||||
|
return next_address;
|
||||||
|
}
|
||||||
|
|
||||||
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace xc7series
|
} // namespace xc7series
|
||||||
|
|
@ -57,23 +73,43 @@ Node convert<xc7series::Part>::encode(const xc7series::Part& rhs) {
|
||||||
node.SetTag("xilinx/xc7series/part");
|
node.SetTag("xilinx/xc7series/part");
|
||||||
|
|
||||||
std::ostringstream idcode_str;
|
std::ostringstream idcode_str;
|
||||||
idcode_str << "0x" << std::hex << rhs.idcode();
|
idcode_str << "0x" << std::hex << rhs.idcode_;
|
||||||
node["idcode"] = idcode_str.str();
|
node["idcode"] = idcode_str.str();
|
||||||
|
node["global_clock_regions"]["top"] = rhs.top_region_;
|
||||||
node["configuration_ranges"] = rhs.configuration_frame_ranges();
|
node["global_clock_regions"]["bottom"] = rhs.bottom_region_;
|
||||||
return node;
|
return node;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool convert<xc7series::Part>::decode(const Node& node, xc7series::Part& lhs) {
|
bool convert<xc7series::Part>::decode(const Node& node, xc7series::Part& lhs) {
|
||||||
if (node.Tag() != "xilinx/xc7series/part" || !node["idcode"] ||
|
if (!node.Tag().empty() && node.Tag() != "xilinx/xc7series/part")
|
||||||
!node["configuration_ranges"])
|
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
lhs = xc7series::Part(
|
if (!node["global_clock_regions"] && !node["configuration_ranges"]) {
|
||||||
node["idcode"].as<uint32_t>(),
|
return false;
|
||||||
node["configuration_ranges"]
|
}
|
||||||
.as<std::vector<xc7series::ConfigurationFrameRange>>());
|
|
||||||
|
lhs.idcode_ = node["idcode"].as<uint32_t>();
|
||||||
|
|
||||||
|
if (node["global_clock_regions"]) {
|
||||||
|
lhs.top_region_ = node["global_clock_regions"]["top"]
|
||||||
|
.as<xc7series::GlobalClockRegion>();
|
||||||
|
lhs.bottom_region_ = node["global_clock_regions"]["bottom"]
|
||||||
|
.as<xc7series::GlobalClockRegion>();
|
||||||
|
} else if (node["configuration_ranges"]) {
|
||||||
|
std::vector<xc7series::FrameAddress> addresses;
|
||||||
|
for (auto range : node["configuration_ranges"]) {
|
||||||
|
auto begin =
|
||||||
|
range["begin"].as<xc7series::FrameAddress>();
|
||||||
|
auto end = range["end"].as<xc7series::FrameAddress>();
|
||||||
|
for (uint32_t cur = begin; cur < end; ++cur) {
|
||||||
|
addresses.push_back(cur);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
lhs = xc7series::Part(lhs.idcode_, addresses);
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
};
|
||||||
|
|
||||||
} // namespace YAML
|
} // namespace YAML
|
||||||
|
|
|
||||||
|
|
@ -4,37 +4,146 @@
|
||||||
|
|
||||||
namespace xc7series = prjxray::xilinx::xc7series;
|
namespace xc7series = prjxray::xilinx::xc7series;
|
||||||
|
|
||||||
TEST(PartTest, GetNextAddressWhereNextIsInValidRange) {
|
TEST(PartTest, IsValidFrameAddress) {
|
||||||
std::vector<xc7series::ConfigurationFrameRange> ranges;
|
std::vector<xc7series::FrameAddress> addresses;
|
||||||
ranges.push_back(xc7series::ConfigurationFrameRange(0x0, 0xF));
|
addresses.push_back(xc7series::FrameAddress(
|
||||||
ranges.push_back(xc7series::ConfigurationFrameRange(0x20, 0x2F));
|
xc7series::BlockType::CLB_IO_CLK, false, 0, 0, 0));
|
||||||
|
addresses.push_back(xc7series::FrameAddress(
|
||||||
|
xc7series::BlockType::CLB_IO_CLK, false, 0, 0, 1));
|
||||||
|
addresses.push_back(xc7series::FrameAddress(
|
||||||
|
xc7series::BlockType::CLB_IO_CLK, false, 0, 1, 0));
|
||||||
|
addresses.push_back(xc7series::FrameAddress(
|
||||||
|
xc7series::BlockType::CLB_IO_CLK, false, 0, 1, 1));
|
||||||
|
addresses.push_back(xc7series::FrameAddress(
|
||||||
|
xc7series::BlockType::BLOCK_RAM, false, 0, 0, 0));
|
||||||
|
addresses.push_back(xc7series::FrameAddress(
|
||||||
|
xc7series::BlockType::BLOCK_RAM, false, 0, 0, 1));
|
||||||
|
addresses.push_back(xc7series::FrameAddress(
|
||||||
|
xc7series::BlockType::BLOCK_RAM, false, 0, 0, 2));
|
||||||
|
addresses.push_back(xc7series::FrameAddress(
|
||||||
|
xc7series::BlockType::CLB_IO_CLK, false, 1, 0, 0));
|
||||||
|
addresses.push_back(xc7series::FrameAddress(
|
||||||
|
xc7series::BlockType::CLB_IO_CLK, false, 1, 0, 1));
|
||||||
|
addresses.push_back(xc7series::FrameAddress(
|
||||||
|
xc7series::BlockType::CLB_IO_CLK, true, 0, 0, 0));
|
||||||
|
addresses.push_back(xc7series::FrameAddress(
|
||||||
|
xc7series::BlockType::CLB_IO_CLK, true, 0, 0, 1));
|
||||||
|
|
||||||
xc7series::Part part(0x1234, ranges);
|
xc7series::Part part(0x1234, addresses.begin(), addresses.end());
|
||||||
|
|
||||||
auto next_address = part.GetNextFrameAddress(0x4);
|
EXPECT_TRUE(part.IsValidFrameAddress(xc7series::FrameAddress(
|
||||||
EXPECT_TRUE(next_address);
|
xc7series::BlockType::CLB_IO_CLK, false, 0, 0, 0)));
|
||||||
EXPECT_EQ(static_cast<uint32_t>(0x5), *next_address);
|
EXPECT_TRUE(part.IsValidFrameAddress(xc7series::FrameAddress(
|
||||||
|
xc7series::BlockType::CLB_IO_CLK, false, 0, 1, 0)));
|
||||||
|
EXPECT_TRUE(part.IsValidFrameAddress(xc7series::FrameAddress(
|
||||||
|
xc7series::BlockType::BLOCK_RAM, false, 0, 0, 2)));
|
||||||
|
EXPECT_TRUE(part.IsValidFrameAddress(xc7series::FrameAddress(
|
||||||
|
xc7series::BlockType::CLB_IO_CLK, false, 1, 0, 0)));
|
||||||
|
EXPECT_TRUE(part.IsValidFrameAddress(xc7series::FrameAddress(
|
||||||
|
xc7series::BlockType::CLB_IO_CLK, true, 0, 0, 0)));
|
||||||
|
|
||||||
|
EXPECT_FALSE(part.IsValidFrameAddress(xc7series::FrameAddress(
|
||||||
|
xc7series::BlockType::CLB_IO_CLK, false, 0, 0, 2)));
|
||||||
|
EXPECT_FALSE(part.IsValidFrameAddress(xc7series::FrameAddress(
|
||||||
|
xc7series::BlockType::CLB_IO_CLK, false, 0, 2, 0)));
|
||||||
|
EXPECT_FALSE(part.IsValidFrameAddress(xc7series::FrameAddress(
|
||||||
|
xc7series::BlockType::CLB_IO_CLK, false, 2, 0, 0)));
|
||||||
|
EXPECT_FALSE(part.IsValidFrameAddress(xc7series::FrameAddress(
|
||||||
|
xc7series::BlockType::CFG_CLB, false, 0, 0, 2)));
|
||||||
|
EXPECT_FALSE(part.IsValidFrameAddress(xc7series::FrameAddress(
|
||||||
|
xc7series::BlockType::CLB_IO_CLK, true, 0, 1, 0)));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(PartTest, GetNextAddressWhereNextIsBetweenRanges) {
|
TEST(PartTest, GetNextFrameAddressYieldNextAddressInPart) {
|
||||||
std::vector<xc7series::ConfigurationFrameRange> ranges;
|
std::vector<xc7series::FrameAddress> addresses;
|
||||||
ranges.push_back(xc7series::ConfigurationFrameRange(0x0, 0xF));
|
addresses.push_back(xc7series::FrameAddress(
|
||||||
ranges.push_back(xc7series::ConfigurationFrameRange(0x20, 0x2F));
|
xc7series::BlockType::CLB_IO_CLK, false, 0, 0, 0));
|
||||||
|
addresses.push_back(xc7series::FrameAddress(
|
||||||
|
xc7series::BlockType::CLB_IO_CLK, false, 0, 0, 1));
|
||||||
|
addresses.push_back(xc7series::FrameAddress(
|
||||||
|
xc7series::BlockType::CLB_IO_CLK, false, 0, 1, 0));
|
||||||
|
addresses.push_back(xc7series::FrameAddress(
|
||||||
|
xc7series::BlockType::CLB_IO_CLK, false, 0, 1, 1));
|
||||||
|
addresses.push_back(xc7series::FrameAddress(
|
||||||
|
xc7series::BlockType::BLOCK_RAM, false, 0, 0, 0));
|
||||||
|
addresses.push_back(xc7series::FrameAddress(
|
||||||
|
xc7series::BlockType::BLOCK_RAM, false, 0, 0, 1));
|
||||||
|
addresses.push_back(xc7series::FrameAddress(
|
||||||
|
xc7series::BlockType::BLOCK_RAM, false, 0, 0, 2));
|
||||||
|
addresses.push_back(xc7series::FrameAddress(
|
||||||
|
xc7series::BlockType::CLB_IO_CLK, false, 1, 0, 0));
|
||||||
|
addresses.push_back(xc7series::FrameAddress(
|
||||||
|
xc7series::BlockType::CLB_IO_CLK, false, 1, 0, 1));
|
||||||
|
addresses.push_back(xc7series::FrameAddress(
|
||||||
|
xc7series::BlockType::CLB_IO_CLK, true, 0, 0, 0));
|
||||||
|
addresses.push_back(xc7series::FrameAddress(
|
||||||
|
xc7series::BlockType::CLB_IO_CLK, true, 0, 0, 1));
|
||||||
|
|
||||||
xc7series::Part part(0x1234, ranges);
|
xc7series::Part part(0x1234, addresses.begin(), addresses.end());
|
||||||
|
|
||||||
auto next_address = part.GetNextFrameAddress(0xF);
|
auto next_address = part.GetNextFrameAddress(xc7series::FrameAddress(
|
||||||
EXPECT_TRUE(next_address);
|
xc7series::BlockType::CLB_IO_CLK, false, 0, 0, 0));
|
||||||
EXPECT_EQ(static_cast<uint32_t>(0x20), *next_address);
|
ASSERT_TRUE(next_address);
|
||||||
|
EXPECT_EQ(*next_address,
|
||||||
|
xc7series::FrameAddress(xc7series::BlockType::CLB_IO_CLK,
|
||||||
|
false, 0, 0, 1));
|
||||||
|
|
||||||
|
next_address = part.GetNextFrameAddress(xc7series::FrameAddress(
|
||||||
|
xc7series::BlockType::CLB_IO_CLK, false, 0, 0, 1));
|
||||||
|
ASSERT_TRUE(next_address);
|
||||||
|
EXPECT_EQ(*next_address,
|
||||||
|
xc7series::FrameAddress(xc7series::BlockType::CLB_IO_CLK,
|
||||||
|
false, 0, 1, 0));
|
||||||
|
|
||||||
|
next_address = part.GetNextFrameAddress(xc7series::FrameAddress(
|
||||||
|
xc7series::BlockType::CLB_IO_CLK, false, 0, 1, 1));
|
||||||
|
ASSERT_TRUE(next_address);
|
||||||
|
EXPECT_EQ(*next_address,
|
||||||
|
xc7series::FrameAddress(xc7series::BlockType::CLB_IO_CLK,
|
||||||
|
false, 1, 0, 0));
|
||||||
|
|
||||||
|
next_address = part.GetNextFrameAddress(xc7series::FrameAddress(
|
||||||
|
xc7series::BlockType::CLB_IO_CLK, false, 1, 0, 1));
|
||||||
|
ASSERT_TRUE(next_address);
|
||||||
|
EXPECT_EQ(*next_address,
|
||||||
|
xc7series::FrameAddress(xc7series::BlockType::CLB_IO_CLK,
|
||||||
|
true, 0, 0, 0));
|
||||||
|
|
||||||
|
next_address = part.GetNextFrameAddress(xc7series::FrameAddress(
|
||||||
|
xc7series::BlockType::CLB_IO_CLK, true, 0, 0, 1));
|
||||||
|
ASSERT_TRUE(next_address);
|
||||||
|
EXPECT_EQ(*next_address,
|
||||||
|
xc7series::FrameAddress(xc7series::BlockType::BLOCK_RAM,
|
||||||
|
false, 0, 0, 0));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(PartTest, GetNextAddressWhereNextWouldBePastLastRange) {
|
TEST(PartTest, GetNextFrameAddressYieldNothingAtEndOfPart) {
|
||||||
std::vector<xc7series::ConfigurationFrameRange> ranges;
|
std::vector<xc7series::FrameAddress> addresses;
|
||||||
ranges.push_back(xc7series::ConfigurationFrameRange(0x0, 0xF));
|
addresses.push_back(xc7series::FrameAddress(
|
||||||
ranges.push_back(xc7series::ConfigurationFrameRange(0x20, 0x2F));
|
xc7series::BlockType::CLB_IO_CLK, false, 0, 0, 0));
|
||||||
|
addresses.push_back(xc7series::FrameAddress(
|
||||||
|
xc7series::BlockType::CLB_IO_CLK, false, 0, 0, 1));
|
||||||
|
addresses.push_back(xc7series::FrameAddress(
|
||||||
|
xc7series::BlockType::CLB_IO_CLK, false, 0, 1, 0));
|
||||||
|
addresses.push_back(xc7series::FrameAddress(
|
||||||
|
xc7series::BlockType::CLB_IO_CLK, false, 0, 1, 1));
|
||||||
|
addresses.push_back(xc7series::FrameAddress(
|
||||||
|
xc7series::BlockType::BLOCK_RAM, false, 0, 0, 0));
|
||||||
|
addresses.push_back(xc7series::FrameAddress(
|
||||||
|
xc7series::BlockType::BLOCK_RAM, false, 0, 0, 1));
|
||||||
|
addresses.push_back(xc7series::FrameAddress(
|
||||||
|
xc7series::BlockType::BLOCK_RAM, false, 0, 0, 2));
|
||||||
|
addresses.push_back(xc7series::FrameAddress(
|
||||||
|
xc7series::BlockType::CLB_IO_CLK, false, 1, 0, 0));
|
||||||
|
addresses.push_back(xc7series::FrameAddress(
|
||||||
|
xc7series::BlockType::CLB_IO_CLK, false, 1, 0, 1));
|
||||||
|
addresses.push_back(xc7series::FrameAddress(
|
||||||
|
xc7series::BlockType::CLB_IO_CLK, true, 0, 0, 0));
|
||||||
|
addresses.push_back(xc7series::FrameAddress(
|
||||||
|
xc7series::BlockType::CLB_IO_CLK, true, 0, 0, 1));
|
||||||
|
|
||||||
xc7series::Part part(0x1234, ranges);
|
xc7series::Part part(0x1234, addresses.begin(), addresses.end());
|
||||||
|
|
||||||
auto next_address = part.GetNextFrameAddress(0x2F);
|
EXPECT_FALSE(part.GetNextFrameAddress(xc7series::FrameAddress(
|
||||||
EXPECT_FALSE(next_address);
|
xc7series::BlockType::BLOCK_RAM, false, 0, 0, 2)));
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,62 @@
|
||||||
|
#include <prjxray/xilinx/xc7series/row.h>
|
||||||
|
|
||||||
|
namespace prjxray {
|
||||||
|
namespace xilinx {
|
||||||
|
namespace xc7series {
|
||||||
|
|
||||||
|
bool Row::IsValidFrameAddress(FrameAddress address) const {
|
||||||
|
auto addr_bus = configuration_buses_.find(address.block_type());
|
||||||
|
if (addr_bus == configuration_buses_.end())
|
||||||
|
return false;
|
||||||
|
return addr_bus->second.IsValidFrameAddress(address);
|
||||||
|
}
|
||||||
|
|
||||||
|
absl::optional<FrameAddress> Row::GetNextFrameAddress(
|
||||||
|
FrameAddress address) const {
|
||||||
|
// Find the bus for the current address.
|
||||||
|
auto addr_bus = configuration_buses_.find(address.block_type());
|
||||||
|
|
||||||
|
// If the current address isn't in a known bus, no way to know the next.
|
||||||
|
if (addr_bus == configuration_buses_.end())
|
||||||
|
return {};
|
||||||
|
|
||||||
|
// Ask the bus for the next address.
|
||||||
|
absl::optional<FrameAddress> next_address =
|
||||||
|
addr_bus->second.GetNextFrameAddress(address);
|
||||||
|
if (next_address)
|
||||||
|
return next_address;
|
||||||
|
|
||||||
|
// The current bus doesn't know what the next address is. Rows come next
|
||||||
|
// in frame address numerical order so punt back to the caller to figure
|
||||||
|
// it out.
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace xc7series
|
||||||
|
} // namespace xilinx
|
||||||
|
} // namespace prjxray
|
||||||
|
|
||||||
|
namespace xc7series = prjxray::xilinx::xc7series;
|
||||||
|
|
||||||
|
namespace YAML {
|
||||||
|
|
||||||
|
Node convert<xc7series::Row>::encode(const xc7series::Row& rhs) {
|
||||||
|
Node node;
|
||||||
|
node.SetTag("xilinx/xc7series/row");
|
||||||
|
node["configuration_buses"] = rhs.configuration_buses_;
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool convert<xc7series::Row>::decode(const Node& node, xc7series::Row& lhs) {
|
||||||
|
if (!node.Tag().empty() && node.Tag() != "xilinx/xc7series/row") {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
lhs.configuration_buses_ =
|
||||||
|
node["configuration_buses"]
|
||||||
|
.as<std::map<xc7series::BlockType,
|
||||||
|
xc7series::ConfigurationBus>>();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace YAML
|
||||||
|
|
@ -0,0 +1,115 @@
|
||||||
|
#include <prjxray/xilinx/xc7series/row.h>
|
||||||
|
|
||||||
|
#include <gtest/gtest.h>
|
||||||
|
|
||||||
|
namespace xc7series = prjxray::xilinx::xc7series;
|
||||||
|
|
||||||
|
TEST(RowTest, IsValidFrameAddress) {
|
||||||
|
std::vector<xc7series::FrameAddress> addresses;
|
||||||
|
addresses.push_back(xc7series::FrameAddress(
|
||||||
|
xc7series::BlockType::CLB_IO_CLK, false, 0, 0, 0));
|
||||||
|
addresses.push_back(xc7series::FrameAddress(
|
||||||
|
xc7series::BlockType::CLB_IO_CLK, false, 0, 0, 1));
|
||||||
|
addresses.push_back(xc7series::FrameAddress(
|
||||||
|
xc7series::BlockType::CLB_IO_CLK, false, 0, 1, 0));
|
||||||
|
addresses.push_back(xc7series::FrameAddress(
|
||||||
|
xc7series::BlockType::CLB_IO_CLK, false, 0, 1, 1));
|
||||||
|
addresses.push_back(xc7series::FrameAddress(
|
||||||
|
xc7series::BlockType::BLOCK_RAM, false, 0, 0, 0));
|
||||||
|
addresses.push_back(xc7series::FrameAddress(
|
||||||
|
xc7series::BlockType::BLOCK_RAM, false, 0, 0, 1));
|
||||||
|
addresses.push_back(xc7series::FrameAddress(
|
||||||
|
xc7series::BlockType::BLOCK_RAM, false, 0, 0, 2));
|
||||||
|
|
||||||
|
xc7series::Row row(addresses.begin(), addresses.end());
|
||||||
|
|
||||||
|
EXPECT_TRUE(row.IsValidFrameAddress(xc7series::FrameAddress(
|
||||||
|
xc7series::BlockType::CLB_IO_CLK, false, 0, 0, 0)));
|
||||||
|
EXPECT_TRUE(row.IsValidFrameAddress(xc7series::FrameAddress(
|
||||||
|
xc7series::BlockType::CLB_IO_CLK, false, 0, 1, 0)));
|
||||||
|
EXPECT_TRUE(row.IsValidFrameAddress(xc7series::FrameAddress(
|
||||||
|
xc7series::BlockType::BLOCK_RAM, false, 0, 0, 2)));
|
||||||
|
|
||||||
|
EXPECT_FALSE(row.IsValidFrameAddress(xc7series::FrameAddress(
|
||||||
|
xc7series::BlockType::CLB_IO_CLK, false, 0, 0, 2)));
|
||||||
|
EXPECT_FALSE(row.IsValidFrameAddress(xc7series::FrameAddress(
|
||||||
|
xc7series::BlockType::CLB_IO_CLK, false, 0, 2, 0)));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(RowTest, GetNextFrameAddressYieldNextAddressInRow) {
|
||||||
|
std::vector<xc7series::FrameAddress> addresses;
|
||||||
|
addresses.push_back(xc7series::FrameAddress(
|
||||||
|
xc7series::BlockType::CLB_IO_CLK, false, 0, 0, 0));
|
||||||
|
addresses.push_back(xc7series::FrameAddress(
|
||||||
|
xc7series::BlockType::CLB_IO_CLK, false, 0, 0, 1));
|
||||||
|
addresses.push_back(xc7series::FrameAddress(
|
||||||
|
xc7series::BlockType::CLB_IO_CLK, false, 0, 1, 0));
|
||||||
|
addresses.push_back(xc7series::FrameAddress(
|
||||||
|
xc7series::BlockType::CLB_IO_CLK, false, 0, 1, 1));
|
||||||
|
addresses.push_back(xc7series::FrameAddress(
|
||||||
|
xc7series::BlockType::BLOCK_RAM, false, 0, 0, 0));
|
||||||
|
addresses.push_back(xc7series::FrameAddress(
|
||||||
|
xc7series::BlockType::BLOCK_RAM, false, 0, 0, 1));
|
||||||
|
addresses.push_back(xc7series::FrameAddress(
|
||||||
|
xc7series::BlockType::BLOCK_RAM, false, 0, 0, 2));
|
||||||
|
|
||||||
|
xc7series::Row row(addresses.begin(), addresses.end());
|
||||||
|
|
||||||
|
auto next_address = row.GetNextFrameAddress(xc7series::FrameAddress(
|
||||||
|
xc7series::BlockType::CLB_IO_CLK, false, 0, 0, 0));
|
||||||
|
ASSERT_TRUE(next_address);
|
||||||
|
EXPECT_EQ(*next_address,
|
||||||
|
xc7series::FrameAddress(xc7series::BlockType::CLB_IO_CLK,
|
||||||
|
false, 0, 0, 1));
|
||||||
|
|
||||||
|
next_address = row.GetNextFrameAddress(xc7series::FrameAddress(
|
||||||
|
xc7series::BlockType::CLB_IO_CLK, false, 0, 0, 1));
|
||||||
|
ASSERT_TRUE(next_address);
|
||||||
|
EXPECT_EQ(*next_address,
|
||||||
|
xc7series::FrameAddress(xc7series::BlockType::CLB_IO_CLK,
|
||||||
|
false, 0, 1, 0));
|
||||||
|
|
||||||
|
// Rows have unique behavior for GetNextFrameAddress() at the end of a
|
||||||
|
// bus. Since the addresses need to be returned in numerically
|
||||||
|
// increasing order, all of the rows need to be returned before moving
|
||||||
|
// to a different bus. That means that Row::GetNextFrameAddress() needs
|
||||||
|
// to return no object at the end of a bus and let the caller use that
|
||||||
|
// as a signal to try the next row.
|
||||||
|
next_address = row.GetNextFrameAddress(xc7series::FrameAddress(
|
||||||
|
xc7series::BlockType::CLB_IO_CLK, false, 0, 1, 1));
|
||||||
|
EXPECT_FALSE(next_address);
|
||||||
|
|
||||||
|
next_address = row.GetNextFrameAddress(xc7series::FrameAddress(
|
||||||
|
xc7series::BlockType::BLOCK_RAM, false, 0, 0, 1));
|
||||||
|
ASSERT_TRUE(next_address);
|
||||||
|
EXPECT_EQ(*next_address,
|
||||||
|
xc7series::FrameAddress(xc7series::BlockType::BLOCK_RAM,
|
||||||
|
false, 0, 0, 2));
|
||||||
|
|
||||||
|
next_address = row.GetNextFrameAddress(xc7series::FrameAddress(
|
||||||
|
xc7series::BlockType::BLOCK_RAM, false, 0, 0, 2));
|
||||||
|
EXPECT_FALSE(next_address);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(RowTest, GetNextFrameAddressYieldNothingAtEndOfRow) {
|
||||||
|
std::vector<xc7series::FrameAddress> addresses;
|
||||||
|
addresses.push_back(xc7series::FrameAddress(
|
||||||
|
xc7series::BlockType::CLB_IO_CLK, false, 0, 0, 0));
|
||||||
|
addresses.push_back(xc7series::FrameAddress(
|
||||||
|
xc7series::BlockType::CLB_IO_CLK, false, 0, 0, 1));
|
||||||
|
addresses.push_back(xc7series::FrameAddress(
|
||||||
|
xc7series::BlockType::CLB_IO_CLK, false, 0, 1, 0));
|
||||||
|
addresses.push_back(xc7series::FrameAddress(
|
||||||
|
xc7series::BlockType::CLB_IO_CLK, false, 0, 1, 1));
|
||||||
|
addresses.push_back(xc7series::FrameAddress(
|
||||||
|
xc7series::BlockType::BLOCK_RAM, false, 0, 0, 0));
|
||||||
|
addresses.push_back(xc7series::FrameAddress(
|
||||||
|
xc7series::BlockType::BLOCK_RAM, false, 0, 0, 1));
|
||||||
|
addresses.push_back(xc7series::FrameAddress(
|
||||||
|
xc7series::BlockType::BLOCK_RAM, false, 0, 0, 2));
|
||||||
|
|
||||||
|
xc7series::Row row(addresses.begin(), addresses.end());
|
||||||
|
|
||||||
|
EXPECT_FALSE(row.GetNextFrameAddress(xc7series::FrameAddress(
|
||||||
|
xc7series::BlockType::BLOCK_RAM, false, 0, 0, 2)));
|
||||||
|
}
|
||||||
|
|
@ -1,13 +1,17 @@
|
||||||
#include <libgen.h>
|
#include <libgen.h>
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
#include <map>
|
||||||
|
#include <memory>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include <absl/types/optional.h>
|
#include <absl/types/optional.h>
|
||||||
#include <absl/types/span.h>
|
#include <absl/types/span.h>
|
||||||
#include <prjxray/memory_mapped_file.h>
|
#include <prjxray/memory_mapped_file.h>
|
||||||
#include <prjxray/xilinx/xc7series/bitstream_reader.h>
|
#include <prjxray/xilinx/xc7series/bitstream_reader.h>
|
||||||
#include <prjxray/xilinx/xc7series/configuration_frame_range.h>
|
#include <prjxray/xilinx/xc7series/frame_address.h>
|
||||||
|
#include <prjxray/xilinx/xc7series/global_clock_region.h>
|
||||||
#include <prjxray/xilinx/xc7series/part.h>
|
#include <prjxray/xilinx/xc7series/part.h>
|
||||||
#include <yaml-cpp/yaml.h>
|
#include <yaml-cpp/yaml.h>
|
||||||
|
|
||||||
|
|
@ -37,7 +41,7 @@ int main(int argc, char* argv[]) {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool found_fdri_write = false;
|
bool found_fdri_write = false;
|
||||||
std::vector<uint32_t> frame_addresses;
|
std::vector<xc7series::FrameAddress> frame_addresses;
|
||||||
absl::optional<uint32_t> idcode;
|
absl::optional<uint32_t> idcode;
|
||||||
for (auto packet : *reader) {
|
for (auto packet : *reader) {
|
||||||
if (packet.opcode() !=
|
if (packet.opcode() !=
|
||||||
|
|
@ -72,26 +76,9 @@ int main(int argc, char* argv[]) {
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
// So far, the addresses appear to be written in increasing order but
|
auto part = xc7series::Part(*idcode, frame_addresses.begin(),
|
||||||
// there is no guarantee. Sort them just in case.
|
frame_addresses.end());
|
||||||
std::sort(frame_addresses.begin(), frame_addresses.end());
|
std::cout << YAML::Node(part) << std::endl;
|
||||||
|
|
||||||
std::vector<xc7series::ConfigurationFrameRange> ranges;
|
|
||||||
for (auto start_of_range = frame_addresses.begin();
|
|
||||||
start_of_range != frame_addresses.end();) {
|
|
||||||
auto end_of_range = start_of_range;
|
|
||||||
while (end_of_range + 1 != frame_addresses.end() &&
|
|
||||||
*(end_of_range + 1) == *end_of_range + 1) {
|
|
||||||
++end_of_range;
|
|
||||||
}
|
|
||||||
|
|
||||||
ranges.push_back(xc7series::ConfigurationFrameRange(
|
|
||||||
*start_of_range, *end_of_range + 1));
|
|
||||||
|
|
||||||
start_of_range = ++end_of_range;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::cout << YAML::Node(xc7series::Part(*idcode, ranges)) << std::endl;
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue