Merge pull request #44 from kc8apf/part_refactor

xc7series: refactor Part()
This commit is contained in:
Rick Altherr 2018-01-12 10:57:43 -08:00 committed by GitHub
commit 908c32adaf
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
26 changed files with 1314 additions and 229 deletions

View File

@ -4,11 +4,14 @@ add_library(libprjxray
segbits_file_reader.cc
xilinx/xc7series/bitstream_reader.cc
xilinx/xc7series/block_type.cc
xilinx/xc7series/frame_address.cc
xilinx/xc7series/configuration_frame_range.cc
xilinx/xc7series/configuration_bus.cc
xilinx/xc7series/configuration_column.cc
xilinx/xc7series/configuration_packet.cc
xilinx/xc7series/configuration_register.cc
xilinx/xc7series/frame_address.cc
xilinx/xc7series/global_clock_region.cc
xilinx/xc7series/part.cc
xilinx/xc7series/row.cc
)
target_include_directories(libprjxray PUBLIC "include")
target_link_libraries(libprjxray absl::optional absl::strings absl::span yaml-cpp)
@ -40,9 +43,13 @@ if (PRJXRAY_BUILD_TESTING)
xilinx/xc7series/bitstream_reader_test.cc
xilinx/xc7series/block_type_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_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)
add_test(NAME xilinx_xc7series_test
COMMAND xilinx_xc7series_test

View File

@ -137,9 +137,8 @@ absl::optional<Configuration> Configuration::InitWithPackets(
// Bitstreams appear to have 2 frames of
// padding between rows.
if (next_address->row_address() !=
current_frame_address
.row_address()) {
if (next_address->row() !=
current_frame_address.row()) {
ii += 2 * kWordsPerFrame;
}
current_frame_address = *next_address;

View File

@ -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_

View File

@ -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_

View File

@ -1,47 +0,0 @@
#ifndef PRJXRAY_LIB_XILINX_XC7SERIES_CONFIGURATION_FRAME_RANGE_H_
#define PRJXRAY_LIB_XILINX_XC7SERIES_CONFIGURATION_FRAME_RANGE_H_
#include <cstdint>
#include <prjxray/xilinx/xc7series/frame_address.h>
#include <yaml-cpp/yaml.h>
namespace prjxray {
namespace xilinx {
namespace xc7series {
class ConfigurationFrameRange {
public:
ConfigurationFrameRange() : begin_(0), end_(0) {}
ConfigurationFrameRange(FrameAddress begin, FrameAddress end)
: begin_(begin), end_(end){};
FrameAddress begin() const { return begin_; }
FrameAddress end() const { return end_; }
size_t size() const { return end_ - begin_; }
bool empty() const { return size() == 0; }
bool Contains(FrameAddress address) const;
private:
FrameAddress begin_;
FrameAddress end_;
};
} // namespace xc7series
} // namespace xilinx
} // namespace prjxray
namespace YAML {
template <>
struct convert<prjxray::xilinx::xc7series::ConfigurationFrameRange> {
static Node encode(
const prjxray::xilinx::xc7series::ConfigurationFrameRange& rhs);
static bool decode(
const Node& node,
prjxray::xilinx::xc7series::ConfigurationFrameRange& lhs);
};
} // namespace YAML
#endif // PRJXRAY_LIB_XILINX_XC7SERIES_CONFIGURATION_FRAME_RANGE_H_

View File

@ -27,9 +27,9 @@ class FrameAddress {
BlockType block_type() const;
bool is_bottom_half_rows() const;
uint8_t row_address() const;
uint16_t column_address() const;
uint8_t minor_address() const;
uint8_t row() const;
uint16_t column() const;
uint8_t minor() const;
private:
uint32_t address_;

View File

@ -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_

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

View File

@ -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_

View File

@ -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

View File

@ -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)));
}

View File

@ -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

View File

@ -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)));
}

View File

