mirror of https://github.com/openXC7/prjxray.git
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:
parent
4f6046d177
commit
d251aa0ed7
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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_
|
||||
|
|
@ -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;
|
||||
|
||||
|
|
@ -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);
|
||||
}
|
||||
Loading…
Reference in New Issue