blob: dfd5d9d4232e1a56657c907eb6b6e463ccca42d5 [file] [log] [blame]
#include "pack_patterns.h"
#include "atom_netlist.h"
#include "pb_graph_walker.h"
#include "globals.h"
#include <fstream>
#include <stack>
#include <queue>
#include <algorithm>
/*
* Local Data Types
*/
struct t_arch_pack_pattern_connection {
t_pb_graph_pin* from_pin;
t_pb_graph_pin* to_pin;
std::string pattern_name;
};
//Walks the PB graph collecting any pack patterns found on edges
class PbGraphPackPatternCollector : public PbGraphWalker {
public:
void on_edge(t_pb_graph_edge* edge) override {
if (edge->num_pack_patterns < 1) return;
for (int ipin = 0; ipin < edge->num_input_pins; ++ipin) {
for (int opin = 0; opin < edge->num_output_pins; ++opin) {
for (int ipattern = 0; ipattern < edge->num_pack_patterns; ++ipattern) {
t_arch_pack_pattern_connection conn;
conn.from_pin = edge->input_pins[ipin];
conn.to_pin = edge->output_pins[opin];
conn.pattern_name = edge->pack_pattern_names[ipattern];
pack_pattern_connections[conn.pattern_name].push_back(conn);
}
}
}
}
std::map<std::string,std::vector<t_arch_pack_pattern_connection>> pack_pattern_connections;
};
/*
* Local Function Delcarations
*/
static std::map<std::string,std::vector<t_arch_pack_pattern_connection>> collect_type_pack_pattern_connections(t_type_ptr type);
static t_arch_pack_pattern build_pack_pattern(std::string pattern_name, const std::vector<t_arch_pack_pattern_connection>& connections);
static t_netlist_pack_pattern abstract_arch_pack_pattern(const t_arch_pack_pattern& arch_pack_pattern);
static NetlistPatternMatch match_largest(AtomBlockId blk, const t_netlist_pack_pattern& pattern, const AtomNetlist& netlist);
static bool match_largest_recur(NetlistPatternMatch& match, const AtomBlockId blk,
int pattern_node_id, const t_netlist_pack_pattern& pattern,
const AtomNetlist& netlist);
static AtomPinId find_matching_pin(const AtomNetlist::pin_range pin_range, const t_netlist_pack_pattern_pin& pattern_pin, const AtomNetlist& netlist);
static AtomPinId find_matching_pin(const AtomNetlist::pin_range pin_range, const t_netlist_pack_pattern_pin& pattern_pin,
const std::set<AtomBlockId>& excluded_blocks, const AtomNetlist& netlist);
static AtomBlockId find_parent_pattern_root(const AtomBlockId blk, const t_netlist_pack_pattern& pattern, const AtomNetlist& netlist);
static bool matches_pattern_root(const AtomBlockId blk, const t_netlist_pack_pattern& pattern, const AtomNetlist& netlist);
static bool matches_pattern_node(const AtomBlockId blk, const t_netlist_pack_pattern_node& pattern_node, const AtomNetlist& netlist);
static bool is_wildcard_node(const t_netlist_pack_pattern_node& pattern_node);
static bool is_wildcard_pin(const t_netlist_pack_pattern_pin& pattern_pin);
static bool matches_pattern_pin(const AtomPinId from_pin, const t_netlist_pack_pattern_pin& pattern_pin, const AtomNetlist& netlist);
static bool match_covered(const NetlistPatternMatch& match, const std::map<std::string,std::set<AtomBlockId>>& pattern_covered_blocks);
/*
* Global Function Implementations
*/
std::vector<t_arch_pack_pattern> identify_arch_pack_patterns(const DeviceContext& device_ctx) {
std::vector<t_arch_pack_pattern> arch_pack_patterns;
//For every complex block
for (int itype = 0; itype < device_ctx.num_block_types; ++itype) {
//Collect the pack pattern connections defined in the architecture
const auto& pack_pattern_connections = collect_type_pack_pattern_connections(&device_ctx.block_types[itype]);
//vtr::printf("TYPE: %s\n", device_ctx.block_types[itype].name);
//Convert each set of connections to an architecture pack pattern
for (auto kv : pack_pattern_connections) {
t_arch_pack_pattern pack_pattern = build_pack_pattern(kv.first, kv.second);
arch_pack_patterns.push_back(pack_pattern);
}
}
return arch_pack_patterns;
}
std::vector<t_netlist_pack_pattern> abstract_arch_pack_patterns(const std::vector<t_arch_pack_pattern>& arch_pack_patterns) {
std::vector<t_netlist_pack_pattern> netlist_pack_patterns;
for (auto arch_pattern : arch_pack_patterns) {
auto netlist_pattern = abstract_arch_pack_pattern(arch_pattern);
netlist_pack_patterns.push_back(netlist_pattern);
//Debug
std::ofstream ofs("netlist_pack_pattern." + netlist_pattern.name + ".echo");
write_netlist_pack_pattern_dot(ofs, netlist_pattern);
}
return netlist_pack_patterns;
}
t_netlist_pack_pattern create_atom_default_pack_pattern() {
t_netlist_pack_pattern atom_pack_pattern;
//A single internal wild-card node
atom_pack_pattern.root_node = atom_pack_pattern.create_node(false);
atom_pack_pattern.name = ATOM_DEFAULT_PACK_PATTERN_NAME;
return atom_pack_pattern;
}
std::vector<NetlistPatternMatch> collect_pattern_matches_in_netlist(const t_netlist_pack_pattern& pattern, const AtomNetlist& netlist) {
std::vector<NetlistPatternMatch> matches;
//Walk through all the blocks in the netlist and see if they match the current pattern
//
//Note that we walk through the blocks in topological order to ensure:
// 1) The 'highest' block in a pattern is matched first,
// 2) consistency, even if the blocks are re-ordered the topological structure is the same
for (auto blk : topological_block_order(netlist)) {
vtr::printf("Matching at block %s for pattern %s\n",
g_vpr_ctx.atom().nlist.block_name(blk).c_str(),
pattern.name.c_str());
NetlistPatternMatch match = match_largest(blk, pattern, netlist);
if (match) {
vtr::printf("\tMatched!\n");
//Save the match
matches.push_back(match);
}
}
return matches;
}
std::vector<NetlistPatternMatch> filter_netlist_pattern_matches(std::vector<NetlistPatternMatch> unfiltered_matches) {
std::vector<NetlistPatternMatch> filtered_matches;
//Sort the matches in descending order based on the following criteria:
// 1) Size
// 2) Base cost
//Note that we use a stable sort to maintain the topological order in which the matches were found
auto by_preference = [&](const NetlistPatternMatch& lhs, const NetlistPatternMatch& rhs) {
return std::make_tuple(lhs.internal_blocks.size(), lhs.base_cost) >
std::make_tuple(rhs.internal_blocks.size(), rhs.base_cost);
};
std::stable_sort(unfiltered_matches.begin(), unfiltered_matches.end(), by_preference);
std::map<std::string,std::set<AtomBlockId>> pattern_covered_blocks;
for (auto& match : unfiltered_matches) {
if (match.internal_blocks.empty()) continue; //Don't keep empty matches
if (match_covered(match, pattern_covered_blocks)) continue; //Each atom should appear in at most one molecule per pack pattern type
pattern_covered_blocks[match.pattern_name].insert(match.internal_blocks.begin(), match.internal_blocks.end());
filtered_matches.push_back(match);
}
return filtered_matches;
}
void print_match(const NetlistPatternMatch& match, const AtomNetlist& netlist) {
vtr::printf("Netlist Match to %s:\n", match.pattern_name.c_str());
for (auto& edge : match.netlist_edges) {
AtomBlockId from_blk = netlist.pin_block(edge.from_pin);
AtomBlockId to_blk = netlist.pin_block(edge.to_pin);
bool from_external = (std::find(match.external_blocks.begin(), match.external_blocks.end(), from_blk) != match.external_blocks.end());
bool to_external = (std::find(match.external_blocks.begin(), match.external_blocks.end(), to_blk) != match.external_blocks.end());
vtr::printf(" %s -> %s (%s%s -> %s%s) (pattern edge #%d.%d)\n",
netlist.pin_name(edge.from_pin).c_str(),
netlist.pin_name(edge.to_pin).c_str(),
netlist.block_model(netlist.pin_block(edge.from_pin))->name,
(from_external) ? "*" : "",
netlist.block_model(netlist.pin_block(edge.to_pin))->name,
(to_external) ? "*" : "",
edge.pattern_edge_id,
edge.pattern_edge_sink);
}
for (auto blk : match.internal_blocks) {
vtr::printf("\tInternal match block: %s (%zu)\n", netlist.block_name(blk).c_str(), size_t(blk));
}
for (auto blk : match.external_blocks) {
vtr::printf("\tExternal match block: %s (%zu)\n", netlist.block_name(blk).c_str(), size_t(blk));
}
}
void write_arch_pack_pattern_dot(std::ostream& os, const t_arch_pack_pattern& arch_pattern) {
os << "#Dot file of architecture pack pattern graph\n";
os << "digraph " << arch_pattern.name << " {\n";
for (size_t inode = 0; inode < arch_pattern.nodes.size(); ++inode) {
os << "N" << inode;
os << " [";
os << "label=\"" << describe_pb_graph_pin_hierarchy(arch_pattern.nodes[inode].pb_graph_pin) << " (#" << inode << ")\"";
os << "];\n";
}
for (size_t iedge = 0; iedge < arch_pattern.edges.size(); ++iedge) {
os << "N" << arch_pattern.edges[iedge].from_node_id << " -> N" << arch_pattern.edges[iedge].to_node_id;
os << " [";
os << "label=\"" ;
os << "(#" << iedge << ")";
os << "\"";
os << "];\n";
}
os << "}\n";
}
void write_netlist_pack_pattern_dot(std::ostream& os, const t_netlist_pack_pattern& netlist_pattern) {
os << "#Dot file of netlist pack pattern graph\n";
os << "digraph " << netlist_pattern.name << " {\n";
//Nodes
for (size_t inode = 0; inode < netlist_pattern.nodes.size(); ++inode) {
os << "N" << inode;
os << " [";
os << "label=\"";
if (netlist_pattern.nodes[inode].model_type) {
os << netlist_pattern.nodes[inode].model_type->name;
} else {
os << "*";
}
os << " (#" << inode << ")";
os << "\"";
if (netlist_pattern.nodes[inode].is_external()) {
os << " shape=invhouse";
}
os << "];\n";
}
//Edges
for (size_t iedge = 0; iedge < netlist_pattern.edges.size(); ++iedge) {
const auto& edge = netlist_pattern.edges[iedge];
const auto& from_pin = edge.from_pin;
for (size_t isink = 0; isink < edge.to_pins.size(); ++isink) {
const auto& to_pin = edge.to_pins[isink];
os << "N" << edge.from_pin.node_id << " -> N" << edge.to_pins[isink].node_id;
os << " [";
os << "label=\"" ;
os << "(#" << iedge << "." << isink << ")\\n";
if (is_wildcard_pin(from_pin)) {
os << "*";
} else {
os << from_pin.model_port->name << "[" << from_pin.port_pin << "]";
}
os << "\\n -> ";
if (is_wildcard_pin(to_pin)) {
os << "*";
} else {
os << to_pin.model_port->name << "[" << to_pin.port_pin << "]";
}
os << "\"";
if (to_pin.required) {
os << " style=solid";
} else {
os << " style=dashed";
}
os << "];\n";
}
}
os << "}\n";
}
/*
* Local Function Implementations
*/
static std::map<std::string,std::vector<t_arch_pack_pattern_connection>> collect_type_pack_pattern_connections(t_type_ptr type) {
PbGraphPackPatternCollector collector;
collector.walk(type->pb_graph_head);
return collector.pack_pattern_connections;
}
static t_arch_pack_pattern build_pack_pattern(std::string pattern_name, const std::vector<t_arch_pack_pattern_connection>& connections) {
t_arch_pack_pattern pack_pattern;
pack_pattern.name = pattern_name;
for (auto conn : connections) {
vtr::printf("pack_pattern '%s' conn: %s -> %s\n",
conn.pattern_name.c_str(),
describe_pb_graph_pin_hierarchy(conn.from_pin).c_str(),
describe_pb_graph_pin_hierarchy(conn.to_pin).c_str());
}
//Create the nodes
std::map<t_pb_graph_pin*, int> pb_graph_pin_to_pattern_node_id;
for (auto conn : connections) {
VTR_ASSERT(conn.pattern_name == pattern_name);
t_pb_graph_pin* from_pin = conn.from_pin;
t_pb_graph_pin* to_pin = conn.to_pin;
if (from_pin) {
if (!pb_graph_pin_to_pattern_node_id.count(from_pin)) {
//Create
int pattern_node_id = pack_pattern.nodes.size();
pack_pattern.nodes.emplace_back(from_pin);
//Save ID
pb_graph_pin_to_pattern_node_id[from_pin] = pattern_node_id;
}
}
if (to_pin) {
if (!pb_graph_pin_to_pattern_node_id.count(to_pin)) {
//Create
int pattern_node_id = pack_pattern.nodes.size();
pack_pattern.nodes.emplace_back(to_pin);
//Save ID
pb_graph_pin_to_pattern_node_id[to_pin] = pattern_node_id;
}
}
}
//Create the edges
std::map<std::pair<t_pb_graph_pin*,t_pb_graph_pin*>, int> conn_to_pattern_edge;
for (auto conn : connections) {
auto key = std::make_pair(conn.from_pin, conn.to_pin);
if (!conn_to_pattern_edge.count(key)) {
//Find connected nodes
VTR_ASSERT(pb_graph_pin_to_pattern_node_id.count(conn.from_pin));
VTR_ASSERT(pb_graph_pin_to_pattern_node_id.count(conn.to_pin));
int from_node_id = pb_graph_pin_to_pattern_node_id[conn.from_pin];
int to_node_id = pb_graph_pin_to_pattern_node_id[conn.to_pin];
//Create edge
int edge_id = pack_pattern.edges.size();
pack_pattern.edges.emplace_back();
pack_pattern.edges[edge_id].from_node_id = from_node_id;
pack_pattern.edges[edge_id].to_node_id = to_node_id;
//Save ID
conn_to_pattern_edge[key] = edge_id;
//Add edge references to connected nodes
pack_pattern.nodes[from_node_id].out_edge_ids.push_back(edge_id);
pack_pattern.nodes[to_node_id].in_edge_ids.push_back(edge_id);
}
}
//Record roots
for (size_t inode = 0; inode < pack_pattern.nodes.size(); ++inode) {
if (pack_pattern.nodes[inode].in_edge_ids.empty()) {
pack_pattern.root_node_ids.push_back(inode);
}
}
//Calculate the base cost of the pattern based on the nodes involved
std::set<t_pb_graph_node*> seen_pb_graph_nodes;
for (size_t inode = 0; inode < pack_pattern.nodes.size(); ++inode) {
t_pb_graph_node* pb_graph_node = pack_pattern.nodes[inode].pb_graph_pin->parent_node;
if (seen_pb_graph_nodes.count(pb_graph_node)) continue;
pack_pattern.base_cost += compute_primitive_base_cost(pb_graph_node);
}
return pack_pattern;
}
static t_netlist_pack_pattern abstract_arch_pack_pattern(const t_arch_pack_pattern& arch_pattern) {
t_netlist_pack_pattern netlist_pattern;
netlist_pattern.name = arch_pattern.name;
netlist_pattern.base_cost = arch_pattern.base_cost;
struct EdgeInfo {
EdgeInfo(int from_node, int from_edge, int via_edge, int curr_node)
: from_arch_node(from_node)
, from_arch_edge(from_edge)
, via_arch_edge(via_edge)
, curr_arch_node(curr_node) {}
int from_arch_node = OPEN; //Last primitive node we came from
int from_arch_edge = OPEN; //Last primitive edge we came from
int via_arch_edge = OPEN; //Edge to current node
int curr_arch_node = OPEN; //Current node
};
//Breadth-First Traversal of arch pack pattern graph,
//to record which primitives are reachable from each other.
//
//The resulting edges are abstracted from the
//pb_graph hierarchy and contain only primitives (no intermediate
//hiearchy)
//
//Note that to get the full set of possible edges we perform a BFT
//from each primitive/top-level node
std::vector<EdgeInfo> arch_abstract_edges;
for (int root_node : arch_pattern.root_node_ids) {
std::set<int> visited;
std::queue<EdgeInfo> q;
vtr::printf("BFT from root %d\n", root_node);
q.emplace(OPEN, OPEN, OPEN, root_node); //Starting at root
while (!q.empty()) {
EdgeInfo info = q.front();
q.pop();
int curr_node = info.curr_arch_node;
if (visited.count(curr_node)) continue; //Don't revisit to avoid loops
visited.insert(curr_node);
bool is_primitive = arch_pattern.nodes[curr_node].pb_graph_pin->parent_node->is_primitive();
bool is_top = arch_pattern.nodes[curr_node].pb_graph_pin->parent_node->is_top_level();
vtr::printf(" Visiting %d via %d (is_prim: %d)\n", curr_node, info.via_arch_edge, is_primitive);
if ((is_primitive || is_top) && info.from_arch_edge != OPEN && info.via_arch_edge != OPEN) {
//Record the primitive-to-primitive edge used to reach this node
arch_abstract_edges.push_back(info);
vtr::printf(" Recording Abstract Edge from node %d (via edge %d) -> node %d (via edge %d)\n", info.from_arch_node, info.from_arch_edge, curr_node, info.via_arch_edge);
}
//Expand out edges
for (auto out_edge : arch_pattern.nodes[curr_node].out_edge_ids) {
int next_node = arch_pattern.edges[out_edge].to_node_id;
if (is_primitive || curr_node == root_node) {
//Expanding from a primitive, use the current node as from
q.emplace(curr_node, out_edge, out_edge, next_node);
} else {
//Expanding from a non-primitive, re-use the previous primtiive as from
q.emplace(info.from_arch_node, info.from_arch_edge, out_edge, next_node);
}
}
}
}
//
//Build the netlist pattern from the edges collected above
//
std::map<t_pb_graph_pin*,int> pb_graph_pin_to_netlist_pattern_node;
std::map<t_pb_graph_node*,int> pb_graph_node_to_netlist_pattern_node;
std::map<t_pb_graph_pin*,int> pb_graph_pin_to_netlist_pattern_edge;
for (auto arch_abstract_edge : arch_abstract_edges) {
//Find or create from node
int netlist_from_node = OPEN;
t_pb_graph_pin* from_pin = arch_pattern.nodes[arch_abstract_edge.from_arch_node].pb_graph_pin;
if (pb_graph_pin_to_netlist_pattern_node.count(from_pin)) {
//Existing
netlist_from_node = pb_graph_pin_to_netlist_pattern_node[from_pin];
} else if (pb_graph_node_to_netlist_pattern_node.count(from_pin->parent_node)) {
//Existing
netlist_from_node = pb_graph_node_to_netlist_pattern_node[from_pin->parent_node];
} else if (from_pin->parent_node->is_top_level()) {
//Create
netlist_from_node = netlist_pattern.create_node(true);
//Default initialization
//Save Id
pb_graph_pin_to_netlist_pattern_node[from_pin] = netlist_from_node;
} else {
//Create
netlist_from_node = netlist_pattern.create_node(false);
//Initialize
netlist_pattern.nodes[netlist_from_node].model_type = from_pin->parent_node->pb_type->model;
//Save Id
pb_graph_node_to_netlist_pattern_node[from_pin->parent_node] = netlist_from_node;
}
VTR_ASSERT(netlist_from_node != OPEN);
//Find or create to node
int netlist_to_node = OPEN;
t_pb_graph_pin* to_pin = arch_pattern.nodes[arch_abstract_edge.curr_arch_node].pb_graph_pin;
if (pb_graph_pin_to_netlist_pattern_node.count(to_pin)) {
//Existing
netlist_to_node = pb_graph_pin_to_netlist_pattern_node[to_pin];
} else if (pb_graph_node_to_netlist_pattern_node.count(to_pin->parent_node)) {
//Existing
netlist_to_node = pb_graph_node_to_netlist_pattern_node[to_pin->parent_node];
} else if (to_pin->parent_node->is_top_level()) {
//Create
netlist_to_node = netlist_pattern.create_node(true);
//Default initialization
//Save Id
pb_graph_pin_to_netlist_pattern_node[to_pin] = netlist_to_node;
} else {
//Create
netlist_to_node = netlist_pattern.create_node(false);
//Initialize
netlist_pattern.nodes[netlist_to_node].model_type = to_pin->parent_node->pb_type->model;
//Save Id
pb_graph_node_to_netlist_pattern_node[to_pin->parent_node] = netlist_to_node;
}
VTR_ASSERT(netlist_to_node != OPEN);
//Create edge
int netlist_edge = OPEN;
if (pb_graph_pin_to_netlist_pattern_edge.count(from_pin)) {
//Existing
netlist_edge = pb_graph_pin_to_netlist_pattern_edge[from_pin];
} else {
//create
netlist_edge = netlist_pattern.create_edge();
//Initialize
netlist_pattern.edges[netlist_edge].from_pin.node_id = netlist_from_node;
if (!pb_graph_pin_to_netlist_pattern_node.count(from_pin)) {
//Only initialze these fields for non source/sink
netlist_pattern.edges[netlist_edge].from_pin.model = from_pin->parent_node->pb_type->model;
netlist_pattern.edges[netlist_edge].from_pin.model_port = from_pin->port->model_port;
netlist_pattern.edges[netlist_edge].from_pin.port_pin = from_pin->pin_number;
}
//Update node ref
netlist_pattern.nodes[netlist_from_node].out_edge_ids.push_back(netlist_edge);
//Save Id
pb_graph_pin_to_netlist_pattern_edge[from_pin] = netlist_edge;
}
//Add sink
t_netlist_pack_pattern_pin sink_pin;
sink_pin.node_id = netlist_to_node;
if (!pb_graph_pin_to_netlist_pattern_node.count(to_pin)) {
sink_pin.model = to_pin->parent_node->pb_type->model;
sink_pin.model_port = to_pin->port->model_port;
sink_pin.port_pin = to_pin->pin_number;
}
netlist_pattern.edges[netlist_edge].to_pins.push_back(sink_pin);
//Update node refs
netlist_pattern.nodes[netlist_to_node].in_edge_ids.push_back(netlist_edge);
}
//Record the roots
std::vector<int> roots;
for (size_t inode = 0; inode < netlist_pattern.nodes.size(); ++inode) {
if (netlist_pattern.nodes[inode].is_root()) {
roots.push_back(inode);
}
}
//Should have a single root
if (roots.size() == 1) {
VTR_ASSERT(netlist_pattern.root_node == OPEN);
netlist_pattern.root_node = roots[0];
} else {
std::string msg = vtr::string_fmt("Pack pattern '%s' has multiple roots (pack pattern "
"connection(s) are likely missing causing the pattern "
"to be disconnected)\n",
netlist_pattern.name.c_str());
msg += vtr::string_fmt(" Involved nodes:\n");
for (auto root : roots) {
if (netlist_pattern.nodes[root].model_type) {
msg += vtr::string_fmt("%s\n", netlist_pattern.nodes[root].model_type->name);
}
}
VPR_THROW(VPR_ERROR_ARCH, msg.c_str());
}
VTR_ASSERT(netlist_pattern.root_node != OPEN);
return netlist_pattern;
}
static NetlistPatternMatch match_largest(AtomBlockId blk, const t_netlist_pack_pattern& pattern, const AtomNetlist& netlist) {
NetlistPatternMatch match;
match.pattern_name = pattern.name;
match.base_cost = pattern.base_cost;
//Prefer matching to an internal node first (i.e. skipping the first external node),
//provided it has only a single fanout to an internal node
//
//For instance, this allows us to match a carry-chain, even if there is no net driving the chain input.
bool pattern_root_external = pattern.nodes[pattern.root_node].is_external();
bool pattern_root_single_fanout = (pattern.nodes[pattern.root_node].out_edge_ids.size() == 1);
if (pattern_root_external && pattern_root_single_fanout) {
VTR_ASSERT_MSG(pattern.nodes[pattern.root_node].out_edge_ids.size() == 1, "Assume single fanout external root");
int pattern_edge_id = pattern.nodes[pattern.root_node].out_edge_ids[0];
const auto& pattern_edge = pattern.edges[pattern_edge_id];
if (pattern_edge.to_pins.size() == 1) { //If a pattern has multiple fanout from external route we can't match to internal
const auto& to_pattern_pin = pattern_edge.to_pins[0];
const auto& to_pattern_node = pattern.nodes[to_pattern_pin.node_id];
VTR_ASSERT_MSG(to_pattern_node.is_internal(), "Assume single level of external root nodes");
//Match to the first internal node
if (match_largest_recur(match, blk, to_pattern_pin.node_id, pattern, netlist)) {
return match;
}
}
}
//Fall back to matching external root node
if (match_largest_recur(match, blk, pattern.root_node, pattern, netlist)) {
return match;
}
return NetlistPatternMatch(); //No match, return empty
}
static bool match_largest_recur(NetlistPatternMatch& match, const AtomBlockId blk,
int pattern_node_id, const t_netlist_pack_pattern& pattern,
const AtomNetlist& netlist) {
if (!matches_pattern_node(blk, pattern.nodes[pattern_node_id], netlist)) {
return false;
}
bool pattern_node_internal = pattern.nodes[pattern_node_id].is_internal();
if (pattern_node_internal) {
match.internal_blocks.push_back(blk);
} else {
VTR_ASSERT(pattern.nodes[pattern_node_id].is_external());
match.external_blocks.push_back(blk);
}
for (int pattern_edge_id : pattern.nodes[pattern_node_id].out_edge_ids) {
const auto& pattern_edge = pattern.edges[pattern_edge_id];
const auto& from_pattern_pin = pattern_edge.from_pin;
AtomPinId from_pin = find_matching_pin(netlist.block_output_pins(blk), from_pattern_pin, netlist);
if (!from_pin) {
//No matching driver pin
if (from_pattern_pin.required) {
//Required: give-up
return false;
} else {
//Optional: try next edge
continue;
}
}
AtomNetId net = netlist.pin_net(from_pin);
auto net_sinks = netlist.net_sinks(net);
for (size_t isink = 0; isink < pattern_edge.to_pins.size(); ++isink) {
const auto& to_pattern_pin = pattern_edge.to_pins[isink];
AtomPinId to_pin = find_matching_pin(net_sinks, to_pattern_pin, netlist);
if (!to_pin) {
//No valid to_pin
if (to_pattern_pin.required) {
//Required: give-up
return false;
} else {
//Optional: try next pattern pin
continue;
}
}
//Collect any downstream matches recursively
AtomBlockId to_blk = netlist.pin_block(to_pin);
bool subtree_matched = match_largest_recur(match, to_blk, to_pattern_pin.node_id, pattern, netlist);
if (!subtree_matched) {
if (to_pattern_pin.required) {
//Required: give-up
return false;
} else {
//Optional: try next pattern pin
continue;
}
}
//Valid match between netlist from_pin/to_pin and from_pattern_pin/to_pattern_pin
//Add it to the match
match.netlist_edges.emplace_back(from_pin, to_pin, pattern_edge_id, isink);
}
}
return true;
}
static AtomPinId find_matching_pin(const AtomNetlist::pin_range pin_range, const t_netlist_pack_pattern_pin& pattern_pin,
const AtomNetlist& netlist) {
return find_matching_pin(pin_range, pattern_pin, {}, netlist);
}
static AtomPinId find_matching_pin(const AtomNetlist::pin_range pin_range, const t_netlist_pack_pattern_pin& pattern_pin,
const std::set<AtomBlockId>& excluded_blocks, const AtomNetlist& netlist) {
for (AtomPinId pin : pin_range) {
if (matches_pattern_pin(pin, pattern_pin, netlist)) {
if (excluded_blocks.count(netlist.pin_block(pin))) {
continue;
} else {
return pin;
}
}
}
return AtomPinId::INVALID(); //No match
}
//Returns a parent block of blk, if it is also a valid root for pattern
static AtomBlockId find_parent_pattern_root(const AtomBlockId blk, const t_netlist_pack_pattern& pattern, const AtomNetlist& netlist) {
int pattern_node_id = pattern.root_node;
VTR_ASSERT_SAFE(matches_pattern_root(blk, pattern, netlist));
//Find an upstream block which is also a valid root
for (auto pins : {netlist.block_input_pins(blk), netlist.block_clock_pins(blk)}) { //Current blocks inputs
for (int pattern_edge_id : pattern.nodes[pattern_node_id].out_edge_ids) { //Root out edges
for (const auto& pattern_pin : pattern.edges[pattern_edge_id].to_pins) { //Edge pins
//Do the inputs of the current block match the output edges of the root pattern?
AtomPinId to_pin = find_matching_pin(pins, pattern_pin, netlist);
if (!to_pin) continue;
AtomNetId in_net = netlist.pin_net(to_pin);
AtomBlockId from_blk = netlist.net_driver_block(in_net);
if (!matches_pattern_root(from_blk, pattern, netlist)) continue;
return from_blk;
}
}
}
return AtomBlockId::INVALID();
}
//Returns true if matches the pattern root node (and it's out-going edges)
static bool matches_pattern_root(const AtomBlockId blk, const t_netlist_pack_pattern& pattern, const AtomNetlist& netlist) {
int pattern_node_id = pattern.root_node;
if (!matches_pattern_node(blk, pattern.nodes[pattern_node_id], netlist)) {
return false;
}
for (int pattern_edge_id : pattern.nodes[pattern_node_id].out_edge_ids) {
const auto& pattern_edge = pattern.edges[pattern_edge_id];
AtomPinId from_pin = find_matching_pin(netlist.block_output_pins(blk), pattern_edge.from_pin, netlist);
if (!from_pin) {
//No matching driver pin
return false;
}
AtomNetId net = netlist.pin_net(from_pin);
auto net_sinks = netlist.net_sinks(net);
for (size_t isink = 0; isink < pattern_edge.to_pins.size(); ++isink) {
const auto& to_pattern_pin = pattern_edge.to_pins[isink];
AtomPinId to_pin = find_matching_pin(net_sinks, to_pattern_pin, netlist);
if (!to_pin) {
if (to_pattern_pin.required) {
//Required: give-up
return false;
} else {
//Optional: try next pattern pin
continue;
}
}
}
}
return true;
}
static bool matches_pattern_node(const AtomBlockId blk, const t_netlist_pack_pattern_node& pattern_node, const AtomNetlist& netlist) {
if (is_wildcard_node(pattern_node)) {
return true; //Wildcard
}
return netlist.block_model(blk) == pattern_node.model_type;
}
static bool is_wildcard_node(const t_netlist_pack_pattern_node& pattern_node) {
return pattern_node.model_type == nullptr;
}
static bool is_wildcard_pin(const t_netlist_pack_pattern_pin& pattern_pin) {
return (pattern_pin.model == nullptr
&& pattern_pin.model_port == nullptr
&& pattern_pin.port_pin == OPEN);
}
static bool matches_pattern_pin(const AtomPinId from_pin, const t_netlist_pack_pattern_pin& pattern_pin, const AtomNetlist& netlist) {
if (is_wildcard_pin(pattern_pin)) {
return true; //Wildcard
}
if (netlist.block_model(netlist.pin_block(from_pin)) != pattern_pin.model
|| netlist.port_model(netlist.pin_port(from_pin)) != pattern_pin.model_port
|| netlist.pin_port_bit(from_pin) != (BitIndex) pattern_pin.port_pin) {
return false;
}
return true;
}
static bool match_covered(const NetlistPatternMatch& match, const std::map<std::string,std::set<AtomBlockId>>& pattern_covered_blocks) {
for (auto blk : match.internal_blocks) {
auto itr = pattern_covered_blocks.find(match.pattern_name);
if (itr != pattern_covered_blocks.end() && itr->second.count(blk)) return true;
}
return false;
}