@ -1,41 +0,0 @@
#include <prjxray/xilinx/xc7series/configuration_frame_range.h>
namespace prjxray {
namespace xilinx {
namespace xc7series {
bool ConfigurationFrameRange::Contains(FrameAddress address) const {
return address >= begin_ && address < end_;
}
} // namespace xc7series
} // namespace xilinx
} // namespace prjxray
namespace xc7series = prjxray::xilinx::xc7series;
namespace YAML {
Node convert<xc7series::ConfigurationFrameRange>::encode(
const xc7series::ConfigurationFrameRange& rhs) {
Node node;
node.SetTag("xilinx/xc7series/configuration_frame_range");
node["begin"] = rhs.begin();
node["end"] = rhs.end();
return node;
}
bool convert<xc7series::ConfigurationFrameRange>::decode(
const Node& node,
xc7series::ConfigurationFrameRange& lhs) {
if (node.Tag() != "xilinx/xc7series/configuration_frame_range" ||
!node["begin"] || !node["end"])
return false;
lhs = xc7series::ConfigurationFrameRange(
node["begin"].as<xc7series::FrameAddress>(),
node["end"].as<xc7series::FrameAddress>());
return true;
}
} // namespace YAML

View File

@ -1,6 +1,7 @@
#include <prjxray/xilinx/xc7series/configuration.h>
#include <cstdint>
#include <iostream>
#include <vector>
#include <absl/types/span.h>
@ -8,16 +9,18 @@
#include <prjxray/memory_mapped_file.h>
#include <prjxray/xilinx/xc7series/configuration_packet.h>
#include <prjxray/xilinx/xc7series/configuration_register.h>
#include <prjxray/xilinx/xc7series/frame_address.h>
#include <prjxray/xilinx/xc7series/part.h>
#include <yaml-cpp/yaml.h>
namespace xc7series = prjxray::xilinx::xc7series;
TEST(ConfigurationTest, ConstructFromPacketsWithSingleFrame) {
std::vector<xc7series::ConfigurationFrameRange> test_part_ranges;
test_part_ranges.push_back(
xc7series::ConfigurationFrameRange(0x4567, 0x4568));
std::vector<xc7series::FrameAddress> test_part_addresses;
test_part_addresses.push_back(0x4567);
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> cmd{0x0001};
@ -61,13 +64,15 @@ TEST(ConfigurationTest, ConstructFromPacketsWithSingleFrame) {
}
TEST(ConfigurationTest, ConstructFromPacketsWithAutoincrement) {
std::vector<xc7series::ConfigurationFrameRange> test_part_ranges;
test_part_ranges.push_back(
xc7series::ConfigurationFrameRange(0x4560, 0x4570));
test_part_ranges.push_back(
xc7series::ConfigurationFrameRange(0x4580, 0x4590));
std::vector<xc7series::FrameAddress> test_part_addresses;
for (int ii = 0x4560; ii < 0x4570; ++ii) {
test_part_addresses.push_back(ii);
}
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> cmd{0x0001};

View File

@ -28,15 +28,15 @@ bool FrameAddress::is_bottom_half_rows() const {
return bit_field_get(address_, 22, 22);
}
uint8_t FrameAddress::row_address() const {
uint8_t FrameAddress::row() const {
return bit_field_get(address_, 21, 17);
}
uint16_t FrameAddress::column_address() const {
uint16_t FrameAddress::column() const {
return bit_field_get(address_, 16, 7);
}
uint8_t FrameAddress::minor_address() const {
uint8_t FrameAddress::minor() const {
return bit_field_get(address_, 6, 0);
}
@ -45,10 +45,9 @@ std::ostream& operator<<(std::ostream& o, const FrameAddress& addr) {
<< static_cast<uint32_t>(addr) << "] "
<< (addr.is_bottom_half_rows() ? "BOTTOM" : "TOP")
<< " Row=" << std::setw(2) << std::dec
<< static_cast<unsigned int>(addr.row_address())
<< " Column=" << std::setw(2) << std::dec << addr.column_address()
<< " Minor=" << std::setw(2) << std::dec
<< static_cast<unsigned int>(addr.minor_address())
<< static_cast<unsigned int>(addr.row()) << " Column=" << std::setw(2)
<< std::dec << addr.column() << " Minor=" << std::setw(2) << std::dec
<< static_cast<unsigned int>(addr.minor())
<< " Type=" << addr.block_type();
return o;
}
@ -67,9 +66,9 @@ Node convert<xc7series::FrameAddress>::encode(
node.SetTag("xilinx/xc7series/frame_address");
node["block_type"] = rhs.block_type();
node["row_half"] = (rhs.is_bottom_half_rows() ? "bottom" : "top");
node["row"] = static_cast<unsigned int>(rhs.row_address());
node["column"] = static_cast<unsigned int>(rhs.column_address());
node["minor"] = static_cast<unsigned int>(rhs.minor_address());
node["row"] = static_cast<unsigned int>(rhs.row());
node["column"] = static_cast<unsigned int>(rhs.column());
node["minor"] = static_cast<unsigned int>(rhs.minor());
return node;
}

View File

@ -30,7 +30,7 @@ TEST(FrameAddressTest, YamlDecode) {
xc7series::FrameAddress address = node.as<xc7series::FrameAddress>();
EXPECT_EQ(address.block_type(), xc7series::BlockType::BLOCK_RAM);
EXPECT_TRUE(address.is_bottom_half_rows());
EXPECT_EQ(address.row_address(), 0);
EXPECT_EQ(address.column_address(), 5);
EXPECT_EQ(address.minor_address(), 11);
EXPECT_EQ(address.row(), 0);
EXPECT_EQ(address.column(), 5);
EXPECT_EQ(address.minor(), 11);
}

View File

@ -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

View File

@ -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)));
}

View File

@ -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(
FrameAddress address) const {
// Start with the next linear address.
FrameAddress target_address(address + 1);
// Ask the current global clock region first.
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
// happens to fall in a valid range, that's the next address.
// Otherwise, find the closest valid range and use it's beginning
// address.
absl::optional<FrameAddress> closest_address;
int32_t closest_distance;
for (auto iter = frame_ranges_.begin(); iter != frame_ranges_.end();
++iter) {
if (iter->Contains(target_address)) {
return target_address;
}
int32_t distance = iter->begin() - target_address;
if (distance > 0 &&
(!closest_address || distance < closest_distance)) {
closest_address = iter->begin();
closest_distance = distance;
}
// If the current address is in the top region, the bottom region is
// next numerically.
if (!address.is_bottom_half_rows()) {
next_address =
FrameAddress(address.block_type(), true, 0, 0, 0);
if (bottom_region_.IsValidFrameAddress(*next_address))
return next_address;
}
return closest_address;
// Block types are next numerically.
if (address.block_type() < BlockType::BLOCK_RAM) {
next_address =
FrameAddress(BlockType::BLOCK_RAM, false, 0, 0, 0);
if (IsValidFrameAddress(*next_address))
return next_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
@ -57,23 +73,43 @@ Node convert<xc7series::Part>::encode(const xc7series::Part& rhs) {
node.SetTag("xilinx/xc7series/part");
std::ostringstream idcode_str;
idcode_str << "0x" << std::hex << rhs.idcode();
idcode_str << "0x" << std::hex << rhs.idcode_;
node["idcode"] = idcode_str.str();
node["configuration_ranges"] = rhs.configuration_frame_ranges();
node["global_clock_regions"]["top"] = rhs.top_region_;
node["global_clock_regions"]["bottom"] = rhs.bottom_region_;
return node;
}
bool convert<xc7series::Part>::decode(const Node& node, xc7series::Part& lhs) {
if (node.Tag() != "xilinx/xc7series/part" || !node["idcode"] ||
!node["configuration_ranges"])
if (!node.Tag().empty() && node.Tag() != "xilinx/xc7series/part")
return false;
lhs = xc7series::Part(
node["idcode"].as<uint32_t>(),
node["configuration_ranges"]
.as<std::vector<xc7series::ConfigurationFrameRange>>());
if (!node["global_clock_regions"] && !node["configuration_ranges"]) {
return false;
}
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;
}
};
} // namespace YAML

View File

@ -4,37 +4,146 @@
namespace xc7series = prjxray::xilinx::xc7series;
TEST(PartTest, GetNextAddressWhereNextIsInValidRange) {
std::vector<xc7series::ConfigurationFrameRange> ranges;
ranges.push_back(xc7series::ConfigurationFrameRange(0x0, 0xF));
ranges.push_back(xc7series::ConfigurationFrameRange(0x20, 0x2F));
TEST(PartTest, 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));
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(next_address);
EXPECT_EQ(static_cast<uint32_t>(0x5), *next_address);
EXPECT_TRUE(part.IsValidFrameAddress(xc7series::FrameAddress(
xc7series::BlockType::CLB_IO_CLK, false, 0, 0, 0)));
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) {
std::vector<xc7series::ConfigurationFrameRange> ranges;
ranges.push_back(xc7series::ConfigurationFrameRange(0x0, 0xF));
ranges.push_back(xc7series::ConfigurationFrameRange(0x20, 0x2F));
TEST(PartTest, GetNextFrameAddressYieldNextAddressInPart) {
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));
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);
EXPECT_TRUE(next_address);
EXPECT_EQ(static_cast<uint32_t>(0x20), *next_address);
auto next_address = part.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 = 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) {
std::vector<xc7series::ConfigurationFrameRange> ranges;
ranges.push_back(xc7series::ConfigurationFrameRange(0x0, 0xF));
ranges.push_back(xc7series::ConfigurationFrameRange(0x20, 0x2F));
TEST(PartTest, GetNextFrameAddressYieldNothingAtEndOfPart) {
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));
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(next_address);
EXPECT_FALSE(part.GetNextFrameAddress(xc7series::FrameAddress(
xc7series::BlockType::BLOCK_RAM, false, 0, 0, 2)));
}

