lib: xc7series: wrapper for part-specific information

Currently just the pieces required to successfully identify and decode
a bitstream into configuration frames.

Signed-off-by: Rick Altherr <kc8apf@kc8apf.net>
Signed-off-by: Tim 'mithro' Ansell <mithro@mithis.com>
This commit is contained in:
Rick Altherr 2017-12-15 13:04:38 -08:00 committed by Tim 'mithro' Ansell
parent 4f6046d177
commit d251aa0ed7
4 changed files with 185 additions and 0 deletions

View File

@ -8,6 +8,7 @@ add_library(libprjxray
xilinx/xc7series/configuration_frame_range.cc
xilinx/xc7series/configuration_packet.cc
xilinx/xc7series/configuration_register.cc
xilinx/xc7series/part.cc
)
target_include_directories(libprjxray PUBLIC "include")
target_link_libraries(libprjxray absl::optional absl::strings absl::span yaml-cpp)
@ -62,4 +63,11 @@ if (PRJXRAY_BUILD_TESTING)
libprjxray gtest_main)
add_test(NAME xilinx_xc7series_configuration_packet_test
COMMAND xilinx_xc7series_configuration_packet_test)
add_executable(xilinx_xc7series_part_test
xilinx/xc7series/part_test.cc)
target_link_libraries(xilinx_xc7series_part_test
libprjxray gtest_main)
add_test(NAME xilinx_xc7series_part_test
COMMAND xilinx_xc7series_part_test)
endif()

View File

@ -0,0 +1,55 @@
#ifndef PRJXRAY_LIB_XILINX_XC7SERIES_PART_H_
#define PRJXRAY_LIB_XILINX_XC7SERIES_PART_H_
#include <vector>
#include <absl/types/optional.h>
#include <prjxray/xilinx/xc7series/configuration_frame_address.h>
#include <prjxray/xilinx/xc7series/configuration_frame_range.h>
namespace prjxray {
namespace xilinx {
namespace xc7series {
class Part {
public:
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(uint32_t idcode,
const std::vector<ConfigurationFrameRange> &ranges)
: idcode_(idcode), frame_ranges_(std::move(ranges)) {}
uint32_t idcode() const { return idcode_; }
const std::vector<ConfigurationFrameRange>&
configuration_frame_ranges() const { return frame_ranges_; }
absl::optional<ConfigurationFrameAddress>
GetNextConfigurationFrameAddress(
ConfigurationFrameAddress address) const;
private:
uint32_t idcode_;
// Ranges are expected to be non-overlapping.
std::vector<ConfigurationFrameRange> frame_ranges_;
};
} // namespace xc7series
} // namespace xilinx
} // namespace prjxray
namespace YAML {
template<>
struct convert<prjxray::xilinx::xc7series::Part> {
static Node encode(const prjxray::xilinx::xc7series::Part &rhs);
static bool decode(const Node& node,
prjxray::xilinx::xc7series::Part &lhs);
};
} // namespace YAML
#endif // PRJXRAY_LIB_XILINX_XC7SERIES_PART_H_

View File

@ -0,0 +1,82 @@
#include <prjxray/xilinx/xc7series/part.h>
#include <iomanip>
#include <sstream>
namespace prjxray {
namespace xilinx {
namespace xc7series {
absl::optional<Part> Part::FromFile(const std::string &path) {
try {
YAML::Node yaml = YAML::LoadFile(path);
return yaml.as<Part>();
} catch (YAML::Exception e) {
return {};
}
}
absl::optional<ConfigurationFrameAddress>
Part::GetNextConfigurationFrameAddress(ConfigurationFrameAddress address) const {
// Start with the next linear address.
ConfigurationFrameAddress target_address(address + 1);
// 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<ConfigurationFrameAddress> 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;
}
}
return closest_address;
}
} // namespace xc7series
} // namespace xilinx
} // namespace prjxray
namespace xc7series = prjxray::xilinx::xc7series;
namespace YAML {
Node convert<xc7series::Part>::encode(const xc7series::Part &rhs) {
Node node;
node.SetTag("xilinx/xc7series/part");
std::ostringstream idcode_str;
idcode_str << "0x" << std::hex << rhs.idcode();
node["idcode"] = idcode_str.str();
node["configuration_ranges"] = rhs.configuration_frame_ranges();
return node;
}
bool convert<xc7series::Part>::decode(const Node &node, xc7series::Part &lhs) {
if (node.Tag() != "xilinx/xc7series/part" ||
!node["idcode"] ||
!node["configuration_ranges"]) return false;
lhs = xc7series::Part(
node["idcode"].as<uint32_t>(),
node["configuration_ranges"].as<
std::vector<xc7series::ConfigurationFrameRange>>());
return true;
}
} // namespace YAML;

View File

@ -0,0 +1,40 @@
#include <prjxray/xilinx/xc7series/part.h>
#include <gtest/gtest.h>
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));
xc7series::Part part(0x1234, ranges);
auto next_address = part.GetNextConfigurationFrameAddress(0x4);
EXPECT_TRUE(next_address);
EXPECT_EQ(static_cast<uint32_t>(0x5), *next_address);
}
TEST(PartTest, GetNextAddressWhereNextIsBetweenRanges) {
std::vector<xc7series::ConfigurationFrameRange> ranges;
ranges.push_back(xc7series::ConfigurationFrameRange(0x0, 0xF));
ranges.push_back(xc7series::ConfigurationFrameRange(0x20, 0x2F));
xc7series::Part part(0x1234, ranges);
auto next_address = part.GetNextConfigurationFrameAddress(0xF);
EXPECT_TRUE(next_address);
EXPECT_EQ(static_cast<uint32_t>(0x20), *next_address);
}
TEST(PartTest, GetNextAddressWhereNextWouldBePastLastRange) {
std::vector<xc7series::ConfigurationFrameRange> ranges;
ranges.push_back(xc7series::ConfigurationFrameRange(0x0, 0xF));
ranges.push_back(xc7series::ConfigurationFrameRange(0x20, 0x2F));
xc7series::Part part(0x1234, ranges);
auto next_address = part.GetNextConfigurationFrameAddress(0x2F);
EXPECT_FALSE(next_address);
}