mirror of
https://github.com/openXC7/prjxray.git
synced 2026-09-07 11:31:17 +02:00
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 <[email protected]>
This commit is contained in:
@@ -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_
|
||||
#define PRJXRAY_LIB_XILINX_XC7SERIES_PART_H_
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include <absl/types/optional.h>
|
||||
#include <prjxray/xilinx/xc7series/configuration_frame_range.h>
|
||||
#include <prjxray/xilinx/xc7series/frame_address.h>
|
||||
#include <prjxray/xilinx/xc7series/global_clock_region.h>
|
||||
#include <yaml-cpp/yaml.h>
|
||||
|
||||
namespace prjxray {
|
||||
namespace xilinx {
|
||||
@@ -13,33 +17,47 @@ namespace xc7series {
|
||||
|
||||
class Part {
|
||||
public:
|
||||
constexpr static uint32_t kInvalidIdcode = 0;
|
||||
|
||||
static absl::optional<Part> FromFile(const std::string& path);
|
||||
|
||||
// Constructs an invalid part with a zero IDCODE. Required for YAML
|
||||
// conversion but shouldn't be used otherwise.
|
||||
Part() : idcode_(0), frame_ranges_() {}
|
||||
Part() : idcode_(kInvalidIdcode) {}
|
||||
|
||||
Part(uint32_t idcode,
|
||||
const std::vector<ConfigurationFrameRange>& ranges)
|
||||
: idcode_(idcode), frame_ranges_(std::move(ranges)) {}
|
||||
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_; }
|
||||
|
||||
const std::vector<ConfigurationFrameRange>& configuration_frame_ranges()
|
||||
const {
|
||||
return frame_ranges_;
|
||||
}
|
||||
bool IsValidFrameAddress(FrameAddress address) const;
|
||||
|
||||
absl::optional<FrameAddress> GetNextFrameAddress(
|
||||
FrameAddress address) const;
|
||||
|
||||
private:
|
||||
uint32_t idcode_;
|
||||
friend class YAML::convert<Part>;
|
||||
|
||||
// Ranges are expected to be non-overlapping.
|
||||
std::vector<ConfigurationFrameRange> frame_ranges_;
|
||||
uint32_t idcode_;
|
||||
GlobalClockRegion top_region_;
|
||||
GlobalClockRegion bottom_region_;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
Part::Part(uint32_t idcode, T first, T last) : idcode_(idcode) {
|
||||
auto first_of_top =
|
||||
std::partition(first, last, [](const FrameAddress& addr) {
|
||||
return addr.is_bottom_half_rows();
|
||||
});
|
||||
|
||||
top_region_ = GlobalClockRegion(first_of_top, last);
|
||||
bottom_region_ = GlobalClockRegion(first, first_of_top);
|
||||
}
|
||||
|
||||
} // namespace xc7series
|
||||
} // namespace xilinx
|
||||
} // namespace prjxray
|
||||
|
||||
@@ -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_
|
||||
Reference in New Issue
Block a user