View File

@ -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

View File

@ -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)));
}

View File

@ -159,11 +159,11 @@ int main(int argc, char** argv) {
static_cast<uint32_t>(it.first),
static_cast<unsigned int>(it.first.block_type()),
it.first.is_bottom_half_rows() ? 1 : 0,
it.first.row_address(), it.first.column_address(),
it.first.minor_address());
it.first.row(), it.first.column(),
it.first.minor());
if (FLAGS_p) {
if (it.first.minor_address() == 0 && !pgmdata.empty())
if (it.first.minor() == 0 && !pgmdata.empty())
pgmsep.push_back(pgmdata.size());
pgmdata.push_back(std::vector<bool>());
@ -195,12 +195,9 @@ int main(int argc, char** argv) {
it.first.is_bottom_half_rows()
? 1
: 0,
it.first
.row_address(),
it.first
.column_address(),
it.first
.minor_address());
it.first.row(),
it.first.column(),
it.first.minor());
else
fprintf(f,
"bit_%08x_%03d_"

View File

@ -18,17 +18,18 @@ int main(int argc, char* argv[]) {
for (uint32_t frame_address_raw;
(*input_stream) >> std::setbase(0) >> frame_address_raw;) {
xc7series::FrameAddress frame_address(frame_address_raw);
std::cout
<< "[" << std::hex << std::showbase << std::setw(10)
<< frame_address_raw << "] "
<< (frame_address.is_bottom_half_rows() ? "BOTTOM" : "TOP")
<< " Row=" << std::setw(2) << std::dec
<< static_cast<unsigned int>(frame_address.row_address())
<< " Column=" << std::setw(2) << std::dec
<< frame_address.column_address()
<< " Minor=" << std::setw(2) << std::dec
<< static_cast<unsigned int>(frame_address.minor_address())
<< " Type=" << frame_address.block_type() << std::endl;
std::cout << "[" << std::hex << std::showbase << std::setw(10)
<< frame_address_raw << "] "
<< (frame_address.is_bottom_half_rows() ? "BOTTOM"
: "TOP")
<< " Row=" << std::setw(2) << std::dec
<< static_cast<unsigned int>(frame_address.row())
<< " Column=" << std::setw(2) << std::dec
<< frame_address.column() << " Minor=" << std::setw(2)
<< std::dec
<< static_cast<unsigned int>(frame_address.minor())
<< " Type=" << frame_address.block_type()
<< std::endl;
}
return 0;

View File

@ -1,13 +1,17 @@
#include <libgen.h>
#include <algorithm>
#include <iostream>
#include <map>
#include <memory>
#include <vector>
#include <absl/types/optional.h>
#include <absl/types/span.h>
#include <prjxray/memory_mapped_file.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 <yaml-cpp/yaml.h>
@ -37,7 +41,7 @@ int main(int argc, char* argv[]) {
}
bool found_fdri_write = false;
std::vector<uint32_t> frame_addresses;
std::vector<xc7series::FrameAddress> frame_addresses;
absl::optional<uint32_t> idcode;
for (auto packet : *reader) {
if (packet.opcode() !=
@ -72,26 +76,9 @@ int main(int argc, char* argv[]) {
return 1;
}
// So far, the addresses appear to be written in increasing order but
// there is no guarantee. Sort them just in case.
std::sort(frame_addresses.begin(), frame_addresses.end());
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;
auto part = xc7series::Part(*idcode, frame_addresses.begin(),
frame_addresses.end());
std::cout << YAML::Node(part) << std::endl;
return 0;
}