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:
Rick Altherr
2018-01-10 14:28:23 -08:00
parent 53d7f1f80b
commit 2b34fbb35d
18 changed files with 1279 additions and 101 deletions
@@ -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_
+31 -13
View File
@@ -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_