| /* | |
| * Copyright (C) 2017-2020 The Project X-Ray Authors. | |
| * | |
| * Use of this source code is governed by a ISC-style | |
| * license that can be found in the LICENSE file or at | |
| * https://opensource.org/licenses/ISC | |
| * | |
| * SPDX-License-Identifier: ISC | |
| */ | |
| #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 {}; | |
| } | |
| } | |
| 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 { | |
| // 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; | |
| // 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; | |
| } | |
| // 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 | |
| } // 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["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().empty() && node.Tag() != "xilinx/xc7series/part") | |
| return false; | |
| 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 |