blob: e9380b12d7e0c0c4e49c4ef070a0362566817252 [file] [log] [blame]
/*This function loads in a routing resource graph written in xml format
* into vpr when the option --read_rr_graph <file name> is specified.
* When it is not specified the build_rr_graph function is then called.
* This is done using the libpugixml library. This is useful
* when specific routing resources should remain constant or when
* some information left out in the architecture can be specified
* in the routing resource graph. The routing resource graph file is
* contained in a <rr_graph> tag. Its subtags describe the rr graph's
* various features such as nodes, edges, and segments. Information such
* as blocks, grids, and segments are verified with the architecture
* to ensure it matches. An error will through if any feature does not match.
* Other elements such as edges, nodes, and switches
* are overwritten by the rr graph file if one is specified. If an optional
* identifier such as capacitance is not specified, it is set to 0*/
#include <string.h>
#include <algorithm>
#include <ctime>
#include <sstream>
#include <utility>
#include "vtr_version.h"
#include "vtr_assert.h"
#include "vtr_util.h"
#include "vtr_memory.h"
#include "vtr_math.h"
#include "vtr_log.h"
#include "vtr_time.h"
#include "pugixml.hpp"
#include "pugixml_util.hpp"
#include "read_xml_arch_file.h"
#include "read_xml_util.h"
#include "globals.h"
#include "rr_graph.h"
#include "rr_graph2.h"
#include "rr_metadata.h"
#include "rr_graph_indexed_data.h"
#include "rr_graph_writer.h"
#include "check_rr_graph.h"
#include "echo_files.h"
#include "vpr_types.h"
#include "vpr_utils.h"
#include "vpr_error.h"
#include "rr_graph_reader.h"
/*********************** Subroutines local to this module *******************/
void process_switches(pugi::xml_node parent, const pugiutil::loc_data& loc_data);
void verify_segments(pugi::xml_node parent, const pugiutil::loc_data& loc_data, const std::vector<t_segment_inf>& segment_inf);
void verify_blocks(pugi::xml_node parent, const pugiutil::loc_data& loc_data);
void process_blocks(pugi::xml_node parent, const pugiutil::loc_data& loc_data);
void verify_grid(pugi::xml_node parent, const pugiutil::loc_data& loc_data, const DeviceGrid& grid);
void process_nodes(pugi::xml_node parent, const pugiutil::loc_data& loc_data);
void process_edges(pugi::xml_node parent, const pugiutil::loc_data& loc_data, int* wire_to_rr_ipin_switch, const int num_rr_switches);
void process_channels(t_chan_width& chan_width, const DeviceGrid& grid, pugi::xml_node parent, const pugiutil::loc_data& loc_data);
void process_rr_node_indices(const DeviceGrid& grid);
void process_seg_id(pugi::xml_node parent, const pugiutil::loc_data& loc_data);
void set_cost_indices(pugi::xml_node parent, const pugiutil::loc_data& loc_data, const bool is_global_graph, const int num_seg_types);
/************************ Subroutine definitions ****************************/
/*loads the given RR_graph file into the appropriate data structures
* as specified by read_rr_graph_name. Set up correct routing data
* structures as well*/
void load_rr_file(const t_graph_type graph_type,
const DeviceGrid& grid,
const std::vector<t_segment_inf>& segment_inf,
const enum e_base_cost_type base_cost_type,
int* wire_to_rr_ipin_switch,
const char* read_rr_graph_name) {
vtr::ScopedStartFinishTimer timer("Loading routing resource graph");
const char* Prop;
pugi::xml_node next_component;
pugi::xml_document doc;
pugiutil::loc_data loc_data;
if (vtr::check_file_name_extension(read_rr_graph_name, ".xml") == false) {
VTR_LOG_WARN(
"RR graph file '%s' may be in incorrect format. "
"Expecting .xml format\n",
read_rr_graph_name);
}
try {
//parse the file
loc_data = pugiutil::load_xml(doc, read_rr_graph_name);
auto& device_ctx = g_vpr_ctx.mutable_device();
auto rr_graph = get_single_child(doc, "rr_graph", loc_data);
//Check for errors
Prop = get_attribute(rr_graph, "tool_version", loc_data, pugiutil::OPTIONAL).as_string(nullptr);
if (Prop != nullptr) {
if (strcmp(Prop, vtr::VERSION) != 0) {
VTR_LOG("\n");
VTR_LOG_WARN("This architecture version is for VPR %s while your current VPR version is %s compatability issues may arise\n",
vtr::VERSION, Prop);
VTR_LOG("\n");
}
}
Prop = get_attribute(rr_graph, "tool_comment", loc_data, pugiutil::OPTIONAL).as_string(nullptr);
std::string correct_string = "Generated from arch file ";
correct_string += get_arch_file_name();
if (Prop != nullptr) {
if (Prop != correct_string) {
VTR_LOG("\n");
VTR_LOG_WARN("This RR graph file is based on %s while your input architecture file is %s compatability issues may arise\n",
get_arch_file_name(), Prop);
VTR_LOG("\n");
}
}
//Compare with the architecture file to ensure consistency
next_component = get_single_child(rr_graph, "grid", loc_data);
verify_grid(next_component, loc_data, grid);
next_component = get_single_child(rr_graph, "block_types", loc_data);
verify_blocks(next_component, loc_data);
next_component = get_single_child(rr_graph, "segments", loc_data);
verify_segments(next_component, loc_data, segment_inf);
VTR_LOG("Starting build routing resource graph...\n");
next_component = get_first_child(rr_graph, "channels", loc_data);
t_chan_width nodes_per_chan;
process_channels(nodes_per_chan, grid, next_component, loc_data);
/* Decode the graph_type */
bool is_global_graph = (GRAPH_GLOBAL == graph_type ? true : false);
/* Global routing uses a single longwire track */
int max_chan_width = (is_global_graph ? 1 : nodes_per_chan.max);
VTR_ASSERT(max_chan_width > 0);
/* Alloc rr nodes and count count nodes */
next_component = get_single_child(rr_graph, "rr_nodes", loc_data);
int num_rr_nodes = count_children(next_component, "node", loc_data);
device_ctx.rr_nodes.resize(num_rr_nodes);
process_nodes(next_component, loc_data);
/* Loads edges, switches, and node look up tables*/
next_component = get_single_child(rr_graph, "switches", loc_data);
int numSwitches = count_children(next_component, "switch", loc_data);
device_ctx.rr_switch_inf.resize(numSwitches);
process_switches(next_component, loc_data);
next_component = get_single_child(rr_graph, "rr_edges", loc_data);
process_edges(next_component, loc_data, wire_to_rr_ipin_switch, numSwitches);
//Partition the rr graph edges for efficient access to configurable/non-configurable
//edge subsets. Must be done after RR switches have been allocated
partition_rr_graph_edges(device_ctx);
process_rr_node_indices(grid);
init_fan_in(device_ctx.rr_nodes, device_ctx.rr_nodes.size());
//sets the cost index and seg id information
next_component = get_single_child(rr_graph, "rr_nodes", loc_data);
set_cost_indices(next_component, loc_data, is_global_graph, segment_inf.size());
alloc_and_load_rr_indexed_data(segment_inf, device_ctx.rr_node_indices,
max_chan_width, *wire_to_rr_ipin_switch, base_cost_type);
process_seg_id(next_component, loc_data);
device_ctx.chan_width = nodes_per_chan;
device_ctx.read_rr_graph_filename = std::string(read_rr_graph_name);
check_rr_graph(graph_type, grid, device_ctx.physical_tile_types);
} catch (pugiutil::XmlError& e) {
vpr_throw(VPR_ERROR_ROUTE, read_rr_graph_name, e.line(), "%s", e.what());
}
}
/* Reads in the switch information and adds it to device_ctx.rr_switch_inf as specified*/
void process_switches(pugi::xml_node parent, const pugiutil::loc_data& loc_data) {
auto& device_ctx = g_vpr_ctx.mutable_device();
pugi::xml_node Switch, SwitchSubnode;
Switch = get_first_child(parent, "switch", loc_data);
while (Switch) {
int iSwitch = get_attribute(Switch, "id", loc_data).as_int();
auto& rr_switch = device_ctx.rr_switch_inf[iSwitch];
const char* name = get_attribute(Switch, "name", loc_data, pugiutil::OPTIONAL).as_string(nullptr);
bool found_arch_name = false;
if (name != nullptr) {
for (int i = 0; i < device_ctx.num_arch_switches; ++i) {
if (strcmp(name, device_ctx.arch_switch_inf[i].name) == 0) {
name = device_ctx.arch_switch_inf[i].name;
found_arch_name = true;
break;
}
}
}
if (name != nullptr && !found_arch_name) {
VPR_FATAL_ERROR(VPR_ERROR_ROUTE, "Switch name '%s' not found in architecture\n", name);
}
rr_switch.name = name;
std::string switch_type_str = get_attribute(Switch, "type", loc_data).as_string();
SwitchType switch_type = SwitchType::INVALID;
if (switch_type_str == "tristate") {
switch_type = SwitchType::TRISTATE;
} else if (switch_type_str == "mux") {
switch_type = SwitchType::MUX;
} else if (switch_type_str == "pass_gate") {
switch_type = SwitchType::PASS_GATE;
} else if (switch_type_str == "short") {
switch_type = SwitchType::SHORT;
} else if (switch_type_str == "buffer") {
switch_type = SwitchType::BUFFER;
} else {
VPR_FATAL_ERROR(VPR_ERROR_ROUTE, "Invalid switch type '%s'\n", switch_type_str.c_str());
}
rr_switch.set_type(switch_type);
SwitchSubnode = get_single_child(Switch, "timing", loc_data, pugiutil::OPTIONAL);
if (SwitchSubnode) {
rr_switch.R = get_attribute(SwitchSubnode, "R", loc_data).as_float();
rr_switch.Cin = get_attribute(SwitchSubnode, "Cin", loc_data).as_float();
rr_switch.Cout = get_attribute(SwitchSubnode, "Cout", loc_data).as_float();
rr_switch.Cinternal = get_attribute(SwitchSubnode, "Cinternal", loc_data).as_float();
rr_switch.Tdel = get_attribute(SwitchSubnode, "Tdel", loc_data).as_float();
} else {
rr_switch.R = 0;
rr_switch.Cin = 0;
rr_switch.Cout = 0;
rr_switch.Cinternal = 0;
rr_switch.Tdel = 0;
}
SwitchSubnode = get_single_child(Switch, "sizing", loc_data);
rr_switch.mux_trans_size = get_attribute(SwitchSubnode, "mux_trans_size", loc_data).as_float();
rr_switch.buf_size = get_attribute(SwitchSubnode, "buf_size", loc_data).as_float();
Switch = Switch.next_sibling(Switch.name());
}
}
/*Only CHANX and CHANY components have a segment id. This function
*reads in the segment id of each node*/
void process_seg_id(pugi::xml_node parent, const pugiutil::loc_data& loc_data) {
auto& device_ctx = g_vpr_ctx.mutable_device();
pugi::xml_node segmentSubnode;
pugi::xml_node rr_node;
pugi::xml_attribute attribute;
int id;
rr_node = get_first_child(parent, "node", loc_data);
while (rr_node) {
id = get_attribute(rr_node, "id", loc_data).as_int();
auto& node = device_ctx.rr_nodes[id];
segmentSubnode = get_single_child(rr_node, "segment", loc_data, pugiutil::OPTIONAL);
if (segmentSubnode) {
attribute = get_attribute(segmentSubnode, "segment_id", loc_data, pugiutil::OPTIONAL);
if (attribute) {
int seg_id = get_attribute(segmentSubnode, "segment_id", loc_data).as_int(0);
device_ctx.rr_indexed_data[node.cost_index()].seg_index = seg_id;
} else {
//-1 for non chanx or chany nodes
device_ctx.rr_indexed_data[node.cost_index()].seg_index = -1;
}
}
rr_node = rr_node.next_sibling(rr_node.name());
}
}
/* Node info are processed. Seg_id of nodes are processed separately when rr_index_data is allocated*/
void process_nodes(pugi::xml_node parent, const pugiutil::loc_data& loc_data) {
auto& device_ctx = g_vpr_ctx.mutable_device();
pugi::xml_node locSubnode, timingSubnode, segmentSubnode;
pugi::xml_node rr_node;
rr_node = get_first_child(parent, "node", loc_data);
while (rr_node) {
int inode = get_attribute(rr_node, "id", loc_data).as_int();
auto& node = device_ctx.rr_nodes[inode];
const char* node_type = get_attribute(rr_node, "type", loc_data).as_string();
if (strcmp(node_type, "CHANX") == 0) {
node.set_type(CHANX);
} else if (strcmp(node_type, "CHANY") == 0) {
node.set_type(CHANY);
} else if (strcmp(node_type, "SOURCE") == 0) {
node.set_type(SOURCE);
} else if (strcmp(node_type, "SINK") == 0) {
node.set_type(SINK);
} else if (strcmp(node_type, "OPIN") == 0) {
node.set_type(OPIN);
} else if (strcmp(node_type, "IPIN") == 0) {
node.set_type(IPIN);
} else {
VPR_FATAL_ERROR(VPR_ERROR_OTHER,
"Valid inputs for class types are \"CHANX\", \"CHANY\",\"SOURCE\", \"SINK\",\"OPIN\", and \"IPIN\".");
}
if (node.type() == CHANX || node.type() == CHANY) {
const char* correct_direction = get_attribute(rr_node, "direction", loc_data).as_string();
if (strcmp(correct_direction, "INC_DIR") == 0) {
node.set_direction(INC_DIRECTION);
} else if (strcmp(correct_direction, "DEC_DIR") == 0) {
node.set_direction(DEC_DIRECTION);
} else if (strcmp(correct_direction, "BI_DIR") == 0) {
node.set_direction(BI_DIRECTION);
} else {
VTR_ASSERT((strcmp(correct_direction, "NO_DIR") == 0));
node.set_direction(NO_DIRECTION);
}
}
node.set_capacity(get_attribute(rr_node, "capacity", loc_data).as_float());
//--------------
locSubnode = get_single_child(rr_node, "loc", loc_data);
short x1, x2, y1, y2;
x1 = get_attribute(locSubnode, "xlow", loc_data).as_float();
x2 = get_attribute(locSubnode, "xhigh", loc_data).as_float();
y1 = get_attribute(locSubnode, "ylow", loc_data).as_float();
y2 = get_attribute(locSubnode, "yhigh", loc_data).as_float();
if (node.type() == IPIN || node.type() == OPIN) {
e_side side;
std::string side_str = get_attribute(locSubnode, "side", loc_data).as_string();
if (side_str == "LEFT") {
side = LEFT;
} else if (side_str == "RIGHT") {
side = RIGHT;
} else if (side_str == "TOP") {
side = TOP;
} else {
VTR_ASSERT(side_str == "BOTTOM");
side = BOTTOM;
}
node.set_side(side);
}
node.set_coordinates(x1, y1, x2, y2);
node.set_ptc_num(get_attribute(locSubnode, "ptc", loc_data).as_int());
//-------
timingSubnode = get_single_child(rr_node, "timing", loc_data, pugiutil::OPTIONAL);
float R = 0.;
float C = 0.;
if (timingSubnode) {
R = get_attribute(timingSubnode, "R", loc_data).as_float();
C = get_attribute(timingSubnode, "C", loc_data).as_float();
}
node.set_rc_index(find_create_rr_rc_data(R, C));
//clear each node edge
node.set_num_edges(0);
// <metadata>
// <meta name='grid_prefix' >CLBLL_L_</meta>
// </metadata>
auto metadata = get_single_child(rr_node, "metadata", loc_data, pugiutil::OPTIONAL);
if (metadata) {
auto rr_node_meta = get_first_child(metadata, "meta", loc_data);
while (rr_node_meta) {
auto key = get_attribute(rr_node_meta, "name", loc_data).as_string();
vpr::add_rr_node_metadata(inode, key, rr_node_meta.child_value());
rr_node_meta = rr_node_meta.next_sibling(rr_node_meta.name());
}
}
rr_node = rr_node.next_sibling(rr_node.name());
}
}
/*Loads the edges information from file into vpr. Nodes and switches must be loaded
* before calling this function*/
void process_edges(pugi::xml_node parent, const pugiutil::loc_data& loc_data, int* wire_to_rr_ipin_switch, const int num_rr_switches) {
auto& device_ctx = g_vpr_ctx.mutable_device();
pugi::xml_node edges;
edges = get_first_child(parent, "edge", loc_data);
//count the number of edges and store it in a vector
std::vector<size_t> num_edges_for_node;
num_edges_for_node.resize(device_ctx.rr_nodes.size(), 0);
while (edges) {
size_t source_node = get_attribute(edges, "src_node", loc_data).as_uint();
if (source_node >= device_ctx.rr_nodes.size()) {
VPR_FATAL_ERROR(VPR_ERROR_OTHER,
"source_node %d is larger than rr_nodes.size() %d",
source_node, device_ctx.rr_nodes.size());
}
num_edges_for_node[source_node]++;
edges = edges.next_sibling(edges.name());
}
//reset this vector in order to start count for num edges again
for (size_t inode = 0; inode < device_ctx.rr_nodes.size(); inode++) {
if (num_edges_for_node[inode] > std::numeric_limits<t_edge_size>::max()) {
VPR_FATAL_ERROR(VPR_ERROR_OTHER,
"source node %d edge count %d is too high",
inode, num_edges_for_node[inode]);
}
device_ctx.rr_nodes[inode].set_num_edges(num_edges_for_node[inode]);
num_edges_for_node[inode] = 0;
}
edges = get_first_child(parent, "edge", loc_data);
/*initialize a vector that keeps track of the number of wire to ipin switches
* There should be only one wire to ipin switch. In case there are more, make sure to
* store the most frequent switch */
std::vector<int> count_for_wire_to_ipin_switches;
count_for_wire_to_ipin_switches.resize(num_rr_switches, 0);
//first is index, second is count
std::pair<int, int> most_frequent_switch(-1, 0);
while (edges) {
size_t source_node = get_attribute(edges, "src_node", loc_data).as_uint();
size_t sink_node = get_attribute(edges, "sink_node", loc_data).as_uint();
int switch_id = get_attribute(edges, "switch_id", loc_data).as_int();
if (sink_node >= device_ctx.rr_nodes.size()) {
VPR_FATAL_ERROR(VPR_ERROR_OTHER,
"sink_node %d is larger than rr_nodes.size() %d",
sink_node, device_ctx.rr_nodes.size());
}
if (switch_id >= num_rr_switches) {
VPR_FATAL_ERROR(VPR_ERROR_OTHER,
"switch_id %d is larger than num_rr_switches %d",
switch_id, num_rr_switches);
}
/*Keeps track of the number of the specific type of switch that connects a wire to an ipin
* use the pair data structure to keep the maximum*/
if (device_ctx.rr_nodes[source_node].type() == CHANX || device_ctx.rr_nodes[source_node].type() == CHANY) {
if (device_ctx.rr_nodes[sink_node].type() == IPIN) {
count_for_wire_to_ipin_switches[switch_id]++;
if (count_for_wire_to_ipin_switches[switch_id] > most_frequent_switch.second) {
most_frequent_switch.first = switch_id;
most_frequent_switch.second = count_for_wire_to_ipin_switches[switch_id];
}
}
}
//set edge in correct rr_node data structure
device_ctx.rr_nodes[source_node].set_edge_sink_node(num_edges_for_node[source_node], sink_node);
device_ctx.rr_nodes[source_node].set_edge_switch(num_edges_for_node[source_node], switch_id);
// Read the metadata for the edge
auto metadata = get_single_child(edges, "metadata", loc_data, pugiutil::OPTIONAL);
if (metadata) {
auto edges_meta = get_first_child(metadata, "meta", loc_data);
while (edges_meta) {
auto key = get_attribute(edges_meta, "name", loc_data).as_string();
vpr::add_rr_edge_metadata(source_node, sink_node, switch_id,
key, edges_meta.child_value());
edges_meta = edges_meta.next_sibling(edges_meta.name());
}
}
num_edges_for_node[source_node]++;
edges = edges.next_sibling(edges.name()); //Next edge
}
*wire_to_rr_ipin_switch = most_frequent_switch.first;
num_edges_for_node.clear();
count_for_wire_to_ipin_switches.clear();
}
/* All channel info is read in and loaded into device_ctx.chan_width*/
void process_channels(t_chan_width& chan_width, const DeviceGrid& grid, pugi::xml_node parent, const pugiutil::loc_data& loc_data) {
pugi::xml_node channel, channelLists;
channel = get_first_child(parent, "channel", loc_data);
chan_width.max = get_attribute(channel, "chan_width_max", loc_data).as_uint();
chan_width.x_min = get_attribute(channel, "x_min", loc_data).as_uint();
chan_width.y_min = get_attribute(channel, "y_min", loc_data).as_uint();
chan_width.x_max = get_attribute(channel, "x_max", loc_data).as_uint();
chan_width.y_max = get_attribute(channel, "y_max", loc_data).as_uint();
chan_width.x_list.resize(grid.height());
chan_width.y_list.resize(grid.width());
channelLists = get_first_child(parent, "x_list", loc_data);
while (channelLists) {
size_t index = get_attribute(channelLists, "index", loc_data).as_uint();
if (index >= chan_width.x_list.size()) {
VPR_FATAL_ERROR(VPR_ERROR_OTHER,
"index %d on x_list exceeds x_list size %u",
index, chan_width.x_list.size());
}
chan_width.x_list[index] = get_attribute(channelLists, "info", loc_data).as_float();
channelLists = channelLists.next_sibling(channelLists.name());
}
channelLists = get_first_child(parent, "y_list", loc_data);
while (channelLists) {
size_t index = get_attribute(channelLists, "index", loc_data).as_uint();
if (index >= chan_width.y_list.size()) {
VPR_FATAL_ERROR(VPR_ERROR_OTHER,
"index %d on y_list exceeds y_list size %u",
index, chan_width.y_list.size());
}
chan_width.y_list[index] = get_attribute(channelLists, "info", loc_data).as_float();
channelLists = channelLists.next_sibling(channelLists.name());
}
}
/* Grid was initialized from the architecture file. This function checks
* if it corresponds to the RR graph. Errors out if it doesn't correspond*/
void verify_grid(pugi::xml_node parent, const pugiutil::loc_data& loc_data, const DeviceGrid& grid) {
pugi::xml_node grid_node;
int num_grid_node = count_children(parent, "grid_loc", loc_data);
grid_node = get_first_child(parent, "grid_loc", loc_data);
for (int i = 0; i < num_grid_node; i++) {
int x = get_attribute(grid_node, "x", loc_data).as_float();
int y = get_attribute(grid_node, "y", loc_data).as_float();
const t_grid_tile& grid_tile = grid[x][y];
if (grid_tile.type->index != get_attribute(grid_node, "block_type_id", loc_data).as_int(0)) {
VPR_FATAL_ERROR(VPR_ERROR_OTHER,
"Architecture file does not match RR graph's block_type_id at (%d, %d): arch used ID %d, RR graph used ID %d.", x, y,
(grid_tile.type->index), get_attribute(grid_node, "block_type_id", loc_data).as_int(0));
}
if (grid_tile.width_offset != get_attribute(grid_node, "width_offset", loc_data).as_float(0)) {
VPR_FATAL_ERROR(VPR_ERROR_OTHER,
"Architecture file does not match RR graph's width_offset at (%d, %d)", x, y);
}
if (grid_tile.height_offset != get_attribute(grid_node, "height_offset", loc_data).as_float(0)) {
VPR_FATAL_ERROR(VPR_ERROR_OTHER,
"Architecture file does not match RR graph's height_offset at (%d, %d)", x, y);
}
grid_node = grid_node.next_sibling(grid_node.name());
}
}
static int pin_index_by_num(const t_class& class_inf, int num) {
for (int index = 0; index < class_inf.num_pins; ++index) {
if (num == class_inf.pinlist[index]) {
return index;
}
}
return -1;
}
/* Blocks were initialized from the architecture file. This function checks
* if it corresponds to the RR graph. Errors out if it doesn't correspond*/
void verify_blocks(pugi::xml_node parent, const pugiutil::loc_data& loc_data) {
pugi::xml_node Block;
pugi::xml_node pin_class;
pugi::xml_node pin;
Block = get_first_child(parent, "block_type", loc_data);
auto& device_ctx = g_vpr_ctx.mutable_device();
while (Block) {
auto block_info = device_ctx.physical_tile_types[get_attribute(Block, "id", loc_data).as_int(0)];
const char* name = get_attribute(Block, "name", loc_data).as_string(nullptr);
if (strcmp(block_info.name, name) != 0) {
VPR_FATAL_ERROR(VPR_ERROR_OTHER,
"Architecture file does not match RR graph's block name: arch uses name %s, RR graph uses name %s", block_info.name, name);
}
if (block_info.width != get_attribute(Block, "width", loc_data).as_float(0)) {
VPR_FATAL_ERROR(VPR_ERROR_OTHER,
"Architecture file does not match RR graph's block width");
}
if (block_info.height != get_attribute(Block, "height", loc_data).as_float(0)) {
VPR_FATAL_ERROR(VPR_ERROR_OTHER,
"Architecture file does not match RR graph's block height");
}
pin_class = get_first_child(Block, "pin_class", loc_data, pugiutil::OPTIONAL);
block_info.num_class = count_children(Block, "pin_class", loc_data, pugiutil::OPTIONAL);
for (int classNum = 0; classNum < block_info.num_class; classNum++) {
auto& class_inf = block_info.class_inf[classNum];
e_pin_type type;
/*Verify types and pins*/
const char* typeInfo = get_attribute(pin_class, "type", loc_data).value();
if (strcmp(typeInfo, "OPEN") == 0) {
type = OPEN;
} else if (strcmp(typeInfo, "OUTPUT") == 0) {
type = DRIVER;
} else if (strcmp(typeInfo, "INPUT") == 0) {
type = RECEIVER;
} else {
VPR_FATAL_ERROR(VPR_ERROR_OTHER,
"Valid inputs for class types are \"OPEN\", \"OUTPUT\", and \"INPUT\".");
type = OPEN;
}
if (class_inf.type != type) {
VPR_FATAL_ERROR(VPR_ERROR_OTHER,
"Architecture file does not match RR graph's block type");
}
if (class_inf.num_pins != (int)count_children(pin_class, "pin", loc_data)) {
VPR_FATAL_ERROR(VPR_ERROR_OTHER,
"Incorrect number of pins in %d pin_class in block %s", classNum, block_info.name);
}
pin = get_first_child(pin_class, "pin", loc_data, pugiutil::OPTIONAL);
while (pin) {
auto num = get_attribute(pin, "ptc", loc_data).as_uint();
auto index = pin_index_by_num(class_inf, num);
if (index < 0) {
VPR_FATAL_ERROR(VPR_ERROR_OTHER,
"Architecture file does not match RR graph's block pin list: invalid ptc for pin class");
}
if (pin.child_value() != block_type_pin_index_to_name(&block_info, class_inf.pinlist[index])) {
VPR_FATAL_ERROR(VPR_ERROR_OTHER,
"Architecture file does not match RR graph's block pin list");
}
pin = pin.next_sibling("pin");
}
pin_class = pin_class.next_sibling(pin_class.name());
}
Block = Block.next_sibling(Block.name());
}
}
/* Segments was initialized already. This function checks
* if it corresponds to the RR graph. Errors out if it doesn't correspond*/
void verify_segments(pugi::xml_node parent, const pugiutil::loc_data& loc_data, const std::vector<t_segment_inf>& segment_inf) {
pugi::xml_node Segment, subnode;
Segment = get_first_child(parent, "segment", loc_data);
while (Segment) {
int segNum = get_attribute(Segment, "id", loc_data).as_int();
const char* name = get_attribute(Segment, "name", loc_data).as_string();
if (strcmp(segment_inf[segNum].name.c_str(), name) != 0) {
VPR_FATAL_ERROR(VPR_ERROR_OTHER,
"Architecture file does not match RR graph's segment name: arch uses %s, RR graph uses %s", segment_inf[segNum].name.c_str(), name);
}
subnode = get_single_child(parent, "timing", loc_data, pugiutil::OPTIONAL);
if (subnode) {
if (segment_inf[segNum].Rmetal != get_attribute(subnode, "R_per_meter", loc_data).as_float(0)) {
VPR_FATAL_ERROR(VPR_ERROR_OTHER,
"Architecture file does not match RR graph's segment R_per_meter");
}
if (segment_inf[segNum].Cmetal != get_attribute(subnode, "C_per_meter", loc_data).as_float(0)) {
VPR_FATAL_ERROR(VPR_ERROR_OTHER,
"Architecture file does not match RR graph's segment C_per_meter");
}
}
Segment = Segment.next_sibling(Segment.name());
}
}
/*Allocates and load the rr_node look up table. SINK and SOURCE, IPIN and OPIN
*share the same look up table. CHANX and CHANY have individual look ups */
void process_rr_node_indices(const DeviceGrid& grid) {
auto& device_ctx = g_vpr_ctx.mutable_device();
/* Alloc the lookup table */
auto& indices = device_ctx.rr_node_indices;
indices.resize(NUM_RR_TYPES);
typedef struct max_ptc {
short chanx_max_ptc = 0;
short chany_max_ptc = 0;
} t_max_ptc;
/*
* Local multi-dimensional vector to hold max_ptc for every coordinate.
* It has same height and width as CHANY and CHANX are inverted
*/
vtr::Matrix<t_max_ptc> coordinates_max_ptc; /* [x][y] */
size_t max_coord_size = std::max(grid.width(), grid.height());
coordinates_max_ptc.resize({max_coord_size, max_coord_size}, t_max_ptc());
/* Alloc the lookup table */
for (t_rr_type rr_type : RR_TYPES) {
if (rr_type == CHANX) {
indices[rr_type].resize(grid.height());
for (size_t y = 0; y < grid.height(); ++y) {
indices[rr_type][y].resize(grid.width());
for (size_t x = 0; x < grid.width(); ++x) {
indices[rr_type][y][x].resize(NUM_SIDES);
}
}
} else {
indices[rr_type].resize(grid.width());
for (size_t x = 0; x < grid.width(); ++x) {
indices[rr_type][x].resize(grid.height());
for (size_t y = 0; y < grid.height(); ++y) {
indices[rr_type][x][y].resize(NUM_SIDES);
}
}
}
}
/*
* Add the correct node into the vector
* For CHANX and CHANY no node is added yet, but the maximum ptc is counted for each
* x/y location. This is needed later to add the correct node corresponding to CHANX
* and CHANY.
*
* Note that CHANX and CHANY 's x and y are swapped due to the chan and seg convention.
*/
for (size_t inode = 0; inode < device_ctx.rr_nodes.size(); inode++) {
auto& node = device_ctx.rr_nodes[inode];
if (node.type() == SOURCE || node.type() == SINK) {
for (int ix = node.xlow(); ix <= node.xhigh(); ix++) {
for (int iy = node.ylow(); iy <= node.yhigh(); iy++) {
if (node.type() == SOURCE) {
indices[SOURCE][ix][iy][0].push_back(inode);
indices[SINK][ix][iy][0].push_back(OPEN);
} else {
VTR_ASSERT(node.type() == SINK);
indices[SINK][ix][iy][0].push_back(inode);
indices[SOURCE][ix][iy][0].push_back(OPEN);
}
}
}
} else if (node.type() == IPIN || node.type() == OPIN) {
for (int ix = node.xlow(); ix <= node.xhigh(); ix++) {
for (int iy = node.ylow(); iy <= node.yhigh(); iy++) {
if (node.type() == OPIN) {
indices[OPIN][ix][iy][node.side()].push_back(inode);
indices[IPIN][ix][iy][node.side()].push_back(OPEN);
} else {
VTR_ASSERT(node.type() == IPIN);
indices[IPIN][ix][iy][node.side()].push_back(inode);
indices[OPIN][ix][iy][node.side()].push_back(OPEN);
}
}
}
} else if (node.type() == CHANX) {
for (int ix = node.xlow(); ix <= node.xhigh(); ix++) {
for (int iy = node.ylow(); iy <= node.yhigh(); iy++) {
coordinates_max_ptc[iy][ix].chanx_max_ptc = std::max(coordinates_max_ptc[iy][ix].chanx_max_ptc, node.ptc_num());
}
}
} else if (node.type() == CHANY) {
for (int ix = node.xlow(); ix <= node.xhigh(); ix++) {
for (int iy = node.ylow(); iy <= node.yhigh(); iy++) {
coordinates_max_ptc[ix][iy].chany_max_ptc = std::max(coordinates_max_ptc[ix][iy].chany_max_ptc, node.ptc_num());
}
}
}
}
/* Alloc the lookup table */
for (t_rr_type rr_type : RR_TYPES) {
if (rr_type == CHANX) {
for (size_t y = 0; y < grid.height(); ++y) {
for (size_t x = 0; x < grid.width(); ++x) {
indices[CHANX][y][x][0].resize(coordinates_max_ptc[y][x].chanx_max_ptc + 1, OPEN);
}
}
} else if (rr_type == CHANY) {
for (size_t x = 0; x < grid.width(); ++x) {
for (size_t y = 0; y < grid.height(); ++y) {
indices[CHANY][x][y][0].resize(coordinates_max_ptc[x][y].chany_max_ptc + 1, OPEN);
}
}
}
}
int count;
/* CHANX and CHANY need to reevaluated with its ptc num as the correct index*/
for (size_t inode = 0; inode < device_ctx.rr_nodes.size(); inode++) {
auto& node = device_ctx.rr_nodes[inode];
if (node.type() == CHANX) {
for (int iy = node.ylow(); iy <= node.yhigh(); iy++) {
for (int ix = node.xlow(); ix <= node.xhigh(); ix++) {
count = node.ptc_num();
if (count >= int(indices[CHANX][iy][ix][0].size())) {
VPR_FATAL_ERROR(VPR_ERROR_ROUTE,
"Ptc index %d for CHANX (%d, %d) is out of bounds, size = %zu",
count, ix, iy, indices[CHANX][iy][ix][0].size());
}
indices[CHANX][iy][ix][0][count] = inode;
}
}
} else if (node.type() == CHANY) {
for (int ix = node.xlow(); ix <= node.xhigh(); ix++) {
for (int iy = node.ylow(); iy <= node.yhigh(); iy++) {
count = node.ptc_num();
if (count >= int(indices[CHANY][ix][iy][0].size())) {
VPR_FATAL_ERROR(VPR_ERROR_ROUTE,
"Ptc index %d for CHANY (%d, %d) is out of bounds, size = %zu",
count, ix, iy, indices[CHANY][ix][iy][0].size());
}
indices[CHANY][ix][iy][0][count] = inode;
}
}
}
}
//Copy the SOURCE/SINK nodes to all offset positions for blocks with width > 1 and/or height > 1
// This ensures that look-ups on non-root locations will still find the correct SOURCE/SINK
for (size_t x = 0; x < grid.width(); x++) {
for (size_t y = 0; y < grid.height(); y++) {
int width_offset = grid[x][y].width_offset;
int height_offset = grid[x][y].height_offset;
if (width_offset != 0 || height_offset != 0) {
int root_x = x - width_offset;
int root_y = y - height_offset;
indices[SOURCE][x][y] = indices[SOURCE][root_x][root_y];
indices[SINK][x][y] = indices[SINK][root_x][root_y];
}
}
}
device_ctx.rr_node_indices = indices;
}
/* This function sets the Source pins, sink pins, ipin, and opin
* to their unique cost index identifier. CHANX and CHANY cost indicies are set after the
* seg_id is read in from the rr graph*/
void set_cost_indices(pugi::xml_node parent, const pugiutil::loc_data& loc_data, const bool is_global_graph, const int num_seg_types) {
auto& device_ctx = g_vpr_ctx.mutable_device();
//set the cost index in order to load the segment information, rr nodes should be set already
for (size_t inode = 0; inode < device_ctx.rr_nodes.size(); inode++) {
if (device_ctx.rr_nodes[inode].type() == SOURCE) {
device_ctx.rr_nodes[inode].set_cost_index(SOURCE_COST_INDEX);
} else if (device_ctx.rr_nodes[inode].type() == SINK) {
device_ctx.rr_nodes[inode].set_cost_index(SINK_COST_INDEX);
} else if (device_ctx.rr_nodes[inode].type() == IPIN) {
device_ctx.rr_nodes[inode].set_cost_index(IPIN_COST_INDEX);
} else if (device_ctx.rr_nodes[inode].type() == OPIN) {
device_ctx.rr_nodes[inode].set_cost_index(OPIN_COST_INDEX);
}
}
pugi::xml_node segmentSubnode;
pugi::xml_node rr_node;
pugi::xml_attribute attribute;
/*Go through each rr_node and use the segment ids to set CHANX and CHANY cost index*/
rr_node = get_first_child(parent, "node", loc_data);
while (rr_node) {
int inode = get_attribute(rr_node, "id", loc_data).as_int();
auto& node = device_ctx.rr_nodes[inode];
/*CHANX and CHANY cost index is dependent on the segment id*/
segmentSubnode = get_single_child(rr_node, "segment", loc_data, pugiutil::OPTIONAL);
if (segmentSubnode) {
attribute = get_attribute(segmentSubnode, "segment_id", loc_data, pugiutil::OPTIONAL);
if (attribute) {
int seg_id = get_attribute(segmentSubnode, "segment_id", loc_data).as_int(0);
if (is_global_graph) {
node.set_cost_index(0);
} else if (node.type() == CHANX) {
node.set_cost_index(CHANX_COST_INDEX_START + seg_id);
} else if (node.type() == CHANY) {
node.set_cost_index(CHANX_COST_INDEX_START + num_seg_types + seg_id);
}
}
}
rr_node = rr_node.next_sibling(rr_node.name());
}
}